This commit is contained in:
team3
2026-09-01 13:07:02 +02:00
parent ef2ecc08f9
commit 8c7da42e64
16 changed files with 1178 additions and 878 deletions

View File

@@ -1,27 +1,26 @@
78098b89c88a74ebe8c20117b8754620 78098b89c88a74ebe8c20117b8754620
::res://::1788113016 ::res://::1788113016
anforderungen.md::TextFile::-1::1788165575::0::1::::<><><>0<>0<><>:: anforderungen.md::TextFile::-1::1788204851::0::1::::<><><>0<>0<><>::
README.md::TextFile::-1::1788165599::0::1::::<><><>0<>0<><>:: README.md::TextFile::-1::1788165734::0::1::::<><><>0<>0<><>::
::res://scenes/::1787989294 ::res://scenes/::1787989294
main.tscn::PackedScene::-1::1787989294::0::1::::<><><>0<>0<><>::res://scripts/main.gd main.tscn::PackedScene::-1::1787989294::0::1::::<><><>0<>0<><>::res://scripts/main.gd
::res://scripts/::1788163668 ::res://scripts/::1788209300
bewohner.gd::GDScript::2125278938812678811::1788165374::0::1::::Bewohner<>Node3D<><>0<>0<><>:: bewohner.gd::GDScript::2125278938812678811::1788209097::0::1::::Bewohner<>Node3D<><>0<>0<><>::
dream_db.gd::GDScript::4217518896355437099::1788164505::0::1::::DreamDB<>RefCounted<><>0<>0<><>:: dream_db.gd::GDScript::4217518896355437099::1788164505::0::1::::DreamDB<>RefCounted<><>0<>0<><>::
dream_world.gd::GDScript::8279158278942867258::1788164873::0::1::::DreamWorld<>Node3D<><>0<>0<><>:: dream_world.gd::GDScript::8279158278942867258::1788164873::0::1::::DreamWorld<>Node3D<><>0<>0<><>::
earth_world.gd::GDScript::6086818627272428934::1788165412::0::1::::EarthWorld<>Node3D<><>0<>0<><>:: earth_world.gd::GDScript::6086818627272428934::1788165412::0::1::::EarthWorld<>Node3D<><>0<>0<><>::
game.gd::GDScript::3600922541500868814::1788165125::0::1::::<>Node<><>0<>0<><>:: game.gd::GDScript::3600922541500868814::1788209816::0::1::::<>Node<><>0<>0<><>::
human_db.gd::GDScript::412604892032291296::1788165374::0::1::::HumanDB<>RefCounted<><>0<>0<><>:: human_db.gd::GDScript::412604892032291296::1788208999::0::1::::HumanDB<>RefCounted<><>0<>0<><>::
main.gd::GDScript::1504009331368977934::1788121650::0::1::::<>Node3D<><>0<>0<><>:: main.gd::GDScript::1504009331368977934::1788209245::0::1::::<>Node3D<><>0<>0<><>::
netz.gd::GDScript::8422943321868648339::1788163667::0::1::::Netz<>RefCounted<><>0<>0<><>:: netz.gd::GDScript::8422943321868648339::1788163667::0::1::::Netz<>RefCounted<><>0<>0<><>::
orte.gd::GDScript::5536340784294498671::1788163839::0::1::::Orte<>RefCounted<><>0<>0<><>:: orte.gd::GDScript::5536340784294498671::1788209635::0::1::::Orte<>RefCounted<><>0<>0<><>::
player.gd::GDScript::8724108548430371398::1788039718::0::1::::Player<>CharacterBody3D<><>0<>0<><>:: player.gd::GDScript::8724108548430371398::1788039718::0::1::::Player<>CharacterBody3D<><>0<>0<><>::
prims.gd::GDScript::322632462553361584::1788039797::0::1::::Prims<>RefCounted<><>0<>0<><>:: prims.gd::GDScript::322632462553361584::1788039797::0::1::::Prims<>RefCounted<><>0<>0<><>::
technique_db.gd::GDScript::4543286561661639701::1788120975::0::1::::TechDB<>RefCounted<><>0<>0<><>:: technique_db.gd::GDScript::4543286561661639701::1788209343::0::1::::TechDB<>RefCounted<><>0<>0<><>::
ui.gd::GDScript::843471304695966286::1788122559::0::1::::UI<>CanvasLayer<><>0<>0<><>:: ui.gd::GDScript::843471304695966286::1788209227::0::1::::UI<>CanvasLayer<><>0<>0<><>::
::res://tests/::1788164088 ursachen.gd::GDScript::7371116734724270900::1788208917::0::1::::Ursachen<>RefCounted<><>0<>0<><>::
probe.gd::GDScript::5788011518062194748::1788165350::0::1::::<>Node<><>0<>0<><>:: ::res://tests/::1788209571
probe.tscn::PackedScene::-1::1788163874::0::1::::<><><>0<>0<><>::res://tests/probe.gd shots.gd::GDScript::5999651346126871724::1788209816::0::1::::<>Node<><>0<>0<><>::
shots.gd::GDScript::5999651346126871724::1788118441::0::1::::<>Node<><>0<>0<><>::
shots.tscn::PackedScene::-1::1787990528::0::1::::<><><>0<>0<><>::res://tests/shots.gd shots.tscn::PackedScene::-1::1787990528::0::1::::<><><>0<>0<><>::res://tests/shots.gd
verify.gd::GDScript::4392606218392155269::1788165669::0::1::::<>Node<><>0<>0<><>:: verify.gd::GDScript::4392606218392155269::1788209816::0::1::::<>Node<><>0<>0<><>::
verify.tscn::PackedScene::-1::1787989983::0::1::::<><><>0<>0<><>::res://tests/verify.gd verify.tscn::PackedScene::-1::1787989983::0::1::::<><><>0<>0<><>::res://tests/verify.gd

View File

@@ -86,4 +86,12 @@ list=[{
"is_tool": false, "is_tool": false,
"language": &"GDScript", "language": &"GDScript",
"path": "res://scripts/ui.gd" "path": "res://scripts/ui.gd"
}, {
"base": &"RefCounted",
"class": &"Ursachen",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://scripts/ursachen.gd"
}] }]

Binary file not shown.

View File

@@ -24,41 +24,50 @@ Das Journal führt Buch — es lehrt und vergibt nichts.
* **Techniken** — was du gelernt hast, mit Stufe und Erfahrung. Besser werden * **Techniken** — was du gelernt hast, mit Stufe und Erfahrung. Besser werden
sie nur draußen. sie nur draußen.
* **Missionen** — die Menschen, für die du dich entschieden hast, jeweils mit * **Missionen** — die Menschen, für die du dich entschieden hast, jeweils mit
ihrem **Regelwerk**. ihrem **Tagesablauf**.
**Regelwerk** — der Kern des Spiels **Diagnose und Systeme** — der Kern des Spiels
Jede Tätigkeit im Tag eines Menschen kann eine Technik auslösen: *wenn Lena Ein Traum scheitert an Ursachen: *kein Anfang*, *keine Zeit*, *keine Kraft*,
zögert, dann Pomodoro*. Wechselt sie in die Tätigkeit, greift die Regel von *kein Halt*, *kein Grund*. Jeder Mensch hat zwei bis drei davon, nie alle. Die
allein, solange du auf der Erde bist. stehen nirgends — du findest sie nur, indem du dabeistehst.
**Ob eine Regel taugt, sagt dir niemand.** Keine Prozentzahl, kein „passt zum Sichtbar wird eine Ursache an den Tätigkeiten, die sie hervorbringt. Wer nie in
Thema", keine Liste guter Momente. Du bekommst nur die Technik selbst: was sie Gang kommt, räumt auf statt anzufangen, recherchiert oder schreibt Später-Listen.
tut, wann sie passt, warum sie hilft. Und den Menschen: seinen Traum und das, Wer keine Kraft hat, schaut abends eine Serie und bleibt danach wach. Jede
was im Weg steht. Beides zusammenzubringen ist das Spiel. dieser Tätigkeiten hat einen **Auslöser** und eine **Belohnung**.
Eine Technik darf an beliebig vielen Tätigkeiten hängen. Sie überall Gegen eine erkannte Ursache baust du ein System in den Tag:
hinzuhängen ist trotzdem falsch: im falschen Moment greift sie kaum und wirft
den Traum um 14 % zurück. Nach jedem Einsatz braucht sie außerdem 30 Sekunden Einführen Ein System, das fehlt — in einen freien Slot.
Ruhe — ein Fehlgriff verbrennt also den Platz, den ein guter Moment gehabt Ändern Eine Tätigkeit, die schadet — mit gleicher Belohnung ersetzt.
hätte. Mehr Momente sauber abzudecken heißt, mehr Techniken zu kennen, und die
gibt es nur auf der gewachsenen Insel. Liefert die Ersatzhandlung dieselbe Belohnung, hält sie besser. Das ist Duhiggs
goldene Regel: Auslöser und Belohnung bleiben, nur die Handlung dazwischen wird
getauscht. Manche Techniken ersetzen nichts, sie nehmen dem Verhalten den
Auslöser.
Der Fortschritt ist keine Kette von Einzeltreffern, sondern ein **Tempo**:
Tempo je Tag = Grundrate × Faktor je Ursache
nichts gelöst 4 % je Tag → der Traum steht praktisch still
eine gelöst 18 % je Tag → er kommt voran
alle gelöst 61 % je Tag → in gut zwei Tagen erfüllt
Ungelöste Ursachen bremsen multiplikativ. Zwei Bremsen sind viel schlimmer als
eine — deshalb lohnt es, alle zu finden.
So laufen die zwei Schleifen: So laufen die zwei Schleifen:
Techniken lernen → Regelwerk anpassen → Probleme lösen Techniken lernen → Systeme bauen → Traum läuft schneller
Träume erfüllen → Dorf wächst → neue Wege entdecken Träume erfüllen → Dorf wächst → neue Wege entdecken
Eine Regel greift immer wieder, solange der Moment anhält — im Takt des
Cooldowns. Wie viel ein Tag einbringt, hängt also daran, wie lange ein Mensch
in der passenden Tätigkeit bleibt.
Ein erfüllter Traum bringt ein Wesen mit seinem Haus ins Dorf, und in diesem Ein erfüllter Traum bringt ein Wesen mit seinem Haus ins Dorf, und in diesem
Haus wird ein neuer Weg gelehrt. Zu Beginn kennt das Dorf nur einen. Haus wird ein neuer Weg gelehrt. Zu Beginn kennt das Dorf nur einen.
Schlafende sind unerreichbar, dieselbe Technik wirkt bei Wiederholung Nach jedem Einsatz braucht eine Technik 30 Sekunden Ruhe. Ein Einbau gelingt
schwächer, ein Fehlversuch macht kurz dicht. Von Hand geht es weiter: nah nicht immer — Übung entscheidet.
herangehen und **E** wirkt sofort.
**Traumwelt** — eine schwebende Insel mit einem Dorf, das wächst **Traumwelt** — eine schwebende Insel mit einem Dorf, das wächst
@@ -133,18 +142,19 @@ Menschen einsetzen — auch Fehlversuche zählen.
project.godot Projekt, Autoload `Game` project.godot Projekt, Autoload `Game`
scenes/main.tscn Einstieg scenes/main.tscn Einstieg
scripts/game.gd Zustand, Regelwerk, wachsende Welt, Uhr (Autoload) scripts/game.gd Zustand, Diagnose, Systeme, Tempo, Uhr (Autoload)
scripts/technique_db.gd die zwölf Techniken mit ihren Hinweisen scripts/technique_db.gd die zwölf Techniken mit ihren Hinweisen
scripts/human_db.gd Bezirk: Arterien, Blöcke, roji, Parzellen, Menschen scripts/human_db.gd Bezirk: Arterien, Blöcke, roji, Parzellen, Menschen
scripts/dream_db.gd Traumdorf: wachsende Häuser, Wege, Bewohner, Lehrorte scripts/dream_db.gd Traumdorf: wachsende Häuser, Wege, Bewohner, Lehrorte
scripts/netz.gd Wegenetz: Knoten, Kanten, Andocken, kürzester Weg scripts/netz.gd Wegenetz: Knoten, Kanten, Andocken, kürzester Weg
scripts/orte.gd Orte, Türen, Wege, Tätigkeiten (beide Welten) scripts/orte.gd Orte, Türen, Wege, Tätigkeiten (beide Welten)
scripts/bewohner.gd Bewohner mit Tagesablauf, löst Regeln aus scripts/bewohner.gd Bewohner mit Tagesablauf und Slots
scripts/main.gd Weltenwechsel, Fokus, Interaktion scripts/main.gd Weltenwechsel, Fokus, Interaktion
scripts/player.gd Läufer in der Traumwelt, Geist auf der Erde scripts/player.gd Läufer in der Traumwelt, Geist auf der Erde
scripts/dream_world.gd Insel mit Dorf scripts/dream_world.gd Insel mit Dorf
scripts/earth_world.gd Stadt mit Tag-Nacht-Wechsel scripts/earth_world.gd Stadt mit Tag-Nacht-Wechsel
scripts/ui.gd HUD, Journal, Regelwerk, Hörsaal, Brett, Panels scripts/ursachen.gd Ursachen, blockierende Tätigkeiten, Belohnungen
scripts/ui.gd HUD, Journal, Tagesablauf, Hörsaal, Brett, Panels
scripts/prims.gd Bausteine aus Primitiven scripts/prims.gd Bausteine aus Primitiven
## Prüfen ## Prüfen
@@ -156,9 +166,9 @@ Menschen einsetzen — auch Fehlversuche zählen.
Tagesabläufen, gewachsenes Dorf und erzeugter Bezirk, Tagesabläufen, gewachsenes Dorf und erzeugter Bezirk,
Häuser betreten, Journal in beiden Welten, im Hörsaal lernen, Häuser betreten, Journal in beiden Welten, im Hörsaal lernen,
Verbessern allein durchs Anwenden, Mission am Schwarzen Brett annehmen, Verbessern allein durchs Anwenden, Mission am Schwarzen Brett annehmen,
Regelwerk bauen und einen Tag unbeaufsichtigt laufen lassen, zur Erde reisen, Ursachen aufdecken, Systeme bauen, Tempo messen, zur Erde reisen,
Tagesabläufe über einen ganzen Tag, Wirkung der Technikwahl, Cooldown, Tagesabläufe über einen ganzen Tag, Wirkung der Technikwahl, Cooldown,
verdeckte Erfolgschance, keine Passungshinweise, Mehrfachbelegung von Regeln, verdeckte Erfolgschance, keine Passungshinweise,
Tempo und Nachtsprung, Tempo und Nachtsprung,
nur lösbare Aushänge, Mission abschließen, nur lösbare Aushänge, Mission abschließen,
Insel wachsen lassen, Tastensteuerung. Beendet sich mit Code 1, wenn etwas Insel wachsen lassen, Tastensteuerung. Beendet sich mit Code 1, wenn etwas

