add support for skills

This commit is contained in:
evilchili
2024-05-06 00:13:52 -07:00
parent b574dacfa1
commit 9a2d28ae75
8 changed files with 278 additions and 113 deletions
+20 -1
View File
@@ -50,20 +50,39 @@ def bootstrap(db):
db.add_or_update([human, dragonborn, tiefling])
# skills
skills = {
name: schema.Skill(name=name)
for name in ("strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma")
}
db.add_or_update(list(skills.values()))
acrobatics = schema.Skill(name="Acrobatics", base_id=skills["dexterity"].id)
athletics = schema.Skill(name="Athletics", base_id=skills["strength"].id)
db.add_or_update([acrobatics, athletics])
# classes
fighting_style = schema.ClassAttribute("Fighting Style")
fighting_style.add_option(name="Archery")
fighting_style.add_option(name="Defense")
db.add_or_update(fighting_style)
fighter = schema.CharacterClass("fighter", hit_dice="1d10", hit_dice_stat="CON")
fighter = schema.CharacterClass("fighter", hit_dice="1d10", hit_dice_stat="CON", starting_skills=2)
db.add_or_update(fighter)
# add skills
fighter.add_skill(acrobatics)
fighter.add_skill(athletics)
fighter.add_attribute(fighting_style, level=2)
db.add_or_update(fighter)
assert acrobatics in fighter.skills
assert athletics in fighter.skills
rogue = schema.CharacterClass("rogue", hit_dice="1d8", hit_dice_stat="DEX")
db.add_or_update([rogue, fighter])
# characters
foo = schema.Character("Foo", ancestry=tiefling, _intelligence=14)
db.add_or_update(foo)
foo.add_class(fighter, level=2)
foo.add_class(rogue, level=3)
+61 -23
View File
@@ -24,6 +24,12 @@ def test_manage_character(db, bootstrap):
assert char.charisma == 10
assert darkvision not in char.traits
# verify basic skills were added at creation time
for skill in db.Skill.filter(
schema.Skill.name.in_(("strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"))
):
assert char.check_modifier(skill) == 0
# switch ancestry to tiefling
tiefling = db.Ancestry.filter_by(name="tiefling").one()
char.ancestry = tiefling
@@ -33,11 +39,21 @@ def test_manage_character(db, bootstrap):
assert char.ancestry.name == "tiefling"
assert darkvision in char.traits
# tiefling ancestry adds INT and CHA modifiers
assert char.intelligence == 11
assert char.intelligence.base == 10
assert char.charisma == 12
assert char.charisma.base == 10
# switch ancestry to dragonborn and assert darkvision persists
char.ancestry = db.Ancestry.filter_by(name="dragonborn").one()
db.add_or_update(char)
assert darkvision in char.traits
# verify tiefling modifiers were removed
assert char.intelligence == 10
assert char.charisma == 10
# switch ancestry to human and assert darkvision is removed
char.ancestry = human
db.add_or_update(char)
@@ -54,33 +70,43 @@ def test_manage_character(db, bootstrap):
assert char.class_attributes == {}
# 'fighting style' is available, but not at this level
fighting_style = fighter.attributes_by_level[2]["Fighting Style"]
assert char.add_class_attribute(fighting_style, fighting_style.options[0]) is False
fighting_style = fighter.attribute("Fighting Style")
assert char.has_class_attribute(fighting_style) is False
assert char.add_class_attribute(fighter, fighting_style, fighting_style.options[0]) is False
db.add_or_update(char)
assert char.class_attributes == {}
# level up
char.add_class(fighter, level=2)
char.add_class(fighter, level=7)
db.add_or_update(char)
assert char.levels == {"fighter": 2}
assert char.level == 2
assert char.levels == {"fighter": 7}
assert char.level == 7
# Assert the fighting style is added automatically and idempotent...ly?
assert char.has_class_attribute(fighting_style)
assert char.class_attributes[fighting_style.name] == fighting_style.options[0]
assert char.add_class_attribute(fighting_style, fighting_style.options[0]) is True
assert char.add_class_attribute(fighter, fighting_style, fighting_style.options[0]) is False
assert char.has_class_attribute(fighting_style)
db.add_or_update(char)
# classes
athletics = db.Skill.filter_by(name="athletics").one()
acrobatics = db.Skill.filter_by(name="acrobatics").one()
assert athletics in char.skills
assert acrobatics in char.skills
assert char.check_modifier(athletics) == char.proficiency_bonus + char.strength.bonus == 3
assert char.check_modifier(acrobatics) == char.proficiency_bonus + char.dexterity.bonus == 3
# multiclass
char.add_class(rogue, level=1)
db.add_or_update(char)
assert char.level == 3
assert char.levels == {"fighter": 2, "rogue": 1}
assert char.level == 8
assert char.levels == {"fighter": 7, "rogue": 1}
# remove a class
char.remove_class(rogue)
db.add_or_update(char)
assert char.levels == {"fighter": 2}
assert char.level == 2
assert char.levels == {"fighter": 7}
assert char.level == 7
# remove remaining class by setting level to zero
char.add_class(fighter, level=0)
@@ -88,13 +114,19 @@ def test_manage_character(db, bootstrap):
assert char.levels == {}
assert char.class_attributes == {}
# verify the proficiencies added by the classes have been removed
assert athletics not in char.skills
assert acrobatics not in char.skills
assert char.check_modifier(athletics) == 0
assert char.check_modifier(acrobatics) == 0
# ensure we're not persisting any orphan records in the map tables
dump = json.loads(db.dump())
assert not [m for m in dump["character_class_attribute_map"] if m["character_id"] == char.id]
assert not [m for m in dump["class_map"] if m["character_id"] == char.id]
def test_ancestries(db):
def test_ancestries(db, bootstrap):
with db.transaction():
# create the Pygmy Orc ancestry
porc = schema.Ancestry(
@@ -102,7 +134,6 @@ def test_ancestries(db):
size="Small",
walk_speed=25,
)
db.add_or_update(porc)
assert porc.name == "Pygmy Orc"
assert porc.creature_type == "humanoid"
assert porc.size == "Small"
@@ -115,26 +146,33 @@ def test_ancestries(db):
db.add_or_update(porc)
assert endurance in porc.traits
# add a +1 STR modifier
str_plus_one = schema.Modifier(
name="STR+1 (Pygmy Orc)",
# add a +3 STR modifier
str_bonus = schema.Modifier(
name="STR+3 (Pygmy Orc)",
target="strength",
relative_value=1,
description="Your Strength score is increased by 1.",
relative_value=3,
description="Your Strength score is increased by 3.",
)
assert porc.add_modifier(str_plus_one) is True
assert porc.add_modifier(str_plus_one) is False # test idempotency
assert str_plus_one in porc.modifiers["strength"]
assert porc.add_modifier(str_bonus) is True
assert porc.add_modifier(str_bonus) is False # test idempotency
assert str_bonus in porc.modifiers["strength"]
# now create an orc character and assert it gets traits and modifiers
grognak = schema.Character(name="Grognak the Mighty", ancestry=porc)
db.add_or_update(grognak)
assert endurance in grognak.traits
# verify the strength bonus is applied
assert grognak.strength.base == 10
assert str_plus_one in grognak.modifiers["strength"]
assert grognak.strength == 11
assert grognak.strength == 13
assert grognak.strength.bonus == 1
assert str_bonus in grognak.modifiers["strength"]
# make sure bonuses are applied to checks and saves
strength = db.Skill.filter_by(name="strength").one()
assert grognak.check_modifier(strength) == 1
assert grognak.check_modifier(strength, save=True) == 1
def test_modifiers(db, bootstrap):