adding tests
This commit is contained in:
@@ -80,9 +80,9 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||
size: Mapped[str] = mapped_column(nullable=False, default="medium")
|
||||
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})
|
||||
_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})
|
||||
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})
|
||||
swim_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||
|
||||
_traits = relationship(
|
||||
"AncestryTraitMap", init=False, uselist=True, cascade="all,delete,delete-orphan", lazy="immediate"
|
||||
@@ -92,16 +92,8 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||
def traits(self):
|
||||
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):
|
||||
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)
|
||||
if not self._traits:
|
||||
self._traits = [mapping]
|
||||
@@ -110,23 +102,16 @@ class Ancestry(BaseObject, ModifierMixin):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class AncestryTrait(BaseObject, ModifierMixin):
|
||||
"""
|
||||
A trait granted to a character via its Ancestry.
|
||||
"""
|
||||
|
||||
__tablename__ = "ancestry_trait"
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(String(collation="NOCASE"), nullable=False, unique=True)
|
||||
description: Mapped[Text] = mapped_column(Text, default="")
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class CharacterSkillMap(BaseObject):
|
||||
__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)
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.character.name}, {self.character_class.name}, level {self.level}"
|
||||
|
||||
|
||||
class CharacterClassAttributeMap(BaseObject):
|
||||
__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}
|
||||
)
|
||||
|
||||
_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})
|
||||
|
||||
class_map = relationship("CharacterClassMap", cascade="all,delete,delete-orphan")
|
||||
@@ -248,11 +230,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||
def expertise_bonus(self):
|
||||
return 2 * self.proficiency_bonus
|
||||
|
||||
@property
|
||||
def proficiencies(self):
|
||||
unified = {}
|
||||
unified.update(**self._proficiencies)
|
||||
|
||||
@property
|
||||
def modifiers(self):
|
||||
unified = {}
|
||||
@@ -262,10 +239,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||
unified.update(**super().modifiers)
|
||||
return unified
|
||||
|
||||
@property
|
||||
def check_modifiers(self):
|
||||
return [self.check_modifier(skill) for skill in self.skills]
|
||||
|
||||
@property
|
||||
def classes(self):
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
def size(self):
|
||||
@@ -332,11 +308,13 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||
def absorbs(self, damage_type: DamageType):
|
||||
return self.defense(damage_type) == Defenses.absorbs
|
||||
|
||||
def conditions(self):
|
||||
return [self._apply_modifiers(f"conditions.{name}") for name in Conditions]
|
||||
def condition(self, condition):
|
||||
if not self.immune(condition):
|
||||
return self._apply_modifiers(condition, False)
|
||||
return False
|
||||
|
||||
def condition(self, condition_name: str):
|
||||
return self._apply_modifiers(f"conditions.{condition_name}", False)
|
||||
def add_condition(self, condition):
|
||||
self.add_modifier(Modifier(condition, target=condition, new_value=True))
|
||||
|
||||
def defense(self, damage_type: DamageType):
|
||||
return self._apply_modifiers(damage_type, None)
|
||||
@@ -431,8 +409,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
||||
return True
|
||||
|
||||
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
|
||||
exists = False
|
||||
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.temp_hit_points = 0
|
||||
return
|
||||
|
||||
def spend_hit_die(self, die):
|
||||
die.spent = True
|
||||
|
||||
def reset_hit_die(self, die):
|
||||
die.spent = False
|
||||
|
||||
def __after_insert__(self, session):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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]:
|
||||
return False
|
||||
@@ -155,7 +157,7 @@ class ModifierMixin:
|
||||
|
||||
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
|
||||
self.modifier_map = [mapping for mapping in self.modifier_map if mapping.modifier != modifier]
|
||||
return True
|
||||
@@ -175,7 +177,7 @@ class ModifierMixin:
|
||||
for key in col.info.keys():
|
||||
if key.startswith("modifiable"):
|
||||
return col
|
||||
return None
|
||||
return None # pragma: no cover
|
||||
|
||||
def _get_modifiable_base(self, attr_name: str) -> object:
|
||||
"""
|
||||
@@ -217,7 +219,7 @@ class ModifierMixin:
|
||||
if modifier.relative_value is not None:
|
||||
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:
|
||||
"""
|
||||
@@ -258,7 +260,7 @@ class ModifierMixin:
|
||||
"""
|
||||
col = self._modifiable_column(attr_name)
|
||||
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)
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
|
||||
Reference in New Issue
Block a user