"""Tests for combat command targeting integration.""" import pytest from mudlib.combat.commands import do_attack from mudlib.combat.moves import CombatMove from mudlib.mobs import Mob from mudlib.player import Player @pytest.fixture def attack_move(): """Create a test attack move.""" return CombatMove( name="punch left", command="punch", variant="left", move_type="attack", stamina_cost=5, hit_time_ms=850, telegraph="telegraphs a left punch at {defender}", telegraph_color="yellow", aliases=[], ) @pytest.mark.asyncio async def test_attack_with_prefix_match(player, nearby_player, attack_move): """Attack with prefix match finds target.""" player.stamina = 10 # Use "veg" to find Vegeta await do_attack(player, "veg", attack_move) # Should send combat start message sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert "You engage Vegeta in combat!" in sent assert "You use punch left!" in sent @pytest.mark.asyncio async def test_attack_with_ordinal(player, nearby_player, test_zone, attack_move): """Attack with ordinal selects correct target.""" # Create two more players on the same tile, both starting with "p" from unittest.mock import AsyncMock, MagicMock mock_writer2 = MagicMock() mock_writer2.write = MagicMock() mock_writer2.drain = AsyncMock() mock_reader2 = MagicMock() player2 = Player(name="Piccolo", x=0, y=0, reader=mock_reader2, writer=mock_writer2) player2.location = test_zone test_zone._contents.append(player2) mock_writer3 = MagicMock() mock_writer3.write = MagicMock() mock_writer3.drain = AsyncMock() mock_reader3 = MagicMock() player3 = Player(name="Puar", x=0, y=0, reader=mock_reader3, writer=mock_writer3) player3.location = test_zone test_zone._contents.append(player3) from mudlib.player import players players[player2.name] = player2 players[player3.name] = player3 player.stamina = 10 # Use "2.p" to find the second player starting with "p" (should be Puar) await do_attack(player, "2.p", attack_move) # Should engage the second "p" match sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert ( "You engage Piccolo in combat!" in sent or "You engage Puar in combat!" in sent ) @pytest.mark.asyncio async def test_attack_nonexistent_target(player, attack_move): """Attack on non-existent target shows error.""" player.stamina = 10 await do_attack(player, "nobody", attack_move) sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert "You need a target to start combat." in sent @pytest.mark.asyncio async def test_attack_z_axis_mismatch(player, nearby_player, attack_move): """Attack on different z-axis is blocked with specific error message.""" player.stamina = 10 player.flying = True nearby_player.flying = False # find_entity_on_tile returns None due to z-axis mismatch await do_attack(player, "Vegeta", attack_move) sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert "You can't reach them from here!" in sent @pytest.mark.asyncio async def test_attack_mob_with_prefix(player, test_zone, attack_move): """Attack mob with prefix match.""" player.stamina = 10 # Create a mob on the same tile mob = Mob(name="Goblin", x=0, y=0) mob.location = test_zone test_zone._contents.append(mob) # Use "gob" to find the goblin await do_attack(player, "gob", attack_move) sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert "You engage Goblin in combat!" in sent @pytest.mark.asyncio async def test_attack_skips_dead_mob(player, test_zone, attack_move): """Attack skips dead mobs.""" player.stamina = 10 # Create a dead mob on the same tile mob = Mob(name="Goblin", x=0, y=0) mob.location = test_zone mob.alive = False test_zone._contents.append(mob) # Should not find the dead mob await do_attack(player, "goblin", attack_move) sent = "".join(call.args[0] for call in player.writer.write.call_args_list) assert "You need a target to start combat." in sent