Files
dreamer-game/scripts/orte.gd
2026-09-16 13:08:59 +02:00

308 lines
10 KiB
GDScript

class_name Orte
## Gemeinsame Geometrie für beide Welten: Orte, Türen, Wege, Tätigkeiten.
static var DIR_VEC: Dictionary = {
"back": Vector3(0, 0, 1),
"forward": Vector3(0, 0, -1),
"left": Vector3(-1, 0, 0),
"right": Vector3(1, 0, 0),
"none": Vector3(0, 0, 0),
}
static var ACTIVITY_TEXT: Dictionary = {
# Erde
"schlaeft": "schläft",
"morgenroutine": "Morgenroutine",
"arbeitet": "arbeitet",
"pause": "macht Pause",
"essen": "isst",
"gruebelt": "grübelt",
"ruht": "ruht sich aus",
"angespannt": "ist angespannt",
"abendroutine": "Abendroutine",
"aufraeumen": "räumt auf",
"wartet": "wartet",
"muede": "ist müde",
"unterwegs": "geht spazieren",
"zoegert": "zögert",
# Traumwelt
"webt": "webt Traumfäden",
"sammelt": "sammelt Erinnerungen",
"sortiert": "sortiert, was verloren ging",
"beobachtet": "schaut zur Erde hinunter",
"wacht": "hält Wache am Tor",
"singt": "singt",
"wandert": "wandert über die Insel",
"traeumt": "träumt",
}
## Blocker tragen ihren Namen im Ursachen-Katalog, alles andere hier.
static func text(a: String) -> String:
if Ursachen.ist_blocker(a):
return str(Ursachen.BLOCKER[a]["name"])
return str(ACTIVITY_TEXT.get(a, a))
static func kennt(a: String) -> bool:
return ACTIVITY_TEXT.has(a) or Ursachen.ist_blocker(a)
const ETAGE := 3.0 # Höhe eines Stockwerks
static func etagen(places: Dictionary, key: String) -> int:
return maxi(1, int(places[key].get("etagen", 1)))
static func hoehe(e: int) -> float:
return float(e) * ETAGE
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"]]
var size: Array = pl["size"]
var quer_ist := absf(d.z) > 0.0
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)
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
if halb_n * 2.0 - TREPPE_TIEFE > 0.2:
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]
var c := center(places, key)
var d: Vector3 = DIR_VEC[pl["dir"]]
var size: Array = pl["size"]
var halb: float = (float(size[1]) if absf(d.z) > 0.0 else float(size[0])) * 0.5
return c + d * (halb - 1.2)
## Ort, in dessen Grundriss der Punkt liegt -- oder "".
static func ort_bei(places: Dictionary, p: Vector3) -> String:
for key in places:
var s: Array = places[key]["size"]
if float(s[0]) <= 0.0:
continue
var c := center(places, key)
if absf(p.x - c.x) <= float(s[0]) * 0.5 and absf(p.z - c.z) <= float(s[1]) * 0.5:
return key
return ""
## Weg für einen Körper von einem freien Punkt zu einem Menschen: aus dem
## eigenen Haus heraus, über das Netz, durch seine Tür und in seine Etage.
static func route_zu(places: Dictionary, netz: Netz, von: Vector3, nach: Vector3,
nach_ort: String, nach_etage: int) -> Array:
var pts: Array = []
var start := von
var von_ort := ort_bei(places, von)
if von_ort != "" and gebaeude(places, von_ort):
var e := int(round(von.y / ETAGE))
if e > 0:
pts.append(treppe(places, von_ort, e))
pts.append_array(treppen_weg(places, von_ort, e, 0))
pts.append(innen_tuer(places, von_ort))
start = door(places, von_ort)
pts.append(start)
var drin := nach_ort != "" and gebaeude(places, nach_ort)
var ende := door(places, nach_ort) if drin else nach
if netz != null:
var a: int = int(netz.andocken(start)["knoten"])
var b: int = int(netz.andocken(ende)["knoten"])
for idx in netz.pfad(a, b):
pts.append(netz.knoten[idx])
if drin:
pts.append(door(places, nach_ort))
pts.append(innen_tuer(places, nach_ort))
if nach_etage > 0:
pts.append(treppe(places, nach_ort, 0))
pts.append_array(treppen_weg(places, nach_ort, 0, nach_etage))
var ziel := nach
if drin:
ziel.y = hoehe(nach_etage)
pts.append(ziel)
return pts
## 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:
var p: Array = places[key]["pos"]
return Vector3(p[0], 0.0, p[1])
## Punkt direkt vor der Türlücke, außerhalb des Gebäudes.
static func door(places: Dictionary, key: String) -> Vector3:
var pl: Dictionary = places[key]
var c := center(places, key)
var d: Vector3 = DIR_VEC[pl["dir"]]
if d == Vector3.ZERO:
return c
var size: Array = pl["size"]
var half: float = size[1] * 0.5 if absf(d.z) > 0.0 else size[0] * 0.5
return c + d * (half + 2.5)
## Route von Ort a nach Ort b. Gibt es ein Wegenetz, wird darauf gelaufen --
## sonst über den freien Platz in der Mitte.
static func route(places: Dictionary, a: String, b: String, offset: Vector3,
netz: Netz = null, anker: Dictionary = {}) -> Array:
var pts: Array = []
if a == b:
return pts
if netz != null and anker.has(a) and anker.has(b):
var weg := netz.pfad(int(anker[a]), int(anker[b]))
if not weg.is_empty():
for idx in weg:
var k: Vector3 = netz.knoten[idx]
if pts.is_empty() or pts[pts.size() - 1].distance_to(k) > 0.6:
pts.append(k)
pts.append(center(places, b) + offset)
return pts
var da := door(places, a)
var db := door(places, b)
pts.append(da)
if da.length() > 17.0:
pts.append(da.normalized() * 15.0)
if db.length() > 17.0:
pts.append(db.normalized() * 15.0)
pts.append(db)
pts.append(center(places, b) + offset)
return pts