Wire target resolution into look command

This commit is contained in:
Jared Miller 2026-02-14 01:16:51 -05:00
parent 86797c3a82
commit a98f340e5a
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
2 changed files with 253 additions and 4 deletions

View file

@ -1,7 +1,6 @@
"""Look command for viewing the world.""" """Look command for viewing the world."""
from mudlib.commands import CommandDefinition, register from mudlib.commands import CommandDefinition, register
from mudlib.commands.examine import cmd_examine
from mudlib.commands.things import _format_thing_name from mudlib.commands.things import _format_thing_name
from mudlib.effects import get_effects_at from mudlib.effects import get_effects_at
from mudlib.entity import Entity from mudlib.entity import Entity
@ -27,11 +26,54 @@ async def cmd_look(player: Player, args: str) -> None:
Args: Args:
player: The player executing the command 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(): 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 return
zone = player.location zone = player.location

View 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