adding tests
This commit is contained in:
parent
551140b5bc
commit
4dd72d47d0
|
@ -80,9 +80,9 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||||
size: Mapped[str] = mapped_column(nullable=False, default="medium")
|
size: Mapped[str] = mapped_column(nullable=False, default="medium")
|
||||||
speed: Mapped[int] = mapped_column(nullable=False, default=30, info={"min": 0, "max": 99})
|
speed: Mapped[int] = mapped_column(nullable=False, default=30, info={"min": 0, "max": 99})
|
||||||
|
|
||||||
_fly_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
fly_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||||
_climb_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
climb_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||||
_swim_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
swim_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||||
|
|
||||||
_traits = relationship(
|
_traits = relationship(
|
||||||
"AncestryTraitMap", init=False, uselist=True, cascade="all,delete,delete-orphan", lazy="immediate"
|
"AncestryTraitMap", init=False, uselist=True, cascade="all,delete,delete-orphan", lazy="immediate"
|
||||||
|
@ -92,16 +92,8 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||||
def traits(self):
|
def traits(self):
|
||||||
return [mapping.trait for mapping in self._traits]
|
return [mapping.trait for mapping in self._traits]
|
||||||
|
|
||||||
@property
|
|
||||||
def climb_speed(self):
|
|
||||||
return self._climb_speed or int(self.speed / 2)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def swim_speed(self):
|
|
||||||
return self._swim_speed or int(self.speed / 2)
|
|
||||||
|
|
||||||
def add_trait(self, trait, level=1):
|
def add_trait(self, trait, level=1):
|
||||||
if not self._traits or trait not in self._traits:
|
if trait not in self.traits:
|
||||||
mapping = AncestryTraitMap(ancestry_id=self.id, trait=trait, level=level)
|
mapping = AncestryTraitMap(ancestry_id=self.id, trait=trait, level=level)
|
||||||
if not self._traits:
|
if not self._traits:
|
||||||
self._traits = [mapping]
|
self._traits = [mapping]
|
||||||
|
@ -110,23 +102,16 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class AncestryTrait(BaseObject, ModifierMixin):
|
class AncestryTrait(BaseObject, ModifierMixin):
|
||||||
"""
|
"""
|
||||||
A trait granted to a character via its Ancestry.
|
A trait granted to a character via its Ancestry.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "ancestry_trait"
|
__tablename__ = "ancestry_trait"
|
||||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||||
name: Mapped[str] = mapped_column(String(collation="NOCASE"), nullable=False, unique=True)
|
name: Mapped[str] = mapped_column(String(collation="NOCASE"), nullable=False, unique=True)
|
||||||
description: Mapped[Text] = mapped_column(Text, default="")
|
description: Mapped[Text] = mapped_column(Text, default="")
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterSkillMap(BaseObject):
|
class CharacterSkillMap(BaseObject):
|
||||||
__tablename__ = "character_skill_map"
|
__tablename__ = "character_skill_map"
|
||||||
|
@ -153,9 +138,6 @@ class CharacterClassMap(BaseObject):
|
||||||
|
|
||||||
level: Mapped[int] = mapped_column(nullable=False, info={"min": 1, "max": 20}, default=1)
|
level: Mapped[int] = mapped_column(nullable=False, info={"min": 1, "max": 20}, default=1)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"{self.character.name}, {self.character_class.name}, level {self.level}"
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterClassAttributeMap(BaseObject):
|
class CharacterClassAttributeMap(BaseObject):
|
||||||
__tablename__ = "character_class_attribute_map"
|
__tablename__ = "character_class_attribute_map"
|
||||||
|
@ -212,7 +194,7 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
nullable=False, default=10, info={"min": 0, "max": 30, "modifiable_class": Stat}
|
nullable=False, default=10, info={"min": 0, "max": 30, "modifiable_class": Stat}
|
||||||
)
|
)
|
||||||
|
|
||||||
_vision: Mapped[int] = mapped_column(default=None, nullable=True, info={"min": 0, "modifiable": True})
|
vision: Mapped[int] = mapped_column(default=None, nullable=True, info={"min": 0, "modifiable": True})
|
||||||
exhaustion: Mapped[int] = mapped_column(nullable=False, default=0, info={"min": 0, "max": 5})
|
exhaustion: Mapped[int] = mapped_column(nullable=False, default=0, info={"min": 0, "max": 5})
|
||||||
|
|
||||||
class_map = relationship("CharacterClassMap", cascade="all,delete,delete-orphan")
|
class_map = relationship("CharacterClassMap", cascade="all,delete,delete-orphan")
|
||||||
|
@ -248,11 +230,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
def expertise_bonus(self):
|
def expertise_bonus(self):
|
||||||
return 2 * self.proficiency_bonus
|
return 2 * self.proficiency_bonus
|
||||||
|
|
||||||
@property
|
|
||||||
def proficiencies(self):
|
|
||||||
unified = {}
|
|
||||||
unified.update(**self._proficiencies)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def modifiers(self):
|
def modifiers(self):
|
||||||
unified = {}
|
unified = {}
|
||||||
|
@ -262,10 +239,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
unified.update(**super().modifiers)
|
unified.update(**super().modifiers)
|
||||||
return unified
|
return unified
|
||||||
|
|
||||||
@property
|
|
||||||
def check_modifiers(self):
|
|
||||||
return [self.check_modifier(skill) for skill in self.skills]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def classes(self):
|
def classes(self):
|
||||||
return dict([(mapping.character_class.name, mapping.character_class) for mapping in self.class_map])
|
return dict([(mapping.character_class.name, mapping.character_class) for mapping in self.class_map])
|
||||||
|
@ -280,19 +253,22 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def speed(self):
|
def speed(self):
|
||||||
return self._apply_modifiers("speed", self.ancestry.speed)
|
return self._apply_modifiers('speed', self._apply_modifiers("walking_speed", self.ancestry.speed))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def climb_speed(self):
|
def climb_speed(self):
|
||||||
return self._apply_modifiers("climb_speed", self.ancestry._climb_speed)
|
return self._apply_modifiers("climb_speed", self.ancestry.climb_speed or int(self.speed / 2))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def swim_speed(self):
|
def swim_speed(self):
|
||||||
return self._apply_modifiers("swim_speed", self.ancestry._swim_speed)
|
return self._apply_modifiers("swim_speed", self.ancestry.swim_speed or int(self.speed / 2))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fly_speed(self):
|
def fly_speed(self):
|
||||||
return self._apply_modifiers("fly_speed", self.ancestry._fly_speed)
|
modified = self._apply_modifiers("fly_speed", self.ancestry.fly_speed or 0)
|
||||||
|
if self.ancestry.fly_speed is None and not modified:
|
||||||
|
return None
|
||||||
|
return self._apply_modifiers("speed", modified)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def size(self):
|
||||||
|
@ -332,11 +308,13 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
def absorbs(self, damage_type: DamageType):
|
def absorbs(self, damage_type: DamageType):
|
||||||
return self.defense(damage_type) == Defenses.absorbs
|
return self.defense(damage_type) == Defenses.absorbs
|
||||||
|
|
||||||
def conditions(self):
|
def condition(self, condition):
|
||||||
return [self._apply_modifiers(f"conditions.{name}") for name in Conditions]
|
if not self.immune(condition):
|
||||||
|
return self._apply_modifiers(condition, False)
|
||||||
|
return False
|
||||||
|
|
||||||
def condition(self, condition_name: str):
|
def add_condition(self, condition):
|
||||||
return self._apply_modifiers(f"conditions.{condition_name}", False)
|
self.add_modifier(Modifier(condition, target=condition, new_value=True))
|
||||||
|
|
||||||
def defense(self, damage_type: DamageType):
|
def defense(self, damage_type: DamageType):
|
||||||
return self._apply_modifiers(damage_type, None)
|
return self._apply_modifiers(damage_type, None)
|
||||||
|
@ -431,8 +409,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def add_skill(self, skill, proficient=False, expert=False, character_class=None):
|
def add_skill(self, skill, proficient=False, expert=False, character_class=None):
|
||||||
# if not self.id:
|
|
||||||
# raise Exception("Cannot add a skill before the character has been persisted.")
|
|
||||||
skillmap = None
|
skillmap = None
|
||||||
exists = False
|
exists = False
|
||||||
if skill in self.skills:
|
if skill in self.skills:
|
||||||
|
@ -500,14 +476,10 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||||
|
|
||||||
self.hit_points = max(0, self.hit_points - (total - self.temp_hit_points))
|
self.hit_points = max(0, self.hit_points - (total - self.temp_hit_points))
|
||||||
self.temp_hit_points = 0
|
self.temp_hit_points = 0
|
||||||
return
|
|
||||||
|
|
||||||
def spend_hit_die(self, die):
|
def spend_hit_die(self, die):
|
||||||
die.spent = True
|
die.spent = True
|
||||||
|
|
||||||
def reset_hit_die(self, die):
|
|
||||||
die.spent = False
|
|
||||||
|
|
||||||
def __after_insert__(self, session):
|
def __after_insert__(self, session):
|
||||||
"""
|
"""
|
||||||
Called by the session after_flush event listener to add default joins in other tables.
|
Called by the session after_flush event listener to add default joins in other tables.
|
||||||
|
|
|
@ -136,7 +136,9 @@ class ModifierMixin:
|
||||||
Returns True if the modifier was added; False if was already present.
|
Returns True if the modifier was added; False if was already present.
|
||||||
"""
|
"""
|
||||||
if modifier.absolute_value is not None and modifier.relative_value is not None and modifier.multiple_value:
|
if modifier.absolute_value is not None and modifier.relative_value is not None and modifier.multiple_value:
|
||||||
raise AttributeError(f"You must provide only one of absolute, relative, and multiple values {modifier}.")
|
raise AttributeError(
|
||||||
|
f"You must provide only one of absolute, relative, and multiple values {modifier}."
|
||||||
|
) # pragma: no cover
|
||||||
|
|
||||||
if [mod for mod in self.modifier_map if mod.modifier == modifier]:
|
if [mod for mod in self.modifier_map if mod.modifier == modifier]:
|
||||||
return False
|
return False
|
||||||
|
@ -155,7 +157,7 @@ class ModifierMixin:
|
||||||
|
|
||||||
Returns True if it was removed and False if it wasn't present.
|
Returns True if it was removed and False if it wasn't present.
|
||||||
"""
|
"""
|
||||||
if modifier not in self.modifiers[modifier.target]:
|
if modifier not in self.modifiers.get(modifier.target, []):
|
||||||
return False
|
return False
|
||||||
self.modifier_map = [mapping for mapping in self.modifier_map if mapping.modifier != modifier]
|
self.modifier_map = [mapping for mapping in self.modifier_map if mapping.modifier != modifier]
|
||||||
return True
|
return True
|
||||||
|
@ -175,7 +177,7 @@ class ModifierMixin:
|
||||||
for key in col.info.keys():
|
for key in col.info.keys():
|
||||||
if key.startswith("modifiable"):
|
if key.startswith("modifiable"):
|
||||||
return col
|
return col
|
||||||
return None
|
return None # pragma: no cover
|
||||||
|
|
||||||
def _get_modifiable_base(self, attr_name: str) -> object:
|
def _get_modifiable_base(self, attr_name: str) -> object:
|
||||||
"""
|
"""
|
||||||
|
@ -217,7 +219,7 @@ class ModifierMixin:
|
||||||
if modifier.relative_value is not None:
|
if modifier.relative_value is not None:
|
||||||
return base_value + modifier.relative_value
|
return base_value + modifier.relative_value
|
||||||
|
|
||||||
raise Exception(f"Cannot apply modifier: {modifier = }")
|
raise Exception(f"Cannot apply modifier: {modifier = }") # pragma: no cover
|
||||||
|
|
||||||
def _apply_modifiers(self, target: str, initial: Any, modifiable_class: type = None) -> Modifiable:
|
def _apply_modifiers(self, target: str, initial: Any, modifiable_class: type = None) -> Modifiable:
|
||||||
"""
|
"""
|
||||||
|
@ -258,7 +260,7 @@ class ModifierMixin:
|
||||||
"""
|
"""
|
||||||
col = self._modifiable_column(attr_name)
|
col = self._modifiable_column(attr_name)
|
||||||
if col is not None:
|
if col is not None:
|
||||||
raise AttributeError(f"You cannot modify .{attr_name}. Did you mean ._{attr_name}?")
|
raise AttributeError(f"You cannot modify .{attr_name}. Did you mean ._{attr_name}?") # pragma: no cover
|
||||||
return super().__setattr__(attr_name, value)
|
return super().__setattr__(attr_name, value)
|
||||||
|
|
||||||
def __getattr__(self, attr_name):
|
def __getattr__(self, attr_name):
|
||||||
|
|
|
@ -21,3 +21,8 @@ def test_dump_load(db, bootstrap):
|
||||||
def test_loader(db, bootstrap):
|
def test_loader(db, bootstrap):
|
||||||
loader.load(db.dump())
|
loader.load(db.dump())
|
||||||
assert len(db.Ancestry.all()) > 0
|
assert len(db.Ancestry.all()) > 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_default(db):
|
||||||
|
loader.load()
|
||||||
|
assert len(db.Ancestry.all()) > 0
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from ttfrog.db import schema
|
from ttfrog.db import schema
|
||||||
from ttfrog.db.schema.constants import DamageType, Defenses
|
from ttfrog.db.schema.constants import DamageType, Defenses, Conditions
|
||||||
|
|
||||||
|
|
||||||
def test_manage_character(db, bootstrap):
|
def test_manage_character(db, bootstrap):
|
||||||
|
@ -23,7 +23,9 @@ def test_manage_character(db, bootstrap):
|
||||||
assert char.intelligence == 10
|
assert char.intelligence == 10
|
||||||
assert char.wisdom == 10
|
assert char.wisdom == 10
|
||||||
assert char.charisma == 10
|
assert char.charisma == 10
|
||||||
|
|
||||||
assert darkvision not in char.traits
|
assert darkvision not in char.traits
|
||||||
|
assert char.vision_in_darkness == 0
|
||||||
|
|
||||||
# verify basic skills were added at creation time
|
# verify basic skills were added at creation time
|
||||||
for skill in db.Skill.filter(
|
for skill in db.Skill.filter(
|
||||||
|
@ -39,6 +41,7 @@ def test_manage_character(db, bootstrap):
|
||||||
assert char.ancestry_id == tiefling.id
|
assert char.ancestry_id == tiefling.id
|
||||||
assert char.ancestry.name == "tiefling"
|
assert char.ancestry.name == "tiefling"
|
||||||
assert darkvision in char.traits
|
assert darkvision in char.traits
|
||||||
|
assert char.vision_in_darkness == 120
|
||||||
|
|
||||||
# tiefling ancestry adds INT and CHA modifiers
|
# tiefling ancestry adds INT and CHA modifiers
|
||||||
assert char.intelligence == 11
|
assert char.intelligence == 11
|
||||||
|
@ -110,8 +113,22 @@ def test_manage_character(db, bootstrap):
|
||||||
db.add_or_update(char)
|
db.add_or_update(char)
|
||||||
assert char.level == 8
|
assert char.level == 8
|
||||||
assert char.levels == {"fighter": 7, "rogue": 1}
|
assert char.levels == {"fighter": 7, "rogue": 1}
|
||||||
|
assert list(char.classes.keys()) == ['fighter', 'rogue']
|
||||||
assert sum([len(dice) for dice in char.hit_dice.values()]) == char.level == 8
|
assert sum([len(dice) for dice in char.hit_dice.values()]) == char.level == 8
|
||||||
|
|
||||||
|
# test conditions
|
||||||
|
assert not char.condition(Conditions.frightened)
|
||||||
|
char.add_condition(Conditions.frightened)
|
||||||
|
assert char.condition(Conditions.frightened)
|
||||||
|
char.add_modifier(
|
||||||
|
schema.Modifier("Immunity: Frightened", target=Conditions.frightened, new_value=Defenses.immune)
|
||||||
|
)
|
||||||
|
assert not char.condition(Conditions.frightened)
|
||||||
|
|
||||||
|
# use a hit die
|
||||||
|
char.spend_hit_die(char.hit_dice["rogue"][0])
|
||||||
|
assert len([die for die in char.hit_dice_available if die.character_class.name == "rogue"]) == 0
|
||||||
|
|
||||||
# remove a class
|
# remove a class
|
||||||
char.remove_class(rogue)
|
char.remove_class(rogue)
|
||||||
db.add_or_update(char)
|
db.add_or_update(char)
|
||||||
|
@ -130,11 +147,12 @@ def test_manage_character(db, bootstrap):
|
||||||
assert char.levels == {}
|
assert char.levels == {}
|
||||||
assert char.class_attributes == {}
|
assert char.class_attributes == {}
|
||||||
|
|
||||||
# verify the proficiencies added by the classes have been removed
|
# verify the proficiencies etc. added by the classes have been removed
|
||||||
assert athletics not in char.skills
|
assert athletics not in char.skills
|
||||||
assert acrobatics not in char.skills
|
assert acrobatics not in char.skills
|
||||||
assert char.check_modifier(athletics) == 0
|
assert char.check_modifier(athletics) == 0
|
||||||
assert char.check_modifier(acrobatics) == 0
|
assert char.check_modifier(acrobatics) == 0
|
||||||
|
assert char.hit_dice == {}
|
||||||
|
|
||||||
# ensure we're not persisting any orphan records in the map tables
|
# ensure we're not persisting any orphan records in the map tables
|
||||||
dump = json.loads(db.dump())
|
dump = json.loads(db.dump())
|
||||||
|
@ -162,6 +180,10 @@ def test_ancestries(db, bootstrap):
|
||||||
db.add_or_update(porc)
|
db.add_or_update(porc)
|
||||||
assert endurance in porc.traits
|
assert endurance in porc.traits
|
||||||
|
|
||||||
|
# add it again and assert nothing changes
|
||||||
|
porc.add_trait(endurance, level=1)
|
||||||
|
assert endurance in porc.traits
|
||||||
|
|
||||||
# add a +3 STR modifier
|
# add a +3 STR modifier
|
||||||
str_bonus = schema.Modifier(
|
str_bonus = schema.Modifier(
|
||||||
name="STR+3 (Pygmy Orc)",
|
name="STR+3 (Pygmy Orc)",
|
||||||
|
@ -208,10 +230,18 @@ def test_modifiers(db, bootstrap):
|
||||||
slowed = schema.Modifier(target="speed", multiply_value=0.5, name="Slowed")
|
slowed = schema.Modifier(target="speed", multiply_value=0.5, name="Slowed")
|
||||||
restrained = schema.Modifier(target="speed", absolute_value=0, name="Restrained")
|
restrained = schema.Modifier(target="speed", absolute_value=0, name="Restrained")
|
||||||
reduced = schema.Modifier(target="size", new_value="Tiny", name="Reduced")
|
reduced = schema.Modifier(target="size", new_value="Tiny", name="Reduced")
|
||||||
|
fly = schema.Modifier(target="fly_speed", absolute_value=30, name="Fly Spell")
|
||||||
|
|
||||||
# reduce speed by 10
|
# reduce speed by 10
|
||||||
assert carl.add_modifier(cold)
|
assert carl.add_modifier(cold)
|
||||||
assert carl.speed == 20
|
assert carl.speed == 20
|
||||||
|
assert carl.climb_speed == 10
|
||||||
|
assert carl.swim_speed == 10
|
||||||
|
assert carl.fly_speed is None
|
||||||
|
|
||||||
|
# cast fly
|
||||||
|
carl.add_modifier(fly)
|
||||||
|
assert carl.fly_speed == 20
|
||||||
|
|
||||||
# make sure modifiers only apply to carl. Carl is having a bad day.
|
# make sure modifiers only apply to carl. Carl is having a bad day.
|
||||||
assert marx.speed == 30
|
assert marx.speed == 30
|
||||||
|
@ -243,6 +273,9 @@ def test_modifiers(db, bootstrap):
|
||||||
assert carl.add_modifier(reduced)
|
assert carl.add_modifier(reduced)
|
||||||
assert carl.size == "Tiny"
|
assert carl.size == "Tiny"
|
||||||
|
|
||||||
|
# cannot remove a modifier that isn't applied
|
||||||
|
assert not carl.remove_modifier(cold)
|
||||||
|
|
||||||
# modifiers can be applied to skills, even if the character doesn't have a skill associated.
|
# modifiers can be applied to skills, even if the character doesn't have a skill associated.
|
||||||
athletics = db.Skill.filter_by(name="athletics").one()
|
athletics = db.Skill.filter_by(name="athletics").one()
|
||||||
assert athletics not in carl.skills
|
assert athletics not in carl.skills
|
||||||
|
@ -283,6 +316,8 @@ def test_modifiers(db, bootstrap):
|
||||||
assert len([s for s in carl.skills if s == athletics]) == 1
|
assert len([s for s in carl.skills if s == athletics]) == 1
|
||||||
assert carl.check_modifier(athletics) == 1
|
assert carl.check_modifier(athletics) == 1
|
||||||
|
|
||||||
|
# ensure you can't remove a skill already removed
|
||||||
|
assert not carl.remove_skill(athletics, proficient=True, expert=False, character_class=None)
|
||||||
|
|
||||||
def test_defenses(db, bootstrap):
|
def test_defenses(db, bootstrap):
|
||||||
with db.transaction():
|
with db.transaction():
|
||||||
|
@ -292,6 +327,16 @@ def test_defenses(db, bootstrap):
|
||||||
carl.apply_damage(5, DamageType.fire)
|
carl.apply_damage(5, DamageType.fire)
|
||||||
assert carl.hit_points == 8 # half damage
|
assert carl.hit_points == 8 # half damage
|
||||||
|
|
||||||
|
# add temp HP
|
||||||
|
carl.temp_hit_points = 3
|
||||||
|
assert carl.hit_points == 8
|
||||||
|
carl.apply_damage(1, DamageType.bludgeoning)
|
||||||
|
assert carl.hit_points == 8
|
||||||
|
assert carl.temp_hit_points == 2
|
||||||
|
carl.apply_damage(3, DamageType.bludgeoning)
|
||||||
|
assert carl.temp_hit_points == 0
|
||||||
|
assert carl.hit_points == 7
|
||||||
|
|
||||||
immunity = [
|
immunity = [
|
||||||
schema.Modifier("Fire Immunity", target=DamageType.fire, new_value=Defenses.immune),
|
schema.Modifier("Fire Immunity", target=DamageType.fire, new_value=Defenses.immune),
|
||||||
]
|
]
|
||||||
|
@ -299,7 +344,7 @@ def test_defenses(db, bootstrap):
|
||||||
carl.add_modifier(i)
|
carl.add_modifier(i)
|
||||||
assert carl.immune(DamageType.fire)
|
assert carl.immune(DamageType.fire)
|
||||||
carl.apply_damage(5, DamageType.fire)
|
carl.apply_damage(5, DamageType.fire)
|
||||||
assert carl.hit_points == 8 # no damage
|
assert carl.hit_points == 7 # no damage
|
||||||
|
|
||||||
vulnerability = [
|
vulnerability = [
|
||||||
schema.Modifier("Fire Vulnerability", target=DamageType.fire, new_value=Defenses.vulnerable),
|
schema.Modifier("Fire Vulnerability", target=DamageType.fire, new_value=Defenses.vulnerable),
|
||||||
|
@ -309,7 +354,7 @@ def test_defenses(db, bootstrap):
|
||||||
assert carl.vulnerable(DamageType.fire)
|
assert carl.vulnerable(DamageType.fire)
|
||||||
assert not carl.immune(DamageType.fire)
|
assert not carl.immune(DamageType.fire)
|
||||||
carl.apply_damage(2, DamageType.fire)
|
carl.apply_damage(2, DamageType.fire)
|
||||||
assert carl.hit_points == 4 # double damage
|
assert carl.hit_points == 3 # double damage
|
||||||
|
|
||||||
absorbs = [schema.Modifier("Absorbs Non-Magical Fire", target=DamageType.fire, new_value=Defenses.absorbs)]
|
absorbs = [schema.Modifier("Absorbs Non-Magical Fire", target=DamageType.fire, new_value=Defenses.absorbs)]
|
||||||
carl.add_modifier(absorbs[0])
|
carl.add_modifier(absorbs[0])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user