move spell management to CharacterSpellInventory

This commit is contained in:
evilchili
2024-09-21 20:45:18 -07:00
parent 9bece1550d
commit d5b81dafb4
4 changed files with 118 additions and 70 deletions
+68 -11
View File
@@ -2,6 +2,8 @@ import itertools
from collections import defaultdict
from dataclasses import dataclass
from pprint import pprint
from sqlalchemy import ForeignKey, String, Text, UniqueConstraint
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declared_attr
@@ -11,6 +13,7 @@ from ttfrog.db.base import BaseObject, SlugMixin
from ttfrog.db.schema.classes import CharacterClass, ClassFeature
from ttfrog.db.schema.constants import DamageType, Defenses, InventoryType
from ttfrog.db.schema.inventory import InventoryMixin
from ttfrog.db.schema.prototypes import ItemType
from ttfrog.db.schema.modifiers import Modifier, ModifierMixin, Stat
from ttfrog.db.schema.skill import Skill
@@ -214,6 +217,69 @@ class CharacterSpellInventory(BaseObject, InventoryMap):
__item_class__ = "Spell"
inventory_type: InventoryType = InventoryType.SPELL
@property
def all_contents(self):
yield from self.inventory.contents
for item in self.character.equipment.all_contents:
if item.prototype.inventory_type == InventoryType.SPELL:
yield from item.inventory.contents
@property
def available(self):
yield from [spell.prototype for spell in self.all_contents]
@property
def known(self):
yield from [spell.prototype for spell in self.inventory.contents]
@property
def prepared(self):
yield from [spell.prototype for spell in self.all_contents if spell.prepared]
def get_all(self, prototype):
return [mapping for mapping in self.all_contents if mapping.prototype == prototype]
def get(self, prototype):
return self.get_all(prototype)[0]
def learn(self, prototype):
return self.inventory.add(prototype)
def forget(self, spell):
return self.inventory.remove(spell)
def prepare(self, prototype):
spell = self.get(prototype)
if spell.prototype.level > 0 and not self.character.spell_slots_by_level[spell.prototype.level]:
return False
spell._prepared = True
return True
def unprepare(self, prototype):
spell = self.get(prototype)
if spell.prepared:
spell._prepared = False
return True
return False
def cast(self, prototype, level=0):
spell = self.get(prototype)
if not spell.prepared:
return False
if not level:
level = spell.prototype.level
# cantrips
if level == 0:
return True
# expend the spell slot
avail = self.character.spell_slots_available[level]
if not avail:
return False
avail[0].expended = True
return True
class Character(BaseObject, SlugMixin, ModifierMixin):
__tablename__ = "character"
@@ -289,7 +355,7 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
lazy="immediate",
back_populates="character",
)
_spells = relationship(
spells = relationship(
"CharacterSpellInventory",
uselist=False,
cascade="all,delete,delete-orphan",
@@ -304,15 +370,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
def equipment(self):
return self._equipment.inventory
@property
def spells(self):
return self._spells.inventory
@property
def prepared_spells(self):
hashmap = dict([(mapping.item.name, mapping) for mapping in self.spells.contents if mapping.prepared])
return list(hashmap.values())
@property
def spell_slots(self):
return list(itertools.chain(*[slot for lvl, slot in self.spell_slots_by_level.items()]))
@@ -694,5 +751,5 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
self.add_skill(skill, proficient=False, expert=False)
self._equipment = CharacterItemInventory(character_id=self.id)
self._spells = CharacterSpellInventory(character_id=self.id)
self.spells = CharacterSpellInventory(character_id=self.id)
session.add(self)
+3 -33
View File
@@ -1,8 +1,11 @@
from dataclasses import dataclass
from typing import List
from pprint import pprint
from sqlalchemy import ForeignKey
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import base as sa_base
from sqlalchemy.orm import mapped_column, relationship
@@ -172,43 +175,10 @@ class Spell(BaseObject, InventoryItemMixin):
prototype: Mapped["prototypes.BaseSpell"] = relationship(uselist=False, lazy="immediate", init=False)
@property
def spell(self):
return self.prototype
@property
def prepared(self):
return self._prepared or self.always_prepared
def prepare(self):
if self.prototype.level > 0 and not self.container.character.spell_slots_by_level[self.prototype.level]:
return False
self._prepared = True
return True
def unprepare(self):
if self.prepared:
self._prepared = False
return True
return False
def cast(self, level=0):
if not self.prepared:
return False
if not level:
level = self.prototype.level
# cantrips
if level == 0:
return True
# expend the spell slot
avail = self.container.character.spell_slots_available[level]
if not avail:
return False
avail[0].expended = True
return True
class Charge(BaseObject):
__tablename__ = "charge"
-3
View File
@@ -86,9 +86,6 @@ class BaseItem(BaseObject, ModifierMixin):
uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: []
)
# _spells: Mapped[int] = mapped_column(ForeignKey("spell.id"), nullable=True, default=None)
# spells: Mapped["Spell"] = relationship(init=False)
# if this item is a container, set the inventory type
inventory_type: Mapped[InventoryType] = mapped_column(nullable=True, default=None)