move inventory verbs to inventory map from character

This commit is contained in:
evilchili 2024-08-29 16:41:15 -07:00
parent 0eda35b90d
commit 68a8f4920b
4 changed files with 89 additions and 70 deletions

View File

@ -10,7 +10,6 @@ from ttfrog.db.base import BaseObject, SlugMixin
from ttfrog.db.schema.classes import CharacterClass, ClassFeature from ttfrog.db.schema.classes import CharacterClass, ClassFeature
from ttfrog.db.schema.constants import DamageType, Defenses from ttfrog.db.schema.constants import DamageType, Defenses
from ttfrog.db.schema.inventory import Inventory, InventoryMap, InventoryType from ttfrog.db.schema.inventory import Inventory, InventoryMap, InventoryType
from ttfrog.db.schema.item import ItemType
from ttfrog.db.schema.modifiers import Modifier, ModifierMixin, Stat from ttfrog.db.schema.modifiers import Modifier, ModifierMixin, Stat
from ttfrog.db.schema.skill import Skill from ttfrog.db.schema.skill import Skill
@ -422,47 +421,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
mapping.attuned = False mapping.attuned = False
return True return True
def equip(self, mapping):
if mapping.equipped:
return False
mapping.equipped = True
return True
def unequip(self, mapping):
if not mapping.equipped:
return False
mapping.equipped = False
return True
def prepare(self, mapping):
if mapping.item.item_type != ItemType.SPELL:
return False
if mapping.item.level > 0 and not self.spell_slots_by_level[mapping.item.level]:
return False
return self.equip(mapping)
def unprepare(self, mapping):
if mapping.item.item_type != ItemType.SPELL:
return False
return self.unequip(mapping)
def cast(self, mapping: InventoryMap, level=0):
if not mapping.prepared:
return False
if not level:
level = mapping.item.level
# cantrips
if level == 0:
return True
# expend the spell slot
avail = self.spell_slots_available[level]
if not avail:
return False
avail[0].expended = True
return True
def level_in_class(self, charclass): def level_in_class(self, charclass):
mapping = [mapping for mapping in self.class_map if mapping.character_class_id == charclass.id] mapping = [mapping for mapping in self.class_map if mapping.character_class_id == charclass.id]
if not mapping: if not mapping:

View File

@ -41,6 +41,8 @@ class Inventory(BaseObject):
uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: [] uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: []
) )
character = relationship("Character", init=False, viewonly=True, lazy="immediate")
def get(self, item): def get(self, item):
return self.get_all(item)[0] return self.get_all(item)[0]
@ -101,6 +103,30 @@ class InventoryMap(BaseObject):
if self.item.item_type == ItemType.SPELL: if self.item.item_type == ItemType.SPELL:
return self.equipped or self.always_prepared return self.equipped or self.always_prepared
def equip(self):
if self.equipped:
return False
self.equipped = True
return True
def unequip(self):
if not self.equipped:
return False
self.equipped = False
return True
def prepare(self):
if self.item.item_type != ItemType.SPELL:
return False
if self.item.level > 0 and not self.inventory.character.spell_slots_by_level[self.item.level]:
return False
return self.equip()
def unprepare(self):
if self.item.item_type != ItemType.SPELL:
return False
return self.unequip()
def use(self, item_property: ItemProperty, charges=None): def use(self, item_property: ItemProperty, charges=None):
if item_property.charge_cost is None: if item_property.charge_cost is None:
return True return True
@ -126,6 +152,42 @@ class InventoryMap(BaseObject):
return 0 return 0
return self.count return self.count
def cast(self, level=0):
if self.item.item_type != ItemType.SPELL:
return False
if not self.prepared:
return False
if not level:
level = self.item.level
# cantrips
if level == 0:
return True
# expend the spell slot
avail = self.inventory.character.spell_slots_available[level]
if not avail:
return False
avail[0].expended = True
return True
def attune(self):
if self.attuned:
return False
if not self.item.requires_attunement:
return False
if len(self.inventory.character.attuned_items) >= 3:
return False
self.attuned = True
return True
def unattune(self):
if not self.attuned:
return False
self.attuned = False
return True
class Charge(BaseObject): class Charge(BaseObject):
__tablename__ = "charge" __tablename__ = "charge"

View File

