extends Node3D # ═══════════════════════════════════════════════════════════════ # HEX-DIAMANT-INSEL — Blockout # Krone (begehbar): flaches 6-Eck-Tisch + 6 Trapez-Facetten zur Rundiste # Pavillon (rein optisch): steile 6-seitige Pyramide unter der Rundiste # ═══════════════════════════════════════════════════════════════ @export var table_radius : float = 80.0 # flaches Sechseck in der Mitte @export var girdle_radius : float = 260.0 # äußerer Sechseck-Rand (Rundiste) @export var crown_drop : float = 30.0 # Höhenabfall Tisch -> Rundiste @export var pavilion_depth : float = 190.0 # Höhenabfall Rundiste -> Spitze @export var angle_offset_deg: float = 0.0 @export var border_spacing : float = 18.0 # Abstand zwischen Grenz-Props entlang eines Grats @export var border_jitter : float = 3.0 # zufälliger seitlicher Versatz const COLOR_TABLE := Color(0.75, 0.74, 0.70) const COLOR_FACET_A := Color(0.55, 0.55, 0.48) const COLOR_FACET_B := Color(0.49, 0.49, 0.43) const COLOR_PAVILION := Color(0.30, 0.28, 0.26) # Grenz-Props: gleiche Modelle + Skalierung + Hitbox-Maße wie in main.tscn const MOUNTAIN_SCENE := preload("res://assets/models/mountain.glb") const MOUNTAIN2_SCENE := preload("res://assets/models/mountain2v5.glb") const FOREST_SCENE := preload("res://assets/models/forest.glb") const MOUNTAIN_SCALE := 0.1 const MOUNTAIN2_SCALE := 0.05 const FOREST_SCALE := 0.2 func _ready() -> void: _build_crown() _build_pavilion() _build_region_borders() func _hex_angle(k: int) -> float: return deg_to_rad(angle_offset_deg) + float(k) / 6.0 * TAU func _add_tri(st: SurfaceTool, a: Vector3, b: Vector3, c: Vector3, col: Color) -> void: var n := (b - a).cross(c - a).normalized() for v in [a, b, c]: st.set_normal(n) st.set_color(col) st.add_vertex(v) # ═══════════════════════════════════════════════ # KRONE — Tisch + 6 Facetten (begehbar, mit Kollision) # ═══════════════════════════════════════════════ func _build_crown() -> void: var st := SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) var center := Vector3.ZERO var table_pts : Array[Vector3] = [] var girdle_pts : Array[Vector3] = [] for k in range(6): var a := _hex_angle(k) table_pts.append(Vector3(cos(a) * table_radius, 0.0, sin(a) * table_radius)) girdle_pts.append(Vector3(cos(a) * girdle_radius, -crown_drop, sin(a) * girdle_radius)) # Tisch-Fan (Zentrum-Region) for k in range(6): var k1 := (k + 1) % 6 _add_tri(st, center, table_pts[k1], table_pts[k], COLOR_TABLE) # 6 Facetten (Außenregionen), abwechselnd eingefärbt für sichtbare Nähte for k in range(6): var k1 := (k + 1) % 6 var col := COLOR_FACET_A if k % 2 == 0 else COLOR_FACET_B _add_tri(st, table_pts[k], table_pts[k1], girdle_pts[k], col) _add_tri(st, table_pts[k1], girdle_pts[k1], girdle_pts[k], col) var mesh := st.commit() var mat := StandardMaterial3D.new() mat.vertex_color_use_as_albedo = true mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED mat.cull_mode = BaseMaterial3D.CULL_DISABLED mesh.surface_set_material(0, mat) var body := StaticBody3D.new() body.name = "Crown" var mi := MeshInstance3D.new() mi.mesh = mesh body.add_child(mi) var cs := CollisionShape3D.new() cs.shape = mesh.create_trimesh_shape() body.add_child(cs) add_child(body) # ═══════════════════════════════════════════════ # PAVILLON — Pyramide unter der Rundiste (rein optisch) # ═══════════════════════════════════════════════ func _build_pavilion() -> void: var st := SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) var apex := Vector3(0, -crown_drop - pavilion_depth, 0) var girdle_pts: Array[Vector3] = [] for k in range(6): var a := _hex_angle(k) girdle_pts.append(Vector3(cos(a) * girdle_radius, -crown_drop, sin(a) * girdle_radius)) for k in range(6): var k1 := (k + 1) % 6 _add_tri(st, girdle_pts[k], girdle_pts[k1], apex, COLOR_PAVILION) var mesh := st.commit() var mat := StandardMaterial3D.new() mat.vertex_color_use_as_albedo = true mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED mat.cull_mode = BaseMaterial3D.CULL_DISABLED mesh.surface_set_material(0, mat) var mi := MeshInstance3D.new() mi.name = "Pavilion" mi.mesh = mesh add_child(mi) # ═══════════════════════════════════════════════ # GRENZEN — Berg/Wald entlang der 6 Grate zwischen den Regionen # Grat k = Strecke von Tisch-Ecke T[k] zu Rundiste-Ecke G[k] # (gemeinsame Kante von Facette k-1 und Facette k) # ═══════════════════════════════════════════════ func _build_region_borders() -> void: var borders := Node3D.new() borders.name = "Borders" add_child(borders) for k in range(6): var a := _hex_angle(k) var dir := Vector3(cos(a), 0.0, sin(a)) var perp := Vector3(-dir.z, 0.0, dir.x) var t := table_radius while t <= girdle_radius: var frac := (t - table_radius) / (girdle_radius - table_radius) var pos := dir * t pos.y = lerp(0.0, -crown_drop, frac) pos += perp * randf_range(-border_jitter, border_jitter) _place_border_prop(borders, pos) t += border_spacing + randf_range(-4.0, 4.0) func _place_border_prop(parent: Node3D, pos: Vector3) -> void: var roll := randf() var inst: Node3D var scale_factor: float var shape: Shape3D if roll < 0.4: inst = MOUNTAIN_SCENE.instantiate() as Node3D scale_factor = MOUNTAIN_SCALE var cap := CapsuleShape3D.new() cap.radius = 7.0 cap.height = 20.0 shape = cap elif roll < 0.7: inst = MOUNTAIN2_SCENE.instantiate() as Node3D scale_factor = MOUNTAIN2_SCALE var cap2 := CapsuleShape3D.new() cap2.radius = 8.5078125 cap2.height = 17.015625 shape = cap2 else: inst = FOREST_SCENE.instantiate() as Node3D scale_factor = FOREST_SCALE var cyl := CylinderShape3D.new() cyl.radius = 0.72021484 cyl.height = 2.2817383 shape = cyl inst.position = pos inst.rotation.y = randf() * TAU inst.scale = Vector3.ONE * scale_factor var cs := CollisionShape3D.new() cs.shape = shape inst.add_child(cs) parent.add_child(inst)