26 KiB
Worldlets design
Status: W0-W3 and J1 built (the Cove without its stream yet; the tunnel single-player only); the rest is planned.
As built in J1:
scripts/world/tunnel.gd: the seam (an outcrop on the clifftop strip, 7 m in from the edge, its angle from the seed) and the tunnel space, out of sight 260 m under the island: a CSG rock block with the landings and six sloping stretches cut out (6 m on, 4 m down each, switching back on two lanes: 24 m down), then a sea cave. Faces (tunnel_face.gd) answer like boulders, so the pick works on them: tier 3 only ("Too hard: you need a better pick"), 10 blows for the seam and 15 per stretch. Each stretch must be propped (2 wood, [E] at its mouth) before the next face takes a blow ("It's loose..."): 10 wood all the way down.- Underground: no sun, moon, sky reflections or sky-tinted fog
(
day_night.underground), no weather, no lake water, sheltered. Candles and torch stands place anywhere on its floor and save as ordinary pieces. - Walking out of the sea cave's mouth travels (
world.travel()): the worldlet you leave is saved, your character and the clock go with you, and the other loads, arriving at its sea cave (sea_cave.gd, on the Cove's beach where the headland starts). [E] at the cave goes back up the same tunnel to its bottom, once it's dug through. - Saves:
world.jsonis the home worldlet's state, as before; others areworldlets/<id>.json; meta.json'sworldletis where you are,homewhere you began,arrivewhere the next load puts you. tests/test_tunnel.gd: dig through, walk every stretch down and back up, light it, reload, out to the Cove, back up, out at the top, and the chest at home is still there.- Not yet: Old Tam's tip about the seam, the Guide page, and travel on a server (J3: the tunnel isn't built online). The journey between worldlets is §10, and the two ways to play (the journey, or one land) are in its decisions.
As built in W3:
data/worldlets/cove.json: low hills, mountains behind, a wide sandy bay (abeachblock) and a small pond. Not a start; itsnextis back to the Valley (tunnel, J1) and on to the Highlands (sail).- The pier (
scripts/world/pier.gd), a world "place" ("places": [{"type": "pier"}]): planks on posts from where the sand is at deck height to 24 m past the waterline. Worked out from the terrain, so it's the same for everyone and never saved or dismantled; scenery keeps clear of it. ~1.9 m of sea at its end, ~2.7 m a cast beyond. - Sea fish in
data/fish.json: mackerel (common) and cod, which only bites in water 2.2 m or deeper (a per-fishmin_depth). Grilled at a campfire, salted, or smoked, like the lake's. - New World has no "Where": the journey always begins in the Valley (the
Host page keeps it, for One land mode). Started with
godot --path . -- --dev-worldlets, New World and Host offer every worldlet, the others marked "(testing)", to try one directly. - No sea stacks without a sea cliff.
- Tests:
test_cove(walk onto the pier, catch a mackerel off its end, cod only in deep water), and the Cove intest_terrain_edgesandtest_boundary. - Still to do: the stream (it needs water that runs downhill, which the one flat water surface can't do: its own ribbon of water, planned with the Cove's sea cave when the tunnel arrives).
As built in W2:
lakeblocks can repeat: every lake and pond is its own body (terrain.lakes,world.water_bodies()), all at one surface (water_level, set by the first). The first is still "the lake" to the older code (ice, its fish, the tutorial).- A
beachblock (angle, width,shore_offset,slope,current_out,wall_out): the land eases to a waterline and shelves gently into the sea, which is then the same surface as the ponds (one ocean sheet, no inland disc). The invisible wall stands well out in the water, and before it a current (terrain.current_at(),world._current()) carries swimmers back faster than they can swim, with a toast. water_at()tells the sea off a beach from a pond.- Test-only worldlets live in
tests/worldlets/(never listed or offered).testbay.json(two ponds and a beach) runs throughtest_water_bodies,test_terrain_edges(8 seeds) andtest_boundary(swimming out across the beach).
As built in W1: a "Where" picker on the New World and Host Game pages
(worldlets with "start": true; only the Valley, so it's greyed out), the
worldlet in the Load list, and a hosted island keeps its worldlet like its
seed.
As built in W0:
data/worldlets/valley.json: the Valley as data, its terrain as five building blocks in order (hills,rim,sea_cliff,lake,ocean_edge) with every number that used to be hard-coded.terrain.gdruns a worldlet's blocks (use_worldlet()); each block is the old code's arithmetic, unchanged.tests/test_worldlets.gdchecks four seeds give exactly the same height grid as before (SHA-256 of the grid, taken from the old code).- The worldlet is chosen like the seed: a server's (it sends its real one),
else the save's
meta.json"worldlet", else the Valley (worlds from before W0). New worlds and servers record it. - The WorldInfo API on the world:
worldlet_id(),water_bodies(),climate(),crossings()(the planned ways on), besidewater_at(). Systems still read the terrain's numbers directly; they move over to these as each new worldlet needs them to differ (W1-W3).
1. What a worldlet is
A worldlet is a small, complete world type: a ~320 m playable area with its own shape, water, climate, wildlife and features, generated from a seed like today's island. The current map becomes the first worldlet, "Valley": a mountain ring, a lake and a sea cliff. Others can differ in kind, not just in seed:
| Worldlet | Shape | Water | Twist |
|---|---|---|---|
| Valley (today) | mountain ring + sea cliff | lake | wolves' den in the rim |
| Cove | low hills down to a sandy bay | sea + harbour, a stream | sea fishing, a pier, tides later |
| Highlands | plateau, cliffs, a tarn | small cold lake | snow most days, harsher nights |
| Marsh | flat, reedy, pools | many shallow pools | fog, insects, bog iron later |
| Tutorial | small (~160 m), calm, fixed seed | one pond | guided steps, a fenced rabbit meadow, named spots (docs/TUTORIAL_DESIGN.md) |
A worldlet is data plus a few generator building blocks, not a new scene with its own code. That keeps the game's systems shared (building, survival, creatures, saving) and makes worldlets addable by addons later (§7).
2. The big decision: one worldlet per save, or linked worldlets?
A. One worldlet per save. New World asks for a name, a seed and a worldlet type. Simple, and it fits everything already built.
B. Linked worldlets in one save. Several worldlets form one world, and you travel between them (a mountain pass, a cave tunnel, a boat). Each keeps its own state. More exciting, but much more work, especially in M2: a server would have to simulate several places at once, or keep all players together.
Recommendation: build A first, with a save layout that allows B later.
Each worldlet's state goes in its own file from day one
(worlds/<id>/worldlets/<worldlet>.json), and the player's own state moves
to worlds/<id>/player.json. Then B is "add travel and a second file",
not "migrate every save".
3. Definition format
data/worldlets/<id>.json. Every number that's an @export on
terrain.gd today moves here, plus the lists that are hard-coded:
{
"id": "cove",
"name": "Cove",
"description": "Low hills running down to a sheltered bay.",
"size": 320,
"terrain": {
"base": {"height_scale": 7.0, "octaves": 4},
"boundary": [
{"type": "mountains", "from_deg": 60, "to_deg": 300, "height": 50},
{"type": "beach", "from_deg": 300, "to_deg": 60, "slope": 0.08}
],
"features": [
{"type": "cove", "angle_deg": 0, "width": 60},
{"type": "stream", "from": "mountains", "to": "cove"}
]
},
"water": {"sea_level": -1.5, "lakes": []},
"climate": {"cold_day_chance": 0.1, "snowline": 45, "day_length": 1200},
"wildlife": {
"budget": {"rabbit": 5, "deer": 3, "boar": 2},
"wolves": {"den": "rim", "den_day": 3, "hunt_day": 5}
},
"places": [{"type": "pier", "at": "cove"}],
"spawn": {"near": "cove", "facing": "sea"}
}
Generator building blocks (GDScript, shared by all worldlets): base hills, mountain rim, sea cliff, beach slope, cove/bay, lake basin, stream bed, plateau. A worldlet only picks and parameterises them, so it can't do anything a block can't.
4. What changes in the code
Most game code already asks the terrain generic questions:
sample_height, sample_normal, inside_boundary and find_spawn. The
work is in the places that know about this island's single lake and
cliff. Today:
| Assumption | Used in | Becomes |
|---|---|---|
one lake at water_level |
player (swimming), creature, spawner, den, build_system, props, lake_fish, world | world.water_at(x, z) (done for fishing) |
lake_center / lake_radius |
lake_fish, world (views) | the water bodies list |
sea_angle_deg, coast_point |
props (sea stacks, cliff-top strip), world (views) | the boundary segment list |
snow_height |
props, spawner, weather | climate.snowline |
play_radius |
props, den | boundary queries |
The steps:
- Terrain pipeline.
terrain.gdbecomes a runner that stacks the chosen building blocks into the height grid. The grid, collision,sample_heightand the boundary wall stay exactly as they are. - A
WorldInfoAPI on the world node, for everything outside terrain:water_at,water_bodies(),boundary_at(angle),features(type),climate. No system reads terrain exports directly any more. - Water bodies. Each lake, pond and the sea is a body with a surface,
shape and type. The water mesh is built per body. Swimming, the "too
deep" check, animal steering and fishing all go through
water_at. - Props and places by feature. Scenery reads densities from the definition. The den, the sea stacks and (in Cove) the pier are "places" positioned by features, not hard-coded angles.
- Saves.
meta.jsongains"worldlet": "valley". Older saves default to Valley, so nothing breaks. - New World screen. Pick a worldlet type (with a short description and a preview render), then name and seed as now.
5. The Cove and sea fishing
- Beach boundary. Sand slopes gently into shallow water. You can wade and swim out until the water is deep, and then a current carries you back (the beach equivalent of the invisible wall, with a toast).
- Harbour. A sheltered bay with a generated wooden pier. The pier is built from the building pieces, so it's "placed by the world" and can't be dismantled.
- Sea fish in
data/fish.jsonunder"sea": mackerel (common), cod (bigger, deeper water), and maybe crab pots later. Casting from the pier reaches deep water. The fishing code needs no changes: it already askswater_atwhat it landed in and looks up that water's fish. - Later: tides (sea level shifting slowly), a raft or rowing boat, and driftwood on the beach.
6. Done already (groundwork)
world.water_at(x, z)returns{type, surface, depth}for the lake and the sea, and fishing uses it instead ofterrain.water_level.data/fish.jsonholds the fish per water type, bite timings and dawn/dusk weights.ItemDB.define_water()registers a water's fish, so an addon or a worldlet can add "sea" without code.
7. How it fits the roadmap
- Before M2 (recommended): steps 1-6 for Valley alone, with no visible change, then the Cove. Doing the refactor before multiplayer means M2 replicates "worldlet id + seed + edits", not one hard-coded island.
- M2: the server loads one worldlet per save (option A). Clients regenerate it from id + seed, which is the same determinism we rely on now.
- M3 (addons):
define_worldlet()takes the same JSON. Addons can only combine building blocks, so a worldlet addon can't run arbitrary code in the generator. - Option B (linked worldlets) fits after M2, once we know how the server handles players in different places.
8. Phasing and tests
| Phase | Delivers | Done when |
|---|---|---|
| W0 | terrain pipeline + WorldInfo API; Valley defined in JSON | seed 1337 generates the same height grid as today (a hash test), and all 9 suites still pass |
| W1 | worldlet picked on New World and saved in meta; old saves = Valley | load an old save and a new Valley save; both play |
| W2 | water bodies (several per world), beach boundary block, swim-back current | a test worldlet with two ponds and a beach passes the boundary and terrain-edge tests |
| W3 | Cove worldlet: bay, stream, pier place, sea fish | you catch a mackerel off the pier |
| W4 | (optional) Highlands / Marsh | each passes the shared suites |
| W5 | (after M2, optional) linked worldlets and travel | walk through a pass from Valley into Cove and back |
Every existing suite runs per worldlet (boundary, terrain edges, survival, building, wildlife, fishing), so a new worldlet can't ship with a hole in its edge or animals that spawn in the sea.
9. Questions for you
- A or B? One worldlet per save to start, or do you want travel between them early? (Recommendation: A, with the save layout ready for B.)
- Worldlet size: keep ~320 m, or let some be bigger?
- Difficulty per worldlet: should harsher worldlets (Highlands) bring wolves sooner, or keep the same grace period everywhere?
- Which second worldlet first: Cove (sea fishing, the beach you mentioned) is my suggestion.
10. The journey: tunnelling out (proposal, Nov 2026)
A goal for the game, from Paul: once you have a good enough pick, you tunnel through the mountain rim into the next, slightly harder worldlet, and now and then go back to earlier ones for what only they have. This is option B (linked worldlets) from §2, reached by digging rather than a path.
The shape of it
- A chain of worldlets, each a little harder: Valley (today's island) → Cove (sea, salt, mackerel) → Highlands (cold, deep snow, bigger packs) → Marsh (bog iron, sickness, mists) → the Ember, the volcanic heart the game is named for: the end of the journey.
- The goal, something to tell players on day 1: Old Tam talks of the land beyond the mountains, where the fire in the earth still burns. Reach the Ember and light the old beacon there. (Whether there's an ending or it's open-ended is a question below.)
- Gates: each tunnel needs the best tool from the worldlet you're in. The antler pickaxe (from the Old Stag) opens the Valley's tunnel; the Highlands need a pick made from something found in the Cove, and so on. Each worldlet has its own boss or trial, like the Old Stag.
A different crossing each time (Paul, Nov 2026)
Players have to work out how to get from each worldlet to the next, and it's a different way every time: tunnelling, climbing, burrowing, sailing. Each crossing is that worldlet's puzzle, and what it needs comes from what you've learned and gathered there:
| From → to | Crossing | Needs (a first idea) |
|---|---|---|
| Valley → Cove | Tunnel through the rim at a seam | the antler pickaxe, timber props, light |
| Cove → Highlands | Sail out of the bay and round the headland | a boat (planks, nails, a hide sail, rope), calm weather, food that keeps |
| Highlands → Marsh | Climb a frozen cliff | climbing picks and rope, warmth to last the climb (bear cloak, pemmican) |
| Marsh → Ember | Burrow under the old lava crust | a digging tool from bog iron, and a way through the heat and gas |
- The hint, not the answer: each worldlet shows its way out without explaining it: a seam in the rock with old pick marks, a wrecked boat on the beach, rope frayed on a cliff ledge. Old Tam (and his tips) nudge, and the guide can keep it vague.
- Coming back uses the same crossing in reverse (the tunnel stays dug, the boat stays moored, the rope stays fixed), so supply runs get easier once you've made the way.
- New systems this needs, one per crossing: digging into a rim seam (tunnel and burrow share most of it), a boat you build and steer on the water, and climbing a marked face. Each is a phase of its own (J1 tunnel; later the sail, the climb, the burrow).
Tunnelling (the first crossing)
- Where: each worldlet's rim has one or two seams: cracked, darker rock faces with a few loose stones at the foot, findable by exploring (and Old Tam's tips). The rim stays a wall everywhere else.
- How: hit a seam with a good enough pick, many times. Each stretch dug needs timber props (wood) or it falls back in, and it's dark in there: candles, torch stands or a lantern. The tunnel is a short, built-for-it cave (a few bends, maybe a side chamber with ore or a hazard), so it stays hand-made rather than a hole through the terrain mesh, which the heightmap can't do.
- Through: the far end opens onto the next worldlet. Walking out loads it (a short fade, "The Cove"), with your bag, cloak and stats as they are.
The first tunnel: down the sea cliff to the Cove
The Valley's sea side ends at a sheer cliff, its top about level with the valley and the sea some 25 m below. The Cove is down there, so the first tunnel goes down, inside the cliff, from the clifftop to the shore.
- The seam: a cracked outcrop on the clifftop strip near the edge (the sea_cliff block marks where), with old pick marks and loose stones at its foot. It can't be dug until you have the antler pickaxe (tier 3); a lesser pick just chips it ("Too hard: you need a better pick").
- Digging down: the tunnel is about six stretches, each a few metres on and a few down: a sloping passage that turns back on itself like a stair, so the 25 m drop fits inside the cliff. A stretch takes many pick blows (stamina, and wear on the pick) to open.
- Props: each new stretch needs a timber prop (2 wood) set at its mouth before you can dig the next. Leave one out and the face won't take another blow ("It's loose: prop it first"); nothing caves in on you, so it's a cost, not a trap. That's 10 wood all the way down.
- Light: the tunnel has no daylight at all. Every stretch is pitch dark until you bring light: a torch in your hand works, but your hands are full with the pick, so the way to do it is to set candles and torch stands as you go (tallow candles from bear fat, torch stands from wood). Placed lights stay, so the way is lit for the trip back and for friends.
- The bottom: the last stretch breaks out into a sea cave, its mouth opening on the Cove's shingle beach. Walking out of the mouth is the crossing: a fade, then the Cove, arriving at the cave's Cove end. The way back starts there and climbs the same tunnel.
- What's saved: the tunnel belongs to the Valley's save: how far it's dug, the props, the lights. On a server it's shared: anyone can dig on from where someone stopped, and the lights are everyone's.
- How it's built: the tunnel is its own small space (a "between" scene next to the worldlet), made from a kit of rock pieces (straight, turn, slope down, the cave mouth) laid out from the seed, with box-shaped colliders. The heightmap can't have holes or overhangs, so the passage is never cut into the terrain itself: you walk into the seam and you're in the tunnel scene.
Why go back
- Each worldlet has things the others lack. Clay and salt only in the Valley and Cove; little wood in the Highlands; no bears outside the Valley (so bear fat for candles and pemmican is a supply run); a fish or herb only one place has. Food that keeps (pemmican, salted and smoked meat, clay jars) becomes what you carry between them.
- Your bases stay where you built them: each worldlet keeps its own save file, so your Valley hut, chests and jars are waiting.
- Time: a worldlet you're not in stands still (as a server does with nobody on), so a base you left doesn't starve or get raided.
Building it (phases)
| Phase | Delivers | Done when |
|---|---|---|
| W0-W2 | the worldlet groundwork above (terrain pipeline, worldlet in the save, several water bodies) | as §8 |
| J1 | seams in the rim, digging with props and light, a tunnel scene, travel between two worldlets in single-player, per-worldlet save files | dig through from the Valley, walk to the Cove, walk back to your hut |
| W3 | the Cove (§5) as the second worldlet | its suites pass, and its tunnel back works |
| J2 | gates and supply needs: tool tiers per tunnel, resources only some worldlets have | a test bot can't open the second tunnel without the Cove's pick |
| J3 | multiplayer travel (see below) | two players in different worldlets on one server, or the rule we choose |
| J4 | Highlands, Marsh, the Ember and its ending | the journey can be finished |
Multiplayer: the hard part
A server today runs one island. Options:
- Everyone together: travel needs every player at the tunnel (like the sleep rule). Simplest; one worldlet runs at a time.
- Spread out: the server runs each worldlet that has someone in it. The most freedom, and the most work and CPU.
- Party leader: whoever goes through brings the others with a vote.
Decisions (Paul, Nov 2026)
- Extendable, not finished: the Ember has a last boss, and after it you keep playing. The chain is built so more worldlets (and crossings) can be added after the Ember later: each worldlet names the next, nothing assumes the Ember is the end.
- The crossings: tunnel, sail, climb, burrow, in that order.
- Harder by biome, not just by numbers: each worldlet has its own, stronger animals and its own hazards. The Marsh, for one: mosquitoes whose bites can make you sick, and bog, mist and the like. Others in the same spirit per biome (to design with each worldlet).
- Multiplayer: spread out. Each player goes where they like; the server
runs every worldlet someone is in, and the rest stand still. Built in J3,
after single-player travel works. What that means for the code:
- one server process holds several worldlet scenes, each with its own clock, weather, creatures and save file;
- every player belongs to one worldlet at a time, and the server only sends them that worldlet's world, creatures, sounds and chat-free events (chat and the player list stay server-wide);
- a crossing moves a player from one worldlet to another (save their place, load or wake the other worldlet, send them its world);
- sleep: "everyone in bed" counts the players in that worldlet only;
- the Old Stag and other bosses scale with the players in their worldlet.
- Boats: one each, no passengers. A boat belongs to whoever built it (like a bed): only the owner, and their friends under the server's locks rule, can sail it, and nobody else can dismantle it. It stays moored where it was last left (the Cove end or the Highlands end), so nobody is stranded by someone else taking the only boat. Lose or wreck it and you build another, which is harder where wood is scarce (the Highlands), so plan the trip back. Each player has to earn their own crossing by boat.
- The other crossings are shared: a dug tunnel, a fixed climbing rope and a dug burrow are changes to the world, there for everyone once one player has made them (like a door or a bridge).
- Two ways to play (Paul, Nov 2026).
- The journey (the standard game), single-player and multiplayer alike: every new world starts in the Valley and the worldlets come in their fixed order (Valley, Cove, Highlands, Marsh, Ember). Each worldlet's shape is set by its definition; the seed only changes the detail (hills, trees, where the den is), so the journey is the same adventure for everyone.
- One land (a hosting choice): no journey, no crossings, the server
is locked to one worldlet, and the host picks which, from all of them.
Co-op or PvP as now (the rules preset). In code: a
modeinserver.json("journey" / "one_land"); in one-land mode the Host page's "Where" lists every worldlet rather than only the starts, and the crossings (world.crossings()) answer none, so a tunnel seam can't be dug. Kept, like the seed and the worldlet, once the island exists. Worth building once the Cove exists (until then there's only the Valley to pick).
Server cost, measured (0.3.1)
One worldlet, a dedicated server on the M5 Pro, never pausing: about 12% of one CPU core and 270 MB of memory, with nobody on. Players add creatures, weather and pickups around them. So with spread out, the worst case of 8 players in all 5 worldlets is about five of today's servers in one process: roughly 1.4 GB and a little over half a core before players, and worldlets with nobody in them stand still and cost next to nothing. Comfortable on the home Mac; a small cloud server (2 GB) would be tight.
(Answered) questions
- The ending: a real ending at the Ember (light the beacon, a last boss, the credits) or open-ended (the journey is the goal, keep living)?
- The crossings: do the four above fit (tunnel, sail, climb, burrow), and in that order? Should a worldlet have more than one way out (a hard one and a long one)? And for the tunnel: only at marked seams, or anywhere in the rim (more freedom, harder to make good tunnels)?
- Multiplayer: everyone together, spread out, or a party vote?
- How much harder each worldlet: stronger animals and weather only, or new rules too (thirst in the Ember, sickness in the Marsh)?
- Order: Cove second (sea fishing, gentle) as planned, or straight into the Highlands for a harder step?