Create librarian mob template as a non-combatant NPC with:
- dialogue tree linking (npc_name field)
- time-based schedule (working 7-21, idle otherwise)
- empty moves list (cannot fight)
Wire dialogue tree loading into server startup to load from content/dialogue/.
Add npc_name field to MobTemplate and spawn_mob to preserve dialogue tree links.
Integration tests verify:
- spawning from template preserves npc_name and schedule
- full conversation flow (start, advance, end)
- converse state blocks movement
- schedule transitions change behavior state
- working state blocks movement
- patrol behavior follows waypoints
Mob movement now respects NPC behavior states:
- converse and working states block movement (NPCs stay put)
- patrol state uses waypoint navigation instead of home region
- flee state moves away from threat coordinates
- idle state uses original home region wander logic
Tests verify each behavior state influences movement correctly.
Implements time-based behavior transitions for NPCs:
- GameTime converts real time to game time (1 real min = 1 game hour)
- ScheduleEntry defines hour/state/location/data transitions
- NpcSchedule manages multiple entries with midnight wrapping
- process_schedules() applies transitions when game hour changes
- TOML support for schedule data in mob templates
- Integrated into game loop with hourly checks
Tests cover schedule transitions, game time calculation, TOML loading, and preventing duplicate processing.
Implements player-NPC dialogue using the dialogue tree data model.
Conversation state tracking manages active conversations and transitions
NPCs to "converse" behavior state during dialogue. Commands support
terminal node cleanup and display formatting with numbered choices.
Implements a TOML-based dialogue tree system for NPCs with:
- DialogueChoice: player response options with optional conditions
- DialogueNode: NPC text with choices and optional actions
- DialogueTree: complete tree with root node and node graph
- Validation for root_node and next_node references
- load_dialogue() for single files, load_all_dialogues() for directories
Includes librarian dialogue example with nested conversation flow.
Adds behavior state tracking to Mob entity with five states: idle, patrol,
converse, flee, and working. Each state has specific processing logic:
- idle: no-op (existing wander logic handles movement)
- patrol: cycles through waypoints with toroidal wrapping support
- converse: stationary during player-driven dialogue
- flee: moves away from threat coordinates
- working: stationary NPC at their post
The behavior module is self-contained and testable, ready for integration
with mob_ai.py in a later step.
Movement now evaluates boundary enter/exit conditions. Exit checks can
block movement based on carrying items (by name or tag). Enter and exit
messages sent when crossing boundary borders. All boundary logic lives
in move_player() before position update.
Tags enable categorizing items for boundary checks and other systems.
Added tags field to Thing and ThingTemplate, updated load and spawn
functions to handle tags from TOML definitions.
Boundaries are rectangular regions within zones that can trigger effects
when players enter or exit. Added BoundaryRegion dataclass with contains()
method, TOML parsing in load_zone(), and export support. Tests verify
parsing, export, and round-trip behavior.
Implements import_map.py script that converts YAML zone definitions to
TOML format used by the engine. YAML format supports all zone features
including terrain, portals, spawns, ambient messages, and boundaries.
Mobs with home regions now pathfind back when they've strayed. Each tick,
process_mob_movement() checks all mobs and moves them one tile toward their
home region center using Manhattan distance. Movement is throttled to 3
seconds, respects impassable terrain, skips mobs in combat, and broadcasts
to nearby players.
These commands enable runtime world editing:
- @goto teleports to a named zone's spawn point
- @dig creates a new blank zone with specified dimensions
- @save exports the current zone to TOML
- @place spawns a thing from templates at player position
Implements TDD feature for readable text on Things:
- Added readable_text field to Thing dataclass
- Extended ThingTemplate to parse readable_text from TOML
- Created read command that finds objects by name/alias in inventory or on ground
- Handles edge cases: no target, not found, not readable
UnlockCondition on CombatMove, parsed from [unlock] TOML section.
check_unlocks evaluates kill_count and mob_kills thresholds.
Locked moves rejected with "You haven't learned that yet." in
do_attack/do_defend. New unlocks announced after kills.
LootEntry defines probabilistic item drops with min/max counts.
roll_loot takes a loot table and returns Thing instances.
MobTemplate now has loot field, parsed from TOML [[loot]] sections.
When a mob dies in combat, create_corpse is called to spawn a corpse
at the mob's position with the mob's inventory transferred. This
replaces the direct despawn_mob call, making combat deaths leave
lootable corpses behind.
The fallback to despawn_mob is kept if the mob somehow has no zone.
Corpse is a non-portable Container subclass that holds a deceased mob's
inventory. The create_corpse factory transfers items from the mob to the
corpse, sets a decompose_at timestamp for eventual cleanup, and calls
despawn_mob to remove the mob from the world.
- Update _find_container to use targeting module (prefix + ordinal)
- Update cmd_put to use find_in_inventory directly
- Add 'get all from <container>' support with portable item filtering
- Add comprehensive tests for all container grammar features
Replace local exact-match helpers with targeting module calls for
prefix matching and ordinal disambiguation. Works in get, drop, and
container extraction (get X from Y).
Objects were comparing by value instead of identity, causing
list.remove() to remove the wrong object when moving items with
identical attributes. Set eq=False on all dataclasses to use
identity-based comparison.
Implements a complete alias system allowing players to create command shortcuts.
Aliases are expanded during dispatch with a recursion guard (max 10 levels).
Changes:
- Add aliases field to Player dataclass (dict[str, str])
- Add player_aliases table to database schema
- Add save_aliases() and load_aliases() persistence functions
- Add alias/unalias commands with built-in command protection
- Integrate alias expansion into dispatch() before command resolution
- Add comprehensive test coverage for all features
Moved common test fixtures (mock_writer, mock_reader, test_zone, player,
nearby_player, clear_state) from individual test files into a shared
conftest.py. This eliminates duplication across test_power.py, test_sleep.py,
test_combat_zaxis.py, test_quit.py, test_stamina_cues.py, and
test_stamina_cue_wiring.py.
Some test files override specific fixtures where they need custom behavior
(e.g., test_quit.py adds a close method to mock_writer, stamina tests use
smaller zones and custom player positions).
Allows instant kill of unconscious opponents. Only works in combat on targets with unconscious posture. Ends encounter, handles mob despawn, sends dramatic messages to both parties.