add support for skills
This commit is contained in:
+20
-1
@@ -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
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user