extends Node3D @export var berg_scale : float = .002 # Welt-Parameter const ISLAND_RADIUS : float = 512.0 const GRID : float = 1.0 # 1m pro Block const CORRIDOR_WIDTH : int = 3 # Korridor-Breite in Blöcken const CENTER_RADIUS : float = 120.0 # Radius Mittelzone # Pass-Position: Anteil vom Rand der Mittelzone (0=Mitte, 1=Inselrand) # 0.35 = ca. erstes Drittel ab Mittelzone Richtung Rand const PASS_POS : float = 0.35 const PASS_WIDTH : int = 3 # Lücke in Blöcken func _ready() -> void: _spawn_kreuz() _spawn_mittelring() _spawn_ozean() # ═══════════════════════════════════════════════════════════════ # KREUZ — 4 Arme: Nord, Süd, West = Berge | Ost = Fluss # ═══════════════════════════════════════════════════════════════ func _spawn_kreuz() -> void: # Jeder Arm geht von CENTER_RADIUS bis ISLAND_RADIUS _spawn_arm_berge("Nord", Vector3( 0, 0, -1)) _spawn_arm_berge("Sued", Vector3( 0, 0, 1)) _spawn_arm_berge("West", Vector3(-1, 0, 0)) _spawn_arm_fluss("Ost", Vector3( 1, 0, 0)) # ── Berg-Arm ───────────────────────────────────────────────── func _spawn_arm_berge(label: String, dir: Vector3) -> void: var grp := Node3D.new() grp.name = "Arm_" + label add_child(grp) var perp := dir.cross(Vector3.UP).normalized() var arm_len := ISLAND_RADIUS - CENTER_RADIUS var steps := int(arm_len / GRID) # Pass-Bereich berechnen (in Steps ab Mittelzone) var pass_start := int(steps * PASS_POS) var pass_end := pass_start + PASS_WIDTH # Große Berg-Modelle vorbauen var large_meshes := _make_large_berge() var small_meshes := _make_small_berge() for step in range(steps): # Position entlang des Arms var dist := CENTER_RADIUS + step * GRID + GRID * 0.5 var center := dir * dist var in_pass := (step >= pass_start and step < pass_end) for col in range(-CORRIDOR_WIDTH / 2, CORRIDOR_WIDTH / 2 + 1): var pos := center + perp * (col * GRID) pos.y = 0.0 if in_pass: # Im Pass: nur außen kleine Berge, Mitte frei if abs(col) == CORRIDOR_WIDTH / 2: _place_berg(grp, pos, small_meshes) # col == 0 → frei (Durchgang) else: # Außenreihen → kleine Berge dicht if abs(col) >= 1: _place_berg(grp, pos, small_meshes) # Mittelreihe → große Berge else: _place_berg(grp, pos, large_meshes) # Pass-Marker (Platzhalter Tor) var pass_dist := CENTER_RADIUS + (pass_start + PASS_WIDTH * 0.5) * GRID var pass_center := dir * pass_dist _spawn_pass_marker(grp, pass_center) func _place_berg(parent: Node3D, pos: Vector3, meshes: Array) -> void: var mi := MeshInstance3D.new() mi.mesh = meshes[randi() % meshes.size()] mi.position = pos # Zufällige Y-Rotation für Abwechslung mi.rotation.y = randf() * TAU parent.add_child(mi) # ── Fluss-Arm ──────────────────────────────────────────────── func _spawn_arm_fluss(label: String, dir: Vector3) -> void: var grp := Node3D.new() grp.name = "Arm_" + label add_child(grp) var arm_len := ISLAND_RADIUS - CENTER_RADIUS var steps := int(arm_len / GRID) var pass_start := int(steps * PASS_POS) var pass_end := pass_start + PASS_WIDTH var mat_wasser := StandardMaterial3D.new() mat_wasser.albedo_color = Color(0.18, 0.45, 0.78) mat_wasser.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED for step in range(steps): var dist := CENTER_RADIUS + step * GRID + GRID * 0.5 var pos := dir * dist pos.y = -0.5 # 1m tief eingeschnitten var in_pass := (step >= pass_start and step < pass_end) if in_pass: # Brücken-Block _spawn_bruecke_block(grp, dir * dist) else: # Fluss-Block: 1x1m Wasser var mi := MeshInstance3D.new() var box := BoxMesh.new() box.size = Vector3(GRID, GRID, GRID) box.surface_set_material(0, mat_wasser) mi.mesh = box mi.position = pos mi.name = "Fluss_" + str(step) grp.add_child(mi) func _spawn_bruecke_block(parent: Node3D, pos: Vector3) -> void: var mi := MeshInstance3D.new() var box := BoxMesh.new() box.size = Vector3(GRID * 3.0, 0.3, GRID * 3.0) var mat := StandardMaterial3D.new() mat.albedo_color = Color(0.55, 0.45, 0.32) mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED box.surface_set_material(0, mat) mi.mesh = box mi.position = pos + Vector3(0, 0.65, 0) mi.name = "Bruecke" parent.add_child(mi) # ── Pass-Marker ─────────────────────────────────────────────── func _spawn_pass_marker(parent: Node3D, pos: Vector3) -> void: var mi := MeshInstance3D.new() var box := BoxMesh.new() box.size = Vector3(2.0, 4.0, 2.0) var mat := StandardMaterial3D.new() mat.albedo_color = Color(0.85, 0.78, 0.40) mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED box.surface_set_material(0, mat) mi.mesh = box mi.position = pos + Vector3(0, 2.0, 0) mi.name = "PassMarker" parent.add_child(mi) # ═══════════════════════════════════════════════════════════════ # MITTELRING — Bergring um die Mittelzone # ═══════════════════════════════════════════════════════════════ func _spawn_mittelring() -> void: var grp := Node3D.new() grp.name = "Mittelring" add_child(grp) var large_meshes := _make_large_berge() var small_meshes := _make_small_berge() # Umfang des Kreises in 1m-Schritten var circumference := TAU * CENTER_RADIUS var steps := int(circumference / GRID) # Die 4 Kreuz-Arme blockieren bestimmte Winkel → dort Pässe # Arm-Winkel: Nord=270°, Süd=90°, West=180°, Ost=0° var arm_angles := [0.0, PI * 0.5, PI, PI * 1.5] var pass_half := (float(PASS_WIDTH) * GRID) / CENTER_RADIUS # Winkel-Halbbreite for step in range(steps): var angle := float(step) / steps * TAU # Prüfen ob dieser Winkel in einem Arm-Durchgang liegt var in_arm := false for arm_a in arm_angles: var diff: float = abs(angle_difference(angle, arm_a)) if diff < pass_half * 1.5: in_arm = true break # Innenring (dicht am Center_radius) var r_inner := CENTER_RADIUS - 1.0 var pos_i := Vector3(cos(angle)*r_inner, 0, sin(angle)*r_inner) # Außenring var r_outer := CENTER_RADIUS + 2.0 var pos_o := Vector3(cos(angle)*r_outer, 0, sin(angle)*r_outer) if not in_arm: _place_berg(grp, pos_i, large_meshes) _place_berg(grp, pos_o, small_meshes) # Hilfsfunktion: Winkel-Differenz -PI..PI func angle_difference(a: float, b: float) -> float: var d := fmod(a - b + TAU, TAU) if d > PI: d -= TAU return d # ═══════════════════════════════════════════════════════════════ # BERG-MODELLE — prozedural, mehrere Varianten # ═══════════════════════════════════════════════════════════════ func _make_large_berge() -> Array: var result := [] # 4 große Varianten: breiter, massiver var configs := [ [8.0, 0.05, 22.0, 6], [10.0, 0.05, 18.0, 7], [7.0, 0.05, 26.0, 5], [12.0, 0.05, 20.0, 8], ] for cfg in configs: result.append(_make_berg_mesh(cfg[0], cfg[1], cfg[2] * berg_scale, int(cfg[3]), false)) return result func _make_small_berge() -> Array: var result := [] # 6 kleine Varianten: schmaler, dichter packbar var configs := [ [3.0, 0.02, 10.0, 5], [2.5, 0.02, 8.0, 6], [4.0, 0.02, 12.0, 5], [2.0, 0.02, 7.0, 4], [3.5, 0.02, 11.0, 6], [2.8, 0.02, 9.0, 5], ] for cfg in configs: result.append(_make_berg_mesh(cfg[0], cfg[1], cfg[2], int(cfg[3]), true)) return result func _make_berg_mesh(bot_r: float, top_r: float, h: float, sides: int, small: bool) -> Mesh: var st := SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) var tip := Vector3(0, h, 0) var gray := randf_range(0.35, 0.55) if not small else randf_range(0.40, 0.58) var col := Color(gray, gray * 0.97, gray * 0.93) var col_b := Color(gray * 0.7, gray * 0.68, gray * 0.65) for s in range(sides): var a0 := float(s) / sides * TAU var a1 := float(s+1) / sides * TAU var b0 := Vector3(cos(a0)*bot_r, 0, sin(a0)*bot_r) var b1 := Vector3(cos(a1)*bot_r, 0, sin(a1)*bot_r) var t0 := Vector3(cos(a0)*top_r, h, sin(a0)*top_r) var t1 := Vector3(cos(a1)*top_r, h, sin(a1)*top_r) # Seite var n := (b1 - b0).cross(tip - b0).normalized() st.set_normal(n); st.set_color(col) st.add_vertex(b0); st.add_vertex(tip); st.add_vertex(b1) # Boden-Cap st.set_normal(Vector3(0,-1,0)); st.set_color(col_b) st.add_vertex(Vector3(0,0,0)); st.add_vertex(b1); st.add_vertex(b0) return st.commit() # ═══════════════════════════════════════════════════════════════ # OZEAN unter der Insel # ═══════════════════════════════════════════════════════════════ func _spawn_ozean() -> void: var mi := MeshInstance3D.new() var plane := PlaneMesh.new() plane.size = Vector2(4096, 4096) var mat := StandardMaterial3D.new() mat.albedo_color = Color(0.08, 0.25, 0.52) mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED plane.surface_set_material(0, mat) mi.mesh = plane mi.position = Vector3(0, -40, 0) mi.name = "Ozean" add_child(mi)