Files
Cinderwild/scripts/world/sea_cave.gd

121 lines
4.5 KiB
GDScript

extends Node3D
## The Cove's sea cave (J1): the bottom of the Valley's cliff tunnel, seen
## from the shore. A dark opening in the rocks at the end of the beach,
## where the headland meets the sand. [E] goes in and up the tunnel, back to
## the Valley, once that tunnel has been dug through from the top. (Until
## then the cave runs back into solid rock.)
##
## Placed from the terrain alone, like the pier, so it's the same for
## everyone; nothing about it is saved here (the tunnel is the Valley's).
var world: Node3D
var to := "valley"
var front := Vector3.ZERO ## where you stand in front of it (arriving from the tunnel)
## Places the cave if this worldlet has a way back by tunnel from a sea cave.
func place(w: Node3D) -> bool:
world = w
var t: Node3D = w.terrain
var way: Dictionary = {}
for c in w.crossings():
if c is Dictionary and c.get("by", "") == "tunnel" and c.get("where", "") == "sea_cave":
way = c
if way.is_empty() or not t._has_beach:
return false
to = String(way.to)
# At the end of the beach, where the headland starts: the first dry,
# gentle spot coming in from the waterline.
var a := deg_to_rad(t.beach_angle_deg + t.beach_half_width_deg + t.beach_fade_deg * 0.35)
var dir := Vector2(cos(a), sin(a))
var at := Vector3.INF
for i in range(60):
var r: float = t._shore_d() - 2.0 - i * 0.5 - t._rim_offset(dir.x, dir.y)
var p := dir * r
var h: float = t.sample_height(p.x, p.y)
if h > t.water_level + 0.5 and t.sample_normal(p.x, p.y).y > 0.86 and t.inside_boundary(p.x, p.y, 3.0):
at = Vector3(p.x, h, p.y)
break
if at == Vector3.INF:
return false
# Facing back along the beach (towards its middle), so you see it from the sand.
var mid: Vector2 = t.shore_point(-6.0)
var face := Vector3(mid.x - at.x, 0.0, mid.y - at.z).normalized()
global_transform = Transform3D(Basis.looking_at(face, Vector3.UP), at)
front = at + face * 2.4
front.y = t.sample_height(front.x, front.z)
_build(w)
add_to_group("sea_cave")
return true
## Whether the Valley's tunnel has been dug right down to here.
func open() -> bool:
if not Saves.has_world():
return false
var s: Dictionary = Saves.load_state(Saves.current_id, to)
return int(s.get("tunnel", {}).get("dug", 0)) >= preload("res://scripts/world/tunnel.gd").STRETCHES
func interact_prompt(_player: Node) -> String:
if open():
return "[E] Go into the sea cave and up the tunnel to the %s" % preload("res://scripts/world/terrain.gd").worldlet_name(to)
return "A sea cave. It runs back into solid rock: the way up hasn't been dug"
func interact(_player: Node) -> void:
if open():
world.travel(to, "tunnel_bottom")
else:
Actions.notify.emit(interact_prompt(null))
func _build(w: Node3D) -> void:
var rock := StandardMaterial3D.new()
rock.albedo_color = Color(0.42, 0.40, 0.38)
rock.roughness = 1.0
if w.terrain.noise_tex != null:
rock.albedo_texture = w.terrain.noise_tex
rock.uv1_triplanar = true
rock.uv1_scale = Vector3(0.18, 0.18, 0.18)
var body := StaticBody3D.new()
add_child(body)
# (local -Z faces the beach; the rocks are behind, +Z)
for part in [[Vector3(-2.0, 1.6, 1.0), Vector3(1.8, 3.6, 3.0)], [Vector3(2.0, 1.4, 1.0), Vector3(1.8, 3.2, 3.0)],
[Vector3(0, 3.5, 1.0), Vector3(5.8, 1.4, 3.2)], [Vector3(0, 1.4, 2.4), Vector3(2.4, 3.0, 0.6)],
[Vector3(-3.4, 1.0, 2.0), Vector3(2.0, 2.4, 3.0)], [Vector3(3.2, 0.8, 2.2), Vector3(1.8, 2.0, 2.6)]]:
var mi := MeshInstance3D.new()
var bm := BoxMesh.new()
bm.size = part[1]
mi.mesh = bm
mi.material_override = rock
mi.position = part[0]
mi.rotation.y = part[0].x * 0.06
add_child(mi)
var cs := CollisionShape3D.new()
var box := BoxShape3D.new()
box.size = part[1]
cs.shape = box
cs.position = part[0]
cs.rotation.y = part[0].x * 0.06
body.add_child(cs)
var dark := MeshInstance3D.new()
var q := QuadMesh.new()
q.size = Vector2(2.2, 2.8)
dark.mesh = q
var m := StandardMaterial3D.new()
m.albedo_color = Color(0.02, 0.02, 0.025)
m.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
dark.material_override = m
dark.position = Vector3(0, 1.4, 2.08)
dark.rotation.y = PI
add_child(dark)
# What you press E on: the opening (interactables layer: you walk into it).
var spot := StaticBody3D.new()
spot.collision_layer = 2
spot.collision_mask = 0
var cs2 := CollisionShape3D.new()
var b2 := BoxShape3D.new()
b2.size = Vector3(2.2, 2.8, 1.6)
cs2.shape = b2
spot.add_child(cs2)
spot.position = Vector3(0, 1.4, 1.2)
spot.set_script(preload("res://scripts/world/sea_cave_spot.gd"))
spot.cave = self
add_child(spot)