93 lines
2.7 KiB
Python
93 lines
2.7 KiB
Python
"""Corpse — a container left behind when an entity dies."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from dataclasses import dataclass
|
|
|
|
from mudlib.container import Container
|
|
from mudlib.entity import Mob
|
|
from mudlib.loot import LootEntry, roll_loot
|
|
from mudlib.mobs import despawn_mob
|
|
from mudlib.zone import Zone
|
|
|
|
# Module-level registry of active corpses
|
|
active_corpses: list[Corpse] = []
|
|
|
|
|
|
@dataclass(eq=False)
|
|
class Corpse(Container):
|
|
"""A corpse left behind when an entity dies.
|
|
|
|
Corpses are containers that hold the deceased entity's inventory.
|
|
They are not portable (can't be picked up) and are always open.
|
|
They have a decompose_at timestamp for eventual cleanup.
|
|
"""
|
|
|
|
portable: bool = False
|
|
closed: bool = False
|
|
decompose_at: float = 0.0
|
|
|
|
|
|
def create_corpse(
|
|
mob: Mob, zone: Zone, ttl: int = 300, loot_table: list[LootEntry] | None = None
|
|
) -> Corpse:
|
|
"""Create a corpse from a defeated mob.
|
|
|
|
Args:
|
|
mob: The mob that died
|
|
zone: The zone where the corpse will be placed
|
|
ttl: Time to live in seconds (default 300)
|
|
loot_table: Optional loot table to roll for additional items
|
|
|
|
Returns:
|
|
The created corpse with the mob's inventory and loot
|
|
"""
|
|
# Create corpse at mob's position
|
|
corpse = Corpse(
|
|
name=f"{mob.name}'s corpse",
|
|
location=zone,
|
|
x=mob.x,
|
|
y=mob.y,
|
|
decompose_at=time.monotonic() + ttl,
|
|
)
|
|
|
|
# Transfer mob's inventory to corpse
|
|
for item in list(mob._contents):
|
|
item.move_to(corpse)
|
|
|
|
# Roll and add loot
|
|
if loot_table:
|
|
for item in roll_loot(loot_table):
|
|
item.move_to(corpse)
|
|
|
|
# Remove mob from world
|
|
despawn_mob(mob)
|
|
|
|
# Register corpse for decomposition tracking
|
|
active_corpses.append(corpse)
|
|
|
|
return corpse
|
|
|
|
|
|
async def process_decomposing() -> None:
|
|
"""Remove expired corpses and broadcast decomposition messages."""
|
|
now = time.monotonic()
|
|
|
|
for corpse in active_corpses[:]: # copy to allow modification
|
|
if now >= corpse.decompose_at:
|
|
# Broadcast to entities at the same tile
|
|
zone = corpse.location
|
|
if isinstance(zone, Zone) and corpse.x is not None and corpse.y is not None:
|
|
from mudlib.entity import Entity
|
|
|
|
for obj in zone.contents_at(corpse.x, corpse.y):
|
|
if isinstance(obj, Entity):
|
|
await obj.send(f"{corpse.name} decomposes.\r\n")
|
|
|
|
# Clear contents — items rot with the corpse
|
|
for item in list(corpse._contents):
|
|
item.move_to(None)
|
|
# Remove corpse from world
|
|
corpse.move_to(None)
|
|
active_corpses.remove(corpse)
|