from pathlib import Path import pytest from engine.character import Character from engine.defs import DefRegistry from engine.entity import EntityPosition from engine.map import Map from engine.tile import Tile, TileLayerInstance from engine.ui import AbilityBar, AbilityBarSlot, populate_ability_bar from engine.world import World from resources.logic.actions import activate_ability_bar_slot 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", 15, 15, 1) for x in range(15): for y in range(15): field.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("grass"))) world = World(registry=registry) world.add_map(field) return world # --- AbilityBar state --------------------------------------------------------------------- def test_ability_bar_starts_with_ten_empty_slots(): bar = AbilityBar() assert len(bar.slots) == 10 assert all(slot is None for slot in bar.slots) assert bar.selected_index is None assert bar.selected_slot() is None def test_assign_places_a_slot_at_the_given_index(): bar = AbilityBar() slot = AbilityBarSlot(kind="hand", source_id="right_hand", ability_id="time_warp_sphere") bar.assign(2, slot) assert bar.slots[2] is slot def test_select_sets_selected_index_and_selected_slot_reads_it_back(): bar = AbilityBar() slot = AbilityBarSlot(kind="implant", source_id="chronoimplant", ability_id="time_warp_sphere") bar.assign(5, slot) bar.select(5) assert bar.selected_index == 5 assert bar.selected_slot() is slot def test_select_out_of_range_is_ignored(): bar = AbilityBar() bar.select(0) bar.select(99) assert bar.selected_index == 0 # unchanged by the invalid select def test_mouse_click_and_hotkey_both_just_call_select(): # No separate "click" concept - a mouse click on bar position i and pressing numkey i+1 # both resolve to the same select(i) call once input is wired to a bar index. bar = AbilityBar() slot = AbilityBarSlot(kind="hand", source_id="left_hand", ability_id="time_warp_sphere") bar.assign(0, slot) bar.select(0) assert bar.selected_slot() is slot # --- activate_ability_bar_slot resolver ----------------------------------------------------- def test_activate_ability_bar_slot_dispatches_to_hand(registry): world = make_world(registry) caster = make_human(registry, position=EntityPosition("field", 5.0, 5.0, 0.0)) caster.wield_item("right_hand", "chronowand", registry) world.add_entity(caster) slot = AbilityBarSlot(kind="hand", source_id="right_hand", ability_id="time_warp_sphere") result = activate_ability_bar_slot(caster, world, registry, slot, (1, 0, 0), now=3.0) assert result is not None assert len(world.time_warp_zones) == 1 def test_activate_ability_bar_slot_dispatches_to_implant(registry): world = make_world(registry) caster = make_human(registry, position=EntityPosition("field", 5.0, 5.0, 0.0)) caster.install_implant("chronoimplant", registry) world.add_entity(caster) slot = AbilityBarSlot(kind="implant", source_id="chronoimplant", ability_id="time_warp_sphere") result = activate_ability_bar_slot(caster, world, registry, slot, (1, 0, 0), now=3.0) assert result is not None assert len(world.time_warp_zones) == 1 def test_activate_ability_bar_slot_unknown_kind_returns_none(registry): world = make_world(registry) caster = make_human(registry, position=EntityPosition("field", 5.0, 5.0, 0.0)) world.add_entity(caster) slot = AbilityBarSlot(kind="bogus", source_id="right_hand", ability_id="time_warp_sphere") assert activate_ability_bar_slot(caster, world, registry, slot, (1, 0, 0), now=3.0) is None # --- populate_ability_bar ------------------------------------------------------------------- def test_populate_fills_a_slot_for_a_wielded_ability_granting_item(registry): bar = AbilityBar() character = make_human(registry) character.wield_item("right_hand", "chronowand", registry) populate_ability_bar(bar, character, registry) assert bar.slots[0] == AbilityBarSlot(kind="hand", source_id="right_hand", ability_id="time_warp_sphere") assert bar.slots[1] is None def test_populate_skips_a_wielded_item_with_no_granted_ability(registry): bar = AbilityBar() character = make_human(registry) character.wield_item("right_hand", "staff_oak", registry) populate_ability_bar(bar, character, registry) assert all(slot is None for slot in bar.slots) def test_populate_fills_a_slot_for_an_installed_implant(registry): bar = AbilityBar() character = make_human(registry) character.install_implant("chronoimplant", registry) populate_ability_bar(bar, character, registry) assert bar.slots[0] == AbilityBarSlot(kind="implant", source_id="chronoimplant", ability_id="time_warp_sphere") def test_populate_orders_hands_before_implants(registry): bar = AbilityBar() character = make_human(registry) character.install_implant("chronoimplant", registry) character.wield_item("right_hand", "storm_rod", registry) populate_ability_bar(bar, character, registry) assert bar.slots[0] == AbilityBarSlot(kind="hand", source_id="right_hand", ability_id="launch_lightning") assert bar.slots[1] == AbilityBarSlot(kind="implant", source_id="chronoimplant", ability_id="time_warp_sphere") def test_populate_clears_stale_slots_from_a_previous_call(registry): bar = AbilityBar() character = make_human(registry) character.wield_item("right_hand", "chronowand", registry) populate_ability_bar(bar, character, registry) assert bar.slots[0] is not None character.unwield("right_hand") populate_ability_bar(bar, character, registry) assert bar.slots[0] is None