211 lines
8.9 KiB
Python
211 lines
8.9 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
|
|
from resources.logic.organs import apply_organ_interactions_tick
|
|
|
|
DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs"
|
|
|
|
|
|
@pytest.fixture()
|
|
def registry():
|
|
return DefRegistry.load(DEFS_DIR)
|
|
|
|
|
|
def make_character(registry, species_id: str, **kwargs) -> Character:
|
|
return Character(species_id=species_id, default_body_parts=registry.new_default_body_parts(species_id), **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
|
|
|
|
|
|
# --- explicit, fully bespoke default_organs per species -------------------------------------
|
|
|
|
|
|
def test_human_species_gets_its_organs(registry):
|
|
parts = registry.new_default_body_parts("human")
|
|
torso = next(p for p in parts if p.slot == "torso")
|
|
assert {o.organ_def_id for o in torso.organs} == {"human_heart", "human_lungs", "human_liver", "human_kidneys"}
|
|
|
|
|
|
def test_bot_species_gets_bespoke_mechanical_organs(registry):
|
|
parts = registry.new_default_body_parts("bot")
|
|
head = next(p for p in parts if p.slot == "head")
|
|
torso = next(p for p in parts if p.slot == "torso")
|
|
assert {o.organ_def_id for o in head.organs} == {"bot_cpu", "bot_npu"}
|
|
assert {o.organ_def_id for o in torso.organs} == {
|
|
"bot_power_cell", "bot_hydraulic_fluid_bladder", "bot_cooling_fan",
|
|
}
|
|
|
|
|
|
def test_bot_power_cell_affects_multiple_organs(registry):
|
|
bot = make_character(registry, "bot")
|
|
torso = bot.get_or_create_body_part_override("torso")
|
|
power_cell = next(o for o in torso.organs if o.organ_def_id == "bot_power_cell")
|
|
power_cell.integrity = 0.0
|
|
|
|
apply_organ_interactions_tick(bot, registry, ticks=10)
|
|
|
|
head = bot.get_body_part("head")
|
|
cpu = next(o for o in head.organs if o.organ_def_id == "bot_cpu")
|
|
npu = next(o for o in head.organs if o.organ_def_id == "bot_npu")
|
|
fluid_bladder = next(o for o in bot.get_body_part("torso").organs if o.organ_def_id == "bot_hydraulic_fluid_bladder")
|
|
assert cpu.integrity < 100.0
|
|
assert npu.integrity < 100.0
|
|
assert fluid_bladder.integrity < 100.0
|
|
|
|
|
|
def test_reptilian_venom_gland_is_bespoke_not_shared_with_other_species(registry):
|
|
parts = registry.new_default_body_parts("reptilian_quad")
|
|
torso = next(p for p in parts if p.slot == "torso")
|
|
assert {o.organ_def_id for o in torso.organs} == {
|
|
"reptilian_heart", "reptilian_lung", "reptilian_liver", "reptilian_venom_gland",
|
|
}
|
|
|
|
|
|
def test_zenari_organs_are_bespoke(registry):
|
|
parts = registry.new_default_body_parts("alien_tri")
|
|
torso = next(p for p in parts if p.slot == "torso")
|
|
head = next(p for p in parts if p.slot == "head")
|
|
assert {o.organ_def_id for o in torso.organs} == {
|
|
"zenari_cardial_node", "zenari_respirator", "zenari_metabolizer", "zenari_filtration_organ",
|
|
}
|
|
assert {o.organ_def_id for o in head.organs} == {"zenari_cortex"}
|
|
|
|
|
|
def test_dog_organs_are_bespoke_not_shared_with_human(registry):
|
|
parts = registry.new_default_body_parts("dog")
|
|
torso = next(p for p in parts if p.slot == "torso")
|
|
assert {o.organ_def_id for o in torso.organs} == {"dog_heart", "dog_lungs", "dog_liver", "dog_kidneys"}
|
|
|
|
|
|
def test_no_species_shares_an_organ_id_with_another(registry):
|
|
species_ids = ("human", "reptilian_quad", "alien_tri", "bot", "dog")
|
|
seen: dict[str, str] = {}
|
|
for species_id in species_ids:
|
|
species = registry.get_species_def(species_id)
|
|
for organ_id in species.default_organs:
|
|
assert organ_id not in seen, f"{organ_id!r} shared between {seen.get(organ_id)!r} and {species_id!r}"
|
|
seen[organ_id] = species_id
|
|
|
|
|
|
# --- reptilian_quad: 4 arm slots -------------------------------------------------------------
|
|
|
|
|
|
def test_reptilian_has_four_arm_slots(registry):
|
|
species = registry.get_species_def("reptilian_quad")
|
|
slots = {p.slot for p in species.default_body_parts}
|
|
assert {"left_arm", "right_arm", "left_arm_2", "right_arm_2"} <= slots
|
|
|
|
|
|
def test_reptilian_can_wield_two_two_handed_tools_at_once(registry):
|
|
reptilian = make_character(registry, "reptilian_quad")
|
|
assert reptilian.wield_item("right_hand", "multi_deconstructor", registry) is True
|
|
assert reptilian.wield_item("right_hand_2", "constructor_tool", registry) is True
|
|
assert reptilian.get_held_item("left_hand").item_def_id == "multi_deconstructor"
|
|
assert reptilian.get_held_item("left_hand_2").item_def_id == "constructor_tool"
|
|
|
|
|
|
def test_reptilian_secondary_hand_pair_does_not_disturb_primary(registry):
|
|
reptilian = make_character(registry, "reptilian_quad")
|
|
reptilian.wield_item("right_hand", "staff_oak", registry)
|
|
reptilian.wield_item("right_hand_2", "multi_deconstructor", registry)
|
|
assert reptilian.get_held_item("right_hand").item_def_id == "staff_oak"
|
|
assert reptilian.get_held_item("left_hand") is None # staff is one-handed, doesn't touch left_hand
|
|
assert reptilian.get_held_item("left_hand_2").item_def_id == "multi_deconstructor"
|
|
|
|
|
|
def test_two_armed_alien_cannot_use_secondary_hand_pair(registry):
|
|
alien = make_character(registry, "alien_tri")
|
|
assert alien.wield_item("right_hand_2", "staff_oak", registry) is False
|
|
|
|
|
|
# --- alien_tri: 3 genders --------------------------------------------------------------------
|
|
|
|
|
|
def test_alien_tri_species_declares_three_genders(registry):
|
|
species = registry.get_species_def("alien_tri")
|
|
assert species.genders == ["alpha", "beta", "gamma"]
|
|
|
|
|
|
# --- dog: quadruped, no arms -----------------------------------------------------------------
|
|
|
|
|
|
def test_dog_has_no_arm_slots(registry):
|
|
species = registry.get_species_def("dog")
|
|
slots = {p.slot for p in species.default_body_parts}
|
|
assert slots == {"head", "torso", "front_left_leg", "front_right_leg", "back_left_leg", "back_right_leg"}
|
|
|
|
|
|
def test_dog_cannot_wield_anything(registry):
|
|
dog = make_character(registry, "dog")
|
|
assert dog.wield_item("right_hand", "staff_oak", registry) is False
|
|
assert dog.held_items == []
|
|
|
|
|
|
def test_dog_effective_speed_uses_all_four_legs(registry):
|
|
dog = make_character(registry, "dog")
|
|
human = make_character(registry, "human")
|
|
# 4 legs at 0.25 speed_factor each should sum to the same total factor as 2 legs at 0.5 each
|
|
assert dog.effective_speed(30.0, registry) == pytest.approx(human.effective_speed(30.0, registry))
|
|
|
|
|
|
def test_dog_can_perform_bite_attack_but_not_arm_dependent_abilities(registry):
|
|
dog = make_character(registry, "dog")
|
|
assert dog.can_perform_ability("melee_attack", registry) is True
|
|
assert dog.can_perform_ability("time_warp_sphere", registry) is False # not in dog's ability list at all
|
|
|
|
|
|
# --- one-ability-per-required-hand: de/constructor tools, verified across species -----------
|
|
|
|
|
|
def test_constructor_via_left_hand_also_builds(registry):
|
|
world, field = make_world(registry)
|
|
character = make_character(registry, "human", position=EntityPosition("field", 5, 5, 0))
|
|
character.wield_item("left_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, "left_hand", (1, 0, 0))
|
|
assert result is True
|
|
assert field.get_tile(6, 5, 0).room.def_id == "wood_wall"
|
|
|
|
|
|
def test_reptilian_secondary_pair_deconstructor_independent_of_primary_pair_tool(registry):
|
|
"""A 4-armed species can wield a 2-handed tool in each hand pair; activating one hand
|
|
pair's tool must not affect the other pair's separately-wielded tool - each required hand
|
|
only ever triggers the ability of whatever it itself is holding.
|
|
"""
|
|
world, field = make_world(registry)
|
|
field.get_tile(6, 5, 0).room = TileLayerInstance("wood_wall")
|
|
reptilian = make_character(registry, "reptilian_quad", position=EntityPosition("field", 5, 5, 0))
|
|
reptilian.wield_item("right_hand", "multi_deconstructor", registry)
|
|
reptilian.wield_item("right_hand_2", "constructor_tool", registry)
|
|
world.add_entity(reptilian)
|
|
|
|
# right_hand's deconstructor: left-click semantics (button="left") -> destroy, no item yielded
|
|
result = activate_hand(reptilian, world, registry, "right_hand", (1, 0, 0))
|
|
assert result is True
|
|
assert field.get_tile(6, 5, 0).room is None
|
|
|
|
# the secondary pair's constructor is untouched by the above and still independently usable
|
|
assert reptilian.get_held_item("left_hand_2").item_def_id == "constructor_tool"
|