Skip to content
Felucca Online

Docs

World atlas

Decision #97. The first stage of building the game world: a seeded generator that plans the whole map (geography, the two factions and their borders, provinces, settlements, roads, resources, dungeons, sites and encounters) and draws it as pixel art at 1 pixel = 1 tile. The next stage builds the playable map (terrain, statics, towns) from this plan; until then the atlas is a design document that happens to be generated.

Code: crates/uo-atlas/. Run:

cargo run --release -p uo-atlas -- --seed 1 --out atlas-out      # ≈ 1.5 min, ≈ 40 MB
cd atlas-out && python3 -m http.server 8765                       # then open http://localhost:8765
cargo test -p uo-atlas                                            # design invariants on 3 seeds

--size N sets the map side in tiles (multiple of 1024, default 16384), --no-tiles skips the 1:1 and 1:4 tile pyramids, --crop x,y,w,h writes 1:1 crops.

Scale

  • 1 tile = 1 m (#74). The default map is 16384 × 16384 tiles (268 km² of map, about 95 km² of land at seed 1).
  • Running costs 250 ms per tile out of a fight (#92): crossing the whole map edge to edge takes 68 minutes in a straight line, the main island about 45 to 60 minutes, more with detours around mountains, lakes and rivers. In a fight a step costs 500 ms. For comparison, UO's Britannia is 6144 tiles wide, about 25 minutes at the same speed.
  • The server cuts the map into 64-tile regions (#87): 256 × 256 = 65 536 regions.
  • Content density is set per land area, so --size scales the number of towns, dungeons and sites with the land.

Shape of the world

The layout is designed, the details are noise (geo.rs, Layout):

  • The main island: a strongly warped blob with lobes (harmonics 2 to 7 of its outline), gulfs that bite deep into the land and fjord-cut coasts. Both factions live here.
  • The frontier: a wavy curve across the island separates the cold north from the warm south. A valley is carved along it, and the Sunder, the great river, runs down it to the sea. Upstream of the valley the frontier is a mountain barrier, so armies meet in the river valley.
  • The north is a highland plateau with ranges, closed by a mountain wall. Beyond its crest lie the wild north and the snow peaks.
  • The south is low, warm and wet: forests, lakes, the magic Elderwood, jungle and fens on the far coast.
  • The archipelagos: one island chain off every corner of the map plus one or two off the sides, cut by straits into many islands. One northern chain is the fire isles, with a volcano and ash lands. A strait always separates the chains from the main island.

Relief and drainage run on a macro grid of 16 × 16 tiles (1024² cells): elevation from the layout, a priority flood from the sea (lakes fill basins, every cell drains to the sea), runoff accumulation (rivers by drained area), temperature by latitude and height, moisture from westerly winds with rain shadows. Biomes come from one rule (geo::classify), used for the macro grid and again per tile.

Biomes (atlas categories, each with the game ground type it will be built from): sea, lake, beach, grassland, steppe, heath, forest, elderwood, taiga, jungle, swamp, tundra, glacier, hills, mountain, snow, ashland, farmland.

Geopolitics

PolityWhereCharacter
Karrak Dominionthe northern plateauminers, smiths, stone castles; gold, gems, rare ores, granite and marble, pine and some frostwood; short of bow woods, fine timber, grain and herbs
Aelwyn Concordthe southern forestsforest cantons sworn around the Elderwood; yew, heartwood, bloodwood, ash, grain, herbal reagents, silk; short of gold, rare ores, gems, good iron and stone
The Marcha band either side of the border, wider in open countryclaimed by both, held by neither: border forts, battlefields, deserters, smugglers; the Sunder's bridges are here
Free citythe Sunder's moutha neutral port and its hinterland
The wildsbeyond the northern wall, the snow peaks, the deep fensno one's: giants, orcs, trolls, rich deep veins
The islesevery island off the main islandno law at all, and the richest land

The world is called Felucca (the atlas is the Atlas of Felucca). Faction names are working names; the generated names (provinces, towns, dungeons, seas) change with the seed.

How the land is split (polity.rs): a Dijkstra from each capital with culture-specific travel costs (Karrak walks hills and taiga easily, Aelwyn forests and the Elderwood; rivers cost to cross, the Sunder a lot; being on the far side of the frontier costs extra). The cheaper realm holds a cell, unless both are too far (wilds). The March grows from the border cells by travel cost, unevenly. Every land cell off the largest landmass belongs to the isles.

Provinces (settle::provinces): every faction capital and city, every March town and the free city is the seat of a province reached by travel cost within its own realm (crownlands, duchies, earldoms, holdfasts; cantons, vales, wealds, glades; marches and baronies). Wild lands are named by what they are (fangs, wastes, fens), each archipelago is one lawless province. Provinces are meant to become the fiefs of the lord and vassal layer (#77).

Designed scarcity

The point of the split: each realm needs what the other has, and both need the isles.

  • Karrak holds the continent's gold, gems and rare ores (shadow iron, bronze, agapite, verite) on its plateau and in the wild peaks beyond it, plus granite, marble and pine. It has no bow woods to speak of, poor harvests (short summers) and few herbs.
  • Aelwyn holds the bow woods (yew, heartwood, bloodwood), ash and oak, most of the grain and the herbal reagents (mandrake, nightshade, ginseng, garlic, spider's silk). Its south has only iron, dull copper, copper and bog iron.
  • The March has the valley's farmland and a few prize veins.
  • The isles hold the largest share of the best of both: gold, agapite, verite and most of the valorite, gems, heartwood, bloodwood, frostwood, almost all black pearls and all the sulfurous ash (black powder for kegs, #71), so sieges need the fire isles.

Seed 1, share of each resource by richness (Karrak / Aelwyn / March / Wilds / Free / Isles):

ResourceKarrakAelwynMarchWildsIsles
gold20 %0 %0 %17 %63 %
valorite2 %0 %0 %13 %85 %
gems4 %0 %0 %12 %84 %
pine87 %0 %9 %4 %0 %
yew2 %62 %1 %0 %35 %
heartwood0 %49 %0 %0 %51 %
sulfurous ash0 %0 %0 %0 %100 %
grain28 %58 %13 %0 %1 %

uo-atlas prints the full table after every run, atlas.json carries it (scarcity), and cargo test -p uo-atlas checks the split on three seeds: no rare ore or gems in Aelwyn land, under 10 % of the bow woods in Karrak land, most valorite in unheld land, the ash only on the isles, more grain in the south, two capitals on their own sides, every island lawless, roads never across the sea, every town in a province.

Resource kinds follow UO: ores are UO's coloured ores (iron, dull copper, shadow iron, copper, bronze, gold, agapite, verite, valorite) plus bog iron; woods Mondain's Legacy's (pine, oak, ash, yew, heartwood, bloodwood, frostwood); reagents the eight UO reagents. Item ids for them are not assigned yet (append-only when they are, uo-proto::items).

Settlements and roads

settle.rs. Capitals: the best site (water, flat, fertile, culture-specific) in the middle of each half; the free city on the Sunder's last cells before the sea. Then cities, towns and villages are placed greedily by habitability with spacing (cities ≈ 1600 tiles apart, towns ≈ 670, villages ≈ 290), up to per-realm quotas scaled by land area. The isles get a few lawless havens. Seed 1: 2 capitals, 11 cities, 42 towns, about 215 villages, 4 forts.

Roads: highways along a spanning tree of capitals and cities plus short extra links (loops), roads from every town to its nearest connected town or city, tracks from every village to the nearest road. Paths are A* on the macro grid; cells already on a road are cheap, so roads merge into a network. Roads never cross the sea; crossing the Sunder is expensive, so it has few bridges. Forts stand at every bridge over the Sunder and where roads leave a realm for the March.

Dungeons, sites and encounters

sites.rs.

  • The ruins of the Old Empire's capital: the great dungeon (9 levels, danger 5) on a river in the middle of the March, as far from living towns as possible.
  • Dungeons (≈ 170 at seed 1) on a jittered lattice, the kind chosen by the land: mines, delves, ice caves (mountains, snow), barrows, crypts and ruined keeps (the March), sunken temples (fens), fey hollows (Elderwood), fire lairs (fire isles), sea grottos (coasts). Almost every lattice point on the isles has one, one level more dangerous.
  • Sites (≈ 2700): ruins, shrines, standing stones, watchtowers, bandit and orc camps, monster lairs, abandoned mines, hermit huts, wizard towers, battlefields, graveyards, ancient trees, shipwrecks, roadside inns, lighthouses, fey rings, waystones, chosen from what is around (road, coast, border, forest type, height, realm).
  • Danger 1 to 5 per cell from the realm (heartlands safe, March and wilds worse, isles worst), the distance from roads and towns, height and the Elderwood.
  • Encounter zones (Daggerfall-style wilderness): every cell belongs to one of 15 zones (Karrish highlands and mountains, Aelwyn cantons, the Elderwood, the March and the river March, free city lands, wild peaks, frozen waste, fen, ashlands, the lawless isles, wildwood, the shadow of the old capital), each with a weighted table of 33 encounter types (wolves, brigands, deserters, faction patrols and raiding parties, smugglers and caravans, the restless dead, wisps and treants, trolls, giants, lizardfolk, fire elementals, drakes, pirates, sea serpents, a rare ancient dragon) and a danger range.

The pixel-art map

render.rs draws every tile with an indexed palette in the calm, warm look of the game (#62):

  • ground: the biome rule per tile on bilinear climate fields plus noise, borders dithered with a 4 × 4 Bayer matrix (#40); hillshade from the north-west in three hard bands;
  • texture: tree crowns on a jittered lattice (jungle denser, rare glowing Elderwood texels), swamp pools, lava cracks in the ash lands, snow caps on peaks, patchwork fields with hedges around towns;
  • water: deep sea, sea, shallows, shoals and a foam line; lakes; rivers drawn along the drainage tree with meanders, 1 to 11 tiles wide by drained area;
  • roads as smoothed polylines (highway 3 tiles, road 2, track 1), bridges over water;
  • towns and forts from their real plans (docs/towns.md, #103): streets, squares, yards, gardens and fields, every building by its wings with its roof colour (slate, thatch, shingle, tile; south and east rows in shadow), town walls, palisades and gates; the ruined old capital with broken walls and rubble;
  • dungeon mouths and every site as a small true-scale mark.

Output

FileWhat
tiles/2/X_Y.pngthe map at 1 px = 1 tile, 1024² indexed PNG chunks (256 at the default size)
tiles/1/X_Y.png, overview.png1 px = 4 and 16 tiles, box-filtered
political.png, danger.pngoverlays on the macro grid (1 px = 16 tiles)
cells.png, cells2.pngper cell: biome, polity, danger; province id, encounter zone
atlas.jsonsummary, factions, provinces, settlements, crossings, dungeons, sites, resources, deposits, scarcity, encounters, zones, feature labels
index.htmlthe viewer: pan and zoom, realms and danger overlays, places and sites, deposits by resource, search, details and the local encounter table on hover, realm and scarcity tabs, a scale bar in tiles and running time

The output is not committed (/atlas-out is ignored); it is regenerated from the seed.

Baked world

Decision #100. The playable map is baked once into static files, like UO's map.mul and statics.mul; nothing is generated at run time. What players change (broken walls, Wall of Stone, later buildings) reaches clients as server patches on top: the store's object rows and ObjectUpdates (#87).

cargo run --release -p uo-atlas -- --seed 1 --no-tiles --out /tmp/atlas --bake assets/world/felucca   # ≈ 25 s
cargo run --release -p uo-server -- --world assets/world/felucca      # the server plays Felucca
cargo run --release -p uo-client -- --server ws://127.0.0.1:7777 --name Me   # the client loads the same bake
FileWhat
map.binevery region (64×64 tiles): terrain id and height per tile, deflated (≈ 13 MB)
statics.binevery region's objects: sprite, tile, facing, storey, roof, exact height for built pieces, building; the region's building boxes; deflated (format 2, ≈ 21 MB)
world.jsonname, seed, size, hash of the two files, places (towns, dungeons, sites), camp sites, buildings (town, kind, box, door, storeys), free plots
atlas.jsonthe atlas (viewer data, names)

Byte layouts are in uo_world::baked. Both data files have an index (offset, length per region), so a region is read on its own and inflated on first use; the server and the client keep only the regions they touch. A world is baked once and shipped, never re-baked per machine: the generator uses floating-point maths whose last bits may differ between platforms, so two bakes on two machines could disagree by a few tiles. world.json carries a hash of the data files, the server sends it in Welcome.world_hash, and a client with another bake warns. The bake is not committed (/assets/world/ is ignored); distribution (a download from R2 or a release asset) comes with the first playtest.

Ground (uo_atlas::game, from the same per-tile rules as the picture, uo_atlas::ground): the biome's terrain with patches (dirt in the steppe, grass on hills, lava cracks in the ash), heights elevation × 100 in Tile::z (1/8 units: mountains about 12 units), sea at −2, lakes at their surface, rivers one step below their banks; mountains are rock (impassable, as in UO), roads cross them as passes. Towns are painted from their plans (docs/towns.md): level ground, streets, squares, yards; atlas roads stop at the town's edge. Roads are cobbled (highways) or dirt; where a road crosses water it is a causeway or a ford until bridges are built.

Statics (uo_atlas::bake): nature by biome (species per biome and north/south, stands and clearings from low-frequency noise, saplings at the edges, undergrowth between the trees), at most one blocking object per 2×2 tiles so a path always stays open, none on roads, rivers and in towns, and a margin along roads; the towns' buildings, walls and props come from their plans; trees are real size (#74), so even a dense forest has a tree on one cell in four. Bandit camps (the combat demo's camp, #69) stand at the atlas's bandit camps, orc camps and monster lairs (451 at seed 1).

Server: Felucca has 65 536 regions; leasing and ticking all of them is impossible, so big worlds are demand-driven (see docs/server-architecture.md § 6.6). New characters start in a town of the realms picked by the name's hash; UO_SPAWN=x,y puts them elsewhere (development).

Next

  • Bridges, docks, castles and gatehouses; townsfolk.
  • Dungeon entrances and the underground layer.
  • Give the resources item ids and connect gathering to deposits.
  • Turn provinces into claimable fiefs (#77) and decide the rules of the lawless isles.