Add mode stack to Player and mode check in dispatch

This commit is contained in:
Jared Miller 2026-02-07 16:17:01 -05:00
parent dcc8b961bb
commit d220835f7d
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 66 additions and 1 deletions

View file

@ -60,5 +60,11 @@ async def dispatch(player: Player, raw_input: str) -> None:
await player.writer.drain() await player.writer.drain()
return return
# Check mode restriction
if defn.mode != "*" and defn.mode != player.mode:
player.writer.write("You can't do that right now.\r\n")
await player.writer.drain()
return
# Execute the handler # Execute the handler
await defn.handler(player, args) await defn.handler(player, args)

View file

@ -1,6 +1,6 @@
"""Player state and registry.""" """Player state and registry."""
from dataclasses import dataclass from dataclasses import dataclass, field
from typing import Any from typing import Any
@ -14,6 +14,12 @@ class Player:
writer: Any # telnetlib3 TelnetWriter for sending output writer: Any # telnetlib3 TelnetWriter for sending output
reader: Any # telnetlib3 TelnetReader for reading input reader: Any # telnetlib3 TelnetReader for reading input
flying: bool = False flying: bool = False
mode_stack: list[str] = field(default_factory=lambda: ["normal"])
@property
def mode(self) -> str:
"""Current mode is the top of the stack."""
return self.mode_stack[-1]
# Global registry of connected players # Global registry of connected players

View file

@ -318,3 +318,56 @@ async def test_effects_dont_override_player_marker(player, mock_world):
assert "@" in output assert "@" in output
active_effects.clear() active_effects.clear()
# Test mode stack
def test_mode_stack_default(player):
"""Player starts in normal mode."""
assert player.mode == "normal"
assert player.mode_stack == ["normal"]
@pytest.mark.asyncio
async def test_dispatch_blocks_wrong_mode(player, mock_writer):
"""Commands with wrong mode get rejected."""
async def combat_handler(p, args):
pass
commands.register(CommandDefinition("strike", combat_handler, mode="combat"))
await commands.dispatch(player, "strike")
assert mock_writer.write.called
written = mock_writer.write.call_args[0][0]
assert "can't" in written.lower()
@pytest.mark.asyncio
async def test_dispatch_allows_wildcard_mode(player):
"""Commands with mode='*' work from any mode."""
called = False
async def any_handler(p, args):
nonlocal called
called = True
commands.register(CommandDefinition("universal", any_handler, mode="*"))
await commands.dispatch(player, "universal")
assert called
@pytest.mark.asyncio
async def test_dispatch_allows_matching_mode(player):
"""Commands work when player mode matches command mode."""
called = False
async def combat_handler(p, args):
nonlocal called
called = True
commands.register(CommandDefinition("strike", combat_handler, mode="combat"))
player.mode_stack.append("combat")
await commands.dispatch(player, "strike")
assert called