122 lines
4.4 KiB
Python
122 lines
4.4 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_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
|