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 now skips spurious empty lines from client negotiation bytes
during the name prompt, only closing on actual connection loss. This
makes the login flow robust against clients that send IAC bytes with
trailing CRLF during negotiation.
Also fixed tintin++ CATCH handlers to use proper \} syntax matching the
documented examples.
The old begin_advanced_negotiation relied on a telnetlib3 hook that races with the negotiation timer — by the time it fires, the timer has already declared negotiation complete. Moving to begin_negotiation sends the offers alongside standard options (TTYPE, NAWS, ECHO) so clients see them immediately.
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.