View File

@@ -9,15 +9,8 @@ Loops
Funktionen Funktionen
- Bewegung: Bewegen, Springen, Geist - Bewegung: Bewegen, Springen, Geist
- Journal: Gelernte Techniken, Akzetierte Missionen - Journal: Gelernte Techniken, Akzetierte Missionen
- Techniken: Lernen am Lehrort, Verbessern durch Anwenden - Techniken: Lernen, Verbessern
- Missionen: Pipeline, nur lösbare werden angeboten - Missionen: Pipeline
- Techniken: Cooldown, Fehlschlag kostet Fortschritt - Techniken: Cooldown, Fehlschlag
- Erfolgschance und Passung bleiben verdeckt - Menschen: Tagesablauf, Tempo
- Eine Technik darf an mehreren Tätigkeiten hängen - Prozedurale Generierung: Dorf, Stadt
- Menschen: Tagesablauf
- Tempo: Stufe 1, 2, 3 wie in Die Sims (1×, 3×, 7×)
- Im Schlaf des Menschen automatisch Stufe 3
- Prozedurale Generierung
- Dorf: wächst Haus für Haus, verwinkelte Wege, grün und gemütlich
- Erde: Stadtbezirk nach japanischem Vorbild, Arterien und roji
- Auf der Erde sind alle Menschen von Anfang an da

View File

