Each land's set pieces from its data: the barrow, the standing stones and the bear are the Valley's alone, the Cove keeps its wolves; the Cove's 0.6.0 plan (treasure: one each, each in its own place)

This commit is contained in:
Paul Bailey
2026-11-07 01:14:32 +00:00
parent 1bf33935e0
commit ec8caa9ef4
5 changed files with 75 additions and 16 deletions

View File

@@ -15,7 +15,7 @@
{"type": "ocean_edge"} {"type": "ocean_edge"}
], ],
"climate": {"snowline": 42.0}, "climate": {"snowline": 42.0},
"places": [{"type": "pier", "at": "beach"}], "places": [{"type": "den"}, {"type": "pier", "at": "beach"}],
"next": [ "next": [
{"to": "valley", "by": "tunnel", "where": "sea_cave"}, {"to": "valley", "by": "tunnel", "where": "sea_cave"},
{"to": "highlands", "by": "sail", "where": "pier"} {"to": "highlands", "by": "sail", "where": "pier"}

View File

@@ -14,5 +14,6 @@
{"type": "ocean_edge"} {"type": "ocean_edge"}
], ],
"climate": {"snowline": 40.0}, "climate": {"snowline": 40.0},
"places": [{"type": "den"}, {"type": "barrow"}, {"type": "stones"}, {"type": "bear"}],
"next": [{"to": "cove", "by": "tunnel", "where": "sea_cliff"}] "next": [{"to": "cove", "by": "tunnel", "where": "sea_cliff"}]
} }

View File

@@ -283,6 +283,46 @@ The steps:
- Later: tides (sea level shifting slowly), a raft or rowing boat, and - Later: tides (sea level shifting slowly), a raft or rowing boat, and
driftwood on the beach. driftwood on the beach.
### The Cove's own dangers and the pirate treasure (Paul, Nov 2026, for 0.6.0)
Paused at the Cove for a playtest before the Highlands. The Cove gets its
own set pieces instead of copies of the Valley's:
- **Set pieces per land**, listed in each worldlet's data (`"places"`). The
Valley keeps the wolf den, the barrow, the standing stones (the Old Stag)
and the bear; the Cove keeps the wolves only, and gets the wreck and the
treasure.
- **The wreck:** a beached ship on the rocks at the far end of the bay, its
timbers along the sand (driftwood). Its captain's sea chest: some nails,
rope, coins and a **torn treasure map**.
- **The map:** reading it puts an X on your N map: a mound of loose earth
by a marker stone in the Cove's hills (your own spot: see below).
- **The dig:** a pickaxe, many blows, turns up the **pirate chest**: a
**ship's compass** (what the boat will need to sail to the Highlands: the
next crossing's gate, J2), a **spyglass** (zooms your view while held),
**pirate gold**, and a **cutlass** (quick, harder-hitting than the iron
spear).
- **One treasure each, each in its own place** (online too): the wreck's
chest gives each player their own map, once, and each map's X is
somewhere different: picked from the world's seed and the player's id
(so it's fixed for them), among dry, gentle spots in the Cove's hills
away from the other places. You only see your own mound; the server knows
every player's spot and what each has found, and checks the right one
when you dig.
- **Gold** is a **trophy you can place** in your hut (a heap of coins) for
now; what else it's for waits until there's a trader.
- **The drowned crew:** the treasure's guards (the Cove's barrow guards).
Two or three rise from the shallows round the dig at night, or as the
chest comes up, for whoever's digging. Slow, hit hard, unafraid of fire,
they drag at you in the water.
- **Crabs** on the beach at dusk (pinch if trodden on; food), and **gulls**,
thieves like the crows (fish off drying racks, things dropped on sand).
- **The boat** (J2's first part): driftwood, seagrass rope and pitch (pine
resin and salt), at a workbench, placed at the pier. One each, moored,
yours and your friends'. Setting sail says the Highlands are uncharted
for now.
- **Report a bug** in the pause menu, and a short playtest guide.
- Order: set pieces per land, the wreck and map, the dig and the drowned
crew, crabs and gulls, the boat, report a bug; then 0.6.0 (protocol 18).
## 6. Done already (groundwork) ## 6. Done already (groundwork)
- `world.water_at(x, z)` returns `{type, surface, depth}` for the lake and - `world.water_at(x, z)` returns `{type, surface, depth}` for the lake and
the sea, and fishing uses it instead of `terrain.water_level`. the sea, and fishing uses it instead of `terrain.water_level`.

View File

