0.4.0: map (N) that fills in as you explore and a compass (closes #1); Old Tam's seam and Cove tips (voiced); the journey and sea fish in the guide; trailer with the tunnel and the Cove; protocol 16

This commit is contained in:
Paul Bailey
2026-11-06 18:36:20 +00:00
parent 980ee53579
commit 2b7e52cd82
28 changed files with 678 additions and 43 deletions

View File

@@ -1,8 +1,9 @@
extends SceneTree
## Temporary: a ~32 s test trailer, recorded with Movie Maker:
## The trailer (~46 s), recorded with Movie Maker (tools/trailer/make_trailer.sh):
## godot --path . --fixed-fps 30 --write-movie out.avi --script tools/trailer/trailer.gd -- --no-creatures
## Six shots: dawn flyover + title, morning meadow, a hut at dusk, the wolf
## moon, a blizzard, and an end card.
## Eight shots: dawn flyover + title, morning meadow, a hut at dusk, the wolf
## moon, a blizzard, down the cliff tunnel, the Cove's pier, and an end card
## over the bay (with the version from project.godot).
const FPS := 30.0
const UITheme = preload("res://scripts/ui/theme.gd")
@@ -32,6 +33,37 @@ func _run() -> void:
H.path = "user://trailer_hints.json" # (never your real tips)
H.load_prefs()
H.enabled = false
# (Its own save folder, so the Cove can be loaded as a world of its own.)
var S: Node = root.get_node("Saves")
S.root_dir = "user://trailer_worlds"
_overlay()
await _enter_world("")
await _stage()
await _shot_dawn()
await _shot_meadow()
await _shot_hut()
await _shot_wolves()
await _shot_blizzard()
await _shot_tunnel()
await _enter_world("cove")
await _shot_cove()
await _shot_end()
for w in S.list_worlds():
S.delete_world(w.id)
DirAccess.remove_absolute(S.root_dir)
DirAccess.remove_absolute(H.path)
quit(0)
## Loads the world to film: the Valley with no save (""), or a fresh world
## in `worldlet`. Hides the HUD and takes the camera.
func _enter_world(worldlet: String) -> void:
var S: Node = root.get_node("Saves")
if world != null:
black.modulate.a = 1.0
world.queue_free()
for _i in range(3):
await process_frame
S.current_id = "" if worldlet.is_empty() else S.create_world("Trailer", 1337, {"worldlet": worldlet})
world = load("res://scenes/world.tscn").instantiate()
root.add_child(world)
for _i in range(8):
@@ -53,16 +85,6 @@ func _run() -> void:
cam.fov = 62
world.add_child(cam)
cam.current = true
_overlay()
await _stage()
await _shot_dawn()
await _shot_meadow()
await _shot_hut()
await _shot_wolves()
await _shot_blizzard()
await _shot_end()
DirAccess.remove_absolute(H.path)
quit(0)
# --- set dressing --------------------------------------------------------------------
@@ -261,14 +283,65 @@ func _shot_blizzard() -> void:
if world.weather_fx != null:
world.weather_fx.visible = false # (no leftover snow on the end card)
## Down the cliff tunnel (J1): dug through and propped, lit by torch stands
## you've set, the camera walking down the first stretch in the dark.
func _shot_tunnel() -> void:
var t: Node3D = world.tunnel
if t == null:
return
t.opened = true
t.dug = t.STRETCHES
for k in range(1, t.STRETCHES + 1):
t.props[k] = true
t._rebuild()
var ts: Dictionary = ItemDBRef().get_item("torch_stand")
var candle: Dictionary = ItemDBRef().get_item("tallow_candle")
var land1: Vector3 = t.ORIGIN + Vector3(t._landing_x(1), t._floor_y(1), t._lane_z(1))
world.spawn_piece(ts, Transform3D(Basis(), t._mouth(1) + Vector3(-0.5, 0, 0.9)))
world.spawn_piece(ts, Transform3D(Basis(), land1 + Vector3(0.6, 0, -0.8)))
world.spawn_piece(candle, Transform3D(Basis(), t._mouth(1) + Vector3(3.0, -2.0, -0.95)))
world.spawn_piece(candle, Transform3D(Basis(), t._mouth(2) + Vector3(-0.4, 0, 0.95)))
for _i in range(5):
await physics_frame
var from: Vector3 = t.top + Vector3(-0.6, 1.65, 0)
var to: Vector3 = land1 + Vector3(-1.4, 1.65, 0)
dn.underground = 1.0
await _shot(5.5, func(_t: float, k: float):
dn.underground = 1.0
_clock(13.0)
var e := k * k * (3.0 - 2.0 * k)
var pos := from.lerp(to, e)
cam.global_position = pos
cam.look_at(pos + Vector3(3.0, -1.4 + e * 0.9, 0.0) + Vector3(0, 0, lerpf(0.0, 1.2, e)))
_park_player(pos), "Dig through the cliffs.")
dn.underground = 0.0
## The Cove (W3): morning over the bay, along the pier.
func _shot_cove() -> void:
var pier: Node3D = world.pier
if pier == null:
return
var side: Vector3 = pier.dir.cross(Vector3.UP)
var from: Vector3 = pier.end + pier.dir * 18.0 + side * 14.0 + Vector3(0, 2.5, 0)
var to: Vector3 = pier.end + pier.dir * 6.0 + side * 7.0 + Vector3(0, 1.6, 0)
await _shot(6.0, func(_t: float, k: float):
_clock(8.2 + k * 0.3)
var e := k * k * (3.0 - 2.0 * k)
cam.global_position = from.lerp(to, e)
cam.look_at(pier.start.lerp(pier.end, 0.35) + Vector3(0, 1.0, 0))
_park_player(Vector3(pier.start.x, pier.start.y + 1.0, pier.start.z)), "Find what lies beyond.")
func _shot_end() -> void:
big.text = "EMBERWILD"
sub.text = "Self-hosted co-op survival · 0.1.0"
var b := Basis(Vector3.UP, yaw)
sub.text = "Self-hosted co-op survival · %s" % String(ProjectSettings.get_setting("application/config/version", ""))
# Rising over the Cove's bay, looking back at the land.
var pier: Node3D = world.pier
var c: Vector3 = Vector3.ZERO if pier == null else pier.start
var out: Vector3 = Vector3(0, 0, 1) if pier == null else pier.dir
await _shot(6.0, func(t: float, k: float):
_clock(17.6 + k * 0.4)
cam.global_position = _g(hut_c + b * Vector3(10, 0, 9), 0.0) + Vector3(0, 6.0 + k * 10.0, 0) + b * Vector3(0, 0, k * 10.0)
cam.look_at(hut_c + Vector3(0, 1.0, 0))
_park_player(cam.global_position)
cam.global_position = c + out * (40.0 + k * 14.0) + Vector3(0, 8.0 + k * 14.0, 0)
cam.look_at(c - out * 30.0 + Vector3(0, 4.0, 0))
_park_player(c + Vector3(0, 1.0, 0))
big.modulate.a = _env(t, 0.8, 5.6, 0.8, 0.6)
sub.modulate.a = _env(t, 1.6, 5.6, 0.8, 0.6), "", 0.7)