Implements a global examine/ex command that shows detailed descriptions of objects. Searches inventory first, then ground at player position. Works with Things, Containers, and Mobs.
89 lines
2.9 KiB
Python
89 lines
2.9 KiB
Python
"""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
|