Skip to content
Felucca Online

Changelog

What changed, week by week

Every change to the game ships with a note in plain words. Open one for the developer detail. The story behind the big ones is on the devlog; new notes are also posted in our Discord’s #changes channel.

Week 43, 202619 Oct – 25 Oct · 32 changes

  1. Fix

    After joining the waitlist, the two optional questions now open in their own window, so the whole form and its button are always visible, on phones too.

    Website#99

    Developer detail

    The questions panel sat inside the first screen, which has a fixed height (its art is sized by container queries) and hides overflow, so the panel's bottom and its button were cut off. It is now a modal <dialog> (top layer, never clipped, fits the viewport), with Skip, and a thank-you with the Discord invite after sending. Kit itself was fine: sign-ups land as unconfirmed subscribers until the confirmation link is clicked.

  2. Fix

    Joining the waitlist now tells you clearly that one step is left, checking your inbox and clicking the confirmation link, and that link brings you to a page that says you are on the list and what happens next.

    Website#99

    Developer detail
    • After joining, the form is replaced by a "Check your inbox to confirm" card with the address used; the questions dialog opens with the same message as its heading and repeats it as "One last step" after the answers are sent. Testers missed the small status line, and the Kit email can land in Spam.
    • New /confirmed page, the Kit form's redirect after confirmation: "You are on the list", the next steps, Discord and devlog buttons, a copy-link button (?ref=friend). Not indexed; it removes any query Kit appends.
  3. Internal

    The website is now hosted on Vercel at felucca.online, rebuilt automatically whenever something on the site changes, with its images served from our own media domain.

    InfrastructureWebsite#81

    Developer detail
    • GitHub repository alerdenisov/felucca (private); Vercel project felucca with root web/ builds production from main.
    • The ignored build step now always builds a redeploy of the already deployed commit: it used to cancel those, so a redeploy after changing env vars (baked into NEXT_PUBLIC_*) never ran.
    • Media variants uploaded to R2 (media.felucca.online, 612 files, 45 MB, immutable caching).
  4. Art

    Towns get their dressing: market stalls for nine trades with their goods, street lamps in each realm's style, fountains, statues of the rulers, trade signs, the banners of the four powers, laundry, puddles and building works.

    Art and assetsWorld#104

    Developer detail

    123 sprites of group town of the world-props order, scripts in tools/blender/models/world_town/, manifest assets/manifest.d/world_town.toml:

    • Market: stalls (2x1, front south and east) for fish, meat, cloth, pottery, weapons, bread, flowers, herbs and jewellery, each with its own awning colours; the awning ends above the counter's back edge so the goods stay in view. A vendor's cart, 21 goods and yard props (crates, barrels, sacks, cheese, scales, butcher's block, chicken crate, firewood).
    • Street furniture: benches, the double lamp, the Dominion's crowned iron lamp and the Concord's crystal lamp (both light the street), brazier, notice board, crier's podium, small and grand fountains (3x3, bronze figure), covered well, pump, trough, hitching post, mounting block, street sign, pillory, market bell; statues of the Dominion's king, the Concord's queen and the free city's founder (2x2).
    • Signs and heraldry: sixteen trade signs on wall brackets (four facings, like shop_sign), banners on poles and wall flags of the Dominion, Concord, free city and isles, bunting and a heraldic shield; wall pieces stand on the north (_x) or west (_y) edge.
    • Clutter and works: flower boxes, planters, potted tree, topiaries, rubbish heaps (2 variants), puddles (4 variants), sewer grate, broken barrel, laundry line and drying rack, broom, wheel and ladder leaning on a wall, outdoor bread oven (glowing), quench barrel, grindstone, two-storey scaffolding, building materials, bricks, mortar, roof tiles.

    Every model sits on the texel lattice (check_lattice() passes everywhere); leaves and petals vanish on breaking. Helpers copied from the catalog kits into the group's town_kit.py.

  5. Art

    Rocks, cliffs and ores for the open world: seven kinds of rock in five sizes, cliff faces that line the mountains, outcrops, arches and spires, mining veins of all nine ores, quarries, gems, glowing crystals, lava rock and steam vents, ice and snow.

    Art and assetsWorld#104

    Developer detail

    The 107 sprites of group stone of the world-props order, modelled procedurally in tools/blender/models/world_stone/ (kit stone_kit.py), every vertex on the texel lattice (check_lattice OK on all 182 frames), manifest assets/manifest.d/world_stone.toml.

    • Rocks (rocks.py, 35 sprites, 91 frames): granite (angular, light flecks), mossy (moss on the tops), sandstone (stacked strata in two bands), basalt (hexagon columns), limestone (pale, dark fissures), beach (sea-rounded, barnacles low down) and river (dark, glossy, algae at the waterline), each as pebbles (flat), small (knee high), medium (waist high), large (2 m, 2x2) and cluster (2x2), 2–3 variants each.
    • Cliffs (cliffs.py, 20 sprites): granite, sandstone, basalt and snow faces, 3 m, on the tile's north (_x) or west (_y) edge like walls. Displacements are periodic per tile and every piece end shares one cross-section, so runs, the inner corner (x + y on one tile), the outer corner and the end (both placed at the north-west corner of the tile where an x run from the west and a y run from the north stop) join without seams. Placement rules are in the script's docstring.
    • Formations (formations.py, 14): outcrops, a big outcrop with ledges, a 6 m spire, arches to walk under (2.1 m clear, _s / _e), a ledge, a balanced rock, a split boulder with a sapling, rocks with an ammonite, carved runes or ochre paintings, a split geode and a meteorite in its crater.
    • Flat ground (ground_stone.py, 7): scree, gravel, stepping stones, slabs, salt crust, obsidian shards on ash, lava crust with glowing seams.
    • Resources (ores.py, 19): ore veins of iron, dull copper, shadow iron, copper, bronze, gold, agapite, verite and valorite (faint glow), granite and marble quarry faces, four gem kinds and four glowing crystal clusters.
    • Volcanic and ice (elements.py, 13): obsidian outcrop, lava rocks (the large one with glowing cracks), steam vent, fumarole, volcanic bomb, ash mounds, ice blocks and spikes, frozen boulders, snow mounds, a rock with icicles.
  6. Art

    Roads, rivers and coasts get their props: milestones and wayside shrines of both realms, toll and border posts, stone and plank bridges, docks and harbour gear, rowboats, fishing boats and shipwrecks, a lighthouse, beach finds, pirate camps and snowed-in carts.

    Art and assetsWorld#104

    Developer detail

    The shore group of the world-props order: 119 sprites modelled on the texel lattice in tools/blender/models/world_shore/ (shore_kit.py plus seven family scripts) and listed in assets/manifest.d/world_shore.toml.

    • Roads (27): milestones (plain in two variants, Karrak with the iron crown, Aelwyn with a carved leaf), three- and four-armed signposts, wayside shrines of both realms and an old stone one, lantern posts, toll barriers and a toll booth, border stones and banner posts, hitching rails, benches, a well, dropped crates, cairns, a traveller's pack, a memorial cross, road works.
    • Bridges and docks (23): stone and plank bridge decks at road height that tile in both directions, parapets and railings on the tile edges like fences, piers, rope bridge planks, dock ends and broken docks matching the catalog's dock_planks, quay stairs, pilings, bollards, a dock crane, harbour and jetty lamps, a bell buoy.
    • Harbour goods (17), boats (13: rowboats afloat and beached, canoe, fishing boat, a boat laid up keel-up on trestles, ferry raft and post), wrecks (10: a heeled 5 x 3 hull, bow, stern, fallen mast, figurehead, flotsam), beach (12, incl. the lighthouse), rivers, pirates and winter roads (17).

    Boat hulls are convex hulls of lattice points; boats afloat put their waterline at z = 0.

  7. Art

    The ruins of the Old Empire are drawn: broken walls in five stone styles that match their houses, rubble, columns, arches, an aqueduct, toppled statues, a giant stone head in the grass, stone circles with glowing runes, dolmens and burnt-out farms.

    Art and assetsWorld#104

    Developer detail

    The ruins group of the world-props order (assets/requests/world-props/ruins.md): all 117 sprites, in assets/manifest.d/world_ruins.toml, scripts in tools/blender/models/world_ruins/.

    • Ruined walls (ruin_<set>_<low|mid|tall>_<x|y>, ruin_<set>_corner for stone, marble, sandstone, brick and fieldstone; ivy_wall_*, charred_wall_wood_*): each is its wall set's own code (wallset_<set>.py, run with walls_kit.run caught) clipped to a jagged column profile, so face plane, thickness, trims and materials are exactly those of the houses. Column heights prefer the set's course tops (the break follows the masonry), a quarter break mid-block; both ends of a profile share one height (low 12, mid 24, tall 40 levels, snapped per set), so pieces of a height join in runs. Sandstone and fieldstone lay their courses out by Frame.mask, so the clip uses a ClipFrame that keeps mask empty while the set code runs.
    • Everything else is texel voxels (nature_kit.Vox) or lattice lathes: rubble heaps stacked from each set's blocks on a height map, coursed arches and piers drawn as south-face patterns, tapered and eroded megaliths, voxel sculptures (ruins_kit.Sculpt: ellipsoids and tapered capsules) for the statues, round towers in staggered courses. Weathering is broad blotches, moss in cushions and lichen patches on stone only (features, metal and glow are left alone). The overgrowth uses nature_kit crowns and trunks with vanishing leaves (#66).
    • Glowing: menhir_runes (blue runes) and statue_maiden_aelwyn (her lantern).
    • Every script ends with check_lattice() (OK for all; the tree and bush crowns are snapped by nature_kit.finish).
  8. Art

    115 new grim props for the wilds: skulls of people and beasts up to a dragon's, skeletons, carcasses, the wreckage of the war between the two realms, graves, barrows, tombs and monster lairs.

    Art and assetsWorld#104

    Developer detail

    Group remains of the world-props order (assets/requests/world-props/remains.md), all 115 sprites modelled and baked: skulls (human, piles, pike, demon, cow, horse, deer, wolf, bear, ram, orc, troll, a giant's skull sunk to the nose, a dragon skull), bones and great skeletons (scattered and gnawed bones, fish, ribcages up to whale and dragon, a giant's spine, femur and skeleton), posed skeletons (sitting, in armour, crawling, hanged, in a web, with a lit lantern, in a hanging cage, a horse), carcasses, battlefield gear and lost things, war wreckage (torn banners of the Dominion, the Concord and the orcs, ballista, catapult, siege tower, chevaux de frise, stake field, burnt wagon), graves and memorials, stains and lairs, barrows, tombs and crypts.

    Scripts in tools/blender/models/world_remains/ on the group kit remains_kit.py: Vox, a voxel model on the texel lattice that emits only faces touching air (one mesh, a slot per material), and mound, a closed heightfield with corner heights on levels for earth and hides. Every script passes check_lattice(). Manifest assets/manifest.d/world_remains.toml states each prop's footprint from the order (the baker otherwise derives 2x1 from overhanging bones). grave_mound_old is ordered with 4 facings and 2 variants, which the baker cannot combine: its two variants are the two orientations (north-south, east-west).

  9. Docs

    We ordered 1060 new props for the world, from rye sprouts and haystacks to skulls, shipwrecks and dungeon gates, and planned where every one of them goes.

    Art and assetsWorldGame design#104

    Developer detail

    tools/world_props_order.py writes the order (assets/requests/world-props.toml, lists per group in assets/requests/world-props/), the brief is assets/requests/world-props.md, the placement plan docs/world-dressing.md. Nine sessions model the nine groups in parallel. The demo village's test that every catalog asset stands in the village skips world_* manifests.

  10. Art

    113 wild plants for every corner of Felucca, from poppies, bluebells and heather to cattails, glowing Elderwood mushrooms, fallen trees and moss-hung swamp trees.

    Art and assetsWorld#104

    Developer detail

    Group flora of the world-props order (decision #104), modelled and baked: flowers (26), grasses, sedges, reeds and ferns (15), ground cover and forest litter (16), bushes and shrubs (18), mushrooms (8), young, dead and odd trees (11), stumps, logs and a big fallen tree (12), jungle, swamp and desert plants (7). Scripts in tools/blender/models/world_flora/, manifest assets/manifest.d/world_flora.toml.

    • Leafy bushes, saplings, the young oak, snowy pine, small palm, juniper, ferns, the mangrove crown and the dead swamp tree come from the Quaternius CC0 nature packs (models by Quaternius, #74) at real size, squashed in plan to their tile, leaf cards recoloured per species, berries as small boxes on the outer cards.
    • Everything else is texel voxels on the lattice (one material per texel, check_lattice OK): the pack grass and flower cards read as pale slabs at 32 px per tile, voxel blades and heads read cleanly. Flat pieces stay on levels 0..1; carpets (poppies, daisies, bluebells, buttercups, dandelions, clover, leaf litter, pine needles, ivy, duckweed) wrap at the tile edge and tile seamlessly. Leaves, petals, grass and moss vanish on breaking (#66); glowing mushrooms and fire lilies have emissive texels.
    • branch_fallen has its 4 facings but one variant (ordered with 2): uo-baker bakes variants only for directions = 1.
  11. Art

    Farmland gets its art: thirteen crops in four stages of growth that fill fields edge to edge, haystacks, sheaves and scarecrows, farmyard furniture from hen houses to cider presses, carts and a hay wagon, wattle and split-rail fences, a windmill and a water wheel.

    Art and assetsWorld#104

    Developer detail

    The farm group of the world-props order: all 124 sprites in assets/manifest.d/world_farm.toml, scripts in tools/blender/models/world_farm/ on the texel lattice.

    • Field pieces (crops.py): rye, wheat, barley, oats, flax, hemp, cabbages, turnips, onions, beans, hops and grapevines in sprout / young / grown / ripe, plus stubble and a melon patch, three variants each (two for melons). Plants are texel voxels in rows on the crests of the furrows ground tile (period 4 texels); each tile is cut out of an endless field (FieldVox wraps cells into the tile), and every plant that can reach over the tile edge is seeded by its position alone, so all variants agree on the edge and any mix of them tiles seamlessly. Sprouts, stubble and melons stay within 2 levels (flat).
    • Harvest (harvest.py): round haystacks (one held down by roped stones), a long rick under a stepped thatch (_s / _e), small stacks, loose hay with a pitchfork, bales, stooks, lying sheaves, sacks, baskets, a crate of apples, heaps of pumpkins and cabbages, three scarecrows.
    • Fences (fences.py): woven wattle hurdles and zig-zag split-rail fences as x / y / corner / post pieces on the doors_fences convention, a step stile, a sluice gate, a 2x2 hurdle pen, vineyard trellis edge pieces, hop poles.
    • Farmyard (yard_gear.py, yard_pens.py, yard_wood.py, yard_presses.py): churns, troughs, a goose pen, grain bin, hitching post and racks, hen house, rabbit hutch, skeps, box hive, bee bench, dovecote, a granary on staddle stones, manure and compost heaps, woodpiles, chopping block, kindling, cider and wine presses.
    • Works (works_carts.py, works_tools.py, works_mills.py, works_orchard.py): wheelbarrows, hand and turnip carts, a hay wagon (_s / _e), plough, harrow, ox yoke, buckets, a well sweep, a post windmill (sails to about 11 m, past its 3x3 footprint like a tree crown), an undershot water wheel cut at the waterline (_s / _e), an orchard ladder and staked fruit saplings.

    Every 1x1 entry declares footprint = [1, 1]: wheelbarrows, ploughs, scarecrow arms and the like reach a little past the tile, and the bake would otherwise infer a 2-tile footprint.

  12. Art

    Camps in the wild now have their own props, from tents, yurts and bonfires to orc totems, bandit loot, woodcutters' saw pits, charcoal kilns, quarry cranes and mine entrances.

    Art and assetsWorld#104

    Developer detail

    All 128 sprites of the camps group of the world-props order are modelled and baked (assets/manifest.d/world_camps.toml, scripts in tools/blender/models/world_camps/):

    • tents and shelters: pup tent, wall tent (s/e), the Dominion's command pavilion, the Concord's silk pavilion, hide tent, ragged bandit tent, yurt, lean-to (s/e), field hospital;
    • fires and cooking: spit roast, cauldron, cold campfire, bonfire, fire pit, cooking tripod, smoking rack, iron field brazier, orc skull fire pit, the ogre's pot;
    • bedding and furniture, racks and stands, war banners of both realms, supplies and loot;
    • orc camps (totems, banner pole, war drum, hide hut, bone fence and spike wall x/y, cages, throne, trophy rack, idol, giant's club), carts and wagons, palisades and barriers, a lookout;
    • woodcutters, charcoal burners, quarries and mines, hunters' traps and the hermit's things.

    Every model sits on the texel lattice (check_lattice OK for all) and bakes with the dominant resolve; flames, embers, the mine lantern and the bloomery are emissive, leaves and herbs vanish when broken. Footprints, facings, variants, glows and flat pieces follow the order contract (assets/requests/world-props.toml). Group helpers live in camps_kit.py, which reuses the fire, log and stone helpers of lights_crafting, the skulls and pictures of civic_dungeon / decor_home and the edge mirror of doors_fences read-only.

  13. Art

    The world gets its magic and its dungeons: the eight virtue shrines with their coloured flames, fey rings, an ancient elder tree, wizards' yards, waystones, a doorway for every kind of dungeon, dark rituals and portals, and reagent plants.

    Art and assetsWorld#104

    Developer detail

    The arcane group of the world-props order: 114 sprites in assets/manifest.d/world_arcane.toml, scripts in tools/blender/models/world_arcane/ on the texel lattice (helpers in arcane_kit.py: coloured magic flames, the virtue table and glyphs, rune bitmaps, snapped mounds, edge pieces).

    • Shrines: the eight virtue shrines (2x2) carry their glyph on a stele that faces the camera, glowing in the virtue's colour above a bowl of that flame (humility burns black); the Dominion's spiked war altar, the Concord's moon basin, a ruined ankh shrine, a candle niche, offerings, prayer flags, a censer and a holy font.
    • Dungeon entrances face the camera with a dark way in: mine, dwarven delve, barrow, crypt and cave in _s/_e (the _e piece is the _s model mirrored across the view diagonal, as UO's pairs are, so the opening stays on the camera side), plus sunken temple, fey hollow, fire lair, ice cave, sea grotto, ruined keep (4x2) and the Old Empire's triumphal gate (5x3).
    • Waystones and wizards' grounds: runes sit on a corner cut along the plan diagonal, so they read square on screen; arcane circle, crystal pylon, armillary astrolabe, telescope, cauldron, mana well and more.
    • Fey, trees, rituals, lairs, reagents: fairy-ring quarters (variant v holds the quarter of a ring centred on its tile's south-east, south-west, north-west, north-east corner for v = 0..3), the ancient tree (a Quaternius CC0 pack tree, models by Quaternius, grown to 20 m on a modelled buttressed base), portals, summoning circle, spider webs, dragon hoard, and the reagent nodes.
  14. New

    The land between the towns comes alive: fields in plots with crops, haystacks and scarecrows, farmsteads and windmills, shrines, ruins, graveyards, battlefields, camps, harbours, bridges, dungeon gates, and flowers, rocks and bones in every biome.

    WorldGame design#104

    Developer detail

    uo-atlas/src/dress/: the world's dressing, placed by the bake.

    • scatter.rs (per region, from its tiles and the global geometry): biome cover in noise drifts with rocks and rare finds; river banks, lake shallows, coasts; cliff faces where mountains rise north or west, with corners and tapering ends; fields cut into plots (crop and growth stage by plot and climate, stubble with stooks and ricks, fallow) behind hedges, wattle and split-rail fences with stiles; milestones, wayside shrines, lanterns before towns, border posts, toll booths, road works, roadside debris by danger; bridges on every ford (decks, parapets, piers, posts); nodes at the atlas's deposits; danger, frontier and isles props.
    • sites.rs (planned once, like towns): a vignette for every kind of place of interest, dungeon entrances by kind, farmsteads (some burnt), orchards and vineyards, windmills, watermills with ferries, work camps at deposits (mines, quarries, woodcutters, hunters, fishers), harbours for ports (pirate guns on the isles), gallows at dangerous crossroads, army camps beside battlefields, corrupted circles. Sites may hold generated buildings on level ground; earlier sites win where two overlap.
    • Towns (#103): stalls by trade with goods, rulers' statues, banners, realm lamps, trade signs by building kind, flags, shields and flower boxes on facades, life by the doors, clutter in back streets, building works on free plots.
    • Statics chunks list the extra tiles multi-tile blockers cover; ruined walls, cliffs, palisades, barricades, bridge parapets and portcullises block their edge.
    • tools/world_props_usage.py: every one of the 1060 ordered props has a rule.
    • Sites move off rock to open ground and put their landmark where it fits; glades; battlefields trample their fields.
    • Client: sprites under 0.4 units cast no shadow shell (a dressed meadow 15 → 8 ms); the sprite atlas starts with 12 layers.
    • Seed 1: 13.97 M statics (6.8 M before), 4232 sites. Numbers: docs/world-dressing.md.
    • Client --prop-gallery a,b,…: chosen sprites in rows on the demo ground, to look at props in the real renderer (the blog's showcases). Orchards keep grass under their trees.
  15. New

    The whole world now has a map. A rugged main island split between two rival realms, a contested river valley between them and lawless island chains around it, with every town, road and dungeon placed, drawn as pixel art at one pixel per tile.

    WorldGame design#97

    Developer detail

    New crate uo-atlas (cargo run --release -p uo-atlas -- --seed 1 --out atlas-out, about a minute and a half, about 40 MB). Deterministic from the seed; nothing of it is in the playable map yet.

    • Scale: 16384 × 16384 tiles by default (1 tile = 1 m), about 68 minutes running edge to edge at 250 ms per tile. Geography and placement run on a 16-tile macro grid, the picture per tile.
    • Shape: a warped main island (lobes, gulfs, fjords), a frontier valley with the great river, the Sunder; a cold northern plateau behind a mountain wall, a warm southern forest country with the Elderwood; five or six archipelagos cut by straits, one of them volcanic.
    • Politics: the Karrak Dominion (north) and the Aelwyn Concord (south) split the main island by culture-specific travel cost from their capitals; the March along the border, a free port at the river mouth, the wilds, and the isles, where there is no law. Provinces around every capital, city and March town.
    • Scarcity: Karrak has the main island's gold, gems and rare ores, Aelwyn the bow woods, grain and herbs and no rare ore; the isles hold the largest share of the best of both and all the sulfurous ash. Checked by cargo test -p uo-atlas on three seeds.
    • Places: about 270 settlements, highways, roads and tracks, forts at the Sunder bridges, about 170 dungeons with the ruined old imperial capital as the great one, about 2700 sites, danger per cell and 15 encounter zones with weighted encounter tables.
    • Output: tile pyramid (1:1, 1:4, 1:16), realm and danger overlays, atlas.json, and a viewer (index.html) with search, layers, deposits per resource, hover details and the local encounter table.

    Details: docs/world-atlas.md.

  16. Art

    Buildings get their missing wall pieces. Cut corners and bay windows now have real diagonal walls, balconies and roof terraces have low parapets, fortified buildings have arrow slits and tower tops have crenellations, in all nine wall styles.

    Art and assets#102

    Developer detail

    The building generator (#101) already placed these pieces; the client drew placeholders until now. 126 new sprites, 14 per full wall set (stone, brick, wood, log, plaster, sandstone, marble, fieldstone, dungeon), ordered in assets/requests/wall-pieces.md:

    • diag_a / diag_b (+ _window): full-height diagonal walls between the corner posts of the wall grid, built in a sheared frame so every vertex stays on the texel lattice and neighbouring diagonal tiles join exactly. Their outlines sit on half-pixel rows, so they read a little softer than straight walls.
    • low_*: parapets, the set's own wall cut at level 16 under a two-level coping.
    • slit_*: arrow slits 2 texels wide at levels 22..36, splayed on the camera side.
    • crenel_*: solid to 12 with a string course, merlons 4 texels wide every 8 up to 24; the corner piece gets an L-shaped corner merlon.

    walls_kit builds them from each set's existing wall and post code: diagonal frames, profile clipping (Frame.mask) for parapets, kit copings with per-set hooks, and the slit reveal. Every piece passes check_lattice() and bakes with resolve = dominant. The village shows all of them in a mason's yard.

  17. New

    The playtest waitlist on the website now really signs you up. Confirm your address by email and you get playtest invites, development news and the occasional offer, then two optional questions and a link to our Discord.

    Website#99

    Developer detail
    • Subscribers live in Kit (kit.com), not on our side: POST /api/waitlist creates the subscriber inactive and adds them to the waitlist form, whose double opt-in sends the confirmation (web/src/lib/waitlist/). Joining is the consent to invites, dev news and offers, as the line under the button says (no checkbox). Fields: opt_in_at, opt_in_text, source (from ?ref= or utm_source).
    • After joining, an optional questions step (POST /api/waitlist/profile, HMAC token valid one hour) stores uo_played and interests, and the Discord invite is shown.
    • Abuse guards: honeypot field, a best-effort per-IP limit (5 per 10 minutes per instance).
    • /privacy page; footer links. npm run kit:setup -w web lists Kit forms and creates the custom fields (--apply). Env: KIT_API_KEY, KIT_FORM_ID, WAITLIST_SECRET.
    • Not verified against the live Kit account yet: the double opt-in email on API sign-ups needs one test with a throwaway address.
  18. Fix

    The same controls and the same HUD everywhere. WASD and the right mouse button now run your character in the offline demo too (they used to move the camera there), the arrow keys move the camera, and the village, the combat demo and the online game share one set of panels.

    Client#96

    Developer detail
    • One control scheme (controls.rs): mouse and keys become intents, which the active mode applies: offline outside a battle (roam.rs: paths around walls and props, running one tile at a time, a click on a destructible object hits it), in an offline battle (battle orders; a held direction orders one step whenever nothing else is queued, as the server does) and online (server commands with prediction).
    • One HUD (hud.rs): status, vitals, spell bar, target card, log, outcome banner, overhead bars and floating text, filled by the active mode. An offline battle adds its tempo timeline.
    • Keys that moved: arrows pan the camera in every mode, H cycles roofs (R is always hold), numpad steps work offline too, Esc disarms a spell or quits only outside a fight, and a plain right click no longer hits objects (left click does; Shift + right click still blasts).
    • --record in the offline village (no battle) no longer waits forever for a battle to start.
  19. New

    Felucca's 268 towns, villages and forts are built: streets laid out so you always see the fronts, a square with a landmark, markets, walls and gates where the roads arrive, about 2600 buildings with yards, and free plots left for players.

    WorldGame designServerClient#103

    Developer detail

    Town plans in uo-atlas (towns.rs), computed once from the natural ground before the bake and read by the ground painter, the bake and the atlas picture.

    • Bastide layout for the fixed camera: bands of plots fronting south onto their street, cross streets at irregular spacing with east-fronting corner plots, the square in the middle band, a market square below it in capitals and cities.
    • Districts (civic, rich, gate, craft, common, outer) choose the kinds; required buildings per tier with caps; 10–20 % free plots listed in world.json.
    • Octagonal walls with diagonal pieces and crenellations (palisades for Aelwyn and the isles), a ring road, gates where atlas roads arrive (a south gate if none), roads end at the edge.
    • Ground levelled per town, streets, squares, yards, vegetable patches; brooks built over, rivers forded. Yards and props by trade, fences, lamp posts, shop signs.
    • Statics format 2: facing, storey, roof, exact height and building per record; arenas and blocking use ground pieces only; the client keeps baked heights and hides upper storeys and roofs as Overhead when you step inside.
    • uo-atlas --town <name> writes plan sheets; --plans-only skips the picture. Forts are bigger (radius 26). Seed 1: 2665 buildings, 326 free plots.
    • Tests: plans of a whole atlas (doors on streets, no overlaps, required buildings, gates, determinism), statics chunks round trip, building pieces read back from a bake.
  20. Fix

    Houses with cut corners are symmetric now: both front corners are cut, or all four, never one alone.

    World#104

    Developer detail

    uo-buildings used to roll every convex corner on its own, so a house could have one cut corner on one side. Now a building picks one pattern (none, the front pair of each wing, or all four), a corner is cut only together with its twin across the wing, on every storey where both exist, and glazed diagonals are all or none. New test: every diagonal outside a bay has its twin. The diversity bar of the plan test drops to 60 % distinct layouts (warehouses: 59 of 96), since lopsided cuts no longer count as variety.

  21. Docs

    Design direction: your gear will set your speed, as in Ultima Online's Age of Shadows: faster swings, faster casting and quicker recovery from properties on items, worth hunting for.

    Game designCombat#93

    Developer detail

    Decision #93 replaces the rigid Renaissance timing with AOS/Mondain's Legacy-style properties (Swing Speed Increase, Faster Casting, Faster Cast Recovery and more), each a modifier on items and effects with a cap. Times are computed in milliseconds and rounded down to the 250 ms quantum, so a bonus saves a whole quantum or nothing and builds aim at breakpoints. Not implemented yet; formulas and caps are open (docs/combat.md › Speed properties).

  22. New

    Run like in Ultima Online. Hold the right mouse button to run toward the cursor, or use WASD. Outside a fight you run twice as fast as you walk, and running stays smooth even when you turn often.

    ClientServer#91

    Developer detail
    • Right mouse button held: run toward the cursor (eight directions, half-tile dead zone). WASD: screen directions. Online the camera pans with the arrow keys (#91).
    • Out of a fight a step is 1 AP (250 ms); in a fight (attacked or attacking in the last 10 s) it stays 2 AP so the tempo against weapon swings holds (#92). The server publishes the player's step time and the client predicts at it.
    • Held running is client-driven as in UO (#94): each step the client shows is sent as a Step; the server queues up to four, takes them in order and checks each, refusing a bad one with StepRejected. Running in circles for 40 s: 128 predicted steps, no corrections (21 before). In a fight the server walks a held Move { dir } instead.
  23. Fix

    River banks and shores no longer look torn: water lies flat at its own level and the shoreline follows the same soft, dithered border as every other ground.

    RenderingWorld#104

    Developer detail

    The client drew water as one flat sheet at −0.2 over the whole map and sank every water tile to a bed at −0.6, so a river up on a plateau carved a trench with steep, spiky slopes that the sheet cut through. Now water is a terrain type like the others: water tiles are flat at their own surface (sea, lake level, rivers one step below their banks), every corner touching water sits on the waterline, and the terrain shader draws water with its ripples and gloss wherever its per-texel border pick lands on a water tile. No sheet. WATER_LEVEL is the sea's −0.25.

  24. New

    The game client plays online. Log in, walk a map far bigger than the demo village, fight bandit camps with other players, and keep playing if your connection drops.

    ClientProtocol and SDK#88

    Developer detail

    uo-client --server ws://… --name … (#88): the terrain is built from a 192 × 192 window that follows the player, region statics stream in and out, server object damage drives cracks, debris and open doors, mobiles are dressed from their equipment, and combat events reuse the offline showcase's effects. A dropped connection resumes on the next gateway in the list. --bot combat-auto, --bot wander and --bot run play by themselves, also headless.

    Movement went through several rounds of fixes found in play: commands are numbered per session (a character walks again after logging back in), walks keep their planned route, the client predicts walks leg by leg with the server's own planner (uo_world::walk), a walk order beats auto-attacking back, lagging mobiles catch up instead of jumping two tiles, and own steps are predicted with rollback on StepRejected.

  25. Art

    New night lighting. Nights are no longer pitch black. Moonlight washes the world in a dark blue you can see in, shadows keep a faint hint of colour, characters take on the moonlight too, and torches, braziers and lanterns glow a calm warm light instead of a strong orange.

    RenderingCharactersClient#95

    Developer detail

    Before (left) and after (right), the village at 23:00:

    The village at night: before, black shadows and orange torchlight; after, moonlit blue with warm white lamps

    • Moonlight. The night phase has a weak cool moon (strength 0.05) and a dim blue sky ambient (20), exposure +0.5 EV instead of +0.75. The black toon band stays only as a guard (toon_t0 0.012 → 0.003).
    • Blue in the dark, colour by the fire. A new per-phase dim tint (Phase::dim_tint, Shading::dim_tint) pulls texels in the two darkest toon bands towards blue at their own luminance (night 0.65; blue light alone leaves green grass green). Pools of firelight sit in the brighter bands and keep their colour.
    • Characters. Their readability light floor (MIN_LIGHT 0.6) and rim take the same tint, and the floor now tops the light level up by luminance instead of a per-channel max, so a character in the dark is moonlit blue and one by a brazier stays warm.
    • Warm, not orange. Sprite lights, torches and the campfire go through Look::light_color (saturation 0.6 in linear light, luminance kept). Combat effects keep their colours.
    • The moon now casts light, so the directional light fades out briefly around the sun-to-moon switch (≈ 5:00 and 19:00) and shadows never jump.
    • Everything stays tunable at runtime: UO_LOOK="night.dim_tint_amount=0.5,light_saturation=0.5".
  26. Internal

    We stress-tested the server with 2,000 bots and real game clients while crashing machines on purpose: nobody was duplicated or lost, a crashed machine's part of the world came back in under five seconds, and restarts didn't kick anyone.

    ServerInfrastructure#89

    Developer detail

    uo-loadtest (#89) runs thousands of protocol bots (combat, wander, duel, siege, churn) and whole scenarios: sims and gateways as processes on the store, real headless Bevy clients, chaos steps (kill, stop, start, rolling) and a check every second that no player is simulated twice and every region is owned.

    The plan's full run (M7) on ScyllaDB: 3 sims and 2 gateways as processes, 2000 bots and 2 Bevy clients through a sim crash, a gateway crash and a rolling restart of every sim: tick p99 4 ms (target 25), failover 4.7 s (target 7), no duplicates, every bot back, 1.7–2 s per sim to restart. Getting there fixed a local-handoff routing loop, a dead tick that kept renewing its leases, LWT renewal storms (now membership fencing, #90), DDL stalling LWT on restarts, tick preemption (time-constraint policy on macOS) and path searches to unreachable targets. Numbers: docs/server-performance.md, docs/server-m7-report.json.

  27. New

    Tree crowns no longer hide your character. Near a tree, or when its crown covers you, the leaves fade away and leave the trunk and branches, so you see where you are and what to chop. A new setting picks when: near you, always, or never.

    RenderingArt and assetsClient#98

    Developer detail

    The village with crowns kept (left) and hidden near the player (right):

    An apple tree's crown in front of the villagers, then only its trunk and branches

    • Bare twins. Hiding leaves by the existing debris.png mask alone left only a stump: the trunk inside a crown was never baked. Trees are now baked twice. Manifest bare = true (uo-baker bake --bare, bake_sprite.py --bare) renders the same model again in the same Blender session with every uo_debris = "vanish" material see-through, and writes the result to assets/sprites/<tree>/bare/ (same frames, anchor and height range; sprite.json gets "bare": true). uo-baker checks that the twin matches and draws only where the tree does. All 13 leafy trees have one; the whole-tree sheets came out unchanged.
    • Swap in the shader. The atlas links every frame record to its twin's (FrameRecord::bare). Hiding a crown writes bare_time/bare into the tree's effect slot, and the impostor shader swaps texels to the twin in a sprite-anchored Bayer 4×4 order over 0.35 s, in the depth prepass and the main pass alike. A reversal mid-fade continues from where it is.
    • When. crowns = "auto" (every preset): the player within 1.5 tiles of the trunk, or a crown texel over the player's body on screen with the tree at most one tile behind them; hysteresis 0.75 tiles / 10 texels so crowns don't flicker. always draws every tree with its twin outright (no effect slot), never keeps every crown. Settings window (O) or --set crowns=always.
    • Shadows stay the whole tree's, like hidden roofs. Clicking a hidden tree hits the trunk (picking uses the twin's coverage). Hits, cracks and breaking are unchanged.
  28. New

    Felucca has a game server: one shared world, split into regions across many machines, built for tens of thousands of players at once. When a machine falls over, its part of the world moves to another in a few seconds and you stay in the game.

    ServerProtocol and SDKWorld#87

    Developer detail

    uo-server is one binary with two roles over a shared store (#87):

    • gateways hold the WebSockets, sessions and each player's area of interest (18 tiles) and route commands; they keep no game state;
    • sims own 64 × 64-tile regions of the 1024 × 1024 map on leases and simulate each one as an open-world uo-combat battle at 20 Hz, players, bandit camps and destructible objects together.

    The store is ScyllaDB or Apache Cassandra (same driver and schema; deploy/docker-compose.yml) or an in-memory store for one process. Ownership is compare-and-set (region leases, character owners), state writes are fenced by generation so a late write from a former owner always loses. Players are handed between regions and machines without noticing; a battle stays on one machine while it lasts; a crashed sim's regions are taken over after its membership expires (#90); planned drains and rolling restarts move players with no dropped connection.

    World content comes from the new uo-world crate: regions, per-tile map windows, deterministic camps and nature per region, and the combat arena builder shared with the client. The server also owns object damage now: hits and breaks travel as object updates and are stored.

    Protocol v2 (uo_proto::msg): snapshot plus per-tick deltas of the area of interest, Resume on any gateway, combat orders and the combat event stream, object updates, vitals. Documentation: docs/server-architecture.md.

  29. New

    You can now walk Felucca. The game map is the world from the atlas, with its rivers, lakes, forests, meadows, snowfields and the open ground of every town, and the server starts the parts of the world where players are.

    WorldServerClient#100

    Developer detail
    • uo-atlas --bake <dir> bakes the playable world once into static files: map.bin (terrain and height of every tile, per 64×64 region, deflated, 12.8 MB), statics.bin (6.2 M nature and camp objects, 18.6 MB), world.json (places, camp sites, a hash of the data files). Nothing is generated at run time; what players change stays server patches.
    • The atlas picture and the bake share the per-tile rules (uo_atlas::ground, uo_atlas::game): biome terrain with patches, heights from the atlas elevation, rivers, lakes at their level, roads (causeways and fords over water for now), towns as levelled terraces with plaza, lanes and plots.
    • Nature by biome and climate: stands and clearings, saplings at the edges, undergrowth, at most one blocking object per 2×2 tiles; real-size trees, so forests keep their ground visible. Bandit camps at the atlas's camp sites (451).
    • uo_world::baked reads the files lazily per region; uo-server --world assets/world/felucca; the client loads the bake named in Welcome and checks its hash.
    • Big worlds are demand-driven on the server: a region_demand table (sims: their players' regions and neighbours; gateways: the region of a login), placement only of demanded regions, idle regions retired after 30 s. Small worlds and every cluster test run as before.
    • UO_SPAWN=x,y (development) starts new characters at a given place; the online bot wanders only to walkable goals.
  30. New

    Real-time tactical combat is in the client. Fight bandits on the open map with cover that breaks, spells, powder kegs and a forest cabin to hold, and watch the timeline show who acts next.

    CombatClient#69

    Developer detail

    The combat showcase branch (feat/combat-demo) lands on main. Rules live in the pure uo-combat crate (#68): every action costs action points, 1 AP is 250 ms of recovery, the clock never stops, orders queue during recovery and a ready fighter auto-attacks its target. Projectiles and targeted spells need a line of fire; destructible statics on it are full or low cover and take the stopped shot (#66 hits).

    What ships in the client (--combat-demo [cabin|camp], --combat-auto):

    • the bandit camp and the forest cabin assault (#69, #71): sappers with powder kegs, halberd sweeps that wreck rooms, Wall of Stone, doors that open;
    • the FFX-style timeline HUD, spell bar, target card, log and overhead bars;
    • hotkeys on numbers and QWERTY (#70), autoplay with the hero holding the house and falling (#72, #79), the cabin mirrored so its entrance faces the camera (#80);
    • the Mega Explosion last stand (#86), --no-hud (#84) and --record video capture.

    Tests cover the tempo (six dagger swings to two halberd swings in six seconds), cover, spells against cover, Wall of Stone, walking, breaking through walls and determinism.

  31. Art

    The bear-skin rug is now life size, and log cabin walls have dark wooden joints instead of grey chinking.

    Art and assets#83

    Developer detail

    rug_fur was rebaked at 2 × 2 tiles (≈ 2.2 m) to fit a real bear against the 1 m tile (#83). Log wall joints are darkened to wood brown (#85). Both are catalog sprites, so the village and the combat scenes pick them up.

  32. New

    Buildings are now planned like an architect would: fourteen kinds from cottages to inns, temples and towers, in many shapes, with real rooms, stairs, front doors facing the camera, bay windows, terraces and furniture that never blocks the way.

    WorldGame designArt and assets#101

    Developer detail

    New crate uo-buildings: a Spec (kind, style, plot, front, seed) becomes a full plan.

    • Shapes I, L, T, U, H, Z, cross and courtyard from rectangular wings, storeys per wing.
    • A room program per kind and storey (25 room kinds); corridors with rooms off them upstairs in inns, manors and barracks; straight 4-tile stairs under a room of the storey above.
    • The front door faces south or east (the camera never turns); east fronts are south plans flipped across the diagonal. Doors inside along a tree from the entrance.
    • Windows, arrow slits on fortified buildings, cut corners (octagonal towers), bay windows, terraces with low-wall parapets, crenellated tower tops.
    • Furniture from room templates, only where every room, door and stair stays reachable; floors and wall sets by style (Karrak, Aelwyn, Free, Isles).
    • Checks: everything reachable from the front door; 14 kinds × 4 styles × 2 fronts × 12 seeds pass, 81–96 distinct layouts of 96 per kind, about 1 ms per building.
    • uo-buildings writes plan sheets and a list of sprites the catalog lacks; the client shows generated buildings with --building-gallery <kinds>.
    • Ordered: diagonal walls, low walls, arrow slits and crenellations (assets/requests/wall-pieces.md); placeholders until they are baked. Upper storeys can't be walked on yet (2D movement), and buildings are not in the world yet (towns come next).

Week 42, 202612 Oct – 18 Oct · 9 changes

  1. New

    The website now has a devlog. Follow every change, read the blog, discuss posts on our Discord, browse the docs and see the roadmap.

    WebsiteInfrastructure#81

    Developer detail

    The site moved to Next.js (web/): the landing page is a pixel-identical port, plus /changelog (these fragments, weekly), /blog with RSS and feed.json, /docs from docs/*.md, /decisions from the CLAUDE.md log and /roadmap from roadmap.yaml and tasks/. Blog comments are the messages of each post's Discord forum thread, cached forever and revalidated by the bot through a signed endpoint. Media: pre-built variants on R2 behind a custom next/image loader. CI requires a fragment for engine, asset and chain PRs. Nothing is deployed yet; see docs/devlog.md.

  2. Internal

    Images and videos on the website can no longer disappear because a local copy of the originals was incomplete.

    Website

    Developer detail

    npm run media:variants used to drop every manifest entry whose original it could not find, so running it against a folder without video/ removed the combat video from the site. Missing originals are now kept with a warning; -- --prune removes them on purpose.

  3. Internal

    Running the website locally no longer crashes in a full checkout of the game.

    Website

    Developer detail

    next dev died with EMFILE: too many open files in the main checkout: Velite's watch mode puts chokidar on the whole repo root (its root is ..) and filters later, so it walked target/ (6.6 GB of Rust builds), node_modules/ and .claude/worktrees/. web/scripts/velite-watch.mjs now watches only the content paths with fs.watch and reruns velite build in a fresh process, one build at a time.

  4. Internal

    Every change to the project now gets a note in this changelog, including the ones behind the scenes.

    InfrastructureDocs#82

    Developer detail

    The rule used to apply only to engine, asset and chain changes. Now every PR and every direct commit to main carries a fragment, whatever it touches; only edits of the devlog content itself (changes/, blog/, tasks/, roadmap.yaml) are exempt. A new type dev (Internal) covers tooling, CI and infrastructure. The PR check in .github/scripts/check-pr-content.mjs enforces it.

  5. New

    Felucca Online has a home page, with real captures from the client and a playtest waitlist.

    Website#76

    Developer detail

    Static HTML and Tailwind v4, flat interface over the game's own pixel art. The logo is shipped as a 1:1 master and only scaled by whole numbers. Gameplay images are client captures, never drawn.

  6. New

    Characters are never lost behind trees or walls any more: a dithered see-through circle opens around every one of them.

    Rendering#75

    Developer detail

    Up to 32 mobiles in view each cut a circle through nearer sprites that rise above their feet: a solid core and a Bayer-dithered rim in whole texels. Floors, rugs and low plants stay. Key 9 or --disable see_through.

  7. Art

    Trees, bushes, flowers and rocks were redrawn from textured models at real size; grown trees now stand 10 to 17 metres tall.

    Art and assetsWorld#74

    Developer detail

    Models from the Quaternius CC0 nature packs, one pack model per variant, scaled to real size (1 unit = 1 m). The baker now clips alpha cut-outs and keeps authored normals on leaf cards.

  8. New

    Walls, doors, fences and trees now take hits, crack, and break apart into debris that sinks into the ground.

    WorldRendering#66

    Developer detail

    Hits shake the sprite and flash it white; cracks follow a seeded Voronoi tiling and grow with damage. The last hit swaps the impostor for chunks built from the sprite itself, so it splits along its own cracks. Leaves and flames burst into particles instead. Durability: stone walls 8 hits, other walls 5, trees and rocks 6. Client-side for now; the server will own it.

  9. Fix

    Fixed black specks that stuck to objects and followed the camera, worst in busy scenes far from the centre.

    Rendering#73

    Developer detail

    The depth prepass and the main pass rebuild the same depth in two shaders that Metal compiles with fast math, so they rounded differently. The main pass now moves its depth 4e-6 towards the camera. Village: 37 k speck pixels down to 0.