Jonas Reith fa38e603a1 Initial commit: WayfarersWanderer project state
Godot 4.6 exploration game with player movement, step-rotation camera
rig, and POI interaction system (explore/rest/close menu). main.tscn
is a test scene with placeholder props; debug/ holds unwired terrain
prototypes (voxel river, procedural island mesh, cross-shaped hub
2026-08-26 19:05:30 +02:00

272 lines
10 KiB
GDScript

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)