158 lines
4.1 KiB
Python
158 lines
4.1 KiB
Python
"""Zone registry and loading."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
from mudlib.portal import Portal
|
|
from mudlib.zone import SpawnRule, Zone
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# Module-level zone registry
|
|
zone_registry: dict[str, Zone] = {}
|
|
|
|
|
|
def register_zone(name: str, zone: Zone) -> None:
|
|
"""Register a zone by name.
|
|
|
|
Args:
|
|
name: Unique name for the zone
|
|
zone: Zone instance to register
|
|
"""
|
|
zone_registry[name] = zone
|
|
|
|
|
|
def get_zone(name: str) -> Zone | None:
|
|
"""Look up a zone by name.
|
|
|
|
Args:
|
|
name: Zone name to look up
|
|
|
|
Returns:
|
|
Zone instance if found, None otherwise
|
|
"""
|
|
return zone_registry.get(name)
|
|
|
|
|
|
def load_zone(path: Path) -> Zone:
|
|
"""Load a zone from a TOML file.
|
|
|
|
Args:
|
|
path: Path to TOML file
|
|
|
|
Returns:
|
|
Zone instance loaded from file
|
|
"""
|
|
with open(path, "rb") as f:
|
|
data = tomllib.load(f)
|
|
|
|
# Extract basic properties
|
|
name = data["name"]
|
|
description = data.get("description", "")
|
|
width = data["width"]
|
|
height = data["height"]
|
|
toroidal = data.get("toroidal", True)
|
|
spawn_x = data.get("spawn_x", 0)
|
|
spawn_y = data.get("spawn_y", 0)
|
|
|
|
# Parse terrain rows into 2D list
|
|
terrain_rows = data.get("terrain", {}).get("rows", [])
|
|
terrain = []
|
|
for row in terrain_rows:
|
|
terrain.append(list(row))
|
|
|
|
# Parse impassable tiles
|
|
impassable_list = data.get("terrain", {}).get("impassable", {}).get("tiles", [])
|
|
impassable = set(impassable_list) if impassable_list else {"^", "~"}
|
|
|
|
# Parse ambient messages
|
|
ambient_data = data.get("ambient", {})
|
|
ambient_messages = ambient_data.get("messages", [])
|
|
ambient_interval = ambient_data.get("interval", 120)
|
|
|
|
# Parse spawn rules
|
|
spawns_data = data.get("spawns", [])
|
|
spawn_rules = [
|
|
SpawnRule(
|
|
mob=spawn["mob"],
|
|
max_count=spawn.get("max_count", 1),
|
|
respawn_seconds=spawn.get("respawn_seconds", 300),
|
|
)
|
|
for spawn in spawns_data
|
|
]
|
|
|
|
zone = Zone(
|
|
name=name,
|
|
description=description,
|
|
width=width,
|
|
height=height,
|
|
toroidal=toroidal,
|
|
terrain=terrain,
|
|
impassable=impassable,
|
|
spawn_x=spawn_x,
|
|
spawn_y=spawn_y,
|
|
ambient_messages=ambient_messages,
|
|
ambient_interval=ambient_interval,
|
|
spawn_rules=spawn_rules,
|
|
)
|
|
|
|
# Load portals
|
|
portals_data = data.get("portals", [])
|
|
for portal_dict in portals_data:
|
|
# Parse target string "zone_name:x,y"
|
|
target = portal_dict["target"]
|
|
try:
|
|
target_zone, coords = target.split(":")
|
|
target_x, target_y = map(int, coords.split(","))
|
|
except ValueError:
|
|
log.warning(
|
|
"skipping portal '%s' at (%d, %d): malformed target '%s'",
|
|
portal_dict["label"],
|
|
portal_dict["x"],
|
|
portal_dict["y"],
|
|
target,
|
|
)
|
|
continue
|
|
|
|
# Create portal (automatically added to zone._contents via Object.__post_init__)
|
|
Portal(
|
|
name=portal_dict["label"],
|
|
aliases=portal_dict.get("aliases", []),
|
|
target_zone=target_zone,
|
|
target_x=target_x,
|
|
target_y=target_y,
|
|
location=zone,
|
|
x=portal_dict["x"],
|
|
y=portal_dict["y"],
|
|
)
|
|
|
|
return zone
|
|
|
|
|
|
def load_zones(directory: Path) -> dict[str, Zone]:
|
|
"""Load all zones from a directory.
|
|
|
|
Args:
|
|
directory: Path to directory containing zone TOML files
|
|
|
|
Returns:
|
|
Dict mapping zone names to Zone instances
|
|
"""
|
|
zones = {}
|
|
|
|
if not directory.exists():
|
|
log.warning("zones directory does not exist: %s", directory)
|
|
return zones
|
|
|
|
for toml_file in directory.glob("*.toml"):
|
|
try:
|
|
zone = load_zone(toml_file)
|
|
zones[zone.name] = zone
|
|
log.debug("loaded zone '%s' from %s", zone.name, toml_file)
|
|
except Exception as e:
|
|
log.error("failed to load zone from %s: %s", toml_file, e, exc_info=True)
|
|
|
|
return zones
|