update
This commit is contained in:
@@ -30,6 +30,10 @@ var nav_map: RID # auf der Erde: Pfade vom Navigationsnetz
|
||||
var nav_fehl := 0 # wie oft das Netz keinen Pfad wusste (Rückfall aufs Wegenetz)
|
||||
var nav_fehl_info := ""
|
||||
var _bob: float = 0.0
|
||||
var _takt := 0.0 # Schild und Haltungsziele alle 0,3 s, nicht je Bild
|
||||
var _neigung_ziel := 0.0
|
||||
var _groesse_ziel := 1.0
|
||||
var _zappelt := false
|
||||
var _offset: Vector3
|
||||
|
||||
|
||||
@@ -127,19 +131,12 @@ func _hut(kopf: Vector3, c: Color) -> void:
|
||||
## Angst duckt, Erschöpfung beugt, Unruhe zappelt -- solange die Klemme nicht
|
||||
## überwunden ist. So sieht man Probleme beim Beobachten am Körper.
|
||||
func _haltung(delta: float, geht: bool) -> void:
|
||||
var id := str(data["id"])
|
||||
var offen: Dictionary = {}
|
||||
for k in data.get("probleme", []):
|
||||
if not Game.problem_ueberwunden(id, str(k)):
|
||||
offen[str(k)] = true
|
||||
var neigung := 0.38 if offen.has("erschoepfung") else 0.0
|
||||
var groesse := 0.86 if offen.has("angst") else 1.0
|
||||
var zappeln := 0.0
|
||||
if offen.has("unruhe") and not geht:
|
||||
zappeln = sin(Time.get_ticks_msec() * 0.011) * 0.06
|
||||
_ober.rotation.x = lerpf(_ober.rotation.x, -neigung, 1.0 - pow(0.02, delta))
|
||||
if _takt <= 0.0:
|
||||
_haltungsziele()
|
||||
var zappeln := sin(Time.get_ticks_msec() * 0.011) * 0.06 if _zappelt and not geht else 0.0
|
||||
_ober.rotation.x = lerpf(_ober.rotation.x, -_neigung_ziel, 1.0 - pow(0.02, delta))
|
||||
_ober.rotation.z = lerpf(_ober.rotation.z, zappeln, 1.0 - pow(0.001, delta))
|
||||
_ober.scale = _ober.scale.lerp(Vector3.ONE * groesse, 1.0 - pow(0.02, delta))
|
||||
_ober.scale = _ober.scale.lerp(Vector3.ONE * _groesse_ziel, 1.0 - pow(0.02, delta))
|
||||
var schwung := sin(_bob) * 0.7 if geht else 0.0
|
||||
for i in range(_arme.size()):
|
||||
var a: Node3D = _arme[i]
|
||||
@@ -147,6 +144,17 @@ func _haltung(delta: float, geht: bool) -> void:
|
||||
1.0 - pow(0.001, delta))
|
||||
|
||||
|
||||
func _haltungsziele() -> void:
|
||||
var id := str(data["id"])
|
||||
var offen: Dictionary = {}
|
||||
for k in data.get("probleme", []):
|
||||
if not Game.problem_ueberwunden(id, str(k)):
|
||||
offen[str(k)] = true
|
||||
_neigung_ziel = 0.38 if offen.has("erschoepfung") else 0.0
|
||||
_groesse_ziel = 0.86 if offen.has("angst") else 1.0
|
||||
_zappelt = offen.has("unruhe")
|
||||
|
||||
|
||||
## [Ort, Tätigkeit, Slotnummer] zur Stunde h. Steht in dem Slot ein System,
|
||||
## ist die Tätigkeit die Ersatzhandlung.
|
||||
func _routine_at(h: float) -> Array:
|
||||
@@ -217,11 +225,15 @@ func _process(roh: float) -> void:
|
||||
_aura.scale.x = 1.0 + sin(Time.get_ticks_msec() * 0.003) * 0.08
|
||||
_aura.scale.z = _aura.scale.x
|
||||
|
||||
_label.text = beschriftung()
|
||||
# Auf der Erde tragen nur Fokus, Verfolgte, Mission und wer unter der Maus
|
||||
# steht ein Schild -- sonst überdecken sich acht Namen im Café.
|
||||
_label.visible = (not mit_mission and not bool(data.get("stumm", false))) \
|
||||
or Game.markiert.has(str(data["id"]))
|
||||
if _takt <= 0.0:
|
||||
_takt = 0.3
|
||||
if _label.visible:
|
||||
_label.text = beschriftung()
|
||||
_takt -= delta
|
||||
_label.modulate = Color(0.55, 0.85, 0.6) if erledigt else (
|
||||
Color(1, 0.9, 0.6) if aktiv else Color(1, 1, 1))
|
||||
|
||||
|
||||
@@ -91,7 +91,8 @@ func _environment() -> void:
|
||||
sun.light_energy = 1.3
|
||||
sun.light_color = Color(1.0, 0.84, 0.74)
|
||||
sun.shadow_enabled = true
|
||||
sun.shadow_blur = 2.5
|
||||
sun.shadow_blur = 1.2
|
||||
sun.directional_shadow_max_distance = 110.0
|
||||
add_child(sun)
|
||||
|
||||
|
||||
@@ -245,9 +246,9 @@ func _haeuser() -> void:
|
||||
var waende: Array = Prims.room(self, c, groesse, farbe, farbe.darkened(0.35),
|
||||
Orte.DIR_VEC[pl["dir"]], 3.4, true)
|
||||
var dach := Prims.dach(self, c, groesse, farbe)
|
||||
var schild := Prims.label(self, c + Vector3(0, groesse.y + 4.2, 0), pl["name"], 0.6, Color(1, 0.95, 0.85))
|
||||
_bauten.append({"c": c, "size": Vector2(groesse.x, groesse.z), "hoch": groesse.y + 3.0,
|
||||
"teile": [dach], "waende": waende})
|
||||
Prims.label(self, c + Vector3(0, groesse.y + 4.2, 0), pl["name"], 0.6, Color(1, 0.95, 0.85))
|
||||
"teile": [dach, schild], "waende": waende})
|
||||
_einrichtung(c, groesse, farbe, key)
|
||||
|
||||
var licht := OmniLight3D.new()
|
||||
|
||||
@@ -7,7 +7,7 @@ var humans: Array = []
|
||||
var passanten: Array = [] # Statisten ohne Geschichte
|
||||
var spieler: Node3D = null # setzt main, für den Etagenschnitt
|
||||
var nav_map: RID # Navigationsnetz: Straßen, Türen, Möbel, Klötze
|
||||
const HOEHE := 3.0 # Raumhöhe; Klötze sind Vielfache davon
|
||||
const HOEHE := 3.0 # Raumhöhe -- alles in der Stadt ist Erdgeschoss
|
||||
const NAV_ZELLE_HOCH := 0.1
|
||||
var _bauten: Array = [] # alles, was der Etagenschnitt aufschneidet
|
||||
|
||||
@@ -66,9 +66,8 @@ func _environment() -> void:
|
||||
_sky = ProceduralSkyMaterial.new()
|
||||
sky.sky_material = _sky
|
||||
_env.sky = sky
|
||||
_env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
_env.glow_enabled = true
|
||||
_env.glow_intensity = 0.35
|
||||
_env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR # der Nachthimmel ist zu dunkel als Lichtquelle
|
||||
_env.glow_enabled = false # Tageslicht braucht kein Leuchten; kostet ein Fünftel der Bildrate
|
||||
_env.fog_enabled = true
|
||||
_env.fog_density = 0.0012
|
||||
we.environment = _env
|
||||
@@ -76,7 +75,8 @@ func _environment() -> void:
|
||||
|
||||
_sun = DirectionalLight3D.new()
|
||||
_sun.shadow_enabled = true
|
||||
_sun.shadow_blur = 1.5
|
||||
_sun.shadow_blur = 1.0
|
||||
_sun.directional_shadow_max_distance = 110.0 # die Kamera sieht 60 m weit; nah und scharf statt weit und weich
|
||||
add_child(_sun)
|
||||
|
||||
|
||||
@@ -132,17 +132,16 @@ func _bebauung() -> void:
|
||||
var g: Vector2 = p["size"]
|
||||
if minf(g.x, g.y) < 3.0:
|
||||
continue
|
||||
var n := 1 + rng.randi_range(0, 3)
|
||||
var hoehe := HOEHE * float(n)
|
||||
var ton := Color.from_hsv(fmod(0.07 + rng.randf() * 0.12, 1.0),
|
||||
0.10 + rng.randf() * 0.14, 0.62 + rng.randf() * 0.26)
|
||||
var hoehe := HOEHE # nur Erdgeschoss, wie alles in der Stadt
|
||||
# acht Töne statt achtzig: gleiche Farbe heißt gleiches Material
|
||||
var ton := Color.from_hsv(0.07 + float(rng.randi_range(0, 3)) * 0.04,
|
||||
0.10 + float(rng.randi_range(0, 1)) * 0.12, 0.62 + float(rng.randi_range(0, 3)) * 0.08)
|
||||
var bau := Node3D.new()
|
||||
add_child(bau)
|
||||
var klotz := Prims.box(bau, c + Vector3(0, hoehe * 0.5, 0), Vector3(g.x, hoehe, g.y), ton, false)
|
||||
# Kollision etwas größer: so schließt sich die Fuge zum Nachbarn, niemand keilt sich hinein
|
||||
Prims._add_box_body(klotz, Vector3(g.x + 0.9, hoehe, g.y + 0.9))
|
||||
for f in range(n):
|
||||
_fenster(bau, c + Vector3(0, HOEHE * float(f), 0), g, ton)
|
||||
_fenster(bau, c, g, ton)
|
||||
Prims.box(bau, c + Vector3(0, hoehe + 0.2, 0),
|
||||
Vector3(g.x + 0.5, 0.4, g.y + 0.5), ton.darkened(0.25))
|
||||
if rng.randf() < 0.35:
|
||||
@@ -167,7 +166,9 @@ func _orte() -> void:
|
||||
add_child(raum)
|
||||
var waende: Array = Prims.room(raum, c, groesse, farbe, farbe.darkened(0.35),
|
||||
Orte.DIR_VEC[pl["dir"]], tuer, true)
|
||||
_fenster(raum, c, Vector2(groesse.x, groesse.z), farbe)
|
||||
var band := Node3D.new() # verschwindet drinnen mit dem Dach
|
||||
add_child(band)
|
||||
_fenster(band, c, Vector2(groesse.x, groesse.z), farbe, true)
|
||||
_einrichtung(raum, c, groesse, farbe, key)
|
||||
var wer := HumanDB.bewohner_von(key)
|
||||
if not wer.is_empty():
|
||||
@@ -183,19 +184,24 @@ func _orte() -> void:
|
||||
add_child(dach)
|
||||
Prims.box(dach, c + Vector3(0, HOEHE + 0.1, 0),
|
||||
Vector3(groesse.x + 0.4, 0.2, groesse.z + 0.4), farbe.darkened(0.3))
|
||||
Prims.label(self, c + Vector3(0, HOEHE + 1.6, 0), str(pl["name"]), 0.55, Color(1, 1, 0.9))
|
||||
Prims.label(dach, c + Vector3(0, HOEHE + 1.6, 0), str(pl["name"]), 0.55, Color(1, 1, 0.9))
|
||||
_bauten.append({"c": c, "size": Vector2(groesse.x, groesse.z), "hoch": HOEHE,
|
||||
"teile": [dach], "waende": waende})
|
||||
"teile": [dach, band], "waende": waende, "raum": raum})
|
||||
|
||||
|
||||
## Ein Fensterband je Wand, damit man die Stockwerke von außen zählt.
|
||||
func _fenster(parent: Node, cf: Vector3, g: Vector2, ton: Color) -> void:
|
||||
## Ein Fensterband: bei Klötzen ein Kasten, der rundum eine Handbreit
|
||||
## übersteht (ein Netz statt vier); bei Häusern vier dünne Streifen außen an
|
||||
## den Wänden -- ein Kasten läge als Decke über dem Raum.
|
||||
func _fenster(parent: Node, cf: Vector3, g: Vector2, ton: Color, hohl: bool = false) -> void:
|
||||
var glas := ton.darkened(0.55).lerp(Color(0.35, 0.45, 0.6), 0.5)
|
||||
if minf(g.x, g.y) < 3.4:
|
||||
return
|
||||
if not hohl:
|
||||
Prims.box(parent, cf + Vector3(0, 1.75, 0), Vector3(g.x + 0.1, 0.9, g.y + 0.1), glas, false)
|
||||
return
|
||||
for d: Vector3 in [Vector3.FORWARD, Vector3.BACK, Vector3.LEFT, Vector3.RIGHT]:
|
||||
var quer := absf(d.z) > 0.0
|
||||
var laenge: float = (g.x if quer else g.y) - 2.4
|
||||
if laenge < 1.0:
|
||||
continue
|
||||
var off := Vector3(d.x * (g.x * 0.5 + 0.05), 1.75, d.z * (g.y * 0.5 + 0.05))
|
||||
Prims.box(parent, cf + off, Vector3(laenge, 0.9, 0.08) if quer else Vector3(0.08, 0.9, laenge),
|
||||
glas, false)
|
||||
@@ -390,7 +396,9 @@ func _himmel_aktualisieren() -> void:
|
||||
_sky.sky_horizon_color = nacht_hor.lerp(tag_hor, sonne)
|
||||
_sky.ground_horizon_color = nacht_hor.lerp(Color(0.55, 0.60, 0.55), sonne)
|
||||
_sky.ground_bottom_color = nacht_top.lerp(Color(0.30, 0.38, 0.28), sonne)
|
||||
_env.ambient_light_energy = 0.45 + sonne * 0.8
|
||||
# nachts mondblau statt schwarz: die Stadt bleibt lesbar, die Lichter setzen Akzente
|
||||
_env.ambient_light_color = Color(0.30, 0.34, 0.52).lerp(Color(0.62, 0.66, 0.74), sonne)
|
||||
_env.ambient_light_energy = 1.0 - sonne * 0.35 # tags übernimmt die Sonne, sonst wäscht es aus
|
||||
_env.fog_light_color = nacht_hor.lerp(tag_hor, sonne)
|
||||
|
||||
var nacht := 1.0 - sonne
|
||||
|
||||
@@ -15,6 +15,8 @@ var _erde_version := -1
|
||||
var auf_erde := false
|
||||
var _fokus: Node3D = null
|
||||
var _fokus_mensch: Node3D = null
|
||||
var _interaktive: Array = [] # Ansprechbares der aktiven Welt, einmal gesammelt
|
||||
var _fokus_takt := 0.0 # Fokus, Markierung und Hinweis alle 0,05 s
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -119,6 +121,8 @@ func _aktivieren(neu: Node3D) -> void:
|
||||
w.set("spieler", player if an else null)
|
||||
w.call("aktiv_setzen", an)
|
||||
welt = neu
|
||||
_interaktive = []
|
||||
_fokus_takt = 0.0
|
||||
Game.teleport_lock = 1.0
|
||||
_fokus = null
|
||||
_fokus_mensch = null
|
||||
@@ -134,6 +138,10 @@ func _process(delta: float) -> void:
|
||||
return
|
||||
Game.notiz_sperre = maxf(0.0, Game.notiz_sperre - delta)
|
||||
Game.situationen_ticken(dt, auf_erde and not Game.schlaeft_ziel())
|
||||
_fokus_takt -= delta
|
||||
if _fokus_takt > 0.0:
|
||||
return
|
||||
_fokus_takt = 0.05
|
||||
_fokus_suchen()
|
||||
_markieren()
|
||||
_dabeisein()
|
||||
@@ -177,9 +185,13 @@ func _fokus_suchen() -> void:
|
||||
_fokus_mensch = null
|
||||
var p := player.global_position
|
||||
var best := REICHWEITE_OBJEKT
|
||||
for n in get_tree().get_nodes_in_group("interactable"):
|
||||
if not welt.is_ancestor_of(n):
|
||||
continue # die andere Welt liegt im selben Raum
|
||||
if _interaktive.is_empty():
|
||||
for n in get_tree().get_nodes_in_group("interactable"):
|
||||
if welt.is_ancestor_of(n): # die andere Welt liegt im selben Raum
|
||||
_interaktive.append(n)
|
||||
for n in _interaktive:
|
||||
if not is_instance_valid(n):
|
||||
continue
|
||||
var d: float = p.distance_to((n as Node3D).global_position)
|
||||
if d < best:
|
||||
best = d
|
||||
|
||||
@@ -68,16 +68,18 @@ func _ready() -> void:
|
||||
# Schleier: oben schmal, unten weit, halb durchsichtig
|
||||
_schleier = Prims.cyl(body, Vector3(0, 1.05, 0), 0.6, 1.7, _farbe, false)
|
||||
(_schleier.mesh as CylinderMesh).top_radius = 0.24
|
||||
_schleier_mat = Prims.mat(Color(_farbe, 0.55), 0.35)
|
||||
_schleier_mat = Prims.mat(Color(_farbe, 0.55), 0.35).duplicate()
|
||||
_schleier.material_override = _schleier_mat
|
||||
_schleier.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
# Lichtkern im Schleier
|
||||
_kern = Prims.sphere(body, Vector3(0, 1.15, 0), 0.26, _farbe, 2.2)
|
||||
_kern_mat = _kern.material_override as StandardMaterial3D
|
||||
_kern_mat = (_kern.material_override as StandardMaterial3D).duplicate()
|
||||
_kern.material_override = _kern_mat
|
||||
_kern.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_kopf = Prims.sphere(body, Vector3(0, 2.0, 0), 0.3, _farbe.lightened(0.4), 0.6)
|
||||
_kopf.name = "Kopf"
|
||||
_kopf_mat = _kopf.material_override as StandardMaterial3D
|
||||
_kopf_mat = (_kopf.material_override as StandardMaterial3D).duplicate()
|
||||
_kopf.material_override = _kopf_mat
|
||||
for sx: float in [-1.0, 1.0]:
|
||||
var auge := Prims.box(body, Vector3(sx * 0.11, 2.04, -0.27), Vector3(0.07, 0.11, 0.05),
|
||||
Color(1, 1, 1), false, 3.0)
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
class_name Prims
|
||||
## Kleine Helfer, um die Welt aus Godot-Primitiven zusammenzusetzen.
|
||||
|
||||
## Gleiche Farbe, gleiches Material: tausend Klötze brauchen keine tausend
|
||||
## Materialien. Wer ein Material verändern will, dupliziert es sich.
|
||||
static var _materialien: Dictionary = {}
|
||||
|
||||
static func mat(c: Color, emis: float = 0.0, rough: float = 0.85) -> StandardMaterial3D:
|
||||
var key := "%s|%.2f|%.2f" % [str(c), emis, rough]
|
||||
if _materialien.has(key):
|
||||
return _materialien[key]
|
||||
var m := StandardMaterial3D.new()
|
||||
m.albedo_color = c
|
||||
m.roughness = rough
|
||||
@@ -12,6 +19,7 @@ static func mat(c: Color, emis: float = 0.0, rough: float = 0.85) -> StandardMat
|
||||
m.emission_energy_multiplier = emis
|
||||
if c.a < 1.0:
|
||||
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
_materialien[key] = m
|
||||
return m
|
||||
|
||||
|
||||
@@ -159,35 +167,44 @@ static func _wand(mi: MeshInstance3D, normale: Vector3) -> MeshInstance3D:
|
||||
return mi
|
||||
|
||||
|
||||
const STUMPF := 0.6 # so hoch bleiben die Wände, wenn man drinnen steht
|
||||
|
||||
## Schnitt wie bei den Sims: steht der Spieler in einem Bau, verschwinden Dach
|
||||
## (bei Klötzen der ganze Klotz) und die beiden Wände, die zur Kamera zeigen.
|
||||
## Ein Bau: {c, size, hoch, teile, waende}. Ein Bau zwischen Kamera und
|
||||
## Spieler wird durchsichtig, sonst verschwände der Spieler hinter jedem Nachbarhaus.
|
||||
static func schnitt(bauten: Array, spieler: Vector3, kamera: Vector3) -> void:
|
||||
## und Schild, und alle Wände des Hauses werden zu kniehohen Stümpfen gekürzt.
|
||||
## Nichts wird durchsichtig. Ein Bau: {c, size, hoch, teile, waende}.
|
||||
static func schnitt(bauten: Array, spieler: Vector3, _kamera: Vector3) -> void:
|
||||
for b in bauten:
|
||||
var c: Vector3 = b["c"]
|
||||
var g: Vector2 = b["size"]
|
||||
var drin: bool = absf(spieler.x - c.x) < g.x * 0.5 + 0.3 \
|
||||
and absf(spieler.z - c.z) < g.y * 0.5 + 0.3 \
|
||||
and spieler.y > c.y - 0.5 and spieler.y < c.y + float(b["hoch"]) + 0.5
|
||||
var kasten := AABB(Vector3(c.x - g.x * 0.5, c.y, c.z - g.y * 0.5),
|
||||
Vector3(g.x, float(b["hoch"]), g.y))
|
||||
var im_weg: bool = not drin \
|
||||
and kasten.intersects_segment(kamera, spieler + Vector3(0, 0.8, 0)) != null
|
||||
if bool(b.get("im_weg", false)) != im_weg:
|
||||
b["im_weg"] = im_weg
|
||||
if not b.has("meshes"):
|
||||
b["meshes"] = _meshes_von(b)
|
||||
for mi in b["meshes"]:
|
||||
(mi as GeometryInstance3D).transparency = 0.82 if im_weg else 0.0
|
||||
(mi as GeometryInstance3D).cast_shadow = \
|
||||
GeometryInstance3D.SHADOW_CASTING_SETTING_OFF if im_weg \
|
||||
else GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
||||
if drin == bool(b.get("drin", false)):
|
||||
continue # nur anfassen, wenn sich etwas ändert
|
||||
b["drin"] = drin
|
||||
for t in b["teile"]:
|
||||
(t as Node3D).visible = not drin
|
||||
for w in b["waende"]:
|
||||
var n: Vector3 = w.get_meta("normal")
|
||||
(w as Node3D).visible = not (drin and n.dot(kamera - c) > 0.0)
|
||||
_stumpf(w as MeshInstance3D, c.y, drin)
|
||||
|
||||
|
||||
## Eine Wand kürzen oder wiederherstellen. Was ganz oben sitzt (der Türsturz),
|
||||
## verschwindet drinnen ganz.
|
||||
static func _stumpf(w: MeshInstance3D, boden: float, kurz: bool) -> void:
|
||||
var bm := w.mesh as BoxMesh
|
||||
if bm == null:
|
||||
w.visible = not kurz
|
||||
return
|
||||
if not w.has_meta("voll"):
|
||||
w.set_meta("voll", Vector2(bm.size.y, w.position.y))
|
||||
var voll: Vector2 = w.get_meta("voll")
|
||||
var unten: float = voll.y - voll.x * 0.5 - boden
|
||||
if unten > 0.3:
|
||||
w.visible = not kurz
|
||||
return
|
||||
w.visible = true
|
||||
bm.size.y = STUMPF if kurz else voll.x
|
||||
w.position.y = (boden + STUMPF * 0.5) if kurz else voll.y
|
||||
|
||||
|
||||
## Ein Satteldach: ein geschlossenes Prisma über den Wänden. Der First läuft
|
||||
|
||||
@@ -144,9 +144,13 @@ func _baue_hud() -> void:
|
||||
|
||||
var steuerung := Label.new()
|
||||
steuerung.text = "J Journal · 1-3 Tempo · Q Kamera · Mausrad Zoom · Esc"
|
||||
# rechts verankert und nach links wachsend: sonst ragt der Text aus dem Fenster
|
||||
steuerung.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||||
steuerung.position = Vector2(-260, 16)
|
||||
steuerung.size = Vector2(240, 24)
|
||||
steuerung.grow_horizontal = Control.GROW_DIRECTION_BEGIN
|
||||
steuerung.offset_right = -16
|
||||
steuerung.offset_left = -520
|
||||
steuerung.offset_top = 16
|
||||
steuerung.offset_bottom = 40
|
||||
steuerung.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
steuerung.add_theme_font_size_override("font_size", 14)
|
||||
steuerung.add_theme_color_override("font_color", Color(1, 1, 1, 0.4))
|
||||
@@ -397,7 +401,10 @@ func _baue_wirte() -> void:
|
||||
_titel.add_theme_color_override("font_color", Color(1, 0.93, 0.75))
|
||||
_box.add_child(_titel)
|
||||
_scroll = ScrollContainer.new()
|
||||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
# nie querscrollen, aber auch nie die Leiste verbreitern: zu breiter Inhalt
|
||||
# wird abgeschnitten statt aus dem Fenster zu ragen
|
||||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
|
||||
_scroll.clip_contents = true
|
||||
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_box.add_child(_scroll)
|
||||
_content = VBoxContainer.new()
|
||||
@@ -411,6 +418,7 @@ func _baue_wirte() -> void:
|
||||
_drawer.offset_right = -12
|
||||
_drawer.offset_top = 56
|
||||
_drawer.offset_bottom = -(LEISTE_H + 22)
|
||||
_drawer.clip_contents = true
|
||||
_drawer.visible = false
|
||||
_root.add_child(_drawer)
|
||||
|
||||
@@ -435,9 +443,20 @@ func _wirt_fuer(kind: String) -> PanelContainer:
|
||||
return _modal
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
var _takt := 0.0 # HUD- und Leistentexte alle 0,1 s; der Puls läuft je Bild
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_takt -= delta
|
||||
var dran := _takt <= 0.0
|
||||
if dran:
|
||||
_takt = 0.1
|
||||
if not offen:
|
||||
_hud_aktualisieren()
|
||||
if dran:
|
||||
_hud_aktualisieren()
|
||||
_ablauf_aktualisieren()
|
||||
else:
|
||||
_ablauf_pulsen()
|
||||
return
|
||||
var wirt := _wirt_fuer(_kind)
|
||||
var wunsch := _content.get_combined_minimum_size().y
|
||||
@@ -461,7 +480,21 @@ func _process(_delta: float) -> void:
|
||||
_karte_host.reset_size()
|
||||
_karte_host.position = Vector2((vp.x - _karte_host.size.x) * 0.5,
|
||||
vp.y - LEISTE_H - 22.0 - _karte_host.size.y)
|
||||
_ablauf_aktualisieren()
|
||||
if dran:
|
||||
_ablauf_aktualisieren()
|
||||
else:
|
||||
_ablauf_pulsen()
|
||||
|
||||
|
||||
## Nur die laufende Stunde pulsiert -- das darf jedes Bild passieren.
|
||||
func _ablauf_pulsen() -> void:
|
||||
var id := Game.active_mission
|
||||
if id == "" or not _ablauf.visible or not Game.situation_bei(id):
|
||||
return
|
||||
var jetzt := int(Game.time_h)
|
||||
var puls := 0.5 + 0.5 * sin(Time.get_ticks_msec() * 0.008)
|
||||
var zelle: ColorRect = _zellen[jetzt]
|
||||
zelle.color = _ablauf_farbe(id, Game.slot_bei_stunde(id, jetzt)).lightened(0.3 + 0.4 * puls)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ Baukasten
|
||||
@@ -549,6 +582,9 @@ func _trenner() -> void:
|
||||
func _zeigen(kind: String, zurueck: Callable = Callable()) -> void:
|
||||
_kind = kind
|
||||
_ziel = null
|
||||
if kind != "akte":
|
||||
_drawer.remove_theme_stylebox_override("panel")
|
||||
_titel.remove_theme_color_override("font_color")
|
||||
var wirt := _wirt_fuer(kind)
|
||||
if _box.get_parent() != wirt:
|
||||
if _box.get_parent() != null:
|
||||
@@ -726,6 +762,7 @@ func akte_panel(human_id: String) -> void:
|
||||
_akte_text(str(h["problem"]), 15)
|
||||
|
||||
_akte_abschnitt("Befund")
|
||||
var offen_urs := 0
|
||||
for u in Game.ursachen_von(human_id):
|
||||
var name: String = str(u)
|
||||
var stand: String
|
||||
@@ -736,10 +773,12 @@ func akte_panel(human_id: String) -> void:
|
||||
stand = "[color=%s]%s · bremst auf %d %%[/color]" % [TINTE_ROT, woher,
|
||||
int(float(Ursachen.LISTE[name]["faktor"]) * 100.0)]
|
||||
else:
|
||||
_akte_text("[color=%s]? noch nichts gefunden — Spuren in der Wohnung, Momente im Tag[/color]" %
|
||||
TINTE_HELL, 15)
|
||||
offen_urs += 1
|
||||
continue
|
||||
_akte_text("[b]%s[/b] %s" % [Ursachen.LISTE[name]["name"], stand], 15)
|
||||
if offen_urs > 0:
|
||||
_akte_text("[color=%s]? %s noch nichts gefunden — Spuren in der Wohnung, Momente im Tag[/color]" %
|
||||
[TINTE_HELL, "eine Ursache," if offen_urs == 1 else "%d Ursachen," % offen_urs], 15)
|
||||
var probleme: Array = []
|
||||
for pr in Game.probleme_von(human_id):
|
||||
var key := str(pr)
|
||||
@@ -768,19 +807,17 @@ func akte_panel(human_id: String) -> void:
|
||||
_scroll_frames = 4
|
||||
|
||||
|
||||
## Ein Blatt Papier im Drawer; alles Weitere der Akte landet darauf.
|
||||
## Die Leiste selbst wird zum Blatt Papier. Ein Container im Container bricht
|
||||
## den Text nicht um -- er richtet sich an der längsten Zeile aus und ragt hinaus.
|
||||
func _papier() -> void:
|
||||
var p := PanelContainer.new()
|
||||
var s := _sb(C_PAPIER, 3, Color(0.78, 0.72, 0.6), 1)
|
||||
s.content_margin_left = 16
|
||||
s.content_margin_right = 16
|
||||
s.content_margin_top = 12
|
||||
var s := _sb(C_PAPIER, 4, Color(0.78, 0.72, 0.6), 1)
|
||||
s.content_margin_left = 18
|
||||
s.content_margin_right = 18
|
||||
s.content_margin_top = 10
|
||||
s.content_margin_bottom = 14
|
||||
p.add_theme_stylebox_override("panel", s)
|
||||
_content.add_child(p)
|
||||
_blatt = VBoxContainer.new()
|
||||
_blatt.add_theme_constant_override("separation", 6)
|
||||
p.add_child(_blatt)
|
||||
_drawer.add_theme_stylebox_override("panel", s)
|
||||
_titel.add_theme_color_override("font_color", Color(0.17, 0.15, 0.13))
|
||||
_blatt = _content
|
||||
|
||||
|
||||
func _akte_text(bb: String, groesse: int = 15) -> RichTextLabel:
|
||||
|
||||
Reference in New Issue
Block a user