adding weapons with charges

This commit is contained in:
evilchili
2024-08-29 15:14:47 -07:00
parent 5d9fde949d
commit 5c80565264
5 changed files with 160 additions and 27 deletions
+8 -6
View File
@@ -1,5 +1,6 @@
import itertools
from collections import defaultdict
from typing import List
from sqlalchemy import ForeignKey, String, Text, UniqueConstraint
from sqlalchemy.ext.associationproxy import association_proxy
@@ -261,15 +262,16 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
ancestry_id: Mapped[int] = mapped_column(ForeignKey("ancestry.id"), nullable=False, default="1")
ancestry: Mapped["Ancestry"] = relationship(uselist=False, default=None)
_inventories = relationship("Inventory", uselist=True, cascade="all,delete,delete-orphan", lazy="immediate")
inventories = association_proxy("_inventories", "id", creator=inventory_creator)
inventories: Mapped[List["Inventory"]] = relationship(
uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: []
)
_hit_dice = relationship("HitDie", uselist=True, cascade="all,delete,delete-orphan", lazy="immediate")
_spell_slots = relationship("SpellSlot", uselist=True, cascade="all,delete,delete-orphan", lazy="immediate")
@property
def spells(self):
return [inv for inv in self._inventories if inv.inventory_type == InventoryType.SPELL][0]
return [inv for inv in self.inventories if inv.inventory_type == InventoryType.SPELL][0]
@property
def prepared_spells(self):
@@ -394,7 +396,7 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
@property
def equipment(self):
return [inv for inv in self._inventories if inv.inventory_type == InventoryType.EQUIPMENT][0]
return [inv for inv in self.inventories if inv.inventory_type == InventoryType.EQUIPMENT][0]
@property
def equipped_items(self):
@@ -701,6 +703,6 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
):
self.add_skill(skill, proficient=False, expert=False)
self._inventories.append(Inventory(inventory_type=InventoryType.EQUIPMENT, character_id=self.id))
self._inventories.append(Inventory(inventory_type=InventoryType.SPELL, character_id=self.id))
self.inventories.append(Inventory(inventory_type=InventoryType.EQUIPMENT, character_id=self.id))
self.inventories.append(Inventory(inventory_type=InventoryType.SPELL, character_id=self.id))
session.add(self)
+45 -11
View File
@@ -1,9 +1,10 @@
from typing import List
from sqlalchemy import ForeignKey, UniqueConstraint
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ttfrog.db.base import BaseObject, EnumField
from ttfrog.db.schema.item import Item, ItemType
from ttfrog.db.schema.item import Item, ItemProperty, ItemType
class InventoryType(EnumField):
@@ -36,11 +37,15 @@ class Inventory(BaseObject):
character_id: Mapped[int] = mapped_column(ForeignKey("character.id"))
inventory_type: Mapped[InventoryType] = mapped_column(nullable=False)
_inventory_map = relationship("InventoryMap", lazy="immediate", uselist=True, cascade="all,delete,delete-orphan")
inventory_map = association_proxy("_inventory_map", "id", creator=inventory_map_creator)
items: Mapped[List["InventoryMap"]] = relationship(
uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: []
)
def get(self, item):
return [mapping for mapping in self._inventory_map if mapping.item == item]
return self.get_all(item)[0]
def get_all(self, item):
return [mapping for mapping in self.items if mapping.item == item]
def add(self, item):
if item.item_type not in inventory_type_map[self.inventory_type]:
@@ -48,23 +53,25 @@ class Inventory(BaseObject):
mapping = InventoryMap(inventory_id=self.id, item_id=item.id)
if item.consumable:
mapping.count = item.count
self.inventory_map.append(mapping)
if item.charges:
mapping.charges = [Charge(inventory_map_id=mapping.id) for i in range(item.charges)]
self.items.append(mapping)
return mapping
def remove(self, mapping):
if mapping.id not in self.inventory_map:
if mapping not in self.items:
return False
self.inventory_map.remove(mapping.id)
self.items.remove(mapping)
return True
def __contains__(self, obj):
for mapping in self._inventory_map:
for mapping in self.items:
if mapping.item == obj:
return True
return False
def __iter__(self):
yield from self._inventory_map
yield from self.items
class InventoryMap(BaseObject):
@@ -81,12 +88,32 @@ class InventoryMap(BaseObject):
always_prepared: Mapped[bool] = mapped_column(default=False)
charges: Mapped[List["Charge"]] = relationship(
uselist=True, cascade="all,delete,delete-orphan", lazy="immediate", default_factory=lambda: []
)
@property
def charges_available(self):
return [charge for charge in self.charges if not charge.expended]
@property
def prepared(self):
if self.item.item_type == ItemType.SPELL:
return self.equipped or self.always_prepared
def use(self, count=1):
def use(self, item_property: ItemProperty, charges=None):
if item_property.charge_cost is None:
return True
avail = self.charges_available
if charges is None:
charges = item_property.charge_cost
if len(avail) < charges:
return False
for charge in avail:
charge.expended = True
return True
def consume(self, count=1):
if count < 0:
return False
if not self.item.consumable:
@@ -98,3 +125,10 @@ class InventoryMap(BaseObject):
self.inventory.remove(self)
return 0
return self.count
class Charge(BaseObject):
__tablename__ = "charge"
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
inventory_map_id: Mapped[int] = mapped_column(ForeignKey("inventory_map.id"))
expended: Mapped[bool] = mapped_column(nullable=False, default=False)
+47
View File
@@ -1,3 +1,5 @@
from typing import List
from sqlalchemy import ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -21,16 +23,36 @@ ITEM_TYPES = [
"SHIELD",
]
RECHARGE_TIMES = [
"short rest",
"long rest",
"dawn",
]
COST_TYPES = ["Action", "Bonus Action", "Reaction"]
RARITY = ["Common", "Uncommon", "Rare", "Very Rare", "Legendary", "Artifact"]
ItemType = EnumField("ItemType", ((k, k) for k in ITEM_TYPES))
Rarity = EnumField("Rarity", ((k, k) for k in RARITY))
RechargeTime = EnumField("RechargeTime", ((k.replace(" ", "_").upper(), k) for k in RECHARGE_TIMES))
Cost = EnumField("Cost", ((k, k) for k in COST_TYPES))
def item_property_creator(fields):
if isinstance(fields, list):
for f in fields:
yield f
elif isinstance(fields, ItemProperty):
return fields
return ItemProperty(**fields)
class Item(BaseObject, ModifierMixin):
__tablename__ = "item"
__mapper_args__ = {"polymorphic_identity": ItemType.ITEM, "polymorphic_on": "item_type"}
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(collation="NOCASE"), nullable=False, unique=True)
description: Mapped[str] = mapped_column(String, nullable=True, default=None)
@@ -42,12 +64,24 @@ class Item(BaseObject, ModifierMixin):
consumable: Mapped[bool] = mapped_column(default=False)
count: Mapped[int] = mapped_column(nullable=False, default=1)
charges: Mapped[int] = mapped_column(nullable=True, info={"min": 0}, default=None)
recharge_time: Mapped[RechargeTime] = mapped_column(default=RechargeTime.LONG_REST)
recharge_amount: Mapped[str] = mapped_column(String(collation="NOCASE"), default="1")
_class_restrictions: Mapped[int] = mapped_column(ForeignKey("character_class.id"), nullable=True, default=None)
class_restrictions: Mapped["CharacterClass"] = relationship(init=False)
properties: Mapped[List["ItemProperty"]] = relationship(
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)
@property
def has_charges(self):
return self.charges is not None
class Spell(Item):
__tablename__ = "spell"
@@ -83,6 +117,7 @@ class Weapon(Item):
versatile: Mapped[bool] = mapped_column(default=False)
silvered: Mapped[bool] = mapped_column(default=False)
adamantine: Mapped[bool] = mapped_column(default=False)
magical: Mapped[bool] = mapped_column(default=False)
@property
def ranged(self):
@@ -101,3 +136,15 @@ class Armor(Item):
__mapper_args__ = {"polymorphic_identity": ItemType.ARMOR}
id: Mapped[int] = mapped_column(ForeignKey("item.id"), primary_key=True, init=False)
item_type: Mapped[ItemType] = ItemType.ARMOR
class ItemProperty(BaseObject):
__tablename__ = "item_property"
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(collation="NOCASE"), nullable=False, unique=True)
description: Mapped[str] = mapped_column(String, nullable=True, default=None)
charge_cost: Mapped[int] = mapped_column(nullable=True, info={"min": 0}, default=None)
item_id: Mapped[int] = mapped_column(ForeignKey("item.id"), default=0)
# action/reaction/bonus
# modifiers?