Files
dreamer-game/scripts/game.gd
2026-09-16 12:08:04 +02:00

711 lines
23 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
## Globaler Spielzustand: Wissen, Fertigkeiten, Missionen, Uhrzeit.
signal state_changed
signal notified(text: String, color: Color)
signal mission_done(human_id: String)
const DAY_SECONDS := 360.0 # eine Erdenrunde in Echtzeit-Sekunden
const XP_LEVELS := [0, 4, 10, 18, 28]
const MISSION_ZIEL := 100.0
const RESIST_SEKUNDEN := 18.0
## Drei Stufen wie in Die Sims: normal, schnell, sehr schnell. Die Stufe ist
## nicht der Faktor — Stufe 2 läuft dreifach, Stufe 3 siebenfach.
const TEMPO_FAKTOR := [1.0, 3.0, 7.0]
const TECHNIK_COOLDOWN := 30.0 # eine Technik braucht Luft, bevor sie wieder greift
# Der Verlust ist mit Absicht so hoch, dass sich nur eine Technik lohnt, die
# Thema UND Moment trifft. Wer eine Regel blind auf jede Tätigkeit hängt,
# verliert unterm Strich — die Wahl muss aus der Technik gelesen werden.
var known: Dictionary = {} # tech_id -> xp
var active_mission: String = "" # human_id oder ""
var progress: Dictionary = {} # human_id -> 0..100
var cooldown: Dictionary = {} # tech_id -> Restsekunden
var systeme: Dictionary = {} # human_id -> {slot -> {tech, guete}}
var erkannt_slots: Dictionary = {} # human_id -> {slot -> true}
var gesehen: Dictionary = {} # human_id -> {slot -> true}, wo man dabei war
var notiz_sperre: float = 0.0 # nach einem Fehlgriff kurz gesperrt
var situation: Dictionary = {} # laufende Situation: mensch, key, rest
var situation_pause: float = 0.0 # bis die nächste kommen darf
var probleme_bekannt: Dictionary = {} # human_id -> {problem -> true}
var problem_stand: Dictionary = {} # "human:problem" -> gelungene Anwendungen
var ueberwunden: Dictionary = {} # human_id -> {problem -> true}
var gesehen_zaehler: Dictionary = {} # "human:problem" -> wie oft dabeigewesen
var completed: Array = []
var time_h: float = 7.0
var day: int = 1
var teleport_lock: float = 0.0
var tempo: int = 1 # 1, 2 oder 3 -- vom Spieler gewählt
func _ready() -> void:
_setup_input()
welt_erzeugen()
_startweg_lernen()
## Den ersten Weg bringt das Wesen schon mit. Ohne ihn hinge am Schwarzen Brett
## nichts aus, und der erste Gang wäre eine Pflichtübung.
func _startweg_lernen() -> void:
for id in DreamDB.techniken_von("hoersaal"):
if not known.has(str(id)):
known[str(id)] = 0
func _setup_input() -> void:
var binds := {
"vor": [KEY_W, KEY_UP],
"zurueck": [KEY_S, KEY_DOWN],
"links": [KEY_A, KEY_LEFT],
"rechts": [KEY_D, KEY_RIGHT],
"steigen": [KEY_SPACE],
"interagieren": [KEY_E],
"folgen": [KEY_F],
"notieren": [KEY_R],
"journal": [KEY_J, KEY_TAB],
"drehen": [KEY_Q],
"tempo_1": [KEY_1, KEY_KP_1],
"tempo_2": [KEY_2, KEY_KP_2],
"tempo_3": [KEY_3, KEY_KP_3],
}
for action in binds:
if not InputMap.has_action(action):
InputMap.add_action(action)
for code in binds[action]:
var ev := InputEventKey.new()
ev.physical_keycode = code
InputMap.action_add_event(action, ev)
## Schläft der Mensch, dem du gerade hilfst? Dann ist ohnehin nichts zu holen.
func schlaeft_ziel() -> bool:
if active_mission == "":
return false
var h := HumanDB.by_id(active_mission)
if h.is_empty():
return false
var r: Array = h["routine"]
var cur: Array = r[r.size() - 1]
for e in r:
if time_h >= float(e[0]):
cur = e
return not erreichbar(str(cur[2]))
## Um wie viel schneller läuft die Welt gerade? Über der Nacht des Menschen
## läuft sie immer auf der höchsten Stufe -- warten bringt dort nichts.
func zeit_faktor() -> float:
return faktor_von(3 if schlaeft_ziel() else tempo)
static func faktor_von(stufe: int) -> float:
return float(TEMPO_FAKTOR[clampi(stufe, 1, 3) - 1])
func tempo_setzen(stufe: int) -> void:
var neu := clampi(stufe, 1, 3)
if neu == tempo:
return
tempo = neu
notified.emit("Tempo %d — %d×" % [tempo, int(faktor_von(tempo))], Color(0.8, 0.9, 1))
state_changed.emit()
func advance_time(delta: float) -> void:
for id in cooldown.keys():
var rest: float = float(cooldown[id]) - delta
if rest <= 0.0:
cooldown.erase(id)
else:
cooldown[id] = rest
time_h += delta * 24.0 / DAY_SECONDS
while time_h >= 24.0:
time_h -= 24.0
day += 1
func time_string() -> String:
var h := int(time_h)
var m := int((time_h - h) * 60.0)
return "Tag %d · %02d:%02d" % [day, h, m]
# ------------------------------------------------------------ Wachsende Welt
# Das Traumdorf wird erzeugt. Fest sind nur Hörsaal, Dorfplatz und Tor.
# Jeder erfüllte Traum bringt einen Bewohner mit seinem Haus, und jedes Haus
# lehrt eine Technik, die es vorher nirgends gab.
const INSEL_GRUND := 46.0
func haeuser() -> int:
return completed.size()
func insel_radius() -> float:
return maxf(INSEL_GRUND, DreamDB.aussenradius() + 12.0)
## Baut Traumdorf und Menschheit auf den Stand der erfüllten Träume.
func welt_erzeugen() -> void:
DreamDB.erzeuge(haeuser())
HumanDB.erzeuge()
## Das Traumdorf beginnt leer. Mit jedem erfüllten Traum zieht ein Wesen ein.
func wesen_eingezogen(index: int) -> bool:
return index < haeuser()
## Steht dieser Ort schon auf der Insel? Erzeugt wird nur, was schon da ist.
func ort_offen(place_key: String) -> bool:
return DreamDB.PLACES.has(place_key)
## Ist die Technik überhaupt schon irgendwo zu finden?
func technik_offen(tech_id: String) -> bool:
var ort := DreamDB.ort_von_technik(tech_id)
return ort != "" and ort_offen(ort)
func offene_techniken() -> int:
var n := 0
for t in TechDB.LIST:
if technik_offen(t["id"]):
n += 1
return n
# ------------------------------------------------------------------- Cooldown
# Jede Anwendung sperrt ihre Technik für eine Weile. Wer einen ganzen Tag
# abdecken will, braucht deshalb mehrere Wege — einer allein kommt nicht nach.
func cooldown_rest(tech_id: String) -> float:
return float(cooldown.get(tech_id, 0.0))
func bereit(tech_id: String) -> bool:
return cooldown_rest(tech_id) <= 0.0
# ---------------------------------------------------------------- Fertigkeiten
# Gelernt wird an einem Lehrort, besser wird eine Technik nur durch Anwendung.
# Es gibt keinen Weg, sie am Schreibtisch zu trainieren.
func knows(tech_id: String) -> bool:
return known.has(tech_id)
func skill_of(tech_id: String) -> int:
if not known.has(tech_id):
return 0
var xp: int = known[tech_id]
var lvl := 1
for i in range(XP_LEVELS.size()):
if xp >= XP_LEVELS[i]:
lvl = i + 1
return lvl
func xp_to_next(tech_id: String) -> String:
var lvl := skill_of(tech_id)
if lvl >= XP_LEVELS.size():
return "Meisterschaft"
var need: int = XP_LEVELS[lvl]
return "%d / %d EP" % [known[tech_id], need]
func learn(tech_id: String) -> bool:
if known.has(tech_id):
return false
if not technik_offen(tech_id):
notified.emit("Diesen Weg kennt im Dorf noch niemand. Erfülle einen Traum.",
Color(1, 0.8, 0.5))
return false
known[tech_id] = 0
var t := TechDB.by_id(tech_id)
notified.emit("Technik gelernt: %s" % t["name"], Color(0.6, 1, 0.7))
state_changed.emit()
return true
func add_xp(tech_id: String, amount: int) -> void:
if not known.has(tech_id):
return
var before := skill_of(tech_id)
known[tech_id] = int(known[tech_id]) + amount
if skill_of(tech_id) > before:
var t := TechDB.by_id(tech_id)
notified.emit("%s ist jetzt Stufe %d." % [t["name"], skill_of(tech_id)], Color(0.7, 0.9, 1))
state_changed.emit()
# ------------------------------------------------------------------ Missionen
func accept_mission(human_id: String) -> bool:
if completed.has(human_id):
return false
if active_mission == human_id:
return false
active_mission = human_id
if not progress.has(human_id):
progress[human_id] = 0.0
var h := HumanDB.by_id(human_id)
notified.emit("Mission angenommen: %s" % h["name"], Color(1, 0.9, 0.5))
state_changed.emit()
return true
## Angenommen ist, wer schon einmal im Auftragsbuch stand.
func angenommen(human_id: String) -> bool:
return progress.has(human_id)
## 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:
for u in ursachen_von(human_id):
for id in known:
if str(TechDB.by_id(id)["ursache"]) == str(u):
return true
return false
## Die Menschen, die gerade am Brett hängen können.
func offene_missionen() -> Array:
var out: Array = []
for h in HumanDB.LIST:
var id: String = h["id"]
if completed.has(id) or not machbar(id):
continue
out.append(id)
return out
func mission_progress(human_id: String) -> float:
return progress.get(human_id, 0.0)
## Ein erfüllter Traum lässt das Dorf wachsen: ein Wesen zieht mit seinem
## Haus ein, und in diesem Haus wird ein neuer Weg gelehrt.
func _insel_waechst() -> void:
welt_erzeugen()
var i := haeuser() - 1
if i < 0 or i >= DreamDB.LIST.size():
return
var w: Dictionary = DreamDB.LIST[i]
notified.emit("%s, %s, ist ins Traumdorf gezogen." % [w["name"], w["rolle"]],
Color(0.85, 0.75, 1))
var neu: Array = DreamDB.techniken_von(DreamDB.haus_key(i))
if neu.is_empty():
return
notified.emit("In %s wird ein neuer Weg gelehrt: %s." %
[DreamDB.PLACES[DreamDB.haus_key(i)]["name"], TechDB.by_id(str(neu[0]))["name"]],
Color(0.75, 0.95, 1))
# --------------------------------------------------------------------- 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.
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 NOTIZ_SPERRE := 6.0 # nach einem Fehlgriff schaut man erst wieder hin
func slots(human_id: String) -> Array:
var h := HumanDB.by_id(human_id)
return h["routine"] if h.has("routine") else []
## Welcher Slot deckt diese Stunde ab? Jeder Tag ist gleich, es gibt keine
## Wochentage -- der Ablauf 0-23 Uhr wiederholt sich.
func slot_bei_stunde(human_id: String, stunde: int) -> int:
var r := slots(human_id)
if r.is_empty():
return -1
var nr := r.size() - 1
for i in range(r.size()):
if float(stunde) >= float(r[i][0]):
nr = i
return nr
## Dabeigewesen: die Stunde ist im Ablauf nicht mehr leer. Was dort schiefläuft,
## steht damit noch nicht fest -- das muss der Spieler benennen.
func gesehen_slot(human_id: String, nr: int) -> bool:
return gesehen.get(human_id, {}).has(nr)
func sehen(human_id: String, nr: int) -> void:
if gesehen_slot(human_id, nr):
return
if not gesehen.has(human_id):
gesehen[human_id] = {}
gesehen[human_id][nr] = true
state_changed.emit()
## Der Zustand einer Stunde. HUD und Panel zeichnen denselben -- zwei
## Auslegungen würden mit der Zeit auseinanderlaufen.
enum Stunde { UNBEKANNT, GESEHEN, SCHLAF, UNGEKLAERT, BENANNT, GEBAUT }
func slot_zustand(human_id: String, nr: int) -> int:
if not gesehen_slot(human_id, nr):
return Stunde.UNBEKANNT
if not system_in(human_id, nr).is_empty():
return Stunde.GEBAUT
var grund := slot_grund(human_id, nr)
if Ursachen.ist_blocker(grund):
return Stunde.BENANNT if erkannt(human_id, nr) else Stunde.UNGEKLAERT
if grund == "schlaeft":
return Stunde.SCHLAF
return Stunde.GESEHEN
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 ""
## 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)
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)
## Zusehen allein deckt nichts auf. Der Spieler muss den Moment benennen:
## "hier läuft etwas schief". Trifft er, steht die Ursache im Journal.
## Greift er daneben, schaut er eine Weile umsonst hin.
## Grundlage bleibt awareness training -- nur ist das Bemerken jetzt ein Akt.
func notieren(human_id: String, nr: int) -> String:
if notiz_sperre > 0.0:
return "gesperrt"
var grund := slot_grund(human_id, nr)
if not Ursachen.ist_blocker(grund):
notiz_sperre = NOTIZ_SPERRE
notified.emit("Nichts schief: %s" % Orte.text(grund), Color(1, 0.75, 0.55))
return "daneben"
if erkannt(human_id, nr):
notified.emit("Schon notiert: %s" % Orte.text(grund), Color(0.8, 0.85, 1))
return "bekannt"
if not erkannt_slots.has(human_id):
erkannt_slots[human_id] = {}
erkannt_slots[human_id][nr] = true
notified.emit("Erkannt: %s — %s" % [Ursachen.BLOCKER[grund]["name"],
Ursachen.LISTE[Ursachen.ursache_von(grund)]["name"]], Color(0.8, 0.95, 1))
state_changed.emit()
return "treffer"
## 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 * (1.0 + 0.15 * float(verhalten(human_id, "tempo")))
for u in ursachen_von(human_id):
var g := ursache_geloest(human_id, str(u))
t *= g if g > 0.0 else float(Ursachen.LISTE[u]["faktor"])
return t
# ------------------------------------------------------------- System setzen
## Passt diese Technik in diesen Slot? Gibt Güte und Grund zurück.
func system_pruefen(human_id: String, nr: int, tech_id: String) -> Dictionary:
var res := {"geht": false, "guete": 0.0, "grund": ""}
if not known.has(tech_id):
res["grund"] = "Diese Technik kennst du nicht."
return res
if not bereit(tech_id):
res["grund"] = "%s ist noch nicht wieder einsatzbereit." % TechDB.by_id(tech_id)["name"]
return res
var grund_akt := slot_grund(human_id, nr)
if grund_akt == "schlaeft":
res["grund"] = "Im Schlaf lässt sich nichts einrichten."
return res
if not erkannt(human_id, nr):
res["grund"] = "Was hier läuft, hast du noch nicht gesehen."
return res
var t := TechDB.by_id(tech_id)
if Ursachen.ist_blocker(grund_akt):
var b: Dictionary = Ursachen.BLOCKER[grund_akt]
if str(t["ursache"]) != str(b["ursache"]):
res["grund"] = "Das hier hat einen anderen Grund."
return res
res["geht"] = true
var gleich: bool = str(t["belohnung"]) == str(b["belohnung"])
res["guete"] = GUETE_ERSATZ if gleich or TechDB.entfernt_ausloeser(tech_id) else GUETE_NEU
return res
res["geht"] = true
res["guete"] = GUETE_NEU
return res
## Richtet das System ein. Gelingt nicht immer -- Übung entscheidet.
func system_setzen(human_id: String, nr: int, tech_id: String, widerstand: bool) -> Dictionary:
var pruef := system_pruefen(human_id, nr, tech_id)
if not pruef["geht"]:
return {"ok": false, "grund": pruef["grund"]}
cooldown[tech_id] = TECHNIK_COOLDOWN
var chance := 0.20 + 0.13 * float(skill_of(tech_id)) - (0.25 if widerstand else 0.0)
chance += 0.08 * float(verhalten(human_id, "einrichten"))
if TechDB.ist_sozial(tech_id):
chance += 0.10 * float(verhalten(human_id, "sozial"))
chance = clampf(chance, 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] = {}
var guete := float(pruef["guete"]) * (1.0 + 0.1 * float(verhalten(human_id, "guete")))
systeme[human_id][nr] = {"tech": tech_id, "guete": guete}
add_xp(tech_id, 2)
notified.emit("System steht: %s um %02d:00" %
[TechDB.by_id(tech_id)["name"], int(slots(human_id)[nr][0])], Color(0.6, 1, 0.7))
state_changed.emit()
return {"ok": true, "erfolg": true, "guete": float(pruef["guete"])}
func system_entfernen(human_id: String, nr: int) -> void:
if systeme.has(human_id):
systeme[human_id].erase(nr)
state_changed.emit()
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
if situation_bei(active_mission):
return # im Moment geht nichts voran
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()
# ------------------------------------------------------- Probleme und Momente
# Die zweite Ebene. Eine Gewohnheit sitzt im Tagesablauf und läuft ohne dich
# weiter, sobald ein System steht. Ein Problem sitzt in keiner Stunde -- es
# bricht in Situationen auf. Solange eine läuft, kommt der Traum nicht voran.
# Du kannst sie aussitzen oder mit der passenden Technik beenden.
const SITUATION_DAUER := 22.0 # so lange steht der Traum still
const SITUATION_PAUSE := 40.0 # Luft zwischen zwei Situationen
const PROBLEM_UEBERWINDEN := 3 # so oft muss eine Technik greifen
const SITUATION_GEWINN := 6.0 # Prozent, wenn sie früh beendet wird
## Der Verhaltenswert eines Menschen, -2 bis +2 -- aus seinen Wörtern.
func verhalten(human_id: String, key: String) -> int:
var h := HumanDB.by_id(human_id)
return int((h.get("verhalten", {}) as Dictionary).get(key, 0))
func probleme_von(human_id: String) -> Array:
var h := HumanDB.by_id(human_id)
return h["probleme"] if h.has("probleme") else []
func problem_bekannt(human_id: String, problem: String) -> bool:
return probleme_bekannt.get(human_id, {}).has(problem)
func problem_ueberwunden(human_id: String, problem: String) -> bool:
return ueberwunden.get(human_id, {}).has(problem)
func problem_fortschritt(human_id: String, problem: String) -> int:
return int(problem_stand.get("%s:%s" % [human_id, problem], 0))
func situation_laeuft() -> bool:
return not situation.is_empty()
func situation_bei(human_id: String) -> bool:
return situation_laeuft() and str(situation["mensch"]) == human_id
func situation_problem() -> String:
return str(situation["problem"]) if situation_laeuft() else ""
## Läuft die Uhr weiter, kommt irgendwann die nächste Situation -- aber nur
## bei einem wachen Menschen, dem man gerade hilft.
func situationen_ticken(delta: float, wach: bool) -> void:
if situation_laeuft():
situation["rest"] = float(situation["rest"]) - delta
if float(situation["rest"]) <= 0.0:
var p := situation_problem()
notified.emit("Moment verpasst: %s" % Probleme.name_von(p),
Color(1, 0.75, 0.55))
var wer := str(situation["mensch"])
situation = {}
situation_pause = _pause_fuer(wer)
state_changed.emit()
return
situation_pause = maxf(0.0, situation_pause - delta)
if active_mission == "" or not wach or situation_pause > 0.0:
return
_situation_starten()
## Luft bis zum nächsten Moment -- ein reizbarer Mensch hat weniger davon.
func _pause_fuer(human_id: String) -> float:
return SITUATION_PAUSE * (1.0 - 0.25 * float(verhalten(human_id, "momente")))
func _situation_starten() -> void:
var offen: Array = []
for p in probleme_von(active_mission):
if not problem_ueberwunden(active_mission, str(p)):
offen.append(str(p))
if offen.is_empty():
situation_pause = SITUATION_PAUSE
return
var problem: String = str(offen[randi() % offen.size()])
var moeglich := Probleme.situationen_von(problem)
if moeglich.is_empty():
situation_pause = SITUATION_PAUSE
return
situation = {
"mensch": active_mission,
"key": str(moeglich[randi() % moeglich.size()]),
"problem": problem,
"rest": SITUATION_DAUER * (1.0 + 0.25 * float(verhalten(active_mission, "dauer"))),
}
if verhalten(active_mission, "sichtbar") > 0:
situation_sehen() # man sieht es ihm sofort an
state_changed.emit()
## Dabeisein zeigt, welches Problem dahintersteckt.
func situation_sehen() -> void:
if not situation_laeuft():
return
var id := str(situation["mensch"])
var p := situation_problem()
if problem_bekannt(id, p):
return
if verhalten(id, "sichtbar") < 0:
var schluessel := "%s:%s" % [id, p]
gesehen_zaehler[schluessel] = int(gesehen_zaehler.get(schluessel, 0)) + 1
if int(gesehen_zaehler[schluessel]) < 2:
return # verschlossen: erst beim zweiten Mal zu durchschauen
if not probleme_bekannt.has(id):
probleme_bekannt[id] = {}
probleme_bekannt[id][p] = true
notified.emit("Erkannt: %s" % Probleme.name_von(p), Color(0.8, 0.95, 1))
state_changed.emit()
## Eine Technik im Moment anwenden. Passt sie zum Problem, ist die Situation
## vorbei; oft genug, und das Problem kommt nicht wieder.
func situation_anwenden(tech_id: String) -> Dictionary:
var res := {"ok": false, "grund": "", "treffer": false, "ueberwunden": false}
if not situation_laeuft():
res["grund"] = "Gerade ist kein Moment offen."
return res
if not known.has(tech_id):
res["grund"] = "Diese Technik kennst du nicht."
return res
if not bereit(tech_id):
res["grund"] = "%s braucht noch %.0f s." % [TechDB.by_id(tech_id)["name"],
cooldown_rest(tech_id)]
return res
var id := str(situation["mensch"])
var problem := situation_problem()
cooldown[tech_id] = TECHNIK_COOLDOWN
res["ok"] = true
if not Probleme.hilft(tech_id, problem):
add_xp(tech_id, 1)
notified.emit("%s greift hier nicht." % TechDB.by_id(tech_id)["name"],
Color(1, 0.7, 0.5))
state_changed.emit()
return res
res["treffer"] = true
add_xp(tech_id, 2)
var schluessel := "%s:%s" % [id, problem]
problem_stand[schluessel] = problem_fortschritt(id, problem) + 1
situation_sehen()
situation = {}
situation_pause = _pause_fuer(id)
if id == active_mission:
progress[id] = minf(mission_progress(id) + SITUATION_GEWINN, MISSION_ZIEL)
notified.emit("%s trägt durch +%d %%" %
[TechDB.by_id(tech_id)["name"], int(SITUATION_GEWINN)], Color(0.6, 1, 0.7))
if problem_fortschritt(id, problem) >= PROBLEM_UEBERWINDEN:
if not ueberwunden.has(id):
ueberwunden[id] = {}
ueberwunden[id][problem] = true
res["ueberwunden"] = true
notified.emit("%s überwunden" % Probleme.name_von(problem), Color(1, 0.85, 0.4))
state_changed.emit()
return res
# --------------------------------------------------- Erreichbarkeit
## Schlafende erreicht niemand.
func erreichbar(activity: String) -> bool:
return activity != "schlaeft"