J1: the cliff tunnel (seam, six propped stretches, dark, lights), travel Valley <-> Cove through the sea cave, a save file per worldlet; New World has no Where (--dev-worldlets to try others)
This commit is contained in:
@@ -54,6 +54,9 @@ var _spawn_yaw := 0.0
|
||||
var _pieces: Node3D
|
||||
var hud: CanvasLayer
|
||||
var pier: Node3D ## the Cove's pier (scripts/world/pier.gd), or null
|
||||
var tunnel: Node3D ## the cliff tunnel (scripts/world/tunnel.gd, J1), or null
|
||||
var sea_cave: Node3D ## the Cove's end of it (scripts/world/sea_cave.gd), or null
|
||||
var traveling := false ## on the way to another worldlet (the scene is about to change)
|
||||
var _session_time := 0.0 ## seconds played since the last save
|
||||
var _autosave_left := AUTOSAVE_SECONDS
|
||||
var _menu_cam: Camera3D
|
||||
@@ -362,6 +365,25 @@ func _on_terrain_ready() -> void:
|
||||
props.pier = pr
|
||||
else:
|
||||
pr.free()
|
||||
# The journey's ways on (J1): the Valley's cliff tunnel and the Cove's sea
|
||||
# cave. Single-player for now (J3 brings travel online).
|
||||
if not server_mode and not menu_mode and not is_online():
|
||||
var tn := preload("res://scripts/world/tunnel.gd").new()
|
||||
tn.name = "Tunnel"
|
||||
add_child(tn)
|
||||
if tn.setup(self):
|
||||
tunnel = tn
|
||||
props.keep_clear.append([tn.seam_at, 4.0])
|
||||
else:
|
||||
tn.free()
|
||||
var sc := preload("res://scripts/world/sea_cave.gd").new()
|
||||
sc.name = "SeaCave"
|
||||
add_child(sc)
|
||||
if sc.place(self):
|
||||
sea_cave = sc
|
||||
props.keep_clear.append([sc.global_position, 6.0])
|
||||
else:
|
||||
sc.free()
|
||||
props.place_on(terrain, player if not menu_mode else _menu_cam, _spawn)
|
||||
# The wolf den: part of the scenery, so it's there from day 1 (empty).
|
||||
den = preload("res://scripts/creatures/den.gd").new()
|
||||
@@ -424,6 +446,7 @@ func _on_terrain_ready() -> void:
|
||||
if not state.is_empty():
|
||||
apply_save(state)
|
||||
print("[world] loaded '%s'" % Saves.current_id)
|
||||
_arrive(Saves.take_arrival(Saves.current_id))
|
||||
player.set_physics_process(true)
|
||||
set_physics_process(true)
|
||||
creatures.setup(self)
|
||||
@@ -652,6 +675,7 @@ func collect_save() -> Dictionary:
|
||||
"bear": bears.save_data() if bears != null else _saved_bear,
|
||||
"barrow": barrow.save_data() if barrow != null else {},
|
||||
"stones": stones.save_data() if stones != null else {},
|
||||
"tunnel": tunnel.to_data() if tunnel != null else {},
|
||||
}
|
||||
|
||||
func _shared_state(harvested: Dictionary, regrow: Dictionary, taken: Array, dropped: Array, pieces: Array) -> Dictionary:
|
||||
@@ -797,6 +821,8 @@ func _apply_save(s: Dictionary, with_player: bool) -> void:
|
||||
barrow.load_data(s.get("barrow", {}))
|
||||
if stones != null:
|
||||
stones.load_data(s.get("stones", {}))
|
||||
if tunnel != null:
|
||||
tunnel.load_data(s.get("tunnel", {}))
|
||||
# The bear (bear_range.gd): applied when it's set up, after loading.
|
||||
_saved_bear = s.get("bear", {})
|
||||
if bears != null:
|
||||
@@ -822,14 +848,82 @@ func _respawn() -> void:
|
||||
player.get_node("Camera3D").rotation.x = 0.0
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_current(delta)
|
||||
var p := player.global_position
|
||||
var under := underground_at(p)
|
||||
day_night.underground = move_toward(day_night.underground, 1.0 if under else 0.0, delta * 3.0)
|
||||
if weather_fx != null:
|
||||
weather_fx.visible = not under
|
||||
# (No water down there: the tunnel is far below the lake's surface.)
|
||||
player.water_level = -INF if under else terrain.water_level
|
||||
if under:
|
||||
return # (the tunnel is its own space, out of sight under the island)
|
||||
_current(delta)
|
||||
var half: float = terrain.size * 0.5
|
||||
if p.y < terrain.min_height - 30.0 or absf(p.x) > half or absf(p.z) > half \
|
||||
or not terrain.inside_boundary(p.x, p.z, -4.0):
|
||||
push_warning("[world] player left the map at %s; respawning" % p)
|
||||
_respawn()
|
||||
|
||||
## In the cliff tunnel (J1): no daylight, no weather, sheltered.
|
||||
func underground_at(p: Vector3) -> bool:
|
||||
return tunnel != null and tunnel.contains(p)
|
||||
|
||||
# --- the journey (J1, docs/WORLDLETS_DESIGN.md §10) ------------------------------
|
||||
|
||||
## Goes on to worldlet `to`: saves this one, takes your character (bag,
|
||||
## stats, what you wear) and the clock along, and loads the other, arriving
|
||||
## at `arrive` ("sea_cave", "tunnel_bottom"). Each worldlet keeps its own
|
||||
## state file (Saves.state_path), so what you built here waits for you.
|
||||
func travel(to: String, arrive: String) -> void:
|
||||
if traveling or not Saves.has_world() or is_online() or server_mode:
|
||||
return
|
||||
traveling = true
|
||||
var id := Saves.current_id
|
||||
var here: Dictionary = collect_save()
|
||||
Saves.save_state(id, here, _session_time)
|
||||
_session_time = 0.0
|
||||
var there: Dictionary = Saves.load_state(id, to)
|
||||
if there.is_empty():
|
||||
there = {"time": here.time} # (a new place: the same day and hour)
|
||||
there["player"] = here.player
|
||||
Saves.save_state(id, there, 0.0, to)
|
||||
Saves.set_worldlet(id, to, arrive)
|
||||
print("[world] travelling to %s (%s)" % [to, arrive])
|
||||
if hud != null:
|
||||
hud.toast("On to the %s..." % Terrain.worldlet_name(to))
|
||||
travelled.emit(to)
|
||||
if get_tree().current_scene == self: # (in a test the world isn't the scene: the test loads the next one)
|
||||
get_tree().change_scene_to_file.call_deferred(scene_file_path if not scene_file_path.is_empty() else "res://scenes/world.tscn")
|
||||
|
||||
signal travelled(to: String) ## saved and on the way to another worldlet
|
||||
|
||||
## Just arrived from another worldlet: put you where you came in.
|
||||
func _arrive(at: String) -> void:
|
||||
if at.is_empty():
|
||||
return
|
||||
var pos := Vector3.INF
|
||||
var facing := Vector3.FORWARD
|
||||
match at:
|
||||
"sea_cave":
|
||||
if sea_cave != null:
|
||||
pos = sea_cave.front
|
||||
facing = (sea_cave.front - sea_cave.global_position).normalized()
|
||||
"tunnel_bottom":
|
||||
if tunnel != null and tunnel.dug >= tunnel.STRETCHES:
|
||||
pos = tunnel.bottom + Vector3(0.8, 0.0, 0.0)
|
||||
facing = Vector3(1, 0, 0)
|
||||
if pos == Vector3.INF:
|
||||
push_warning("[world] nowhere to arrive at '%s': the start instead" % at)
|
||||
_respawn()
|
||||
return
|
||||
player.global_position = pos + Vector3(0, 0.2, 0)
|
||||
player.velocity = Vector3.ZERO
|
||||
player.rotation = Vector3(0.0, atan2(-facing.x, -facing.z), 0.0)
|
||||
if hud != null:
|
||||
hud.toast("The %s" % Terrain.worldlet_name(worldlet_id()))
|
||||
|
||||
const Terrain = preload("res://scripts/world/terrain.gd")
|
||||
|
||||
## Off a beach (W2), a current carries anyone swimming too far out back
|
||||
## towards the shore: the sea's version of the invisible wall. It's faster
|
||||
## than you can swim, so you can't push through it, and it says so once.
|
||||
@@ -858,9 +952,10 @@ func placement_valid(def: Dictionary, xf: Transform3D) -> bool:
|
||||
if BuildPiece.is_build(def):
|
||||
return build.check(def, xf).is_empty()
|
||||
var p := xf.origin
|
||||
if not terrain.inside_boundary(p.x, p.z, 1.5):
|
||||
var under := underground_at(p) # (candles and torch stands down the tunnel: its floor is the ground)
|
||||
if not under and not terrain.inside_boundary(p.x, p.z, 1.5):
|
||||
return false
|
||||
if p.y < terrain.water_level + 0.15:
|
||||
if not under and p.y < terrain.water_level + 0.15:
|
||||
return false
|
||||
var fp: Array = def.place.get("footprint", [1.0, 1.0])
|
||||
var hx := float(fp[0]) * 0.5
|
||||
|
||||
Reference in New Issue
Block a user