From aebe890e3e0875c44f9e96acdf1a57d1775f0b26 Mon Sep 17 00:00:00 2001 From: Brandon Cornejo Date: Sun, 2 Feb 2020 23:56:15 -0600 Subject: [PATCH] Template updates, API --- acks/__init__.py | 16 +- acks/api.py | 8 + acks/npc/api.py | 38 +++ acks/npc/commands.py | 8 + acks/npc/models.py | 197 +++++++++++++- acks/npc/npc_party.py | 98 ++++--- acks/npc/templates/generate_npc_party.html | 282 +++++++++++++++++---- acks/npc/views.py | 14 +- acks/static/favicon.ico | Bin 0 -> 4335 bytes acks/templates/base.html | 31 ++- acks/templates/handbook.html | 2 + acks/templates/index.html | 2 + 12 files changed, 594 insertions(+), 102 deletions(-) create mode 100644 acks/api.py create mode 100644 acks/npc/api.py create mode 100644 acks/static/favicon.ico diff --git a/acks/__init__.py b/acks/__init__.py index dceea86..142cbc3 100644 --- a/acks/__init__.py +++ b/acks/__init__.py @@ -1,6 +1,6 @@ from flask import Flask -from flask_admin import Admin -from flask_admin.contrib.sqla import ModelView + + def create_app(): app = Flask(__name__) @@ -28,10 +28,22 @@ def create_app(): app.cli.add_command(npc_cli) # Load the Admin views + from flask_admin import Admin + from flask_admin.contrib.sqla import ModelView admin = Admin(app, name='acks', template_mode='bootstrap3') from acks.npc.models import admin_models as npc_admin_models for mdl in npc_admin_models: admin.add_view(ModelView(mdl, db.session)) + # Initialize API and load resources + from flask_potion import Api, ModelResource + api = Api(app) + # from acks.api import api + # api.init_app(app) + + from acks.npc.api import resources as npc_resources + for resource in npc_resources: + api.add_resource(resource) + return app diff --git a/acks/api.py b/acks/api.py new file mode 100644 index 0000000..0666298 --- /dev/null +++ b/acks/api.py @@ -0,0 +1,8 @@ +from flask_potion import Api, ModelResource + + +# api = Api() + +class BaseModelResource(ModelResource): + class Meta: + exclude_fields = ['created_at'] diff --git a/acks/npc/api.py b/acks/npc/api.py new file mode 100644 index 0000000..90de548 --- /dev/null +++ b/acks/npc/api.py @@ -0,0 +1,38 @@ +from ..api import BaseModelResource +from .models import ( + CharacterClass, + EquipmentArmour, + EquipmentMeleeWeapon, + EquipmentRangedWeapon, + CharacterNPC +) + +class CharacterClassResource(BaseModelResource): + class Meta(BaseModelResource.Meta): + model = CharacterClass + name = 'classes' + +class EquipmentArmourResource(BaseModelResource): + class Meta(BaseModelResource.Meta): + model = EquipmentArmour + +class EquipmentMeleeWeaponResource(BaseModelResource): + class Meta(BaseModelResource.Meta): + model = EquipmentMeleeWeapon + +class EquipmentRangedWeaponResource(BaseModelResource): + class Meta(BaseModelResource.Meta): + model = EquipmentRangedWeapon + +class CharacterNPCResource(BaseModelResource): + class Meta(BaseModelResource.Meta): + model = CharacterNPC + + +resources = [ + CharacterClassResource, + EquipmentArmourResource, + EquipmentMeleeWeaponResource, + EquipmentRangedWeaponResource, + CharacterNPCResource +] diff --git a/acks/npc/commands.py b/acks/npc/commands.py index 698be87..fb86b1d 100644 --- a/acks/npc/commands.py +++ b/acks/npc/commands.py @@ -11,6 +11,7 @@ def populate_npc_database(): import csv from .models import ( CharacterClass, + ClassLevelProgression, EquipmentArmour, EquipmentRangedWeapon, EquipmentMeleeWeapon @@ -39,4 +40,11 @@ def populate_npc_database(): wep.two_handed = (wep.two_handed == 'True') db.session.bulk_save_objects(melee_weps) + # Level Progressions + progressions = load_csv_data('default_progression.csv', ClassLevelProgression) + classes = {c.name: c.id for c in CharacterClass.query.all()} + for prog in progressions: + prog.guild_id = classes[prog.guild_id] + db.session.bulk_save_objects(progressions) + db.session.commit() diff --git a/acks/npc/models.py b/acks/npc/models.py index 151540c..6d4bf64 100644 --- a/acks/npc/models.py +++ b/acks/npc/models.py @@ -23,6 +23,25 @@ class CharacterClass(BaseModel): def __repr__(self): return ''.format(self.name) +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) + + guild_id = db.Column(db.Integer, db.ForeignKey('character_class.id'), nullable=False) + guild = db.relationship('CharacterClass', backref=db.backref('progressions', lazy=True)) + + def __repr__(self): + return ''.format(self.level, self.guild.name) + + class EquipmentArmour(BaseModel): __tablename__ = 'eq_armour' @@ -61,18 +80,188 @@ class CharacterNPC(BaseModel): 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) - guild_id = db.Column(db.Integer, db.ForeignKey('guild.id'), 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) + + 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('melee.id'), nullable=False) + 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('ranged.id'), nullable=False) + 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 ''.format(self.name) -admin_models = [CharacterClass, EquipmentArmour, EquipmentRangedWeapon, EquipmentMeleeWeapon, CharacterNPC] + @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 + + @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.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 + + 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] diff --git a/acks/npc/npc_party.py b/acks/npc/npc_party.py index 9318a95..a4563e8 100644 --- a/acks/npc/npc_party.py +++ b/acks/npc/npc_party.py @@ -1,4 +1,7 @@ -from random import randint, choice +import requests + +from math import floor, ceil +from random import randint, choice, shuffle from .models import ( CharacterClass, @@ -12,8 +15,11 @@ from .models import ( def number_encountered(): return (randint(1, 4) + 2) -def npc_class(): - classes = [c for cls in CharacterClass.query.all() for c in ([cls] * cls.frequency_modifier)] +def npc_class(excluded_buckets): + if excluded_buckets: + classes = [c for cls in CharacterClass.query.filter(CharacterClass.bucket.notin_(excluded_buckets)).all() for c in ([cls] * cls.frequency_modifier)] + else: + classes = [c for cls in CharacterClass.query.all() for c in ([cls] * cls.frequency_modifier)] return choice(classes) def npc_alignment(): @@ -46,7 +52,7 @@ def npc_baselevel(base_level): return base_level def npc_abilities(): - ability_list = ['str', 'int', 'wis', 'dex', 'con', 'chr'] + ability_list = ['strength', 'intelligence', 'wisdom', 'dexterity', 'constitution', 'charisma'] abilities = [(randint(1, 6) + randint(1, 6) + randint(1, 6)) for x in range(0,6)] return zip(ability_list, abilities) @@ -88,38 +94,62 @@ def select_ranged_weapon(guild, data): selection = randint(0, len(weapons) - 1) return weapons[selection] -def generate_npc(base_level, data): - guild = npc_class() - - npc = {} - npc['guild'] = guild.name - - npc['level'] = npc_baselevel(base_level) - npc.update(npc_abilities()) - - npc['hp'] = 0 - conmod = attribute_mod(npc['con']) - hitdice = [randint(1, guild.hit_die_size) for x in range(0, npc['level'])] +def calc_hp(conmod, hit_die_size, level): + hp = 0 + hitdice = [randint(1, hit_die_size) for x in range(0, level)] for die in hitdice: die += conmod if die < 1: die = 1 - npc['hp'] += die + hp += die + + return hp - armourval = randint(0, len(data['armours']) - 1) + guild.armour_modifier +def calc_armour(armour_mod, armours): + armourval = randint(0, len(armours) - 1) + armour_mod if armourval < 0: armourval = 0 - if armourval > len(data['armours']) - 1: - armourval = len(data['armours']) - 1 - armour = data['armours'][armourval] - npc['armour'] = [armour.name, armour.gp_value, armour.ac_mod] - melee = select_melee_weapon(guild, data) - npc['melee'] = [melee.name, melee.gp_value, melee.damage_die] if melee else None - ranged = select_ranged_weapon(guild, data) - npc['ranged'] = [ranged.name, ranged.gp_value, ranged.damage_die] if ranged else None + if armourval > len(armours) - 1: + armourval = len(armours) - 1 + return armours[armourval] + +def generate_npc(base_level, data): + npc = CharacterNPC() + + npc.guild = npc_class(['Demi-Human']) + npc.level = npc_baselevel(base_level) + npc.alignment = npc_alignment() + + abilities = npc_abilities() + npc.update(npc_abilities()) + + npc.hit_points = calc_hp(attribute_mod(npc.constitution), npc.guild.hit_die_size, npc.level) + + npc.armour = calc_armour(npc.guild.armour_modifier, data['armours']) + npc.melee = select_melee_weapon(npc.guild, data) + npc.ranged = select_ranged_weapon(npc.guild, data) return npc +def name_party(party): + male_names = requests.get( + 'http://names.drycodes.com/{}'.format(ceil(len(party))), + params={'nameOptions': 'boy_names', 'separator': 'space'} + ).json() + + female_names = requests.get( + 'http://names.drycodes.com/{}'.format(floor(len(party))), + params={'nameOptions': 'girl_names', 'separator': 'space'} + ).json() + + names = female_names + male_names + shuffle(names) + + for i in range(0, len(party)): + party[i].name = names[i] + + return party + def create_party(base_level): data = { 'armours': EquipmentArmour.query.all(), @@ -133,22 +163,22 @@ def create_party(base_level): 'heavy': EquipmentMeleeWeapon.query.filter_by(bucket='Heavy').all() }, } - party_size = number_encountered() - return [generate_npc(base_level, data) for x in range(0, party_size)] + + return name_party([generate_npc(base_level, data) for x in range(0, number_encountered())]) def print_party(party): def print_npc(npc): - print('Level {0} NPC, {1}, {2} HP'.format(npc['level'], npc['guild'], npc['hp'])) + print('Level {0} NPC, {1}, {2} HP'.format(npc.level, npc.guild, npc.hp)) print('{0} Str, {1} Int, {2} Wis, {3} Dex, {4} Con, {5} Chr'.format( - npc['str'], npc['int'], npc['wis'], - npc['dex'], npc['con'], npc['chr'] + npc.str, npc.int, npc.wis, + npc.dex, npc.con, npc.chr )) - print('Armour Class: {0} - {1}, {2}gp'.format(npc['armour'][2], npc['armour'][0], npc['armour'][1])) + print('Armour Class: {0} - {1}, {2}gp'.format(npc.armour.name, npc.armour.ac_mod, npc.armour.gp_value)) print('{:^16} - {:^10} - {:^10} - {:^10}'.format('Weapon', 'Gold', 'Throw Mod', 'Damage')) print('-------------------------------------------------------') - print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc['melee'][0], npc['melee'][1], 0, npc['melee'][2])) + print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc.melee.name, 0, npc.melee.damage_die)) if npc['ranged']: - print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc['ranged'][0], npc['ranged'][1], 0, npc['ranged'][2])) + print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc.ranged.name, 0, npc.melee.damage_die)) print('\n') for npc in party: diff --git a/acks/npc/templates/generate_npc_party.html b/acks/npc/templates/generate_npc_party.html index 63c866c..b581f10 100644 --- a/acks/npc/templates/generate_npc_party.html +++ b/acks/npc/templates/generate_npc_party.html @@ -1,94 +1,204 @@ {% extends "base.html" %} +{% set active_page = "npcparty" %} + {% block title %}NPC Party Generation{% endblock %} {% block content %} -
- - +
+

