fix tests

This commit is contained in:
evilchili 2024-06-30 16:09:20 -07:00
parent a8bb6de008
commit 68251ff4e9
5 changed files with 59 additions and 47 deletions

View File

@ -9,7 +9,7 @@ packages = [
]
[tool.poetry.dependencies]
python = "^3.10"
python = "^3.11"
python-dotenv = "^0.21.0"
typer = "^0.9.0"
rich = "^13.7.0"

View File

@ -1,5 +1,6 @@
from .character import *
from .classes import *
from .constants import *
from .log import *
from .modifiers import *
from .skill import *

View File

@ -6,6 +6,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
from ttfrog.db.base import BaseObject, SlugMixin
from ttfrog.db.schema.classes import CharacterClass, ClassAttribute
from ttfrog.db.schema.constants import Conditions, DamageType, Defenses
from ttfrog.db.schema.modifiers import Modifier, ModifierMixin, Stat
from ttfrog.db.schema.skill import Skill
@ -212,6 +213,7 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
)
_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")
class_list = association_proxy("class_map", "id", creator=class_map_creator)
@ -272,6 +274,10 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
def traits(self):
return self.ancestry.traits
@property
def initiative(self):
return self._apply_modifiers("initiative", self.dexterity.bonus)
@property
def speed(self):
return self._apply_modifiers("speed", self.ancestry.speed)
@ -294,7 +300,7 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
@property
def vision_in_darkness(self):
return self.apply_modifiers("vision_in_darkness", self.vision if self.vision is not None else 0)
return self._apply_modifiers("vision_in_darkness", self.vision if self.vision is not None else 0)
@property
def level(self):
@ -314,23 +320,26 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
return None
return mapping[0]
def immune(self, damage_type: str, magical: bool = False):
return self.defense(damage_type, magical) == 'immune'
def immune(self, damage_type: DamageType):
return self.defense(damage_type) == Defenses.immune
def resistant(self, damage_type: str, magical: bool = False):
return self.defense(damage_type, magical) == 'resistant'
def resistant(self, damage_type: DamageType):
return self.defense(damage_type) == Defenses.resistant.value
def vulnerable(self, damage_type: str, magical: bool = False):
return self.defense(damage_type, magical) == 'vulnerable'
def vulnerable(self, damage_type: DamageType):
return self.defense(damage_type) == Defenses.vulnerable
def absorbs(self, damage_type: str, magical: bool = False):
return self.defense(damage_type, magical) == 'absorbs'
def absorbs(self, damage_type: DamageType):
return self.defense(damage_type) == Defenses.absorbs
def defense(self, damage_type: str, magical: bool = False):
attr_name = damage_type
if magical:
attr_name = f"magical_{attr_name}"
return self._apply_modifiers(f"defenses.{attr_name}", None)
def conditions(self):
return [self._apply_modifiers(f"conditions.{name}") for name in Conditions]
def condition(self, condition_name: str):
return self._apply_modifiers(f"conditions.{condition_name}", False)
def defense(self, damage_type: DamageType):
return self._apply_modifiers(damage_type, None)
def check_modifier(self, skill: Skill, save: bool = False):
# if the skill is not assigned, but we have modifiers, apply them to zero.
@ -387,13 +396,13 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
self._hit_dice.append(HitDie(character_id=self.id, character_class_id=newclass.id))
def remove_class(self, target):
self.class_map = [m for m in self.class_map if m.character_class != target]
for mapping in self.character_class_attribute_map:
if mapping.character_class == target:
self.remove_class_attribute(mapping.class_attribute)
for skill in target.skills:
self.remove_skill(skill, proficient=True, expert=False, character_class=target)
self._hit_dice = [die for die in self._hit_dice if die.character_class != target]
self.class_map = [m for m in self.class_map if m.character_class != target]
def remove_class_attribute(self, attribute):
self.character_class_attribute_map = [
@ -474,15 +483,15 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
def apply_healing(self, value: int):
self.hit_points = min(self.hit_points + value, self._max_hit_points)
def apply_damage(self, value: int, damage_type: str, magical=False):
def apply_damage(self, value: int, damage_type: DamageType):
total = value
if self.absorbs(damage_type, magical):
if self.absorbs(damage_type):
return self.apply_healing(total)
if self.immune(damage_type, magical):
if self.immune(damage_type):
return
if self.resistant(damage_type, magical):
if self.resistant(damage_type):
total = int(value / 2)
elif self.vulnerable(damage_type, magical):
elif self.vulnerable(damage_type):
total = value * 2
if total <= self.temp_hit_points:

View File

@ -6,6 +6,7 @@ import pytest
from ttfrog.db import schema
from ttfrog.db.manager import db as _db
from ttfrog.db.schema.constants import DamageType, Defenses
FIXTURE_PATH = Path(__file__).parent / "fixtures"
@ -49,14 +50,12 @@ def bootstrap(db):
darkvision.add_modifier(schema.Modifier("Darkvision", target="vision_in_darkness", absolute_value=120))
tiefling.add_trait(darkvision)
# resistant to both magical and non-magical sources of fire
# resistant to fire
infernal_origin = schema.AncestryTrait("Infernal Origin")
infernal_origin.add_modifier(schema.Modifier("Infernal Origin", target="defenses.fire", new_value="resistant"))
infernal_origin.add_modifier(
schema.Modifier("Infernal Origin", target="defenses.magical_fire", new_value="resistant")
schema.Modifier("Infernal Origin", target=DamageType.fire, new_value=Defenses.resistant)
)
tiefling.add_trait(infernal_origin)
db.add_or_update(tiefling)
dragonborn = schema.Ancestry("dragonborn")
dragonborn.add_trait(darkvision)

View File

@ -1,6 +1,7 @@
import json
from ttfrog.db import schema
from ttfrog.db.schema.constants import DamageType, Defenses
def test_manage_character(db, bootstrap):
@ -96,6 +97,14 @@ def test_manage_character(db, bootstrap):
assert char.check_modifier(athletics) == char.proficiency_bonus + char.strength.bonus == 3
assert char.check_modifier(acrobatics) == char.proficiency_bonus + char.dexterity.bonus == 3
# assert dexterity bonus apply to initiative
char._dexterity = 17
assert char.dexterity.bonus == 3
assert char.initiative == char.dexterity.bonus == 3
char.add_modifier(schema.Modifier("+1 initiative", target="initiative", relative_value=1))
assert char.initiative == 4
char._dexterity = 10
# multiclass
char.add_class(rogue, level=1)
db.add_or_update(char)
@ -279,45 +288,39 @@ def test_defenses(db, bootstrap):
with db.transaction():
tiefling = db.Ancestry.filter_by(name="tiefling").one()
carl = schema.Character(name="Carl", ancestry=tiefling)
assert carl.resistant("fire", magical=False)
assert carl.resistant("fire", magical=True)
carl.apply_damage(5, "fire", magical=True)
assert carl.resistant(DamageType.fire)
carl.apply_damage(5, DamageType.fire)
assert carl.hit_points == 8 # half damage
immunity = [
schema.Modifier("Fire Immunity", target="defenses.fire", new_value="immune"),
schema.Modifier("Fire Immunity", target="defenses.magical_fire", new_value="immune")
schema.Modifier("Fire Immunity", target=DamageType.fire, new_value=Defenses.immune),
]
for i in immunity:
carl.add_modifier(i)
assert carl.immune("fire")
carl.apply_damage(5, "fire", magical=True)
carl.apply_damage(5, "fire", magical=False)
assert carl.immune(DamageType.fire)
carl.apply_damage(5, DamageType.fire)
assert carl.hit_points == 8 # no damage
vulnerability = [
schema.Modifier("Fire Vulnerability", target="defenses.fire", new_value="vulnerable"),
schema.Modifier("Fire Vulnerability", target="defenses.magical_fire", new_value="vulnerable")
schema.Modifier("Fire Vulnerability", target=DamageType.fire, new_value=Defenses.vulnerable),
]
for i in vulnerability:
carl.add_modifier(i)
assert carl.vulnerable("fire")
assert not carl.immune("fire")
carl.apply_damage(2, "fire", magical=True)
assert carl.vulnerable(DamageType.fire)
assert not carl.immune(DamageType.fire)
carl.apply_damage(2, DamageType.fire)
assert carl.hit_points == 4 # double damage
absorbs = [
schema.Modifier("Absorbs Non-Magical Fire", target="defenses.fire", new_value="absorbs"),
]
absorbs = [schema.Modifier("Absorbs Non-Magical Fire", target=DamageType.fire, new_value=Defenses.absorbs)]
carl.add_modifier(absorbs[0])
carl.apply_damage(20, "fire", magical=False)
carl.apply_damage(20, DamageType.fire)
assert carl.hit_points == carl._max_hit_points == 10
for i in immunity + vulnerability + absorbs:
carl.remove_modifier(i)
carl.apply_damage(5, "fire", magical=True)
assert carl.resistant("fire")
assert not carl.immune("fire")
assert not carl.vulnerable("fire")
assert not carl.absorbs("fire")
carl.apply_damage(5, DamageType.fire)
assert carl.resistant(DamageType.fire)
assert not carl.immune(DamageType.fire)
assert not carl.vulnerable(DamageType.fire)
assert not carl.absorbs(DamageType.fire)
assert carl.hit_points == 8 # half damage