Files
dreamer-game/scripts/game.gd
2026-08-30 11:50:09 +02:00

224 lines
6.0 KiB
GDScript

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
var einsicht: int = 3
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 completed: Array = []
var time_h: float = 7.0
var day: int = 1
var teleport_lock: float = 0.0
func _ready() -> void:
_setup_input()
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],
"sinken": [KEY_SHIFT],
"interagieren": [KEY_E],
"journal": [KEY_J, KEY_TAB],
}
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)
func advance_time(delta: float) -> void:
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]
# ---------------------------------------------------------------- Fertigkeiten
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 einsicht < 1:
notified.emit("Dafür fehlt dir Einsicht. Hilf erst einem Menschen.", Color(1, 0.6, 0.4))
return false
einsicht -= 1
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()
func deepen(tech_id: String) -> bool:
if not known.has(tech_id):
return false
if einsicht < 1:
notified.emit("Keine Einsicht übrig.", Color(1, 0.6, 0.4))
return false
einsicht -= 1
add_xp(tech_id, 3)
notified.emit("Vertieft: %s (+3 EP)" % TechDB.by_id(tech_id)["name"], Color(0.7, 0.9, 1))
return true
# ------------------------------------------------------------------ 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
func mission_progress(human_id: String) -> float:
return progress.get(human_id, 0.0)
# ----------------------------------------------------- Technik auf Mensch anwenden
## 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 activity == "schlaeft":
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)
var c := 0.08 + 0.11 * float(skill)
if res["tag"]:
c += 0.25
if res["moment"]:
c += 0.22
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:
var ev := evaluate(tech_id, human_id, activity, resisting)
if ev["blocked"]:
return {"ok": false, "grund": ev["grund"]}
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
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:
add_xp(tech_id, 1)
var done := false
if human_id == active_mission and gain > 0.0:
var p: float = progress.get(human_id, 0.0) + gain
progress[human_id] = minf(p, MISSION_ZIEL)
if progress[human_id] >= MISSION_ZIEL and not completed.has(human_id):
completed.append(human_id)
active_mission = ""
einsicht += 3
done = true
mission_done.emit(human_id)
notified.emit("%s hat es geschafft. (+3 Einsicht)" % HumanDB.by_id(human_id)["name"],
Color(1, 0.85, 0.4))
state_changed.emit()
return {
"ok": true, "success": success, "chance": ev["chance"], "gain": gain,
"wiederholung": wiederholung, "mission_done": done,
"tag": ev["tag"], "moment": ev["moment"],
}