adding weapons and attunement
This commit is contained in:
parent
708d6fe9e9
commit
b7732f1581
|
@ -316,13 +316,25 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
|||
|
||||
@property
|
||||
def modifiers(self):
|
||||
unified = {}
|
||||
unified.update(**self.ancestry.modifiers)
|
||||
for trait in self.traits:
|
||||
unified.update(**trait.modifiers)
|
||||
for condition in self.conditions:
|
||||
unified.update(**condition.modifiers)
|
||||
unified.update(**super().modifiers)
|
||||
unified = defaultdict(list)
|
||||
|
||||
def merge_modifiers(object_list):
|
||||
for obj in object_list:
|
||||
for target, mods in obj.modifiers.items():
|
||||
unified[target] += mods
|
||||
|
||||
merge_modifiers([self.ancestry])
|
||||
merge_modifiers(self.traits)
|
||||
merge_modifiers(self.conditions)
|
||||
|
||||
for mapping in self.equipped_items:
|
||||
for (target, mods) in mapping.item.modifiers.items():
|
||||
for mod in mods:
|
||||
if mod.requires_attunement and not mapping.attuned:
|
||||
continue
|
||||
unified[target].append(mod)
|
||||
|
||||
merge_modifiers([super()])
|
||||
return unified
|
||||
|
||||
@property
|
||||
|
@ -384,6 +396,30 @@ class Character(BaseObject, SlugMixin, ModifierMixin):
|
|||
def equipment(self):
|
||||
return [inv for inv in self._inventories if inv.inventory_type == InventoryType.EQUIPMENT][0]
|
||||
|
||||
@property
|
||||
def equipped_items(self):
|
||||
return [item for item in self.equipment if item.equipped]
|
||||
|
||||
@property
|
||||
def attuned_items(self):
|
||||
return [item for item in self.equipment if item.attuned]
|
||||
|
||||
def attune(self, mapping):
|
||||
if mapping.attuned:
|
||||
return False
|
||||
if not mapping.item.requires_attunement:
|
||||
return False
|
||||
if len(self.attuned_items) >= 3:
|
||||
return False
|
||||
mapping.attuned = True
|
||||
return True
|
||||
|
||||
def unattune(self, mapping):
|
||||
if not mapping.attuned:
|
||||
return False
|
||||
mapping.attuned = False
|
||||
return True
|
||||
|
||||
def equip(self, mapping):
|
||||
if mapping.equipped:
|
||||
return False
|
||||
|
|
|
@ -43,6 +43,8 @@ class DamageType(StrEnum):
|
|||
adamantium_piercing = auto()
|
||||
adamantium_slashing = auto()
|
||||
adamantium_bludgeoning = auto()
|
||||
ranged_weapon_attacks = auto()
|
||||
melee_weapon_attacks = auto()
|
||||
|
||||
|
||||
class Defenses(StrEnum):
|
||||
|
|
|
@ -13,6 +13,9 @@ class InventoryType(EnumField):
|
|||
|
||||
inventory_type_map = {
|
||||
InventoryType.EQUIPMENT: [
|
||||
ItemType.WEAPON,
|
||||
ItemType.ARMOR,
|
||||
ItemType.SHIELD,
|
||||
ItemType.ITEM,
|
||||
ItemType.SCROLL,
|
||||
],
|
||||
|
@ -54,18 +57,6 @@ class Inventory(BaseObject):
|
|||
self.inventory_map.remove(mapping.id)
|
||||
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 __contains__(self, obj):
|
||||
for mapping in self._inventory_map:
|
||||
if mapping.item == obj:
|
||||
|
@ -85,6 +76,7 @@ class InventoryMap(BaseObject):
|
|||
inventory: Mapped["Inventory"] = relationship(uselist=False, viewonly=True, init=False)
|
||||
|
||||
equipped: Mapped[bool] = mapped_column(default=False)
|
||||
attuned: Mapped[bool] = mapped_column(default=False)
|
||||
count: Mapped[int] = mapped_column(nullable=False, default=1)
|
||||
|
||||
always_prepared: Mapped[bool] = mapped_column(default=False)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from sqlalchemy import ForeignKey, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ttfrog.db.base import BaseObject, EnumField
|
||||
from ttfrog.db.schema.modifiers import ConditionMixin
|
||||
from ttfrog.db.schema.constants import DamageType
|
||||
from ttfrog.db.schema.modifiers import ModifierMixin
|
||||
from ttfrog.db.schema.classes import CharacterClass
|
||||
|
||||
__all__ = [
|
||||
"Item",
|
||||
|
@ -10,20 +12,48 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class ItemType(EnumField):
|
||||
ITEM = "ITEM"
|
||||
SPELL = "SPELL"
|
||||
SCROLL = "SCROLL"
|
||||
ITEM_TYPES = [
|
||||
"ITEM",
|
||||
"SPELL",
|
||||
"SCROLL",
|
||||
"WEAPON",
|
||||
"ARMOR",
|
||||
"SHIELD",
|
||||
]
|
||||
|
||||
RARITY = [
|
||||
"Common",
|
||||
"Uncommon",
|
||||
"Rare",
|
||||
"Very Rare",
|
||||
"Legendary",
|
||||
"Artifact"
|
||||
]
|
||||
|
||||
|
||||
class Item(BaseObject, ConditionMixin):
|
||||
ItemType = EnumField("ItemType", ((k, k) for k in ITEM_TYPES))
|
||||
Rarity = EnumField("Rarity", ((k, k) for k in RARITY))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
item_type: Mapped[ItemType] = mapped_column(default=ItemType.ITEM, nullable=False)
|
||||
rarity: Mapped[Rarity] = mapped_column(default=Rarity.Common, nullable=False)
|
||||
requires_attunement: Mapped[bool] = mapped_column(nullable=False, default=False)
|
||||
|
||||
consumable: Mapped[bool] = mapped_column(default=False)
|
||||
count: Mapped[int] = mapped_column(nullable=False, default=1)
|
||||
item_type: Mapped[ItemType] = mapped_column(default=ItemType.ITEM, nullable=False)
|
||||
|
||||
_class_restrictions: Mapped[int] = mapped_column(ForeignKey("character_class.id"), nullable=True, default=None)
|
||||
class_restrictions: Mapped["CharacterClass"] = relationship(init=False)
|
||||
|
||||
# _spells: Mapped[int] = mapped_column(ForeignKey("spell.id"), nullable=True, default=None)
|
||||
# spells: Mapped["Spell"] = relationship(init=False)
|
||||
|
||||
|
||||
class Spell(Item):
|
||||
|
@ -33,3 +63,32 @@ class Spell(Item):
|
|||
level: Mapped[int] = mapped_column(nullable=False, info={"min": 0, "max": 9}, default=0)
|
||||
concentration: Mapped[bool] = mapped_column(default=False)
|
||||
item_type: Mapped[ItemType] = mapped_column(default=ItemType.SPELL, init=False)
|
||||
|
||||
|
||||
class Weapon(Item):
|
||||
__tablename__ = "weapon"
|
||||
__mapper_args__ = {"polymorphic_identity": ItemType.WEAPON}
|
||||
id: Mapped[int] = mapped_column(ForeignKey("item.id"), primary_key=True, init=False)
|
||||
damage_die: Mapped[str] = mapped_column(nullable=False, default="1d6")
|
||||
damage_type: Mapped[DamageType] = mapped_column(nullable=False, default=DamageType.slashing)
|
||||
item_type: Mapped[ItemType] = mapped_column(default=ItemType.WEAPON)
|
||||
attack_range: Mapped[int] = mapped_column(nullable=False, info={"min": 0}, default=0)
|
||||
attack_range_long: Mapped[int] = mapped_column(nullable=True, info={"min": 0}, default=None)
|
||||
targets: Mapped[int] = mapped_column(nullable=False, info={"min": 1}, default=1)
|
||||
martial: Mapped[bool] = mapped_column(default=False)
|
||||
melee: Mapped[bool] = mapped_column(default=False)
|
||||
ammunition: Mapped[bool] = mapped_column(default=False)
|
||||
finesse: Mapped[bool] = mapped_column(default=False)
|
||||
heavy: Mapped[bool] = mapped_column(default=False)
|
||||
light: Mapped[bool] = mapped_column(default=False)
|
||||
loading: Mapped[bool] = mapped_column(default=False)
|
||||
reach: Mapped[bool] = mapped_column(default=False)
|
||||
thrown: Mapped[bool] = mapped_column(default=False)
|
||||
two_handed: Mapped[bool] = mapped_column(default=False)
|
||||
versatile: Mapped[bool] = mapped_column(default=False)
|
||||
silvered: Mapped[bool] = mapped_column(default=False)
|
||||
adamantine: Mapped[bool] = mapped_column(default=False)
|
||||
|
||||
@property
|
||||
def ranged(self):
|
||||
return self.attack_range > 0
|
||||
|
|
|
@ -66,6 +66,7 @@ class Modifier(BaseObject):
|
|||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
target: Mapped[str] = mapped_column(nullable=False)
|
||||
stacks: Mapped[bool] = mapped_column(nullable=False, default=False)
|
||||
requires_attunement: Mapped[bool] = mapped_column(nullable=False, default=False)
|
||||
absolute_value: Mapped[int] = mapped_column(nullable=True, default=None)
|
||||
multiply_value: Mapped[float] = mapped_column(nullable=True, default=None)
|
||||
multiply_attribute: Mapped[str] = mapped_column(nullable=True, default=None)
|
||||
|
@ -174,6 +175,8 @@ class ModifierMixin:
|
|||
|
||||
Returns the matching column if it was found, or None.
|
||||
"""
|
||||
if attr_name.startswith('_'):
|
||||
return None
|
||||
col = getattr(self.__table__.columns, f"_{attr_name}", None)
|
||||
if col is None:
|
||||
return None
|
||||
|
@ -279,7 +282,7 @@ class ModifierMixin:
|
|||
self._get_modifiable_base(col.info.get("modifiable_base", col.name)),
|
||||
modifiable_class=col.info.get("modifiable_class", None),
|
||||
)
|
||||
raise AttributeError(f"No such attribute on {self.__class__.__name__} object: {attr_name}.")
|
||||
raise AttributeError(f"{self.__class__.__name__} object does not have the attribute '{attr_name}'")
|
||||
|
||||
|
||||
class ConditionMixin:
|
||||
|
|
Loading…
Reference in New Issue
Block a user