mud/docs/how/loot-and-corpses.rst

104 lines
3.1 KiB
ReStructuredText

=======================
loot and corpse system
=======================
the death system has three phases: knockout, finisher, and decomposition.
corpses are containers that rot over time. loot is probabilistic.
loot tables
===========
loot is defined per-mob in toml with ``LootEntry`` records::
[[loot]]
name = "crude club"
chance = 0.8
description = "a crude wooden club"
[[loot]]
name = "gold coin"
chance = 0.5
min_count = 1
max_count = 3
fields:
- ``name`` - item name
- ``chance`` - probability (0.0-1.0) to drop
- ``min_count`` / ``max_count`` - quantity range (defaults to 1)
- ``description`` - item description string
``roll_loot()`` processes the table entry by entry. for each entry, it rolls
random against chance. if successful, picks a count in the min/max range and
creates that many ``Thing`` objects. returns a flat list of items.
death flow
==========
knockout (automatic)
--------------------
when stamina or pl drops to/below zero, the entity's ``posture`` becomes
``"unconscious"``. the mob stays registered in ``mobs`` dict. no corpse is
created yet. items stay in the mob's inventory.
finisher (explicit)
-------------------
requires a command (e.g. ``snapneck``) targeting an unconscious entity. sets
``pl`` to -100 (dead). loads mob template to get loot table. calls
``create_corpse()`` with the mob, zone, and loot table.
no corpse until finisher — knockout alone doesn't drop loot.
corpse creation
===============
``create_corpse(mob, zone, ttl=300, loot_table=None)``
1. creates ``Corpse`` object (extends ``Container``) at mob's coords
2. names it ``"{mob.name}'s corpse"``
3. transfers all items from mob inventory to corpse
4. rolls loot table, adds generated items to corpse
5. calls ``despawn_mob()`` to remove mob from world
6. registers corpse in global ``active_corpses`` list
7. returns the corpse
corpse properties:
- ``closed=False`` - always open
- ``portable=False`` - can't be picked up
- ``decompose_at`` - timestamp (now + ttl seconds)
corpses work with existing item commands (``get``, ``put``, ``look in``) because
they're containers.
decomposition
=============
``process_decomposing()`` runs every game loop tick. checks each corpse in
``active_corpses`` against current time. when ``decompose_at`` passes:
1. broadcasts "X decomposes." to entities on same tile
2. clears all items (items rot with corpse — no loot recovery)
3. removes corpse from world
4. removes from ``active_corpses`` registry
ttl-based cleanup ensures corpses don't clutter the world. default 300s (5min).
code
====
implementation:
- ``src/mudlib/loot.py`` - loot table dataclass and roll logic
- ``src/mudlib/corpse.py`` - Corpse container, creation, decomposition
- ``src/mudlib/commands/snapneck.py`` - finisher command
- ``content/mobs/*.toml`` - per-mob loot tables
related systems:
- ``src/mudlib/combat.py`` - knockout logic (posture="unconscious")
- ``src/mudlib/entity.py`` - Entity.posture, inventory
- ``src/mudlib/item.py`` - Thing, Container base classes
- ``src/mudlib/server.py`` - calls process_decomposing() each tick