Browse Source

Beginning to add DB backend

master
Brandon Cornejo 2 years ago
commit
acb3621146
  1. 15
      .gitignore
  2. 26
      acks/__init__.py
  3. 13
      acks/commands.py
  4. 10
      acks/models.py
  5. 184
      acks/npc/commands.py
  6. 23
      acks/npc/models.py
  7. 309
      acks/npc/npc_party.py
  8. 122
      acks/npc/templates/generate_npc_party.html
  9. 19
      acks/npc/views.py
  10. 29
      acks/templates/base.html
  11. 24
      acks/templates/handbook.html
  12. 11
      acks/templates/index.html
  13. 12
      acks/views.py
  14. 18
      requirements.txt
  15. 7
      start.py
  16. 11
      uwsgi.ini

15
.gitignore

@ -0,0 +1,15 @@
# Python
*.py[co]
# Vim
*.swp
# Virtualenv
bin
data
include
lib
lib64
__pycache__
share
pyvenv.cfg

26
acks/__init__.py

@ -0,0 +1,26 @@
from flask import Flask
def create_app():
app = Flask(__name__)
# Prep the database
from acks.models import db
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///../data/acks.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
# Load our views
from acks.views import default_views
app.register_blueprint(default_views)
from acks.npc.views import npc_views
app.register_blueprint(npc_views)
# Load our CLI commands
from acks.commands import default_cli
app.cli.add_command(default_cli)
from acks.npc.commands import npc_cli
app.cli.add_command(npc_cli)
return app

13
acks/commands.py

@ -0,0 +1,13 @@
import click
from flask.cli import AppGroup
from acks.models import db
default_cli = AppGroup('acks')
@default_cli.command('initdb')
def initialize_database():
from acks import models
from acks.npc import models
db.create_all()

10
acks/models.py

@ -0,0 +1,10 @@
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class BaseModel(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
created_at = db.Column(db.DateTime)

184
acks/npc/commands.py

@ -0,0 +1,184 @@
import click
from flask.cli import AppGroup
from ..models import db
npc_cli = AppGroup('npc')
@npc_cli.command('create classes')
def create_classes():
from .models import CharacterClass
classes = []
classes.append(CharacterClass(
name = 'Elven Nightblade',
bucket = 'Demi-Human',
prime_requisite = 'D+I',
hit_die_size = 6,
maximum_level = 11,
armour_modifier = -4,
melee_light = 1,
melee_medium = 2,
melee_heavy = 0,
ranged_light = 0,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Elven Spellsword',
bucket = 'Demi-Human',
prime_requisite = 'S+I',
hit_die_size = 6,
maximum_level = 10,
armour_modifier = -4,
melee_light = 0,
melee_medium = 1,
melee_heavy = 0,
ranged_light = 0,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Explorer',
bucket = 'Campaign',
prime_requisite = 'S+D',
hit_die_size = 6,
maximum_level = 14,
armour_modifier = -3,
melee_light = 1,
melee_medium = 1,
melee_heavy = 0,
ranged_light = 0,
ranged_heavy = 1
))
classes.append(CharacterClass(
name = 'Bladedancer',
bucket = 'Campaign',
prime_requisite = 'W+D',
hit_die_size = 6,
maximum_level = 14,
armour_modifier = -2,
melee_light = 0,
melee_medium = 1,
melee_heavy = 0,
ranged_light = 0,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Cleric',
bucket = 'Core',
prime_requisite = 'WIS',
hit_die_size = 6,
maximum_level = 14,
armour_modifier = 0,
melee_light = 1,
melee_medium = 3,
melee_heavy = 1,
ranged_light = 1,
ranged_heavy = 3
))
classes.append(CharacterClass(
name = 'Fighter',
bucket = 'Core',
prime_requisite = 'STR',
hit_die_size = 8,
maximum_level = 14,
armour_modifier = 1,
melee_light = 1,
melee_medium = 4,
melee_heavy = 1,
ranged_light = 1,
ranged_heavy = 1
))
classes.append(CharacterClass(
name = 'Thief',
bucket = 'Core',
prime_requisite = 'DEX',
hit_die_size = 4,
maximum_level = 14,
armour_modifier = -6,
melee_light = 1,
melee_medium = 1,
melee_heavy = 0,
ranged_light = 1,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Mage',
bucket = 'Core',
prime_requisite = 'INT',
hit_die_size = 4,
maximum_level = 14,
armour_modifier = -8,
melee_light = 1,
melee_medium = 0,
melee_heavy = 0,
ranged_light = 0,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Assassin',
bucket = 'Campaign',
prime_requisite = 'S+D',
hit_die_size = 6,
maximum_level = 14,
armour_modifier = -6,
melee_light = 1,
melee_medium = 2,
melee_heavy = 0,
ranged_light = 1,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Bard',
bucket = 'Campaign',
prime_requisite = 'D+H',
hit_die_size = 6,
maximum_level = 14,
armour_modifier = -3,
melee_light = 1,
melee_medium = 2,
melee_heavy = 0,
ranged_light = 1,
ranged_heavy = 0
))
classes.append(CharacterClass(
name = 'Dwarven Vaultguard',
bucket = 'Demi-Human',
prime_requisite = 'STR',
hit_die_size = 8,
maximum_level = 13,
armour_modifier = -1,
melee_light = 0,
melee_medium = 3,
melee_heavy = 1,
ranged_light = 1,
ranged_heavy = 1
))
classes.append(CharacterClass(
name = 'Dwarven Craftpriest',
bucket = 'Demi-Human',
prime_requisite = 'WIS',
hit_die_size = 6,
maximum_level = 10,
armour_modifier = -2,
melee_light = 0,
melee_medium = 1,
melee_heavy = 0,
ranged_light = 1,
ranged_heavy = 0
))
db.session.bulk_save_objects(classes)
db.session.commit()

23
acks/npc/models.py

@ -0,0 +1,23 @@
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)
bucket = db.Column(db.String(50))
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)

