from ttfrog.db.schema.container import Container from ttfrog.db.schema.item import Item, ItemType, Spell def test_equipment_inventory(db, carl): with db.transaction(): # trigger the creation of inventory mappings db.add_or_update(carl) # create some items ten_foot_pole = Item(name="10ft. Pole", item_type=ItemType.ITEM, consumable=False) fireball = Spell(name="Fireball", level=3, concentration=False) db.add_or_update([ten_foot_pole, fireball]) # add the pole to carl's equipment, and the spell to his spell list. assert carl.equipment.add(ten_foot_pole) assert carl.spells.add(fireball) # can't mix and match inventory item types assert not carl.equipment.add(fireball) assert not carl.spells.add(ten_foot_pole) # add two more 10 foot poles. You can never have too many. carl.equipment.add(ten_foot_pole) carl.equipment.add(ten_foot_pole) db.add_or_update(carl) all_carls_poles = carl.equipment.get_all(ten_foot_pole) assert len(all_carls_poles) == 3 # check the "contains" logic assert ten_foot_pole in carl.equipment assert ten_foot_pole not in carl.spells assert fireball in carl.spells assert fireball not in carl.equipment pole_one = all_carls_poles[0] # equip one pole assert pole_one.equip() # can't equip it twice assert not pole_one.equip() # can't prepare or cast an item assert not pole_one.prepare() assert not pole_one.unprepare() assert not pole_one.cast() # not consumable or attunable assert not pole_one.consume() assert not pole_one.attune() assert pole_one.unequip() # can't unequip the unequipped ones assert not all_carls_poles[1].unequip() assert not all_carls_poles[2].unequip() # drop one pole assert carl.equipment.remove(pole_one) assert ten_foot_pole in carl.equipment # drop the remaining poles assert carl.equipment.remove(all_carls_poles[1]) assert carl.equipment.remove(all_carls_poles[2]) assert ten_foot_pole not in carl.equipment # can't drop what you don't have assert not carl.equipment.remove(pole_one) def test_inventory_bundles(db, carl): with db.transaction(): arrows = Item(name="Arrows", item_type=ItemType.ITEM, consumable=True, count=20) db.add_or_update([carl, arrows]) quiver = carl.equipment.add(arrows) db.add_or_update(carl) # full quiver assert arrows in carl.equipment assert quiver.count == 20 # use one assert quiver.consume(1) == 19 assert quiver.count == 19 # cannot use more than you have assert not quiver.consume(20) # cannot use a negative amount assert not quiver.consume(-1) # consume all remaining arrows assert quiver.consume(19) == 0 assert arrows not in carl.equipment def test_spell_slots(db, carl, wizard): with db.transaction(): prestidigitation = Spell(name="Prestidigitation", level=0, concentration=False) fireball = Spell(name="Fireball", level=3, concentration=False) db.add_or_update([carl, prestidigitation, fireball]) carl.spells.add(prestidigitation) carl.spells.add(fireball) db.add_or_update(carl) # verify carl has the spell slots granted by wizard at 1st level assert len(carl.spell_slots) == 2 assert carl.spell_slots[0].spell_level == 1 assert carl.spell_slots[1].spell_level == 1 # carl knows the spells but hasn't prepared them assert prestidigitation in carl.spells assert fireball in carl.spells assert prestidigitation not in carl.prepared_spells assert fireball not in carl.prepared_spells # prepare the cantrip carls_prestidigitation = carl.spells.get(prestidigitation) assert carls_prestidigitation.prepare() assert carls_prestidigitation.cast() # can't prepare a 3rd level spell if you don't have 3rd level slots assert carl.spellcaster_level == 1 assert not carl.spells.get(fireball).prepare() # make carl a 5th level wizard so he gets a 3rd level spell slot carl.level_up(wizard, num_levels=4) assert carl.level == 5 assert carl.spellcaster_level == 3 # cast fireball until he's out of 3rd level slots assert not carl.spells.get(fireball).cast() assert carl.spells.get(fireball).prepare() assert carl.spells.get(fireball).cast() assert carl.spells.get(fireball).cast() assert not carl.spells.get(fireball).cast() # level up to 7th level, gaining 1 4th level slot and 1 more 3rd level slot carl.add_class(wizard) carl.level_up(wizard, num_levels=2) assert carl.spellcaster_level == 4 assert len(carl.spell_slots_available[4]) == 1 assert len(carl.spell_slots_available[3]) == 1 # cast at 4th level assert carl.spells.get(fireball).cast(level=4) assert not carl.spells.get(fireball).cast(level=4) # use the last 3rd level slot assert carl.spells.get(fireball).cast() assert not carl.spells.get(fireball).cast() # unprepare it assert carl.spells.get(fireball).unprepare() assert not carl.spells.get(fireball).unprepare() def test_containers(db, carl): with db.transaction(): ten_foot_pole = Item(name="10ft. Pole") rope = Item(name="50 ft. of Rope", consumable=True, count=50) bag_of_holding = Container(name="Bag of Holding") db.add_or_update([carl, ten_foot_pole, rope, bag_of_holding]) # add some items to the bag of holding assert bag_of_holding.add(ten_foot_pole) assert bag_of_holding.add(rope) db.add_or_update(bag_of_holding) pole_from_bag = bag_of_holding.get(ten_foot_pole) rope_from_bag = bag_of_holding.get(rope) assert pole_from_bag.item == ten_foot_pole assert pole_from_bag in bag_of_holding assert pole_from_bag not in carl.equipment # add the bag of holding to carl's equipment assert carl.equipment.add(bag_of_holding) db.add_or_update(bag_of_holding) assert pole_from_bag in carl.equipment assert rope_from_bag in carl.equipment # test equality of mappings carls_bag = carl.equipment.get(bag_of_holding) carls_pole = carl.equipment.get(ten_foot_pole) carls_rope = carl.equipment.get(rope) assert carls_pole == pole_from_bag assert carls_rope == rope_from_bag # use some rope carls_rope.consume(10) assert carls_rope.count == 40 # move the rope out of the bag of holding, but not the pole assert carls_rope.move_to(carl.equipment) assert carls_rope not in carls_bag assert carls_pole in carls_bag db.add_or_update(carl) # get the db record anew, in case the in-memory representation isn't # what's recorded in the database. Then make sure we didn't break # anything by asserting we still only have 40ft of rope. carl = db.Character.filter_by(name="carl").one() assert carls_rope in carl.equipment assert carls_rope not in carl.equipment.get(bag_of_holding) assert carls_rope.count == 40 # old references are still valid assert rope_from_bag == carls_rope # use the rest of the rope assert carls_rope.consume(40) == 0 print(rope_from_bag.inventory) assert rope_from_bag not in carl.equipment assert rope_from_bag not in carl.equipment.get(bag_of_holding)