mud/src/mudlib/entity.py

82 lines
2.5 KiB
Python

"""Base entity class for characters in the world."""
from __future__ import annotations
from dataclasses import dataclass, field
from mudlib.object import Object
@dataclass(eq=False)
class Entity(Object):
"""Base class for anything with position and identity in the world.
Inherits name, location from Object. Narrows x, y to int (entities
always have spatial coordinates).
"""
# Entities always have spatial coordinates (override Object's int | None)
x: int = 0
y: int = 0
# Combat stats
pl: float = 100.0 # power level (health and damage multiplier)
max_pl: float = 100.0 # maximum power level
stamina: float = 100.0 # current stamina
max_stamina: float = 100.0 # stamina ceiling
defense_locked_until: float = 0.0 # monotonic time when defense recovery ends
resting: bool = False # whether this entity is currently resting
sleeping: bool = False # whether this entity is currently sleeping (deep rest)
_last_stamina_cue: float = 1.0 # Last stamina percentage that triggered a cue
@property
def posture(self) -> str:
"""Return entity's current posture for room display.
Priority order: unconscious > fighting > flying > sleeping > resting > standing
"""
# Unconscious (highest priority)
if self.pl <= 0 or self.stamina <= 0:
return "unconscious"
# Fighting (only for Player with mode="combat")
if hasattr(self, "mode") and self.mode == "combat":
return "fighting"
# Flying (only for Player)
if getattr(self, "flying", False):
return "flying"
# Sleeping (before resting since sleeping implies resting)
if self.sleeping:
return "sleeping"
# Resting
if self.resting:
return "resting"
# Default
return "standing"
def can_accept(self, obj: Object) -> bool:
"""Entities accept portable Things (inventory)."""
from mudlib.thing import Thing
return isinstance(obj, Thing) and obj.portable
async def send(self, message: str) -> None:
"""Send a message to this entity. Base implementation is a no-op."""
pass
@dataclass(eq=False)
class Mob(Entity):
"""Represents a non-player character (NPC) in the world."""
description: str = ""
alive: bool = True
moves: list[str] = field(default_factory=list)
next_action_at: float = 0.0
home_x_min: int | None = None
home_x_max: int | None = None
home_y_min: int | None = None
home_y_max: int | None = None