@@ -23,7 +23,7 @@ var _aura: MeshInstance3D
var _strahl: MeshInstance3D var _strahl: MeshInstance3D
var _path: Array = [] var _path: Array = []
var _letzte_aktivitaet: String = "" var _letzte_aktivitaet: String = ""
var _seit_regel: float = 0.0 var slot: int = 0
var _bob: float = 0.0 var _bob: float = 0.0
var _offset: Vector3 var _offset: Vector3
@@ -70,13 +70,18 @@ func setup(d: Dictionary, index: int, orte: Dictionary,
global_position = Orte.center(places, place) + _offset global_position = Orte.center(places, place) + _offset
## [Ort, Tätigkeit, Slotnummer] zur Stunde h. Steht in dem Slot ein System,
## ist die Tätigkeit die Ersatzhandlung.
func _routine_at(h: float) -> Array: func _routine_at(h: float) -> Array:
var r: Array = data["routine"] var r: Array = data["routine"]
var cur: Array = r[r.size() - 1] var nr := r.size() - 1
for eintrag in r: for i in range(r.size()):
if h >= float(eintrag[0]): if h >= float(r[i][0]):
cur = eintrag nr = i
return [cur[1], cur[2]] var akt: String = str(r[nr][2])
if mit_mission:
akt = Game.slot_aktivitaet(str(data["id"]), nr)
return [str(r[nr][1]), akt, nr]
func _process(roh: float) -> void: func _process(roh: float) -> void:
@@ -84,6 +89,7 @@ func _process(roh: float) -> void:
resist_left = maxf(0.0, resist_left - delta) resist_left = maxf(0.0, resist_left - delta)
var soll := _routine_at(Game.time_h) var soll := _routine_at(Game.time_h)
slot = int(soll[2])
if soll[0] != place: if soll[0] != place:
var war_unterwegs := not _path.is_empty() var war_unterwegs := not _path.is_empty()
_path = Orte.route(places, place, soll[0], _offset, netz, anker) _path = Orte.route(places, place, soll[0], _offset, netz, anker)
@@ -117,16 +123,8 @@ func _process(roh: float) -> void:
_visual.rotation.z = lerp_angle(_visual.rotation.z, PI * 0.5 if activity == "schlaeft" else 0.0, _visual.rotation.z = lerp_angle(_visual.rotation.z, PI * 0.5 if activity == "schlaeft" else 0.0,
1.0 - pow(0.01, delta)) 1.0 - pow(0.01, delta))
# Solange der Moment anhält, greift die Regel erneut, sobald ihre Technik
# wieder frei ist. Der Cooldown gibt damit den Takt vor.
_seit_regel += delta
if activity != _letzte_aktivitaet: if activity != _letzte_aktivitaet:
_letzte_aktivitaet = activity _letzte_aktivitaet = activity
_seit_regel = 0.0
_regel_pruefen()
elif _seit_regel >= 1.0:
_seit_regel = 0.0
_regel_pruefen()
var erledigt: bool = mit_mission and Game.completed.has(data["id"]) var erledigt: bool = mit_mission and Game.completed.has(data["id"])
var aktiv: bool = mit_mission and Game.active_mission == data["id"] var aktiv: bool = mit_mission and Game.active_mission == data["id"]
@@ -158,20 +156,6 @@ func beschriftung() -> String:
return s return s
## Wechselt der Mensch die Tätigkeit, greift das Regelwerk der Mission von allein.
func _regel_pruefen() -> void:
if not mit_mission:
return
var res := Game.regel_ausloesen(data["id"], activity, resist_left > 0.0)
if res.is_empty():
return
var t := TechDB.by_id(str(res["tech"]))
if res["success"]:
reagiere(true, "%s\n+%d %%" % [t["name"], int(round(float(res["gain"])))])
else:
reagiere(false, "%s prallt ab" % t["name"])
## Sichtbare Reaktion auf eine angewandte Technik. ## Sichtbare Reaktion auf eine angewandte Technik.
func reagiere(success: bool, text: String) -> void: func reagiere(success: bool, text: String) -> void:
var l := Prims.label(self, Vector3(0, 3.5, 0), text, 0.34, var l := Prims.label(self, Vector3(0, 3.5, 0), text, 0.34,

View File

@@ -21,9 +21,10 @@ const FEHLVERSUCH_VERLUST := 14.0 # ein Fehlschlag wirft den Traum zurück
var known: Dictionary = {} # tech_id -> xp var known: Dictionary = {} # tech_id -> xp
var active_mission: String = "" # human_id oder "" var active_mission: String = "" # human_id oder ""
var progress: Dictionary = {} # human_id -> 0..100 var progress: Dictionary = {} # human_id -> 0..100
var used: Dictionary = {} # "human:tech" -> Anzahl
var cooldown: Dictionary = {} # tech_id -> Restsekunden var cooldown: Dictionary = {} # tech_id -> Restsekunden
var pipeline: Dictionary = {} # human_id -> {activity -> tech_id} var systeme: Dictionary = {} # human_id -> {slot -> {tech, guete}}
var erkannt_slots: Dictionary = {} # human_id -> {slot -> true}
var beobachtung: Dictionary = {} # "human:slot" -> Sekunden
var completed: Array = [] var completed: Array = []
var time_h: float = 7.0 var time_h: float = 7.0
var day: int = 1 var day: int = 1
@@ -113,15 +114,6 @@ func advance_time(delta: float) -> void:
while time_h >= 24.0: while time_h >= 24.0:
time_h -= 24.0 time_h -= 24.0
day += 1 day += 1
_gewoehnung_klingt_ab()
## Abstand macht eine Technik wieder wirksam: über Nacht verfällt die Hälfte
## der Gewöhnung. Wer täglich denselben Weg geht, hält ihn brauchbar — wer ihn
## an einem Tag immer wieder auffährt, stumpft ihn ab.
func _gewoehnung_klingt_ab() -> void:
for key in used:
used[key] = int(used[key]) / 2
func time_string() -> String: func time_string() -> String:
@@ -261,25 +253,12 @@ func angenommen(human_id: String) -> bool:
return progress.has(human_id) return progress.has(human_id)
## Eine Mission ist machbar, wenn mindestens eine gelernte Technik das Thema ## Eine Mission ist machbar, wenn mindestens eine ihrer Ursachen mit einer
## des Menschen trifft und in seinem Tag auch einen Moment dafür findet. ## gelernten Technik anzugehen ist. Die übrigen bremsen dann eben noch.
## Nur solche werden am Schwarzen Brett überhaupt ausgehängt.
func machbar(human_id: String) -> bool: func machbar(human_id: String) -> bool:
var h := HumanDB.by_id(human_id) for u in ursachen_von(human_id):
if h.is_empty():
return false
var tag_liste: Array = HumanDB.aktivitaeten(human_id)
for id in known: for id in known:
var t := TechDB.by_id(id) if str(TechDB.by_id(id)["ursache"]) == str(u):
var thema := false
for tag in t["tags"]:
if (h["tags"] as Array).has(tag):
thema = true
break
if not thema:
continue
for a in t["momente"]:
if tag_liste.has(a) and erreichbar(a):
return true return true
return false return false
@@ -317,161 +296,188 @@ func _insel_waechst() -> void:
Color(0.75, 0.95, 1)) Color(0.75, 0.95, 1))
# ------------------------------------------------------------------- Regelwerk # --------------------------------------------------------------------- Systeme
# Eine Regel ist ein Wenn-Dann: tut der Mensch etwas Bestimmtes, greift die # Ein Traum scheitert an Ursachen. Die sind verdeckt und zeigen sich nur im
# zugeordnete Technik von allein. Damit automatisiert der Spieler den Fortschritt. # Tagesablauf. Wer sie gefunden hat, baut Systeme dagegen: in einen freien
# Slot ein neues, oder auf einen Blocker ein besseres. Je mehr Ursachen
# abgedeckt sind, desto schneller kommt der Traum voran.
func regeln(human_id: String) -> Dictionary: const BASIS_TEMPO := 45.0 # Prozent je Tag, wenn nichts mehr bremst
return pipeline.get(human_id, {}) const GUETE_ERSATZ := 1.35 # Blocker mit gleicher Belohnung ersetzt
const GUETE_NEU := 1.0 # System in einen freien Slot gesetzt
const ERKENNEN_SEK := 3.0 # so lange muss man dabeistehen
func regel_fuer(human_id: String, activity: String) -> String: func slots(human_id: String) -> Array:
return regeln(human_id).get(activity, "") var h := HumanDB.by_id(human_id)
return h["routine"] if h.has("routine") else []
func regel_anzahl(human_id: String) -> int: func slot_grund(human_id: String, nr: int) -> String:
return regeln(human_id).size() var r := slots(human_id)
return str(r[nr][2]) if nr >= 0 and nr < r.size() else ""
## An welchen Tätigkeiten hängt diese Technik gerade? ## Was in diesem Slot wirklich passiert: die Ersatzhandlung, wenn ein System
func regel_momente(human_id: String, tech_id: String) -> Array: ## steht -- sonst das, was von Haus aus dort steht.
var out: Array = [] func slot_aktivitaet(human_id: String, nr: int) -> String:
for activity in regeln(human_id): var sys := system_in(human_id, nr)
if regeln(human_id)[activity] == tech_id: if not sys.is_empty():
out.append(activity) return str(TechDB.by_id(str(sys["tech"]))["ersatz"])
return out return slot_grund(human_id, nr)
## Leere tech_id löscht die Regel. Eine Technik darf an beliebig vielen func system_in(human_id: String, nr: int) -> Dictionary:
## Tätigkeiten hängen — ob das klug ist, muss der Spieler aus ihr herauslesen. return systeme.get(human_id, {}).get(nr, {})
func regel_setzen(human_id: String, activity: String, tech_id: String) -> void:
if not pipeline.has(human_id):
pipeline[human_id] = {} # ------------------------------------------------------------------- Erkennen
var r: Dictionary = pipeline[human_id]
if tech_id == "": func erkannt(human_id: String, nr: int) -> bool:
r.erase(activity) if not Ursachen.ist_blocker(slot_grund(human_id, nr)):
else: return true
r[activity] = tech_id return erkannt_slots.get(human_id, {}).has(nr)
## Dabeistehen deckt auf, was dort wirklich läuft -- und damit die Ursache.
func beobachten(human_id: String, nr: int, delta: float) -> bool:
if erkannt(human_id, nr):
return false
var schluessel := "%s:%d" % [human_id, nr]
var stand: float = float(beobachtung.get(schluessel, 0.0)) + delta
beobachtung[schluessel] = stand
if stand < ERKENNEN_SEK:
return false
if not erkannt_slots.has(human_id):
erkannt_slots[human_id] = {}
erkannt_slots[human_id][nr] = true
var b := slot_grund(human_id, nr)
notified.emit("Erkannt: %s — %s" % [Ursachen.BLOCKER[b]["name"],
Ursachen.LISTE[Ursachen.ursache_von(b)]["name"]], Color(0.8, 0.95, 1))
state_changed.emit()
return true
## Eine Ursache gilt als erkannt, sobald eine ihrer Tätigkeiten aufgedeckt ist.
## Nur eine Quelle -- sonst driften Slot und Ursache auseinander.
func ursache_bekannt(human_id: String, ursache: String) -> bool:
for nr in erkannt_slots.get(human_id, {}):
if Ursachen.ursache_von(slot_grund(human_id, int(nr))) == ursache:
return true
return false
func ursachen_von(human_id: String) -> Array:
var h := HumanDB.by_id(human_id)
return h["ursachen"] if h.has("ursachen") else []
# -------------------------------------------------------------------- Tempo
## Steht gegen diese Ursache ein System? Gibt die Güte zurück, sonst 0.
func ursache_geloest(human_id: String, ursache: String) -> float:
var beste := 0.0
for nr in systeme.get(human_id, {}):
var sys: Dictionary = systeme[human_id][nr]
if str(TechDB.by_id(str(sys["tech"]))["ursache"]) == ursache:
beste = maxf(beste, float(sys["guete"]))
return beste
## Wie schnell kommt der Traum voran? Jede ungelöste Ursache bremst.
func traum_tempo(human_id: String) -> float:
var t := BASIS_TEMPO
for u in ursachen_von(human_id):
var g := ursache_geloest(human_id, str(u))
t *= g if g > 0.0 else float(Ursachen.LISTE[u]["faktor"])
return t
# ------------------------------------------------------------- System setzen
## Passt diese Technik in diesen Slot? Gibt Güte und Grund zurück.
func system_pruefen(human_id: String, nr: int, tech_id: String) -> Dictionary:
var res := {"geht": false, "guete": 0.0, "grund": ""}
if not known.has(tech_id):
res["grund"] = "Diese Technik kennst du nicht."
return res
if not bereit(tech_id):
res["grund"] = "%s ist noch nicht wieder einsatzbereit." % TechDB.by_id(tech_id)["name"]
return res
var grund_akt := slot_grund(human_id, nr)
if grund_akt == "schlaeft":
res["grund"] = "Im Schlaf lässt sich nichts einrichten."
return res
if not erkannt(human_id, nr):
res["grund"] = "Was hier läuft, hast du noch nicht gesehen."
return res
var t := TechDB.by_id(tech_id)
if Ursachen.ist_blocker(grund_akt):
var b: Dictionary = Ursachen.BLOCKER[grund_akt]
if str(t["ursache"]) != str(b["ursache"]):
res["grund"] = "Das hier hat einen anderen Grund."
return res
res["geht"] = true
var gleich: bool = str(t["belohnung"]) == str(b["belohnung"])
res["guete"] = GUETE_ERSATZ if gleich or TechDB.entfernt_ausloeser(tech_id) else GUETE_NEU
return res
res["geht"] = true
res["guete"] = GUETE_NEU
return res
## Richtet das System ein. Gelingt nicht immer -- Übung entscheidet.
func system_setzen(human_id: String, nr: int, tech_id: String, widerstand: bool) -> Dictionary:
var pruef := system_pruefen(human_id, nr, tech_id)
if not pruef["geht"]:
return {"ok": false, "grund": pruef["grund"]}
cooldown[tech_id] = TECHNIK_COOLDOWN
var chance := clampf(0.20 + 0.13 * float(skill_of(tech_id)) - (0.25 if widerstand else 0.0),
0.05, 0.95)
var erfolg := randf() < chance
if not erfolg:
add_xp(tech_id, 1)
state_changed.emit()
return {"ok": true, "erfolg": false, "guete": 0.0}
if not systeme.has(human_id):
systeme[human_id] = {}
systeme[human_id][nr] = {"tech": tech_id, "guete": float(pruef["guete"])}
add_xp(tech_id, 2)
notified.emit("System steht: %s um %02d:00" %
[TechDB.by_id(tech_id)["name"], int(slots(human_id)[nr][0])], Color(0.6, 1, 0.7))
state_changed.emit()
return {"ok": true, "erfolg": true, "guete": float(pruef["guete"])}
func system_entfernen(human_id: String, nr: int) -> void:
if systeme.has(human_id):
systeme[human_id].erase(nr)
state_changed.emit() state_changed.emit()
## Greift für diese Tätigkeit eine Regel? Wird nicht nur beim Wechsel gefragt, func system_anzahl(human_id: String) -> int:
## sondern immer wieder — solange der Moment anhält und die Technik frei ist. return systeme.get(human_id, {}).size()
func regel_ausloesen(human_id: String, activity: String, resisting: bool) -> Dictionary:
if human_id != active_mission:
return {}
var tech_id := regel_fuer(human_id, activity)
if tech_id == "" or not known.has(tech_id):
return {}
if not bereit(tech_id):
return {} # still: die Technik greift wieder, sobald ihre Sperre fällt
var res := apply_technique(tech_id, human_id, activity, resisting)
if not res["ok"]:
return {}
res["tech"] = tech_id
var t := TechDB.by_id(tech_id)
if res["success"]:
notified.emit("Regel greift — %s bei »%s« (+%d %%)" %
[t["name"], Orte.text(activity), int(round(float(res["gain"])))],
Color(0.6, 1, 0.7))
else:
notified.emit("Regel läuft ins Leere — %s bei »%s« (−%d %%)" %
[t["name"], Orte.text(activity), int(round(float(res["verlust"])))],
Color(1, 0.7, 0.5))
return res
# ----------------------------------------------------- Technik auf Mensch anwenden ## Der Traum kommt voran, solange Systeme stehen.
func traum_voranbringen(stunden: float) -> void:
## Schlafende erreicht keine Technik — weder von Hand noch über eine Regel. if active_mission == "":
func erreichbar(activity: String) -> bool: return
return activity != "schlaeft" var p: float = progress.get(active_mission, 0.0) \
+ traum_tempo(active_mission) * stunden / 24.0
progress[active_mission] = minf(p, MISSION_ZIEL)
## Bewertet eine Technik in genau dieser Situation, ohne sie anzuwenden. if progress[active_mission] >= MISSION_ZIEL and not completed.has(active_mission):
func evaluate(tech_id: String, human_id: String, activity: String, resisting: bool) -> Dictionary: var wer := active_mission
var t := TechDB.by_id(tech_id) completed.append(wer)
var h := HumanDB.by_id(human_id)
var skill := skill_of(tech_id)
var res := {
"chance": 0.0, "tag": false, "moment": false, "blocked": false,
"grund": "", "skill": skill,
}
if not erreichbar(activity):
res["blocked"] = true
res["grund"] = "Sie oder er schläft. Techniken erreichen nur wache Menschen."
return res
if skill == 0:
res["blocked"] = true
res["grund"] = "Technik nicht gelernt."
return res
for tag in t["tags"]:
if h["tags"].has(tag):
res["tag"] = true
break
res["moment"] = t["momente"].has(activity)
# Der Moment wiegt am schwersten: eine Technik zur falschen Zeit greift kaum,
# egal wie gut sie zum Menschen passt. Genau das steht in ihrem "Wann".
var c := 0.05 + 0.09 * float(skill)
if res["tag"]:
c += 0.20
if res["moment"]:
c += 0.35
if resisting:
c -= 0.30
res["grund"] = "Der Mensch macht gerade dicht."
res["chance"] = clampf(c, 0.05, 0.95)
return res
## Wendet die Technik wirklich an: würfelt, verteilt EP und Fortschritt.
func apply_technique(tech_id: String, human_id: String, activity: String, resisting: bool) -> Dictionary:
if not bereit(tech_id):
return {"ok": false, "grund": "%s ist noch nicht wieder einsatzbereit." %
TechDB.by_id(tech_id)["name"]}
var ev := evaluate(tech_id, human_id, activity, resisting)
if ev["blocked"]:
return {"ok": false, "grund": ev["grund"]}
cooldown[tech_id] = TECHNIK_COOLDOWN
var roll := randf()
var success: bool = roll < float(ev["chance"])
var key := "%s:%s" % [human_id, tech_id]
var wiederholung: int = used.get(key, 0)
used[key] = wiederholung + 1
var gain := 0.0
var verlust := 0.0
if success:
var base := 8.0 + 2.0 * float(ev["skill"])
if ev["tag"]:
base += 5.0
if ev["moment"]:
base += 5.0
gain = base / (1.0 + 0.6 * float(wiederholung))
add_xp(tech_id, 2)
else:
verlust = FEHLVERSUCH_VERLUST
add_xp(tech_id, 1)
var done := false
if human_id == active_mission:
var p: float = progress.get(human_id, 0.0) + gain - verlust
progress[human_id] = clampf(p, 0.0, MISSION_ZIEL)
if progress[human_id] >= MISSION_ZIEL and not completed.has(human_id):
completed.append(human_id)
active_mission = "" active_mission = ""
done = true mission_done.emit(wer)
mission_done.emit(human_id) notified.emit("%s hat es geschafft." % HumanDB.by_id(wer)["name"], Color(1, 0.85, 0.4))
notified.emit("%s hat es geschafft." % HumanDB.by_id(human_id)["name"],
Color(1, 0.85, 0.4))
_insel_waechst() _insel_waechst()
state_changed.emit() state_changed.emit()
return {
"ok": true, "success": success, "chance": ev["chance"], "gain": gain, # --------------------------------------------------- Erreichbarkeit
"verlust": verlust, "wiederholung": wiederholung, "mission_done": done,
"tag": ev["tag"], "moment": ev["moment"], ## Schlafende erreicht niemand.
} func erreichbar(activity: String) -> bool:
return activity != "schlaeft"

View File

@@ -101,32 +101,20 @@ static var ANLIEGEN: Array = [
}, },
] ]
## Tagesabläufe: Platzhalter werden durch Wohnung und Arbeitsplatz ersetzt. ## Der Grundtag. Blocker werden später in passende Fenster gesetzt.
## Tagesabläufe: Platzhalter werden durch Wohnung und Arbeitsplatz ersetzt. ## [Stunde, Ort, Tätigkeit, Fenster]
## Die Reihenfolge passt zu den Anliegen — wer aufschiebt, arbeitet zu Hause. static var GRUNDTAG: Array = [
static var ABLAEUFE: Array = [ [0, "heim", "schlaeft", "nacht"],
[[0, "heim", "schlaeft"], [7, "heim", "zoegert"], [10, "heim", "arbeitet"], [7, "heim", "morgenroutine", "morgen"],
[13, "park", "pause"], [15, "heim", "arbeitet"], [18, "cafe", "wartet"], [9, "arbeit", "arbeitet", "tag"],
[20, "heim", "zoegert"], [22, "heim", "gruebelt"], [23, "heim", "abendroutine"]], [12, "cafe", "essen", "tag"],
[[0, "heim", "schlaeft"], [4, "arbeit", "arbeitet"], [10, "cafe", "pause"], [14, "arbeit", "arbeitet", "tag"],
[12, "heim", "essen"], [14, "heim", "ruht"], [16, "heim", "zoegert"], [17, "park", "unterwegs", "abend"],
[18, "park", "unterwegs"], [20, "heim", "muede"], [22, "heim", "abendroutine"]], [19, "heim", "ruht", "abend"],
[[0, "heim", "schlaeft"], [8, "heim", "morgenroutine"], [10, "arbeit", "arbeitet"], [21, "heim", "ruht", "abend"],
[13, "cafe", "angespannt"], [15, "arbeit", "wartet"], [18, "heim", "gruebelt"], [23, "heim", "abendroutine", "nacht"],
[20, "park", "unterwegs"], [22, "heim", "abendroutine"]],
[[0, "heim", "schlaeft"], [6, "heim", "morgenroutine"], [8, "arbeit", "arbeitet"],
[12, "cafe", "essen"], [14, "arbeit", "arbeitet"], [19, "arbeit", "angespannt"],
[21, "heim", "essen"], [23, "heim", "muede"]],
[[0, "heim", "schlaeft"], [7, "heim", "morgenroutine"], [9, "arbeit", "arbeitet"],
[12, "cafe", "essen"], [14, "arbeit", "arbeitet"], [17, "park", "unterwegs"],
[19, "heim", "aufraeumen"], [21, "heim", "gruebelt"], [23, "heim", "abendroutine"]],
] ]
## Zu jeder Technik ein Anliegen und ein Tagesablauf, bei dem sie greift:
## [Anliegen, Ablauf]. Mensch i bekommt den Eintrag zu Technik i — und Technik i
## ist genau dann entdeckt, wenn i Träume erfüllt sind. So hängt am Schwarzen
## Brett immer mindestens ein Mensch, dem mit dem Gelernten zu helfen ist.
static var PASSEND: Array = [ static var PASSEND: Array = [
[0, 0], # Pomodoro -> aufschieben, zögert [0, 0], # Pomodoro -> aufschieben, zögert
[6, 4], # Zwei-Minuten -> Werkstatt, räumt auf [6, 4], # Zwei-Minuten -> Werkstatt, räumt auf
@@ -403,25 +391,68 @@ static func _anker_setzen() -> void:
ANKER[key] = int(NETZ.andocken(tuer)["knoten"]) ANKER[key] = int(NETZ.andocken(tuer)["knoten"])
static func _mensch(i: int) -> Dictionary: ## Woran der Traum dieses Menschen scheitert. Zwei bis drei Ursachen, nie alle.
# Bis alle Wege entdeckt sind, folgt jeder Mensch dem Weg gleicher Nummer. ## Die erste hängt am Weg gleicher Nummer -- so ist der neueste Mensch immer
# Danach mischt sich das Feld wieder. ## mit dem neuesten Weg anzugehen.
var a_nr: int = i % ANLIEGEN.size() static func ursachen_von(i: int) -> Array:
var b_nr: int = i % ABLAEUFE.size() var alle: Array = Ursachen.LISTE.keys()
if i < PASSEND.size(): var erste: String = str(alle[i % alle.size()])
a_nr = int(PASSEND[i][0]) if i < TechDB.LIST.size():
b_nr = int(PASSEND[i][1]) erste = str(TechDB.LIST[i]["ursache"])
var anliegen: Dictionary = ANLIEGEN[a_nr] var out: Array = [erste]
var heim := heim_key(i) var zweite: String = str(alle[(alle.find(erste) + 1 + i % 3) % alle.size()])
var arbeit := _naechster_arbeitsplatz(heim, i) if zweite != erste:
var ablauf: Array = [] out.append(zweite)
for e in ABLAEUFE[b_nr]: if i % 3 == 2:
var dritte: String = str(alle[(alle.find(erste) + 3) % alle.size()])
if not out.has(dritte):
out.append(dritte)
return out
## Der Tagesablauf: Grundtag, in den je Ursache ein Blocker gesetzt wird.
static func _tagesablauf(i: int, heim: String, arbeit: String) -> Array:
var slots: Array = []
for e in GRUNDTAG:
var ort: String = str(e[1]) var ort: String = str(e[1])
if ort == "heim": if ort == "heim":
ort = heim ort = heim
elif ort == "arbeit": elif ort == "arbeit":
ort = arbeit ort = arbeit
ablauf.append([e[0], ort, e[2]]) slots.append([int(e[0]), ort, str(e[2]), str(e[3])])
var belegt: Dictionary = {}
var nr := 0
for u in ursachen_von(i):
for fenster in ["tag", "abend", "morgen", "nacht"]:
var kandidaten: Array = Ursachen.blocker_fuer(str(u), fenster)
if kandidaten.is_empty():
continue
var blocker: String = str(kandidaten[(i + nr) % kandidaten.size()])
var gesetzt := false
for k in range(slots.size()):
if belegt.has(k) or str(slots[k][2]) == "schlaeft":
continue
if str(slots[k][3]) != fenster:
continue
slots[k][2] = blocker
belegt[k] = true
gesetzt = true
break
if gesetzt:
break
nr += 1
var out: Array = []
for e in slots:
out.append([e[0], e[1], e[2]])
return out
static func _mensch(i: int) -> Dictionary:
var anliegen: Dictionary = ANLIEGEN[i % ANLIEGEN.size()]
if i < PASSEND.size():
anliegen = ANLIEGEN[int(PASSEND[i][0])]
var heim := heim_key(i)
var arbeit := _naechster_arbeitsplatz(heim, i)
return { return {
"id": "mensch_%d" % i, "id": "mensch_%d" % i,
"name": "%s %s" % [_vorname(i), _nachname(i)], "name": "%s %s" % [_vorname(i), _nachname(i)],
@@ -430,10 +461,11 @@ static func _mensch(i: int) -> Dictionary:
"traum": str(anliegen["traum"]), "traum": str(anliegen["traum"]),
"problem": str(anliegen["problem"]), "problem": str(anliegen["problem"]),
"tags": (anliegen["tags"] as Array).duplicate(), "tags": (anliegen["tags"] as Array).duplicate(),
"ursachen": ursachen_von(i),
"mission": true, "mission": true,
"heim": heim, "heim": heim,
"farbe": Color.from_hsv(fmod(0.02 + float(i) * 0.191, 1.0), 0.5, 0.8), "farbe": Color.from_hsv(fmod(0.02 + float(i) * 0.191, 1.0), 0.5, 0.8),
"routine": ablauf, "routine": _tagesablauf(i, heim, arbeit),
} }

View File

@@ -65,15 +65,28 @@ func _setze_welt(neu: Node3D) -> void:
func _process(delta: float) -> void: func _process(delta: float) -> void:
Game.advance_time(delta * Game.zeit_faktor()) var dt := delta * Game.zeit_faktor()
Game.advance_time(dt)
Game.traum_voranbringen(dt * 24.0 / Game.DAY_SECONDS)
Game.teleport_lock = maxf(0.0, Game.teleport_lock - delta) Game.teleport_lock = maxf(0.0, Game.teleport_lock - delta)
if ui.offen: if ui.offen:
ui.setze_hinweis("") ui.setze_hinweis("")
return return
_fokus_suchen() _fokus_suchen()
_beobachten(dt)
_hinweis_setzen() _hinweis_setzen()
## Dabeistehen deckt auf, was der Mensch gerade wirklich tut.
func _beobachten(dt: float) -> void:
if _fokus_mensch == null or not auf_erde:
return
var id: String = str(_fokus_mensch.data["id"])
if id != Game.active_mission:
return
Game.beobachten(id, int(_fokus_mensch.slot), dt)
func _fokus_suchen() -> void: func _fokus_suchen() -> void:
_fokus = null _fokus = null
_fokus_mensch = null _fokus_mensch = null
@@ -99,8 +112,13 @@ func _fokus_suchen() -> void:
func _hinweis_setzen() -> void: func _hinweis_setzen() -> void:
if _fokus_mensch != null: if _fokus_mensch != null:
ui.setze_hinweis("[E] %s ansprechen — %s" % var mid: String = str(_fokus_mensch.data["name"])
[_fokus_mensch.data["name"], Orte.text(_fokus_mensch.activity)]) var mnr: int = int(_fokus_mensch.slot)
var mkey: String = str(_fokus_mensch.data["id"])
if mkey == Game.active_mission and not Game.erkannt(mkey, mnr):
ui.setze_hinweis("%s — schau zu, was sie hier tut …" % mid)
else:
ui.setze_hinweis("[E] %s — %s" % [mid, Orte.text(_fokus_mensch.activity)])
elif _fokus != null: elif _fokus != null:
var kind: String = _fokus.get_meta("kind") var kind: String = _fokus.get_meta("kind")
var titel: String = _fokus.get_meta("titel") var titel: String = _fokus.get_meta("titel")

View File

@@ -37,8 +37,15 @@ static var ACTIVITY_TEXT: Dictionary = {
} }
## Blocker tragen ihren Namen im Ursachen-Katalog, alles andere hier.
static func text(a: String) -> String: static func text(a: String) -> String:
return ACTIVITY_TEXT.get(a, a) if Ursachen.ist_blocker(a):
return str(Ursachen.BLOCKER[a]["name"])
return str(ACTIVITY_TEXT.get(a, a))
static func kennt(a: String) -> bool:
return ACTIVITY_TEXT.has(a) or Ursachen.ist_blocker(a)
static func center(places: Dictionary, key: String) -> Vector3: static func center(places: Dictionary, key: String) -> Vector3:

View File

@@ -9,7 +9,10 @@ static var LIST: Array = [
"was": "25 Minuten am Stück an einer Sache arbeiten, dann 5 Minuten Pause. Nach vier Runden eine lange Pause.", "was": "25 Minuten am Stück an einer Sache arbeiten, dann 5 Minuten Pause. Nach vier Runden eine lange Pause.",
"wann": "Wenn eine Aufgabe zu groß wirkt und der Anfang immer wieder verschoben wird.", "wann": "Wenn eine Aufgabe zu groß wirkt und der Anfang immer wieder verschoben wird.",
"warum": "Ein klar begrenzter Zeitraum senkt die Einstiegshürde. Der Kopf muss nur 25 Minuten durchhalten, nicht den ganzen Tag.", "warum": "Ein klar begrenzter Zeitraum senkt die Einstiegshürde. Der Kopf muss nur 25 Minuten durchhalten, nicht den ganzen Tag.",
"woran": "jemandem, der zögert — oder der arbeitet und trotzdem nicht vorankommt.", "woran": "jemandem, der aufräumt oder recherchiert, statt endlich anzufangen.",
"ursache": "anfang",
"belohnung": "erfolg",
"ersatz": "arbeitet in Blöcken",
"tags": ["prokrastination", "fokus"], "tags": ["prokrastination", "fokus"],
"momente": ["arbeitet", "zoegert"], "momente": ["arbeitet", "zoegert"],
"farbe": Color(0.95, 0.45, 0.30), "farbe": Color(0.95, 0.45, 0.30),
@@ -20,7 +23,10 @@ static var LIST: Array = [
"was": "Was unter zwei Minuten dauert, sofort erledigen. Große Vorhaben auf einen Zwei-Minuten-Anfang schrumpfen.", "was": "Was unter zwei Minuten dauert, sofort erledigen. Große Vorhaben auf einen Zwei-Minuten-Anfang schrumpfen.",
"wann": "Bei einem Berg von Kleinkram oder wenn schon der erste Schritt blockiert.", "wann": "Bei einem Berg von Kleinkram oder wenn schon der erste Schritt blockiert.",
"warum": "Der Start kostet die meiste Energie. Zwei Minuten sind zu klein zum Ablehnen und ziehen meist die ganze Aufgabe nach sich.", "warum": "Der Start kostet die meiste Energie. Zwei Minuten sind zu klein zum Ablehnen und ziehen meist die ganze Aufgabe nach sich.",
"woran": "jemandem, der zögert, lieber aufräumt als anzufangen, oder beim Arbeiten im Kleinkram hängt.", "woran": "jemandem, der aufräumt statt anzufangen oder eine Später-Liste schreibt.",
"ursache": "anfang",
"belohnung": "erfolg",
"ersatz": "fängt zwei Minuten an",
"tags": ["prokrastination", "ueberforderung"], "tags": ["prokrastination", "ueberforderung"],
"momente": ["zoegert", "aufraeumen", "arbeitet"], "momente": ["zoegert", "aufraeumen", "arbeitet"],
"farbe": Color(0.98, 0.72, 0.25), "farbe": Color(0.98, 0.72, 0.25),
@@ -31,7 +37,10 @@ static var LIST: Array = [
"was": "Den Vorsatz an einen konkreten Auslöser binden: \"Wenn ich X tue, dann mache ich Y.\"", "was": "Den Vorsatz an einen konkreten Auslöser binden: \"Wenn ich X tue, dann mache ich Y.\"",
"wann": "Wenn ein Vorhaben im Alltag jedes Mal untergeht.", "wann": "Wenn ein Vorhaben im Alltag jedes Mal untergeht.",
"warum": "Der Plan hängt an einer Situation statt an Willenskraft. Der Auslöser erinnert von allein.", "warum": "Der Plan hängt an einer Situation statt an Willenskraft. Der Auslöser erinnert von allein.",
"woran": "der Morgenroutine und der Pause — den festen Punkten, an die sich ein Vorsatz hängen lässt.", "woran": "jemandem, der eine Später-Liste schreibt und dann doch nichts tut.",
"ursache": "anfang",
"belohnung": "ordnung",
"ersatz": "folgt dem Wenn-Dann-Plan",
"tags": ["gewohnheit", "prokrastination"], "tags": ["gewohnheit", "prokrastination"],
"momente": ["morgenroutine", "pause"], "momente": ["morgenroutine", "pause"],
"farbe": Color(0.45, 0.80, 0.55), "farbe": Color(0.45, 0.80, 0.55),
@@ -42,7 +51,10 @@ static var LIST: Array = [
"was": "Eine neue, kleine Gewohnheit direkt an eine bestehende anhängen.", "was": "Eine neue, kleine Gewohnheit direkt an eine bestehende anhängen.",
"wann": "Wenn eine neue Routine nie einen Platz im Tag findet.", "wann": "Wenn eine neue Routine nie einen Platz im Tag findet.",
"warum": "Die alte Gewohnheit ist der Anker. Es muss kein neuer Zeitpunkt erfunden werden.", "warum": "Die alte Gewohnheit ist der Anker. Es muss kein neuer Zeitpunkt erfunden werden.",
"woran": "den Ankern des Tages: Morgenroutine, Essen, Abendroutine.", "woran": "jemandem, der auf etwas anderes umschaltet, sobald es zäh wird.",
"ursache": "halt",
"belohnung": "ordnung",
"ersatz": "hängt es an die Routine",
"tags": ["gewohnheit"], "tags": ["gewohnheit"],
"momente": ["morgenroutine", "essen", "abendroutine"], "momente": ["morgenroutine", "essen", "abendroutine"],
"farbe": Color(0.40, 0.70, 0.85), "farbe": Color(0.40, 0.70, 0.85),
@@ -53,7 +65,10 @@ static var LIST: Array = [
"was": "Ein großes Ziel in benennbare Einzelschritte teilen, bis der nächste Schritt trivial ist.", "was": "Ein großes Ziel in benennbare Einzelschritte teilen, bis der nächste Schritt trivial ist.",
"wann": "Wenn jemand vor einem Vorhaben erstarrt und nicht weiß, wo er anfangen soll.", "wann": "Wenn jemand vor einem Vorhaben erstarrt und nicht weiß, wo er anfangen soll.",
"warum": "Unklarheit lähmt stärker als Aufwand. Ein sichtbarer nächster Schritt macht Handeln wieder möglich.", "warum": "Unklarheit lähmt stärker als Aufwand. Ein sichtbarer nächster Schritt macht Handeln wieder möglich.",
"woran": "jemandem, der grübelt statt anzufangen, oder der arbeitet und sich im Vorhaben verliert.", "woran": "jemandem, der recherchiert statt anzufangen oder eine Später-Liste schreibt.",
"ursache": "anfang",
"belohnung": "ordnung",
"ersatz": "zerlegt die Aufgabe",
"tags": ["ueberforderung", "prokrastination", "ziel"], "tags": ["ueberforderung", "prokrastination", "ziel"],
"momente": ["gruebelt", "arbeitet"], "momente": ["gruebelt", "arbeitet"],
"farbe": Color(0.75, 0.55, 0.95), "farbe": Color(0.75, 0.55, 0.95),
@@ -64,7 +79,10 @@ static var LIST: Array = [
"was": "Das gewünschte Verhalten leichter machen, das störende schwerer. Handy in einen anderen Raum, Instrument sichtbar aufstellen.", "was": "Das gewünschte Verhalten leichter machen, das störende schwerer. Handy in einen anderen Raum, Instrument sichtbar aufstellen.",
"wann": "Wenn gute Vorsätze regelmäßig an Ablenkung scheitern.", "wann": "Wenn gute Vorsätze regelmäßig an Ablenkung scheitern.",
"warum": "Verhalten folgt dem geringsten Widerstand. Die Umgebung wirkt zuverlässiger als Disziplin.", "warum": "Verhalten folgt dem geringsten Widerstand. Die Umgebung wirkt zuverlässiger als Disziplin.",
"woran": "jemandem, der aufräumt, arbeitet oder sich ausruht — dann ist der Raum um ihn herum greifbar.", "woran": "jemandem, der noch eine Runde spielt oder ständig umschaltet.",
"ursache": "halt",
"belohnung": "",
"ersatz": "hat es sich leicht gemacht",
"tags": ["ablenkung", "gewohnheit"], "tags": ["ablenkung", "gewohnheit"],
"momente": ["aufraeumen", "arbeitet", "ruht"], "momente": ["aufraeumen", "arbeitet", "ruht"],
"farbe": Color(0.55, 0.85, 0.40), "farbe": Color(0.55, 0.85, 0.40),
@@ -75,7 +93,10 @@ static var LIST: Array = [
"was": "Einen belastenden Gedanken prüfen und durch eine ebenso wahre, aber tragfähigere Sicht ersetzen.", "was": "Einen belastenden Gedanken prüfen und durch eine ebenso wahre, aber tragfähigere Sicht ersetzen.",
"wann": "Wenn jemand in einem Gedanken feststeckt, der ihn klein hält.", "wann": "Wenn jemand in einem Gedanken feststeckt, der ihn klein hält.",
"warum": "Nicht das Ereignis belastet, sondern seine Deutung. Eine andere Deutung verändert Gefühl und Handlung.", "warum": "Nicht das Ereignis belastet, sondern seine Deutung. Eine andere Deutung verändert Gefühl und Handlung.",
"woran": "jemandem, der grübelt, angespannt ist oder sich ausruht und den Gedanken doch nicht loswird.", "woran": "jemandem, der sich mit anderen vergleicht und dabei klein wird.",
"ursache": "grund",
"belohnung": "einordnung",
"ersatz": "deutet den Gedanken um",
"tags": ["selbstzweifel", "angst"], "tags": ["selbstzweifel", "angst"],
"momente": ["gruebelt", "ruht", "angespannt"], "momente": ["gruebelt", "ruht", "angespannt"],
"farbe": Color(0.95, 0.55, 0.75), "farbe": Color(0.95, 0.55, 0.75),
@@ -86,7 +107,10 @@ static var LIST: Array = [
"was": "Vier Sekunden einatmen, vier halten, vier ausatmen, vier halten. Mehrere Runden lang.", "was": "Vier Sekunden einatmen, vier halten, vier ausatmen, vier halten. Mehrere Runden lang.",
"wann": "Bei akuter Anspannung, kurz vor einem gefürchteten Gespräch oder Auftritt.", "wann": "Bei akuter Anspannung, kurz vor einem gefürchteten Gespräch oder Auftritt.",
"warum": "Langes Ausatmen bremst den Alarmzustand des Körpers. Der Puls sinkt, der Kopf wird wieder klar.", "warum": "Langes Ausatmen bremst den Alarmzustand des Körpers. Der Puls sinkt, der Kopf wird wieder klar.",
"woran": "jemandem, der angespannt ist, wartet oder in der Pause nicht runterkommt.", "woran": "jemandem, der abends eine Serie schaut, um überhaupt runterzukommen.",
"ursache": "kraft",
"belohnung": "abschalten",
"ersatz": "atmet sich ruhig",
"tags": ["angst", "stress"], "tags": ["angst", "stress"],
"momente": ["angespannt", "wartet", "pause"], "momente": ["angespannt", "wartet", "pause"],
"farbe": Color(0.40, 0.85, 0.95), "farbe": Color(0.40, 0.85, 0.95),
@@ -97,7 +121,10 @@ static var LIST: Array = [
"was": "Mit sich selbst so sprechen wie mit einem guten Freund in derselben Lage.", "was": "Mit sich selbst so sprechen wie mit einem guten Freund in derselben Lage.",
"wann": "Nach einem Fehler oder Rückschlag, wenn die innere Kritik lauter wird als der Vorsatz.", "wann": "Nach einem Fehler oder Rückschlag, wenn die innere Kritik lauter wird als der Vorsatz.",
"warum": "Harte Selbstkritik erhöht die Vermeidung. Freundlichkeit hält handlungsfähig und senkt die Abbruchquote.", "warum": "Harte Selbstkritik erhöht die Vermeidung. Freundlichkeit hält handlungsfähig und senkt die Abbruchquote.",
"woran": "jemandem, der nach einem Fehler grübelt, angespannt ist oder sich ausruht, ohne zur Ruhe zu kommen.", "woran": "jemandem, der am Telefon klagt, statt weiterzumachen.",
"ursache": "grund",
"belohnung": "verbundenheit",
"ersatz": "ist freundlich mit sich",
"tags": ["selbstzweifel", "rueckschlag"], "tags": ["selbstzweifel", "rueckschlag"],
"momente": ["gruebelt", "ruht", "angespannt"], "momente": ["gruebelt", "ruht", "angespannt"],
"farbe": Color(1.0, 0.80, 0.60), "farbe": Color(1.0, 0.80, 0.60),
@@ -108,7 +135,10 @@ static var LIST: Array = [
"was": "Feste Aufsteh- und Schlafenszeit, die letzte Stunde ohne Bildschirm, Zimmer dunkel und kühl.", "was": "Feste Aufsteh- und Schlafenszeit, die letzte Stunde ohne Bildschirm, Zimmer dunkel und kühl.",
"wann": "Bei Dauermüdigkeit, Reizbarkeit und wegbrechender Konzentration.", "wann": "Bei Dauermüdigkeit, Reizbarkeit und wegbrechender Konzentration.",
"warum": "Der innere Rhythmus braucht feste Anker. Erholter Schlaf ist die Grundlage für jede andere Veränderung.", "warum": "Der innere Rhythmus braucht feste Anker. Erholter Schlaf ist die Grundlage für jede andere Veränderung.",
"woran": "der Abendroutine, an jemandem, der müde ist oder sich mitten am Tag ausruht.", "woran": "jemandem, der wach bleibt oder abends am Kühlschrank steht.",
"ursache": "kraft",
"belohnung": "zeit_fuer_sich",
"ersatz": "hält das Schlaffenster",
"tags": ["erschoepfung", "gesundheit"], "tags": ["erschoepfung", "gesundheit"],
"momente": ["abendroutine", "muede", "ruht"], "momente": ["abendroutine", "muede", "ruht"],
"farbe": Color(0.45, 0.50, 0.90), "farbe": Color(0.45, 0.50, 0.90),
@@ -119,7 +149,10 @@ static var LIST: Array = [
"was": "Eine klare, kleine Bitte an eine konkrete Person richten, statt allgemein zu hoffen.", "was": "Eine klare, kleine Bitte an eine konkrete Person richten, statt allgemein zu hoffen.",
"wann": "Wenn jemand allein an etwas hängen bleibt, das andere leicht lösen könnten.", "wann": "Wenn jemand allein an etwas hängen bleibt, das andere leicht lösen könnten.",
"warum": "Vage Bitten werden überhört. Menschen helfen gern, wenn sie genau wissen, was zu tun ist.", "warum": "Vage Bitten werden überhört. Menschen helfen gern, wenn sie genau wissen, was zu tun ist.",
"woran": "jemandem, der wartet, Pause macht oder unterwegs ist — da ist Luft für eine Bitte.", "woran": "jemandem, der zu allem ja sagt und alles allein stemmt.",
"ursache": "zeit",
"belohnung": "anschluss",
"ersatz": "bittet um Hilfe",
"tags": ["einsamkeit", "ueberforderung"], "tags": ["einsamkeit", "ueberforderung"],
"momente": ["wartet", "pause", "unterwegs"], "momente": ["wartet", "pause", "unterwegs"],
"farbe": Color(0.90, 0.90, 0.45), "farbe": Color(0.90, 0.90, 0.45),
@@ -130,7 +163,10 @@ static var LIST: Array = [
"was": "Absagen und zugleich benennen, was ginge: \"Heute nicht, am Donnerstag gern.\"", "was": "Absagen und zugleich benennen, was ginge: \"Heute nicht, am Donnerstag gern.\"",
"wann": "Wenn der eigene Kalender von fremden Anliegen aufgebraucht wird.", "wann": "Wenn der eigene Kalender von fremden Anliegen aufgebraucht wird.",
"warum": "Ein nacktes Nein fühlt sich riskant an. Mit Alternative bleibt die Beziehung heil und die Zeit geschützt.", "warum": "Ein nacktes Nein fühlt sich riskant an. Mit Alternative bleibt die Beziehung heil und die Zeit geschützt.",
"woran": "jemandem, der arbeitet, angespannt ist oder unterwegs zwischen Terminen hängt.", "woran": "jemandem, der zu allem ja sagt, bis für ihn nichts übrig bleibt.",
"ursache": "zeit",
"belohnung": "anerkennung",
"ersatz": "sagt Nein mit Alternative",
"tags": ["stress", "grenzen"], "tags": ["stress", "grenzen"],
"momente": ["arbeitet", "angespannt", "unterwegs"], "momente": ["arbeitet", "angespannt", "unterwegs"],
"farbe": Color(0.85, 0.35, 0.45), "farbe": Color(0.85, 0.35, 0.45),
@@ -138,6 +174,21 @@ static var LIST: Array = [
] ]
## Techniken, die gegen diese Ursache helfen.
static func fuer_ursache(ursache: String) -> Array:
var out: Array = []
for t in LIST:
if t["ursache"] == ursache:
out.append(t["id"])
return out
## Manche Techniken ersetzen die Handlung nicht, sie nehmen ihr den Auslöser.
## Die passen überall, wo ihre Ursache stimmt.
static func entfernt_ausloeser(id: String) -> bool:
return str(by_id(id).get("belohnung", "")) == ""
static func by_id(id: String) -> Dictionary: static func by_id(id: String) -> Dictionary:
for t in LIST: for t in LIST:
if t["id"] == id: if t["id"] == id:

View File

@@ -154,9 +154,9 @@ func _hud_aktualisieren() -> void:
[Game.known.size(), Game.offene_techniken(), TechDB.LIST.size()]) [Game.known.size(), Game.offene_techniken(), TechDB.LIST.size()])
if Game.active_mission != "": if Game.active_mission != "":
var h := HumanDB.by_id(Game.active_mission) var h := HumanDB.by_id(Game.active_mission)
zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d Regeln" % zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d %% je Tag" %
[h["name"], int(Game.mission_progress(Game.active_mission)), [h["name"], int(Game.mission_progress(Game.active_mission)),
Game.regel_anzahl(Game.active_mission)]) int(round(Game.traum_tempo(Game.active_mission)))])
zeilen.append("[color=#aaaaaa]%s[/color]" % h["traum"]) zeilen.append("[color=#aaaaaa]%s[/color]" % h["traum"])
elif Game.completed.size() >= HumanDB.LIST.size(): elif Game.completed.size() >= HumanDB.LIST.size():
zeilen.append("[color=#88ff99]Alle Träume erfüllt.[/color]") zeilen.append("[color=#88ff99]Alle Träume erfüllt.[/color]")
@@ -347,84 +347,126 @@ func _journal_missionen() -> void:
_text("[color=#aaaaaa]Zurückgestellt — %d %%[/color]" % _text("[color=#aaaaaa]Zurückgestellt — %d %%[/color]" %
int(Game.mission_progress(id)), 15) int(Game.mission_progress(id)), 15)
if not Game.completed.has(id): if not Game.completed.has(id):
_knopf("Regelwerk — %d Regeln" % Game.regel_anzahl(id), _knopf("Tagesablauf — %d Systeme" % Game.system_anzahl(id),
func() -> void: pipeline_panel(id)) func() -> void: systeme_panel(id))
_trenner() _trenner()
# -------------------------------------------------------- Regelwerk einer Mission # ---------------------------------------------- Diagnose und Systeme
## Wenn-Dann-Regeln: tut der Mensch etwas Bestimmtes, greift die zugeordnete ## Der Traum scheitert an Ursachen. Die stehen nicht im Panel -- man findet sie,
## Technik von allein. So automatisiert der Spieler den Fortschritt. ## indem man dabeisteht. Gegen jede gefundene Ursache baut man ein System in
func pipeline_panel(human_id: String) -> void: ## den Tag. Je mehr abgedeckt ist, desto schneller kommt der Traum voran.
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "pipeline" else 0 func systeme_panel(human_id: String) -> void:
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "systeme" else 0
var h := HumanDB.by_id(human_id) var h := HumanDB.by_id(human_id)
_leeren("Regelwerk — %s" % h["name"]) _leeren("%s — Tagesablauf" % h["name"])
_text("Jede Tätigkeit im Tag von %s kann eine Technik auslösen. " % h["name"] + _text("[b]Traum:[/b] %s" % h["traum"], 16)
"Wechselt sie hinein, greift die Regel von allein — solange du auf der Erde bist.\n" + _text("[b]Fortschritt:[/b] %d %% · [b]Tempo:[/b] %d %% je Tag" %
"Nach jedem Einsatz braucht eine Technik Ruhe, bevor sie wieder anschlägt.", 15) [int(Game.mission_progress(human_id)), int(round(Game.traum_tempo(human_id)))], 16)
_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() _trenner()
for activity in HumanDB.aktivitaeten(human_id):
var tat := Orte.text(activity) _text("[b]Woran es scheitert[/b]", 17)
if not Game.erreichbar(activity): var offene := 0
_knopf("%s → unerreichbar" % tat, func() -> void: pass, false) for u in Game.ursachen_von(human_id):
var name: String = str(u)
if not Game.ursache_bekannt(human_id, name):
_text("[color=#8e93a8]? — noch nicht erkannt[/color]", 15)
offene += 1
continue continue
var tech: String = Game.regel_fuer(human_id, activity) var g := Game.ursache_geloest(human_id, name)
var beschriftung := "%s → keine Regel" % tat var stand := "[color=#88ff99]gelöst[/color]" if g > 0.0 else \
if tech != "": "[color=#ff9977]offen · bremst auf %d %%[/color]" % \
beschriftung = "%s → %s" % [tat, TechDB.by_id(tech)["name"]] int(float(Ursachen.LISTE[name]["faktor"]) * 100.0)
if not Game.bereit(tech): _text("[b]%s[/b] — %s %s" % [Ursachen.LISTE[name]["name"],
beschriftung += " (noch gesperrt)" Ursachen.LISTE[name]["kurz"], stand], 15)
_knopf(beschriftung, func() -> void: regel_panel(human_id, activity)) if offene > 0:
_text("[color=#aaaaaa]Steh dabei, wenn sie etwas tut, das du noch nicht " +
"kennst. Dann zeigt sich, warum.[/color]", 14)
_trenner()
_text("[b]Ihr Tag[/b]", 17)
for nr in range(Game.slots(human_id).size()):
_slot_zeile(human_id, nr)
_trenner() _trenner()
_knopf("Zurück zum Journal", func() -> void: journal_panel("missionen")) _knopf("Zurück zum Journal", func() -> void: journal_panel("missionen"))
_zeigen("pipeline") _zeigen("systeme")
_scroll_wunsch = scroll_alt _scroll_wunsch = scroll_alt
_scroll_frames = 4 _scroll_frames = 4
## Welche Technik soll bei dieser Tätigkeit greifen? func _slot_zeile(human_id: String, nr: int) -> void:
func regel_panel(human_id: String, activity: String) -> void: var slot: Array = Game.slots(human_id)[nr]
var h := HumanDB.by_id(human_id) var stunde := "%02d:00" % int(slot[0])
_leeren("Regel für »%s«" % Orte.text(activity)) var grund := Game.slot_grund(human_id, nr)
if Game.known.is_empty(): if grund == "schlaeft":
_text("[color=#ffcc66]Du kennst keine Technik. Im Hörsaal der Wege " + _knopf("%s schläft" % stunde, func() -> void: pass, false)
"werden sie gelehrt.[/color]", 16)
_knopf("Zurück", func() -> void: pipeline_panel(human_id))
_zeigen("regel")
return return
_text("Was soll greifen, wenn %s %s? Ob eine Technik hier taugt, steht in ihr " % var sys := Game.system_in(human_id, nr)
[h["name"], Orte.text(activity)] + if not sys.is_empty():
"selbst — nachzulesen im Journal. Ein Fehlgriff wirft den Traum zurück.", 15) _knopf("%s %s ← %s" % [stunde, Game.slot_aktivitaet(human_id, nr),
TechDB.by_id(str(sys["tech"]))["name"]],
func() -> void: slot_panel(human_id, nr))
return
if not Game.erkannt(human_id, nr):
_knopf("%s ?" % stunde, func() -> void: pass, false)
return
var text := Orte.text(grund)
if Ursachen.ist_blocker(grund):
text = "%s (%s)" % [Ursachen.BLOCKER[grund]["name"],
Ursachen.LISTE[Ursachen.BLOCKER[grund]["ursache"]]["name"]]
_knopf("%s %s" % [stunde, text], func() -> void: slot_panel(human_id, nr))
## Was soll in diesem Slot stehen?
func slot_panel(human_id: String, nr: int) -> void:
var h := HumanDB.by_id(human_id)
var grund := Game.slot_grund(human_id, nr)
var stunde := "%02d:00" % int(Game.slots(human_id)[nr][0])
_leeren("%s um %s" % [h["name"], stunde])
if Ursachen.ist_blocker(grund):
var b: Dictionary = Ursachen.BLOCKER[grund]
_text("Sie %s.\n[b]Auslöser:[/b] %s\n[b]Belohnung:[/b] %s" %
[b["name"], b["ausloeser"], Ursachen.belohnung_text(str(b["belohnung"]))], 16)
_text("[color=#aaaaaa]Eine Ersatzhandlung muss dieselbe Belohnung liefern, " +
"sonst hält sie nicht.[/color]", 15)
else:
_text("Hier ist Platz. Ein neues System passt in diesen Slot.", 16)
_trenner()
var sys := Game.system_in(human_id, nr)
if not sys.is_empty():
_text("[color=#88ff99]Hier steht: %s[/color]" %
TechDB.by_id(str(sys["tech"]))["name"], 16)
_knopf("System wieder herausnehmen", func() -> void:
Game.system_entfernen(human_id, nr)
systeme_panel(human_id))
_trenner() _trenner()
for id in TechDB.ids(): for id in TechDB.ids():
if not Game.knows(id): if not Game.knows(id):
continue continue
var pruef := Game.system_pruefen(human_id, nr, id)
var t := TechDB.by_id(id) var t := TechDB.by_id(id)
var ev := Game.evaluate(id, human_id, activity, false)
var wo: Array = Game.regel_momente(human_id, id)
var gewaehlt := ""
if wo.has(activity):
gewaehlt = " — hier eingestellt"
elif not wo.is_empty():
var namen: Array = []
for a in wo:
namen.append("»%s«" % Orte.text(a))
gewaehlt = " — hängt an %s" % ", ".join(namen)
var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(id)] var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(id)]
if ev["blocked"]: if not pruef["geht"]:
beschriftung += " — " + str(ev["grund"]) beschriftung += " — " + str(pruef["grund"])
elif gewaehlt != "": elif float(pruef["guete"]) > Game.GUETE_NEU:
beschriftung += gewaehlt beschriftung += " — passt genau"
_knopf(beschriftung, func() -> void: _knopf(beschriftung, func() -> void: _system_setzen(human_id, nr, id),
Game.regel_setzen(human_id, activity, id) bool(pruef["geht"]))
pipeline_panel(human_id), not ev["blocked"]) _trenner()
_knopf("Keine Regel für diese Tätigkeit", func() -> void: _knopf("Zurück", func() -> void: systeme_panel(human_id))
Game.regel_setzen(human_id, activity, "") _zeigen("slot")
pipeline_panel(human_id))
_zeigen("regel")
func _system_setzen(human_id: String, nr: int, tech_id: String) -> void:
var res := Game.system_setzen(human_id, nr, tech_id, false)
if not res["ok"]:
Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4))
return
if not res["erfolg"]:
Game.notified.emit("%s greift diesmal nicht. Später noch einmal." %
TechDB.by_id(tech_id)["name"], Color(1, 0.6, 0.5))
systeme_panel(human_id)
# -------------------------------------------------------- Lehrorte # -------------------------------------------------------- Lehrorte
@@ -522,70 +564,66 @@ func brett_panel() -> void:
_scroll_frames = 4 _scroll_frames = 4
# -------------------------------------------------------- Anwenden auf der Erde # -------------------------------------------------------- Vor Ort
## Draußen am Menschen: hier greift man in den Slot ein, der gerade läuft.
func anwenden_panel(mensch: Node3D) -> void: func anwenden_panel(mensch: Node3D) -> void:
_apply_human = mensch _apply_human = mensch
var h: Dictionary = mensch.data var h: Dictionary = mensch.data
var aktiv: String = mensch.activity var id: String = str(h["id"])
_leeren("%s — %s" % [h["name"], Orte.text(aktiv)]) var nr: int = int(mensch.slot)
_leeren("%s — %s" % [h["name"], Orte.text(mensch.activity)])
_text("[b]Traum:[/b] %s" % h["traum"], 16) _text("[b]Traum:[/b] %s" % h["traum"], 16)
if Game.active_mission == h["id"]: if Game.active_mission != id:
_text("Fortschritt: [b]%d %%[/b]" % int(Game.mission_progress(h["id"])), 16) _text("[color=#ffcc66]Keine aktive Mission für diesen Menschen.[/color]", 15)
elif Game.completed.has(h["id"]): _zeigen("anwenden")
_text("[color=#88ff99]★ Traum erfüllt.[/color]", 16) return
else: var grund := Game.slot_grund(id, nr)
_text("[color=#ffcc66]Keine aktive Mission für diesen Menschen. " + if not Game.erkannt(id, nr):
"Fortschritt zählt nur mit angenommener Mission.[/color]", 15) _text("[color=#ffcc66]Was hier läuft, hast du noch nicht gesehen. " +
"Bleib stehen und schau zu.[/color]", 16)
_zeigen("anwenden")
return
if Ursachen.ist_blocker(grund):
var b: Dictionary = Ursachen.BLOCKER[grund]
_text("Sie %s. [b]Auslöser:[/b] %s · [b]Belohnung:[/b] %s" %
[b["name"], b["ausloeser"], Ursachen.belohnung_text(str(b["belohnung"]))], 15)
_trenner() _trenner()
if Game.known.is_empty(): if Game.known.is_empty():
_text("[color=#ffcc66]Du kennst keine Technik.[/color]") _text("[color=#ffcc66]Du kennst keine Technik.[/color]")
_zeigen("anwenden") _zeigen("anwenden")
return return
_text("[color=#aaaaaa]Was eine Technik tut, steht im Journal.[/color]", 15) _text("[color=#aaaaaa]Was eine Technik tut, steht im Journal.[/color]", 15)
for tid in TechDB.ids():
for id in TechDB.ids(): if not Game.knows(tid):
if not Game.knows(id):
continue continue
var t := TechDB.by_id(id) var pruef := Game.system_pruefen(id, nr, tid)
var ev := Game.evaluate(id, h["id"], aktiv, mensch.resist_left > 0.0) var t := TechDB.by_id(tid)
var gesperrt := not Game.bereit(id) var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(tid)]
var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(id)] if not pruef["geht"]:
if gesperrt: beschriftung += " — " + str(pruef["grund"])
beschriftung += " — noch %d s gesperrt" % int(ceil(Game.cooldown_rest(id)))
elif ev["blocked"]:
beschriftung += " — " + str(ev["grund"])
elif mensch.resist_left > 0.0: elif mensch.resist_left > 0.0:
beschriftung += " — macht gerade dicht" beschriftung += " — macht gerade dicht"
_knopf(beschriftung, func() -> void: _anwenden(id), not ev["blocked"] and not gesperrt) _knopf(beschriftung, func() -> void: _vor_ort_setzen(tid), bool(pruef["geht"]))
_zeigen("anwenden") _zeigen("anwenden")
func _anwenden(id: String) -> void: func _vor_ort_setzen(tech_id: String) -> void:
if _apply_human == null or not is_instance_valid(_apply_human): if _apply_human == null or not is_instance_valid(_apply_human):
schliessen() schliessen()
return return
var mensch := _apply_human var mensch := _apply_human
var h: Dictionary = mensch.data var id: String = str(mensch.data["id"])
var res := Game.apply_technique(id, h["id"], mensch.activity, mensch.resist_left > 0.0) var res := Game.system_setzen(id, int(mensch.slot), tech_id, mensch.resist_left > 0.0)
schliessen() schliessen()
if not res["ok"]: if not res["ok"]:
Game.notified.emit(res["grund"], Color(1, 0.6, 0.4)) Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4))
return return
var t := TechDB.by_id(id) var t := TechDB.by_id(tech_id)
if res["success"]: if res["erfolg"]:
mensch.reagiere(true, "%s wirkt\n+%d %%" % [t["name"], int(round(float(res["gain"])))]) mensch.reagiere(true, "%s\nsteht" % t["name"])
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: else:
mensch.reagiere(false, "%s prallt ab\n−%d %%" % [t["name"], mensch.reagiere(false, "%s greift nicht" % t["name"])
int(round(float(res["verlust"])))])
Game.notified.emit("%s dringt nicht durch. (−%d %%)" %
[t["name"], int(round(float(res["verlust"])))], Color(1, 0.6, 0.5))
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:

136
scripts/ursachen.gd Normal file
View File

@@ -0,0 +1,136 @@
class_name Ursachen
## Woran ein Traum scheitert, und was im Tag davon zu sehen ist.
##
## Ein Traum hat eine Grundrate. Jede ungelöste Ursache bremst sie. Zwei
## ungelöste Ursachen bremsen doppelt -- deshalb lohnt es, alle zu finden.
## Sichtbar wird eine Ursache nur an den Tätigkeiten, die sie hervorbringt:
## wer nie in Gang kommt, räumt auf, recherchiert und schreibt Listen.
##
## Grundlage: Habit Reversal Training beginnt mit awareness training -- erst
## bemerken, dann ersetzen. Und Duhiggs goldene Regel: Auslöser und Belohnung
## bleiben, nur die Handlung dazwischen wird getauscht.
static var LISTE: Dictionary = {
"anfang": {
"name": "Kein Anfang", "kurz": "kommt nie in Gang",
"symptom": "Sie setzt sich hin und tut dann etwas anderes.",
"faktor": 0.30,
},
"zeit": {
"name": "Keine Zeit", "kurz": "der Tag ist voll",
"symptom": "Der Tag ist voll mit dem, was andere wollen.",
"faktor": 0.30,
},
"kraft": {
"name": "Keine Kraft", "kurz": "abends ist nichts mehr übrig",
"symptom": "Am Abend reicht es nur noch für Berieselung.",
"faktor": 0.40,
},
"halt": {
"name": "Kein Halt", "kurz": "fängt an und hört wieder auf",
"symptom": "Angefangen wird oft, drangeblieben selten.",
"faktor": 0.50,
},
"grund": {
"name": "Kein Grund", "kurz": "glaubt nicht mehr daran",
"symptom": "Sie redet sich das Vorhaben selbst klein.",
"faktor": 0.40,
},
}
## Tätigkeiten, die einen Traum aufhalten. Jede hat einen Auslöser und eine
## Belohnung -- genau die muss eine Ersatzhandlung wieder liefern.
## zeit: wann im Tag die Tätigkeit auftaucht.
static var BLOCKER: Dictionary = {
"putzflucht": {
"name": "räumt auf statt anzufangen", "ausloeser": "eine große Aufgabe",
"belohnung": "erfolg", "ursache": "anfang", "zeit": "tag",
},
"recherche": {
"name": "recherchiert statt anzufangen", "ausloeser": "Unsicherheit",
"belohnung": "erfolg", "ursache": "anfang", "zeit": "tag",
},
"liste": {
"name": "schreibt eine Später-Liste", "ausloeser": "Überforderung",
"belohnung": "ordnung", "ursache": "anfang", "zeit": "morgen",
},
"jasagen": {
"name": "sagt zu allem ja", "ausloeser": "eine fremde Bitte",
"belohnung": "anerkennung", "ursache": "zeit", "zeit": "tag",
},
"scrollen": {
"name": "scrollt endlos", "ausloeser": "Leerlauf",
"belohnung": "ablenkung", "ursache": "zeit", "zeit": "tag",
},
"serie": {
"name": "schaut eine Serie", "ausloeser": "Erschöpfung",
"belohnung": "abschalten", "ursache": "kraft", "zeit": "abend",
},
"wach": {
"name": "bleibt wach", "ausloeser": "ein fremdbestimmter Tag",
"belohnung": "zeit_fuer_sich", "ursache": "kraft", "zeit": "nacht",
},
"kuehlschrank": {
"name": "steht am Kühlschrank", "ausloeser": "Langeweile",
"belohnung": "unterbrechung", "ursache": "kraft", "zeit": "abend",
},
"spielen": {
"name": "spielt noch eine Runde", "ausloeser": "Anspannung",
"belohnung": "erfolg", "ursache": "halt", "zeit": "abend",
},
"umschalten": {
"name": "schaltet auf etwas anderes um", "ausloeser": "Widerstand",
"belohnung": "neuheit", "ursache": "halt", "zeit": "tag",
},
"klagen": {
"name": "klagt am Telefon", "ausloeser": "Frust",
"belohnung": "verbundenheit", "ursache": "grund", "zeit": "abend",
},
"vergleichen": {
"name": "vergleicht sich mit anderen", "ausloeser": "ein fremder Erfolg",
"belohnung": "einordnung", "ursache": "grund", "zeit": "morgen",
},
}
static var BELOHNUNG_TEXT: Dictionary = {
"erfolg": "ein Erfolgserlebnis",
"ordnung": "Ordnung",
"anerkennung": "Anerkennung",
"anschluss": "Anschluss",
"abschalten": "Abschalten",
"zeit_fuer_sich": "Zeit für sich",
"unterbrechung": "eine Unterbrechung",
"neuheit": "etwas Neues",
"verbundenheit": "Verbundenheit",
"einordnung": "Einordnung",
"ablenkung": "Ablenkung",
}
static func ist_blocker(activity: String) -> bool:
return BLOCKER.has(activity)
static func ursache_von(activity: String) -> String:
return str(BLOCKER[activity]["ursache"]) if BLOCKER.has(activity) else ""
static func belohnung_text(schluessel: String) -> String:
return str(BELOHNUNG_TEXT.get(schluessel, schluessel))
## Blocker einer Ursache, die in dieses Zeitfenster passen.
static func blocker_fuer(ursache: String, zeit: String) -> Array:
var out: Array = []
for id in BLOCKER:
if BLOCKER[id]["ursache"] == ursache and BLOCKER[id]["zeit"] == zeit:
out.append(id)
return out
static func alle_blocker(ursache: String) -> Array:
var out: Array = []
for id in BLOCKER:
if BLOCKER[id]["ursache"] == ursache:
out.append(id)
return out