309
acks/npc/npc_party.py

@ -0,0 +1,309 @@
from random import randint
def number_encountered():
return (randint(1, 4) + 2)
def npc_class():
roll = (randint(1, 6) + randint(1, 6) + randint(1, 6))
if roll == 3 or roll == 4:
return 'Elven Nightblade'
elif roll == 5:
return 'Elven Spellsword'
elif roll == 6:
return 'Explorer'
elif roll == 7:
return 'Bladedancer'
elif roll == 8:
return 'Cleric'
elif roll == 9 or roll == 10 or roll == 11:
return 'Fighter'
elif roll == 12:
return 'Thief'
elif roll == 13:
return 'Mage'
elif roll == 14:
return 'Assassin'
elif roll == 15:
return 'Bard'
elif roll == 16:
return 'Dwarven Vaultguard'
elif roll == 17 or roll == 18:
return 'Dwarven Craftpriest'
def npc_alignment():
roll = randint(1, 6)
if roll == 1 or roll == 2:
return 'Lawful'
elif roll == 3 or roll == 4 or roll == 5:
return 'Neutral'
elif roll == 6:
return 'Chaotic'
def npc_baselevel(base_level):
roll = randint(1, 6)
mod = 0
if roll == 1:
mod = -2
elif roll == 2:
mod = -1
elif roll == 3 or roll == 4:
mod = 0
elif roll == 5:
mod = 1
elif roll == 6:
mod = 2
base_level += mod
if base_level < 1:
base_level = 1
return base_level
def npc_abilities():
return [(randint(1, 6) + randint(1, 6) + randint(1, 6)) for x in range(0,6)]
def attribute_mod(atr):
mod = 0
if atr <= 3: mod = -3
if atr >= 4 and atr <= 5: mod = -2
if atr >= 6 and atr <= 8: mod = -1
if atr >= 9 and atr <= 12: mod = 0
if atr >= 13 and atr <= 15: mod = 1
if atr >= 16 and atr <= 17: mod = 2
if atr >= 18: mod = 3
return mod
npc_class_data = {
'Elven Nightblade': {
'hd': [1, 6],
'armour': -4,
'melee': [0, 2, 1],
'ranged': [0, 0, 1]
},
'Elven Spellsword': {
'hd': [1, 6],
'armour': -4,
'melee': [0, 1, 0],
'ranged': [0, 0, 1]
},
'Explorer': {
'hd': [1, 6],
'armour': -3,
'melee': [0, 1, 1],
'ranged': [0, 1, 2]
},
'Bladedancer': {
'hd': [1, 6],
'armour': -2,
'melee': [0, 1, 0],
'ranged': [0, 0, 1]
},
'Cleric': {
'hd': [1, 6],
'armour': 0,
'melee': [1, 3, 1],
'ranged': [1, 3, 4]
},
'Fighter': {
'hd': [1, 8],
'armour': 1,
'melee': [1, 4, 1],
'ranged': [1, 1, 4],
},
'Thief': {
'hd': [1, 4],
'armour': -6,
'melee': [0, 1, 1],
'ranged': [1, 0, 6]
},
'Mage': {
'hd': [1, 4],
'armour': -8,
'melee': [0, 0, 1],
'ranged': [0, 0, 1]
},
'Assassin': {
'hd': [1, 6],
'armour': -6,
'melee': [0, 2, 1],
'ranged': [1, 0, 5]
},
'Bard': {
'hd': [1, 6],
'armour': -3,
'melee': [0, 2, 1],
'ranged': [1, 0, 4]
},
'Dwarven Vaultguard': {
'hd': [1, 8],
'armour': -1,
'melee': [1, 3, 0],
'ranged': [1, 1, 6]
},
'Dwarven Craftpriest': {
'hd': [1, 6],
'armour': -2,
'melee': [0, 1, 0],
'ranged': [1, 0, 5]
}
}
npc_armour_list = [
['Clothing Only', 0, 0],
['Hide and Fur Armour', 10, 1],
['Leather Armour', 20, 2],
['Ring Mail Armour', 30, 3],
['Scale Mail Armour', 30, 3],
['Chain Mail Armour', 40, 4],
['Banded Plate', 50, 5],
['Lamellar Armour', 50, 5],
['Plate Armour', 60, 6],
]
npc_melee_weapons_data = {
'light': [
['Club', 1, '1d4'],
['Dagger', 3, '1d4'],
['Sap', 1, '1d4'],
['Staff (1h)', 1, '1d4'],
['Staff (2h)', 1, '1d6'],
['Whip', 5, '1d2']
],
'medium': [
['Battle Axe (1h)', 7, '1d6'],
['Battle Axe (2h)', 7, '1d8'],
['Hand Axe', 4, '1d6'],
['Flail (1h)', 5, '1d6'],
['Flail (2h)', 5, '1d8'],
['Mace (1h)', 5, '1d6'],
['Mace (2h)', 5, '1d8'],
['War Hammer (1h)', 5, '1d6'],
['War Hammer (2h)', 5, '1d8'],
['Spear (1h)', 3, '1d6'],
['Spear (2h)', 3, '1d8'],
['Short Sword', 7, '1d6'],
['Sword (1h)', 10, '1d6'],
['Sword (2h)', 10, '1d8']
],
'heavy': [
['Great Axe (2h)', 10, '1d10'],
['Morning Star (2h)', 10, '1d10'],
['Pole Arm (2h)', 7, '1d10'],
['Two-Handed Sword (2h)', 15, '1d10']
]
}
npc_ranged_weapons_data = {
'light': [
['Crossbow', 30, '1d6'],
['Shortbow', 3, '1d6'],
['Javelin', 1, '1d6'],
],
'heavy': [
['Arbalest', 50, '1d8'],
['Composite Bow', 40, '1d6'],
['Longbow', 7, '1d6'],
]
}
def select_melee_weapon(guild):
melee = npc_class_data[guild]['melee']
weapons = []
for x in range(0, melee[0]):
weapons.extend(npc_melee_weapons_data['heavy'])
for x in range(0, melee[1]):
weapons.extend(npc_melee_weapons_data['medium'])
for x in range(0, melee[2]):
weapons.extend(npc_melee_weapons_data['light'])
selection = randint(0, len(weapons) - 1)
return weapons[selection]
def select_ranged_weapon(guild):
ranged = npc_class_data[guild]['ranged']
weapons = []
for x in range(0, ranged[0]):
weapons.extend(npc_ranged_weapons_data['light'])
for x in range(0, ranged[1]):
weapons.extend(npc_ranged_weapons_data['heavy'])
for x in range(0, ranged[2]):
weapons.append(None)
selection = randint(0, len(weapons) - 1)
return weapons[selection]
def generate_npc(base_level):
attributes = npc_abilities()
npc = {}
npc['guild'] = npc_class()
npc['level'] = npc_baselevel(base_level)
npc['str'] = attributes[0]
npc['int'] = attributes[1]
npc['wis'] = attributes[2]
npc['dex'] = attributes[3]
npc['con'] = attributes[4]
npc['chr'] = attributes[5]
npc['hp'] = 0
conmod = attribute_mod(npc['con'])
for x in range(0, npc['level']):
hitdice = [randint(1, npc_class_data[npc['guild']]['hd'][1]) for x in range(0, npc_class_data[npc['guild']]['hd'][0])]
for die in hitdice:
die += conmod
if die < 1:
die = 1
npc['hp'] += die
armourval = randint(0, len(npc_armour_list) - 1) + npc_class_data[npc['guild']]['armour']
if armourval < 0:
armourval = 0
if armourval > len(npc_armour_list) - 1:
armourval = len(npc_armour_list) - 1
npc['armour'] = npc_armour_list[armourval]
npc['melee'] = select_melee_weapon(npc['guild'])
npc['ranged'] = select_ranged_weapon(npc['guild'])
return npc
def create_party(base_level):
party_size = number_encountered()
return [generate_npc(base_level) for x in range(0, party_size)]
def print_party(party):
def print_npc(npc):
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']
))
print('Armour Class: {0} - {1}, {2}gp'.format(npc['armour'][2], npc['armour'][0], npc['armour'][1]))
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]))
if npc['ranged']:
print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc['ranged'][0], npc['ranged'][1], 0, npc['ranged'][2]))
print('\n')
for npc in party:
print_npc(npc)
if __name__ == '__main__':
party = create_party(2)
print_party(party)