@@ -394,13 +394,16 @@ func _on_terrain_ready() -> void:
props.static_cuts.append([den.global_transform, 2.2, 3.4]) props.static_cuts.append([den.global_transform, 2.2, 3.4])
_update_grass_cuts() _update_grass_cuts()
# The barrow: scenery too (P3), out on open ground. Its guards only run # The barrow: scenery too (P3), out on open ground. Its guards only run
# where creatures do (_setup_bear). # where creatures do (_setup_bear). (Each land has its own set pieces:
# the barrow and the stones are the Valley's: has_place().)
if has_place("barrow"):
barrow = preload("res://scripts/world/barrow.gd").new() barrow = preload("res://scripts/world/barrow.gd").new()
barrow.name = "Barrow" barrow.name = "Barrow"
add_child(barrow) add_child(barrow)
if barrow.place(self): if barrow.place(self):
_update_grass_cuts() _update_grass_cuts()
# The standing stones (P5): where the Old Stag is called. # The standing stones (P5): where the Old Stag is called.
if has_place("stones"):
stones = preload("res://scripts/world/standing_stones.gd").new() stones = preload("res://scripts/world/standing_stones.gd").new()
stones.name = "StandingStones" stones.name = "StandingStones"
add_child(stones) add_child(stones)
@@ -420,6 +423,7 @@ func _on_terrain_ready() -> void:
hazards.name = "Hazards" hazards.name = "Hazards"
add_child(hazards) add_child(hazards)
hazards.setup(self, not is_online()) hazards.setup(self, not is_online())
if stones != null:
stones.deciding = not is_online() stones.deciding = not is_online()
if server_mode: if server_mode:
if Saves.has_world(): if Saves.has_world():
@@ -491,6 +495,8 @@ func _setup_bear() -> void:
thieves.enabled = not "--no-creatures" in OS.get_cmdline_user_args() thieves.enabled = not "--no-creatures" in OS.get_cmdline_user_args()
if barrow != null and barrow.found and thieves.enabled: if barrow != null and barrow.found and thieves.enabled:
barrow.start() barrow.start()
if not has_place("bear"):
return # (bears only in the lands that list one: the Valley)
bears = preload("res://scripts/creatures/bear_range.gd").new() bears = preload("res://scripts/creatures/bear_range.gd").new()
bears.name = "Bear" bears.name = "Bear"
add_child(bears) add_child(bears)
@@ -1202,6 +1208,16 @@ func water_at(x: float, z: float) -> Dictionary:
# and its ways out. (W0 adds these; systems move over to them as each worldlet # and its ways out. (W0 adds these; systems move over to them as each worldlet
# needs them to be different.) # needs them to be different.)
## Whether this land has set piece `kind` ("den", "barrow", "stones",
## "bear", "pier", "wreck"...): its worldlet's "places". A worldlet that
## doesn't list them has the Valley's set (den, barrow, stones, bear).
const DEFAULT_PLACES := ["den", "barrow", "stones", "bear"]
func has_place(kind: String) -> bool:
var places: Array = terrain.worldlet.get("places", [])
if places.is_empty():
return kind in DEFAULT_PLACES
return places.any(func(p): return p is Dictionary and String(p.get("type", "")) == kind)
## This worldlet's id ("valley") and its whole definition. ## This worldlet's id ("valley") and its whole definition.
func worldlet_id() -> String: func worldlet_id() -> String:
return terrain.worldlet_id return terrain.worldlet_id

View File

@@ -59,7 +59,9 @@ func _body() -> void:
check(world.crossings().any(func(c): return c.to == "valley") and world.crossings().any(func(c): return c.by == "sail"), check(world.crossings().any(func(c): return c.to == "valley") and world.crossings().any(func(c): return c.by == "sail"),
"its ways on: back to the Valley, and on by sail") "its ways on: back to the Valley, and on by sail")
check(world._spawn.y > t.water_level + 1.0, "you start on dry land") check(world._spawn.y > t.water_level + 1.0, "you start on dry land")
check(world.den != null and world.barrow != null and world.stones != null, "the den, the barrow and the stones found places") check(world.den != null and world.den.found, "the Cove's wolves have a den")
check(world.barrow == null and world.stones == null and world.bears == null,
"but the barrow, the standing stones and the bear are the Valley's alone")
check(t.get_node_or_null("Water") == null, "one water surface (the bay is the sea)") check(t.get_node_or_null("Water") == null, "one water surface (the bay is the sea)")
print("the stream") print("the stream")
@@ -77,9 +79,9 @@ func _body() -> void:
"its banks are dry, and above the water") "its banks are dry, and above the water")
check(DB.water_fishing("stream").get("fish", []).any(func(f): return f.id == "trout"), "trout in the stream") check(DB.water_fishing("stream").get("fish", []).any(func(f): return f.id == "trout"), "trout in the stream")
var away := true var away := true
for p in [world.den.global_position, world.barrow.global_position, world.stones.global_position, world._spawn]: for p in [world.den.global_position, world._spawn]:
away = away and not t.near_stream(p.x, p.z, 3.0) away = away and not t.near_stream(p.x, p.z, 3.0)
check(away, "the den, the barrow, the stones and the start are all clear of it") check(away, "the den and the start are clear of it")
var in_bed := 0 var in_bed := 0
for n in world.props.resource_nodes.values(): for n in world.props.resource_nodes.values():
if is_instance_valid(n) and t.near_stream(n.global_position.x, n.global_position.z, 0.0): if is_instance_valid(n) and t.near_stream(n.global_position.x, n.global_position.z, 0.0):