66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from engine.character import ClothingPiece, Character
|
|
from engine.defs import DefRegistry
|
|
from resources.logic.combat import defender_armor, perform_attack
|
|
|
|
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)
|
|
|
|
|
|
class FixedRng:
|
|
def __init__(self, roll: int, choice_value=None):
|
|
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_defender_armor_sums_worn_clothing(registry):
|
|
defender = make_human(registry, clothing=[ClothingPiece("torso", "leather_jacket")])
|
|
assert defender_armor(defender, registry) == pytest.approx(3.0) # leather_jacket's armor_reduction
|
|
|
|
|
|
def test_unarmored_defender_has_zero_armor(registry):
|
|
defender = make_human(registry)
|
|
assert defender_armor(defender, registry) == 0.0
|
|
|
|
|
|
def test_armor_reduces_damage_taken(registry):
|
|
attacker = make_human(registry)
|
|
attacker.bio["athletics"] = 10
|
|
|
|
unarmored = make_human(registry)
|
|
armored = make_human(registry, clothing=[ClothingPiece("torso", "leather_jacket")])
|
|
|
|
unarmored_result = perform_attack(attacker, unarmored, registry, rng=FixedRng(roll=15, choice_value="torso"))
|
|
armored_result = perform_attack(attacker, armored, registry, rng=FixedRng(roll=15, choice_value="torso"))
|
|
|
|
assert armored_result.armor_reduction == pytest.approx(3.0)
|
|
assert armored_result.damage == pytest.approx(max(0.0, unarmored_result.damage - 3.0))
|
|
|
|
|
|
def test_heavy_armor_can_absorb_a_hit_entirely(registry):
|
|
attacker = make_human(registry)
|
|
defender = make_human(registry, clothing=[ClothingPiece("torso", "leather_jacket")])
|
|
|
|
# a roll that only barely clears the DC: margin (1) is smaller than armor_reduction (3)
|
|
result = perform_attack(attacker, defender, registry, rng=FixedRng(roll=11, choice_value="torso"))
|
|
assert result.hit is True # the check itself succeeded...
|
|
assert result.damage == 0.0 # ...but armor soaked up all of it
|
|
assert defender.get_body_part("torso").integrity == pytest.approx(100.0)
|