Compare commits
No commits in common. "94a01186c882572b905aecd9efa06fc7b92199c9" and "74538756d5bb44dd475c435259d14a21e0e1390c" have entirely different histories.
94a01186c8
...
74538756d5
3 changed files with 0 additions and 892 deletions
|
|
@ -1,232 +0,0 @@
|
||||||
prompt system
|
|
||||||
=============
|
|
||||||
|
|
||||||
modal prompts that show useful info, with color markup support.
|
|
||||||
|
|
||||||
|
|
||||||
what we have now
|
|
||||||
================
|
|
||||||
|
|
||||||
server.py lines 314-320 picks between three hardcoded strings:
|
|
||||||
|
|
||||||
editor mode: " {line_number}> "
|
|
||||||
IF mode: "> "
|
|
||||||
everything: "mud> "
|
|
||||||
|
|
||||||
no stats, no color, no per-player customization. the prompt function
|
|
||||||
is inline in the server loop.
|
|
||||||
|
|
||||||
|
|
||||||
what we want
|
|
||||||
============
|
|
||||||
|
|
||||||
a prompt renderer: given a player, return the prompt string (with ANSI
|
|
||||||
already baked in based on their color_depth). each mode has its own
|
|
||||||
prompt template. templates use variables that get filled from player
|
|
||||||
state.
|
|
||||||
|
|
||||||
default prompts by mode:
|
|
||||||
|
|
||||||
normal mode:
|
|
||||||
<56%> 200 >
|
|
||||||
|
|
||||||
- stamina as percentage (stamina / max_stamina * 100, rounded)
|
|
||||||
- power level as integer (floor of pl)
|
|
||||||
- the > delimiter
|
|
||||||
|
|
||||||
combat mode:
|
|
||||||
<56%> 200 [punch left] vs Goku >
|
|
||||||
|
|
||||||
- same stamina/pl gauges
|
|
||||||
- current attack or defense stance in brackets
|
|
||||||
- opponent name
|
|
||||||
- if no active move: [idle]
|
|
||||||
|
|
||||||
editor mode:
|
|
||||||
(keep as-is: " {line}> ")
|
|
||||||
editor prompt is special — it comes from the editor, not the
|
|
||||||
prompt system. leave it alone.
|
|
||||||
|
|
||||||
IF mode:
|
|
||||||
(keep as-is: "> ")
|
|
||||||
the z-machine has its own prompt conventions. don't override.
|
|
||||||
|
|
||||||
|
|
||||||
color markup codes
|
|
||||||
==================
|
|
||||||
|
|
||||||
add a lightweight color markup language for prompt templates and
|
|
||||||
eventually for all builder-authored text. similar to miniboa's caret
|
|
||||||
codes but using {color} syntax so it reads more naturally and doesn't
|
|
||||||
collide with content text.
|
|
||||||
|
|
||||||
codes (16-color baseline):
|
|
||||||
|
|
||||||
{red} {RED} (bright)
|
|
||||||
{green} {GREEN}
|
|
||||||
{yellow} {YELLOW}
|
|
||||||
{blue} {BLUE}
|
|
||||||
{magenta} {MAGENTA}
|
|
||||||
{cyan} {CYAN}
|
|
||||||
{white} {WHITE}
|
|
||||||
{black} {BLACK}
|
|
||||||
{bold} {dim}
|
|
||||||
{reset} {/} (shorthand for reset)
|
|
||||||
|
|
||||||
two escaping approaches considered:
|
|
||||||
|
|
||||||
1. miniboa-style: ^r for red, ^R for bright red. terse, fast to type
|
|
||||||
in-game. but ^ collides with mountain terrain char and regex.
|
|
||||||
2. {tag} style: {red}, {bold}, {/}. more readable, no collisions,
|
|
||||||
familiar from other MUDs (CircleMUD uses &, ROM uses {).
|
|
||||||
|
|
||||||
going with {tag} style. it's what builders will type in room descriptions
|
|
||||||
and NPC dialogue too, so it should be readable. the render function
|
|
||||||
converts tags to ANSI based on client color depth, or strips them for
|
|
||||||
clients with no color support.
|
|
||||||
|
|
||||||
implementation: a colorize(text, color_depth) function in render/colors.py
|
|
||||||
that replaces {tag} tokens with the appropriate ANSI escape. clients with
|
|
||||||
caps.ansi=False get tags stripped entirely.
|
|
||||||
|
|
||||||
|
|
||||||
prompt template variables
|
|
||||||
=========================
|
|
||||||
|
|
||||||
templates are strings with {variable} references. the prompt renderer
|
|
||||||
fills them from player state.
|
|
||||||
|
|
||||||
core variables (available in all modes):
|
|
||||||
|
|
||||||
{stamina_pct} stamina as integer percentage
|
|
||||||
{stamina} current stamina (float, 1 decimal)
|
|
||||||
{max_stamina} stamina ceiling
|
|
||||||
{pl} power level (integer)
|
|
||||||
{name} player name
|
|
||||||
{mode} current mode string
|
|
||||||
{x}, {y} coordinates
|
|
||||||
|
|
||||||
combat-only variables:
|
|
||||||
|
|
||||||
{move} current attack/defense name, or "idle"
|
|
||||||
{opponent} opponent name
|
|
||||||
{combat_state} telegraph/window/idle
|
|
||||||
|
|
||||||
these overlap with color tag syntax ({red} vs {pl}). resolution:
|
|
||||||
color tags are a fixed set of known names. anything not in the color
|
|
||||||
tag set gets looked up as a template variable. or: process template
|
|
||||||
variables first (they produce plain text), then process color tags
|
|
||||||
(they produce ANSI). two-pass approach, clean separation.
|
|
||||||
|
|
||||||
|
|
||||||
the prompt function
|
|
||||||
===================
|
|
||||||
|
|
||||||
def render_prompt(player: Player) -> str
|
|
||||||
|
|
||||||
lives in a new module: src/mudlib/prompt.py
|
|
||||||
|
|
||||||
logic:
|
|
||||||
1. pick the template string based on player.mode
|
|
||||||
2. build a context dict from player state
|
|
||||||
3. fill template variables (str.format_map or similar)
|
|
||||||
4. convert color tags to ANSI (based on player.caps.color_depth)
|
|
||||||
5. return the final string
|
|
||||||
|
|
||||||
the server loop replaces its inline if/elif with:
|
|
||||||
|
|
||||||
from mudlib.prompt import render_prompt
|
|
||||||
_writer.write(render_prompt(player))
|
|
||||||
|
|
||||||
editor and IF modes short-circuit before template processing (they have
|
|
||||||
their own prompt logic that shouldn't change).
|
|
||||||
|
|
||||||
stamina gauge coloring: the prompt template itself can include
|
|
||||||
conditional color. simplest approach: the template variable {stamina_pct}
|
|
||||||
is just a number, but we provide a {stamina_gauge} that includes color:
|
|
||||||
|
|
||||||
>= 60%: {green}<{stamina_pct}%>{/}
|
|
||||||
30-59%: {yellow}<{stamina_pct}%>{/}
|
|
||||||
< 30%: {red}<{stamina_pct}%>{/}
|
|
||||||
|
|
||||||
the gauge is a computed variable, not raw data. the template for normal
|
|
||||||
mode becomes:
|
|
||||||
|
|
||||||
{stamina_gauge} {pl} >
|
|
||||||
|
|
||||||
with the gauge expanding to e.g. {green}<56%>{/} which then gets
|
|
||||||
colorized.
|
|
||||||
|
|
||||||
pl coloring: same idea. {pl_gauge} colors based on absolute value or
|
|
||||||
percentage of starting PL. details TBD — we might want max_pl on Entity
|
|
||||||
first.
|
|
||||||
|
|
||||||
|
|
||||||
implementation phases
|
|
||||||
=====================
|
|
||||||
|
|
||||||
phase 1: prompt module + normal mode prompt
|
|
||||||
- create src/mudlib/prompt.py with render_prompt()
|
|
||||||
- implement stamina_gauge and pl display
|
|
||||||
- wire into server loop (replace hardcoded "mud> ")
|
|
||||||
- tests for render_prompt with mock players
|
|
||||||
- editor and IF modes unchanged (short-circuit)
|
|
||||||
|
|
||||||
phase 2: combat mode prompt
|
|
||||||
- add combat context variables (move, opponent, state)
|
|
||||||
- combat prompt template
|
|
||||||
- tests with mock encounters
|
|
||||||
|
|
||||||
phase 3: color markup engine
|
|
||||||
- create src/mudlib/render/colors.py with colorize()
|
|
||||||
- {tag} to ANSI conversion, respects color_depth
|
|
||||||
- strip tags for no-color clients
|
|
||||||
- integrate into prompt renderer
|
|
||||||
- tests for colorize at each color depth
|
|
||||||
|
|
||||||
phase 4: per-player prompt customization (future)
|
|
||||||
- player.prompt_template stored in sqlite
|
|
||||||
- "prompt" command to set custom template
|
|
||||||
- default template per mode if player hasn't customized
|
|
||||||
- builder mode template (future: brush, color hex, etc.)
|
|
||||||
|
|
||||||
|
|
||||||
files touched
|
|
||||||
=============
|
|
||||||
|
|
||||||
new:
|
|
||||||
src/mudlib/prompt.py — the prompt renderer
|
|
||||||
src/mudlib/render/colors.py — color markup engine
|
|
||||||
tests/test_prompt.py — prompt tests
|
|
||||||
tests/test_colors.py — color markup tests
|
|
||||||
|
|
||||||
modified:
|
|
||||||
src/mudlib/server.py — replace inline prompt with render_prompt()
|
|
||||||
src/mudlib/entity.py — maybe add max_pl if we want PL gauge coloring
|
|
||||||
|
|
||||||
|
|
||||||
future ideas
|
|
||||||
============
|
|
||||||
|
|
||||||
- GMCP prompt data: send structured prompt info over GMCP so graphical
|
|
||||||
clients can render their own gauges. the text prompt is for raw telnet.
|
|
||||||
|
|
||||||
- builder mode prompt: when we add the world editor, show brush type,
|
|
||||||
current terrain char, and palette color.
|
|
||||||
[brush: T] [#1b5e20] build>
|
|
||||||
|
|
||||||
- rest/regen indicator: show a + or ~ when stamina is regenerating.
|
|
||||||
<56%+> 200 >
|
|
||||||
|
|
||||||
- compass rose: show available exits inline.
|
|
||||||
<56%> 200 [NSEW] >
|
|
||||||
|
|
||||||
- danger indicator: flash or color the prompt when PL drops low enough
|
|
||||||
that one more hit could KO.
|
|
||||||
|
|
||||||
- custom prompt command examples:
|
|
||||||
prompt {stamina_gauge} {pl} >
|
|
||||||
prompt {stamina_gauge} {pl} [{move}] >
|
|
||||||
prompt {name} [{mode}] >
|
|
||||||
|
|
||||||
- MXP/MNES clickable prompt elements (far future)
|
|
||||||
|
|
@ -1,596 +0,0 @@
|
||||||
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).
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
project documentation
|
|
||||||
=====================
|
|
||||||
|
|
||||||
all project docs, grouped by topic.
|
|
||||||
|
|
||||||
vision
|
|
||||||
------
|
|
||||||
|
|
||||||
- ``DREAMBOOK.md`` — vision, philosophy, wild ideas
|
|
||||||
- ``docs/why/telnet-first.txt`` — why telnet as the protocol
|
|
||||||
- ``docs/why/text-worlds.txt`` — why text worlds matter
|
|
||||||
|
|
||||||
engine
|
|
||||||
------
|
|
||||||
|
|
||||||
- ``docs/how/architecture-plan.txt`` — engine/content boundary, mode stack, entity model, game loop
|
|
||||||
- ``docs/how/commands.txt`` — command registration, async handlers, aliases, mode filtering
|
|
||||||
- ``docs/how/persistence.txt`` — SQLite storage, what's persisted vs runtime
|
|
||||||
- ``docs/how/terrain-generation.txt`` — perlin noise, elevation thresholds, river tracing
|
|
||||||
- ``docs/how/things-and-building.rst`` — interactive objects, python class decorators
|
|
||||||
- ``docs/how/zones-and-building.rst`` — zones as spatial unit, procedural overworld, player homes
|
|
||||||
- ``docs/how/prompt-system.txt`` — modal prompts, color markup, per-player customization
|
|
||||||
|
|
||||||
combat
|
|
||||||
------
|
|
||||||
|
|
||||||
- ``docs/how/combat.rst`` — state machine, TOML-defined moves, timing windows
|
|
||||||
|
|
||||||
interactive fiction
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
- ``docs/how/if-journey.rst`` — master IF doc: five integration levels, audit findings, milestones
|
|
||||||
- ``docs/how/if-terminal.txt`` — level 1: dfrotz subprocess, spectator broadcasting
|
|
||||||
- ``docs/how/if-integration.txt`` — original IF research (predates audits)
|
|
||||||
- ``docs/how/viola-embedding-audit.rst`` — viola interpreter audit
|
|
||||||
- ``docs/how/zvm-embedding-audit.rst`` — zvm interpreter audit
|
|
||||||
- ``docs/how/mojozork-audit.rst`` — MojoZork multiplayer C implementation audit
|
|
||||||
- ``docs/how/zmachine-performance.rst`` — profiling, optimization (cold-start 4720ms to 786ms)
|
|
||||||
- ``docs/how/multiplayer-zmachine-design.rst`` — level 4 multiplayer design
|
|
||||||
- ``docs/plans/zmachine-game-compatibility.rst`` — game test plan for interpreter
|
|
||||||
|
|
||||||
research
|
|
||||||
--------
|
|
||||||
|
|
||||||
- ``docs/how/mudlib-landscape.txt`` — survey of MUD architectures (Diku, MOO, LPC, evennia)
|
|
||||||
- ``docs/how/mud-ecosystem.txt`` — catalog of engines, clients, protocols
|
|
||||||
- ``docs/how/dsls.md`` — scripting language catalog (MOO, LPC, MUSH) with examples
|
|
||||||
- ``docs/research/dbzfe_notes.rst`` — DBZFE MUD reference notes
|
|
||||||
- ``docs/research/dbzfe.log`` — sample MUD output
|
|
||||||
- ``docs/research/dbzfe.tin`` — TinTin++ config
|
|
||||||
|
|
||||||
lessons
|
|
||||||
-------
|
|
||||||
|
|
||||||
- ``docs/lessons/charset-vs-mtts.txt`` — telnet CHARSET negotiation hang, MTTS workaround
|
|
||||||
- ``docs/lessons/dfrotz-prompt-detection.txt`` — dfrotz prompt format varies, detection logic
|
|
||||||
- ``docs/how/utf8-design-lesson.rst`` — ASCII bias in UTF-8, international MUD implications
|
|
||||||
- ``docs/how/zmachine-garbled-output-investigation.rst`` — string decoding corruption debug trace
|
|
||||||
|
|
||||||
plans
|
|
||||||
-----
|
|
||||||
|
|
||||||
- ``docs/plans/if-terminal.txt`` — level 1 IF architecture design (predates implementation)
|
|
||||||
- ``docs/plans/zmachine-game-compatibility.rst`` — game test plan for interpreter
|
|
||||||
Loading…
Reference in a new issue