extends Node3D class_name Bewohner ## Bewohner einer Welt: läuft seinen Tagesablauf ab. Auf der Erde kann er von ## einer Technik erreicht werden -- oder eben nicht. const GEHTEMPO := 8.0 var data: Dictionary var idx: int = 0 var place: String = "" var activity: String = "" var resist_left: float = 0.0 var places: Dictionary = {} var netz: Netz = null var anker: Dictionary = {} var mit_mission := false var _visual: Node3D var _label: Label3D var _koerper: MeshInstance3D var _kopf: MeshInstance3D var _aura: MeshInstance3D var _strahl: MeshInstance3D var _path: Array = [] var _letzte_aktivitaet: String = "" var slot: int = 0 var _bob: float = 0.0 var _offset: Vector3 func setup(d: Dictionary, index: int, orte: Dictionary, wegenetz: Netz = null, wegeanker: Dictionary = {}) -> void: data = d idx = index places = orte netz = wegenetz anker = wegeanker mit_mission = bool(d.get("mission", false)) name = "Bewohner_" + str(d["id"]) var winkel := float(index) * TAU / 4.0 _offset = Vector3(cos(winkel), 0, sin(winkel)) * 2.2 _visual = Node3D.new() add_child(_visual) var c: Color = d["farbe"] var leuchtet := bool(d.get("leuchtet", false)) var hoehe := 1.05 + float(index % 3) * 0.08 _koerper = Prims.capsule(_visual, Vector3(0, hoehe * 0.62, 0), 0.32, hoehe, c) _kopf = Prims.sphere(_visual, Vector3(0, hoehe * 1.18 + 0.1, 0), 0.24, c.lightened(0.45)) Prims.box(_visual, Vector3(0, hoehe * 0.95, 0), Vector3(0.9, 0.14, 0.3), c.darkened(0.25), false) Prims.box(_visual, Vector3(0, 0.06, 0), Vector3(0.5, 0.12, 0.7), c.darkened(0.45), false) if leuchtet: _koerper.material_override = Prims.mat(c, 0.9) _kopf.material_override = Prims.mat(c.lightened(0.45), 0.8) var reif := Prims.cyl(_visual, Vector3(0, hoehe * 0.5, 0), 0.62, 0.05, c.lightened(0.3), false, 1.6) reif.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF _aura = Prims.cyl(self, Vector3(0, 0.05, 0), 0.95, 0.05, Color(1, 0.85, 0.35), false, 2.0) _aura.visible = false _strahl = Prims.cyl(self, Vector3(0, 11.0, 0), 0.14, 16.0, Color(1, 0.85, 0.35, 0.22), false, 1.2) _strahl.visible = false _label = Prims.label(self, Vector3(0, 2.9, 0), d["name"], 0.30) var start := _routine_at(Game.time_h) place = start[0] activity = start[1] _letzte_aktivitaet = activity 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 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: var delta := roh * Game.zeit_faktor() 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) if war_unterwegs and _path.size() > 1: _path.remove_at(0) # steht schon draußen, alte Tür überspringen place = soll[0] activity = "unterwegs" elif _path.is_empty() and activity != soll[1]: activity = soll[1] if not _path.is_empty(): var ziel: Vector3 = _path[0] ziel.y = 0.0 var hier := global_position hier.y = 0.0 var d := ziel - hier if d.length() < 0.4: _path.remove_at(0) if _path.is_empty(): activity = soll[1] else: # nie über den Wegpunkt hinaus -- sonst pendelt er bei hohem Tempo # um ihn herum, statt anzukommen var schritt := d.normalized() * minf(GEHTEMPO * delta, d.length()) global_position += schritt _visual.rotation.y = lerp_angle(_visual.rotation.y, atan2(-d.x, -d.z), 1.0 - pow(0.002, delta)) _bob += delta * 9.0 _visual.position.y = absf(sin(_bob)) * 0.09 if _path.is_empty(): _visual.position.y = lerpf(_visual.position.y, 0.0, 1.0 - pow(0.01, delta)) _visual.rotation.z = lerp_angle(_visual.rotation.z, PI * 0.5 if activity == "schlaeft" else 0.0, 1.0 - pow(0.01, delta)) if activity != _letzte_aktivitaet: _letzte_aktivitaet = activity var erledigt: bool = mit_mission and Game.completed.has(data["id"]) var aktiv: bool = mit_mission and Game.active_mission == data["id"] _aura.visible = aktiv or erledigt _strahl.visible = aktiv if _aura.visible: _aura.material_override = Prims.mat( Color(0.4, 1.0, 0.5) if erledigt else Color(1, 0.85, 0.35), 2.0) _aura.scale.x = 1.0 + sin(Time.get_ticks_msec() * 0.003) * 0.08 _aura.scale.z = _aura.scale.x _label.text = beschriftung() _label.modulate = Color(0.55, 0.85, 0.6) if erledigt else ( Color(1, 0.9, 0.6) if aktiv else Color(1, 1, 1)) func beschriftung() -> String: var s: String = data["name"] if data.has("rolle"): s += ", " + str(data["rolle"]) s += "\n" + Orte.text(activity) if mit_mission and Game.situation_bei(str(data["id"])): s += "\n» %s «" % Probleme.SITUATIONEN[Game.situation["key"]]["name"] if resist_left > 0.0: s += " (macht dicht)" if mit_mission: if Game.completed.has(data["id"]): s += "\n★ Traum erfüllt" elif Game.active_mission == data["id"]: s += "\nTraum: %d %%" % int(Game.mission_progress(data["id"])) return s ## 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, Color(0.55, 1.0, 0.6) if success else Color(1, 0.55, 0.45)) var tw := l.create_tween() tw.set_parallel(true) tw.tween_property(l, "position:y", 5.2, 2.4) tw.tween_property(l, "modulate:a", 0.0, 2.4).set_delay(0.9) tw.chain().tween_callback(l.queue_free) if not success: resist_left = Game.RESIST_SEKUNDEN var blitz := Prims.mat( Color(0.6, 1.0, 0.7) if success else Color(1, 0.5, 0.4), 1.6) var orig := _koerper.material_override _koerper.material_override = blitz _kopf.material_override = blitz var t2 := create_tween() t2.tween_interval(0.35) t2.tween_callback(func() -> void: if is_instance_valid(_koerper): _koerper.material_override = orig _kopf.material_override = Prims.mat((data["farbe"] as Color).lightened(0.45)))