"""Play interactive fiction games.""" import pathlib from mudlib.commands import CommandDefinition, register from mudlib.embedded_if_session import EmbeddedIFSession 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 # Ensure story_path is a Path object (for mocking compatibility) if not isinstance(story_path, pathlib.Path): story_path = pathlib.Path(story_path) # Use embedded interpreter for z3 files, dfrotz for others if story_path.suffix == ".z3": try: session = EmbeddedIFSession(player, str(story_path), game_name) except (FileNotFoundError, OSError) as e: await player.send(f"error starting game: {e}\r\n") return else: 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 (both session types now support _do_restore) if hasattr(session, "_do_restore") and 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" ) )