update
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
extends Node3D
|
||||
class_name Human
|
||||
## Ein Mensch auf der Erde. Läuft seinen Tagesablauf ab und kann von einer
|
||||
## Technik erreicht werden -- oder eben nicht.
|
||||
class_name Bewohner
|
||||
## Bewohner einer Welt: läuft seinen Tagesablauf ab. Auf der Erde kann er von
|
||||
## einer Technik erreicht werden -- oder eben nicht.
|
||||
|
||||
const GEHTEMPO := 6.0
|
||||
|
||||
var data: Dictionary
|
||||
var idx: int = 0
|
||||
var place: String = ""
|
||||
var activity: String = "schlaeft"
|
||||
var activity: String = ""
|
||||
var resist_left: float = 0.0
|
||||
var places: Dictionary = {}
|
||||
var mit_mission := false
|
||||
|
||||
var _visual: Node3D
|
||||
var _label: Label3D
|
||||
@@ -22,21 +24,30 @@ var _bob: float = 0.0
|
||||
var _offset: Vector3
|
||||
|
||||
|
||||
func setup(d: Dictionary, index: int) -> void:
|
||||
func setup(d: Dictionary, index: int, orte: Dictionary) -> void:
|
||||
data = d
|
||||
idx = index
|
||||
name = "Human_" + str(d["id"])
|
||||
places = orte
|
||||
mit_mission = bool(d.get("mission", false))
|
||||
name = "Bewohner_" + str(d["id"])
|
||||
var winkel := float(index) * TAU / 4.0
|
||||
_offset = Vector3(cos(winkel), 0, sin(winkel)) * 2.2
|
||||
|
||||
_visual = Node3D.new()
|
||||
add_child(_visual)
|
||||
var c: Color = d["farbe"]
|
||||
var leuchtet := bool(d.get("leuchtet", false))
|
||||
var hoehe := 1.05 + float(index % 3) * 0.08
|
||||
_koerper = Prims.capsule(_visual, Vector3(0, hoehe * 0.62, 0), 0.32, hoehe, c)
|
||||
_kopf = Prims.sphere(_visual, Vector3(0, hoehe * 1.18 + 0.1, 0), 0.24, c.lightened(0.45))
|
||||
Prims.box(_visual, Vector3(0, hoehe * 0.95, 0), Vector3(0.9, 0.14, 0.3), c.darkened(0.25), false)
|
||||
Prims.box(_visual, Vector3(0, 0.06, 0), Vector3(0.5, 0.12, 0.7), c.darkened(0.45), false)
|
||||
if leuchtet:
|
||||
_koerper.material_override = Prims.mat(c, 0.9)
|
||||
_kopf.material_override = Prims.mat(c.lightened(0.45), 0.8)
|
||||
var reif := Prims.cyl(_visual, Vector3(0, hoehe * 0.5, 0), 0.62, 0.05,
|
||||
c.lightened(0.3), false, 1.6)
|
||||
reif.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
|
||||
_aura = Prims.cyl(self, Vector3(0, 0.05, 0), 0.95, 0.05, Color(1, 0.85, 0.35), false, 2.0)
|
||||
_aura.visible = false
|
||||
@@ -48,7 +59,7 @@ func setup(d: Dictionary, index: int) -> void:
|
||||
var start := _routine_at(Game.time_h)
|
||||
place = start[0]
|
||||
activity = start[1]
|
||||
global_position = HumanDB.place_center(place) + _offset
|
||||
global_position = Orte.center(places, place) + _offset
|
||||
|
||||
|
||||
func _routine_at(h: float) -> Array:
|
||||
@@ -66,7 +77,7 @@ func _process(delta: float) -> void:
|
||||
var soll := _routine_at(Game.time_h)
|
||||
if soll[0] != place:
|
||||
var war_unterwegs := not _path.is_empty()
|
||||
_path = HumanDB.route(place, soll[0], _offset)
|
||||
_path = Orte.route(places, place, soll[0], _offset)
|
||||
if war_unterwegs and _path.size() > 1:
|
||||
_path.remove_at(0) # steht schon draußen, alte Tür überspringen
|
||||
place = soll[0]
|
||||
@@ -97,8 +108,8 @@ func _process(delta: float) -> void:
|
||||
_visual.rotation.z = lerp_angle(_visual.rotation.z, PI * 0.5 if activity == "schlaeft" else 0.0,
|
||||
1.0 - pow(0.01, delta))
|
||||
|
||||
var erledigt: bool = Game.completed.has(data["id"])
|
||||
var aktiv: bool = Game.active_mission == data["id"]
|
||||
var erledigt: bool = mit_mission and Game.completed.has(data["id"])
|
||||
var aktiv: bool = mit_mission and Game.active_mission == data["id"]
|
||||
_aura.visible = aktiv or erledigt
|
||||
_strahl.visible = aktiv
|
||||
if _aura.visible:
|
||||
@@ -113,13 +124,17 @@ func _process(delta: float) -> void:
|
||||
|
||||
|
||||
func beschriftung() -> String:
|
||||
var s: String = data["name"] + "\n" + HumanDB.activity_text(activity)
|
||||
var s: String = data["name"]
|
||||
if data.has("rolle"):
|
||||
s += ", " + str(data["rolle"])
|
||||
s += "\n" + Orte.text(activity)
|
||||
if resist_left > 0.0:
|
||||
s += " (macht dicht)"
|
||||
if Game.completed.has(data["id"]):
|
||||
s += "\n★ Traum erfüllt"
|
||||
elif Game.active_mission == data["id"]:
|
||||
s += "\nTraum: %d %%" % int(Game.mission_progress(data["id"]))
|
||||
if mit_mission:
|
||||
if Game.completed.has(data["id"]):
|
||||
s += "\n★ Traum erfüllt"
|
||||
elif Game.active_mission == data["id"]:
|
||||
s += "\nTraum: %d %%" % int(Game.mission_progress(data["id"]))
|
||||
return s
|
||||
|
||||
|
||||
1
scripts/bewohner.gd.uid
Normal file
1
scripts/bewohner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://5jrbe4hct8jn
|
||||
100
scripts/dream_db.gd
Normal file
100
scripts/dream_db.gd
Normal file
@@ -0,0 +1,100 @@
|
||||
class_name DreamDB
|
||||
## Die Traumwelt: ein Dorf. Jedes Wesen hat ein Haus und einen Tagesablauf.
|
||||
|
||||
static var PLACES: Dictionary = {
|
||||
"haus_ailen": {
|
||||
"name": "Ailens Webstube", "pos": [-28.0, -22.0], "size": [12.0, 10.0],
|
||||
"dir": "back", "color": Color(0.72, 0.62, 0.92),
|
||||
},
|
||||
"haus_morr": {
|
||||
"name": "Morrs Kammer", "pos": [28.0, -22.0], "size": [12.0, 10.0],
|
||||
"dir": "back", "color": Color(0.58, 0.72, 0.90),
|
||||
},
|
||||
"haus_ivo": {
|
||||
"name": "Ivos Unterstand", "pos": [-28.0, 22.0], "size": [12.0, 10.0],
|
||||
"dir": "forward", "color": Color(0.88, 0.66, 0.80),
|
||||
},
|
||||
"haus_sela": {
|
||||
"name": "Selas Turmstube", "pos": [28.0, 22.0], "size": [12.0, 10.0],
|
||||
"dir": "forward", "color": Color(0.62, 0.82, 0.88),
|
||||
},
|
||||
"lichtung": {
|
||||
"name": "Die Lichtung", "pos": [0.0, 2.0], "size": [0.0, 0.0],
|
||||
"dir": "none", "color": Color(0.60, 0.50, 0.85),
|
||||
},
|
||||
"tor": {
|
||||
"name": "Tor zur Erde", "pos": [0.0, -34.0], "size": [0.0, 0.0],
|
||||
"dir": "none", "color": Color(0.50, 0.80, 1.0),
|
||||
},
|
||||
}
|
||||
|
||||
static var LIST: Array = [
|
||||
{
|
||||
"id": "ailen",
|
||||
"name": "Ailen",
|
||||
"rolle": "Traumweberin",
|
||||
"leuchtet": true,
|
||||
"farbe": Color(0.78, 0.55, 0.95),
|
||||
"routine": [
|
||||
[0, "haus_ailen", "webt"],
|
||||
[6, "lichtung", "ruht"],
|
||||
[8, "haus_ailen", "webt"],
|
||||
[14, "lichtung", "singt"],
|
||||
[16, "haus_ailen", "webt"],
|
||||
[20, "haus_ailen", "traeumt"],
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "morr",
|
||||
"name": "Morr",
|
||||
"rolle": "Erinnerungssammler",
|
||||
"leuchtet": true,
|
||||
"farbe": Color(0.45, 0.70, 0.95),
|
||||
"routine": [
|
||||
[0, "haus_morr", "traeumt"],
|
||||
[5, "lichtung", "sammelt"],
|
||||
[9, "haus_morr", "sortiert"],
|
||||
[15, "lichtung", "sammelt"],
|
||||
[19, "haus_morr", "sortiert"],
|
||||
[22, "haus_morr", "traeumt"],
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "ivo",
|
||||
"name": "Ivo",
|
||||
"rolle": "Bote",
|
||||
"leuchtet": true,
|
||||
"farbe": Color(0.95, 0.60, 0.75),
|
||||
"routine": [
|
||||
[0, "haus_ivo", "traeumt"],
|
||||
[4, "tor", "beobachtet"],
|
||||
[8, "lichtung", "wandert"],
|
||||
[10, "haus_ivo", "ruht"],
|
||||
[13, "tor", "beobachtet"],
|
||||
[18, "lichtung", "singt"],
|
||||
[21, "haus_ivo", "traeumt"],
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "sela",
|
||||
"name": "Sela",
|
||||
"rolle": "Wächterin des Tors",
|
||||
"leuchtet": true,
|
||||
"farbe": Color(0.55, 0.95, 0.90),
|
||||
"routine": [
|
||||
[0, "tor", "wacht"],
|
||||
[7, "lichtung", "ruht"],
|
||||
[9, "tor", "wacht"],
|
||||
[17, "haus_sela", "ruht"],
|
||||
[19, "lichtung", "singt"],
|
||||
[21, "haus_sela", "traeumt"],
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
static func by_id(id: String) -> Dictionary:
|
||||
for w in LIST:
|
||||
if w["id"] == id:
|
||||
return w
|
||||
return {}
|
||||
1
scripts/dream_db.gd.uid
Normal file
1
scripts/dream_db.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b1c86jtc5qd3h
|
||||
@@ -1,24 +1,21 @@
|
||||
extends Node3D
|
||||
class_name DreamWorld
|
||||
## Die Traumwelt: Insel mit drei betretbaren Gebäuden und dem Tor zur Erde.
|
||||
## Die Traumwelt: eine schwebende Insel mit einem Dorf. Vier Wesen wohnen hier,
|
||||
## jedes in seinem Haus, jedes mit eigenem Tagesablauf.
|
||||
|
||||
const INNEN_Y := -300.0
|
||||
var spawn := Vector3(0, 2.0, 20.0)
|
||||
var bewohner: Array = []
|
||||
|
||||
var spawn := Vector3(0, 2.0, 14.0)
|
||||
var _deko: Array = []
|
||||
var _regale: Dictionary = {} # tech_id -> MeshInstance3D
|
||||
var _stelen: Dictionary = {} # human_id -> Dictionary
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_environment()
|
||||
_insel()
|
||||
_gebaeude_bibliothek()
|
||||
_gebaeude_missionshalle()
|
||||
_gebaeude_meditation()
|
||||
_haeuser()
|
||||
_lichtung()
|
||||
_tor_zur_erde()
|
||||
Game.state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
_wesen()
|
||||
|
||||
|
||||
func _environment() -> void:
|
||||
@@ -35,19 +32,19 @@ func _environment() -> void:
|
||||
sky.sky_material = mat
|
||||
env.sky = sky
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
env.ambient_light_energy = 1.8
|
||||
env.ambient_light_energy = 2.1
|
||||
env.glow_enabled = true
|
||||
env.glow_intensity = 0.5
|
||||
env.glow_bloom = 0.15
|
||||
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.006
|
||||
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.15
|
||||
sun.light_energy = 1.5
|
||||
sun.light_color = Color(0.85, 0.80, 1.0)
|
||||
sun.shadow_enabled = true
|
||||
sun.shadow_blur = 2.0
|
||||
@@ -55,24 +52,23 @@ func _environment() -> void:
|
||||
|
||||
|
||||
func _insel() -> void:
|
||||
Prims.cyl(self, Vector3(0, -1.0, 0), 50.0, 2.0, Color(0.28, 0.20, 0.42))
|
||||
Prims.cyl(self, Vector3(0, -4.0, 0), 38.0, 4.0, Color(0.20, 0.14, 0.32), false)
|
||||
Prims.cyl(self, Vector3(0, -9.0, 0), 20.0, 6.0, Color(0.15, 0.10, 0.26), false)
|
||||
# Wege
|
||||
var wege: Array[Vector3] = [Vector3(-22, 0, -6), Vector3(0, 0, -12), Vector3(22, 0, -6),
|
||||
Vector3(0, 0, 8)]
|
||||
for w in wege:
|
||||
var d: Vector3 = w.normalized() * (w.length() * 0.5)
|
||||
var b := Prims.box(self, Vector3(d.x, 0.02, d.z),
|
||||
Vector3(3.0, 0.08, w.length()), Color(0.45, 0.35, 0.62), false)
|
||||
b.rotation.y = atan2(w.x, w.z)
|
||||
Prims.cyl(self, Vector3(0, -1.03, 0), 52.0, 2.0, Color(0.48, 0.40, 0.64))
|
||||
Prims.cyl(self, Vector3(0, -4.0, 0), 40.0, 4.0, Color(0.34, 0.26, 0.50), false)
|
||||
Prims.cyl(self, Vector3(0, -9.0, 0), 22.0, 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(16.0, 46.0)
|
||||
var p := Vector3(cos(a) * r, rng.randf_range(4.0, 22.0), sin(a) * r)
|
||||
var r := rng.randf_range(18.0, 48.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)
|
||||
@@ -80,175 +76,108 @@ func _insel() -> void:
|
||||
_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:
|
||||
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:
|
||||
var w := groesse.x * 0.5 - 1.8
|
||||
var h := groesse.z * 0.5 - 1.8
|
||||
# Schlafnest hat jedes Haus
|
||||
Prims.cyl(self, c + Vector3(-w, 0.3, -h), 1.4, 0.5, farbe.darkened(0.2), false)
|
||||
match key:
|
||||
"haus_ailen": # 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)
|
||||
"haus_morr": # 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)
|
||||
"haus_ivo": # 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
|
||||
"haus_sela": # 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)
|
||||
|
||||
|
||||
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), DreamDB.PLACES["lichtung"]["name"], 0.6,
|
||||
Color(1, 0.95, 0.85))
|
||||
|
||||
|
||||
func _tor_zur_erde() -> void:
|
||||
var pos := Orte.center(DreamDB.PLACES, "tor")
|
||||
Prims.cyl(self, pos + Vector3(0, 0.3, 0), 5.5, 0.6, Color(0.35, 0.30, 0.50))
|
||||
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))
|
||||
|
||||
var n := Node3D.new()
|
||||
n.position = pos + Vector3(0, 1.6, 0)
|
||||
add_child(n)
|
||||
n.add_to_group("interactable")
|
||||
n.set_meta("kind", "erde")
|
||||
n.set_meta("arg", "")
|
||||
n.set_meta("titel", "Tor zur Erde")
|
||||
|
||||
|
||||
func _wesen() -> void:
|
||||
for i in range(DreamDB.LIST.size()):
|
||||
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
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Bausteine
|
||||
|
||||
## Erzeugt Außenbau + Innenraum und verbindet beide mit Tür-Zonen.
|
||||
func _gebaeude(titel: String, a_center: Vector3, a_size: Vector3,
|
||||
i_center: Vector3, i_size: Vector3, farbe: Color) -> Node3D:
|
||||
Prims.room(self, a_center, a_size, farbe, farbe.darkened(0.3), Vector3.BACK, 3.6, false)
|
||||
Prims.box(self, a_center + Vector3(0, a_size.y + 0.25, 0),
|
||||
Vector3(a_size.x + 1.0, 0.5, a_size.z + 1.0), farbe.lightened(0.15))
|
||||
Prims.box(self, a_center + Vector3(0, a_size.y + 1.1, 0),
|
||||
Vector3(a_size.x * 0.5, 1.2, a_size.z * 0.5), farbe.lightened(0.3))
|
||||
Prims.label(self, a_center + Vector3(0, a_size.y + 3.0, 0), titel, 1.4,
|
||||
Color(1, 0.95, 0.8))
|
||||
|
||||
var aussen_tuer := a_center + Vector3(0, 0, a_size.z * 0.5)
|
||||
var draussen := a_center + Vector3(0, 0.2, a_size.z * 0.5 + 3.2)
|
||||
|
||||
var innen := Node3D.new()
|
||||
innen.name = titel.replace(" ", "_")
|
||||
add_child(innen)
|
||||
Prims.room(innen, i_center, i_size, farbe.lightened(0.12), farbe.darkened(0.25),
|
||||
Vector3.BACK, 3.6, true)
|
||||
Prims.box(innen, i_center + Vector3(0, i_size.y + 0.2, 0),
|
||||
Vector3(i_size.x, 0.4, i_size.z), farbe.darkened(0.4))
|
||||
for sx: float in [-1.0, 1.0]:
|
||||
for sz: float in [-1.0, 1.0]:
|
||||
var l := OmniLight3D.new()
|
||||
l.position = i_center + Vector3(sx * i_size.x * 0.28, i_size.y - 0.8, sz * i_size.z * 0.28)
|
||||
l.omni_range = 34.0
|
||||
l.light_energy = 4.5
|
||||
l.light_color = Color(0.90, 0.90, 1.0)
|
||||
innen.add_child(l)
|
||||
|
||||
var innen_spawn := i_center + Vector3(0, 0.3, i_size.z * 0.5 - 3.0)
|
||||
_tuer(self, aussen_tuer + Vector3(0, 1.2, 0), innen_spawn)
|
||||
_tuer(innen, i_center + Vector3(0, 1.2, i_size.z * 0.5), draussen)
|
||||
Prims.label(innen, i_center + Vector3(0, i_size.y - 0.9, i_size.z * 0.5 - 0.35), "Ausgang", 0.45,
|
||||
Color(1, 0.85, 0.6))
|
||||
return innen
|
||||
|
||||
|
||||
func _tuer(parent: Node, pos: Vector3, ziel: Vector3) -> void:
|
||||
var a := Area3D.new()
|
||||
a.position = pos
|
||||
var cs := CollisionShape3D.new()
|
||||
var sh := BoxShape3D.new()
|
||||
sh.size = Vector3(3.4, 2.4, 1.6)
|
||||
cs.shape = sh
|
||||
a.add_child(cs)
|
||||
parent.add_child(a)
|
||||
a.body_entered.connect(func(body: Node3D) -> void:
|
||||
if body is Player and Game.teleport_lock <= 0.0:
|
||||
Game.teleport_lock = 0.9
|
||||
body.global_position = ziel
|
||||
body.velocity = Vector3.ZERO)
|
||||
|
||||
|
||||
func _interaktion(parent: Node, pos: Vector3, kind: String, arg: String, titel: String) -> Node3D:
|
||||
var n := Node3D.new()
|
||||
n.position = pos
|
||||
parent.add_child(n)
|
||||
n.add_to_group("interactable")
|
||||
n.set_meta("kind", kind)
|
||||
n.set_meta("arg", arg)
|
||||
n.set_meta("titel", titel)
|
||||
return n
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Gebäude
|
||||
|
||||
func _gebaeude_bibliothek() -> void:
|
||||
var innen := _gebaeude("Bibliothek der Techniken",
|
||||
Vector3(-24, 0, -16), Vector3(16, 5, 14),
|
||||
Vector3(0, INNEN_Y, 0), Vector3(34, 6, 24),
|
||||
Color(0.58, 0.50, 0.80))
|
||||
Prims.label(innen, Vector3(0, INNEN_Y + 5.0, -10.5),
|
||||
"Berühre eine Technik, um ihre Hinweise zu lesen.", 0.4, Color(0.9, 0.9, 1))
|
||||
var ids := TechDB.ids()
|
||||
for i in range(ids.size()):
|
||||
var reihe := i / 6
|
||||
var spalte := i % 6
|
||||
var p := Vector3(-12.5 + spalte * 5.0, INNEN_Y, -6.5 + reihe * 7.0)
|
||||
var t := TechDB.by_id(ids[i])
|
||||
Prims.cyl(innen, p + Vector3(0, 0.5, 0), 0.8, 1.0, Color(0.30, 0.26, 0.45))
|
||||
var kristall := Prims.box(innen, p + Vector3(0, 1.8, 0), Vector3(0.9, 0.9, 0.9),
|
||||
t["farbe"], false, 1.4)
|
||||
kristall.rotation = Vector3(0.6, 0.8, 0.0)
|
||||
_regale[ids[i]] = kristall
|
||||
Prims.label(innen, p + Vector3(0, 3.2 + float(reihe) * 0.6, 0), t["name"], 0.36, Color(1, 1, 1))
|
||||
_interaktion(innen, p + Vector3(0, 1.2, 0), "technik", ids[i], t["name"])
|
||||
|
||||
|
||||
func _gebaeude_missionshalle() -> void:
|
||||
var innen := _gebaeude("Missionshalle",
|
||||
Vector3(0, 0, -28), Vector3(18, 5, 14),
|
||||
Vector3(90, INNEN_Y, 0), Vector3(26, 6, 20),
|
||||
Color(0.74, 0.55, 0.48))
|
||||
Prims.label(innen, Vector3(90, INNEN_Y + 5.0, -8.5),
|
||||
"Menschen, deren Traum feststeckt.", 0.42, Color(1, 0.92, 0.8))
|
||||
for i in range(HumanDB.LIST.size()):
|
||||
var h: Dictionary = HumanDB.LIST[i]
|
||||
var p := Vector3(90.0 + (float(i) - 1.5) * 7.0, INNEN_Y, -3.0)
|
||||
Prims.cyl(innen, p + Vector3(0, 0.4, 0), 1.2, 0.8, Color(0.35, 0.28, 0.26))
|
||||
var saeule := Prims.box(innen, p + Vector3(0, 2.0, 0), Vector3(1.4, 3.2, 1.4),
|
||||
h["farbe"], false, 0.8)
|
||||
var sphere := Prims.sphere(innen, p + Vector3(0, 4.1, 0), 0.55,
|
||||
(h["farbe"] as Color).lightened(0.4), 1.6)
|
||||
var lab := Prims.label(innen, p + Vector3(0, 5.2, 0), h["name"], 0.32, Color(1, 1, 1))
|
||||
_stelen[h["id"]] = {"saeule": saeule, "kugel": sphere, "label": lab}
|
||||
_interaktion(innen, p + Vector3(0, 1.5, 0), "mission", h["id"], h["name"])
|
||||
|
||||
|
||||
func _gebaeude_meditation() -> void:
|
||||
var innen := _gebaeude("Meditationskammer",
|
||||
Vector3(24, 0, -16), Vector3(14, 5, 14),
|
||||
Vector3(-90, INNEN_Y, 0), Vector3(20, 6, 18),
|
||||
Color(0.46, 0.66, 0.74))
|
||||
Prims.label(innen, Vector3(-90, INNEN_Y + 5.0, -7.5),
|
||||
"Übung vertieft, was du schon kannst.", 0.42, Color(0.85, 1, 1))
|
||||
Prims.cyl(innen, Vector3(-90, INNEN_Y + 0.5, -1.0), 3.0, 1.0, Color(0.24, 0.34, 0.40))
|
||||
var kugel := Prims.sphere(innen, Vector3(-90, INNEN_Y + 2.6, -1.0), 1.2,
|
||||
Color(0.55, 0.95, 1.0), 1.8)
|
||||
kugel.name = "Meditationskugel"
|
||||
_interaktion(innen, Vector3(-90, INNEN_Y + 1.5, -1.0), "vertiefen", "", "Meditationskugel")
|
||||
|
||||
|
||||
func _tor_zur_erde() -> void:
|
||||
var pos := Vector3(0, 0, 18)
|
||||
Prims.cyl(self, pos + Vector3(0, 0.3, 0), 5.0, 0.6, Color(0.35, 0.30, 0.50))
|
||||
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.7, Color(0.7, 0.95, 1))
|
||||
_interaktion(self, pos + Vector3(0, 1.6, 0), "erde", "", "Tor zur Erde")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Aktualisieren
|
||||
|
||||
func _refresh() -> void:
|
||||
for id in _regale:
|
||||
var m: MeshInstance3D = _regale[id]
|
||||
var t := TechDB.by_id(id)
|
||||
if Game.knows(id):
|
||||
m.material_override = Prims.mat(t["farbe"], 2.2)
|
||||
m.scale = Vector3.ONE * (1.0 + 0.12 * Game.skill_of(id))
|
||||
else:
|
||||
m.material_override = Prims.mat((t["farbe"] as Color).darkened(0.35), 0.5)
|
||||
m.scale = Vector3.ONE * 0.75
|
||||
for id in _stelen:
|
||||
var s: Dictionary = _stelen[id]
|
||||
var h := HumanDB.by_id(id)
|
||||
var lab: Label3D = s["label"]
|
||||
if Game.completed.has(id):
|
||||
s["kugel"].material_override = Prims.mat(Color(0.5, 1.0, 0.6), 2.4)
|
||||
lab.text = h["name"] + "\n★ erfüllt"
|
||||
elif Game.active_mission == id:
|
||||
s["kugel"].material_override = Prims.mat(Color(1, 0.85, 0.35), 2.4)
|
||||
lab.text = h["name"] + "\naktive Mission · %d %%" % int(Game.mission_progress(id))
|
||||
else:
|
||||
s["kugel"].material_override = Prims.mat((h["farbe"] as Color).lightened(0.4), 1.2)
|
||||
lab.text = h["name"] + "\nwartet"
|
||||
|
||||
@@ -44,7 +44,7 @@ func _environment() -> void:
|
||||
|
||||
|
||||
func _boden() -> void:
|
||||
Prims.box(self, Vector3(0, -0.5, 0), Vector3(240, 1, 240), Color(0.36, 0.52, 0.30))
|
||||
Prims.box(self, Vector3(0, -0.53, 0), Vector3(240, 1, 240), Color(0.36, 0.52, 0.30))
|
||||
# Ringweg, auf dem die Menschen gehen
|
||||
for i in range(36):
|
||||
var a := TAU * float(i) / 36.0
|
||||
@@ -57,16 +57,16 @@ func _orte() -> void:
|
||||
if key == "park":
|
||||
continue
|
||||
var pl: Dictionary = HumanDB.PLACES[key]
|
||||
var c := HumanDB.place_center(key)
|
||||
var c := Orte.center(HumanDB.PLACES, key)
|
||||
var s: Array = pl["size"]
|
||||
var groesse := Vector3(s[0], 2.3, s[1])
|
||||
var farbe: Color = pl["color"]
|
||||
var richtung: Vector3 = HumanDB.DIR_VEC[pl["dir"]]
|
||||
var richtung: Vector3 = Orte.DIR_VEC[pl["dir"]]
|
||||
Prims.room(self, c, groesse, farbe, farbe.darkened(0.35), richtung, 3.4, true)
|
||||
Prims.label(self, c + Vector3(0, 5.0, 0), pl["name"], 0.6, Color(1, 1, 0.9))
|
||||
_einrichtung(c, groesse, farbe, key)
|
||||
|
||||
var tuer := HumanDB.place_door(key)
|
||||
var tuer := Orte.door(HumanDB.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.6, 0.06, weg.length()), Color(0.62, 0.58, 0.50), false)
|
||||
@@ -152,9 +152,9 @@ func _saeule() -> void:
|
||||
|
||||
func _menschen() -> void:
|
||||
for i in range(HumanDB.LIST.size()):
|
||||
var m := Human.new()
|
||||
var m := Bewohner.new()
|
||||
add_child(m)
|
||||
m.setup(HumanDB.LIST[i], i)
|
||||
m.setup(HumanDB.LIST[i], i, HumanDB.PLACES)
|
||||
humans.append(m)
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func _setup_input() -> void:
|
||||
"steigen": [KEY_SPACE],
|
||||
"sinken": [KEY_SHIFT],
|
||||
"interagieren": [KEY_E],
|
||||
"kodex": [KEY_TAB],
|
||||
"journal": [KEY_J, KEY_TAB],
|
||||
}
|
||||
for action in binds:
|
||||
if not InputMap.has_action(action):
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
uid://let5f4ntq0j8
|
||||
@@ -1,5 +1,5 @@
|
||||
class_name HumanDB
|
||||
## Orte der Kleinstadt und die Menschen mit ihren Tagesabläufen.
|
||||
## Die Kleinstadt auf der Erde und die Menschen mit ihren Tagesabläufen.
|
||||
|
||||
## Gebäude liegen im Ring um den Park. Alle Türen zeigen zur Mitte.
|
||||
## pos = Standplatz drinnen, size = Grundfläche, dir = Richtung der Türlücke.
|
||||
@@ -42,31 +42,6 @@ static var PLACES: Dictionary = {
|
||||
},
|
||||
}
|
||||
|
||||
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 = {
|
||||
"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",
|
||||
}
|
||||
|
||||
static var LIST: Array = [
|
||||
{
|
||||
"id": "lena",
|
||||
@@ -76,6 +51,7 @@ static var LIST: Array = [
|
||||
"traum": "Ihr Bilderbuch endlich zu Ende zeichnen.",
|
||||
"problem": "Sie schiebt die letzten zwölf Seiten seit acht Monaten vor sich her. Jeder Entwurf kommt ihr zu schlecht vor, also fängt sie lieber gar nicht erst an.",
|
||||
"tags": ["prokrastination", "selbstzweifel", "ablenkung"],
|
||||
"mission": true,
|
||||
"farbe": Color(0.85, 0.45, 0.55),
|
||||
"routine": [
|
||||
[0, "haus_lena", "schlaeft"],
|
||||
@@ -100,6 +76,7 @@ static var LIST: Array = [
|
||||
"traum": "Wieder Klavier spielen wie mit zwanzig.",
|
||||
"problem": "Das Klavier steht zugestellt im Flur. Nach der Frühschicht ist er zu müde, um auch nur den Deckel zu öffnen.",
|
||||
"tags": ["gewohnheit", "erschoepfung", "ablenkung"],
|
||||
"mission": true,
|
||||
"farbe": Color(0.55, 0.45, 0.30),
|
||||
"routine": [
|
||||
[0, "haus_bruno", "schlaeft"],
|
||||
@@ -122,6 +99,7 @@ static var LIST: Array = [
|
||||
"traum": "Ihre Abschlussarbeit vortragen, ohne zu erstarren.",
|
||||
"problem": "Die Arbeit ist längst fertig. Aber vor jedem Vortrag wird ihr schwarz vor Augen, und sie meldet sich wieder ab.",
|
||||
"tags": ["angst", "ueberforderung", "selbstzweifel"],
|
||||
"mission": true,
|
||||
"farbe": Color(0.35, 0.60, 0.85),
|
||||
"routine": [
|
||||
[0, "haus_mira", "schlaeft"],
|
||||
@@ -145,6 +123,7 @@ static var LIST: Array = [
|
||||
"traum": "Abends wieder Zeit für seine Tochter haben.",
|
||||
"problem": "Er sagt zu jeder Bitte ja und sitzt dafür jeden Abend bis acht im Büro. Zu Hause ist er nur noch müde.",
|
||||
"tags": ["stress", "grenzen", "erschoepfung"],
|
||||
"mission": true,
|
||||
"farbe": Color(0.40, 0.55, 0.45),
|
||||
"routine": [
|
||||
[0, "haus_tobias", "schlaeft"],
|
||||
@@ -166,41 +145,3 @@ static func by_id(id: String) -> Dictionary:
|
||||
if h["id"] == id:
|
||||
return h
|
||||
return {}
|
||||
|
||||
|
||||
static func place_center(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 place_door(key: String) -> Vector3:
|
||||
var pl: Dictionary = PLACES[key]
|
||||
var c := place_center(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: aus der Tür, über den Platz, zur neuen Tür, hinein.
|
||||
static func route(a: String, b: String, offset: Vector3) -> Array:
|
||||
var pts: Array = []
|
||||
if a == b:
|
||||
return pts
|
||||
var da := place_door(a)
|
||||
var db := place_door(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(place_center(b) + offset)
|
||||
return pts
|
||||
|
||||
|
||||
static func activity_text(a: String) -> String:
|
||||
return ACTIVITY_TEXT.get(a, a)
|
||||
|
||||
@@ -100,18 +100,11 @@ func _fokus_suchen() -> void:
|
||||
func _hinweis_setzen() -> void:
|
||||
if _fokus_mensch != null:
|
||||
ui.setze_hinweis("[E] %s ansprechen — %s" %
|
||||
[_fokus_mensch.data["name"], HumanDB.activity_text(_fokus_mensch.activity)])
|
||||
[_fokus_mensch.data["name"], Orte.text(_fokus_mensch.activity)])
|
||||
elif _fokus != null:
|
||||
var kind: String = _fokus.get_meta("kind")
|
||||
var titel: String = _fokus.get_meta("titel")
|
||||
match kind:
|
||||
"technik":
|
||||
ui.setze_hinweis("[E] %s%s" % [titel,
|
||||
"" if not Game.knows(_fokus.get_meta("arg")) else " (gelernt)"])
|
||||
"mission":
|
||||
ui.setze_hinweis("[E] %s — Mission ansehen" % titel)
|
||||
"vertiefen":
|
||||
ui.setze_hinweis("[E] Meditieren")
|
||||
"erde":
|
||||
ui.setze_hinweis("[E] Zur Erde reisen")
|
||||
"traum":
|
||||
@@ -135,8 +128,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
player.look_enabled = true
|
||||
return
|
||||
if event.is_action_pressed("kodex"):
|
||||
ui.kodex_panel()
|
||||
if event.is_action_pressed("journal"):
|
||||
ui.journal_panel()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if event.is_action_pressed("interagieren"):
|
||||
@@ -151,14 +144,7 @@ func _interagieren() -> void:
|
||||
if _fokus == null:
|
||||
return
|
||||
var kind: String = _fokus.get_meta("kind")
|
||||
var arg: String = _fokus.get_meta("arg")
|
||||
match kind:
|
||||
"technik":
|
||||
ui.technik_panel(arg)
|
||||
"mission":
|
||||
ui.mission_panel(arg)
|
||||
"vertiefen":
|
||||
ui.vertiefen_panel()
|
||||
"erde":
|
||||
if Game.active_mission == "" and Game.completed.size() < HumanDB.LIST.size():
|
||||
Game.notified.emit(
|
||||
|
||||
75
scripts/orte.gd
Normal file
75
scripts/orte.gd
Normal file
@@ -0,0 +1,75 @@
|
||||
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",
|
||||
}
|
||||
|
||||
|
||||
static func text(a: String) -> String:
|
||||
return ACTIVITY_TEXT.get(a, a)
|
||||
|
||||
|
||||
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: aus der Tür, über den freien Platz, wieder hinein.
|
||||
static func route(places: Dictionary, a: String, b: String, offset: Vector3) -> Array:
|
||||
var pts: Array = []
|
||||
if a == b:
|
||||
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
|
||||
1
scripts/orte.gd.uid
Normal file
1
scripts/orte.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ck43yu1d5s856
|
||||
@@ -51,11 +51,15 @@ func _ready() -> void:
|
||||
pivot.position.y = 1.5
|
||||
add_child(pivot)
|
||||
arm = SpringArm3D.new()
|
||||
arm.position.x = 0.75
|
||||
arm.spring_length = 6.5
|
||||
arm.margin = 0.3
|
||||
arm.margin = 0.2
|
||||
var arm_form := SphereShape3D.new()
|
||||
arm_form.radius = 0.45 # Kugel statt Strahl: die Kamera klemmt nicht in Wänden
|
||||
arm.shape = arm_form
|
||||
pivot.add_child(arm)
|
||||
arm.add_excluded_object(get_rid())
|
||||
cam = Camera3D.new()
|
||||
cam.position.x = 0.7 # Blick über die Schulter, am Ende des Arms
|
||||
cam.fov = 74.0
|
||||
arm.add_child(cam)
|
||||
set_mode(Mode.TRAUM)
|
||||
|
||||
@@ -97,7 +97,7 @@ static func label(parent: Node, pos: Vector3, text: String, size: float = 0.35,
|
||||
l.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
l.no_depth_test = false
|
||||
l.modulate = c
|
||||
l.outline_size = 24
|
||||
l.outline_size = 10
|
||||
l.outline_modulate = Color(0, 0, 0, 0.85)
|
||||
l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
parent.add_child(l)
|
||||
@@ -112,7 +112,7 @@ static func room(parent: Node, center: Vector3, size: Vector3, wall_c: Color,
|
||||
var t := 0.4
|
||||
var h := size.y
|
||||
if with_floor:
|
||||
box(parent, center + Vector3(0, 0.04, 0), Vector3(size.x, 0.2, size.z), floor_c)
|
||||
box(parent, center + Vector3(0, -0.1, 0), Vector3(size.x, 0.2, size.z), floor_c)
|
||||
var seiten: Array[Vector3] = [Vector3.FORWARD, Vector3.BACK, Vector3.LEFT, Vector3.RIGHT]
|
||||
for d in seiten:
|
||||
var quer := absf(d.z) > 0.0 # Wand liegt entlang der X-Achse
|
||||
|
||||
@@ -154,7 +154,7 @@ static func tags_text(tags: Array) -> String:
|
||||
static func momente_text(momente: Array) -> String:
|
||||
var out: Array = []
|
||||
for m in momente:
|
||||
out.append(HumanDB.activity_text(m))
|
||||
out.append(Orte.text(m))
|
||||
return ", ".join(out)
|
||||
|
||||
|
||||
|
||||
167
scripts/ui.gd
167
scripts/ui.gd
@@ -18,6 +18,9 @@ var _titel: Label
|
||||
var _content: VBoxContainer
|
||||
var _scroll: ScrollContainer
|
||||
var _apply_human: Node3D
|
||||
var _journal_seite := "techniken"
|
||||
var _scroll_wunsch := 0
|
||||
var _scroll_frames := 0
|
||||
var _on_travel: Callable
|
||||
|
||||
|
||||
@@ -45,7 +48,7 @@ func _baue_hud() -> void:
|
||||
add_child(_hud)
|
||||
|
||||
var steuerung := Label.new()
|
||||
steuerung.text = "WASD Bewegen · Maus Umsehen · E Interagieren · Tab Kodex · Esc Maus frei"
|
||||
steuerung.text = "WASD Bewegen · Maus Umsehen · E Interagieren · J Journal · Esc Maus frei"
|
||||
steuerung.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||||
steuerung.position = Vector2(-640, 16)
|
||||
steuerung.size = Vector2(620, 24)
|
||||
@@ -130,6 +133,9 @@ func _process(_delta: float) -> void:
|
||||
var wunsch := _content.get_combined_minimum_size().y
|
||||
var platz := get_viewport().get_visible_rect().size.y - 220.0
|
||||
_scroll.custom_minimum_size.y = clampf(wunsch, 120.0, platz)
|
||||
if _scroll_frames > 0:
|
||||
_scroll.scroll_vertical = _scroll_wunsch
|
||||
_scroll_frames -= 1
|
||||
_panel.position = (get_viewport().get_visible_rect().size - _panel.size) * 0.5
|
||||
|
||||
|
||||
@@ -148,7 +154,7 @@ func _hud_aktualisieren() -> void:
|
||||
elif Game.completed.size() >= HumanDB.LIST.size():
|
||||
zeilen.append("[color=#88ff99]Alle Träume erfüllt.[/color]")
|
||||
else:
|
||||
zeilen.append("[color=#aaaaaa]Keine Mission. Missionshalle in der Traumwelt.[/color]")
|
||||
zeilen.append("[color=#aaaaaa]Keine Mission. Journal öffnen mit J.[/color]")
|
||||
_hud.text = "\n".join(zeilen)
|
||||
|
||||
|
||||
@@ -242,88 +248,96 @@ func _hinweis_block(t: Dictionary) -> String:
|
||||
[t["was"], t["wann"], t["warum"]])
|
||||
|
||||
|
||||
# -------------------------------------------------------- Bibliothek
|
||||
# -------------------------------------------------------- Journal
|
||||
|
||||
func technik_panel(id: String) -> void:
|
||||
var t := TechDB.by_id(id)
|
||||
_leeren(t["name"])
|
||||
_text(_hinweis_block(t))
|
||||
_trenner()
|
||||
_text("[color=#9fb6ff]Hilft bei:[/color] %s\n[color=#9fb6ff]Gute Momente:[/color] %s" %
|
||||
[TechDB.tags_text(t["tags"]), TechDB.momente_text(t["momente"])], 15)
|
||||
if Game.knows(id):
|
||||
_text("[color=#88ff99]Gelernt — Stufe %d (%s)[/color]" %
|
||||
[Game.skill_of(id), Game.xp_to_next(id)])
|
||||
## Das Journal ist immer dabei. Hier werden Techniken gelernt und vertieft
|
||||
## und Missionen angenommen.
|
||||
func journal_panel(seite: String = "") -> void:
|
||||
if seite != "":
|
||||
_journal_seite = seite
|
||||
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "journal" else 0
|
||||
_leeren("Journal")
|
||||
_reiter()
|
||||
if _journal_seite == "missionen":
|
||||
_journal_missionen()
|
||||
else:
|
||||
_text("[color=#ffcc66]Noch nicht gelernt. Kosten: 1 Einsicht (du hast %d).[/color]" %
|
||||
Game.einsicht)
|
||||
_knopf("Technik lernen (1 Einsicht)", func() -> void:
|
||||
if Game.learn(id):
|
||||
technik_panel(id), Game.einsicht >= 1)
|
||||
_zeigen("technik")
|
||||
_journal_techniken()
|
||||
_zeigen("journal")
|
||||
_scroll_wunsch = scroll_alt
|
||||
_scroll_frames = 4
|
||||
|
||||
|
||||
# -------------------------------------------------------- Missionshalle
|
||||
|
||||
func mission_panel(id: String) -> void:
|
||||
var h := HumanDB.by_id(id)
|
||||
_leeren("%s, %d — %s" % [h["name"], h["alter"], h["beruf"]])
|
||||
_text("[b]Traum:[/b] %s" % h["traum"])
|
||||
_text("[b]Was im Weg steht:[/b] %s" % h["problem"])
|
||||
_text("[color=#9fb6ff]Themen:[/color] %s" % TechDB.tags_text(h["tags"]), 15)
|
||||
func _reiter() -> void:
|
||||
var hb := HBoxContainer.new()
|
||||
hb.add_theme_constant_override("separation", 8)
|
||||
_content.add_child(hb)
|
||||
for eintrag in [["techniken", "Techniken"], ["missionen", "Missionen"]]:
|
||||
var b := Button.new()
|
||||
b.text = eintrag[1]
|
||||
b.custom_minimum_size = Vector2(0, 36)
|
||||
b.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
b.add_theme_font_size_override("font_size", 17)
|
||||
b.disabled = _journal_seite == eintrag[0]
|
||||
if b.disabled:
|
||||
b.add_theme_color_override("font_disabled_color", Color(1, 0.88, 0.55))
|
||||
hb.add_child(b)
|
||||
if not b.disabled:
|
||||
b.pressed.connect(func() -> void: journal_panel(eintrag[0]))
|
||||
var mission := "keine"
|
||||
if Game.active_mission != "":
|
||||
mission = "%s (%d %%)" % [HumanDB.by_id(Game.active_mission)["name"],
|
||||
int(Game.mission_progress(Game.active_mission))]
|
||||
_text("[color=#aaaaaa]Einsicht: [b]%d[/b] · Mission: %s[/color]" %
|
||||
[Game.einsicht, mission], 15)
|
||||
_trenner()
|
||||
if Game.completed.has(id):
|
||||
_text("[color=#88ff99]★ Der Traum ist erfüllt.[/color]")
|
||||
elif Game.active_mission == id:
|
||||
_text("[color=#ffdd77]Aktive Mission — %d %%[/color]" % int(Game.mission_progress(id)))
|
||||
else:
|
||||
_text("Fortschritt: %d %%" % int(Game.mission_progress(id)))
|
||||
_knopf("Mission annehmen", func() -> void:
|
||||
Game.accept_mission(id)
|
||||
mission_panel(id))
|
||||
_zeigen("mission")
|
||||
|
||||
|
||||
# -------------------------------------------------------- Meditation
|
||||
|
||||
func vertiefen_panel() -> void:
|
||||
_leeren("Meditationskammer")
|
||||
_text("Übung kostet 1 Einsicht und bringt 3 Erfahrungspunkte. Du hast [b]%d[/b] Einsicht." %
|
||||
Game.einsicht)
|
||||
func _journal_techniken() -> void:
|
||||
_text("Zwölf Wege, die Menschen wirklich weiterbringen. Jede Anwendung " +
|
||||
"draußen macht eine Technik von allein besser. Vertiefen geht auch hier.", 15)
|
||||
_trenner()
|
||||
if Game.known.is_empty():
|
||||
_text("[color=#ffcc66]Du kennst noch keine Technik. Geh in die Bibliothek.[/color]")
|
||||
for id in TechDB.ids():
|
||||
if not Game.knows(id):
|
||||
continue
|
||||
var t := TechDB.by_id(id)
|
||||
_text("[b]%s[/b] — Stufe %d (%s)" % [t["name"], Game.skill_of(id), Game.xp_to_next(id)], 16)
|
||||
_knopf("Vertiefen (1 Einsicht)", func() -> void:
|
||||
if Game.deepen(id):
|
||||
vertiefen_panel(), Game.einsicht >= 1)
|
||||
_zeigen("vertiefen")
|
||||
|
||||
|
||||
# -------------------------------------------------------- Kodex
|
||||
|
||||
func kodex_panel() -> void:
|
||||
_leeren("Kodex der Techniken")
|
||||
if Game.known.is_empty():
|
||||
_text("Du kennst noch keine Technik. In der Bibliothek der Traumwelt liegen zwölf.")
|
||||
for id in TechDB.ids():
|
||||
if not Game.knows(id):
|
||||
continue
|
||||
var t := TechDB.by_id(id)
|
||||
_text("[b][color=#ffe3a0]%s[/color][/b] · Stufe %d (%s)" %
|
||||
[t["name"], Game.skill_of(id), Game.xp_to_next(id)], 18)
|
||||
if Game.knows(id):
|
||||
_text("[b][color=#ffe3a0]%s[/color][/b] · Stufe %d (%s)" %
|
||||
[t["name"], Game.skill_of(id), Game.xp_to_next(id)], 18)
|
||||
else:
|
||||
_text("[b][color=#9aa0b8]%s[/color][/b] · noch nicht gelernt" % t["name"], 18)
|
||||
_text(_hinweis_block(t), 15)
|
||||
_text("[color=#9fb6ff]Hilft bei:[/color] %s [color=#9fb6ff]Momente:[/color] %s" %
|
||||
[TechDB.tags_text(t["tags"]), TechDB.momente_text(t["momente"])], 14)
|
||||
if Game.knows(id):
|
||||
_knopf("Vertiefen (1 Einsicht)", func() -> void:
|
||||
if Game.deepen(id):
|
||||
journal_panel(), Game.einsicht >= 1)
|
||||
else:
|
||||
_knopf("Lernen (1 Einsicht)", func() -> void:
|
||||
if Game.learn(id):
|
||||
journal_panel(), Game.einsicht >= 1)
|
||||
_trenner()
|
||||
|
||||
|
||||
func _journal_missionen() -> void:
|
||||
_text("Menschen, deren Traum feststeckt.", 15)
|
||||
_trenner()
|
||||
for h in HumanDB.LIST:
|
||||
var id: String = h["id"]
|
||||
_text("[b][color=#ffe3a0]%s[/color][/b], %d — %s" %
|
||||
[h["name"], h["alter"], h["beruf"]], 18)
|
||||
_text("[b]Traum:[/b] %s" % h["traum"], 16)
|
||||
_text("[b]Was im Weg steht:[/b] %s" % h["problem"], 15)
|
||||
_text("[color=#9fb6ff]Themen:[/color] %s" % TechDB.tags_text(h["tags"]), 14)
|
||||
if Game.completed.has(id):
|
||||
_text("[color=#88ff99]★ Der Traum ist erfüllt.[/color]", 15)
|
||||
elif Game.active_mission == id:
|
||||
_text("[color=#ffdd77]Aktive Mission — %d %%[/color]" %
|
||||
int(Game.mission_progress(id)), 15)
|
||||
else:
|
||||
_text("Fortschritt: %d %%" % int(Game.mission_progress(id)), 15)
|
||||
_knopf("Mission annehmen", func() -> void:
|
||||
Game.accept_mission(id)
|
||||
journal_panel())
|
||||
_trenner()
|
||||
var fehlend := TechDB.LIST.size() - Game.known.size()
|
||||
if fehlend > 0:
|
||||
_text("[color=#aaaaaa]%d weitere Techniken warten in der Bibliothek.[/color]" % fehlend, 15)
|
||||
_zeigen("kodex")
|
||||
|
||||
|
||||
# -------------------------------------------------------- Anwenden auf der Erde
|
||||
@@ -332,7 +346,7 @@ func anwenden_panel(mensch: Node3D) -> void:
|
||||
_apply_human = mensch
|
||||
var h: Dictionary = mensch.data
|
||||
var aktiv: String = mensch.activity
|
||||
_leeren("%s — %s" % [h["name"], HumanDB.activity_text(aktiv)])
|
||||
_leeren("%s — %s" % [h["name"], Orte.text(aktiv)])
|
||||
_text("[b]Traum:[/b] %s" % h["traum"], 16)
|
||||
if Game.active_mission == h["id"]:
|
||||
_text("Fortschritt: [b]%d %%[/b]" % int(Game.mission_progress(h["id"])), 16)
|
||||
@@ -404,11 +418,14 @@ func intro_panel(on_close: Callable) -> void:
|
||||
_text("Du bist ein Wesen aus der Traumwelt. Menschen tragen Träume mit sich herum, " +
|
||||
"die sie allein nicht bewegt bekommen. Du wirst zu ihnen geschickt.")
|
||||
_trenner()
|
||||
_text("[b]Dein Journal — Taste J[/b]\n" +
|
||||
"· Es ist immer dabei, in beiden Welten.\n" +
|
||||
"· [b]Techniken[/b] — lerne echte Lösungswege. Jede sagt dir, was sie tut, " +
|
||||
"wann sie passt und warum sie hilft. Anwenden macht sie von allein besser.\n" +
|
||||
"· [b]Missionen[/b] — nimm einen Menschen an, dem du helfen willst.")
|
||||
_text("[b]In der Traumwelt[/b]\n" +
|
||||
"· [b]Bibliothek der Techniken[/b] — lerne echte Lösungswege. Jede Technik sagt dir, " +
|
||||
"was sie tut, wann sie passt und warum sie hilft.\n" +
|
||||
"· [b]Missionshalle[/b] — nimm einen Menschen an, dem du helfen willst.\n" +
|
||||
"· [b]Meditationskammer[/b] — vertiefe, was du schon kannst.\n" +
|
||||
"· Ein Dorf auf einer schwebenden Insel. Vier Wesen wohnen hier, jedes in " +
|
||||
"seinem Haus, jedes mit eigenem Tagesablauf. Die Häuser stehen offen.\n" +
|
||||
"· [b]Tor zur Erde[/b] — reise hinab, sobald du eine Mission hast.")
|
||||
_text("[b]Auf der Erde[/b]\n" +
|
||||
"· Du bist ein unsichtbarer Geist und schwebst durch Wände. Leertaste hoch, Umschalt runter.\n" +
|
||||
|
||||
Reference in New Issue
Block a user