122
acks/npc/templates/generate_npc_party.html

@ -0,0 +1,122 @@
{% extends "base.html" %}
{% block title %}NPC Party Generation{% endblock %}
{% block content %}
<div>
<label for="base_level">Base level of party to generate: </label>
<input type="number" name="base_level" id="base_level" default="1">
</div>
<div>
<button onclick="generateParty();">Generate</button>
</div>
<br>
{% if party %}
<h3>NPC Party of Size {{ party | length }}</h3>
<div class="uk-grid-medium uk-grid-match" uk-grid>
{% for npc in party %}
<div class="acks-npc-card">
<div class="uk-card uk-card-body uk-card-default">
<h4 class="uk-card-title">{{ npc.guild }}</h4>
<div class="uk-card-badge uk-label">Level {{ npc.level }}</div>
<div class="uk-flex uk-flex-around uk-text-center uk-margin-bottom">
<div>
<div>{{ npc.hp }}</div>
<div>HP</div>
</div>
<div>
<div>{{ npc.armour[2] }}</div>
<div>AC</div>
</div>
</div>
<div class="uk-flex uk-flex-around stat-block">
<div>
<div>{{ npc.str }}</div>
<div>Str</div>
</div>
<div>
<div>{{ npc.int }}</div>
<div>Int</div>
</div>
<div>
<div>{{ npc.wis }}</div>
<div>Wis</div>
</div>
<div>
<div>{{ npc.dex }}</div>
<div>Dex</div>
</div>
<div>
<div>{{ npc.con }}</div>
<div>Con</div>
</div>
<div>
<div>{{ npc.chr }}</div>
<div>Chr</div>
</div>
</div>
<table class="uk-table uk-table-hover uk-table-small item-table">
<thead>
<tr> <th>Name</th><th>Worth</th><th>Thr</th><th>Dmg</th> </tr>
</thead>
<tbody>
<tr>
<td>{{ npc.melee[0] }}</td>
<td>{{ npc.melee[1] }}gp</td>
<td>0</td>
<td>{{ npc.melee[2] }}</td>
</tr>
{% if npc.ranged %}
<tr>
<td>{{ npc.ranged[0] }}</td>
<td>{{ npc.ranged[1] }}gp</td>
<td>0</td>
<td>{{ npc.ranged[2] }}</td>
</tr>
{% endif %}
<tr>
<td>{{ npc.armour[0] }}</td>
<td>{{ npc.armour[1] }}gp</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<br>
<style>
table.item-table, table.item-table th {
font-size: 12px;
}
div.stat-block > div {
text-align: center;
padding: 3px;
width: 26px;
}
div.stat-block > div > div:first-child {
font-weight: bold;
color: green;
}
div.stat-block > div > div:last-child {
font-weight: bold;
font-size: 12px;
color: #888;
}
div.acks-npc-card {
width: 370px;
}
</style>
<script type="text/javascript">
function generateParty() {
let bl = document.querySelector('#base_level').value;
window.location = "/npcparty/" + bl.toString();
}
</script>
{% endblock %}

