update
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
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.
|
||||
## Die Traumwelt: eine schwebende Insel, die ein Hügel ist. Unten der Dorfplatz
|
||||
## um den Traumbaum, der Hörsaal und das Tor; hangaufwärts wächst das Dorf,
|
||||
## ein Haus je erfülltem Traum, jedes eine Terrasse höher.
|
||||
|
||||
var spawn := Vector3(0, 2.0, 20.0)
|
||||
var bewohner: Array = []
|
||||
var spieler: Node3D = null # setzt main, für den Etagenschnitt
|
||||
var _bauten: Array = []
|
||||
|
||||
var _deko: Array = []
|
||||
var _lichter: Array = []
|
||||
var _freihalten: Array[Vector3] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_environment()
|
||||
_insel()
|
||||
_wege()
|
||||
_haeuser()
|
||||
_lehrpulte()
|
||||
_schwarzes_brett()
|
||||
@@ -22,146 +26,178 @@ func _ready() -> void:
|
||||
_wesen()
|
||||
|
||||
|
||||
## Dämmerung: tiefes Blau oben, warmes Lavendel am Horizont, Nebel im Tal.
|
||||
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
|
||||
mat.sky_top_color = Color(0.05, 0.05, 0.19)
|
||||
mat.sky_horizon_color = Color(0.58, 0.34, 0.56)
|
||||
mat.ground_bottom_color = Color(0.04, 0.03, 0.11)
|
||||
mat.ground_horizon_color = Color(0.30, 0.19, 0.42)
|
||||
mat.sun_angle_max = 30.0
|
||||
sky.sky_material = mat
|
||||
env.sky = sky
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
env.ambient_light_energy = 2.1
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
||||
env.ambient_light_color = Color(0.58, 0.50, 0.78)
|
||||
env.ambient_light_energy = 1.5
|
||||
env.glow_enabled = true
|
||||
env.glow_intensity = 0.32
|
||||
env.glow_bloom = 0.05
|
||||
env.glow_intensity = 0.42
|
||||
env.glow_bloom = 0.08
|
||||
env.fog_enabled = true
|
||||
env.fog_light_color = Color(0.30, 0.18, 0.45)
|
||||
env.fog_density = 0.0015
|
||||
env.fog_light_color = Color(0.46, 0.30, 0.56)
|
||||
env.fog_density = 0.0011
|
||||
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.rotation_degrees = Vector3(-34, 40, 0)
|
||||
sun.light_energy = 1.3
|
||||
sun.light_color = Color(1.0, 0.84, 0.74)
|
||||
sun.shadow_enabled = true
|
||||
sun.shadow_blur = 2.0
|
||||
sun.shadow_blur = 2.5
|
||||
add_child(sun)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ Insel
|
||||
|
||||
## Der Hügel als Höhenfeld: ein Gitter, das die Höhe von DreamDB abfragt --
|
||||
## innen eben, außen ansteigend, in jedem Gebäude eine Terrasse. Darauf läuft
|
||||
## der Spieler (Kollision) und die Bewohner (Bodenhöhe je Bild).
|
||||
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)
|
||||
var schritt := 1.5
|
||||
var n := int(ceil(radius * 2.0 / schritt)) + 7
|
||||
var halb := float(n - 1) * schritt * 0.5
|
||||
var daten := PackedFloat32Array()
|
||||
daten.resize(n * n)
|
||||
var hoehen: Array = []
|
||||
for iz in range(n):
|
||||
var zeile: PackedFloat32Array = PackedFloat32Array()
|
||||
zeile.resize(n)
|
||||
for ix in range(n):
|
||||
var x := -halb + float(ix) * schritt
|
||||
var z := -halb + float(iz) * schritt
|
||||
var r := Vector2(x, z).length()
|
||||
var h: float
|
||||
if r <= radius - 4.0:
|
||||
h = DreamDB.hoehe_bei(x, z)
|
||||
elif r <= radius:
|
||||
# der Rand rundet sich ab, statt abzubrechen
|
||||
var t := (r - (radius - 4.0)) / 4.0
|
||||
h = lerpf(DreamDB.hoehe_bei(x, z), -1.5, t * t)
|
||||
else:
|
||||
# die Kante fällt ab -- die Insel schwebt. Begrenzt, sonst hängt
|
||||
# das quadratische Gitter in den Ecken als Vorhang herab.
|
||||
h = maxf(-1.5 - (r - radius) * 2.4, -12.0)
|
||||
zeile[ix] = h
|
||||
daten[iz * n + ix] = h
|
||||
hoehen.append(zeile)
|
||||
|
||||
_wege()
|
||||
var st := SurfaceTool.new()
|
||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||
var tief := Color(0.40, 0.40, 0.62)
|
||||
var hoch := Color(0.70, 0.60, 0.88)
|
||||
var kante := Color(0.16, 0.12, 0.27)
|
||||
var eck := func(ix: int, iz: int) -> void:
|
||||
var x := -halb + float(ix) * schritt
|
||||
var z := -halb + float(iz) * schritt
|
||||
var h: float = hoehen[iz][ix]
|
||||
var hx0: float = hoehen[iz][maxi(ix - 1, 0)]
|
||||
var hx1: float = hoehen[iz][mini(ix + 1, n - 1)]
|
||||
var hz0: float = hoehen[maxi(iz - 1, 0)][ix]
|
||||
var hz1: float = hoehen[mini(iz + 1, n - 1)][ix]
|
||||
var nrm := Vector3(-(hx1 - hx0) / (2.0 * schritt), 1.0, -(hz1 - hz0) / (2.0 * schritt)).normalized()
|
||||
var c: Color = kante if h < 0.0 else tief.lerp(hoch, clampf(h / DreamDB.GIPFEL, 0.0, 1.0))
|
||||
st.set_color(c)
|
||||
st.set_normal(nrm)
|
||||
st.add_vertex(Vector3(x, h, z))
|
||||
for iz in range(n - 1):
|
||||
for ix in range(n - 1):
|
||||
var mx := -halb + (float(ix) + 0.5) * schritt
|
||||
var mz := -halb + (float(iz) + 0.5) * schritt
|
||||
if Vector2(mx, mz).length() > radius + 5.0:
|
||||
continue # außerhalb der Kante nichts zeichnen
|
||||
eck.call(ix, iz)
|
||||
eck.call(ix, iz + 1)
|
||||
eck.call(ix + 1, iz)
|
||||
eck.call(ix + 1, iz)
|
||||
eck.call(ix, iz + 1)
|
||||
eck.call(ix + 1, iz + 1)
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = st.commit()
|
||||
var m := StandardMaterial3D.new()
|
||||
m.vertex_color_use_as_albedo = true
|
||||
m.roughness = 0.92
|
||||
m.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mi.material_override = m
|
||||
add_child(mi)
|
||||
|
||||
# schwebende Deko
|
||||
var body := StaticBody3D.new()
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := HeightMapShape3D.new()
|
||||
shape.map_width = n
|
||||
shape.map_depth = n
|
||||
shape.map_data = daten
|
||||
cs.shape = shape
|
||||
cs.scale = Vector3(schritt, 1.0, schritt)
|
||||
body.add_child(cs)
|
||||
add_child(body)
|
||||
|
||||
# der Fels darunter -- klein, die Insel soll schweben, nicht stehen
|
||||
Prims.cyl(self, Vector3(0, -12.0, 0), radius * 0.52, 5.0, Color(0.17, 0.13, 0.28), false)
|
||||
Prims.cyl(self, Vector3(0, -17.0, 0), radius * 0.22, 6.0, Color(0.12, 0.09, 0.21), false)
|
||||
|
||||
# treibende Lichter statt schwebender Würfel
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = 7
|
||||
for i in range(34):
|
||||
for i in range(44):
|
||||
var a := rng.randf() * TAU
|
||||
var r := rng.randf_range(18.0, maxf(24.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)
|
||||
|
||||
|
||||
## Die gewachsenen Wege: je Kante ein flaches Band, an den Knoten ein Fleck,
|
||||
## damit Ecken nicht ausfransen.
|
||||
## Begrünung nach Regel, nicht von Hand: Poisson-Scheibe über der freien
|
||||
## Fläche. Nichts wächst auf einem Weg, in einem Haus oder auf dem Dorfplatz.
|
||||
func _gruen() -> void:
|
||||
_freihalten.append(Vector3(spawn.x, 0.0, spawn.z))
|
||||
var radius := Game.insel_radius() - 4.0
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = 91
|
||||
var gesetzt: Array[Vector3] = []
|
||||
var abstand := 9.0
|
||||
for versuch in range(1400):
|
||||
var a := rng.randf() * TAU
|
||||
var r := sqrt(rng.randf()) * radius
|
||||
var p := Vector3(cos(a) * r, 0.0, sin(a) * r)
|
||||
if p.length() < 20.0:
|
||||
continue
|
||||
var frei := true
|
||||
for g in gesetzt:
|
||||
if g.distance_to(p) < abstand:
|
||||
frei = false
|
||||
break
|
||||
if not frei:
|
||||
continue
|
||||
if not _freie_stelle(p, 3.2):
|
||||
continue
|
||||
gesetzt.append(p)
|
||||
_baum(p, rng)
|
||||
|
||||
|
||||
func _freie_stelle(p: Vector3, luft: float) -> bool:
|
||||
for frei in _freihalten:
|
||||
if p.distance_to(frei) < 7.0:
|
||||
return false
|
||||
for key in DreamDB.PLACES:
|
||||
var s: Array = DreamDB.PLACES[key]["size"]
|
||||
if float(s[0]) <= 0.0 or not Game.ort_offen(key):
|
||||
continue
|
||||
var c := Orte.center(DreamDB.PLACES, key)
|
||||
if absf(p.x - c.x) < float(s[0]) * 0.5 + luft \
|
||||
and absf(p.z - c.z) < float(s[1]) * 0.5 + luft:
|
||||
return false
|
||||
for k in DreamDB.NETZ.kanten:
|
||||
if Netz.trifft_rechteck(DreamDB.NETZ.knoten[k[0]], DreamDB.NETZ.knoten[k[1]],
|
||||
p, Vector2(luft * 2.0, luft * 2.0), 0.6):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## Traumbäume: schlanker Stamm, zwei versetzte Kronen, leuchtende Spitze.
|
||||
func _baum(p: Vector3, rng: RandomNumberGenerator) -> void:
|
||||
var hoehe := rng.randf_range(3.0, 6.0)
|
||||
# Grün gegen den lila Grund -- das macht die Insel erst gemütlich.
|
||||
var ton := Color.from_hsv(0.26 + rng.randf() * 0.12, 0.48 + rng.randf() * 0.18,
|
||||
0.58 + rng.randf() * 0.26)
|
||||
Prims.cyl(self, p + Vector3(0, hoehe * 0.5, 0), 0.24, hoehe,
|
||||
Color(0.34, 0.26, 0.30))
|
||||
var k1 := Prims.sphere(self, p + Vector3(0, hoehe + 0.4, 0),
|
||||
rng.randf_range(1.1, 2.0), ton)
|
||||
k1.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
if rng.randf() < 0.6:
|
||||
var k2 := Prims.sphere(self, p + Vector3(rng.randf_range(-0.8, 0.8),
|
||||
hoehe - 0.7, rng.randf_range(-0.8, 0.8)), rng.randf_range(0.8, 1.5),
|
||||
ton.lightened(0.12))
|
||||
k2.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
if rng.randf() < 0.3:
|
||||
Prims.sphere(self, p + Vector3(0, hoehe + 1.6, 0), 0.28,
|
||||
Color(0.9, 0.95, 1.0), 1.6)
|
||||
var r := rng.randf_range(10.0, maxf(24.0, radius - 4.0))
|
||||
var p := Vector3(cos(a) * r, 0, sin(a) * r)
|
||||
p.y = DreamDB.hoehe_bei(p.x, p.z) + rng.randf_range(2.5, 9.0)
|
||||
var c := Color(0.62 + rng.randf() * 0.3, 0.7 + rng.randf() * 0.25, 1.0)
|
||||
var licht := Prims.sphere(self, p, rng.randf_range(0.14, 0.34), c, 2.4)
|
||||
licht.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_lichter.append(licht)
|
||||
|
||||
|
||||
## Die gewachsenen Wege folgen dem Hang: jedes Band von Knoten zu Knoten ist
|
||||
## geneigt. An jedem zweiten Knoten eine Laterne.
|
||||
func _wege() -> void:
|
||||
var farbe := Color(0.66, 0.58, 0.84)
|
||||
var farbe := Color(0.70, 0.62, 0.86)
|
||||
for k in DreamDB.NETZ.kanten:
|
||||
var a: Vector3 = DreamDB.NETZ.knoten[k[0]]
|
||||
var b: Vector3 = DreamDB.NETZ.knoten[k[1]]
|
||||
a.y = DreamDB.hoehe_bei(a.x, a.z) + 0.04
|
||||
b.y = DreamDB.hoehe_bei(b.x, b.z) + 0.04
|
||||
var d := b - a
|
||||
if d.length() < 0.2:
|
||||
continue
|
||||
var band := Prims.box(self, (a + b) * 0.5 + Vector3(0, 0.03, 0),
|
||||
Vector3(2.6, 0.06, d.length()), farbe, false)
|
||||
band.rotation.y = atan2(d.x, d.z)
|
||||
var band := Prims.box(self, (a + b) * 0.5, Vector3(2.6, 0.06, d.length()), farbe, false)
|
||||
band.basis = Basis.looking_at(d.normalized(), Vector3.UP)
|
||||
var nr := 0
|
||||
for k in DreamDB.NETZ.knoten:
|
||||
Prims.cyl(self, k + Vector3(0, 0.035, 0), 1.3, 0.06, farbe, false)
|
||||
var p: Vector3 = k
|
||||
p.y = DreamDB.hoehe_bei(p.x, p.z)
|
||||
Prims.cyl(self, p + Vector3(0, 0.04, 0), 1.3, 0.06, farbe, false)
|
||||
if nr % 2 == 0 and Vector2(p.x, p.z).length() > 12.0:
|
||||
_laterne(p + Vector3(1.6, 0, 0))
|
||||
nr += 1
|
||||
|
||||
|
||||
## Eine Laterne: Pfahl und leuchtende Kugel. Kein eigenes Licht -- der Glow
|
||||
## trägt sie, und so dürfen es viele sein.
|
||||
func _laterne(p: Vector3) -> void:
|
||||
Prims.cyl(self, p + Vector3(0, 1.1, 0), 0.09, 2.2, Color(0.36, 0.30, 0.48), false)
|
||||
var k := Prims.sphere(self, p + Vector3(0, 2.35, 0), 0.24, Color(1.0, 0.86, 0.62), 2.6)
|
||||
k.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
|
||||
|
||||
## Jedes Gebäude steht auf seiner Terrasse: Wände, Dach, Laterne an der Tür,
|
||||
## Licht innen. Das Schild darüber.
|
||||
func _haeuser() -> void:
|
||||
for key in DreamDB.PLACES:
|
||||
var pl: Dictionary = DreamDB.PLACES[key]
|
||||
@@ -169,26 +205,29 @@ func _haeuser() -> void:
|
||||
if s[0] <= 0.0 or not Game.ort_offen(key):
|
||||
continue
|
||||
var c := Orte.center(DreamDB.PLACES, key)
|
||||
c.y = DreamDB.terrasse(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),
|
||||
var waende: Array = 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))
|
||||
var dach := Prims.dach(self, c, groesse, farbe)
|
||||
_bauten.append({"c": c, "size": Vector2(groesse.x, groesse.z), "hoch": groesse.y + 3.0,
|
||||
"etagen": [], "dach": [dach], "waende": [waende]})
|
||||
Prims.label(self, c + Vector3(0, groesse.y + 4.2, 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.omni_range = 16.0
|
||||
licht.light_energy = 1.8
|
||||
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)
|
||||
tuer.y = c.y
|
||||
var seite: Vector3 = Orte.DIR_VEC[pl["dir"]]
|
||||
var quer := Vector3(-seite.z, 0, seite.x)
|
||||
_laterne(tuer - seite * 1.2 + quer * 2.4)
|
||||
|
||||
|
||||
func _einrichtung(c: Vector3, groesse: Vector3, farbe: Color, key: String) -> void:
|
||||
@@ -250,6 +289,7 @@ func _lehrpulte() -> void:
|
||||
var tief: float = float(pl["size"][1])
|
||||
var innen: Vector3 = Orte.DIR_VEC[pl["dir"]] * -1.0
|
||||
var c := Orte.center(DreamDB.PLACES, key)
|
||||
c.y = DreamDB.terrasse(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))
|
||||
@@ -291,23 +331,48 @@ func _marke(pos: Vector3, kind: String, titel: String) -> Node3D:
|
||||
return n
|
||||
|
||||
|
||||
## Der Dorfplatz: der Traumbaum in der Mitte, Bänke darum, Laternen am Rand.
|
||||
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,
|
||||
Prims.cyl(self, c + Vector3(0, 0.14, 0), 3.4, 0.2, Color(0.30, 0.24, 0.40), false)
|
||||
|
||||
# der Traumbaum
|
||||
Prims.cyl(self, c + Vector3(0, 4.5, 0), 1.1, 9.0, Color(0.36, 0.26, 0.40))
|
||||
var kronen := [
|
||||
[Vector3(0, 9.6, 0), 3.6, Color(0.62, 0.55, 0.96)],
|
||||
[Vector3(1.6, 11.4, -0.8), 2.7, Color(0.70, 0.62, 1.0)],
|
||||
[Vector3(-1.4, 12.2, 1.0), 2.4, Color(0.55, 0.78, 1.0)],
|
||||
[Vector3(0.3, 14.0, 0.2), 1.7, Color(0.80, 0.90, 1.0)],
|
||||
]
|
||||
for k in kronen:
|
||||
var s := Prims.sphere(self, c + k[0], k[1], k[2], 1.3)
|
||||
s.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
var licht := OmniLight3D.new()
|
||||
licht.position = c + Vector3(0, 11.0, 0)
|
||||
licht.omni_range = 46.0
|
||||
licht.light_energy = 3.2
|
||||
licht.light_color = Color(0.72, 0.78, 1.0)
|
||||
add_child(licht)
|
||||
|
||||
for i in range(4):
|
||||
var a := TAU * float(i) / 4.0 + 0.4
|
||||
var p := c + Vector3(cos(a) * 5.6, 0.42, sin(a) * 5.6)
|
||||
var bank := Prims.box(self, p, Vector3(2.6, 0.16, 0.7), Color(0.46, 0.38, 0.58))
|
||||
bank.rotation.y = -a
|
||||
Prims.box(self, p + Vector3(0, -0.2, 0), Vector3(0.4, 0.3, 0.6), Color(0.36, 0.30, 0.48), false)
|
||||
for i in range(4):
|
||||
var a := TAU * float(i) / 4.0
|
||||
_laterne(c + Vector3(cos(a) * 8.8, 0, sin(a) * 8.8))
|
||||
Prims.label(self, c + Vector3(0, 16.0, 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
|
||||
pos.y = DreamDB.hoehe_bei(pos.x, pos.z)
|
||||
# flach und ohne Kollision wie der Platz — eine 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
|
||||
@@ -317,24 +382,96 @@ func _tor_zur_erde() -> void:
|
||||
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")
|
||||
|
||||
|
||||
## Begrünung nach Regel, nicht von Hand: Poisson-Scheibe über der freien
|
||||
## Fläche. Nichts wächst auf einem Weg, in einem Haus oder auf dem Dorfplatz.
|
||||
func _gruen() -> void:
|
||||
_freihalten.append(Vector3(spawn.x, 0.0, spawn.z))
|
||||
var radius := Game.insel_radius() - 4.0
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = 91
|
||||
var gesetzt: Array[Vector3] = []
|
||||
var abstand := 9.0
|
||||
for versuch in range(1400):
|
||||
var a := rng.randf() * TAU
|
||||
var r := sqrt(rng.randf()) * radius
|
||||
var p := Vector3(cos(a) * r, 0.0, sin(a) * r)
|
||||
if p.length() < 20.0:
|
||||
continue
|
||||
var frei := true
|
||||
for g in gesetzt:
|
||||
if g.distance_to(p) < abstand:
|
||||
frei = false
|
||||
break
|
||||
if not frei:
|
||||
continue
|
||||
if not _freie_stelle(p, 3.2):
|
||||
continue
|
||||
gesetzt.append(p)
|
||||
p.y = DreamDB.hoehe_bei(p.x, p.z)
|
||||
_baum(p, rng)
|
||||
|
||||
|
||||
func _freie_stelle(p: Vector3, luft: float) -> bool:
|
||||
for frei in _freihalten:
|
||||
if p.distance_to(frei) < 7.0:
|
||||
return false
|
||||
for key in DreamDB.PLACES:
|
||||
var s: Array = DreamDB.PLACES[key]["size"]
|
||||
if float(s[0]) <= 0.0 or not Game.ort_offen(key):
|
||||
continue
|
||||
var c := Orte.center(DreamDB.PLACES, key)
|
||||
if absf(p.x - c.x) < float(s[0]) * 0.5 + luft \
|
||||
and absf(p.z - c.z) < float(s[1]) * 0.5 + luft:
|
||||
return false
|
||||
for k in DreamDB.NETZ.kanten:
|
||||
if Netz.trifft_rechteck(DreamDB.NETZ.knoten[k[0]], DreamDB.NETZ.knoten[k[1]],
|
||||
p, Vector2(luft * 2.0, luft * 2.0), 0.6):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## Traumbäume: schlanker Stamm, zwei versetzte Kronen, leuchtende Spitze.
|
||||
func _baum(p: Vector3, rng: RandomNumberGenerator) -> void:
|
||||
var hoehe := rng.randf_range(3.0, 6.0)
|
||||
# Grün gegen den lila Grund -- das macht die Insel erst gemütlich.
|
||||
var ton := Color.from_hsv(0.26 + rng.randf() * 0.12, 0.48 + rng.randf() * 0.18,
|
||||
0.58 + rng.randf() * 0.26)
|
||||
Prims.cyl(self, p + Vector3(0, hoehe * 0.5, 0), 0.24, hoehe,
|
||||
Color(0.34, 0.26, 0.30))
|
||||
var k1 := Prims.sphere(self, p + Vector3(0, hoehe + 0.4, 0),
|
||||
rng.randf_range(1.1, 2.0), ton)
|
||||
k1.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
if rng.randf() < 0.6:
|
||||
var k2 := Prims.sphere(self, p + Vector3(rng.randf_range(-0.8, 0.8),
|
||||
hoehe - 0.7, rng.randf_range(-0.8, 0.8)), rng.randf_range(0.8, 1.5),
|
||||
ton.lightened(0.12))
|
||||
k2.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
if rng.randf() < 0.3:
|
||||
Prims.sphere(self, p + Vector3(0, hoehe + 1.6, 0), 0.28,
|
||||
Color(0.9, 0.95, 1.0), 1.6)
|
||||
|
||||
|
||||
## Am Anfang ist das Dorf leer; mit jedem erfüllten Traum zieht ein Wesen ein.
|
||||
## Jedes läuft auf dem Hügel -- der Boden sagt ihm, wie hoch.
|
||||
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.boden = Callable(DreamDB, "hoehe_bei")
|
||||
w.setup(DreamDB.LIST[i], i, DreamDB.PLACES, DreamDB.NETZ, DreamDB.ANKER)
|
||||
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
|
||||
if spieler != null:
|
||||
Prims.schnitt(_bauten, spieler.global_position, spieler.cam.global_position)
|
||||
var t := Time.get_ticks_msec() * 0.001
|
||||
for i in range(_lichter.size()):
|
||||
var n: Node3D = _lichter[i]
|
||||
n.position += Vector3(sin(t * 0.5 + i) * 0.35, cos(t * 0.7 + i * 0.3) * 0.25,
|
||||
cos(t * 0.4 + i * 0.5) * 0.35) * delta
|
||||
|
||||
Reference in New Issue
Block a user