mud/scripts/import_map.py
Jared Miller da76b6004e
Add YAML map import script with tests
Implements import_map.py script that converts YAML zone definitions to
TOML format used by the engine. YAML format supports all zone features
including terrain, portals, spawns, ambient messages, and boundaries.
2026-02-14 12:39:48 -05:00

171 lines
4.9 KiB
Python
Executable file

#!/usr/bin/env python3
"""Import YAML zone definitions and convert to TOML format.
Usage:
python scripts/import_map.py path/to/zone.yaml
python scripts/import_map.py path/to/zone.yaml --output content/zones/
"""
from __future__ import annotations
import argparse
from pathlib import Path
import yaml
def import_map(yaml_path: Path, output_dir: Path) -> Path:
"""Import a YAML zone definition and convert to TOML.
Args:
yaml_path: Path to YAML file
output_dir: Directory where TOML file should be written
Returns:
Path to created TOML file
Raises:
KeyError: If required fields are missing
"""
# Load YAML
with open(yaml_path) as f:
data = yaml.safe_load(f)
# Required fields
name = data["name"]
width = data["width"]
height = data["height"]
# Optional fields with defaults
description = data.get("description", "")
toroidal = data.get("toroidal", True)
safe = data.get("safe", False)
# Handle spawn coordinates
spawn = data.get("spawn", [0, 0])
spawn_x = spawn[0]
spawn_y = spawn[1]
# Build TOML lines
lines = []
# Basic fields
escaped_name = name.replace('"', '\\"')
lines.append(f'name = "{escaped_name}"')
if description:
escaped_description = description.replace('"', '\\"')
lines.append(f'description = "{escaped_description}"')
lines.append(f"width = {width}")
lines.append(f"height = {height}")
lines.append(f"toroidal = {str(toroidal).lower()}")
lines.append(f"spawn_x = {spawn_x}")
lines.append(f"spawn_y = {spawn_y}")
if safe:
lines.append("safe = true")
lines.append("")
# Terrain section
terrain_data = data.get("terrain", {})
rows = terrain_data.get("rows", [])
lines.append("[terrain]")
lines.append("rows = [")
for row in rows:
lines.append(f' "{row}",')
lines.append("]")
lines.append("")
# Impassable tiles
lines.append("[terrain.impassable]")
impassable = terrain_data.get("impassable", [])
if impassable:
tiles_str = ", ".join(f'"{tile}"' for tile in impassable)
lines.append(f"tiles = [{tiles_str}]")
else:
lines.append('tiles = ["^", "~"]') # default from zones.py
lines.append("")
# Ambient messages
ambient_data = data.get("ambient", {})
if ambient_data:
messages = ambient_data.get("messages", [])
interval = ambient_data.get("interval", 120)
lines.append("[ambient]")
lines.append(f"interval = {interval}")
lines.append("messages = [")
for msg in messages:
# Escape quotes in messages
escaped_msg = msg.replace('"', '\\"')
lines.append(f' "{escaped_msg}",')
lines.append("]")
lines.append("")
# Portals
portals = data.get("portals", [])
for portal in portals:
lines.append("[[portals]]")
lines.append(f"x = {portal['x']}")
lines.append(f"y = {portal['y']}")
escaped_target = portal["target"].replace('"', '\\"')
lines.append(f'target = "{escaped_target}"')
escaped_label = portal["label"].replace('"', '\\"')
lines.append(f'label = "{escaped_label}"')
if "aliases" in portal:
aliases_str = ", ".join(f'"{alias}"' for alias in portal["aliases"])
lines.append(f"aliases = [{aliases_str}]")
lines.append("")
# Spawn rules
spawns = data.get("spawns", [])
for spawn in spawns:
lines.append("[[spawns]]")
lines.append(f'mob = "{spawn["mob"]}"')
lines.append(f"max_count = {spawn.get('max_count', 1)}")
lines.append(f"respawn_seconds = {spawn.get('respawn_seconds', 300)}")
if "home_region" in spawn:
hr = spawn["home_region"]
lines.append(
f"home_region = {{ x = [{hr['x'][0]}, {hr['x'][1]}], "
f"y = [{hr['y'][0]}, {hr['y'][1]}] }}"
)
lines.append("")
# Write TOML file
toml_content = "\n".join(lines)
output_path = output_dir / f"{name}.toml"
output_path.write_text(toml_content)
return output_path
def main() -> None:
"""CLI entry point."""
parser = argparse.ArgumentParser(
description="Import YAML zone definition and convert to TOML"
)
parser.add_argument("yaml_file", type=Path, help="Path to YAML zone file")
parser.add_argument(
"--output",
type=Path,
default=Path("content/zones"),
help="Output directory (default: content/zones)",
)
args = parser.parse_args()
yaml_path = args.yaml_file
output_dir = args.output
if not yaml_path.exists():
print(f"Error: {yaml_path} does not exist")
return
output_dir.mkdir(parents=True, exist_ok=True)
output_file = import_map(yaml_path, output_dir)
print(f"Imported {yaml_path} -> {output_file}")
if __name__ == "__main__":
main()