19
acks/npc/views.py

@ -0,0 +1,19 @@
from flask import current_app, Blueprint, render_template
from .npc_party import create_party
npc_views = Blueprint(
'npc_views',
__name__,
template_folder='templates',
url_prefix='/npc'
)
@npc_views.route('/party')
@npc_views.route('/party/<int:base_level>')
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)

29
acks/templates/base.html

@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<title>{% block title %}{% endblock %} - Atr0phy ACKS</title>
<meta charset="utf-8">
<meta name="viewport" contents="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/css/uikit.min.css" integrity="sha256-5YtK9j+Nl/245lAkSjrIs600d6edKTevi+3JYdjuHhY=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.min.js" integrity="sha256-rhLALrRmAQVu/OxzVDpQaiHAEMxiRSN8h8RDydUEh2g=" crossorigin="anonymous"></script>
{% block head %} {% endblock %}
</head>
<body>
<nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-left">
<a href="" class="uk-navbar-item uk-logo">Atr0phy ACKS</a>
</div>
<div class="uk-navbar-center">
<ul class="uk-navbar-nav">
<li><a href="/handbook">Handbook</a></li>
<li class="uk-active"><a href="/npc/party">NPC Party</a></li>
<li><a href="">Treasure</a></li>
<li><a href="">Henchmen</a></li>
</ul>
</div>
</nav>
<div class="uk-container">
{% block content %}{% endblock %}
</div>
</body>
</html>

