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
::res://::1788113016
anforderungen.md::TextFile::-1::1788165575::0::1::::<><><>0<>0<><>::
README.md::TextFile::-1::1788165599::0::1::::<><><>0<>0<><>::
anforderungen.md::TextFile::-1::1788204851::0::1::::<><><>0<>0<><>::
README.md::TextFile::-1::1788165734::0::1::::<><><>0<>0<><>::
::res://scenes/::1787989294
main.tscn::PackedScene::-1::1787989294::0::1::::<><><>0<>0<><>::res://scripts/main.gd
::res://scripts/::1788163668
bewohner.gd::GDScript::2125278938812678811::1788165374::0::1::::Bewohner<>Node3D<><>0<>0<><>::
::res://scripts/::1788209300
bewohner.gd::GDScript::2125278938812678811::1788209097::0::1::::Bewohner<>Node3D<><>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<><>::
earth_world.gd::GDScript::6086818627272428934::1788165412::0::1::::EarthWorld<>Node3D<><>0<>0<><>::
game.gd::GDScript::3600922541500868814::1788165125::0::1::::<>Node<><>0<>0<><>::
human_db.gd::GDScript::412604892032291296::1788165374::0::1::::HumanDB<>RefCounted<><>0<>0<><>::
main.gd::GDScript::1504009331368977934::1788121650::0::1::::<>Node3D<><>0<>0<><>::
game.gd::GDScript::3600922541500868814::1788209816::0::1::::<>Node<><>0<>0<><>::
human_db.gd::GDScript::412604892032291296::1788208999::0::1::::HumanDB<>RefCounted<><>0<>0<><>::
main.gd::GDScript::1504009331368977934::1788209245::0::1::::<>Node3D<><>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<><>::
prims.gd::GDScript::322632462553361584::1788039797::0::1::::Prims<>RefCounted<><>0<>0<><>::
technique_db.gd::GDScript::4543286561661639701::1788120975::0::1::::TechDB<>RefCounted<><>0<>0<><>::
ui.gd::GDScript::843471304695966286::1788122559::0::1::::UI<>CanvasLayer<><>0<>0<><>::
::res://tests/::1788164088
probe.gd::GDScript::5788011518062194748::1788165350::0::1::::<>Node<><>0<>0<><>::
probe.tscn::PackedScene::-1::1788163874::0::1::::<><><>0<>0<><>::res://tests/probe.gd
shots.gd::GDScript::5999651346126871724::1788118441::0::1::::<>Node<><>0<>0<><>::
technique_db.gd::GDScript::4543286561661639701::1788209343::0::1::::TechDB<>RefCounted<><>0<>0<><>::
ui.gd::GDScript::843471304695966286::1788209227::0::1::::UI<>CanvasLayer<><>0<>0<><>::
ursachen.gd::GDScript::7371116734724270900::1788208917::0::1::::Ursachen<>RefCounted<><>0<>0<><>::
::res://tests/::1788209571
shots.gd::GDScript::5999651346126871724::1788209816::0::1::::<>Node<><>0<>0<><>::
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

View File

