224 lines
6.0 KiB
GDScript
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"],
|
|
}
|