update
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
68
README.md
68
README.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
340
scripts/game.gd
340
scripts/game.gd
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
236
scripts/ui.gd
236
scripts/ui.gd
@@ -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
136
scripts/ursachen.gd
Normal 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
1
scripts/ursachen.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ddavhllmw3nlk
|
||||
@@ -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")
|
||||
|
||||
|
||||
922
tests/verify.gd
922
tests/verify.gd
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user