This commit is contained in:
Team3
2026-09-16 01:23:42 +02:00
parent 12cbb88692
commit 936773c4e9
13 changed files with 1300 additions and 314 deletions

View File

@@ -13,6 +13,13 @@ const WEG_ABSTAND := 3.0 # Mindestluft zwischen Haus und Weg
const KANDIDATEN := 64 # Probepunkte je Haus
const SPINE_SCHRITT := 8.0 # Abstand der Knoten auf der Hauptachse
## Die Insel ist ein Hügel. Innen eben -- Dorfplatz, Hörsaal, Tor --, ab FLACH
## steigt sie an. Wer später einzieht, wohnt weiter außen und damit höher:
## das Dorf wächst den Hügel hinauf, und vom Platz aus sieht man, wie weit.
const FLACH := 24.0
const STEIGUNG := 0.30
const GIPFEL := 36.0
static var FEST: Dictionary = {
"hoersaal": {
"name": "Hörsaal der Wege", "pos": [0.0, 34.0], "size": [16.0, 12.0],
@@ -56,6 +63,72 @@ static var ABLAEUFE: Array = [
]
## Höhe des nackten Hangs bei diesem Abstand von der Mitte.
static func hang(r: float) -> float:
return clampf((r - FLACH) * STEIGUNG, 0.0, GIPFEL)
## Jedes Gebäude steht auf einer ebenen Terrasse in Höhe seiner Tür -- so ist
## die Türseite ebenerdig, die Bergseite eingegraben. Das Tor hat keine Tür;
## seine Terrasse liegt auf Höhe der Dorfseite.
const TOR_FLAECHE := 12.0
const TERRASSE_RAND := 2.6 # bis zur Türschwelle davor
const BOESCHUNG := 5.0 # so weit läuft die Terrasse in den Hang aus
static func terrasse(key: String) -> float:
if not PLACES.has(key):
return 0.0
if key == "tor":
var c := Orte.center(PLACES, key)
return hang(Vector2(c.x, c.z).length() - TOR_FLAECHE * 0.5)
var t := Orte.door(PLACES, key)
return hang(Vector2(t.x, t.z).length())
## Höhe des Bodens an dieser Stelle: Terrasse in einem Gebäude, sonst Hang.
## Wo sich Terrassenränder überlappen, gilt das Gebäude, in dem der Punkt
## wirklich liegt -- erst danach der Rand.
static func hoehe_bei(x: float, z: float) -> float:
var h := hang(Vector2(x, z).length())
var rand_treffer := ""
var boeschung_key := ""
var boeschung_t := 1.0
var reihenfolge: Array = PLACES.keys()
reihenfolge.erase("tor")
reihenfolge.append("tor")
for key in reihenfolge:
var s: Array = PLACES[key]["size"]
var bx: float = float(s[0])
var bz: float = float(s[1])
if key == "tor":
bx = TOR_FLAECHE
bz = TOR_FLAECHE
if bx <= 0.0 or not Game.ort_offen(key):
continue
var c := Orte.center(PLACES, key)
var dx := absf(x - c.x) - bx * 0.5
var dz := absf(z - c.z) - bz * 0.5
if dx <= 0.0 and dz <= 0.0:
return terrasse(key)
if dx <= TERRASSE_RAND and dz <= TERRASSE_RAND:
if rand_treffer == "":
rand_treffer = key
continue
# außerhalb des Rands: die Terrasse läuft als Böschung in den Hang aus
var d := maxf(dx, dz) - TERRASSE_RAND
if d < BOESCHUNG:
var t := d / BOESCHUNG
if t < boeschung_t:
boeschung_t = t
boeschung_key = key
if rand_treffer != "":
return terrasse(rand_treffer)
if boeschung_key != "":
var tt := boeschung_t * boeschung_t * (3.0 - 2.0 * boeschung_t) # weich
return lerpf(terrasse(boeschung_key), h, tt)
return h
static func haus_key(i: int) -> String:
return "haus_%d" % i
@@ -171,11 +244,16 @@ static func _platz_frei(p: Vector3, groesse: Vector2) -> bool:
return false
for key in PLACES:
var s: Array = PLACES[key]["size"]
if float(s[0]) <= 0.0:
var bx: float = float(s[0])
var bz: float = float(s[1])
if key == "tor":
bx = TOR_FLAECHE
bz = TOR_FLAECHE
if bx <= 0.0:
continue
var c := Orte.center(PLACES, key)
var dx: float = absf(p.x - c.x) - (groesse.x + float(s[0])) * 0.5
var dz: float = absf(p.z - c.z) - (groesse.y + float(s[1])) * 0.5
var dx: float = absf(p.x - c.x) - (groesse.x + bx) * 0.5
var dz: float = absf(p.z - c.z) - (groesse.y + bz) * 0.5
if dx < HAUS_ABSTAND and dz < HAUS_ABSTAND:
return false
for k in NETZ.kanten:
@@ -249,6 +327,18 @@ static func _anschliessen(key: String) -> void:
var an := NETZ.andocken(tuer)
var von: Vector3 = an["punkt"]
var linie := _route_um_haeuser(tuer, von, key)
if not _linie_frei(linie, key):
# Der nächste Punkt des Netzes liegt eingeklemmt. Dann den nächsten
# Knoten nehmen, zu dem ein sauberer Weg führt -- nie durch ein Haus.
var reihe: Array = range(NETZ.knoten.size())
reihe.sort_custom(func(i, j) -> bool:
return NETZ.knoten[i].distance_to(tuer) < NETZ.knoten[j].distance_to(tuer))
for idx in reihe.slice(0, 16):
var l2 := _route_um_haeuser(tuer, NETZ.knoten[idx], key)
if _linie_frei(l2, key):
linie = l2
an = {"punkt": NETZ.knoten[idx], "knoten": idx}
break
var vorher := -1
for i in range(linie.size()):
var idx := NETZ.fuege_knoten(linie[i])
@@ -264,17 +354,33 @@ static func _anschliessen(key: String) -> void:
_schleife(int(ANKER[key]), key)
## Schneidet diese Linie irgendwo ein Gebäude?
static func _linie_frei(linie: Array, ausser: String) -> bool:
for i in range(linie.size() - 1):
if not _weg_frei(linie[i], linie[i + 1], ausser):
return false
return true
## Weg von a nach b, der kein Haus schneidet. Frei? Dann die Gerade. Sonst
## Dijkstra über ein grobes Raster, danach auf wenige Knicke geglättet.
## Dijkstra über ein Raster -- erst grob, dann fein --, danach geglättet.
static func _route_um_haeuser(a: Vector3, b: Vector3, ausser: String) -> Array:
if _weg_frei(a, b, ausser):
return [a, b]
var rand := 30.0
for schritt_start in [5.0, 2.5]:
var weg := _route_raster(a, b, ausser, schritt_start)
if not weg.is_empty():
return weg
return [a, b]
static func _route_raster(a: Vector3, b: Vector3, ausser: String, schritt_start: float) -> Array:
var rand := 34.0
var min_x := minf(a.x, b.x) - rand
var max_x := maxf(a.x, b.x) + rand
var min_z := minf(a.z, b.z) - rand
var max_z := maxf(a.z, b.z) + rand
var schritt := 5.0
var schritt := schritt_start
var nx := int((max_x - min_x) / schritt) + 1
var nz := int((max_z - min_z) / schritt) + 1
while nx * nz > 2600:
@@ -320,7 +426,7 @@ static func _route_um_haeuser(a: Vector3, b: Vector3, ausser: String) -> Array:
if not offen.has(n):
offen.append(n)
if not gefunden:
return [a, b]
return []
var roh: Array = []
var cur: Vector2i = ziel