Skip to content

Lateania: overworld + 3 capitals, D&D stats, look/fountains, titles & veteran revives#310

Open
hardlygospel wants to merge 21 commits into
mpiorowski:mainfrom
hardlygospel:feat/mud-lateania
Open

Lateania: overworld + 3 capitals, D&D stats, look/fountains, titles & veteran revives#310
hardlygospel wants to merge 21 commits into
mpiorowski:mainfrom
hardlygospel:feat/mud-lateania

Conversation

@hardlygospel
Copy link
Copy Markdown
Contributor

@hardlygospel hardlygospel commented Jun 5, 2026

Lateania is a complete multiplayer D&D MUD, designed and built from scratch by Tasmania (https://hardlygospel.github.io) as a door game inside late.sh — the world, the engine, and every system in it. This PR continues that work; it builds on the merged #295 and rebases cleanly onto the later check-fix/rename pass.

Thanks 🙏

  • mat (@mpiorowski) — Lateania only exists because of late.sh and the foundation you built and keep building. Getting to build a whole world inside it has been a genuine joy. Thank you.
  • Mike Clark (@mevanlc) — for the "Fix Lateania check failures" pass (formatting, the rooms/muddoor/lateania move, session-aware service); this slice is rebased on top of it.

What I built (Tasmania) — the whole game

Engine & architecture

  • A single late "door" hosting one persistent, multiplayer world: LateaniaService owns the room graph, mobs, and players; a 2s world tick resolves round-based combat, effects, resource regen, and respawns; per-session snapshots are filtered to each player's room + sheet.
  • Character persistence across logout and restart (mud_characters table, schema-versioned JSON blob; load+hydrate on join, save on leave, 60s autosave, and now a graceful-shutdown flush).

World

  • 298 hand-described rooms across the original nine zones (Embergate hub → King's Road → Whisperwood, Duskhollow, Drowned Crypts, Emberpeak, Frostspire, Sunken Citadel, the Obsidian Throne) plus the new overworld: a Greatroad out to the Sapphire Coast, Verdant Highlands, Mistfen, Fungal Hollow, Sahra Wastes, Amber Savanna, and Skyreach Mesas. Every room is a full paragraph (enforced by a test); the graph is fully connected and reciprocal by construction.
  • Three capital cities — Tasmania, Melvanala, Matlatesh — each a safe haven with a healing fountain and a bronze dedication plaque.

Characters & combat

  • 5 classes, a 50-level progression, passive class traits, and 55 abilities under one unified effect system (strikes, DoTs, heals, wards, stuns, finishers).
  • An interactive damage-type system (8 schools with resist/weak profiles) across 67+ enemy types and zone bosses up to the archdemon Mal'gareth.
  • A full inventory / equipment / gold / shop economy with rarities and stat mods.
  • D&D ability scores (4d6-drop-lowest, rerollable), driving HP (CON) and attack (class key).
  • Titles earned by slaying foes, a look/examine layer for room features, and veteran resurrections for long-standing citizens.

Latest additions

  • Overhead minimap. A small automap now sits in the bottom corner of the Room panel and shows where you've been: your current room (@), rooms you've already visited (o), unexplored exits beckoning onward (.), and the corridors that link them (- | / \). It's built by walking the room graph out from where you stand (BFS, so the shortest path wins the world's occasional non-Euclidean loops) onto a 7×5-room grid centred on you; up/down exits, which a flat map can't place, are called out in words. Each character tracks its own visited-room set, which persists with the save (schema bumped to v3, serde-default so older saves load fine and just start the map fresh).
  • Clearer stair controls. Vertical travel (</, up, >/. down) always worked, but nothing on screen said so — so players standing on a "down" exit didn't know which key to press. The Commands panel now shows a contextual stair hint exactly when the current room has a way up or down.

47 Lateania unit tests pass (incl. minimap layout/corridor coverage and the v3 save round-trip); library + binary build clean, no new clippy warnings. No new DB migration.

Tony Hosaroygard and others added 17 commits June 1, 2026 10:31
Lateania is a swords-and-sorcery MUD built on the room-game trait surface.
Players SSH in, enter a shared persistent world, and meet, move, talk, and
fight together in real time. Unlike the seated games, one GameRoom is a whole
world: the room graph, mobs, and players live inside a single MudService, and
movement happens within the service rather than the rooms directory.

Implements the standard module shape (svc/state/input/ui/manager/create_modal)
plus world.rs for the static world data, and wires GameKind::Mud through the
enum, registry, filter, activity feed, and main.rs.

Engine highlights:
- 2-second world tick resolves round-based combat and mob/player respawns,
  reusing the loop shape proven by Tron.
- Per-session snapshots are filtered to the player's own room and character
  sheet, following the poker public/private split.
- Warrior class, xp/leveling, three mobs, and a safe-haven town for the slice.
- world.rs seeds Embergate plus the King's Road; the design targets 200 rooms
  across nine zones, loaded as data.

Input note: the slice uses wasd/arrows to move, space/x to attack, z to flee,
o to look, avoiding the chat-reserved keys (i/j/k and selection actions). A
full typed command prompt needs an input-capture mode and is deferred.

Inline unit tests cover world-graph integrity (every exit resolves, all rooms
reachable, start room safe), movement, safe-zone combat blocking, a full mob
kill awarding xp, and room-scoped say. CONTEXT.md updated per the contributor
guide.

World and design by Tasmania (Tony Hosaroygard, hardlygospel.github.io), with
thanks to the creator of late.sh and its contributors.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Grow the seed world from 10 rooms to 110, opening the King's Road southward
into a continuous descent through seven new zones: Whisperwood (forest),
Duskhollow Caverns, the Drowned Crypts, Emberpeak Mines, the Frostspire Ascent,
the Sunken Citadel, and the demon realm of the Obsidian Throne.

Each new zone past the safe hub adds regular mobs plus a named boss scaled by
tier, ending at the final boss, the Archdemon Mal'gareth. Every room has a
hand-written description.

The world stays pure data behind seed_world(); only the data grew. The graph is
fully connected and reciprocal: every exit resolves to a real room, every room
is reachable from the start, and every passage can be walked back the way it
came. Added inline tests for room count (110), unique mob spawn ids, and that
every mob homes to a real room, alongside the existing exit-resolution and
reachability tests.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
…hops

Build out the core RPG systems for the MUD.

Classes (classes.rs): five classes - Warrior, Mage, Cleric, Rogue, Ranger -
each with a rich description, a one-line role tagline, a distinct resource
(Rage/Mana/Energy/Focus), and a passive class trait (Unbreakable, Arcane
Mastery, Light of the Dawn, Opportunist, Hunter's Instinct). Players choose a
class on entry. A formula-driven progression runs to level 50 with a rising
cubic xp curve, so the climb is a real grind; stats scale per class per level.

Abilities (abilities.rs): 55 abilities, 11 per class, unlocking across levels
1-50. Spells, skills, poisons, and buffs are unified under one AbilityEffect
enum (Strike, DamageOverTime, Heal, HealOverTime, Empower, Ward, Stun,
Finisher) so the runtime needs a single resolution path. Each has a cost,
cooldown, and flavorful description. The action bar exposes unlocked abilities
on number keys 1-9.

Items and economy (items.rs): an item catalog of weapons, armor across eight
equipment slots, rings, trinkets, consumables, and valuables, with rarities and
stat modifiers. A fleshed inventory: carry items, equip one per slot (recomputes
attack/hp/armor), use consumables, earn gold from kills, buy and sell at shops.

Town and shops: Embergate gains a shop district (rooms 201-205: outfitter,
apothecary, curio cart, bank, wall walk) and richer descriptions for the square,
tavern, temple, and market. Four NPC merchants - Bruna Ironhand the smith, Tomas
Threadneedle the outfitter, Old Mirela the apothecary, and Pell the Magpie -
each run a themed storefront the player can browse and trade at.

Movement gains diagonals (ne/nw/se/sw on y/u/n/m and the Dir enum). The world is
now 115 rooms, still fully connected and reciprocal (verified by parse plus the
inline graph tests). The runtime tick now also advances ability cooldowns,
resource regen, buff/shield timers, and mob damage-over-time and stuns.

Inline tests cover the xp curve and 50-level round-trip, per-class growth,
ability roster integrity (unique ids, a level-1 opener and level-50 capstone
each), item and shop integrity, class selection, buying/equipping, and the
Warrior death-save trait.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Two fixes to the Lateania combat systems.

Loot: MobSpawn gains a loot table and a boss flag. Every zone boss (the Elder
Treant through the Archdemon Mal'gareth) now drops a guaranteed item from a
themed, tier-appropriate table when slain; regular mobs have a modest chance at
a common drop. Drops land straight in the pack and are announced in the log,
with bosses calling theirs out loudly. Rolls use the existing rand dependency.

Rogue trait: Opportunist was defined but never applied. The Rogue now arms an
opening strike when engaging, and the first auto-attack of the fight lands as a
doubled critical hit before the flag clears. This completes trait coverage -
all five classes now have their passive live (Warrior death-save, Mage and
Cleric amplification, Ranger wounded-bonus, Rogue opening crit).

Inline tests cover that every boss has a valid loot table, all mob loot
references real items, and that the Rogue arms and spends its opening crit while
other classes do not.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Characters now survive logout. Progress - class, level, xp, gold, current hp,
inventory, and equipped gear - is saved to the database and restored on return,
so the world is genuinely persistent rather than resetting each session.

Storage: a new mud_characters table (migration 067) holds one row per user with
a schema-versioned JSON blob, mirroring the runtime_state trade-off game_rooms
already makes - the game owns the blob's shape and can add fields without a
migration each time. late_core::models::mud_character loads and upserts it.

Game side: persist.rs defines SavedCharacter, the durable slice of a player,
serialized with serde. Transient combat state (target, effects, cooldowns,
respawn timers) is intentionally not saved, so a character reloads at full
readiness in a safe room rather than resuming a logged-out fight. MudService
now carries a Db handle (threaded through the manager and main.rs): join loads
and hydrates any saved character before taking the world lock, leave captures
and saves it, and a 60-second autosave loop persists every present character so
an idle-timeout drop loses nothing.

Corrupt or empty blobs degrade to a fresh character instead of crashing, and
missing fields fall back to defaults, so old saves keep loading as the shape
evolves. Inventory and equipment are validated against the live item catalog on
load, dropping any items that no longer exist.

Inline tests cover the SavedCharacter JSON round-trip, empty-blob handling, and
partial/old-blob defaulting.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Three big additions to Lateania.

Damage types (damage.rs): every offensive ability and every mob attack now
carries a DamageType - Physical, Fire, Frost, Holy, Shadow, Poison, Arcane, or
Lightning. Mobs have a DamageProfile declaring the school they deal plus the
schools they resist (half damage) and are weak to (+50%), so element choice is a
real tactical lever. Undead are weak to Holy and resist Shadow, fire-things
resist Fire and melt to Frost, constructs shrug off Physical and break to
Arcane, and so on. Combat resolves all of it through one multiplier and calls
out resists and weaknesses in the log ("it's weak to this!"). Armor now blunts
physical blows fully but only half-shields against elemental damage, so caster
foes punch through plate. All 55 abilities were tagged with a thematic school
(Mage fire/frost/arcane/lightning, Cleric holy, Rogue/Ranger physical+poison,
Warrior physical).

World to 200 rooms (world.rs): eight new exploration wings branch off existing
zones, taking the world from 115 to 200 hand-described rooms. Each wing ends in
a new named miniboss (the Hexcrone of the Glade, the Barrow King, the
Tide-Drowned Leviathan, the Forgeheart Guardian, the Heart-of-Winter Wyrm, the
Warden of the Sealed Heart, the Herald of Mal'gareth, the Bandit Chief Garrote).
Wings are built through a link() helper that wires every exit reciprocally by
construction, so the one-way-exit class of bug cannot occur; anchors and id
ranges are spaced to never collide.

Enemy variety: the roster grows from ~30 to 67 distinct enemy types, each tagged
with a damage profile, spread across the new wings and scaled by tier.

Inline tests now assert 200 rooms, 50+ distinct enemy types, the damage
resist/weak math, and that every mob (including wing mobs) homes to a real room,
alongside the existing reachability and reciprocity checks.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
@hardlygospel hardlygospel requested a review from mpiorowski as a code owner June 5, 2026 12:27
…an revives

Major expansion of the Lateania MUD:

- 100 new rooms across new biomes (the Greatroad, Sapphire Coast,
  Verdant Highlands, Mistfen, Fungal Hollow, Sahra Wastes, Amber Savanna,
  Skyreach Mesas) reachable from Embergate's South Gate. Three capital
  cities - Tasmania, Melvanala, Matlatesh - each a safe haven with a
  healing fountain and a dedication plaque. Built on the reciprocal
  add_wing spine, so reachability and exit-reciprocity hold by construction.

- D&D ability scores (4d6 drop lowest) rolled at creation and rerollable
  (r) on the selection screen until a class is chosen. Constitution adds
  max HP and each class's key score adds attack; scores persist in the
  character blob (schema v2, serde-defaulted for old saves).

- A look/examine layer: room features (fountains, plaques, vistas) whose
  descriptions are revealed only when looked at (press o). A capital
  fountain restores HP/mana and refreshes resurrection charges.

- Titles earned by slaying foes (e.g. Wretchbane, Bane of the Barrow King),
  persisted and shown on the character sheet.

- Veteran resurrections: accounts older than 20 days rise in place twice
  per adventure instead of respawning at the temple; refreshed at a fountain.

- Every room now carries a paragraph-length description, enforced by a new
  test; the 86 terse base/extension rooms were expanded to match.

- Fixed the pre-existing stale strike_player unit test (3 vs 4 args).

All 45 mud unit tests pass; the library builds clean.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Tony Hosaroygard added 3 commits June 5, 2026 23:01
Adds LateaniaService::flush_all and calls it from the server shutdown
sequence next to the artboard and pinstar flushes, so an adventure in
progress survives a clean restart with no loss. Previously up to one
autosave interval (60s) could be lost when the server stopped between
ticks; characters were never wiped (they reload from their last save),
but recent progress could be. Saves are best-effort per character.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Adds a small automap to the bottom of the Room side-panel showing where
you've been. The current room (@), visited rooms (o), unexplored exits
(.), and the corridors between them are laid onto a 7x5-room grid
centred on the player; up/down exits, which a flat map can't place, are
noted as text.

Each character now tracks a visited room set, seeded on spawn and
extended on every move. The set persists with the character (save schema
bumped to v3, serde-default so older saves load and simply start the map
fresh). World::minimap BFS-walks exits out from the current room so the
shortest path wins the world's occasional non-Euclidean clashes.

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
The world has vertical exits (15 down, 6 up) and the < > / , . keys
already move through them, but the Commands panel only listed move,
diagonals, and attack - so when a room had a 'down' exit players had no
idea which key to press. Add a contextual hint that names the stair keys
exactly when the current room has a way up or down. Also corrects the
stale key-scheme comment (diagonals are y/u/n/m, not y/u/b/n - b is the
shop key).

Signed-off-by: Tony Hosaroygard <tasmaniamate@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants