Wire target resolution into look command
This commit is contained in:
parent
86797c3a82
commit
a98f340e5a
2 changed files with 253 additions and 4 deletions
|
|
@ -1,7 +1,6 @@
|
|||
"""Look command for viewing the world."""
|
||||
|
||||
from mudlib.commands import CommandDefinition, register
|
||||
from mudlib.commands.examine import cmd_examine
|
||||
from mudlib.commands.things import _format_thing_name
|
||||
from mudlib.effects import get_effects_at
|
||||
from mudlib.entity import Entity
|
||||
|
|
@ -27,11 +26,54 @@ async def cmd_look(player: Player, args: str) -> None:
|
|||
|
||||
Args:
|
||||
player: The player executing the command
|
||||
args: Command arguments (if provided, route to examine)
|
||||
args: Command arguments (if provided, use targeting to resolve)
|
||||
"""
|
||||
# If args provided, route to examine
|
||||
# If args provided, use targeting to resolve
|
||||
if args.strip():
|
||||
await cmd_examine(player, args)
|
||||
from mudlib.targeting import (
|
||||
find_entity_on_tile,
|
||||
find_in_inventory,
|
||||
find_thing_on_tile,
|
||||
)
|
||||
|
||||
target_name = args.strip()
|
||||
|
||||
# First try to find an entity on the tile
|
||||
entity = find_entity_on_tile(target_name, player)
|
||||
if entity:
|
||||
# Show entity info (name and posture)
|
||||
if hasattr(entity, "description") and entity.description:
|
||||
await player.send(f"{entity.description}\r\n")
|
||||
else:
|
||||
await player.send(f"{entity.name} is {entity.posture}.\r\n")
|
||||
return
|
||||
|
||||
# Then try to find a thing on the ground
|
||||
zone = player.location
|
||||
if zone is not None and isinstance(zone, Zone):
|
||||
thing = find_thing_on_tile(target_name, zone, player.x, player.y)
|
||||
if thing:
|
||||
# Show thing description
|
||||
desc = getattr(thing, "description", "")
|
||||
if desc:
|
||||
await player.send(f"{desc}\r\n")
|
||||
else:
|
||||
await player.send("You see nothing special.\r\n")
|
||||
return
|
||||
|
||||
# Finally try inventory
|
||||
thing = find_in_inventory(target_name, player)
|
||||
if thing:
|
||||
# Show thing description
|
||||
desc = getattr(thing, "description", "")
|
||||
if desc:
|
||||
await player.send(f"{desc}\r\n")
|
||||
else:
|
||||
await player.send("You see nothing special.\r\n")
|
||||
return
|
||||
|
||||
# Nothing found
|
||||
await player.send("You don't see that here.\r\n")
|
||||
return
|
||||
|
||||
zone = player.location
|
||||
|
|
|
|||
207
tests/test_look_targeting.py
Normal file
207
tests/test_look_targeting.py
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
"""Tests for look command target resolution."""
|
||||
|
||||
import pytest
|
||||
|
||||
from mudlib.commands.look import cmd_look
|
||||
from mudlib.entity import Mob
|
||||
from mudlib.player import Player
|
||||
from mudlib.thing import Thing
|
||||
from mudlib.zone import Zone
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def zone():
|
||||
"""Create a test zone."""
|
||||
terrain = [["." for _ in range(100)] for _ in range(100)]
|
||||
return Zone(
|
||||
name="test",
|
||||
description="Test Zone",
|
||||
width=100,
|
||||
height=100,
|
||||
toroidal=False,
|
||||
terrain=terrain,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_writer():
|
||||
"""Create a mock writer that captures output."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
class MockWriter:
|
||||
def __init__(self):
|
||||
self.output = []
|
||||
# Mock telnet options
|
||||
self.local_option = MagicMock()
|
||||
self.remote_option = MagicMock()
|
||||
self.local_option.enabled = MagicMock(return_value=False)
|
||||
self.remote_option.enabled = MagicMock(return_value=False)
|
||||
|
||||
def write(self, data):
|
||||
self.output.append(data)
|
||||
|
||||
async def drain(self):
|
||||
pass
|
||||
|
||||
def get_output(self):
|
||||
return "".join(self.output)
|
||||
|
||||
return MockWriter()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def player(zone, mock_writer):
|
||||
"""Create a test player."""
|
||||
p = Player(
|
||||
name="TestPlayer",
|
||||
location=zone,
|
||||
x=50,
|
||||
y=50,
|
||||
writer=mock_writer,
|
||||
)
|
||||
return p
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_mob_by_exact_name(player, zone, mock_writer):
|
||||
"""look goblin finds mob with exact name."""
|
||||
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
||||
|
||||
await cmd_look(player, "goblin")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "goblin" in output.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_mob_by_prefix(player, zone, mock_writer):
|
||||
"""look gob prefix matches goblin."""
|
||||
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
||||
|
||||
await cmd_look(player, "gob")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "goblin" in output.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_second_mob_with_ordinal(player, zone, mock_writer):
|
||||
"""look 2.goblin finds second goblin."""
|
||||
_mob1 = Mob(name="goblin", location=zone, x=50, y=50)
|
||||
_mob2 = Mob(name="goblin", location=zone, x=50, y=50)
|
||||
|
||||
await cmd_look(player, "2.goblin")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
# Should find the second goblin
|
||||
assert "goblin" in output.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_thing_on_ground(player, zone, mock_writer):
|
||||
"""look sword finds thing on ground and shows description."""
|
||||
_sword = Thing(
|
||||
name="sword",
|
||||
description="A sharp blade.",
|
||||
location=zone,
|
||||
x=50,
|
||||
y=50,
|
||||
)
|
||||
|
||||
await cmd_look(player, "sword")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "A sharp blade." in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_shows_error_for_nonexistent(player, zone, mock_writer):
|
||||
"""look nonexistent shows 'You don't see that here.'"""
|
||||
await cmd_look(player, "nonexistent")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "You don't see that here." in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_prioritizes_entity_over_thing(player, zone, mock_writer):
|
||||
"""look target finds entity before thing with same name."""
|
||||
_mob = Mob(name="target", location=zone, x=50, y=50)
|
||||
_thing = Thing(
|
||||
name="target",
|
||||
description="A thing.",
|
||||
location=zone,
|
||||
x=50,
|
||||
y=50,
|
||||
)
|
||||
|
||||
await cmd_look(player, "target")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
# Should show entity info (name/posture), not thing description
|
||||
assert "target" in output.lower()
|
||||
# Should not show thing description
|
||||
assert "A thing." not in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_skips_dead_mobs(player, zone, mock_writer):
|
||||
"""look goblin skips dead mobs."""
|
||||
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
||||
_mob.alive = False
|
||||
|
||||
await cmd_look(player, "goblin")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "You don't see that here." in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_skips_self(player, zone, mock_writer):
|
||||
"""look TestPlayer doesn't target the player themselves."""
|
||||
await cmd_look(player, "TestPlayer")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "You don't see that here." in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_thing_in_inventory(player, zone, mock_writer):
|
||||
"""look sword finds thing in inventory when not on ground."""
|
||||
_sword = Thing(
|
||||
name="sword",
|
||||
description="A sharp blade.",
|
||||
location=player,
|
||||
)
|
||||
|
||||
await cmd_look(player, "sword")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
assert "A sharp blade." in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_look_finds_second_thing_with_ordinal(player, zone, mock_writer):
|
||||
"""look 2.sword finds second sword on ground."""
|
||||
_sword1 = Thing(
|
||||
name="sword",
|
||||
description="A rusty blade.",
|
||||
location=zone,
|
||||
x=50,
|
||||
y=50,
|
||||
)
|
||||
_sword2 = Thing(
|
||||
name="sword",
|
||||
description="A sharp blade.",
|
||||
location=zone,
|
||||
x=50,
|
||||
y=50,
|
||||
)
|
||||
|
||||
await cmd_look(player, "2.sword")
|
||||
|
||||
output = mock_writer.get_output()
|
||||
# Should show the second sword's description
|
||||
assert "A sharp blade." in output
|
||||
# Should not show the first sword's description
|
||||
assert "A rusty blade." not in output
|
||||
Loading…
Reference in a new issue