@@ -86,4 +86,12 @@ list=[{
"is_tool": false,
"language": &"GDScript",
"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
sie nur draußen.
* **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
zögert, dann Pomodoro*. Wechselt sie in die Tätigkeit, greift die Regel von
allein, solange du auf der Erde bist.
Ein Traum scheitert an Ursachen: *kein Anfang*, *keine Zeit*, *keine Kraft*,
*kein Halt*, *kein Grund*. Jeder Mensch hat zwei bis drei davon, nie alle. Die
stehen nirgends — du findest sie nur, indem du dabeistehst.
**Ob eine Regel taugt, sagt dir niemand.** Keine Prozentzahl, kein „passt zum
Thema", keine Liste guter Momente. Du bekommst nur die Technik selbst: was sie
tut, wann sie passt, warum sie hilft. Und den Menschen: seinen Traum und das,
was im Weg steht. Beides zusammenzubringen ist das Spiel.
Sichtbar wird eine Ursache an den Tätigkeiten, die sie hervorbringt. Wer nie in
Gang kommt, räumt auf statt anzufangen, recherchiert oder schreibt Später-Listen.
Wer keine Kraft hat, schaut abends eine Serie und bleibt danach wach. Jede
dieser Tätigkeiten hat einen **Auslöser** und eine **Belohnung**.
Eine Technik darf an beliebig vielen Tätigkeiten hängen. Sie überall
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
Ruhe — ein Fehlgriff verbrennt also den Platz, den ein guter Moment gehabt
hätte. Mehr Momente sauber abzudecken heißt, mehr Techniken zu kennen, und die
gibt es nur auf der gewachsenen Insel.
Gegen eine erkannte Ursache baust du ein System in den Tag:
Einführen Ein System, das fehlt — in einen freien Slot.
Ändern Eine Tätigkeit, die schadet — mit gleicher Belohnung ersetzt.
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:
Techniken lernen → Regelwerk anpassen → Probleme lösen
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.
Techniken lernen → Systeme bauen → Traum läuft schneller
Träume erfüllen → Dorf wächst → neue Wege entdecken
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.
Schlafende sind unerreichbar, dieselbe Technik wirkt bei Wiederholung
schwächer, ein Fehlversuch macht kurz dicht. Von Hand geht es weiter: nah
herangehen und **E** wirkt sofort.
Nach jedem Einsatz braucht eine Technik 30 Sekunden Ruhe. Ein Einbau gelingt
nicht immer — Übung entscheidet.
**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`
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/human_db.gd Bezirk: Arterien, Blöcke, roji, Parzellen, Menschen
scripts/dream_db.gd Traumdorf: wachsende Häuser, Wege, Bewohner, Lehrorte
scripts/netz.gd Wegenetz: Knoten, Kanten, Andocken, kürzester Weg
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/player.gd Läufer in der Traumwelt, Geist auf der Erde
scripts/dream_world.gd Insel mit Dorf
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
## Prüfen
@@ -156,9 +166,9 @@ Menschen einsetzen — auch Fehlversuche zählen.
Tagesabläufen, gewachsenes Dorf und erzeugter Bezirk,
Häuser betreten, Journal in beiden Welten, im Hörsaal lernen,
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,
verdeckte Erfolgschance, keine Passungshinweise, Mehrfachbelegung von Regeln,
verdeckte Erfolgschance, keine Passungshinweise,
Tempo und Nachtsprung,
nur lösbare Aushänge, Mission abschließen,
Insel wachsen lassen, Tastensteuerung. Beendet sich mit Code 1, wenn etwas

View File

@@ -9,15 +9,8 @@ Loops
Funktionen
- Bewegung: Bewegen, Springen, Geist
- Journal: Gelernte Techniken, Akzetierte Missionen
- Techniken: Lernen am Lehrort, Verbessern durch Anwenden
- Missionen: Pipeline, nur lösbare werden angeboten
- Techniken: Cooldown, Fehlschlag kostet Fortschritt
- Erfolgschance und Passung bleiben verdeckt
- Eine Technik darf an mehreren Tätigkeiten hängen
- 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
- Techniken: Lernen, Verbessern
- Missionen: Pipeline
- Techniken: Cooldown, Fehlschlag
- Menschen: Tagesablauf, Tempo
- Prozedurale Generierung: Dorf, Stadt

View File

@@ -23,7 +23,7 @@ var _aura: MeshInstance3D
var _strahl: MeshInstance3D
var _path: Array = []
var _letzte_aktivitaet: String = ""
var _seit_regel: float = 0.0
var slot: int = 0
var _bob: float = 0.0
var _offset: Vector3
@@ -70,13 +70,18 @@ func setup(d: Dictionary, index: int, orte: Dictionary,
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:
var r: Array = data["routine"]
var cur: Array = r[r.size() - 1]
for eintrag in r:
if h >= float(eintrag[0]):
cur = eintrag
return [cur[1], cur[2]]
var nr := r.size() - 1
for i in range(r.size()):
if h >= float(r[i][0]):
nr = i
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:
@@ -84,6 +89,7 @@ func _process(roh: float) -> void:
resist_left = maxf(0.0, resist_left - delta)
var soll := _routine_at(Game.time_h)
slot = int(soll[2])
if soll[0] != place:
var war_unterwegs := not _path.is_empty()
_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,
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:
_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 aktiv: bool = mit_mission and Game.active_mission == data["id"]
@@ -158,20 +156,6 @@ func beschriftung() -> String:
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.
func reagiere(success: bool, text: String) -> void:
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 active_mission: String = "" # human_id oder ""
var progress: Dictionary = {} # human_id -> 0..100
var used: Dictionary = {} # "human:tech" -> Anzahl
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 time_h: float = 7.0
var day: int = 1
@@ -113,15 +114,6 @@ func advance_time(delta: float) -> void:
while time_h >= 24.0:
time_h -= 24.0
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:
@@ -261,25 +253,12 @@ func angenommen(human_id: String) -> bool:
return progress.has(human_id)
## Eine Mission ist machbar, wenn mindestens eine gelernte Technik das Thema
## des Menschen trifft und in seinem Tag auch einen Moment dafür findet.
## Nur solche werden am Schwarzen Brett überhaupt ausgehängt.
## Eine Mission ist machbar, wenn mindestens eine ihrer Ursachen mit einer
## gelernten Technik anzugehen ist. Die übrigen bremsen dann eben noch.
func machbar(human_id: String) -> bool:
var h := HumanDB.by_id(human_id)
if h.is_empty():
return false
var tag_liste: Array = HumanDB.aktivitaeten(human_id)
for id in known:
var t := TechDB.by_id(id)
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):
for u in ursachen_von(human_id):
for id in known:
if str(TechDB.by_id(id)["ursache"]) == str(u):
return true
return false
@@ -317,161 +296,188 @@ func _insel_waechst() -> void:
Color(0.75, 0.95, 1))
# ------------------------------------------------------------------- Regelwerk
# Eine Regel ist ein Wenn-Dann: tut der Mensch etwas Bestimmtes, greift die
# zugeordnete Technik von allein. Damit automatisiert der Spieler den Fortschritt.
# --------------------------------------------------------------------- Systeme
# Ein Traum scheitert an Ursachen. Die sind verdeckt und zeigen sich nur im
# 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:
return pipeline.get(human_id, {})
const BASIS_TEMPO := 45.0 # Prozent je Tag, wenn nichts mehr bremst
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:
return regeln(human_id).get(activity, "")
func slots(human_id: String) -> Array:
var h := HumanDB.by_id(human_id)
return h["routine"] if h.has("routine") else []
func regel_anzahl(human_id: String) -> int:
return regeln(human_id).size()
func slot_grund(human_id: String, nr: int) -> String:
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?
func regel_momente(human_id: String, tech_id: String) -> Array:
var out: Array = []
for activity in regeln(human_id):
if regeln(human_id)[activity] == tech_id:
out.append(activity)
return out
## Was in diesem Slot wirklich passiert: die Ersatzhandlung, wenn ein System
## steht -- sonst das, was von Haus aus dort steht.
func slot_aktivitaet(human_id: String, nr: int) -> String:
var sys := system_in(human_id, nr)
if not sys.is_empty():
return str(TechDB.by_id(str(sys["tech"]))["ersatz"])
return slot_grund(human_id, nr)
## Leere tech_id löscht die Regel. Eine Technik darf an beliebig vielen
## Tätigkeiten hängen — ob das klug ist, muss der Spieler aus ihr herauslesen.
func regel_setzen(human_id: String, activity: String, tech_id: String) -> void:
if not pipeline.has(human_id):
pipeline[human_id] = {}
var r: Dictionary = pipeline[human_id]
if tech_id == "":
r.erase(activity)
else:
r[activity] = tech_id
func system_in(human_id: String, nr: int) -> Dictionary:
return systeme.get(human_id, {}).get(nr, {})
# ------------------------------------------------------------------- Erkennen
func erkannt(human_id: String, nr: int) -> bool:
if not Ursachen.ist_blocker(slot_grund(human_id, nr)):
return true
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
## Greift für diese Tätigkeit eine Regel? Wird nicht nur beim Wechsel gefragt,
## sondern immer wieder — solange der Moment anhält und die Technik frei ist.
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 {}
## 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):
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
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 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))
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
# ----------------------------------------------------- Technik auf Mensch anwenden
## 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"])}
## Schlafende erreicht keine Technik — weder von Hand noch über eine Regel.
func system_entfernen(human_id: String, nr: int) -> void:
if systeme.has(human_id):
systeme[human_id].erase(nr)
state_changed.emit()
func system_anzahl(human_id: String) -> int:
return systeme.get(human_id, {}).size()
## Der Traum kommt voran, solange Systeme stehen.
func traum_voranbringen(stunden: float) -> void:
if active_mission == "":
return
var p: float = progress.get(active_mission, 0.0) \
+ traum_tempo(active_mission) * stunden / 24.0
progress[active_mission] = minf(p, MISSION_ZIEL)
if progress[active_mission] >= MISSION_ZIEL and not completed.has(active_mission):
var wer := active_mission
completed.append(wer)
active_mission = ""
mission_done.emit(wer)
notified.emit("%s hat es geschafft." % HumanDB.by_id(wer)["name"], Color(1, 0.85, 0.4))
_insel_waechst()
state_changed.emit()
# --------------------------------------------------- Erreichbarkeit
## Schlafende erreicht niemand.
func erreichbar(activity: String) -> bool:
return activity != "schlaeft"
## Bewertet eine Technik in genau dieser Situation, ohne sie anzuwenden.
func evaluate(tech_id: String, human_id: String, activity: String, resisting: bool) -> Dictionary:
var t := TechDB.by_id(tech_id)
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 = ""
done = true
mission_done.emit(human_id)
notified.emit("%s hat es geschafft." % HumanDB.by_id(human_id)["name"],
Color(1, 0.85, 0.4))
_insel_waechst()
state_changed.emit()
return {
"ok": true, "success": success, "chance": ev["chance"], "gain": gain,
"verlust": verlust, "wiederholung": wiederholung, "mission_done": done,
"tag": ev["tag"], "moment": ev["moment"],
}

View File

@@ -101,32 +101,20 @@ static var ANLIEGEN: Array = [
},
]
## Tagesabläufe: Platzhalter werden durch Wohnung und Arbeitsplatz ersetzt.
## Tagesabläufe: Platzhalter werden durch Wohnung und Arbeitsplatz ersetzt.
## Die Reihenfolge passt zu den Anliegen — wer aufschiebt, arbeitet zu Hause.
static var ABLAEUFE: Array = [
[[0, "heim", "schlaeft"], [7, "heim", "zoegert"], [10, "heim", "arbeitet"],
[13, "park", "pause"], [15, "heim", "arbeitet"], [18, "cafe", "wartet"],
[20, "heim", "zoegert"], [22, "heim", "gruebelt"], [23, "heim", "abendroutine"]],
[[0, "heim", "schlaeft"], [4, "arbeit", "arbeitet"], [10, "cafe", "pause"],
[12, "heim", "essen"], [14, "heim", "ruht"], [16, "heim", "zoegert"],
[18, "park", "unterwegs"], [20, "heim", "muede"], [22, "heim", "abendroutine"]],
[[0, "heim", "schlaeft"], [8, "heim", "morgenroutine"], [10, "arbeit", "arbeitet"],
[13, "cafe", "angespannt"], [15, "arbeit", "wartet"], [18, "heim", "gruebelt"],
[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"]],
## Der Grundtag. Blocker werden später in passende Fenster gesetzt.
## [Stunde, Ort, Tätigkeit, Fenster]
static var GRUNDTAG: Array = [
[0, "heim", "schlaeft", "nacht"],
[7, "heim", "morgenroutine", "morgen"],
[9, "arbeit", "arbeitet", "tag"],
[12, "cafe", "essen", "tag"],
[14, "arbeit", "arbeitet", "tag"],
[17, "park", "unterwegs", "abend"],
[19, "heim", "ruht", "abend"],
[21, "heim", "ruht", "abend"],
[23, "heim", "abendroutine", "nacht"],
]
## 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 = [
[0, 0], # Pomodoro -> aufschieben, zögert
[6, 4], # Zwei-Minuten -> Werkstatt, räumt auf
@@ -403,25 +391,68 @@ static func _anker_setzen() -> void:
ANKER[key] = int(NETZ.andocken(tuer)["knoten"])
static func _mensch(i: int) -> Dictionary:
# Bis alle Wege entdeckt sind, folgt jeder Mensch dem Weg gleicher Nummer.
# Danach mischt sich das Feld wieder.
var a_nr: int = i % ANLIEGEN.size()
var b_nr: int = i % ABLAEUFE.size()
if i < PASSEND.size():
a_nr = int(PASSEND[i][0])
b_nr = int(PASSEND[i][1])
var anliegen: Dictionary = ANLIEGEN[a_nr]
var heim := heim_key(i)
var arbeit := _naechster_arbeitsplatz(heim, i)
var ablauf: Array = []
for e in ABLAEUFE[b_nr]:
## Woran der Traum dieses Menschen scheitert. Zwei bis drei Ursachen, nie alle.
## Die erste hängt am Weg gleicher Nummer -- so ist der neueste Mensch immer
## mit dem neuesten Weg anzugehen.
static func ursachen_von(i: int) -> Array:
var alle: Array = Ursachen.LISTE.keys()
var erste: String = str(alle[i % alle.size()])
if i < TechDB.LIST.size():
erste = str(TechDB.LIST[i]["ursache"])
var out: Array = [erste]
var zweite: String = str(alle[(alle.find(erste) + 1 + i % 3) % alle.size()])
if zweite != erste:
out.append(zweite)
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])
if ort == "heim":
ort = heim
elif 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 {
"id": "mensch_%d" % i,
"name": "%s %s" % [_vorname(i), _nachname(i)],
@@ -430,10 +461,11 @@ static func _mensch(i: int) -> Dictionary:
"traum": str(anliegen["traum"]),
"problem": str(anliegen["problem"]),
"tags": (anliegen["tags"] as Array).duplicate(),
"ursachen": ursachen_von(i),
"mission": true,
"heim": heim,
"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:
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)
if ui.offen:
ui.setze_hinweis("")
return
_fokus_suchen()
_beobachten(dt)
_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:
_fokus = null
_fokus_mensch = null
@@ -99,8 +112,13 @@ func _fokus_suchen() -> void:
func _hinweis_setzen() -> void:
if _fokus_mensch != null:
ui.setze_hinweis("[E] %s ansprechen — %s" %
[_fokus_mensch.data["name"], Orte.text(_fokus_mensch.activity)])
var mid: String = str(_fokus_mensch.data["name"])
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:
var kind: String = _fokus.get_meta("kind")
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:
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:

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.",
"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.",
"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"],
"momente": ["arbeitet", "zoegert"],
"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.",
"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.",
"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"],
"momente": ["zoegert", "aufraeumen", "arbeitet"],
"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.\"",
"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.",
"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"],
"momente": ["morgenroutine", "pause"],
"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.",
"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.",
"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"],
"momente": ["morgenroutine", "essen", "abendroutine"],
"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.",
"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.",
"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"],
"momente": ["gruebelt", "arbeitet"],
"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.",
"wann": "Wenn gute Vorsätze regelmäßig an Ablenkung scheitern.",
"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"],
"momente": ["aufraeumen", "arbeitet", "ruht"],
"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.",
"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.",
"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"],
"momente": ["gruebelt", "ruht", "angespannt"],
"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.",
"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.",
"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"],
"momente": ["angespannt", "wartet", "pause"],
"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.",
"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.",
"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"],
"momente": ["gruebelt", "ruht", "angespannt"],
"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.",
"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.",
"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"],
"momente": ["abendroutine", "muede", "ruht"],
"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.",
"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.",
"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"],
"momente": ["wartet", "pause", "unterwegs"],
"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.\"",
"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.",
"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"],
"momente": ["arbeitet", "angespannt", "unterwegs"],
"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:
for t in LIST:
if t["id"] == id:

View File

@@ -154,9 +154,9 @@ func _hud_aktualisieren() -> void:
[Game.known.size(), Game.offene_techniken(), TechDB.LIST.size()])
if Game.active_mission != "":
var h := HumanDB.by_id(Game.active_mission)
zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d Regeln" %
zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d %% je Tag" %
[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"])
elif Game.completed.size() >= HumanDB.LIST.size():
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]" %
int(Game.mission_progress(id)), 15)
if not Game.completed.has(id):
_knopf("Regelwerk — %d Regeln" % Game.regel_anzahl(id),
func() -> void: pipeline_panel(id))
_knopf("Tagesablauf — %d Systeme" % Game.system_anzahl(id),
func() -> void: systeme_panel(id))
_trenner()
# -------------------------------------------------------- Regelwerk einer Mission
# ---------------------------------------------- Diagnose und Systeme
## Wenn-Dann-Regeln: tut der Mensch etwas Bestimmtes, greift die zugeordnete
## Technik von allein. So automatisiert der Spieler den Fortschritt.
func pipeline_panel(human_id: String) -> void:
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "pipeline" else 0
## Der Traum scheitert an Ursachen. Die stehen nicht im Panel -- man findet sie,
## indem man dabeisteht. Gegen jede gefundene Ursache baut man ein System in
## den Tag. Je mehr abgedeckt ist, desto schneller kommt der Traum voran.
func systeme_panel(human_id: String) -> void:
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "systeme" else 0
var h := HumanDB.by_id(human_id)
_leeren("Regelwerk — %s" % h["name"])
_text("Jede Tätigkeit im Tag von %s kann eine Technik auslösen. " % h["name"] +
"Wechselt sie hinein, greift die Regel von allein — solange du auf der Erde bist.\n" +
"Nach jedem Einsatz braucht eine Technik Ruhe, bevor sie wieder anschlägt.", 15)
_text("[color=#aaaaaa]Fortschritt: [b]%d %%[/b] · Regeln: [b]%d[/b][/color]" %
[int(Game.mission_progress(human_id)), Game.regel_anzahl(human_id)], 15)
_leeren("%s — Tagesablauf" % h["name"])
_text("[b]Traum:[/b] %s" % h["traum"], 16)
_text("[b]Fortschritt:[/b] %d %% · [b]Tempo:[/b] %d %% je Tag" %
[int(Game.mission_progress(human_id)), int(round(Game.traum_tempo(human_id)))], 16)
_trenner()
for activity in HumanDB.aktivitaeten(human_id):
var tat := Orte.text(activity)
if not Game.erreichbar(activity):
_knopf("%s → unerreichbar" % tat, func() -> void: pass, false)
_text("[b]Woran es scheitert[/b]", 17)
var offene := 0
for u in Game.ursachen_von(human_id):
var name: String = str(u)
if not Game.ursache_bekannt(human_id, name):
_text("[color=#8e93a8]? — noch nicht erkannt[/color]", 15)
offene += 1
continue
var tech: String = Game.regel_fuer(human_id, activity)
var beschriftung := "%s → keine Regel" % tat
if tech != "":
beschriftung = "%s → %s" % [tat, TechDB.by_id(tech)["name"]]
if not Game.bereit(tech):
beschriftung += " (noch gesperrt)"
_knopf(beschriftung, func() -> void: regel_panel(human_id, activity))
var g := Game.ursache_geloest(human_id, name)
var stand := "[color=#88ff99]gelöst[/color]" if g > 0.0 else \
"[color=#ff9977]offen · bremst auf %d %%[/color]" % \
int(float(Ursachen.LISTE[name]["faktor"]) * 100.0)
_text("[b]%s[/b] — %s %s" % [Ursachen.LISTE[name]["name"],
Ursachen.LISTE[name]["kurz"], stand], 15)
if offene > 0:
_text("[color=#aaaaaa]Steh dabei, 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()
_knopf("Zurück zum Journal", func() -> void: journal_panel("missionen"))
_zeigen("pipeline")
_zeigen("systeme")
_scroll_wunsch = scroll_alt
_scroll_frames = 4
## Welche Technik soll bei dieser Tätigkeit greifen?
func regel_panel(human_id: String, activity: String) -> void:
var h := HumanDB.by_id(human_id)
_leeren("Regel für »%s«" % Orte.text(activity))
if Game.known.is_empty():
_text("[color=#ffcc66]Du kennst keine Technik. Im Hörsaal der Wege " +
"werden sie gelehrt.[/color]", 16)
_knopf("Zurück", func() -> void: pipeline_panel(human_id))
_zeigen("regel")
func _slot_zeile(human_id: String, nr: int) -> void:
var slot: Array = Game.slots(human_id)[nr]
var stunde := "%02d:00" % int(slot[0])
var grund := Game.slot_grund(human_id, nr)
if grund == "schlaeft":
_knopf("%s schläft" % stunde, func() -> void: pass, false)
return
_text("Was soll greifen, wenn %s %s? Ob eine Technik hier taugt, steht in ihr " %
[h["name"], Orte.text(activity)] +
"selbst — nachzulesen im Journal. Ein Fehlgriff wirft den Traum zurück.", 15)
var sys := Game.system_in(human_id, nr)
if not sys.is_empty():
_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()
for id in TechDB.ids():
if not Game.knows(id):
continue
var pruef := Game.system_pruefen(human_id, nr, id)
var t := TechDB.by_id(id)
var 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)]
if ev["blocked"]:
beschriftung += " — " + str(ev["grund"])
elif gewaehlt != "":
beschriftung += gewaehlt
_knopf(beschriftung, func() -> void:
Game.regel_setzen(human_id, activity, id)
pipeline_panel(human_id), not ev["blocked"])
_knopf("Keine Regel für diese Tätigkeit", func() -> void:
Game.regel_setzen(human_id, activity, "")
pipeline_panel(human_id))
_zeigen("regel")
if not pruef["geht"]:
beschriftung += " — " + str(pruef["grund"])
elif float(pruef["guete"]) > Game.GUETE_NEU:
beschriftung += " — passt genau"
_knopf(beschriftung, func() -> void: _system_setzen(human_id, nr, id),
bool(pruef["geht"]))
_trenner()
_knopf("Zurück", func() -> void: systeme_panel(human_id))
_zeigen("slot")
func _system_setzen(human_id: String, nr: int, tech_id: String) -> void:
var res := Game.system_setzen(human_id, nr, tech_id, false)
if not res["ok"]:
Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4))
return
if not res["erfolg"]:
Game.notified.emit("%s greift diesmal nicht. Später noch einmal." %
TechDB.by_id(tech_id)["name"], Color(1, 0.6, 0.5))
systeme_panel(human_id)
# -------------------------------------------------------- Lehrorte
@@ -522,70 +564,66 @@ func brett_panel() -> void:
_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:
_apply_human = mensch
var h: Dictionary = mensch.data
var aktiv: String = mensch.activity
_leeren("%s — %s" % [h["name"], Orte.text(aktiv)])
var id: String = str(h["id"])
var nr: int = int(mensch.slot)
_leeren("%s — %s" % [h["name"], Orte.text(mensch.activity)])
_text("[b]Traum:[/b] %s" % h["traum"], 16)
if Game.active_mission == h["id"]:
_text("Fortschritt: [b]%d %%[/b]" % int(Game.mission_progress(h["id"])), 16)
elif Game.completed.has(h["id"]):
_text("[color=#88ff99]★ Traum erfüllt.[/color]", 16)
else:
_text("[color=#ffcc66]Keine aktive Mission für diesen Menschen. " +
"Fortschritt zählt nur mit angenommener Mission.[/color]", 15)
if Game.active_mission != id:
_text("[color=#ffcc66]Keine aktive Mission für diesen Menschen.[/color]", 15)
_zeigen("anwenden")
return
var grund := Game.slot_grund(id, nr)
if not Game.erkannt(id, nr):
_text("[color=#ffcc66]Was hier läuft, hast du noch nicht 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()
if Game.known.is_empty():
_text("[color=#ffcc66]Du kennst keine Technik.[/color]")
_zeigen("anwenden")
return
_text("[color=#aaaaaa]Was eine Technik tut, steht im Journal.[/color]", 15)
for id in TechDB.ids():
if not Game.knows(id):
for tid in TechDB.ids():
if not Game.knows(tid):
continue
var t := TechDB.by_id(id)
var ev := Game.evaluate(id, h["id"], aktiv, mensch.resist_left > 0.0)
var gesperrt := not Game.bereit(id)
var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(id)]
if gesperrt:
beschriftung += " — noch %d s gesperrt" % int(ceil(Game.cooldown_rest(id)))
elif ev["blocked"]:
beschriftung += " — " + str(ev["grund"])
var pruef := Game.system_pruefen(id, nr, tid)
var t := TechDB.by_id(tid)
var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(tid)]
if not pruef["geht"]:
beschriftung += " — " + str(pruef["grund"])
elif mensch.resist_left > 0.0:
beschriftung += " — macht gerade dicht"
_knopf(beschriftung, func() -> void: _anwenden(id), not ev["blocked"] and not gesperrt)
_knopf(beschriftung, func() -> void: _vor_ort_setzen(tid), bool(pruef["geht"]))
_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):
schliessen()
return
var mensch := _apply_human
var h: Dictionary = mensch.data
var res := Game.apply_technique(id, h["id"], mensch.activity, mensch.resist_left > 0.0)
var id: String = str(mensch.data["id"])
var res := Game.system_setzen(id, int(mensch.slot), tech_id, mensch.resist_left > 0.0)
schliessen()
if not res["ok"]:
Game.notified.emit(res["grund"], Color(1, 0.6, 0.4))
Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4))
return
var t := TechDB.by_id(id)
if res["success"]:
mensch.reagiere(true, "%s wirkt\n+%d %%" % [t["name"], int(round(float(res["gain"])))])
Game.notified.emit("%s erreicht %s. (+%d %%)" %
[t["name"], h["name"], int(round(float(res["gain"])))], Color(0.6, 1, 0.7))
if res["wiederholung"] >= 2:
Game.notified.emit("Immer dieselbe Technik wirkt schwächer. Probier eine andere.",
Color(0.85, 0.85, 0.6))
var t := TechDB.by_id(tech_id)
if res["erfolg"]:
mensch.reagiere(true, "%s\nsteht" % t["name"])
else:
mensch.reagiere(false, "%s prallt ab\n−%d %%" % [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))
mensch.reagiere(false, "%s greift nicht" % t["name"])
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 knipse("11_journal_missionen")
# Regelwerk: Wenn-Dann-Ketten, die den Fortschritt automatisieren
Game.regel_setzen("mensch_0", "zoegert", str(DreamDB.techniken_von("hoersaal")[0]))
main.ui.pipeline_panel("mensch_0")
# Diagnose: erst aufdecken, dann Systeme bauen
Game.erkannt_slots["mensch_0"] = {}
main.ui.systeme_panel("mensch_0")
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 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()
# Lichtung, wenn sich die Wesen dort treffen
@@ -145,26 +154,30 @@ func _ready() -> void:
await warte(20)
await knipse("17_lena_zoegert")
# Das Regelwerk greift ohne Zutun, sobald Lena die Tätigkeit wechselt
var vor := Game.mission_progress("mensch_0")
for i in range(20000):
# Der Traum läuft von allein weiter, solange Systeme stehen
for i in range(4000):
Game.time_h += 0.004
main._process(0.02)
for k in main.welt.get_children():
if k is Bewohner:
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.yaw = PI
main.player.pitch = -0.10
await warte(4)
await knipse("18_regel_greift")
await knipse("18_traum_laeuft")
main.ui.anwenden_panel(lena)
await warte(6)
await knipse("19_anwenden")
main.ui._anwenden(str(DreamDB.techniken_von("hoersaal")[0]))
await knipse("19_vor_ort")
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 knipse("20_reaktion")

File diff suppressed because it is too large Load Diff