24
acks/templates/handbook.html

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}ACKS Handbook{% endblock %}
{% block content %}
<div id="frame-container">
<iframe id="handbook-frame" src="https://atr0phy.net/acks/handbook"/>
</div>
{% endblock %}
{% block head %}
<style>
#frame-container {
display: flex;
width: 100%;
height: 85vh;
flex-direction: column:
}
#handbook-frame {
flex-grow: 1;
border: none;
margin: 0;
padding: 0;
}
</style>
{% endblock %}

11
acks/templates/index.html

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}ACKS Toolset Home{% endblock %}
{% block content %}
<div>
</div>
{% endblock %}
{% block head %}
<style>
</style>
{% endblock %}

12
acks/views.py

@ -0,0 +1,12 @@
from flask import current_app, Blueprint, render_template
default_views = Blueprint('default_views', __name__, url_prefix='/')
@default_views.route('/')
def index():
return render_template('index.html')
@default_views.route('/handbook')
def handbook():
return render_template('handbook.html')

18
requirements.txt

@ -0,0 +1,18 @@
ansi2html==1.5.2
cryptography==1.7.1
culour==0.1
enum34==1.1.6
idna==2.2
ipaddress==1.0.17
keyring==10.1
keyrings.alt==1.3
pudb==2018.1
pyasn1==0.1.9
pycrypto==2.6.1
Pygments==2.3.1
pygobject==3.22.0
pyxdg==0.25
SecretStorage==2.3.1
six==1.12.0
urwid==2.0.1
virtualenv==16.6.1

7
start.py

@ -0,0 +1,7 @@
from acks import create_app
app = create_app()
if __name__ == "__main__":
app.run()

11
uwsgi.ini

@ -0,0 +1,11 @@
[uwsgi]
module = wsgi:application
master = true
processes = 4
socket = ackstools.sock
chmod-scoket = 660
vacuum = true
die-on-term = true
Loading…
Cancel
Save