move inventory verbs to inventory map from character
This commit is contained in:
parent
0eda35b90d
commit
68a8f4920b
|
@ -10,7 +10,6 @@ from ttfrog.db.base import BaseObject, SlugMixin
|
|||
from ttfrog.db.schema.classes import CharacterClass, ClassFeature
|
||||
from ttfrog.db.schema.constants import DamageType, Defenses
|
||||
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.skill import Skill
|
||||
|
||||
|
@ -422,47 +421,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
|||
mapping.attuned = False
|
||||
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):
|
||||
mapping = [mapping for mapping in self.class_map if mapping.character_class_id == charclass.id]
|
||||
if not mapping:
|
||||
|
|
|
@ -41,6 +41,8 @@ class Inventory(BaseObject):
|
|||
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):
|
||||
return self.get_all(item)[0]
|
||||
|
||||
|
@ -101,6 +103,30 @@ class InventoryMap(BaseObject):
|
|||
if self.item.item_type == ItemType.SPELL:
|
||||
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):
|
||||
if item_property.charge_cost is None:
|
||||
return True
|
||||
|
@ -126,6 +152,42 @@ class InventoryMap(BaseObject):
|
|||
return 0
|
||||
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):
|
||||
__tablename__ = "charge"
|
||||
|
|
|
@ -33,21 +33,23 @@ def test_equipment_inventory(db, carl):
|
|||
assert fireball in carl.spells
|
||||
assert fireball not in carl.equipment
|
||||
|
||||
pole_one = all_carls_poles[0]
|
||||
|
||||
# equip one pole
|
||||
assert carl.equip(all_carls_poles[0])
|
||||
assert pole_one.equip()
|
||||
|
||||
# can't equip it twice
|
||||
assert not carl.equip(all_carls_poles[0])
|
||||
assert not pole_one.equip()
|
||||
|
||||
# unequip it
|
||||
assert carl.unequip(all_carls_poles[0])
|
||||
assert pole_one.unequip()
|
||||
|
||||
# can't unequip the unequipped ones
|
||||
assert not carl.unequip(all_carls_poles[1])
|
||||
assert not carl.unequip(all_carls_poles[2])
|
||||
assert not all_carls_poles[1].unequip()
|
||||
assert not all_carls_poles[2].unequip()
|
||||
|
||||
# drop one pole
|
||||
assert carl.equipment.remove(all_carls_poles[0])
|
||||
assert carl.equipment.remove(pole_one)
|
||||
assert ten_foot_pole in carl.equipment
|
||||
|
||||
# drop the remaining poles
|
||||
|
@ -56,7 +58,7 @@ def test_equipment_inventory(db, carl):
|
|||
assert ten_foot_pole not in carl.equipment
|
||||
|
||||
# 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):
|
||||
|
@ -108,15 +110,12 @@ def test_spell_slots(db, carl, wizard):
|
|||
|
||||
# prepare the cantrip
|
||||
carls_prestidigitation = carl.spells.get(prestidigitation)
|
||||
assert carl.prepare(carls_prestidigitation)
|
||||
assert carl.cast(carls_prestidigitation)
|
||||
|
||||
# prepare() and cast() require a spell from the spell inventory
|
||||
carls_fireball = carl.spells.get(fireball)
|
||||
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.prepare(carls_fireball)
|
||||
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)
|
||||
|
@ -124,10 +123,10 @@ def test_spell_slots(db, carl, wizard):
|
|||
assert carl.spellcaster_level == 3
|
||||
|
||||
# cast fireball until he's out of 3rd level slots
|
||||
assert carl.prepare(carls_fireball)
|
||||
assert carl.cast(carls_fireball)
|
||||
assert carl.cast(carls_fireball)
|
||||
assert not carl.cast(carls_fireball)
|
||||
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)
|
||||
|
@ -137,9 +136,9 @@ def test_spell_slots(db, carl, wizard):
|
|||
assert len(carl.spell_slots_available[3]) == 1
|
||||
|
||||
# cast at 4th level
|
||||
assert carl.cast(carls_fireball, 4)
|
||||
assert not carl.cast(carls_fireball, 4)
|
||||
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.cast(carls_fireball)
|
||||
assert not carl.cast(carls_fireball)
|
||||
assert carl.spells.get(fireball).cast()
|
||||
assert not carl.spells.get(fireball).cast()
|
||||
|
|
|
@ -82,8 +82,8 @@ def test_charges(db, carl):
|
|||
db.add_or_update(carl)
|
||||
|
||||
carls_dagger = carl.equipment.get(dagger_of_lulz)
|
||||
assert carl.equip(carls_dagger)
|
||||
assert carl.attune(carls_dagger)
|
||||
assert carls_dagger.equip()
|
||||
assert carls_dagger.attune()
|
||||
|
||||
assert len(carls_dagger.charges) == 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 len(carl.attuned_items) == 0
|
||||
|
||||
carl.equip(carls_shield)
|
||||
carls_shield.equip()
|
||||
|
||||
assert plus_two_ac in carl.modifiers["armor_class"]
|
||||
assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks]
|
||||
assert carl.armor_class == 12
|
||||
assert carls_shield not in carl.attuned_items
|
||||
|
||||
carl.attune(carls_shield)
|
||||
carls_shield.attune()
|
||||
assert carl.armor_class == 12
|
||||
assert plus_two_ac in carl.modifiers["armor_class"]
|
||||
assert ranged_resistance in carl.modifiers[DamageType.ranged_weapon_attacks]
|
||||
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.unattune(carls_shield)
|
||||
assert carls_shield.unattune()
|
||||
assert carl.armor_class == 13
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user