================
npc/mob system
================

the npc/mob system handles non-player characters: enemies, allies, townsfolk. mobs can wander, patrol, converse, flee, and fight. they follow schedules and have behavior states that transition based on game events.

entity model
============

all characters (players and mobs) inherit from ``Entity`` in ``entity.py``. this gives them:

- ``pl`` (power level) - combat effectiveness
- ``stamina`` and ``max_stamina`` - resource for moves
- ``posture`` - computed @property with priority order: "unconscious", "fighting", "flying", "sleeping", "resting", "standing"
- ``x``, ``y``, ``zone`` - location in the world

the ``Mob`` subclass adds:

- ``description`` - what you see when you look at them
- ``alive`` - whether they're dead
- ``moves`` - list of combat move names they can use
- ``next_action_at`` - throttle for AI decisions
- ``home_x_min``, ``home_x_max``, ``home_y_min``, ``home_y_max`` - wander bounds
- ``behavior_state`` - current state: "idle", "patrol", "converse", "flee", "working"
- ``behavior_data`` - dict with state-specific data (waypoints, threat coords, etc)
- ``npc_name`` - key into dialogue tree registry
- ``schedule`` - ``NpcSchedule`` instance with hourly behavior changes

mob templates
=============

templates live in ``content/mobs/`` as TOML files. ``MobTemplate`` in ``mobs.py`` defines the schema::

    name = "goblin"
    description = "a snarling goblin with a crude club"
    pl = 50.0
    stamina = 40.0
    max_stamina = 40.0
    moves = ["punch left", "punch right", "sweep"]

    [[loot]]
    name = "crude club"
    chance = 0.8
    description = "a crude wooden club"

``spawn_mob(template, x, y, zone, home_region)`` creates a ``Mob`` from a template and registers it. ``despawn_mob(mob)`` marks it dead and removes it from the registry. ``get_nearby_mob(name, x, y, zone, radius)`` finds mobs by name within range (toroidal-aware).

behavior states
===============

the behavior system in ``npc_behavior.py`` is a state machine. ``process_behavior(mob)`` dispatches to handlers based on ``mob.behavior_state``:

- **idle** - default state, wanders toward home region center
- **patrol** - moves toward waypoints in ``behavior_data["waypoints"]``
- **converse** - locked in place, talking to a player
- **flee** - runs away from threat at ``behavior_data["flee_from"]``
- **working** - schedule-driven state (librarian at desk, blacksmith at forge)

transitions happen when:

- player starts conversation → "converse"
- schedule hour changes → state from active schedule entry
- conversation ends → restores ``previous_state`` from conversation data
- flee timeout expires → return to previous state

helper functions calculate movement direction:

- ``get_patrol_direction(mob)`` - toward next waypoint (toroidal)
- ``get_flee_direction(mob)`` - away from threat (toroidal)

schedules
=========

scheduled NPCs have ``NpcSchedule`` in ``npc_schedule.py``. a schedule is a list of ``ScheduleEntry``::

    [[schedule]]
    hour = 7
    state = "working"
    location = [10, 20, "town"]

    [[schedule]]
    hour = 21
    state = "idle"

``get_active_entry(hour)`` returns the most recent entry at or before the given hour. ``process_schedules(game_time)`` is called from the game loop when the hour changes. it:

- finds all mobs with schedules
- skips dead mobs and mobs in conversation
- checks if the active entry changed
- teleports mob to new location if specified
- transitions to new state
- clears behavior_data if state changed

mob ai
======

combat ai
---------

``process_mobs()`` in ``mob_ai.py`` runs every tick (10/sec). for mobs in combat:

- **defender with incoming attack** - 40% chance of correct counter, 60% random affordable defense
- **attacker outside defend window** - random affordable attack

the ai respects throttles (``next_action_at``) and stamina costs. it uses ``get_affordable_moves()`` to filter by stamina.

movement ai
-----------

``process_mob_movement()`` runs every tick with a 3-second cooldown per mob. behavior-based movement:

- **patrol** - toward next waypoint, cycles through list
- **flee** - away from threat coordinates
- **idle** - toward home region center
- **working** - no movement

movement checks passability via ``world.is_passable(x, y, zone)``. broadcasts departure/arrival to nearby players. uses toroidal distance for direction calculation.

dialogue and conversation
=========================

``DialogueTree`` in ``dialogue.py`` has ``DialogueNode`` entries::

    [dialogue.librarian.greeting]
    text = "welcome to the library"

    [[dialogue.librarian.greeting.choices]]
    text = "what books do you have?"
    next_node = "books"

``DialogueChoice`` has optional ``condition`` (python expression) and ``action`` (function name).

``ConversationState`` in ``conversation.py`` tracks:

- ``tree`` - the dialogue tree
- ``current_node`` - id of current node
- ``npc`` - the mob being talked to
- ``previous_state`` - mob's state before conversation

``start_conversation(player, npc)`` transitions the mob to "converse" state. ``end_conversation(player)`` restores the mob's previous state. active conversations are stored in a dict keyed by player name.

commands
========

talk and reply
--------------

``cmd_talk`` in ``commands/talk.py``:

- finds nearby NPC by name
- starts conversation
- shows greeting text and choices

``cmd_reply``:

- takes choice number as argument
- advances to next node
- ends conversation if no next node

spawn
-----

``cmd_spawn`` in ``commands/spawn.py``:

- takes template name
- creates mob at player location
- sets home region to 5-tile radius

game loop integration
=====================

the ``game_loop()`` function in ``server.py`` calls:

- ``process_mobs()`` - every tick (10/sec)
- ``process_mob_movement()`` - every tick (10/sec)
- ``process_schedules(game_time)`` - when hour changes

this keeps mob AI responsive while preventing spam. the throttles (``next_action_at`` for combat, 3s cooldown for movement) ensure mobs don't act too frequently.

code
====

- ``src/mudlib/entity.py`` - Entity and Mob classes
- ``src/mudlib/mobs.py`` - MobTemplate, spawn/despawn registry
- ``src/mudlib/npc_behavior.py`` - behavior state machine
- ``src/mudlib/npc_schedule.py`` - NpcSchedule and ScheduleEntry
- ``src/mudlib/mob_ai.py`` - combat and movement AI
- ``src/mudlib/dialogue.py`` - DialogueTree, DialogueNode, DialogueChoice
- ``src/mudlib/conversation.py`` - ConversationState, start/end conversation
- ``src/mudlib/commands/talk.py`` - talk and reply commands
- ``src/mudlib/commands/spawn.py`` - spawn command
- ``content/mobs/`` - mob template TOML files
- ``content/dialogue/`` - dialogue tree TOML files
