================ 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