183 lines
5.8 KiB
GDScript
183 lines
5.8 KiB
GDScript
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 := 6.0
|
|
|
|
var data: Dictionary
|
|
var idx: int = 0
|
|
var place: String = ""
|
|
var activity: String = ""
|
|
var resist_left: float = 0.0
|
|
var places: 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 _bob: float = 0.0
|
|
var _offset: Vector3
|
|
|
|
|
|
func setup(d: Dictionary, index: int, orte: Dictionary) -> void:
|
|
data = d
|
|
idx = index
|
|
places = orte
|
|
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
|
|
|
|
|
|
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]]
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
resist_left = maxf(0.0, resist_left - delta)
|
|
|
|
var soll := _routine_at(Game.time_h)
|
|
if soll[0] != place:
|
|
var war_unterwegs := not _path.is_empty()
|
|
_path = Orte.route(places, place, soll[0], _offset)
|
|
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:
|
|
var schritt := d.normalized() * GEHTEMPO * delta
|
|
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
|
|
_regel_pruefen()
|
|
|
|
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 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
|
|
|
|
|
|
## 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,
|
|
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)))
|