mud/src/mudlib/commands/play.py
Jared Miller 57afe9a3ce
Wire restore into play command
When starting an IF game, check for existing save file and restore
if present. Shows 'restoring saved game...' message and broadcasts
restored game state to spectators.

Also cleaned up redundant tests that didn't properly mock the
auto-save functionality now present in ::quit and stop().
2026-02-09 16:39:15 -05:00

101 lines
3.2 KiB
Python

"""Play interactive fiction games."""
import pathlib
from mudlib.commands import CommandDefinition, register
from mudlib.if_session import IFSession, broadcast_to_spectators
from mudlib.player import Player
# Story files directory
_stories_dir = pathlib.Path(__file__).resolve().parents[3] / "content" / "stories"
# Map of game name -> file extension for lookup
_STORY_EXTENSIONS = (".z3", ".z5", ".z8", ".zblorb")
def _find_story(name: str) -> pathlib.Path | None:
"""Find a story file by name in content/stories/."""
# exact match first
for ext in _STORY_EXTENSIONS:
path = _stories_dir / f"{name}{ext}"
if path.exists():
return path
# prefix match (e.g. "zork" matches "zork1.z3")
if _stories_dir.exists():
for path in sorted(_stories_dir.iterdir()):
if path.stem.startswith(name) and path.suffix in _STORY_EXTENSIONS:
return path
return None
def _list_stories() -> list[str]:
"""Return available story names."""
if not _stories_dir.exists():
return []
return sorted(
p.stem for p in _stories_dir.iterdir() if p.suffix in _STORY_EXTENSIONS
)
async def cmd_play(player: Player, args: str) -> None:
"""Start playing an interactive fiction game."""
game_name = args.strip().lower()
if not game_name:
stories = _list_stories()
if stories:
names = ", ".join(stories)
await player.send(f"play what? available: {names}\r\n")
else:
await player.send("play what? (no stories installed)\r\n")
return
story_path = _find_story(game_name)
if not story_path:
stories = _list_stories()
if stories:
names = ", ".join(stories)
msg = f"no story '{game_name}'. available: {names}\r\n"
else:
msg = f"no story found for '{game_name}'.\r\n"
await player.send(msg)
return
# Create and start IF session
session = IFSession(player, str(story_path), game_name)
try:
intro = await session.start()
except FileNotFoundError:
await session.stop()
await player.send("error: dfrotz not found. cannot play IF games.\r\n")
return
except OSError as e:
await session.stop()
await player.send(f"error starting game: {e}\r\n")
return
player.if_session = session
player.mode_stack.append("if")
await player.send("(type ::help for escape commands)\r\n")
# Check for saved game
if session.save_path.exists():
await player.send("restoring saved game...\r\n")
restored_text = await session._do_restore()
if restored_text:
await player.send(restored_text + "\r\n")
# Broadcast restored text to spectators
spectator_msg = f"[{player.name}'s terminal]\r\n{restored_text}\r\n"
await broadcast_to_spectators(player, spectator_msg)
elif intro:
await player.send(intro + "\r\n")
# Broadcast intro to spectators
spectator_msg = f"[{player.name}'s terminal]\r\n{intro}\r\n"
await broadcast_to_spectators(player, spectator_msg)
register(
CommandDefinition(
"play", cmd_play, mode="normal", help="play an interactive fiction game"
)
)