extends Node3D ## The Cove's pier (W3, docs/WORLDLETS_DESIGN.md ยง5): a wooden walkway from ## the sand straight out over the bay, so a cast from its end reaches deep ## sea water (mackerel, and cod further out). Part of the world, like the ## barrow: worked out from the terrain alone, so every player and the server ## get the same one, and it's never saved, built or dismantled. ## ## It starts where the beach has risen to deck height (so you walk straight ## on) and runs `LENGTH_OUT` metres past the waterline. const WIDTH := 2.4 const DECK_ABOVE := 1.0 ## deck height over the water const LENGTH_OUT := 24.0 ## how far past the waterline it reaches const POST_EVERY := 3.0 const WOOD := Color(0.42, 0.30, 0.19) const POST := Color(0.30, 0.22, 0.15) var start := Vector3.ZERO ## the landward end, on the sand var end := Vector3.ZERO ## the seaward end (fish from here) var dir := Vector3.ZERO ## out to sea along the deck ## Lays the pier off the terrain's beach. Returns false (and adds nothing) ## when the worldlet has no beach. func place(terrain: Node3D) -> bool: if not terrain._has_beach or not terrain.worldlet.get("places", []).any(func(p): return p is Dictionary and p.get("type", "") == "pier"): return false var deck_y: float = terrain.water_level + DECK_ABOVE var out2: Vector2 = terrain.shore_point(LENGTH_OUT) var root2: Vector2 = terrain.shore_point(-2.0) var d2 := (out2 - root2).normalized() # Walk inland from the waterline to where the sand is at deck height. var s2 := root2 for _i in range(40): if terrain.sample_height(s2.x, s2.y) >= deck_y - 0.15: break s2 -= d2 * 0.5 start = Vector3(s2.x, deck_y, s2.y) end = Vector3(out2.x, deck_y, out2.y) dir = (end - start).normalized() _build(terrain) return true ## Whether (x, z) is under or right beside the pier (scenery keeps clear). func covers(x: float, z: float, margin: float = 1.0) -> bool: var a := Vector2(start.x, start.z) var b := Vector2(end.x, end.z) var p := Vector2(x, z) var t := clampf((p - a).dot(b - a) / maxf((b - a).length_squared(), 0.001), 0.0, 1.0) return p.distance_to(a.lerp(b, t)) < WIDTH * 0.5 + margin func _build(terrain: Node3D) -> void: var length := start.distance_to(end) var yaw := atan2(dir.x, dir.z) var basis := Basis(Vector3.UP, yaw) var mid := (start + end) * 0.5 # The deck: planks across it, with a little gap and wobble between them. var st := SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) var n := int(length / 0.32) var rng := RandomNumberGenerator.new() rng.seed = 7171 for i in range(n): var z := -length * 0.5 + (i + 0.5) * length / n var h := 0.08 + rng.randf_range(-0.01, 0.01) _box(st, Vector3(rng.randf_range(-0.04, 0.04), -h * 0.5, z), Vector3(WIDTH, h, length / n - 0.03), WOOD.darkened(rng.randf_range(0.0, 0.18))) # Posts down to the bed, in pairs, and a rail beam along each side. var k := 0.0 while k <= length + 0.01: var along := -length * 0.5 + k for side in [-1.0, 1.0]: var local := Vector3(side * (WIDTH * 0.5 - 0.1), 0.0, along) var world_p: Vector3 = mid + basis * local var bed: float = terrain.sample_height(world_p.x, world_p.z) var tall := start.y - bed + 0.3 if tall > 0.2: _box(st, local + Vector3(0, -tall * 0.5 - 0.05, 0), Vector3(0.22, tall, 0.22), POST) k += POST_EVERY for side in [-1.0, 1.0]: _box(st, Vector3(side * (WIDTH * 0.5 - 0.06), -0.16, 0.0), Vector3(0.12, 0.14, length), POST) st.generate_normals() var mi := MeshInstance3D.new() mi.name = "Deck" mi.mesh = st.commit() var mat := StandardMaterial3D.new() mat.vertex_color_use_as_albedo = true mat.roughness = 0.9 mi.material_override = mat add_child(mi) # Walkable deck (on the ground layer, like the terrain). var body := StaticBody3D.new() body.name = "DeckBody" var cs := CollisionShape3D.new() var box := BoxShape3D.new() box.size = Vector3(WIDTH, 0.12, length) cs.shape = box cs.position = Vector3(0, -0.06, 0) body.add_child(cs) add_child(body) transform = Transform3D(basis, mid) add_to_group("pier") static func _box(st: SurfaceTool, c: Vector3, s: Vector3, col: Color) -> void: var h := s * 0.5 var v := [ c + Vector3(-h.x, -h.y, -h.z), c + Vector3(h.x, -h.y, -h.z), c + Vector3(h.x, h.y, -h.z), c + Vector3(-h.x, h.y, -h.z), c + Vector3(-h.x, -h.y, h.z), c + Vector3(h.x, -h.y, h.z), c + Vector3(h.x, h.y, h.z), c + Vector3(-h.x, h.y, h.z), ] var faces := [[0, 3, 2, 1], [4, 5, 6, 7], [0, 4, 7, 3], [1, 2, 6, 5], [3, 7, 6, 2], [0, 1, 5, 4]] st.set_color(col) for f in faces: # (listed anticlockwise from outside; Godot's front faces are clockwise) st.add_vertex(v[f[0]]); st.add_vertex(v[f[2]]); st.add_vertex(v[f[1]]) st.add_vertex(v[f[0]]); st.add_vertex(v[f[3]]); st.add_vertex(v[f[2]])