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.
285 lines
6.5 KiB
Python
285 lines
6.5 KiB
Python
"""Tests for YAML map import script."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from mudlib.portal import Portal
|
|
from mudlib.zones import load_zone
|
|
|
|
|
|
def test_minimal_yaml_import(tmp_path: Path) -> None:
|
|
"""Test importing a minimal YAML map."""
|
|
yaml_content = """
|
|
name: test_zone
|
|
width: 5
|
|
height: 5
|
|
terrain:
|
|
rows:
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
"""
|
|
yaml_file = tmp_path / "test_zone.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
# Import the script module
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
|
|
# Verify TOML was created
|
|
assert output_file.exists()
|
|
assert output_file.name == "test_zone.toml"
|
|
|
|
# Verify it can be loaded by existing zone loader
|
|
zone = load_zone(output_file)
|
|
assert zone.name == "test_zone"
|
|
assert zone.width == 5
|
|
assert zone.height == 5
|
|
assert zone.description == ""
|
|
assert zone.toroidal is True # default
|
|
assert zone.spawn_x == 0 # default
|
|
assert zone.spawn_y == 0 # default
|
|
assert zone.safe is False # default
|
|
assert len(zone.terrain) == 5
|
|
assert all(len(row) == 5 for row in zone.terrain)
|
|
|
|
|
|
def test_full_featured_yaml_import(tmp_path: Path) -> None:
|
|
"""Test importing a YAML map with all features."""
|
|
yaml_content = """
|
|
name: complex_zone
|
|
description: a complex test zone
|
|
width: 10
|
|
height: 10
|
|
toroidal: false
|
|
spawn: [5, 5]
|
|
safe: true
|
|
|
|
terrain:
|
|
rows:
|
|
- "##########"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "#........#"
|
|
- "##########"
|
|
impassable: ["#"]
|
|
|
|
ambient:
|
|
interval: 60
|
|
messages:
|
|
- "wind howls..."
|
|
- "a distant sound"
|
|
|
|
portals:
|
|
- x: 5
|
|
y: 0
|
|
target: "other_zone:10,10"
|
|
label: "a wide path"
|
|
aliases: ["path", "wide"]
|
|
|
|
spawns:
|
|
- mob: goblin
|
|
max_count: 3
|
|
respawn_seconds: 300
|
|
home_region:
|
|
x: [1, 9]
|
|
y: [1, 9]
|
|
"""
|
|
yaml_file = tmp_path / "complex_zone.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
|
|
# Load and verify all fields
|
|
zone = load_zone(output_file)
|
|
assert zone.name == "complex_zone"
|
|
assert zone.description == "a complex test zone"
|
|
assert zone.width == 10
|
|
assert zone.height == 10
|
|
assert zone.toroidal is False
|
|
assert zone.spawn_x == 5
|
|
assert zone.spawn_y == 5
|
|
assert zone.safe is True
|
|
|
|
# Verify terrain
|
|
assert len(zone.terrain) == 10
|
|
assert zone.terrain[0] == list("##########")
|
|
assert zone.terrain[1] == list("#........#")
|
|
|
|
# Verify impassable
|
|
assert "#" in zone.impassable
|
|
|
|
# Verify ambient messages
|
|
assert zone.ambient_messages == ["wind howls...", "a distant sound"]
|
|
assert zone.ambient_interval == 60
|
|
|
|
# Verify portals
|
|
portals = [obj for obj in zone._contents if isinstance(obj, Portal)]
|
|
assert len(portals) == 1
|
|
portal = portals[0]
|
|
assert portal.x == 5
|
|
assert portal.y == 0
|
|
assert portal.target_zone == "other_zone"
|
|
assert portal.target_x == 10
|
|
assert portal.target_y == 10
|
|
assert portal.name == "a wide path"
|
|
assert portal.aliases == ["path", "wide"]
|
|
|
|
# Verify spawn rules
|
|
assert len(zone.spawn_rules) == 1
|
|
spawn = zone.spawn_rules[0]
|
|
assert spawn.mob == "goblin"
|
|
assert spawn.max_count == 3
|
|
assert spawn.respawn_seconds == 300
|
|
assert spawn.home_region == {"x": [1, 9], "y": [1, 9]}
|
|
|
|
|
|
def test_missing_required_field_raises_error(tmp_path: Path) -> None:
|
|
"""Test that missing required fields raise appropriate errors."""
|
|
yaml_content = """
|
|
width: 5
|
|
height: 5
|
|
"""
|
|
yaml_file = tmp_path / "incomplete.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
with pytest.raises(KeyError, match="name"):
|
|
import_map(yaml_file, tmp_path)
|
|
|
|
|
|
def test_spawn_list_format(tmp_path: Path) -> None:
|
|
"""Test that spawn coordinates as [x, y] list are parsed correctly."""
|
|
yaml_content = """
|
|
name: spawn_test
|
|
width: 3
|
|
height: 3
|
|
spawn: [1, 2]
|
|
terrain:
|
|
rows:
|
|
- "..."
|
|
- "..."
|
|
- "..."
|
|
"""
|
|
yaml_file = tmp_path / "spawn_test.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
zone = load_zone(output_file)
|
|
assert zone.spawn_x == 1
|
|
assert zone.spawn_y == 2
|
|
|
|
|
|
def test_defaults_applied(tmp_path: Path) -> None:
|
|
"""Test that sensible defaults are applied for optional fields."""
|
|
yaml_content = """
|
|
name: defaults_test
|
|
width: 3
|
|
height: 3
|
|
terrain:
|
|
rows:
|
|
- "..."
|
|
- "..."
|
|
- "..."
|
|
"""
|
|
yaml_file = tmp_path / "defaults_test.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
zone = load_zone(output_file)
|
|
|
|
# Verify defaults
|
|
assert zone.toroidal is True
|
|
assert zone.spawn_x == 0
|
|
assert zone.spawn_y == 0
|
|
assert zone.safe is False
|
|
assert zone.ambient_messages == []
|
|
assert zone.spawn_rules == []
|
|
|
|
|
|
def test_multiple_portals(tmp_path: Path) -> None:
|
|
"""Test importing multiple portals."""
|
|
yaml_content = """
|
|
name: multi_portal
|
|
width: 5
|
|
height: 5
|
|
terrain:
|
|
rows:
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
portals:
|
|
- x: 0
|
|
y: 0
|
|
target: "zone1:5,5"
|
|
label: "north path"
|
|
- x: 4
|
|
y: 4
|
|
target: "zone2:0,0"
|
|
label: "south path"
|
|
"""
|
|
yaml_file = tmp_path / "multi_portal.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
zone = load_zone(output_file)
|
|
|
|
portals = [obj for obj in zone._contents if isinstance(obj, Portal)]
|
|
assert len(portals) == 2
|
|
|
|
|
|
def test_multiple_spawns(tmp_path: Path) -> None:
|
|
"""Test importing multiple spawn rules."""
|
|
yaml_content = """
|
|
name: multi_spawn
|
|
width: 5
|
|
height: 5
|
|
terrain:
|
|
rows:
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
- "....."
|
|
spawns:
|
|
- mob: goblin
|
|
max_count: 2
|
|
- mob: orc
|
|
max_count: 1
|
|
respawn_seconds: 600
|
|
"""
|
|
yaml_file = tmp_path / "multi_spawn.yaml"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
from scripts.import_map import import_map
|
|
|
|
output_file = import_map(yaml_file, tmp_path)
|
|
zone = load_zone(output_file)
|
|
|
|
assert len(zone.spawn_rules) == 2
|
|
assert zone.spawn_rules[0].mob == "goblin"
|
|
assert zone.spawn_rules[0].max_count == 2
|
|
assert zone.spawn_rules[1].mob == "orc"
|
|
assert zone.spawn_rules[1].respawn_seconds == 600
|