|
|
import base64
from flask_sqlalchemy import SQLAlchemy from ..models import db, BaseModel
class CharacterClass(BaseModel): __tablename__ = 'character_class'
name = db.Column(db.String(50), unique=True, nullable=False) spellcaster = db.Column(db.String(10)) bucket = db.Column(db.String(50)) frequency_modifier = db.Column(db.Integer, default=1)
prime_requisite = db.Column(db.String(3)) hit_die_size = db.Column(db.Integer) maximum_level = db.Column(db.Integer)
armour_modifier = db.Column(db.Integer) melee_light = db.Column(db.Integer) melee_medium = db.Column(db.Integer) melee_heavy = db.Column(db.Integer) ranged_light = db.Column(db.Integer) ranged_heavy = db.Column(db.Integer)
def __repr__(self): return '<CharacterClass: {0}>'.format(self.name)
@property def is_divine_spellcaster(self): return self.spellcaster == 'Divine'
@property def is_arcane_spellcaster(self): return self.spellcaster == 'Arcane'
class ClassLevelProgression(BaseModel): __tablename__ = 'class_progression'
level = db.Column(db.Integer) attack_throw = db.Column(db.Integer)
save_petrification_paralysis = db.Column(db.Integer) save_poison_death = db.Column(db.Integer) save_blast_breath = db.Column(db.Integer) save_staffs_wands = db.Column(db.Integer) save_spells = db.Column(db.Integer)
spellslots1 = db.Column(db.Integer) spellslots2 = db.Column(db.Integer) spellslots3 = db.Column(db.Integer) spellslots4 = db.Column(db.Integer) spellslots5 = db.Column(db.Integer) spellslots6 = db.Column(db.Integer)
guild_id = db.Column(db.Integer, db.ForeignKey('character_class.id'), nullable=False) guild = db.relationship('CharacterClass', backref=db.backref('progressions', lazy=True))
def spell_slots(self, level=None): if level > self.guild.maximum_level: level = self.guild.maximum_level
spellslots = [ self.spellslots1, self.spellslots2, self.spellslots3, self.spellslots4, self.spellslots5, self.spellslots6 ]
if level: return spellslots[level - 1]
return spellslots
def __repr__(self): return '<LevelProgression: {0} {1}>'.format(self.level, self.guild.name)
class Spell(BaseModel): __tablename__ = 'spells'
name = db.Column(db.String(50), unique=True, nullable=False) range = db.Column(db.String(50)) duration = db.Column(db.String(50)) arcane = db.Column(db.Integer, nullable=False) divine = db.Column(db.Integer, nullable=False) description = db.Column(db.Text(1000), nullable=False)
def __repr__(self): return '<Spell: {0} ({1})>'.format(self.name, self.school)
def level_for(self, npc): return self.arcane if npc.is_arcane_spellcaster else self.divine
@property def school(self): if self.is_arcane and self.is_divine: return 'Multi' if self.is_arcane: return 'Arcane' if self.is_divine: return 'Divine'
@property def is_divine(self): return bool(self.divine and self.divine > 0)
@property def is_arcane(self): return bool(self.arcane and self.arcane > 0)
@property def dom_id(self): return self.name.lower().replace("*", "").replace("'", "").replace(",", "").replace(" ", "_")
@property def roll20_format(self): spell_dict = { 'id': self.id, 'name': self.name, 'range': self.range, 'duration': self.duration, 'divine': self.divine, 'is_divine': self.is_divine, 'arcane': self.arcane, 'is_arcane': self.is_arcane, 'description': base64.b64encode(self.description.encode('ascii')).decode('ascii') } return spell_dict
class EquipmentArmour(BaseModel): __tablename__ = 'eq_armour'
name = db.Column(db.String(50), unique=True, nullable=False) gp_value = db.Column(db.Integer, nullable=False) ac_mod = db.Column(db.Integer, nullable=False)
def __repr__(self): return '<EquipmentArmour: {0}>'.format(self.name)
class EquipmentRangedWeapon(BaseModel): __tablename__ = 'eq_ranged_wep'
name = db.Column(db.String(50), unique=True, nullable=False) bucket = db.Column(db.String(20), nullable=False) gp_value = db.Column(db.Integer, nullable=False) damage_die = db.Column(db.String(10), nullable=False)
def __repr__(self): return '<EquipmentRangedWeapon: {0}>'.format(self.name)
class EquipmentMeleeWeapon(BaseModel): __tablename__ = 'eq_melee_wep'
name = db.Column(db.String(50), unique=False, nullable=False) bucket = db.Column(db.String(20), nullable=False) gp_value = db.Column(db.Integer, nullable=False) damage_die = db.Column(db.String(10), nullable=False) two_handed = db.Column(db.Boolean, nullable=False)
def __repr__(self): return '<EquipmentMeleeWeapon: {0}>'.format(self.name)
class CharacterNPC(BaseModel): __tablename__ = 'npcs'
name = db.Column(db.String(50), unique=False, nullable=True) level = db.Column(db.Integer, nullable=False) alignment = db.Column(db.String(20), unique=False, nullable=False) hit_points = db.Column(db.Integer, nullable=False)
strength = db.Column(db.Integer, nullable=False) intelligence = db.Column(db.Integer, nullable=False) wisdom = db.Column(db.Integer, nullable=False) dexterity = db.Column(db.Integer, nullable=False) constitution = db.Column(db.Integer, nullable=False) charisma = db.Column(db.Integer, nullable=False)
spells = db.Column(db.String(200))
guild_id = db.Column(db.Integer, db.ForeignKey('character_class.id'), nullable=False) guild = db.relationship('CharacterClass', backref=db.backref('npcs', lazy=True))
melee_id = db.Column(db.Integer, db.ForeignKey('eq_melee_wep.id'), nullable=False) melee = db.relationship('EquipmentMeleeWeapon')
ranged_id = db.Column(db.Integer, db.ForeignKey('eq_ranged_wep.id'), nullable=True) ranged = db.relationship('EquipmentRangedWeapon')
armour_id = db.Column(db.Integer, db.ForeignKey('eq_armour.id'), nullable=False) armour = db.relationship('EquipmentArmour')
def __repr__(self): return '<CharacterNPC: {0}>'.format(self.name)
@staticmethod def calculate_attr_mod(attr): mod = 0 if attr <= 3: mod = -3 elif attr >= 4 and attr <= 5: mod = -2 elif attr >= 6 and attr <= 8: mod = -1 elif attr >= 9 and attr <= 12: mod = 0 elif attr >= 13 and attr <= 15: mod = 1 elif attr >= 16 and attr <= 16: mod = 2 elif attr >= 18: mod = 3 return mod
def spell_slots(self, level=None): if level > self.guild.maximum_level: level = self.guild.maximum_level return self.current_progression.spell_slots(level)
def spells_known(self, level=None): if level: if level > self.guild.maximum_level: level = self.guild.maximum_level # Return for a specific level if self.is_arcane_spellcaster: return (self.spell_slots(level) + self.wis_mod) return self.spell_slots(level)
# Return for all levels if self.is_arcane_spellcaster: return [self.spell_slots(x) + self.wis_mod for x in range(1,6)] return [self.spell_slots(x) for x in range(1,6)]
def spell_list(self, level=None): spell_ids = self.spells.split(',') spells = Spell.query.filter(Spell.id.in_(spell_ids)).all()
spells_by_level = {} for spell in spells: spells_by_level.setdefault(spell.level_for(self), []).append(spell)
return spells_by_level
@property def roll20_format(self): npc_dict = { 'name': self.name, 'level': self.level, 'hp': self.hit_points, 'guild': self.guild.name, 'alignment': self.alignment, "attack_throw": self.attack_throw, 'attributes': { 'str': self.strength, 'int': self.intelligence, 'wis': self.wisdom, 'dex': self.dexterity, 'con': self.constitution, 'chr': self.charisma }, 'attribute_mods': { 'str': self.str_mod, 'int': self.int_mod, 'wis': self.wis_mod, 'dex': self.dex_mod, 'con': self.con_mod, 'chr': self.chr_mod }, "saves": { "pp": self.save_pp, "pd": self.save_pd, "bb": self.save_bb, "sw": self.save_sw, "sp": self.save_sp }, 'armour': { 'name': self.armour.name, 'value': self.armour.gp_value, 'ac_mod': self.armour.ac_mod }, 'melee': { 'name': self.melee.name, 'value': self.melee.gp_value, 'damage': self.melee.damage_die, 'throw_mod': 0, 'two_handed': self.melee.two_handed } }
if self.guild.is_divine_spellcaster or self.guild.is_arcane_spellcaster: npc_dict['spells'] = [] spell_list = self.spell_list() for level in spell_list: for spell in spell_list[level]: npc_dict['spells'].append(spell.roll20_format)
if self.ranged: npc_dict['ranged'] = { 'name': self.ranged.name, 'value': self.ranged.gp_value, 'damage': self.ranged.damage_die, 'throw_mod': 0 }
return npc_dict
@property def str(self): return self.strength
@property def int(self): return self.intelligence
@property def wis(self): return self.wisdom
@property def dex(self): return self.dexterity
@property def con(self): return self.constitution
@property def chr(self): return self.charisma
@property def str_mod(self): return self.calculate_attr_mod(self.strength)
@property def int_mod(self): return self.calculate_attr_mod(self.intelligence)
@property def wis_mod(self): return self.calculate_attr_mod(self.wisdom)
@property def dex_mod(self): return self.calculate_attr_mod(self.dexterity)
@property def con_mod(self): return self.calculate_attr_mod(self.constitution)
@property def chr_mod(self): return self.calculate_attr_mod(self.charisma)
@property def current_progression(self): return ClassLevelProgression.query.filter_by(guild_id=self.guild.id, level=self.level).first()
@property def attack_throw(self): return self.current_progression.attack_throw
@property def save_pp(self): return self.current_progression.save_petrification_paralysis
@property def save_pd(self): return self.current_progression.save_petrification_paralysis
@property def save_bb(self): return self.current_progression.save_blast_breath
@property def save_sw(self): return self.current_progression.save_staffs_wands
@property def save_sp(self): return self.current_progression.save_spells
@property def hp(self): return self.hit_points
@property def is_divine_spellcaster(self): return self.guild.is_divine_spellcaster
@property def is_arcane_spellcaster(self): return self.guild.is_arcane_spellcaster
def update(self, kwargs): # Allows us to update like a dict, used for inserting attributes zip self.__dict__.update(kwargs)
admin_models = [CharacterClass, ClassLevelProgression, EquipmentArmour, EquipmentRangedWeapon, EquipmentMeleeWeapon, CharacterNPC, Spell]
|