pipeworks_mud_mapper.models.zone ================================ .. py:module:: pipeworks_mud_mapper.models.zone .. autoapi-nested-parse:: Zone model for game truth representation. This module defines the Zone model - the "game truth" format that the MUD server consumes. Zone files contain everything the game engine needs to run: rooms, exits, items, and metadata. They explicitly exclude coordinates because the engine operates on topology (connections) not geometry (positions). The Two-File Philosophy ----------------------- As described in ``goblin_cartography.md`` Section 2.3, there's a deliberate separation between: 1. **Zone files** (game truth): What the server needs. Pure topology. 2. **Map files** (authoring source): What humans need. Includes coordinates. Zone files are "build artifacts" - they're generated from map files by stripping coordinates. This separation ensures: - The server never sees or depends on visual layout - Authors can freely rearrange map layouts without affecting gameplay - Zone files remain clean and focused on game logic Zone Structure -------------- A zone is a self-contained region of the MUD world:: { "id": "crooked_pipe", "name": "Crooked Pipe District", "description": "A warren of goblin pubs...", "spawn_room": "spawn", "rooms": { "spawn": { ... }, "front_parlour": { ... } }, "items": { "ale_mug": { ... } } } Cross-Zone Exits ---------------- Exits can reference rooms in other zones using the format ``zone:room``:: "exits": { "north": "front_parlour", # Same zone "west": "cobbled_street:market" # Cross-zone } The engine parses the ``:`` delimiter and loads the target zone if needed. .. admonition:: Examples Creating a zone programmatically:: zone = Zone( id="tutorial", name="Tutorial Area", spawn_room="spawn", rooms={ "spawn": Room(id="spawn", name="Arrival Chamber", exits={"north": "hall"}), "hall": Room(id="hall", name="Main Hall", exits={"south": "spawn"}), }, ) Serializing to JSON:: json_str = zone.model_dump_json(indent=2, exclude_none=True) .. seealso:: :py:obj:`MapFile` Authoring format that includes coordinates. :py:obj:`Room` Individual room within a zone. Classes ------- .. autoapisummary:: pipeworks_mud_mapper.models.zone.Zone Module Contents --------------- .. py:class:: Zone(/, **data) Bases: :py:obj:`pydantic.BaseModel` A zone in game truth format (no coordinates). Zones are self-contained regions of the MUD world. Each zone has its own spawn point, rooms, and items. Zones connect to each other through cross-zone exits. This model represents the format consumed by the MUD server. It excludes coordinates because the game engine operates on topology, not geometry. .. attribute:: id Unique zone identifier. Used in cross-zone references and as filename. Should be lowercase with underscores (e.g., "crooked_pipe"). :type: :py:class:`str` .. attribute:: name Human-readable display name (e.g., "Crooked Pipe District"). :type: :py:class:`str` .. attribute:: metadata Versioning metadata that accompanies exported zone files. :type: :py:class:`ZoneMetadata` .. attribute:: description Optional zone description for authoring reference. :type: :py:class:`str` .. attribute:: spawn_room Room ID where players enter this zone. Must exist in rooms dict. :type: :py:class:`str` .. attribute:: rooms Mapping of room ID to Room objects. :type: :py:class:`dict[str`, :py:class:`Room]` .. attribute:: items Mapping of item ID to item definitions. Item structure is flexible to support future item system expansion. :type: :py:class:`dict[str`, :py:class:`dict]` .. admonition:: Examples >>> zone = Zone( ... id="tutorial", ... name="Tutorial Area", ... spawn_room="spawn", ... rooms={"spawn": Room(id="spawn", name="Start")}, ... ) Validation ensures spawn_room exists:: >>> Zone( ... id="bad", ... name="Bad Zone", ... spawn_room="nonexistent", ... rooms={}, ... ) Traceback (most recent call last): ... pydantic_core._pydantic_core.ValidationError: ... .. seealso:: :py:obj:`MapFile` Authoring format with coordinates. :py:obj:`Room` Individual room model. .. py:method:: validate_id(v) :classmethod: Validate zone ID format. Zone IDs must start with a letter and contain only lowercase letters, numbers, and underscores. .. py:method:: validate_spawn_room_exists() Ensure spawn_room references an existing room. :returns: The validated zone instance. :rtype: :py:class:`Zone` :raises ValueError: If spawn_room is not in the rooms dictionary. .. py:method:: validate_room_ids_match_keys() Ensure room IDs match their dictionary keys. :returns: The validated zone instance. :rtype: :py:class:`Zone` :raises ValueError: If any room's ID doesn't match its dictionary key. .. py:method:: get_room(room_id) Get a room by ID. :param room_id: The room ID to look up. :type room_id: :py:class:`str` :returns: The room if found, None otherwise. :rtype: :py:class:`Room` or :py:obj:`None` .. admonition:: Examples >>> zone.get_room("spawn") Room(id='spawn', ...) >>> zone.get_room("nonexistent") None .. py:method:: validate_exits() Validate all exit targets exist (within this zone). Cross-zone exits (containing `:`) are skipped as they reference other zones that may not be loaded. :returns: List of warning messages for invalid exit targets. :rtype: :py:class:`list[str]` .. admonition:: Examples >>> zone.validate_exits() ["Room 'spawn' has exit 'north' to nonexistent room 'bad_room'"] .. py:method:: find_unreachable_rooms() Find rooms that cannot be reached from the spawn room. Uses breadth-first search from spawn_room to find all reachable rooms, then returns any rooms not in that set. :returns: List of room IDs that are unreachable from spawn. :rtype: :py:class:`list[str]` .. admonition:: Examples >>> zone.find_unreachable_rooms() ['isolated_room', 'another_orphan'] .. py:method:: find_dead_ends() Find rooms with no outgoing exits. Note: Dead ends are not always errors - some rooms are intentionally terminal (treasure rooms, trap rooms, etc.). :returns: List of room IDs with no exits. :rtype: :py:class:`list[str]` .. admonition:: Examples >>> zone.find_dead_ends() ['treasure_room', 'pit_of_doom']