convert to modern MappedAsDataclass models
This commit is contained in:
parent
1ff0e5ca7d
commit
3980be5f07
|
@ -2,9 +2,9 @@ import enum
|
|||
|
||||
import nanoid
|
||||
from nanoid_dictionary import human_alphabet
|
||||
from pyramid_sqlalchemy import BaseObject as _BaseObject
|
||||
from slugify import slugify
|
||||
from sqlalchemy import Column, String
|
||||
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass
|
||||
|
||||
|
||||
def genslug():
|
||||
|
@ -19,7 +19,7 @@ class SlugMixin:
|
|||
return "-".join([self.slug, slugify(self.name.title().replace(" ", ""), ok="", only_ascii=True, lower=False)])
|
||||
|
||||
|
||||
class BaseObject(_BaseObject):
|
||||
class BaseObject(MappedAsDataclass, DeclarativeBase):
|
||||
"""
|
||||
Allows for iterating over Model objects' column names and values
|
||||
"""
|
||||
|
|
|
@ -7,30 +7,30 @@ def bootstrap():
|
|||
db.init()
|
||||
with db.transaction():
|
||||
# ancestries
|
||||
human = schema.Ancestry(name="human")
|
||||
tiefling = schema.Ancestry(name="tiefling")
|
||||
tiefling.add_modifier(schema.Modifier(name="Ability Score Increase", target="intelligence", relative_value=1))
|
||||
tiefling.add_modifier(schema.Modifier(name="Ability Score Increase", target="charisma", relative_value=2))
|
||||
human = schema.Ancestry("human")
|
||||
tiefling = schema.Ancestry("tiefling")
|
||||
tiefling.add_modifier(schema.Modifier("Ability Score Increase", target="intelligence", relative_value=1))
|
||||
tiefling.add_modifier(schema.Modifier("Ability Score Increase", target="charisma", relative_value=2))
|
||||
darkvision = schema.AncestryTrait(
|
||||
name="Darkvision",
|
||||
"Darkvision",
|
||||
description=(
|
||||
"You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it "
|
||||
"were dim light. You can’t discern color in darkness, only shades of gray."
|
||||
),
|
||||
)
|
||||
darkvision.add_modifier(schema.Modifier(name="Darkvision", target="vision_in_darkness", absolute_value=120))
|
||||
darkvision.add_modifier(schema.Modifier("Darkvision", target="vision_in_darkness", absolute_value=120))
|
||||
tiefling.add_trait(darkvision)
|
||||
|
||||
# classes
|
||||
fighter = schema.CharacterClass(name="fighter", hit_dice="1d10", hit_dice_stat="CON")
|
||||
rogue = schema.CharacterClass(name="rogue", hit_dice="1d8", hit_dice_stat="DEX")
|
||||
fighter = schema.CharacterClass("fighter", hit_dice="1d10", hit_dice_stat="CON")
|
||||
rogue = schema.CharacterClass("rogue", hit_dice="1d8", hit_dice_stat="DEX")
|
||||
|
||||
# characters
|
||||
sabetha = schema.Character(name="Sabetha", ancestry=tiefling)
|
||||
sabetha = schema.Character("Sabetha", ancestry=tiefling)
|
||||
sabetha.add_class(fighter, level=2)
|
||||
sabetha.add_class(rogue, level=3)
|
||||
|
||||
bob = schema.Character(name="Bob", ancestry=human)
|
||||
bob = schema.Character("Bob", ancestry=human)
|
||||
|
||||
# persist all the records we've created
|
||||
db.add_or_update([sabetha, bob])
|
||||
|
|
|
@ -6,8 +6,7 @@ from contextlib import contextmanager
|
|||
from functools import cached_property
|
||||
|
||||
import transaction
|
||||
from pyramid_sqlalchemy import Session, init_sqlalchemy
|
||||
from pyramid_sqlalchemy import metadata as _metadata
|
||||
from pyramid_sqlalchemy.meta import Session
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
import ttfrog.db.schema
|
||||
|
@ -43,7 +42,7 @@ class SQLDatabaseManager:
|
|||
|
||||
@cached_property
|
||||
def metadata(self):
|
||||
return _metadata
|
||||
return ttfrog.db.schema.BaseObject.metadata
|
||||
|
||||
@cached_property
|
||||
def tables(self):
|
||||
|
@ -77,7 +76,8 @@ class SQLDatabaseManager:
|
|||
return base64.urlsafe_b64encode(sha1bytes.digest()).decode("ascii")[:10]
|
||||
|
||||
def init(self):
|
||||
init_sqlalchemy(self.engine)
|
||||
self.session.configure(bind=self.engine, autoflush=False)
|
||||
self.metadata.bind = self.engine
|
||||
self.metadata.create_all(self.engine)
|
||||
|
||||
def dump(self, names: list = []):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from sqlalchemy import Column, Enum, ForeignKey, Integer, String, Text, UniqueConstraint
|
||||
from sqlalchemy import ForeignKey, Text, UniqueConstraint
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ttfrog.db.base import BaseObject, CreatureTypesEnum, SavingThrowsMixin, SizesEnum, SkillsMixin, SlugMixin
|
||||
from ttfrog.db.base import BaseObject, SavingThrowsMixin, SkillsMixin, SlugMixin
|
||||
from ttfrog.db.schema.classes import CharacterClass, ClassAttribute
|
||||
from ttfrog.db.schema.modifiers import Modifier, ModifierMixin
|
||||
|
||||
__all__ = [
|
||||
|
@ -31,11 +32,11 @@ def attr_map_creator(fields):
|
|||
class AncestryTraitMap(BaseObject):
|
||||
__tablename__ = "trait_map"
|
||||
__table_args__ = (UniqueConstraint("ancestry_id", "ancestry_trait_id"),)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
ancestry_id = Column(Integer, ForeignKey("ancestry.id"))
|
||||
ancestry_trait_id = Column(Integer, ForeignKey("ancestry_trait.id"))
|
||||
trait = relationship("AncestryTrait", uselist=False, lazy="immediate")
|
||||
level = Column(Integer, nullable=False, info={"min": 1, "max": 20})
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
ancestry_id: Mapped[int] = mapped_column(ForeignKey("ancestry.id"))
|
||||
ancestry_trait_id: Mapped[int] = mapped_column(ForeignKey("ancestry_trait.id"), init=False)
|
||||
trait: Mapped["AncestryTrait"] = relationship(uselist=False, lazy="immediate")
|
||||
level: Mapped[int] = mapped_column(nullable=False, info={"min": 1, "max": 20})
|
||||
|
||||
|
||||
class Ancestry(BaseObject, ModifierMixin):
|
||||
|
@ -44,15 +45,20 @@ class Ancestry(BaseObject, ModifierMixin):
|
|||
"""
|
||||
|
||||
__tablename__ = "ancestry"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, index=True, unique=True)
|
||||
creature_type = Column(Enum(CreatureTypesEnum), nullable=False, default="humanoid")
|
||||
size = Column(Enum(SizesEnum), nullable=False, default="Medium")
|
||||
walk_speed = Column(Integer, nullable=False, default=30, info={"min": 0, "max": 99})
|
||||
_fly_speed = Column(Integer, info={"min": 0, "max": 99})
|
||||
_climb_speed = Column(Integer, info={"min": 0, "max": 99})
|
||||
_swim_speed = Column(Integer, info={"min": 0, "max": 99})
|
||||
_traits = relationship("AncestryTraitMap", cascade="all,delete,delete-orphan", lazy="immediate")
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(unique=True, nullable=False)
|
||||
|
||||
creature_type: Mapped[str] = mapped_column(nullable=False, default="humanoid")
|
||||
size: Mapped[str] = mapped_column(nullable=False, default="medium")
|
||||
walk_speed: Mapped[int] = mapped_column(nullable=False, default=30, info={"min": 0, "max": 99})
|
||||
|
||||
_fly_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||
_climb_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||
_swim_speed: Mapped[int] = mapped_column(init=False, nullable=True, info={"min": 0, "max": 99})
|
||||
|
||||
_traits = relationship(
|
||||
"AncestryTraitMap", init=False, uselist=True, cascade="all,delete,delete-orphan", lazy="immediate"
|
||||
)
|
||||
|
||||
@property
|
||||
def traits(self):
|
||||
|
@ -71,8 +77,12 @@ class Ancestry(BaseObject, ModifierMixin):
|
|||
return self._swim_speed or int(self.speed / 2)
|
||||
|
||||
def add_trait(self, trait, level=1):
|
||||
if trait not in self._traits:
|
||||
self._traits.append(AncestryTraitMap(ancestry_id=self.id, trait=trait, level=level))
|
||||
if not self._traits or trait not in self._traits:
|
||||
mapping = AncestryTraitMap(ancestry_id=self.id, trait=trait, level=level)
|
||||
if not self._traits:
|
||||
self._traits = [mapping]
|
||||
else:
|
||||
self._traits.append(mapping)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -86,9 +96,9 @@ class AncestryTrait(BaseObject, ModifierMixin):
|
|||
"""
|
||||
|
||||
__tablename__ = "ancestry_trait"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(Text)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
description: Mapped[Text] = mapped_column(Text, default="")
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
@ -97,13 +107,14 @@ class AncestryTrait(BaseObject, ModifierMixin):
|
|||
class CharacterClassMap(BaseObject):
|
||||
__tablename__ = "class_map"
|
||||
__table_args__ = (UniqueConstraint("character_id", "character_class_id"),)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
character_id = Column(Integer, ForeignKey("character.id"), nullable=False)
|
||||
character_class_id = Column(Integer, ForeignKey("character_class.id"), nullable=False)
|
||||
level = Column(Integer, nullable=False, info={"min": 1, "max": 20}, default=1)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
character: Mapped["Character"] = relationship(uselist=False, viewonly=True)
|
||||
character_class: Mapped["CharacterClass"] = relationship(lazy="immediate")
|
||||
|
||||
character_class = relationship("CharacterClass", lazy="immediate")
|
||||
character = relationship("Character", uselist=False, viewonly=True)
|
||||
character_id: Mapped[int] = mapped_column(ForeignKey("character.id"), init=False, nullable=False)
|
||||
character_class_id: Mapped[int] = mapped_column(ForeignKey("character_class.id"), init=False, nullable=False)
|
||||
|
||||
level: Mapped[int] = mapped_column(nullable=False, info={"min": 1, "max": 20}, default=1)
|
||||
|
||||
def __repr__(self):
|
||||
return "{self.character.name}, {self.character_class.name}, level {self.level}"
|
||||
|
@ -112,12 +123,12 @@ class CharacterClassMap(BaseObject):
|
|||
class CharacterClassAttributeMap(BaseObject):
|
||||
__tablename__ = "character_class_attribute_map"
|
||||
__table_args__ = (UniqueConstraint("character_id", "class_attribute_id"),)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
character_id = Column(Integer, ForeignKey("character.id"), nullable=False)
|
||||
class_attribute_id = Column(Integer, ForeignKey("class_attribute.id"), nullable=False)
|
||||
option_id = Column(Integer, ForeignKey("class_attribute_option.id"), nullable=False)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
character_id: Mapped[int] = mapped_column(ForeignKey("character.id"), nullable=False)
|
||||
class_attribute_id: Mapped[int] = mapped_column(ForeignKey("class_attribute.id"), nullable=False)
|
||||
option_id: Mapped[int] = mapped_column(ForeignKey("class_attribute_option.id"), nullable=False)
|
||||
|
||||
class_attribute = relationship("ClassAttribute", lazy="immediate")
|
||||
class_attribute: Mapped["ClassAttribute"] = relationship(lazy="immediate")
|
||||
option = relationship("ClassAttributeOption", lazy="immediate")
|
||||
|
||||
character_class = relationship(
|
||||
|
@ -132,22 +143,23 @@ class CharacterClassAttributeMap(BaseObject):
|
|||
|
||||
class Character(BaseObject, ModifierMixin, SlugMixin, SavingThrowsMixin, SkillsMixin):
|
||||
__tablename__ = "character"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, default="New Character", nullable=False)
|
||||
armor_class = Column(Integer, default=10, nullable=False, info={"min": 1, "max": 99})
|
||||
hit_points = Column(Integer, default=1, nullable=False, info={"min": 0, "max": 999})
|
||||
max_hit_points = Column(Integer, default=10, nullable=False, info={"min": 0, "max": 999})
|
||||
temp_hit_points = Column(Integer, default=0, nullable=False, info={"min": 0, "max": 999})
|
||||
strength = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
dexterity = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
constitution = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
intelligence = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
wisdom = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
charisma = Column(Integer, nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
|
||||
_vision = Column(Integer, info={"min": 0})
|
||||
name: Mapped[str] = mapped_column(default="New Character", nullable=False)
|
||||
armor_class: Mapped[int] = mapped_column(default=10, nullable=False, info={"min": 1, "max": 99})
|
||||
hit_points: Mapped[int] = mapped_column(default=1, nullable=False, info={"min": 0, "max": 999})
|
||||
max_hit_points: Mapped[int] = mapped_column(default=10, nullable=False, info={"min": 0, "max": 999})
|
||||
temp_hit_points: Mapped[int] = mapped_column(default=0, nullable=False, info={"min": 0, "max": 999})
|
||||
strength: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
dexterity: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
constitution: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
intelligence: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
wisdom: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
charisma: Mapped[int] = mapped_column(nullable=False, default=10, info={"min": 0, "max": 30})
|
||||
|
||||
proficiencies = Column(String)
|
||||
_vision: Mapped[int] = mapped_column(default=None, nullable=True, info={"min": 0})
|
||||
|
||||
proficiencies: Mapped[str] = mapped_column(nullable=False, default="")
|
||||
|
||||
class_map = relationship("CharacterClassMap", cascade="all,delete,delete-orphan")
|
||||
class_list = association_proxy("class_map", "id", creator=class_map_creator)
|
||||
|
@ -155,8 +167,8 @@ class Character(BaseObject, ModifierMixin, SlugMixin, SavingThrowsMixin, SkillsM
|
|||
character_class_attribute_map = relationship("CharacterClassAttributeMap", cascade="all,delete,delete-orphan")
|
||||
attribute_list = association_proxy("character_class_attribute_map", "id", creator=attr_map_creator)
|
||||
|
||||
ancestry_id = Column(Integer, ForeignKey("ancestry.id"), nullable=False, default="1")
|
||||
ancestry = relationship("Ancestry", uselist=False)
|
||||
ancestry_id: Mapped[int] = mapped_column(ForeignKey("ancestry.id"), nullable=False, default="1")
|
||||
ancestry: Mapped["Ancestry"] = relationship(uselist=False, default=None)
|
||||
|
||||
@property
|
||||
def modifiers(self):
|
||||
|
@ -255,7 +267,7 @@ class Character(BaseObject, ModifierMixin, SlugMixin, SavingThrowsMixin, SkillsM
|
|||
level_in_class = level_in_class[0]
|
||||
level_in_class.level = level
|
||||
else:
|
||||
self.class_list.append(CharacterClassMap(character_id=self.id, character_class=newclass, level=level))
|
||||
self.class_list.append(CharacterClassMap(character=self, character_class=newclass, level=level))
|
||||
for lvl in range(1, level + 1):
|
||||
if not newclass.attributes_by_level[lvl]:
|
||||
continue
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ttfrog.db.base import BaseObject, SavingThrowsMixin, SkillsMixin, StatsEnum
|
||||
from ttfrog.db.base import BaseObject, SavingThrowsMixin, SkillsMixin
|
||||
|
||||
__all__ = [
|
||||
"ClassAttributeMap",
|
||||
|
@ -15,16 +15,16 @@ __all__ = [
|
|||
|
||||
class ClassAttributeMap(BaseObject):
|
||||
__tablename__ = "class_attribute_map"
|
||||
class_attribute_id = Column(Integer, ForeignKey("class_attribute.id"), primary_key=True)
|
||||
character_class_id = Column(Integer, ForeignKey("character_class.id"), primary_key=True)
|
||||
level = Column(Integer, nullable=False, info={"min": 1, "max": 20}, default=1)
|
||||
class_attribute_id: Mapped[int] = mapped_column(ForeignKey("class_attribute.id"), primary_key=True)
|
||||
character_class_id: Mapped[int] = mapped_column(ForeignKey("character_class.id"), primary_key=True)
|
||||
level: Mapped[int] = mapped_column(nullable=False, info={"min": 1, "max": 20}, default=1)
|
||||
attribute = relationship("ClassAttribute", uselist=False, viewonly=True, lazy="immediate")
|
||||
|
||||
|
||||
class ClassAttribute(BaseObject):
|
||||
__tablename__ = "class_attribute"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, nullable=False)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
options = relationship("ClassAttributeOption", cascade="all,delete,delete-orphan", lazy="immediate")
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -33,18 +33,18 @@ class ClassAttribute(BaseObject):
|
|||
|
||||
class ClassAttributeOption(BaseObject):
|
||||
__tablename__ = "class_attribute_option"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, nullable=False)
|
||||
attribute_id = Column(Integer, ForeignKey("class_attribute.id"), nullable=False)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
attribute_id: Mapped[int] = mapped_column(ForeignKey("class_attribute.id"), nullable=False)
|
||||
|
||||
|
||||
class CharacterClass(BaseObject, SavingThrowsMixin, SkillsMixin):
|
||||
__tablename__ = "character_class"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, index=True, unique=True)
|
||||
hit_dice = Column(String, default="1d6")
|
||||
hit_dice_stat = Column(Enum(StatsEnum))
|
||||
proficiencies = Column(String)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(index=True, unique=True)
|
||||
hit_dice: Mapped[str] = mapped_column(default="1d6")
|
||||
hit_dice_stat: Mapped[str] = mapped_column(default="")
|
||||
proficiencies: Mapped[str] = mapped_column(default="")
|
||||
attributes = relationship("ClassAttributeMap", cascade="all,delete,delete-orphan", lazy="immediate")
|
||||
|
||||
@property
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from sqlalchemy import Column, Float, ForeignKey, Integer, String, UniqueConstraint
|
||||
from sqlalchemy import ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ttfrog.db.base import BaseObject
|
||||
|
||||
|
@ -14,12 +14,12 @@ class ModifierMap(BaseObject):
|
|||
|
||||
__tablename__ = "modifier_map"
|
||||
__table_args__ = (UniqueConstraint("primary_table_name", "primary_table_id", "modifier_id"),)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
modifier_id = Column(Integer, ForeignKey("modifier.id"), nullable=False)
|
||||
modifier = relationship("Modifier", uselist=False, lazy="immediate")
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
modifier_id: Mapped[int] = mapped_column(ForeignKey("modifier.id"), init=False)
|
||||
modifier: Mapped["Modifier"] = relationship(uselist=False, lazy="immediate")
|
||||
|
||||
primary_table_name = Column(String, nullable=False)
|
||||
primary_table_id = Column(Integer, nullable=False)
|
||||
primary_table_name: Mapped[str] = mapped_column(nullable=False)
|
||||
primary_table_id: Mapped[int] = mapped_column(nullable=False)
|
||||
|
||||
|
||||
class Modifier(BaseObject):
|
||||
|
@ -31,14 +31,14 @@ class Modifier(BaseObject):
|
|||
"""
|
||||
|
||||
__tablename__ = "modifier"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
target = Column(String, nullable=False)
|
||||
absolute_value = Column(Integer)
|
||||
relative_value = Column(Integer)
|
||||
multiply_value = Column(Float)
|
||||
new_value = Column(String)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(String)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
target: Mapped[str] = mapped_column(nullable=False)
|
||||
absolute_value: Mapped[int] = mapped_column(nullable=True, default=None)
|
||||
relative_value: Mapped[int] = mapped_column(nullable=True, default=None)
|
||||
multiply_value: Mapped[float] = mapped_column(nullable=True, default=None)
|
||||
new_value: Mapped[str] = mapped_column(nullable=True, default=None)
|
||||
description: Mapped[str] = mapped_column(default="")
|
||||
|
||||
|
||||
class ModifierMixin:
|
||||
|
@ -56,14 +56,16 @@ class ModifierMixin:
|
|||
Example:
|
||||
|
||||
>>> class Item(BaseObject, ModifierMixin):
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String, nullable=False)
|
||||
id: Mapped[int] = mapped_column(init=False, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
>>> dwarven_belt = Item(name="Dwarven Belt")
|
||||
>>> dwarven_belt.add_modifier(Modifier(name="STR+1", target="strength", relative_value=1))
|
||||
>>> dwarven_belt.modifiers
|
||||
{'strength': [Modifier(id=1, target='strength', name='STR+1', relative_value=1 ... ]}
|
||||
"""
|
||||
|
||||
_modifiable_attributes = dict()
|
||||
|
||||
@declared_attr
|
||||
def modifier_map(cls):
|
||||
return relationship(
|
||||
|
@ -104,7 +106,36 @@ class ModifierMixin:
|
|||
return True
|
||||
|
||||
def remove_modifier(self, modifier):
|
||||
if modifier.id not in [mod.modifier_id for mod in self.modifier_map]:
|
||||
if modifier not in self.modifiers[modifier.target]:
|
||||
return False
|
||||
self.modifier_map = [mapping for mapping in self.modifier_map if mapping.modifier != modifier]
|
||||
return True
|
||||
|
||||
def apply_modifiers(self, target, initial):
|
||||
if not self._modifiable_attributes:
|
||||
raise NotImplementedError(
|
||||
f"You must define the '_modifiable_attributes' property on {self.__class__.__name__}."
|
||||
)
|
||||
|
||||
modifiers = list(reversed(self.modifiers.get(target, [])))
|
||||
if initial is None:
|
||||
return initial
|
||||
if isinstance(initial, int):
|
||||
absolute = [mod for mod in modifiers if mod.absolute_value is not None]
|
||||
if absolute:
|
||||
return absolute[0].absolute_value
|
||||
multiple = [mod for mod in modifiers if mod.multiply_value is not None]
|
||||
if multiple:
|
||||
return int(initial * multiple[0].multiply_value + 0.5)
|
||||
return initial + sum(mod.relative_value for mod in modifiers if mod.relative_value is not None)
|
||||
|
||||
new = [mod for mod in modifiers if mod.new_value is not None]
|
||||
if new:
|
||||
return new[0].new_value
|
||||
return initial
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
prop = self._modifiable_attributes.get(attr_name, None)
|
||||
if not prop:
|
||||
raise AttributeError(f"Attribute not found: {attr_name}")
|
||||
return self.apply_modifiers(attr_name, getattr(self, prop))
|
||||
|
|
Loading…
Reference in New Issue
Block a user