mud/src/mudlib/verbs.py
Jared Miller fcfa13c785
Add verb infrastructure on Object
Verbs let any Object have interactive handlers players can trigger.
Uses @verb decorator to mark methods that auto-register on instantiation.

- Object._verbs dict stores verb name to async handler mapping
- Object.register_verb(), get_verb(), has_verb() API
- @verb decorator marks methods with _verb_name attribute
- __post_init__ scans for decorated methods and registers them
- find_object() helper searches inventory then ground by name/alias
- Bound methods stored in _verbs (self already bound)
- Works on Object and all subclasses (Thing, Entity, etc)
- 18 tests covering registration, lookup, decoration, inheritance
2026-02-11 21:47:33 -05:00

62 lines
1.8 KiB
Python

"""Verb system for interactive objects."""
from __future__ import annotations
from collections.abc import Callable
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mudlib.object import Object
from mudlib.player import Player
def verb(name: str) -> Callable:
"""Decorator to mark a method as a verb handler.
Usage:
class Fountain(Thing):
@verb("drink")
async def drink(self, player, args):
await player.send("You drink from the fountain.\\r\\n")
"""
def decorator(func: Callable) -> Callable:
func._verb_name = name # type: ignore
return func
return decorator
def find_object(name: str, player: Player) -> Object | None:
"""Find an object by name or alias.
Searches inventory first, then ground at player position.
Works on all Objects that have a name and optional aliases attribute.
"""
name_lower = name.lower()
# Search inventory first
for obj in player.contents:
if obj.name.lower() == name_lower:
return obj
# Check aliases if the object has them
aliases = getattr(obj, "aliases", None)
if aliases and name_lower in (a.lower() for a in aliases):
return obj
# Search ground at player position
if player.location is not None:
from mudlib.zone import Zone
if isinstance(player.location, Zone):
for obj in player.location.contents_at(player.x, player.y):
if obj is player:
continue
if obj.name.lower() == name_lower:
return obj
# Check aliases if the object has them
aliases = getattr(obj, "aliases", None)
if aliases and name_lower in (a.lower() for a in aliases):
return obj
return None