Mi2dRPGamEng/tests/test_character_health.py

171 lines
6.3 KiB
Python

from pathlib import Path
import pytest
from engine.character import BodyPart, Character, ClothingPiece
from engine.defs import DefRegistry
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",
gender="female",
default_body_parts=registry.new_default_body_parts("human"),
**kwargs,
)
def test_blood_label_defaults_to_blood(registry):
human = make_human(registry)
assert human.blood_label(registry) == "Blood"
def test_factions_default_to_an_empty_list(registry):
human = make_human(registry)
assert human.factions == []
def test_factions_can_be_set_at_construction(registry):
human = make_human(registry, factions=["player", "crew"])
assert human.factions == ["player", "crew"]
def test_blood_label_is_blood_with_no_species(registry):
assert Character().blood_label(registry) == "Blood"
def test_species_penalty_zero_by_default(registry):
human = make_human(registry)
assert human.species_penalty("sleight_of_hand", registry) == 0.0
def test_species_penalty_zero_with_no_species(registry):
assert Character().species_penalty("sleight_of_hand", registry) == 0.0
def test_hydraulibot_blood_label_is_synthmuscle_fluid(registry):
hydraulibot = Character(species_id="hydraulibot", default_body_parts=registry.new_default_body_parts("hydraulibot"))
assert hydraulibot.blood_label(registry) == "Synthmuscle-Fluid"
def test_motorbot_has_an_innate_sleight_of_hand_penalty(registry):
motorbot = Character(species_id="motorbot", default_body_parts=registry.new_default_body_parts("motorbot"))
assert motorbot.species_penalty("sleight_of_hand", registry) == pytest.approx(0.4)
def test_motorbot_innate_penalty_reduces_total_check_penalty_even_when_healthy(registry):
motorbot = Character(species_id="motorbot", default_body_parts=registry.new_default_body_parts("motorbot"))
# fully healthy - only the innate species penalty should show up
assert motorbot.total_check_penalty("sleight_of_hand", registry) == pytest.approx(0.4)
def test_motorbot_has_no_innate_penalty_for_an_unrelated_check(registry):
motorbot = Character(species_id="motorbot", default_body_parts=registry.new_default_body_parts("motorbot"))
assert motorbot.species_penalty("perception", registry) == 0.0
def test_blood_status_thresholds(registry):
species = registry.get_species_def("human")
character = make_human(registry, blood_percentage=100.0)
assert character.blood_status(species) == "normal"
character.blood_percentage = 50.0
assert character.blood_status(species) == "danger"
character.blood_percentage = 10.0
assert character.blood_status(species) == "critical"
def test_bio_defaults_to_zeroed_dnd_skills(registry):
character = make_human(registry)
assert character.bio["stealth"] == 0
assert character.bio["athletics"] == 0
assert set(character.bio.keys()) >= {"perception", "insight", "acrobatics"}
def test_mental_stats_optional_by_default(registry):
character = make_human(registry)
assert character.mental_stats is None
npc = make_human(registry, mental_stats={"insanity": 0.0})
assert npc.mental_stats == {"insanity": 0.0}
def test_skill_penalty_zero_when_all_parts_healthy(registry):
character = make_human(registry)
assert character.skill_penalty("acrobatics", registry) == 0.0
def test_skill_penalty_full_when_relevant_parts_missing(registry):
character = make_human(registry)
character.body_parts = [
BodyPart("left_leg", "human_left_leg", removed=True),
BodyPart("right_leg", "human_right_leg", removed=True),
]
# acrobatics is weighted only on the legs (0.5 each) -> fully lost when both are gone
assert character.skill_penalty("acrobatics", registry) == pytest.approx(1.0)
# athletics also draws from torso/arms, which are untouched -> partial loss
penalty = character.skill_penalty("athletics", registry)
assert 0.0 < penalty < 1.0
def test_skill_penalty_scales_with_integrity_not_just_removal(registry):
character = make_human(registry)
character.body_parts = [BodyPart("head", "human_head", integrity=50.0)]
penalty = character.skill_penalty("perception", registry)
assert penalty == pytest.approx(0.5)
def test_get_body_part_returns_none_when_explicitly_removed(registry):
character = make_human(registry)
character.body_parts = [BodyPart("left_arm", "human_left_arm", removed=True)]
assert character.get_body_part("left_arm") is None
assert character.get_body_part("right_arm") is not None # still falls back to default
def test_get_clothing_falls_back_to_default_like_body_parts(registry):
character = make_human(
registry,
default_clothing=registry.new_default_clothing("player"),
)
assert character.get_clothing("back").item_def_id == "backpack_basic"
def test_get_clothing_override_beats_default_and_removed_returns_none(registry):
character = make_human(
registry,
clothing=[ClothingPiece("torso", "leather_jacket")],
default_clothing=registry.new_default_clothing("player"),
)
assert character.get_clothing("torso").item_def_id == "leather_jacket"
assert character.get_clothing("back").item_def_id == "backpack_basic" # still falls back
character.clothing.append(ClothingPiece("back", "backpack_basic", removed=True))
assert character.get_clothing("back") is None
def test_equipped_backpack_grants_a_lazily_created_inventory(registry):
character = make_human(
registry,
default_clothing=registry.new_default_clothing("player"),
)
assert character.get_clothing("back").inventory is None
inventory = character.get_equipped_inventory("back", registry)
assert (inventory.width, inventory.height) == (4, 4)
# same instance on repeated access, and matches what's stored on the clothing piece
assert character.get_equipped_inventory("back", registry) is inventory
assert character.get_clothing("back").inventory is inventory
def test_equipped_inventory_is_none_for_non_container_clothing(registry):
character = make_human(registry, clothing=[ClothingPiece("torso", "leather_jacket")])
assert character.get_equipped_inventory("torso", registry) is None