from pathlib import Path import pytest from engine.character import Character from engine.defs import DefRegistry from engine.entity import EntityPosition from engine.inventory import Inventory from engine.item import ItemInstance from engine.map import Map from engine.tile import Tile, TileLayerInstance from engine.world import World from resources.logic.actions import activate_hand DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs" @pytest.fixture() def registry(): return DefRegistry.load(DEFS_DIR) def make_human(registry, **kwargs) -> Character: return Character(species_id="human", default_body_parts=registry.new_default_body_parts("human"), **kwargs) def make_world(registry): field = Map("field", 10, 10, 1) for x in range(10): for y in range(10): field.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("grass"))) world = World(registry=registry) world.add_map(field) return world, field class FixedRng: def __init__(self, roll=15, choice_value="torso"): self._roll = roll self._choice_value = choice_value def randint(self, _lo, _hi): return self._roll def choice(self, seq): return self._choice_value if self._choice_value is not None else seq[0] def test_empty_hand_attacks_unarmed(registry): world, _field = make_world(registry) attacker = make_human(registry, position=EntityPosition("field", 5, 5, 0)) attacker.bio["athletics"] = 10 defender = make_human(registry, position=EntityPosition("field", 6, 5, 0)) world.add_entity(attacker) world.add_entity(defender) result = activate_hand(attacker, world, registry, "right_hand", (1, 0, 0), rng=FixedRng()) assert result is not None assert result.hit is True assert result.weapon_bonus == 0.0 def test_empty_hand_with_no_target_ahead_is_a_no_op(registry): world, _field = make_world(registry) attacker = make_human(registry, position=EntityPosition("field", 5, 5, 0)) world.add_entity(attacker) assert activate_hand(attacker, world, registry, "right_hand", (1, 0, 0)) is None def test_wielded_melee_weapon_attacks(registry): world, _field = make_world(registry) attacker = make_human(registry, position=EntityPosition("field", 5, 5, 0)) attacker.wield_item("right_hand", "staff_oak", registry) defender = make_human(registry, position=EntityPosition("field", 6, 5, 0)) world.add_entity(attacker) world.add_entity(defender) result = activate_hand(attacker, world, registry, "right_hand", (1, 0, 0), rng=FixedRng()) assert result.weapon_bonus == pytest.approx(6.0) # staff_oak def test_wielded_gun_shoots(registry): world, _field = make_world(registry) shooter = make_human(registry, position=EntityPosition("field", 0, 5, 0)) shooter.wield_item("right_hand", "pistol_basic", registry) target = make_human(registry, position=EntityPosition("field", 3, 5, 0)) world.add_entity(shooter) world.add_entity(target) result = activate_hand(shooter, world, registry, "right_hand", (1, 0, 0), rng=FixedRng()) assert result.hit is True assert result.target_entity_id == target.entity_id def test_wielded_deconstructor_interacts_with_tile_ahead(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("wood_wall") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.wield_item("right_hand", "multi_deconstructor", registry) world.add_entity(character) result = activate_hand(character, world, registry, "right_hand", (1, 0, 0)) assert result is True assert field.get_tile(6, 5, 0).room is None def test_left_hand_deconstructor_uses_right_click_semantics(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("wood_wall") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.wield_item("left_hand", "multi_deconstructor", registry) character.inventory = Inventory(4, 4) world.add_entity(character) result = activate_hand(character, world, registry, "left_hand", (1, 0, 0)) assert result is not None assert result.def_id == "wood_planks" # right-click behavior: yields an item assert field.get_tile(6, 5, 0).room is None def test_wielded_constructor_builds_on_bare_tile_ahead(registry): world, field = make_world(registry) character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.wield_item("right_hand", "constructor_tool", registry) character.inventory = Inventory(4, 4) planks_def = registry.get_item_def("wood_planks") character.inventory.place(ItemInstance("planks-1", "wood_planks"), planks_def, 0, 0) world.add_entity(character) result = activate_hand(character, world, registry, "right_hand", (1, 0, 0)) assert result is True assert field.get_tile(6, 5, 0).room.def_id == "wood_wall" def test_deconstructor_out_of_bounds_target_is_a_no_op(registry): world, _field = make_world(registry) character = make_human(registry, position=EntityPosition("field", 0, 0, 0)) character.wield_item("right_hand", "multi_deconstructor", registry) world.add_entity(character) assert activate_hand(character, world, registry, "right_hand", (-1, 0, 0)) is None def test_wielded_hacking_device_hacks_a_locked_door_ahead(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("door_locked") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.wield_item("right_hand", "hacking_device", registry) character.bio["intelligence"] = 10 character.bio["perception"] = 10 world.add_entity(character) result = activate_hand(character, world, registry, "right_hand", (1, 0, 0), rng=FixedRng(roll=10)) assert result is True assert field.get_tile(6, 5, 0).room.def_id == "door_open" def test_wielded_hacking_device_fails_a_low_roll_and_leaves_the_door_locked(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("door_locked") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.wield_item("right_hand", "hacking_device", registry) world.add_entity(character) result = activate_hand(character, world, registry, "right_hand", (1, 0, 0), rng=FixedRng(roll=1)) assert result is False assert field.get_tile(6, 5, 0).room.def_id == "door_locked" def test_bare_hand_hacks_a_door_when_a_hacking_implant_is_installed(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("door_locked") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) character.install_implant("cyberdeck_the_miro_model_3a", registry) world.add_entity(character) result = activate_hand(character, world, registry, "right_hand", (1, 0, 0), rng=FixedRng(roll=1)) assert result is True assert field.get_tile(6, 5, 0).room.def_id == "door_open" def test_bare_hand_without_a_hacking_implant_does_not_touch_a_locked_door(registry): world, field = make_world(registry) field.get_tile(6, 5, 0).room = TileLayerInstance("door_locked") character = make_human(registry, position=EntityPosition("field", 5, 5, 0)) world.add_entity(character) assert activate_hand(character, world, registry, "right_hand", (1, 0, 0), rng=FixedRng(roll=20)) is None assert field.get_tile(6, 5, 0).room.def_id == "door_locked"