zones and building — from grids to worlds ========================================= a design doc. captures the vision for how players and builders create, connect, and inhabit spaces in the MUD. born from a riffing session, fed by research into how other engines handle this. living document — update as decisions get made and things get built. see also: ``things-and-building.rst`` (item/verb system), ``architecture-plan.txt`` (engine/content boundary), ``prompt-system.txt`` (modal prompts). the core idea — everything is a zone -------------------------------------- a zone is a rectangular grid of tiles. it has dimensions, a palette, and tile data. that's it. everything is a zone. - the procedural overworld? a zone with a generator (perlin noise) - a hand-built treehouse? a zone with tile data from a file - a single room (office, closet)? a small zone - a dungeon? a zone with procedural guts - the flower you spawn in? a tiny zone the differences between zones are properties, not types: **wrapping** does the zone loop? options: toroidal (wraps both axes, like a sphere — walk far enough and you're back), cylindrical (wraps one axis), bounded (hard edges, you hit a wall or void). **generator** how tiles get filled. ``none`` means blank canvas (you paint it). ``perlin`` means procedural terrain from a seed. ``file`` means tile data loaded from a TOML. generators and hand-placed tiles can coexist — generate a base, then paint over specific areas. **dimensions** 5x5, 100x50, 1000x1000. whatever the space needs. **palette** what each symbol means. char + name + color + properties (passable, movement cost, opacity, etc). every zone has a palette. zones can share palettes or define their own. zone data format ----------------- the web editor already saves something close to this. extend it with zone metadata and portal definitions. :: [zone] name = "treehouse" description = "a treehouse nestled in the canopy of an ancient oak" wrapping = "bounded" spawn = [10, 7] [map] width = 20 height = 15 data = """ ..oooo..TTT......... ..o..o..TTT......... ..o..o..TTT......... ..oooo....T......... ..........T......... ........+.T......... ..........T......... ..........TTTTT..... ................~~~~ ................~~~~ .................... .................... .................... .................... .................... """ [palette] entries = """ o|wall|#8b4513|solid .|floor|#d2b48c|passable T|leaves|#0a5f0a|passable ~|water|#2244aa|solid +|door|#c8a456|passable """ [[portals]] x = 10 y = 14 target = "zone:forest_path:20,5" label = "a rope bridge stretches into the canopy" [[portals]] x = 10 y = 0 target = "overworld:450,300" label = "a ladder descends to the ground far below" zones live in ``content/zones/``. loaded at startup like mobs and commands. hot-reloadable via ``reload``. player location model ---------------------- today a player is ``(x, y)`` on the overworld. with zones, a player is either on the overworld OR in a zone. conceptually: ``(zone_name, x, y)`` where ``zone_name`` is ``None`` for the overworld. the viewport, movement, look — all the same mechanics, just reading from a different grid. portals are tiles with a target. step on one, you transition. your location changes from ``(treehouse, 10, 14)`` to ``(forest_path, 20, 5)``. the viewport redraws with the new zone's tiles and palette. open question: do portals auto-trigger (step on tile = transition) or require explicit action (``enter``, ``use door``)? could depend on the portal — a path auto-triggers, a locked door requires a key and ``open door`` + ``enter``. the portal definition could specify this:: [[portals]] x = 5 y = 3 target = "zone:basement:5,0" trigger = "auto" # step on it and you go label = "stairs descend into darkness" [[portals]] x = 12 y = 7 target = "zone:vault:0,0" trigger = "manual" # requires 'enter' command label = "a heavy iron door" requires = "iron_key" # item check (future) the flower spawn ----------------- new player experience. you wake up inside a sealed flower. the flower is a tiny zone, maybe 7x7. petals surround you. the description mentions perfume, texture on your hands, dim light filtering through. you can look around but every direction is sealed — petal walls. any force command (push, kick, punch, pull) opens the flower. the petal tiles change (solid → passable, or the zone swaps entirely), you "fall" — a text transition, maybe a brief animation of symbols scrolling — and land in the treehouse zone. the treehouse is the real starting area. hand-built, 20x15 or whatever feels right. has a few things to interact with, maybe a note or a simple puzzle. eventually you find the way down (a portal) and land on the overworld. infinite procedural landscape in every direction. tutorial funnel: tiny space → medium space → infinite space. :: flower (7x7, sealed) ↓ player breaks out treehouse (20x15, hand-built) ↓ portal (ladder, rope, etc) overworld (1000x1000, procedural) ↓ portals to other zones dungeons, houses, other players' creations... creating zones — three workflows ---------------------------------- all three produce the same format. interchangeable. workflow A: web editor ~~~~~~~~~~~~~~~~~~~~~~~ open the map editor in a browser. paint tiles, define palette, set dimensions. save as TOML. drop the file in ``content/zones/``. reload in the MUD (or restart). the zone exists. the web editor is not special — it's just faster for large-scale work. photoshop vs MS paint. good for: laying out a big area, precise pixel work, working offline, iterating visually. workflow B: in-game commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @new-zone jareds-house 100 50 creates a blank bounded zone. you're placed in the center. the viewport shows void/empty tiles. now you build. :: @new-zone the-wilds 500 500 --wrap creates a toroidal zone. walk far enough and you loop. :: @new-zone dungeon-3 40 40 --gen perlin creates a zone with procedural terrain. paint over specific parts. workflow C: paint mode ~~~~~~~~~~~~~~~~~~~~~~~ this is the one that buzzes. push into paint mode like editor mode or IF mode. your ``@`` becomes a cursor. movement works freely — no passability checks, you float over everything. two states within paint mode: **survey** (default) move around, look at things, nothing changes. scouting. **painting** every tile you leave behind gets stamped with your brush. walk east 5 tiles, leave 5 walls behind you. toggle with ``p``. status line shows state:: [paint: . floor] survey [paint: o wall] PAINTING change brush: type the symbol character. type ``o`` and your brush is now wall. type ``.`` for floor. type ``~`` for water. single-char input sets the brush — no ambiguity because you're in a special mode where normal commands don't apply. or explicit: ``b o`` for brush=wall. both work. you could even render as the brush symbol instead of ``@`` when painting. you see an ``o`` moving around leaving ``o``'s behind it. entering and exiting:: @paint enter paint mode (survey by default) p toggle painting on/off o set brush to 'o' (wall) n/s/e/w/ne/etc move (and paint if painting) @paint exit paint mode (or :q, or @done) workflow D: @dig (expanding the canvas) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ not room-linking like traditional MUDs. you're extending the zone's tile grid itself. :: @dig 5 e zone grows 5 tiles east (new tiles are void) @dig 10 s zone grows 10 tiles south @dig n grow 1 tile north (default) new space is void/empty. paint it into existence. this means zones aren't fixed-size — they grow as you build. start with a 1x1 zone and @dig your way to a mansion. materials and palettes ----------------------- every symbol needs at minimum: char, name, color. that's the palette. :: [palette] entries = """ .|floor|#d2b48c|passable o|wall|#8b4513|solid ~|water|#2244aa|solid +|door|#c8a456|passable #|window|#87ceeb|solid ^|mountain|#666666|solid T|tree|#0a5f0a|passable :|gravel|#9a9996|passable =|brick|#a51d2d|solid """ each zone has a palette. could be inline or reference a shared one:: [zone] palette = "default" # use content/palettes/default.toml a shared palette means consistent symbols across zones. a custom palette means a zone can redefine what ``~`` looks like (lava instead of water). future properties per material: movement cost (forest is slower), opacity (walls block line of sight), damage (lava hurts), wetness (water makes you wet). for now, just passable/solid is enough. portals connect zones ---------------------- a portal is a tile with a target. defined in the zone TOML or placed in-game. in-game portal creation:: @portal here → jareds-house:5,3 "this tile, right where I'm standing, is a portal to jareds-house at coordinates 5,3." portals are bidirectional by default? or one-way? probably one-way by default (you set up both ends), with a convenience flag for bidirectional:: @portal here ↔ jareds-house:5,3 # creates both sides what stepping on a portal looks like: the viewport dissolves (or snaps) to the new zone. you get a brief transition message. your prompt updates if the zone has different ambient info. connecting to the overworld ---------------------------- the overworld is just another zone — the big one. portals from the overworld into smaller zones work the same way. a tile at (450, 300) on the overworld has a portal to ``treehouse:10,0``. the treehouse has a portal back to ``overworld:450,300``. buildings, dungeons, caves, interiors — all zones with portals from the overworld. the overworld tile might show a special symbol (a door icon, a cave mouth) to hint that something's there. procedural as a tool, not the default --------------------------------------- the procedural perlin world is useful but it's a tool. good for: - wilderness areas (forests, plains, mountains) - dungeon generation (random layouts with a seed) - filler terrain between hand-built areas - prototyping (generate a base, paint over it) the main game world should be hand-built. the starting experience, key locations, cities, landmarks — all authored by humans. procedural fills in the gaps, provides variety, makes the world feel larger than what was hand-crafted. a zone can mix both: generate perlin terrain, then overlay hand-placed features (roads, buildings, rivers) on top. the generator runs first, then the overlay applies. layers (future) ---------------- jared mentioned up/down and layers. a zone could have a z-axis — floor 1 is the ground, floor 2 is the second story, floor -1 is the basement. same x,y grid, different z level. portals (stairs, ladders, holes) connect layers vertically. not needed for v1 but the data model should not prevent it. a zone's tile data could eventually be:: [map] width = 20 height = 15 layers = 3 data.0 = """...""" # ground floor data.1 = """...""" # second floor data.-1 = """...""" # basement or layers could just be separate zones with portals. simpler, same result. decide later. what we learned from other engines ----------------------------------- research into how DikuMUD, LPC, MOO, Evennia, and others handle building. key takeaways and things to avoid. what works ~~~~~~~~~~~ **MOO's immediacy.** in LambdaMOO, @dig creates a room and you're done. no save step, no compilation, no restart. changes are instant. builders stay in flow. we want this — @new-zone, @paint, and your changes are live. **evennia's XYZGrid.** optional coordinate system drawn as ASCII maps in python files. rooms at grid intersections, links along paths. transition nodes connect separate maps. proves grid-based worlds work in a room-based engine. their wilderness contrib is smart: players get their own room instance at a coordinate, share when co-located. **diku OLC menus.** redit/oedit/medit push you into an edit mode with numbered options. simple, discoverable, no memorization. the nested mode pattern (game → editor → description editor) maps well to our mode stack. **LPC file-based content.** builders write .c files, ``update`` compiles them live. version controllable, diffable, shareable. our TOML approach has the same benefits without requiring a programming language. **ranvier's hybrid coordinates.** optional (x,y,z) per room, local to each area. explicit exits override inferred ones. areas can mix coordinate and non-coordinate rooms. flexible and clean. **dead souls QCS.** wizard-driven creation (``addnpc``, ``addroom``) generates LPC files via interactive prompts. lowers the barrier — builders answer questions instead of writing code. what people hate ~~~~~~~~~~~~~~~~~ **menu-driven OLC is clunky.** everyone acknowledges OLC is better than manual file editing, but it's still tedious. numbered menus, nested sub-menus, explicit save commands. modern builders expect something faster. our paint mode is a direct response to this. **descriptions nobody reads.** "most players don't read the descriptions after seeing them the first time, and probably only skim them the first time anyway." high effort, low return. our tile-based visual approach sidesteps this — you SEE the room, you don't read a paragraph about it. descriptions can supplement the visual, not replace it. **too much coding required.** evennia requires python for anything beyond basic building. MUSHcode has a brutal learning curve. MOO programming is its own language. builders want to create content, not learn to program. our approach: TOML data files for definitions, paint mode for visuals, minimal programming needed for basic building. **collaborative building is an afterthought.** evennia's default setup doesn't support collaborative building well. MOO's permission system creates unexpected barriers. we should design for collaboration from the start — multiple builders in a zone, clear ownership, easy sharing. **framework lock-in.** evennia's ObjectDB forces assumptions that don't fit all games. our zone model is simple data (TOML files, tile grids) — no complex object hierarchy to fight against. **no good copy-paste or templating.** every room built from scratch. repetitive work. our web editor handles this naturally (select, copy, paste). in-game: could support @copy-region or stamp/template tools. **saving and losing work.** diku OLC requires explicit saves; crash before saving = lost work. MOO's auto-persistence is better. our zones should auto-save on edit (or at least on leaving paint mode). **recruiting builders is hard.** the harder the tools, the fewer builders you get. "building can be an enjoyable activity, but it's got its downsides. which probably explains why MUD creators have a difficult time recruiting dedicated builders." paint mode should make building feel like play, not work. patterns from zone connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **evennia XYZGrid transition nodes.** metadata-only nodes that hold target coordinates on another map. never spawn as rooms. clean abstraction for zone-to-zone links. similar to our portal concept. **diku inter-zone exits.** reference format: ``room_name@zone_name``. globally unique vnums (virtual numbers). our equivalent: ``zone_name:x,y``. **evennia wilderness fixed locations.** certain wilderness coordinates are "fixed rooms" — reach those coords, transition into a real room. this is how building entrances work in their wilderness system. maps directly to our portal-on-overworld-tile concept. **alex kallend's wilderness (1999).** generate descriptions deterministically from coordinates. same coords always produce same room. memory-efficient. this is basically what our perlin generator does. **ranvier cross-area linking.** reference format: ``areaname:roomid``. works between coordinate and non-coordinate rooms. our format: ``zone:name:x,y`` or ``overworld:x,y``. .. note:: open research: dig deeper into other MUD codebases for building patterns. specifically: - evennia's prototype/spawn system for templating objects across zones - how discworld (LPC) handles its massive hand-built world - AresMUSH's web-based building tools (they moved building to the browser) - graphicMUD's tile-based building (closest to our model — they use sixel graphics on a tile grid) - how any engine handles "undo" for building mistakes - permission models that actually work for collaborative building without being bureaucratic build order ------------ rough priority. each step is usable on its own. 1. **zone data model + registry.** load TOML zone files from ``content/zones/``. zone has dimensions, tile grid, palette, portals, metadata. registry maps zone names to loaded zones. 2. **player-in-zone tracking.** extend player location from ``(x, y)`` to ``(zone, x, y)``. zone=None means overworld. persist to sqlite. 3. **viewport reads from zone.** ``look`` checks which zone the player is in and reads tiles from that zone's grid instead of the overworld. movement checks that zone's passability rules. same rendering pipeline, different data source. 4. **@new-zone + @goto.** create blank zones from in-game. ``@goto treehouse`` teleports you into a zone. ``@goto overworld`` returns you. builder/admin commands. 5. **paint mode.** push onto mode stack. cursor movement without passability. brush selection. paint toggle. status line integration. 6. **file import/export.** load zone from TOML file (web editor output). save zone state back to TOML. the web editor and in-game building produce the same files. 7. **portals.** portal definitions in zone TOML. portal tiles trigger zone transitions. ``@portal`` command for in-game portal creation. 8. **@dig.** expand zone dimensions from in-game. grow the canvas in any direction. 9. **procedural generator as zone option.** ``--gen perlin`` on zone creation. generate base terrain, allow painting over it. 10. **the flower spawn.** hand-build the flower and treehouse zones. wire the new-player experience to start in the flower instead of the overworld center. open questions --------------- - should the overworld be a zone, or is it a special case? making it a zone simplifies the code (everything goes through the same path) but the overworld has unique properties (huge, procedural, wrapping). probably: overworld IS a zone, just a large wrapping one with a generator. - zone ownership and permissions. who can edit a zone? the creator? anyone with builder role? co-ownership? defer this until the permission model from things-and-building.rst is designed. - how big can a zone be before performance matters? a 1000x1000 zone is 1MB of chars. loading, saving, and transmitting that is fine. but do we want to support zones larger than that? probably not initially. - persistence model. are zones purely file-based (edit file, reload) or do runtime changes (paint mode edits) persist automatically? leaning toward: paint mode edits modify in-memory state AND write back to the TOML file. the file is always the source of truth, and it stays current. - what symbol is void/empty? tiles that haven't been painted yet. could be space (invisible), could be a dim dot, could be configurable. needs to be visually distinct from any palette symbol. - should portals be visible on the map? a special symbol, a color, a blinking character? or do they look like their underlying tile (a door looks like a door, not like a portal)? - layers vs separate zones for vertical space. layers are elegant but add complexity. separate zones with portals (stairs) are simpler and get the same result. start with separate zones. relation to things-and-building.rst ------------------------------------- that doc covers the item/verb system (phase 1-2) and room system (phase 3). zones and building here are the spatial foundation that the item system lives on top of. zones provide the WHERE. things provide the WHAT. a zone has tiles (terrain, walls, floor). things sit on tiles (items, furniture, NPCs). portals are a zone-level concept. doors with locks are a thing-level concept (the door is a @thing with an @verb for ``open`` that checks for a key). build zones first (this doc), then populate them with things (things-and-building.rst).