"""Base class for everything in the world.""" from __future__ import annotations from collections.abc import Awaitable, Callable from dataclasses import dataclass, field @dataclass class Object: """Base class for everything in the world. Every object has a location (another Object, or None for top-level things like zones). The location pointer is the entire containment system — contents is just the reverse lookup. """ name: str location: Object | None = None x: int | None = None y: int | None = None _contents: list[Object] = field( default_factory=list, init=False, repr=False, compare=False ) _verbs: dict[str, Callable[..., Awaitable[None]]] = field( default_factory=dict, init=False, repr=False, compare=False ) def __post_init__(self) -> None: if self.location is not None: self.location._contents.append(self) # Auto-register verbs from @verb decorated methods for attr_name in dir(self): # Skip private/magic attributes if attr_name.startswith("_"): continue attr = getattr(self, attr_name) # Check if this is a verb-decorated method if hasattr(attr, "_verb_name"): self.register_verb(attr._verb_name, attr) @property def contents(self) -> list[Object]: """Everything whose location is this object.""" return list(self._contents) def move_to( self, destination: Object | None, *, x: int | None = None, y: int | None = None, ) -> None: """Move this object to a new location. Removes from old location's contents, updates the location pointer, and adds to new location's contents. Coordinates are set from the keyword arguments (cleared to None if not provided). """ # Remove from old location if self.location is not None and self in self.location._contents: self.location._contents.remove(self) # Update location and coordinates self.location = destination self.x = x self.y = y # Add to new location if destination is not None: destination._contents.append(self) def can_accept(self, obj: Object) -> bool: """Whether this object accepts obj as contents. Default: no.""" return False def register_verb(self, name: str, handler: Callable[..., Awaitable[None]]) -> None: """Register a verb handler on this object.""" self._verbs[name] = handler def get_verb(self, name: str) -> Callable[..., Awaitable[None]] | None: """Get a verb handler by name, or None if not found.""" return self._verbs.get(name) def has_verb(self, name: str) -> bool: """Check if this object has a verb registered.""" return name in self._verbs