@ -33,21 +33,23 @@ def test_equipment_inventory(db, carl):
assert fireball in carl.spells assert fireball in carl.spells
assert fireball not in carl.equipment assert fireball not in carl.equipment
pole_one = all_carls_poles[0]
# equip one pole # equip one pole
assert carl.equip(all_carls_poles[0]) assert pole_one.equip()
# can't equip it twice # can't equip it twice
assert not carl.equip(all_carls_poles[0]) assert not pole_one.equip()
# unequip it # unequip it
assert carl.unequip(all_carls_poles[0]) assert pole_one.unequip()
# can't unequip the unequipped ones # can't unequip the unequipped ones
assert not carl.unequip(all_carls_poles[1]) assert not all_carls_poles[1].unequip()
assert not carl.unequip(all_carls_poles[2]) assert not all_carls_poles[2].unequip()
# drop one pole # drop one pole
assert carl.equipment.remove(all_carls_poles[0]) assert carl.equipment.remove(pole_one)
assert ten_foot_pole in carl.equipment assert ten_foot_pole in carl.equipment
# drop the remaining poles # drop the remaining poles
@ -56,7 +58,7 @@ def test_equipment_inventory(db, carl):
assert ten_foot_pole not in carl.equipment assert ten_foot_pole not in carl.equipment
# can't drop what you don't have # can't drop what you don't have
assert not carl.equipment.remove(all_carls_poles[0]) assert not carl.equipment.remove(pole_one)
def test_inventory_bundles(db, carl): def test_inventory_bundles(db, carl):
@ -108,15 +110,12 @@ def test_spell_slots(db, carl, wizard):
# prepare the cantrip # prepare the cantrip
carls_prestidigitation = carl.spells.get(prestidigitation) carls_prestidigitation = carl.spells.get(prestidigitation)
assert carl.prepare(carls_prestidigitation) assert carls_prestidigitation.prepare()
assert carl.cast(carls_prestidigitation) assert carls_prestidigitation.cast()
# prepare() and cast() require a spell from the spell inventory
carls_fireball = carl.spells.get(fireball)
# can't prepare a 3rd level spell if you don't have 3rd level slots # can't prepare a 3rd level spell if you don't have 3rd level slots
assert carl.spellcaster_level == 1 assert carl.spellcaster_level == 1
assert not carl.prepare(carls_fireball) assert not carl.spells.get(fireball).prepare()
# make carl a 5th level wizard so he gets a 3rd level spell slot # make carl a 5th level wizard so he gets a 3rd level spell slot
carl.level_up(wizard, num_levels=4) carl.level_up(wizard, num_levels=4)
@ -124,10 +123,10 @@ def test_spell_slots(db, carl, wizard):
assert carl.spellcaster_level == 3 assert carl.spellcaster_level == 3
# cast fireball until he's out of 3rd level slots # cast fireball until he's out of 3rd level slots
assert carl.prepare(carls_fireball) assert carl.spells.get(fireball).prepare()
assert carl.cast(carls_fireball) assert carl.spells.get(fireball).cast()
assert carl.cast(carls_fireball) assert carl.spells.get(fireball).cast()
assert not carl.cast(carls_fireball) assert not carl.spells.get(fireball).cast()
# level up to 7th level, gaining 1 4th level slot and 1 more 3rd level slot # level up to 7th level, gaining 1 4th level slot and 1 more 3rd level slot
carl.add_class(wizard) carl.add_class(wizard)
@ -137,9 +136,9 @@ def test_spell_slots(db, carl, wizard):
assert len(carl.spell_slots_available[3]) == 1 assert len(carl.spell_slots_available[3]) == 1
# cast at 4th level # cast at 4th level
assert carl.cast(carls_fireball, 4) assert carl.spells.get(fireball).cast(level=4)
assert not carl.cast(carls_fireball, 4) assert not carl.spells.get(fireball).cast(level=4)
# use the last 3rd level slot # use the last 3rd level slot
assert carl.cast(carls_fireball) assert carl.spells.get(fireball).cast()
assert not carl.cast(carls_fireball) assert not carl.spells.get(fireball).cast()

View File

@ -82,8 +82,8 @@ def test_charges(db, carl):
db.add_or_update(carl) db.add_or_update(carl)
carls_dagger = carl.equipment.get(dagger_of_lulz) carls_dagger = carl.equipment.get(dagger_of_lulz)
assert carl.equip(carls_dagger) assert carls_dagger.equip()
assert carl.attune(carls_dagger) assert carls_dagger.attune()
assert len(carls_dagger.charges) == dagger_of_lulz.charges == 6 assert len(carls_dagger.charges) == dagger_of_lulz.charges == 6
assert len(carls_dagger.charges_available) == dagger_of_lulz.charges == 6 assert len(carls_dagger.charges_available) == dagger_of_lulz.charges == 6
@ -132,24 +132,24 @@ def test_attunement(db, carl):
assert carl.armor_class == 10 assert carl.armor_class == 10
assert len(carl.attuned_items) == 0 assert len(carl.attuned_items) == 0
carl.equip(carls_shield) carls_shield.equip()
assert plus_two_ac in carl.modifiers["armor_class"] assert plus_two_ac in carl.modifiers["armor_class"]
assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks] assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks]
assert carl.armor_class == 12 assert carl.armor_class == 12
assert carls_shield not in carl.attuned_items assert carls_shield not in carl.attuned_items
carl.attune(carls_shield) carls_shield.attune()
assert carl.armor_class == 12 assert carl.armor_class == 12
assert plus_two_ac in carl.modifiers["armor_class"] assert plus_two_ac in carl.modifiers["armor_class"]
assert ranged_resistance in carl.modifiers[DamageType.ranged_weapon_attacks] assert ranged_resistance in carl.modifiers[DamageType.ranged_weapon_attacks]
assert carls_shield in carl.attuned_items assert carls_shield in carl.attuned_items
assert carl.equip(carl.equipment.get(helm)) assert carl.equipment.get(helm).equip()
assert carl.armor_class == 13 assert carl.armor_class == 13
assert carl.unattune(carls_shield) assert carls_shield.unattune()
assert carl.armor_class == 13 assert carl.armor_class == 13
assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks] assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks]
assert carl.unequip(carls_shield) assert carls_shield.unequip()
assert carl.armor_class == 11 assert carl.armor_class == 11