Adventurer Conqueror King NPC Party Generator

-
- +
+
+ + +
+
+ +
-
+
{% if party %} -

NPC Party of Size {{ party | length }}

+
+

NPC Party of Size {{ party | length }}

+ +
-
+
{% for npc in party %}
-
-

{{ npc.guild }}

-
Level {{ npc.level }}
+
+

{{ npc.name }}

+
{{ npc.guild.name }}
+
Level
+
{{ npc.level }}
+
+
+
HP
{{ npc.hp }}
-
HP
-
{{ npc.armour[2] }}
-
AC
+
AC
+
{{ npc.armour.ac_mod }}
-
{{ npc.str }}
-
Str
+
{{ npc.str }}
+
Str
-
{{ npc.int }}
-
Int
+
{{ npc.int }}
+
Int
-
{{ npc.wis }}
-
Wis
+
{{ npc.wis }}
+
Wis
-
{{ npc.dex }}
-
Dex
+
{{ npc.dex }}
+
Dex
-
{{ npc.con }}
-
Con
+
{{ npc.con }}
+
Con
-
{{ npc.chr }}
-
Chr
+
{{ npc.chr }}
+
Chr
- - - - - - - - - - - - {% if npc.ranged %} - - - - - - - {% endif %} - - - - - - - -
NameWorthThrDmg
{{ npc.melee[0] }}{{ npc.melee[1] }}gp0{{ npc.melee[2] }}
{{ npc.ranged[0] }}{{ npc.ranged[1] }}gp0{{ npc.ranged[2] }}
{{ npc.armour[0] }}{{ npc.armour[1] }}gp
+
+
+
+
{{ npc.save_pp }}
+
P & P
+
+
+
{{ npc.save_pd }}
+
P & D
+
+
+
{{ npc.save_bb }}
+
B & B
+
+
+
{{ npc.save_sw }}
+
S & W
+
+
+
{{ npc.save_sp }}
+
Spells
+
+
+ +
    +
  • + + + + + + + + + + + + {% if npc.ranged %} + + + + + + + {% endif %} + + + + + + + +
    NameWorthThrDmg
    {{ npc.melee.name }}{{ npc.melee.gp_value }}gp{{ npc.attack_throw }}{{ npc.melee.damage_die }}
    {{ npc.ranged.name }}{{ npc.ranged.gp_value }}gp{{ npc.attack_throw }}{{ npc.ranged.damage_die }}
    {{ npc.armour.name }}{{ npc.armour.gp_value }}gp
    +
  • +
  • + + + + + + + + + + + + {% if npc.ranged %} + + + + + + + {% endif %} + + + + + + + +
    NameWorthThrDmg
    {{ npc.melee.name }}{{ npc.melee.gp_value }}gp{{ npc.attack_throw }}{{ npc.melee.damage_die }}
    {{ npc.ranged.name }}{{ npc.ranged.gp_value }}gp{{ npc.attack_throw }}{{ npc.ranged.damage_die }}
    {{ npc.armour.name }}{{ npc.armour.gp_value }}gp
    +
  • +
{% endfor %}
+ {% endif %} +
+
+ +
+

