The {s} conjugation check had incorrect operator precedence that
evaluated the ch/sh suffix check independently of the prev_text
existence check. This could lead to confusing logic flow even
though it didn't crash due to len() handling empty strings safely.
Fixed by wrapping both suffix conditions in parentheses so they're
both guarded by the prev_text truthiness check.
Implements power level management system with tick-based power-up loop.
Players can raise PL toward max_pl (costs stamina per tick), lower PL
instantly, set exact PL targets, and cancel ongoing power-ups.
- Add Where: header with zone description
- Add Location: line with quadrant and coordinates
- Add Nearby: line showing entities in viewport (not on player's tile)
- Add Exits: line showing available cardinal directions
- Replace 'Here:' with individual entity lines showing posture
- Replace 'Portals:' with individual 'You see {name}.' lines
- Add look <thing> routing to examine command
- Add comprehensive tests for new structured output
- Update existing tests to match new output format
Player objects were removed from the players dict on quit/disconnect
but never removed from zone._contents, leaving ghost * markers on
other players' maps.
Player objects were removed from the players dict on quit/disconnect
but never removed from zone._contents, leaving ghost * markers on
other players' maps.
The server never proactively offered GMCP or MSDP to clients, so
telnetlib3 logged "cannot send MSDP without negotiation" every second.
Now the server sends WILL GMCP and WILL MSDP on connection, and
send_msdp_vitals checks negotiation state before attempting to send.
Implements Phase 7 foundation:
- gmcp.py module with package builders for Char.Vitals, Char.Status,
Room.Info, Room.Map, and MSDP vitals
- Player helper methods send_gmcp() and send_msdp() for convenience
- Full test coverage for all GMCP/MSDP functions and edge cases
Zones can now define spawn rules in TOML:
- [[spawns]] sections specify mob type, max count, and respawn timer
- SpawnRule dataclass stores configuration
- load_zone() parses spawn rules from TOML
- Added example spawn rules to treehouse zone (squirrel, crow)
This is configuration infrastructure only - actual spawning logic
will be handled by the game loop in a future phase.
Implements the new player funnel with two tutorial zones:
- Flower: 7x7 sealed zone with translucent petals, spawn at center
- Treehouse: 20x15 platform zone with rope ladder and branch exits
Both zones are bounded (non-toroidal) and include portals for progression.
Zone TOML files can now define portals using [[portals]] sections.
Each portal specifies coordinates (x, y), a target (zone_name:x,y),
and a label. Optional aliases are supported. Portals are
automatically created and placed in the zone when it loads.
Thing templates can now define verbs in TOML using [verbs] section with
module:function references. Verbs are resolved at spawn time and bound
to the spawned object instance using functools.partial. Works for both
Thing and Container instances.
Implements unlock_handler that checks for a key in player inventory
and unlocks containers. Tests cover error cases (non-container,
not locked, no key), success case, key aliasing, and state preservation.
Implements a TDD-built 'use' command that lets players invoke
object verbs with optional targets:
- use X - calls X's use verb
- use X on Y - calls X's use verb with Y as args
- Proper error messages for missing objects/verbs
- Tests cover all edge cases including inventory/ground search
Also fixes type checking issue in verb dispatch where get_verb
could return None.
Implements a global examine/ex command that shows detailed descriptions
of objects. Searches inventory first, then ground at player position.
Works with Things, Containers, and Mobs.
Verbs let any Object have interactive handlers players can trigger.
Uses @verb decorator to mark methods that auto-register on instantiation.
- Object._verbs dict stores verb name to async handler mapping
- Object.register_verb(), get_verb(), has_verb() API
- @verb decorator marks methods with _verb_name attribute
- __post_init__ scans for decorated methods and registers them
- find_object() helper searches inventory then ground by name/alias
- Bound methods stored in _verbs (self already bound)
- Works on Object and all subclasses (Thing, Entity, etc)
- 18 tests covering registration, lookup, decoration, inheritance
Look command now displays portals separately from ground items.
Portals at the player's position are shown after ground items with
the format "Portals: name1, name2". This separates portals from
regular items since they serve a different purpose in gameplay.
Implements portal-based zone transitions with the enter command.
Players can enter portals at their position to move to target zones
with specified coordinates. Includes departure/arrival messaging to
nearby players and automatic look output in the destination zone.
Portals are matched by partial name or exact alias match.
Extended ThingTemplate with optional container fields (capacity, closed, locked).
When a template includes capacity, spawn_thing now creates a Container instead
of a regular Thing.
Added two example container templates:
- chest.toml: non-portable, capacity 5, starts closed
- sack.toml: portable, capacity 3, starts open
Containers now display their state when viewed:
- Closed containers show "(closed)"
- Open empty containers show "(open, empty)"
- Open containers with items show "(open, containing: item1, item2)"
This applies to both ground items in the look command and inventory items.
Added _format_thing_name helper to both look.py and things.py to handle
the display formatting consistently.
Implements load_zone() and load_zones() functions to parse zone
definitions from TOML files. Wires zone loading into server startup
to register all zones from content/zones/ directory. Updates player
zone lookup to use the registry instead of hardcoded overworld check.
Includes tavern.toml as first hand-built interior zone (8x6 bounded).
Implements a module-level zone registry for looking up zones by name.
Includes register_zone() and get_zone() functions with comprehensive
tests covering single/multiple zones, unknown lookups, and overwrites.