This commit is contained in:
Team3
2026-09-16 12:08:04 +02:00
parent 936773c4e9
commit 89fad5dc7a
12 changed files with 493 additions and 167 deletions

View File

@@ -28,6 +28,7 @@ var _path: Array = []
var _letzte_aktivitaet: String = ""
var boden: Callable = Callable() # Höhe des Bodens (x, z) -> y; leer heißt eben
var slot: int = 0
var spur: Array = [] # letzte Positionen -- der Geist folgt ihr durch Türen
var _bob: float = 0.0
var _offset: Vector3
@@ -87,33 +88,19 @@ func setup(d: Dictionary, index: int, orte: Dictionary,
global_position = Orte.center(places, place) + _offset + Vector3(0, Orte.hoehe(etage(place)), 0)
## Punkt auf der Spur, der mindestens `abstand` hinter dem Menschen liegt.
func spur_punkt(abstand: float) -> Vector3:
for i in range(spur.size() - 1, -1, -1):
if (spur[i] as Vector3).distance_to(global_position) >= abstand:
return spur[i]
return spur[0] if not spur.is_empty() else global_position
## In welcher Etage dieser Mensch an einem Ort ist -- 0, wenn er dort nur unten ist.
func etage(ort: String) -> int:
return int((data.get("etage", {}) as Dictionary).get(ort, 0))
## Wege führen durchs Treppenhaus: aus der Etage runter zur Tür, und am Ziel
## von der Tür hinauf in die Etage.
func _mit_etagen(pts: Array, von: String, nach: String, war_unterwegs: bool) -> Array:
if pts.is_empty():
return pts
var e_von := etage(von)
if e_von > 0 and not war_unterwegs:
var runter: Array = [Orte.treppe(places, von, e_von)]
runter.append_array(Orte.treppen_weg(places, von, e_von, 0))
runter.append(Orte.innen_tuer(places, von))
runter.append_array(pts)
pts = runter
var e_nach := etage(nach)
if e_nach > 0:
pts.pop_back()
pts.append(Orte.innen_tuer(places, nach))
pts.append(Orte.treppe(places, nach, 0))
pts.append_array(Orte.treppen_weg(places, nach, 0, e_nach))
pts.append(Orte.center(places, nach) + _offset + Vector3(0, Orte.hoehe(e_nach), 0))
return pts
## Kopfbedeckung nach Tagestyp des Berufs -- so unterscheiden sich die Leute
## schon aus der Ferne.
func _hut(kopf: Vector3, c: Color) -> void:
@@ -180,10 +167,10 @@ func _process(roh: float) -> void:
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)
_path = Orte.route_voll(places, place, soll[0], _offset,
data.get("etage", {}), netz, anker, war_unterwegs)
if war_unterwegs and _path.size() > 1:
_path.remove_at(0) # steht schon draußen, alte Tür überspringen
_path = _mit_etagen(_path, place, soll[0], war_unterwegs)
place = soll[0]
activity = "unterwegs"
elif _path.is_empty() and activity != soll[1]:
@@ -213,6 +200,10 @@ func _process(roh: float) -> void:
_haltung(delta, not _path.is_empty())
if boden.is_valid():
global_position.y = float(boden.call(global_position.x, global_position.z))
if spur.is_empty() or (spur.back() as Vector3).distance_to(global_position) > 0.4:
spur.append(global_position)
if spur.size() > 120:
spur.pop_front()
_visual.rotation.z = lerp_angle(_visual.rotation.z, PI * 0.5 if activity == "schlaeft" else 0.0,
1.0 - pow(0.01, delta))

View File