Roll20 Export

+
+
+
+
Characters to Export:
+
+ {% if party %} + {% for npc in party %} + + {% endfor %} + {% endif %} +
+
+
+ + +
+
+
+ + +
+ +
+
+
{% endblock %} diff --git a/acks/npc/views.py b/acks/npc/views.py index 729efa7..399e9df 100644 --- a/acks/npc/views.py +++ b/acks/npc/views.py @@ -1,4 +1,10 @@ -from flask import current_app, Blueprint, render_template +from flask import ( + request, + jsonify, + current_app, + render_template, + Blueprint +) from .npc_party import create_party @@ -16,4 +22,8 @@ def generate_npc_party(base_level=None): party = None if base_level: party = create_party(base_level) - return render_template('generate_npc_party.html', party=party) + + # If asked for JSON, return the party, otherwise render HTML template + if request.args.get('format', 'html') == 'json': + return jsonify([npc.roll20_format for npc in party]) + return render_template('generate_npc_party.html', party=party, base_level=base_level) diff --git a/acks/static/favicon.ico b/acks/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c5c67f5f105db217eb6f8cfa93b2ff714c762df1 GIT binary patch literal 4335 zcma)AcQhPK*I&D=-b=76S+eTtf?y>&t2}D-B*N;VM+m|q`m#29M6?jmyQnMbhG-Gp zlO+TpdYABe-tYPT{=PYL=g#lm-`u(PoH=LCOq|hQ+VnKsGynjA9;KsUa-|3U5i0Q2 zSYcV2d!@+z)KF$rS1p7J9eahTy>+bo003Ine}oK>ox=$LFr}b0RLuf&e!X_CzxS0n zyz~6JiC8R^ZX5+I?;wZGE$&--28j!n<^_F!%Vtl%6sqa(dFNBA!RDY{=rfZ}j|h-T zw`8W!R8fafjW4!m-01$?N@_jc3LX|;%~Aelz5l)a$%ukXo%3Haj&efr1^;&sDwx?l z4JHF�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 literal 0 HcmV?d00001 diff --git a/acks/templates/base.html b/acks/templates/base.html index 52841f6..cd72862 100644 --- a/acks/templates/base.html +++ b/acks/templates/base.html @@ -1,11 +1,29 @@ +{% set navigation_bar = [ + ('/', 'index', 'Home'), + ('/handbook', 'handbook', 'Handbook'), + ('/npc/party', 'npcparty', 'NPC Party'), + ('#', 'treasure', 'Treasure Generator'), + ('#', 'henchmen', 'Henchmen') +] %} +{% set active_page = active_page|default('index') %} + {% block title %}{% endblock %} - Atr0phy ACKS + - - + + + + + + + + + + {% block head %} {% endblock %} @@ -15,10 +33,11 @@
diff --git a/acks/templates/handbook.html b/acks/templates/handbook.html index d912fff..dddafce 100644 --- a/acks/templates/handbook.html +++ b/acks/templates/handbook.html @@ -1,4 +1,6 @@ {% extends "base.html" %} +{% set active_page = "handbook" %} + {% block title %}ACKS Handbook{% endblock %} {% block content %}
diff --git a/acks/templates/index.html b/acks/templates/index.html index fcb00bd..7511ad9 100644 --- a/acks/templates/index.html +++ b/acks/templates/index.html @@ -1,4 +1,6 @@ {% extends "base.html" %} +{% set active_page = "index" %} + {% block title %}ACKS Toolset Home{% endblock %} {% block content %}