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