@@ -122,10 +122,12 @@ func _orte() -> void:
var cf := c + Vector3(0, Orte.hoehe(f), 0)
var ton := farbe.lightened(0.06 * float(f))
waende.append(Prims.room(eb, cf, groesse, ton, farbe.darkened(0.35),
Orte.DIR_VEC[pl["dir"]] if f == 0 else Vector3.ZERO, tuer, true))
Orte.DIR_VEC[pl["dir"]] if f == 0 else Vector3.ZERO, tuer, f == 0))
if f > 0:
_decke(eb, key, cf, groesse, farbe.darkened(0.35))
_fenster(eb, cf, Vector2(groesse.x, groesse.z), ton)
_einrichtung(eb, cf, groesse, farbe, key if f == 0 or HumanDB.ETAGEN_ARBEIT.has(key)
else "heim_")
else "heim_", key)
if f + 1 < n:
_treppe(eb, key, f)
var licht := OmniLight3D.new()
@@ -159,51 +161,75 @@ func _fenster(parent: Node, cf: Vector3, g: Vector2, ton: Color) -> void:
glas, false)
## Eine Rampe von Podest zu Podest -- die Treppe, die die Bewohner hochgehen.
## Ein Lauf von Podest zu Podest -- die Treppe, die Bewohner und Geist
## hochgehen. Sie trägt, deshalb mit Kollision.
func _treppe(parent: Node, key: String, f: int) -> void:
var a := Orte.treppe(HumanDB.PLACES, key, f)
var b := Orte.treppe(HumanDB.PLACES, key, f + 1)
var d := b - a
var stufe := Prims.box(parent, (a + b) * 0.5, Vector3(1.2, 0.18, d.length()),
Color(0.55, 0.50, 0.46), false)
var lauf := Orte.treppen_lauf(HumanDB.PLACES, key, f)
var von: Vector3 = lauf[0]
var bis: Vector3 = lauf[1]
var d := bis - von
var stufe := Prims.box(parent, (von + bis) * 0.5, Vector3(1.6, 0.18, d.length()),
Color(0.55, 0.50, 0.46))
stufe.basis = Basis.looking_at(d.normalized(), Vector3.UP)
Prims.box(parent, b + Vector3(0, -0.09, 0), Vector3(1.4, 0.18, 1.4), Color(0.55, 0.50, 0.46), false)
var r := Orte.treppen_rahmen(HumanDB.PLACES, key)
var podest := Orte.treppe(HumanDB.PLACES, key, f + 1)
var mass := Orte._xz(r["a"], Orte.PODEST * 2.0, Orte.TREPPE_TIEFE - 0.8)
Prims.box(parent, podest + Vector3(0, -0.09, 0), Vector3(mass.x, 0.18, mass.y),
Color(0.55, 0.50, 0.46))
## Ein paar Möbel, damit man sieht, wofür ein Raum da ist.
func _einrichtung(parent: Node, c: Vector3, groesse: Vector3, farbe: Color, key: String) -> void:
## Der Boden einer oberen Etage lässt über dem Treppenhaus ein Loch frei --
## sonst stieße, wer die Treppe hochkommt, mit dem Kopf an die Decke.
func _decke(parent: Node, key: String, cf: Vector3, _groesse: Vector3, farbe: Color) -> void:
for platte in Orte.treppen_decke(HumanDB.PLACES, key):
var mitte: Vector3 = platte[0]
var mass: Vector2 = platte[1]
mitte.y = cf.y - 0.1
Prims.box(parent, mitte, Vector3(mass.x, 0.2, mass.y), farbe)
## Ein paar Möbel, damit man sieht, wofür ein Raum da ist. Nichts steht im
## Treppenstreifen.
func _einrichtung(parent: Node, c: Vector3, groesse: Vector3, farbe: Color, key: String,
ort: String = "") -> void:
if ort == "":
ort = key
var w := maxf(0.5, groesse.x * 0.5 - 2.0)
var h := maxf(0.5, groesse.z * 0.5 - 2.0)
var frei := func(v: Vector3, halb: float) -> Vector3:
var p := Orte.frei_von_treppe(HumanDB.PLACES, ort, c + v, halb)
p.y = c.y + v.y
return p
if key.begins_with("heim_"):
Prims.box(parent, c + Vector3(-w * 0.6, 0.35, -h * 0.5), Vector3(1.9, 0.7, 3.0),
Prims.box(parent, frei.call(Vector3(-w * 0.6, 0.35, -h * 0.5), 1.5), Vector3(1.9, 0.7, 3.0),
Color(0.85, 0.85, 0.90))
Prims.box(parent, c + Vector3(w * 0.6, 0.75, 0), Vector3(1.5, 0.15, 2.2),
Prims.box(parent, frei.call(Vector3(w * 0.6, 0.75, 0), 1.1), Vector3(1.5, 0.15, 2.2),
farbe.darkened(0.5))
Prims.box(parent, c + Vector3(w * 0.6, 1.05, 0), Vector3(0.8, 0.55, 0.1),
Prims.box(parent, frei.call(Vector3(w * 0.6, 1.05, 0), 0.1), Vector3(0.8, 0.55, 0.1),
Color(0.2, 0.25, 0.3))
elif key == "cafe":
for i in range(3):
Prims.cyl(parent, c + Vector3(-w * 0.5 + float(i) * w * 0.5, 0.4, 0.4), 0.7, 0.8,
Prims.cyl(parent, frei.call(Vector3(-w * 0.5 + float(i) * w * 0.5, 0.4, 0.4), 0.7), 0.7, 0.8,
Color(0.55, 0.40, 0.30))
Prims.box(parent, c + Vector3(0, 0.55, -h * 0.7), Vector3(groesse.x - 3.0, 1.1, 0.8),
Prims.box(parent, frei.call(Vector3(0, 0.55, -h * 0.7), 0.4), Vector3(groesse.x - 3.0, 1.1, 0.8),
Color(0.45, 0.32, 0.24))
elif key == "buero":
for i in range(3):
Prims.box(parent, c + Vector3(-w * 0.5 + float(i) * w * 0.5, 0.75, 0),
Prims.box(parent, frei.call(Vector3(-w * 0.5 + float(i) * w * 0.5, 0.75, 0), 0.8),
Vector3(1.6, 0.15, 1.2), Color(0.75, 0.75, 0.78))
elif key == "baeckerei":
Prims.box(parent, c + Vector3(0, 0.55, h * 0.5), Vector3(groesse.x - 3.0, 1.1, 1.0),
Prims.box(parent, frei.call(Vector3(0, 0.55, h * 0.5), 0.5), Vector3(groesse.x - 3.0, 1.1, 1.0),
Color(0.70, 0.55, 0.35))
Prims.box(parent, c + Vector3(0, 0.85, -h * 0.6), Vector3(2.6, 1.7, 1.2),
Prims.box(parent, frei.call(Vector3(0, 0.85, -h * 0.6), 1.3), Vector3(2.6, 1.7, 1.2),
Color(0.4, 0.4, 0.42))
elif key == "uni":
for i in range(3):
Prims.box(parent, c + Vector3(0, 0.5, -h * 0.6 + float(i) * h * 0.6),
Prims.box(parent, frei.call(Vector3(0, 0.5, -h * 0.6 + float(i) * h * 0.6), 0.35),
Vector3(groesse.x - 3.0, 0.2, 0.7), Color(0.66, 0.62, 0.58))
Prims.box(parent, c + Vector3(0, 1.1, -h), Vector3(groesse.x * 0.55, 1.6, 0.15),
Prims.box(parent, frei.call(Vector3(0, 1.1, -h), 0.1), Vector3(groesse.x * 0.55, 1.6, 0.15),
Color(0.15, 0.25, 0.20))
elif key == "werkstatt":
Prims.box(parent, c + Vector3(0, 0.6, 0), Vector3(groesse.x - 4.0, 1.2, 1.4),
Prims.box(parent, frei.call(Vector3(0, 0.6, 0), 0.7), Vector3(groesse.x - 4.0, 1.2, 1.4),
Color(0.52, 0.48, 0.46))

View File

@@ -59,7 +59,6 @@ func _setup_input() -> void:
"links": [KEY_A, KEY_LEFT],
"rechts": [KEY_D, KEY_RIGHT],
"steigen": [KEY_SPACE],
"sinken": [KEY_SHIFT],
"interagieren": [KEY_E],
"folgen": [KEY_F],
"notieren": [KEY_R],

View File

@@ -388,7 +388,9 @@ static func _sonderbauten_verteilen() -> void:
# Das Café gehört in die Mitte -- jeder Tagesablauf führt dorthin.
# Die übrigen Sonderbauten verteilen sich rundum.
var wunsch := Vector3.ZERO
if str(SONDER[s][0]) != "cafe":
if str(SONDER[s][0]) == "werkstatt":
wunsch = Vector3(0, 0, 30.0) # kurze Mittagspause: das Café muss nah sein
elif str(SONDER[s][0]) != "cafe":
var w := TAU * float(s) / float(SONDER.size() - 1)
wunsch = Vector3(cos(w), 0, sin(w)) * 42.0
var idx: int = -1

View File

@@ -59,9 +59,13 @@ static func hoehe(e: int) -> float:
return float(e) * ETAGE
## Treppenhaus: die Ecke neben der Tür. Die Podeste wechseln je Etage die
## Seite, so läuft die Treppe im Zickzack an der Türwand hoch.
static func treppe(places: Dictionary, key: String, e: int) -> Vector3:
const TREPPE_LAUF := 6.0 # Abstand der Wendepodeste entlang der Wand
const TREPPE_TIEFE := 4.4 # Streifen an der Treppenwand: zwei Läufe nebeneinander
const PODEST := 0.9 # halbe Podestlänge
## Rahmen des Treppenhauses: an der längeren Wand, die nicht die Türwand ist.
## n zeigt von der Mitte zur Treppenwand, a läuft an ihr entlang.
static func treppen_rahmen(places: Dictionary, key: String) -> Dictionary:
var pl: Dictionary = places[key]
var c := center(places, key)
var d: Vector3 = DIR_VEC[pl["dir"]]
@@ -70,11 +74,98 @@ static func treppe(places: Dictionary, key: String, e: int) -> Vector3:
var halb_d: float = (float(size[1]) if quer_ist else float(size[0])) * 0.5
var halb_q: float = (float(size[0]) if quer_ist else float(size[1])) * 0.5
var quer := Vector3(-d.z, 0, d.x)
var p := c + d * (halb_d - 1.6) + quer * (halb_q - 1.4) - quer * (3.0 if e % 2 == 1 else 0.0)
if halb_q >= halb_d:
return {"c": c, "n": -d, "a": quer, "halb_n": halb_d, "halb_a": halb_q}
return {"c": c, "n": -quer, "a": -d, "halb_n": halb_q, "halb_a": halb_d}
static func _lauf(r: Dictionary) -> float:
return minf(TREPPE_LAUF, float(r["halb_a"]) * 2.0 - 2.8)
static func _podest_a(r: Dictionary, e: int) -> float:
var p0: float = float(r["halb_a"]) - 1.4
return p0 if e % 2 == 0 else p0 - _lauf(r)
## Wendepodest der Etage e: zwei Läufe liegen nebeneinander, das Podest
## verbindet sie. Der obere Lauf ist so nie die Decke des unteren.
static func treppe(places: Dictionary, key: String, e: int) -> Vector3:
var r := treppen_rahmen(places, key)
var p: Vector3 = r["c"] + r["n"] * (float(r["halb_n"]) - TREPPE_TIEFE * 0.5) \
+ r["a"] * _podest_a(r, e)
p.y = hoehe(e)
return p
## Ein Lauf: [Anfang auf Etage f, Ende auf Etage f + 1].
static func treppen_lauf(places: Dictionary, key: String, f: int) -> Array:
var r := treppen_rahmen(places, key)
var tiefe: float = float(r["halb_n"]) - (1.4 if f % 2 == 0 else 3.0)
var richtung := -1.0 if f % 2 == 0 else 1.0
var von: Vector3 = r["c"] + r["n"] * tiefe + r["a"] * (_podest_a(r, f) + richtung * PODEST)
var bis: Vector3 = r["c"] + r["n"] * tiefe + r["a"] * (_podest_a(r, f + 1) - richtung * PODEST)
von.y = hoehe(f)
bis.y = hoehe(f + 1)
return [von, bis]
## Wegpunkte durchs Treppenhaus von Etage a nach Etage b, ohne a selbst:
## Laufanfang, Laufende, Podest -- je Etage.
static func treppen_weg(places: Dictionary, key: String, a: int, b: int) -> Array:
var out: Array = []
if b > a:
for f in range(a, b):
var l := treppen_lauf(places, key, f)
out.append(l[0])
out.append(l[1])
out.append(treppe(places, key, f + 1))
else:
for f in range(a - 1, b - 1, -1):
var l := treppen_lauf(places, key, f)
out.append(l[1])
out.append(l[0])
out.append(treppe(places, key, f))
return out
## Deckenplatten einer oberen Etage: alles außer dem Treppenloch.
## Liefert [[mitte, groesse_xz], ...] ohne Höhe.
static func treppen_decke(places: Dictionary, key: String) -> Array:
var r := treppen_rahmen(places, key)
var c: Vector3 = r["c"]
var n: Vector3 = r["n"]
var a: Vector3 = r["a"]
var halb_n := float(r["halb_n"])
var halb_a := float(r["halb_a"])
var out: Array = []
# Hauptplatte: alles außer dem Streifen an der Treppenwand
out.append([c - n * (TREPPE_TIEFE * 0.5), _xz(a, halb_a * 2.0, halb_n * 2.0 - TREPPE_TIEFE)])
# Rest des Streifens jenseits des Lochs
var ende: float = _podest_a(r, 1) - PODEST
var rest := ende + halb_a
if rest > 0.3:
out.append([c + n * (halb_n - TREPPE_TIEFE * 0.5) + a * ((ende - halb_a) * 0.5),
_xz(a, rest, TREPPE_TIEFE)])
return out
## Kastenmaß in Weltachsen: laenge_a entlang a, laenge_n quer dazu.
static func _xz(a: Vector3, laenge_a: float, laenge_n: float) -> Vector2:
return Vector2(laenge_a, laenge_n) if absf(a.x) > 0.0 else Vector2(laenge_n, laenge_a)
## Rückt einen Punkt aus dem Treppenstreifen heraus (für Möbel).
static func frei_von_treppe(places: Dictionary, key: String, pos: Vector3, halb: float) -> Vector3:
var r := treppen_rahmen(places, key)
var n: Vector3 = r["n"]
var pn: float = (pos - r["c"]).dot(n)
var grenze: float = float(r["halb_n"]) - TREPPE_TIEFE - halb - 0.2
if pn > grenze:
pos -= n * (pn - grenze)
return pos
## Punkt gleich hinter der Tür, im Haus.
static func innen_tuer(places: Dictionary, key: String) -> Vector3:
var pl: Dictionary = places[key]
@@ -85,15 +176,42 @@ static func innen_tuer(places: Dictionary, key: String) -> Vector3:
return c + d * (halb - 1.2)
## Wegpunkte durchs Treppenhaus von Etage a nach Etage b, ohne a selbst.
static func treppen_weg(places: Dictionary, key: String, a: int, b: int) -> Array:
var out: Array = []
var schritt := 1 if b > a else -1
var e := a
while e != b:
e += schritt
out.append(treppe(places, key, e))
return out
## Ein Ort ist ein Gebäude, wenn er Grundriss und Tür hat.
static func gebaeude(places: Dictionary, key: String) -> bool:
return places.has(key) and float(places[key]["size"][0]) > 0.0 \
and DIR_VEC[places[key]["dir"]] != Vector3.ZERO
## Route mit allem, was ein Körper braucht: aus der Etage runter, gerade durch
## die Tür, über das Netz, durch die nächste Tür und die Treppe hinauf.
## `etagen` sagt, in welcher Etage der Mensch an welchem Ort ist.
static func route_voll(places: Dictionary, a: String, b: String, offset: Vector3,
etagen: Dictionary, netz: Netz = null, anker: Dictionary = {},
war_unterwegs: bool = false) -> Array:
var pts := route(places, a, b, offset, netz, anker)
if pts.is_empty():
return pts
if gebaeude(places, a) and not war_unterwegs:
var e_von := int(etagen.get(a, 0))
var runter: Array = []
if e_von > 0:
runter.append(treppe(places, a, e_von))
runter.append_array(treppen_weg(places, a, e_von, 0))
runter.append(innen_tuer(places, a))
runter.append(door(places, a))
runter.append_array(pts)
pts = runter
if gebaeude(places, b):
var e_nach := int(etagen.get(b, 0))
var ziel: Vector3 = pts.pop_back()
pts.append(door(places, b))
pts.append(innen_tuer(places, b))
if e_nach > 0:
pts.append(treppe(places, b, 0))
pts.append_array(treppen_weg(places, b, 0, e_nach))
ziel.y = hoehe(e_nach)
pts.append(ziel)
return pts
static func center(places: Dictionary, key: String) -> Vector3:

View File

@@ -9,8 +9,9 @@ const SPEED_TRAUM := 8.0
const SPEED_GEIST := 11.0
const GRAVITY := 24.0
const JUMP := 9.0
const FOLGE_ABSTAND := 2.2
const FOLGE_SPIEL := 1.5
const FOLGE_ABSTAND := 2.5 # so weit bleibt der Geist auf der Spur zurück
const LEINE := 14.0 # weiter weg reißt die Leine: er wird nachgezogen
const SCHWEBE := 0.25 # eine Handbreit über dem Boden
const PITCH := -0.96 # 55° von oben, fest wie in Diablo
const CAM_YAW := PI * 0.25 # Blick diagonal über den Bezirk
const ZOOM := [26.0, 40.0] # zwei Stufen, Mausrad
@@ -27,7 +28,6 @@ var arm: Node3D
var cam: Camera3D
var _dreh_rest := 0.0 # noch zu drehender Winkel (Q)
var _zoom_stufe := 0
var _schwebe_ziel := -1.0 # Etage, zu der der Geist gerade gleitet
var body: Node3D
var _schleier: MeshInstance3D
var _kern: MeshInstance3D
@@ -51,10 +51,10 @@ const FUNKEN_MAX := 6
func _ready() -> void:
var cs := CollisionShape3D.new()
var shape := CapsuleShape3D.new()
shape.radius = 0.45
shape.height = 1.9
shape.radius = 0.4
shape.height = 1.6
cs.shape = shape
cs.position.y = 0.95
cs.position.y = 0.8
add_child(cs)
body = Node3D.new()
@@ -107,18 +107,15 @@ func _ready() -> void:
func set_mode(m: int) -> void:
mode = m
folgt = null
collision_layer = 1
collision_mask = 1
if m == Mode.GEIST:
collision_layer = 0
collision_mask = 0
body.scale = Vector3.ONE * 0.6
_schleier_mat.albedo_color.a = 0.3
else:
collision_layer = 1
collision_mask = 1
body.scale = Vector3.ONE * 0.8
_schleier_mat.albedo_color.a = 0.55
velocity = Vector3.ZERO
_schwebe_ziel = -1.0
func _unhandled_input(event: InputEvent) -> void:
@@ -157,56 +154,16 @@ func _physics_process(delta: float) -> void:
elif dir.length() > 0.01:
folgt = null # eigene Bewegung löst die Verfolgung
else:
var rechts := Basis(Vector3.UP, yaw) * Vector3(1, 0, 0)
var schulter := folgt.global_position + Vector3(0, 1.4, 0) \
+ rechts * FOLGE_ABSTAND - blickrichtung() * 0.8
global_position = global_position.lerp(schulter, 1.0 - pow(0.004, delta))
# weich nachziehen, aber an der Leine: sonst reißt der Faden beim Gehen
var ab := global_position - schulter
if ab.length() > FOLGE_SPIEL:
global_position = schulter + ab.normalized() * FOLGE_SPIEL
velocity = Vector3.ZERO
body.rotation.y = lerp_angle(body.rotation.y, yaw, 1.0 - pow(0.001, delta))
_an_der_leine(delta)
return
if mode == Mode.GEIST:
var v := dir * SPEED_GEIST
velocity.x = lerpf(velocity.x, v.x, 1.0 - pow(0.001, delta))
velocity.z = lerpf(velocity.z, v.z, 1.0 - pow(0.001, delta))
# Steigen und Sinken wechseln die Etage: der Geist gleitet ein Stockwerk
if look_enabled:
var von: float = _schwebe_ziel if _schwebe_ziel >= 0.0 else global_position.y
if Input.is_action_just_pressed("steigen"):
_schwebe_ziel = clampf(von + Orte.ETAGE, 0.4, 45.0)
if Input.is_action_just_pressed("sinken"):
_schwebe_ziel = clampf(von - Orte.ETAGE, 0.4, 45.0)
if _schwebe_ziel >= 0.0:
var dy := _schwebe_ziel - global_position.y
velocity.y = clampf(dy * 6.0, -SPEED_GEIST, SPEED_GEIST)
if absf(dy) < 0.05:
global_position.y = _schwebe_ziel
velocity.y = 0.0
_schwebe_ziel = -1.0
else:
velocity.y = 0.0
move_and_slide()
if global_position.y < 0.4:
global_position.y = 0.4
if global_position.y > 45.0:
global_position.y = 45.0
else:
velocity.x = lerpf(velocity.x, dir.x * SPEED_TRAUM, 1.0 - pow(0.0005, delta))
velocity.z = lerpf(velocity.z, dir.z * SPEED_TRAUM, 1.0 - pow(0.0005, delta))
if is_on_floor():
if look_enabled and Input.is_action_pressed("steigen"):
velocity.y = JUMP
else:
velocity.y = -1.0
else:
velocity.y -= GRAVITY * delta
move_and_slide()
if global_position.y < -400.0:
global_position.y = 2.0
var tempo := SPEED_GEIST if mode == Mode.GEIST else SPEED_TRAUM
velocity.x = lerpf(velocity.x, dir.x * tempo, 1.0 - pow(0.0005, delta))
velocity.z = lerpf(velocity.z, dir.z * tempo, 1.0 - pow(0.0005, delta))
_fallen(delta)
move_and_slide()
if global_position.y < -400.0:
global_position.y = 2.0
if dir.length() > 0.01:
var ziel := atan2(-dir.x, -dir.z)
@@ -217,6 +174,40 @@ func blickrichtung() -> Vector3:
return -Vector3(sin(yaw), 0, cos(yaw))
## Schwerkraft in beiden Welten. Springen kann nur der Läufer im Traum.
func _fallen(delta: float) -> void:
if is_on_floor():
if mode == Mode.TRAUM and look_enabled and Input.is_action_pressed("steigen"):
velocity.y = JUMP
else:
velocity.y = -1.0
else:
velocity.y -= GRAVITY * delta
## Folgen heißt: seiner Spur nachgehen, durch dieselben Türen und Treppen.
## Er läuft so schnell wie nötig, um dranzubleiben -- auch bei hohem Tempo.
func _an_der_leine(delta: float) -> void:
var mensch := folgt as Bewohner
var ziel: Vector3 = mensch.spur_punkt(FOLGE_ABSTAND)
var d := ziel - global_position
d.y = 0.0
var tempo := maxf(SPEED_GEIST, Bewohner.GEHTEMPO * Game.zeit_faktor() * 1.4)
if d.length() > 0.3:
var v := d.normalized() * minf(tempo, d.length() / delta)
velocity.x = v.x
velocity.z = v.z
body.rotation.y = lerp_angle(body.rotation.y, atan2(-d.x, -d.z), 1.0 - pow(0.001, delta))
else:
velocity.x = 0.0
velocity.z = 0.0
_fallen(delta)
move_and_slide()
if global_position.distance_to(mensch.global_position) > LEINE:
global_position = ziel # hängen geblieben: die Leine zieht ihn nach
velocity = Vector3.ZERO
## Schweben, Funken, Schweif und Modusfarbe -- der Zustand ist am Geist ablesbar.
func _beleben(delta: float) -> void:
var t := Time.get_ticks_msec() * 0.001
@@ -233,7 +224,7 @@ func _beleben(delta: float) -> void:
_kopf_mat.albedo_color = _farbe.lightened(0.4)
_kopf_mat.emission = _farbe.lightened(0.4)
body.position.y = sin(t * 1.6) * 0.08
body.position.y = (SCHWEBE if mode == Mode.GEIST else 0.0) + sin(t * 1.6) * 0.08
_kern.scale = Vector3.ONE * (1.0 + sin(t * 3.1) * 0.12)
var tempo_flach := Vector2(velocity.x, velocity.z).length()
var neigung := -0.16 * clampf(tempo_flach / SPEED_TRAUM, 0.0, 1.0)