extends Node3D class_name DreamWorld ## Die Traumwelt: eine schwebende Insel mit einem Dorf. Vier Wesen wohnen hier, ## jedes in seinem Haus, jedes mit eigenem Tagesablauf. var spawn := Vector3(0, 2.0, 20.0) var bewohner: Array = [] var _deko: Array = [] func _ready() -> void: _environment() _insel() _haeuser() _lehrpulte() _schwarzes_brett() _lichtung() _tor_zur_erde() _wesen() func _environment() -> void: var we := WorldEnvironment.new() var env := Environment.new() env.background_mode = Environment.BG_SKY var sky := Sky.new() var mat := ProceduralSkyMaterial.new() mat.sky_top_color = Color(0.10, 0.05, 0.24) mat.sky_horizon_color = Color(0.42, 0.24, 0.55) mat.ground_bottom_color = Color(0.06, 0.03, 0.14) mat.ground_horizon_color = Color(0.32, 0.18, 0.45) mat.sun_angle_max = 40.0 sky.sky_material = mat env.sky = sky env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY env.ambient_light_energy = 2.1 env.glow_enabled = true env.glow_intensity = 0.32 env.glow_bloom = 0.05 env.fog_enabled = true env.fog_light_color = Color(0.30, 0.18, 0.45) env.fog_density = 0.0015 we.environment = env add_child(we) var sun := DirectionalLight3D.new() sun.rotation_degrees = Vector3(-46, 25, 0) sun.light_energy = 1.5 sun.light_color = Color(0.85, 0.80, 1.0) sun.shadow_enabled = true sun.shadow_blur = 2.0 add_child(sun) func _insel() -> void: var radius := Game.insel_radius() Prims.cyl(self, Vector3(0, -1.03, 0), radius, 2.0, Color(0.48, 0.40, 0.64)) Prims.cyl(self, Vector3(0, -4.0, 0), radius * 0.77, 4.0, Color(0.34, 0.26, 0.50), false) Prims.cyl(self, Vector3(0, -9.0, 0), radius * 0.42, 6.0, Color(0.24, 0.17, 0.38), false) # Ringweg, auf dem die Wesen gehen for i in range(36): var a := TAU * float(i) / 36.0 Prims.box(self, Vector3(cos(a) * 15.0, 0.03, sin(a) * 15.0), Vector3(2.6, 0.06, 2.6), Color(0.66, 0.58, 0.84), false) # schwebende Deko var rng := RandomNumberGenerator.new() rng.seed = 7 for i in range(34): var a := rng.randf() * TAU var r := rng.randf_range(18.0, Game.insel_radius() - 4.0) var p := Vector3(cos(a) * r, rng.randf_range(5.0, 24.0), sin(a) * r) var s := rng.randf_range(0.6, 2.0) var c := Color(0.6 + rng.randf() * 0.3, 0.5 + rng.randf() * 0.4, 1.0) var wuerfel := Prims.box(self, p, Vector3(s, s, s), c, false, 1.1) wuerfel.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF _deko.append(wuerfel) func _haeuser() -> void: for key in DreamDB.PLACES: var pl: Dictionary = DreamDB.PLACES[key] var s: Array = pl["size"] if s[0] <= 0.0 or not Game.ort_offen(key): continue var c := Orte.center(DreamDB.PLACES, key) var groesse := Vector3(s[0], 3.2, s[1]) var farbe: Color = pl["color"] Prims.room(self, c, groesse, farbe, farbe.darkened(0.35), Orte.DIR_VEC[pl["dir"]], 3.4, true) Prims.label(self, c + Vector3(0, 5.0, 0), pl["name"], 0.6, Color(1, 0.95, 0.85)) _einrichtung(c, groesse, farbe, key) var licht := OmniLight3D.new() licht.position = c + Vector3(0, 2.4, 0) licht.omni_range = 18.0 licht.light_energy = 2.2 licht.light_color = farbe.lightened(0.5) add_child(licht) # Weg von der Tür zum Ring var tuer := Orte.door(DreamDB.PLACES, key) var weg := tuer.normalized() * 15.0 - tuer var b := Prims.box(self, (tuer + tuer.normalized() * 15.0) * 0.5 + Vector3(0, 0.03, 0), Vector3(2.4, 0.06, weg.length()), Color(0.66, 0.58, 0.84), false) b.rotation.y = atan2(weg.x, weg.z) func _einrichtung(c: Vector3, groesse: Vector3, farbe: Color, key: String) -> void: if key == "hoersaal": _hoersaal_einrichten(c, groesse, farbe, key) return var w := groesse.x * 0.5 - 1.8 var h := groesse.z * 0.5 - 1.8 Prims.cyl(self, c + Vector3(-w, 0.3, -h), 1.4, 0.5, farbe.darkened(0.2), false) match int(key.substr(5)) % 4: 0: # Webstuhl Prims.box(self, c + Vector3(w, 1.1, 0), Vector3(0.3, 2.2, 3.0), farbe.darkened(0.45)) for i in range(5): Prims.box(self, c + Vector3(w, 1.1, -1.2 + i * 0.6), Vector3(0.1, 1.8, 0.06), Color(0.9, 0.85, 1.0), false, 1.4) 1: # Regale voller Fundstücke for i in range(3): Prims.box(self, c + Vector3(w, 0.6 + i * 0.7, 0), Vector3(1.2, 0.12, 4.0), farbe.darkened(0.45)) for j in range(4): Prims.box(self, c + Vector3(w, 0.85 + i * 0.7, -1.5 + j * 1.0), Vector3(0.4, 0.4, 0.4), Color(0.8, 0.9, 1.0), false, 0.8) 2: # Karten und ein Fernrohr Prims.box(self, c + Vector3(w, 0.75, 0), Vector3(1.6, 0.12, 2.6), farbe.darkened(0.45)) var rohr := Prims.cyl(self, c + Vector3(w, 1.6, 0), 0.18, 2.0, Color(0.85, 0.9, 1.0), false, 0.6) rohr.rotation.x = 0.9 _: # Schale mit Licht Prims.cyl(self, c + Vector3(w, 0.5, 0), 1.0, 1.0, farbe.darkened(0.4), false) Prims.sphere(self, c + Vector3(w, 1.4, 0), 0.55, Color(0.8, 1.0, 1.0), 2.0) ## Bankreihen mit Mittelgang, Tafel an der Rückwand. func _hoersaal_einrichten(c: Vector3, groesse: Vector3, farbe: Color, key: String) -> void: var richtung: Vector3 = Orte.DIR_VEC[DreamDB.PLACES[key]["dir"]] * -1.0 var quer := absf(richtung.z) > 0.0 var bank := ((groesse.x if quer else groesse.z) - 5.0) * 0.5 var laengs: Vector3 = Vector3(1, 0, 0) if quer else Vector3(0, 0, 1) for i in range(3): var tief := -3.6 + float(i) * 1.8 for sx: float in [-1.0, 1.0]: var mitte: Vector3 = c + richtung * tief + laengs * (sx * (bank * 0.5 + 1.0)) var sitz: Vector3 = Vector3(bank, 0.25, 0.9) if quer else Vector3(0.9, 0.25, bank) var lehne: Vector3 = Vector3(bank, 0.9, 0.15) if quer else Vector3(0.15, 0.9, bank) Prims.box(self, mitte + Vector3(0, 0.5, 0), sitz, farbe.darkened(0.35)) Prims.box(self, mitte + richtung * 0.7 + Vector3(0, 0.9, 0), lehne, farbe.darkened(0.45)) var tafel: Vector3 = Vector3(groesse.x - 5.0, 2.0, 0.15) if quer \ else Vector3(0.15, 2.0, groesse.z - 5.0) Prims.box(self, c + richtung * 4.4 + Vector3(0, 1.4, 0), tafel, Color(0.16, 0.18, 0.30)) ## Vorn in jedem offenen Lehrort steht ein Pult. func _lehrpulte() -> void: for key in DreamDB.lehrorte(): if not Game.ort_offen(key) or DreamDB.techniken_von(key).is_empty(): continue var pl: Dictionary = DreamDB.PLACES[key] var tief: float = float(pl["size"][1]) var innen: Vector3 = Orte.DIR_VEC[pl["dir"]] * -1.0 var c := Orte.center(DreamDB.PLACES, key) var pos := c + innen * (tief * 0.5 - 2.6) var farbe: Color = pl["color"] Prims.box(self, pos + Vector3(0, 0.55, 0), Vector3(2.4, 1.1, 0.9), farbe.darkened(0.35)) Prims.box(self, pos + Vector3(0, 1.18, 0), Vector3(2.8, 0.16, 1.2), farbe.darkened(0.2)) Prims.sphere(self, pos + Vector3(0, 1.7, 0), 0.34, Color(0.85, 0.92, 1.0), 2.0) var lehrt: Array = DreamDB.techniken_von(key) Prims.label(self, pos + Vector3(0, 2.6, 0), TechDB.by_id(str(lehrt[0]))["name"], 0.42, Color(0.85, 0.92, 1)) var n := _marke(pos - innen * 1.8 + Vector3(0, 1.4, 0), "lehre", str(pl["name"])) n.set_meta("arg", key) ## An der Lichtung: hier hängen die Menschen aus, die Hilfe brauchen. func _schwarzes_brett() -> void: var pos := Orte.center(DreamDB.PLACES, "lichtung") + Vector3(-6.0, 0, 9.0) for s: float in [-1.4, 1.4]: Prims.cyl(self, pos + Vector3(s, 1.3, 0), 0.16, 2.6, Color(0.45, 0.38, 0.60)) Prims.box(self, pos + Vector3(0, 2.2, 0), Vector3(3.6, 2.4, 0.18), Color(0.18, 0.16, 0.26)) for i in range(HumanDB.LIST.size()): var x := -1.2 + float(i % 2) * 2.4 var y := 2.8 - float(i / 2) * 1.1 Prims.box(self, pos + Vector3(x, y, 0.12), Vector3(1.1, 0.8, 0.04), Color(0.92, 0.90, 0.80), false, 0.5) Prims.label(self, pos + Vector3(0, 4.2, 0), "Schwarzes Brett", 0.5, Color(1, 0.95, 0.85)) _marke(pos + Vector3(0, 1.6, 1.2), "brett", "Schwarzes Brett") ## Unsichtbarer Punkt, an dem die Interaktion hängt. func _marke(pos: Vector3, kind: String, titel: String) -> Node3D: var n := Node3D.new() n.position = pos add_child(n) n.add_to_group("interactable") n.set_meta("kind", kind) n.set_meta("arg", "") n.set_meta("titel", titel) return n func _lichtung() -> void: var c := Orte.center(DreamDB.PLACES, "lichtung") Prims.cyl(self, c + Vector3(0, 0.04, 0), 11.0, 0.12, Color(0.60, 0.52, 0.78), false) Prims.cyl(self, c + Vector3(0, 0.14, 0), 3.2, 0.2, Color(0.55, 0.80, 1.0, 0.5), false, 1.2) for i in range(8): var a := TAU * float(i) / 8.0 var p := c + Vector3(cos(a) * 8.5, 0, sin(a) * 8.5) Prims.cyl(self, p + Vector3(0, 1.6, 0), 0.28, 3.2, Color(0.72, 0.64, 0.88)) Prims.sphere(self, p + Vector3(0, 3.6, 0), 0.42, Color(0.75, 0.85, 1.0), 2.0) Prims.label(self, c + Vector3(0, 5.2, 0), str(DreamDB.PLACES["lichtung"]["name"]), 0.6, Color(1, 0.95, 0.85)) func _tor_zur_erde() -> void: var pos := Orte.center(DreamDB.PLACES, "tor") # flach und ohne Kollision wie die Lichtung — eine 0,6 hohe Stufe hätte den # Spieler vor dem Tor stehen lassen Prims.cyl(self, pos + Vector3(0, 0.05, 0), 5.5, 0.1, Color(0.35, 0.30, 0.50), false) for i in range(10): var a := PI * float(i) / 9.0 Prims.box(self, pos + Vector3(cos(a) * 4.2, sin(a) * 4.2 + 0.6, 0), Vector3(0.7, 0.7, 0.7), Color(0.5, 0.85, 1.0), false, 1.8) var scheibe := Prims.cyl(self, pos + Vector3(0, 2.6, 0), 3.0, 0.2, Color(0.45, 0.75, 1.0, 0.28), false, 1.0) scheibe.rotation.x = PI * 0.5 Prims.label(self, pos + Vector3(0, 6.4, 0), "Tor zur Erde", 0.8, Color(0.7, 0.95, 1)) _marke(pos + Vector3(0, 1.6, 0), "erde", "Tor zur Erde") ## Am Anfang ist das Dorf leer; mit jedem erfüllten Traum zieht ein Wesen ein. func _wesen() -> void: for i in range(DreamDB.LIST.size()): if not Game.wesen_eingezogen(i): continue var w := Bewohner.new() add_child(w) w.setup(DreamDB.LIST[i], i, DreamDB.PLACES) bewohner.append(w) func _process(delta: float) -> void: for i in range(_deko.size()): var n: Node3D = _deko[i] n.rotation.y += delta * (0.2 + 0.05 * (i % 5)) n.rotation.x += delta * 0.13 n.position.y += sin(Time.get_ticks_msec() * 0.0007 + i) * delta * 0.6