1
scripts/ursachen.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://ddavhllmw3nlk

View File

@@ -87,15 +87,24 @@ func _ready() -> void:
await warte(6) await warte(6)
await knipse("11_journal_missionen") await knipse("11_journal_missionen")
# Regelwerk: Wenn-Dann-Ketten, die den Fortschritt automatisieren # Diagnose: erst aufdecken, dann Systeme bauen
Game.regel_setzen("mensch_0", "zoegert", str(DreamDB.techniken_von("hoersaal")[0])) Game.erkannt_slots["mensch_0"] = {}
main.ui.pipeline_panel("mensch_0") main.ui.systeme_panel("mensch_0")
await warte(6) await warte(6)
await knipse("12_regelwerk") await knipse("12_tagesablauf_verdeckt")
main.ui.regel_panel("mensch_0", "arbeitet") var blocker := -1
for nr in range(Game.slots("mensch_0").size()):
Game.beobachten("mensch_0", nr, Game.ERKENNEN_SEK * 2.0)
if Ursachen.ist_blocker(Game.slot_grund("mensch_0", nr)) and blocker < 0:
blocker = nr
main.ui.systeme_panel("mensch_0")
await warte(6) await warte(6)
await knipse("13_regel_waehlen") await knipse("12b_tagesablauf_erkannt")
main.ui.slot_panel("mensch_0", blocker)
await warte(6)
await knipse("13_system_waehlen")
main.ui.schliessen() main.ui.schliessen()
# Lichtung, wenn sich die Wesen dort treffen # Lichtung, wenn sich die Wesen dort treffen
@@ -145,26 +154,30 @@ func _ready() -> void:
await warte(20) await warte(20)
await knipse("17_lena_zoegert") await knipse("17_lena_zoegert")
# Das Regelwerk greift ohne Zutun, sobald Lena die Tätigkeit wechselt # Der Traum läuft von allein weiter, solange Systeme stehen
var vor := Game.mission_progress("mensch_0") for i in range(4000):
for i in range(20000):
Game.time_h += 0.004 Game.time_h += 0.004
main._process(0.02) main._process(0.02)
for k in main.welt.get_children(): for k in main.welt.get_children():
if k is Bewohner: if k is Bewohner:
k._process(0.05) k._process(0.05)
if Game.mission_progress("mensch_0") > vor:
break
main.player.global_position = lena.global_position + Vector3(0, 1.6, -5.5) main.player.global_position = lena.global_position + Vector3(0, 1.6, -5.5)
main.player.yaw = PI main.player.yaw = PI
main.player.pitch = -0.10 main.player.pitch = -0.10
await warte(4) await warte(4)
await knipse("18_regel_greift") await knipse("18_traum_laeuft")
main.ui.anwenden_panel(lena) main.ui.anwenden_panel(lena)
await warte(6) await warte(6)
await knipse("19_anwenden") await knipse("19_vor_ort")
main.ui._anwenden(str(DreamDB.techniken_von("hoersaal")[0])) main.ui.schliessen()
Game.cooldown.clear()
var wege: Array = []
for tid in Game.known:
wege.append(str(tid))
if not wege.is_empty():
main.ui._apply_human = lena
main.ui._vor_ort_setzen(str(wege[0]))
await warte(25) await warte(25)
await knipse("20_reaktion") await knipse("20_reaktion")

File diff suppressed because it is too large Load Diff