193 lines
7.5 KiB
Python
193 lines
7.5 KiB
Python
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"
|