Add describe command for home zone descriptions

Allows players to set custom descriptions for their home zones.
Only works in the player's own home zone. Saves to TOML file.
This commit is contained in:
Jared Miller 2026-02-14 17:41:43 -05:00
parent 5d14011684
commit 5b6c808050
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 204 additions and 0 deletions

View file

@ -0,0 +1,50 @@
"""Describe command — set home zone description."""
from mudlib.commands import CommandDefinition, register
from mudlib.housing import save_home_zone
from mudlib.player import Player
from mudlib.zone import Zone
async def cmd_describe(player: Player, args: str) -> None:
"""Set the description for your home zone.
Usage:
describe <text> set your home zone description
describe show current description
"""
zone = player.location
# Must be in a zone
if not isinstance(zone, Zone):
await player.send("You aren't anywhere.\r\n")
return
# Must be in own home zone
if zone.name != player.home_zone:
await player.send("You can only describe your own home zone.\r\n")
return
# No args — show current description
if not args.strip():
await player.send(f"Current description: {zone.description}\r\n")
return
# Set new description
description = args.strip()
if len(description) > 500:
await player.send("Description too long (max 500 characters).\r\n")
return
zone.description = description
save_home_zone(player.name, zone)
await player.send(f"Home zone description set to: {zone.description}\r\n")
register(
CommandDefinition(
"describe",
cmd_describe,
help="Set your home zone description.",
)
)

View file

@ -16,6 +16,8 @@ from telnetlib3.server_shell import readline2
import mudlib.combat.commands import mudlib.combat.commands
import mudlib.commands import mudlib.commands
import mudlib.commands.containers import mudlib.commands.containers
import mudlib.commands.crafting
import mudlib.commands.describe
import mudlib.commands.edit import mudlib.commands.edit
import mudlib.commands.examine import mudlib.commands.examine
import mudlib.commands.fly import mudlib.commands.fly
@ -31,6 +33,7 @@ import mudlib.commands.reload
import mudlib.commands.snapneck import mudlib.commands.snapneck
import mudlib.commands.spawn import mudlib.commands.spawn
import mudlib.commands.talk import mudlib.commands.talk
import mudlib.commands.terrain
import mudlib.commands.things import mudlib.commands.things
import mudlib.commands.use import mudlib.commands.use
from mudlib.caps import parse_mtts from mudlib.caps import parse_mtts

View file

@ -0,0 +1,151 @@
"""Tests for the describe command."""
from unittest.mock import AsyncMock, MagicMock
import pytest
from mudlib.commands.describe import cmd_describe
from mudlib.housing import init_housing
from mudlib.player import Player
from mudlib.zone import Zone
from mudlib.zones import register_zone, zone_registry
@pytest.fixture(autouse=True)
def _clean_registries():
saved = dict(zone_registry)
zone_registry.clear()
yield
zone_registry.clear()
zone_registry.update(saved)
@pytest.fixture
def _init_housing(tmp_path):
from mudlib.store import create_account, init_db
init_housing(tmp_path / "player_zones")
init_db(tmp_path / "test.db")
# Create accounts for test players
for name in ["alice", "bob", "charlie"]:
create_account(name, "testpass")
def _make_home_zone(player_name="alice"):
name = f"home:{player_name}"
terrain = []
for y in range(9):
row = []
for x in range(9):
if x == 0 or x == 8 or y == 0 or y == 8:
row.append("#")
else:
row.append(".")
terrain.append(row)
zone = Zone(
name=name,
description=f"{player_name}'s home",
width=9,
height=9,
terrain=terrain,
toroidal=False,
impassable={"#", "^", "~"},
spawn_x=4,
spawn_y=4,
safe=True,
)
register_zone(name, zone)
return zone
def _make_player(name="alice", zone=None, x=4, y=4):
mock_writer = MagicMock()
mock_writer.write = MagicMock()
mock_writer.drain = AsyncMock()
p = Player(name=name, location=zone, x=x, y=y, writer=mock_writer)
p.home_zone = f"home:{name}"
return p
@pytest.mark.asyncio
async def test_describe_sets_description(_init_housing):
"""describe <text> sets zone.description."""
home = _make_home_zone("alice")
player = _make_player("alice", zone=home)
await cmd_describe(player, "a cozy cottage by the sea")
assert home.description == "a cozy cottage by the sea"
@pytest.mark.asyncio
async def test_describe_not_in_home_zone(_init_housing):
"""describe fails if not in player's own home zone."""
_make_home_zone("alice")
other_zone = Zone(
name="overworld",
description="The world",
width=20,
height=20,
terrain=[["." for _ in range(20)] for _ in range(20)],
toroidal=True,
)
register_zone("overworld", other_zone)
player = _make_player("alice", zone=other_zone)
await cmd_describe(player, "trying to describe the overworld")
# Should show error message
assert player.writer.write.called
output = "".join(c[0][0] for c in player.writer.write.call_args_list)
assert "only" in output.lower() and "home" in output.lower()
@pytest.mark.asyncio
async def test_describe_no_args_shows_current(_init_housing):
"""describe with no args shows current description."""
home = _make_home_zone("alice")
home.description = "a warm and welcoming place"
player = _make_player("alice", zone=home)
await cmd_describe(player, "")
assert player.writer.write.called
output = "".join(c[0][0] for c in player.writer.write.call_args_list)
assert "a warm and welcoming place" in output
@pytest.mark.asyncio
async def test_describe_saves_zone(_init_housing):
"""describe saves the zone to disk."""
import tomllib
from mudlib.housing import _zone_path
home = _make_home_zone("alice")
player = _make_player("alice", zone=home)
await cmd_describe(player, "a newly described home")
# Check TOML file was saved
zone_path = _zone_path("alice")
assert zone_path.exists()
with open(zone_path, "rb") as f:
data = tomllib.load(f)
assert data["description"] == "a newly described home"
@pytest.mark.asyncio
async def test_describe_multiword(_init_housing):
"""describe handles multiword descriptions."""
home = _make_home_zone("bob")
player = _make_player("bob", zone=home)
await cmd_describe(player, "a cozy cottage with warm lighting")
assert home.description == "a cozy cottage with warm lighting"
assert player.writer.write.called
output = "".join(c[0][0] for c in player.writer.write.call_args_list)
assert "description" in output.lower() or "set" in output.lower()