Compare commits
2 Commits
aebe890e3e
...
9282331512
Author | SHA1 | Date | |
---|---|---|---|
9282331512 | |||
3cc5bb079d |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ pyvenv.cfg
|
||||
# App specific
|
||||
data
|
||||
config
|
||||
logs
|
||||
|
@ -38,7 +38,7 @@ def create_app():
|
||||
|
||||
# Initialize API and load resources
|
||||
from flask_potion import Api, ModelResource
|
||||
api = Api(app)
|
||||
api = Api(app, prefix='/api')
|
||||
# from acks.api import api
|
||||
# api.init_app(app)
|
||||
|
||||
|
@ -4,7 +4,8 @@ from .models import (
|
||||
EquipmentArmour,
|
||||
EquipmentMeleeWeapon,
|
||||
EquipmentRangedWeapon,
|
||||
CharacterNPC
|
||||
CharacterNPC,
|
||||
Spell
|
||||
)
|
||||
|
||||
class CharacterClassResource(BaseModelResource):
|
||||
@ -28,11 +29,16 @@ class CharacterNPCResource(BaseModelResource):
|
||||
class Meta(BaseModelResource.Meta):
|
||||
model = CharacterNPC
|
||||
|
||||
class SpellResource(BaseModelResource):
|
||||
class Meta(BaseModelResource.Meta):
|
||||
model = Spell
|
||||
|
||||
|
||||
resources = [
|
||||
CharacterClassResource,
|
||||
EquipmentArmourResource,
|
||||
EquipmentMeleeWeaponResource,
|
||||
EquipmentRangedWeaponResource,
|
||||
CharacterNPCResource
|
||||
CharacterNPCResource,
|
||||
SpellResource
|
||||
]
|
||||
|
@ -14,7 +14,8 @@ def populate_npc_database():
|
||||
ClassLevelProgression,
|
||||
EquipmentArmour,
|
||||
EquipmentRangedWeapon,
|
||||
EquipmentMeleeWeapon
|
||||
EquipmentMeleeWeapon,
|
||||
Spell
|
||||
)
|
||||
|
||||
def load_csv_data(file_name, cls):
|
||||
@ -47,4 +48,13 @@ def populate_npc_database():
|
||||
prog.guild_id = classes[prog.guild_id]
|
||||
db.session.bulk_save_objects(progressions)
|
||||
|
||||
# Spells
|
||||
spells = load_csv_data('default_spells.csv', Spell)
|
||||
for spell in spells:
|
||||
if spell.arcane == '':
|
||||
spell.arcane = 0
|
||||
if spell.divine == '':
|
||||
spell.divine = 0
|
||||
db.session.bulk_save_objects(spells)
|
||||
|
||||
db.session.commit()
|
||||
|
@ -6,6 +6,7 @@ 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)
|
||||
|
||||
@ -21,7 +22,15 @@ class CharacterClass(BaseModel):
|
||||
ranged_heavy = db.Column(db.Integer)
|
||||
|
||||
def __repr__(self):
|
||||
return '<CharacterClass {0}>'.format(self.name)
|
||||
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'
|
||||
@ -35,12 +44,85 @@ class ClassLevelProgression(BaseModel):
|
||||
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 __repr__(self):
|
||||
return '<LevelProgression {0} {1}>'.format(self.level, self.guild.name)
|
||||
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 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': self.description #.replace('"', '\\"').replace("'", "\\'")
|
||||
}
|
||||
return spell_dict
|
||||
|
||||
class EquipmentArmour(BaseModel):
|
||||
__tablename__ = 'eq_armour'
|
||||
@ -50,7 +132,7 @@ class EquipmentArmour(BaseModel):
|
||||
ac_mod = db.Column(db.Integer, nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return '<EquipmentArmour {0}>'.format(self.name)
|
||||
return '<EquipmentArmour: {0}>'.format(self.name)
|
||||
|
||||
class EquipmentRangedWeapon(BaseModel):
|
||||
__tablename__ = 'eq_ranged_wep'
|
||||
@ -61,7 +143,7 @@ class EquipmentRangedWeapon(BaseModel):
|
||||
damage_die = db.Column(db.String(10), nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return '<EquipmentRangedWeapon {0}>'.format(self.name)
|
||||
return '<EquipmentRangedWeapon: {0}>'.format(self.name)
|
||||
|
||||
class EquipmentMeleeWeapon(BaseModel):
|
||||
__tablename__ = 'eq_melee_wep'
|
||||
@ -73,7 +155,7 @@ class EquipmentMeleeWeapon(BaseModel):
|
||||
two_handed = db.Column(db.Boolean, nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return '<EquipmentMeleeWeapon {0}>'.format(self.name)
|
||||
return '<EquipmentMeleeWeapon: {0}>'.format(self.name)
|
||||
|
||||
class CharacterNPC(BaseModel):
|
||||
__tablename__ = 'npcs'
|
||||
@ -90,6 +172,8 @@ class CharacterNPC(BaseModel):
|
||||
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))
|
||||
|
||||
@ -103,7 +187,7 @@ class CharacterNPC(BaseModel):
|
||||
armour = db.relationship('EquipmentArmour')
|
||||
|
||||
def __repr__(self):
|
||||
return '<CharacterNPC {0}>'.format(self.name)
|
||||
return '<CharacterNPC: {0}>'.format(self.name)
|
||||
|
||||
@staticmethod
|
||||
def calculate_attr_mod(attr):
|
||||
@ -124,6 +208,35 @@ class CharacterNPC(BaseModel):
|
||||
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 = {
|
||||
@ -260,8 +373,16 @@ class CharacterNPC(BaseModel):
|
||||
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]
|
||||
admin_models = [CharacterClass, ClassLevelProgression, EquipmentArmour, EquipmentRangedWeapon, EquipmentMeleeWeapon, CharacterNPC, Spell]
|
||||
|
@ -1,3 +1,4 @@
|
||||
import csv
|
||||
import requests
|
||||
|
||||
from math import floor, ceil
|
||||
@ -8,7 +9,8 @@ from .models import (
|
||||
EquipmentArmour,
|
||||
EquipmentRangedWeapon,
|
||||
EquipmentMeleeWeapon,
|
||||
CharacterNPC
|
||||
CharacterNPC,
|
||||
Spell
|
||||
)
|
||||
|
||||
|
||||
@ -77,9 +79,7 @@ def select_melee_weapon(guild, data):
|
||||
weapons.extend(data['melee']['medium'])
|
||||
for x in range(0, guild.melee_light):
|
||||
weapons.extend(data['melee']['light'])
|
||||
|
||||
selection = randint(0, len(weapons) - 1)
|
||||
return weapons[selection]
|
||||
return choice(weapons)
|
||||
|
||||
def select_ranged_weapon(guild, data):
|
||||
weapons = []
|
||||
@ -90,9 +90,23 @@ def select_ranged_weapon(guild, data):
|
||||
|
||||
if not weapons:
|
||||
return None
|
||||
return choice(weapons)
|
||||
|
||||
selection = randint(0, len(weapons) - 1)
|
||||
return weapons[selection]
|
||||
def select_spell_list(npc, data):
|
||||
npc_spell_list = []
|
||||
overall_spell_list = []
|
||||
if npc.is_arcane_spellcaster:
|
||||
overall_spell_list = data['spells']['arcane']
|
||||
if npc.is_divine_spellcaster:
|
||||
overall_spell_list = data['spells']['divine']
|
||||
|
||||
for level, known in enumerate(npc.spells_known(), start=1):
|
||||
level_set = set()
|
||||
while len(level_set) < known:
|
||||
level_set.add(choice(overall_spell_list[level]).id)
|
||||
npc_spell_list.extend(level_set)
|
||||
|
||||
return ','.join(str(sid) for sid in npc_spell_list)
|
||||
|
||||
def calc_hp(conmod, hit_die_size, level):
|
||||
hp = 0
|
||||
@ -118,6 +132,8 @@ def generate_npc(base_level, data):
|
||||
|
||||
npc.guild = npc_class(['Demi-Human'])
|
||||
npc.level = npc_baselevel(base_level)
|
||||
if npc.level > npc.guild.maximum_level:
|
||||
npc.level = npc.guild.maximum_level
|
||||
npc.alignment = npc_alignment()
|
||||
|
||||
abilities = npc_abilities()
|
||||
@ -128,10 +144,11 @@ def generate_npc(base_level, data):
|
||||
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)
|
||||
npc.spells = select_spell_list(npc, data)
|
||||
|
||||
return npc
|
||||
|
||||
def name_party(party):
|
||||
def name_party_api(party):
|
||||
male_names = requests.get(
|
||||
'http://names.drycodes.com/{}'.format(ceil(len(party))),
|
||||
params={'nameOptions': 'boy_names', 'separator': 'space'}
|
||||
@ -150,6 +167,33 @@ def name_party(party):
|
||||
|
||||
return party
|
||||
|
||||
def load_name_data():
|
||||
names = []
|
||||
surnames = []
|
||||
with open('acks/npc/data/fantasy_names.csv', newline='') as data:
|
||||
reader = csv.DictReader(data)
|
||||
for row in reader:
|
||||
for k,v in row.items():
|
||||
if v == '1':
|
||||
row[k] = True
|
||||
elif v == '':
|
||||
row[k] = False
|
||||
if row['Family Name']:
|
||||
surnames.append(row['Name'])
|
||||
else:
|
||||
names.append(row['Name'])
|
||||
return (names, surnames)
|
||||
|
||||
def name_party(party):
|
||||
names, surnames = load_name_data()
|
||||
shuffle(names)
|
||||
shuffle(surnames)
|
||||
|
||||
for i in range(0, len(party)):
|
||||
party[i].name = '{} {}'.format(choice(names), choice(surnames))
|
||||
|
||||
return party
|
||||
|
||||
def create_party(base_level):
|
||||
data = {
|
||||
'armours': EquipmentArmour.query.all(),
|
||||
@ -162,8 +206,24 @@ def create_party(base_level):
|
||||
'medium': EquipmentMeleeWeapon.query.filter_by(bucket='Medium').all(),
|
||||
'heavy': EquipmentMeleeWeapon.query.filter_by(bucket='Heavy').all()
|
||||
},
|
||||
'spells': {
|
||||
'divine': {},
|
||||
'arcane': {},
|
||||
}
|
||||
}
|
||||
|
||||
for spell in Spell.query.all():
|
||||
if spell.is_arcane:
|
||||
spells = [spell]
|
||||
if data['spells']['arcane'] and spell.arcane in data['spells']['arcane']:
|
||||
spells.extend(data['spells']['arcane'][spell.arcane])
|
||||
data['spells']['arcane'][spell.arcane] = spells
|
||||
if spell.is_divine:
|
||||
spells = [spell]
|
||||
if data['spells']['divine'] and spell.divine in data['spells']['divine']:
|
||||
spells.extend(data['spells']['divine'][spell.divine])
|
||||
data['spells']['divine'][spell.divine] = spells
|
||||
|
||||
return name_party([generate_npc(base_level, data) for x in range(0, number_encountered())])
|
||||
|
||||
def print_party(party):
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="uk-flex uk-flex-bottom uk-flex-center uk-margin-large-bottom">
|
||||
<div>
|
||||
<label for="base_level">Base level of party to generate: </label>
|
||||
<input type="number" name="base_level" id="base_level" class="uk-input" value="{{ base_level if base_level else 1 }}" >
|
||||
<input type="number" name="base_level" id="base_level" class="uk-input" value="{{ base_level if base_level else 1 }}" min="0" max="14">
|
||||
</div>
|
||||
<div class="uk-margin-left">
|
||||
<button class="uk-button uk-button-primary" onclick="generateParty();">Generate</button>
|
||||
@ -97,7 +97,7 @@
|
||||
</div>
|
||||
<ul uk-tab>
|
||||
<li class="uk-active"><a href="">Equipment</a></li>
|
||||
<li {% if not npc.spellcaster %}class="uk-disabled"{% endif %}><a href="">Spells</a></li>
|
||||
<li {% if not npc.is_divine_spellcaster and not npc.is_arcane_spellcaster %}class="uk-disabled"{% endif %}><a href="">Spells</a></li>
|
||||
</ul>
|
||||
<ul class="uk-switcher uk-margin">
|
||||
<li>
|
||||
@ -130,31 +130,21 @@
|
||||
</table>
|
||||
</li>
|
||||
<li>
|
||||
<table class="uk-table uk-table-hover uk-table-small item-table">
|
||||
<table class="uk-table uk-table-hover uk-table-small spell-table">
|
||||
<thead>
|
||||
<tr> <th>Name</th><th>Worth</th><th>Thr</th><th>Dmg</th> </tr>
|
||||
<tr> <th>Name</th><th>Range</th><th>Duration</th><th>Level</th> </tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ npc.melee.name }}</td>
|
||||
<td>{{ npc.melee.gp_value }}gp</td>
|
||||
<td>{{ npc.attack_throw }}</td>
|
||||
<td>{{ npc.melee.damage_die }}</td>
|
||||
</tr>
|
||||
{% if npc.ranged %}
|
||||
<tr>
|
||||
<td>{{ npc.ranged.name }}</td>
|
||||
<td>{{ npc.ranged.gp_value }}gp</td>
|
||||
<td>{{ npc.attack_throw }}</td>
|
||||
<td>{{ npc.ranged.damage_die }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{{ npc.armour.name }}</td>
|
||||
<td>{{ npc.armour.gp_value }}gp</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% for level in npc.spell_list() %}
|
||||
{% for spell in npc.spell_list()[level] %}
|
||||
<tr>
|
||||
<td data-spell-id="{{ spell.id }}" onclick="showSpellModal();">{{ spell.name }}</td>
|
||||
<td>{{ spell.range }}</td>
|
||||
<td>{{ spell.duration }}</td>
|
||||
<td>{{ spell.level_for(npc) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
@ -166,6 +156,23 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div id="spell-modal" uk-modal>
|
||||
<div class="uk-modal-dialog uk-margin-auto-vertical">
|
||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||
<div class="uk-modal-header">
|
||||
<h2 class="uk-modal-title"><span id="spell-modal-title">{Spell Title}</h2>
|
||||
</div>
|
||||
<div class="uk-modal-body">
|
||||
<ul>
|
||||
<li>Range: <span id="spell-modal-range">{Spell Range}</span></li>
|
||||
<li>Duration: <span id="spell-modal-duration">{Spell Duration}</span></li>
|
||||
<li><span id="spell-modal-school">{Spell School}</span></li>
|
||||
</ul>
|
||||
<p id="spell-modal-desc">{Spell Description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="party-export-modal" uk-modal>
|
||||
<div class="uk-modal-dialog uk-margin-auto-vertical">
|
||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||
@ -202,7 +209,7 @@
|
||||
<br>
|
||||
|
||||
<style>
|
||||
table.item-table, table.item-table th {
|
||||
table.item-table, table.item-table th, table.spell-table, table.spell-table th {
|
||||
font-size: 12px;
|
||||
}
|
||||
div.stat-block > div {
|
||||
@ -237,13 +244,58 @@ div.acks-npc-card {
|
||||
|
||||
function generateParty() {
|
||||
let bl = document.querySelector('#base_level').value;
|
||||
if(bl < 0) { bl = 0; }
|
||||
if(bl > 14) { bl = 14; }
|
||||
window.location = "/npc/party/" + bl.toString();
|
||||
}
|
||||
|
||||
function showSpellModal() {
|
||||
spells = new Set();
|
||||
{% if party %}
|
||||
{% for npc in party %}
|
||||
{% for level in npc.spell_list() %}
|
||||
{% for spell in npc.spell_list()[level] %}
|
||||
spells.add({{ spell.roll20_format | tojson }});
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
var modal_spell, spell_id = event.target.dataset.spellId;
|
||||
for(var spell of spells) {
|
||||
if(spell.id == spell_id) {
|
||||
modal_spell = spell
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in spell details
|
||||
document.querySelector('#spell-modal-title').innerText = modal_spell.name;
|
||||
document.querySelector('#spell-modal-desc').innerText = modal_spell.description;
|
||||
|
||||
if(spell.range) {
|
||||
document.querySelector('#spell-modal-range').innerText = modal_spell.range;
|
||||
}
|
||||
|
||||
if(spell.duration) {
|
||||
document.querySelector('#spell-modal-duration').innerText = modal_spell.duration;
|
||||
}
|
||||
|
||||
var school = '';
|
||||
if(modal_spell.is_arcane) {
|
||||
school += 'Arcane ' + modal_spell.arcane + ' ';
|
||||
}
|
||||
if(modal_spell.is_divine) {
|
||||
school += 'Divine ' + modal_spell.divine + ' ';
|
||||
}
|
||||
document.querySelector('#spell-modal-school').innerText = school;
|
||||
|
||||
UIkit.modal(document.querySelector('#spell-modal')).show();
|
||||
}
|
||||
|
||||
function showExportModal() {
|
||||
{% if party %}
|
||||
{% for npc in party %}
|
||||
party.push(JSON.parse('{{ npc.roll20_format | tojson }}'));
|
||||
party.push(JSON.parse('{{ npc.roll20_format | tojson }}'));
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
73
acks/npc/templates/spell_list.html
Normal file
73
acks/npc/templates/spell_list.html
Normal file
@ -0,0 +1,73 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "spells" %}
|
||||
|
||||
{% block title %}Spell List{% endblock %}
|
||||
{% block content %}
|
||||
<div class="uk-flex uk-flex-center uk-margin-bottom uk-margin-top">
|
||||
<h1>Adventurer Conqueror King Spell List</h1>
|
||||
</div>
|
||||
<div class="uk-flex uk-flex-bottom uk-flex-center uk-margin-large-bottom">
|
||||
<div>
|
||||
<label for="base_level">TURN INTO FILTER AREA</label>
|
||||
<input type="number" name="base_level" id="base_level" class="uk-input" value="{{ base_level if base_level else 1 }}" >
|
||||
</div>
|
||||
<div class="uk-margin-left">
|
||||
<button class="uk-button uk-button-primary" onclick="generateParty();">Generate</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="uk-divider-icon">
|
||||
|
||||
{% if spells %}
|
||||
<div class="uk-grid-medium uk-grid-match uk-flex-center" uk-grid>
|
||||
{% for spell in spells %}
|
||||
<div class="acks-npc-card">
|
||||
<div class="uk-card uk-card-body uk-card-default uk-box-shadow-hover-large">
|
||||
<h4 class="uk-card-title uk-margin-small-top">{{ spell.name }}</h4>
|
||||
<ul>
|
||||
<li>Arcane: {{ spell.arcane }}</li>
|
||||
<li>Divine: {{ spell.divine }}</li>
|
||||
<li>Duration: {{ spell.duration }}</li>
|
||||
<li>Range: {{ spell.range }}</li>
|
||||
</ul>
|
||||
<div>
|
||||
{{ spell.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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.save-block > div > div:first-child {
|
||||
font-weight: bold;
|
||||
color: purple;
|
||||
}
|
||||
div.save-block > div > div:last-child {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
div.acks-npc-card {
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
@ -7,6 +7,7 @@ from flask import (
|
||||
)
|
||||
|
||||
from .npc_party import create_party
|
||||
from .models import Spell
|
||||
|
||||
|
||||
npc_views = Blueprint(
|
||||
@ -27,3 +28,8 @@ def generate_npc_party(base_level=None):
|
||||
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)
|
||||
|
||||
@npc_views.route('/spells')
|
||||
def spell_list():
|
||||
spells = Spell.query.all()
|
||||
return render_template('spell_list.html', spells=spells)
|
||||
|
@ -2,8 +2,10 @@
|
||||
('/', 'index', 'Home'),
|
||||
('/handbook', 'handbook', 'Handbook'),
|
||||
('/npc/party', 'npcparty', 'NPC Party'),
|
||||
('#', 'treasure', 'Treasure Generator'),
|
||||
('#', 'henchmen', 'Henchmen')
|
||||
('/npc/spells', 'spells', 'Spells'),
|
||||
('/api/schema', 'api', 'API'),
|
||||
('#', 'treasure', 'Treasure'),
|
||||
('#', 'henchmen', 'Henchmen'),
|
||||
] %}
|
||||
{% set active_page = active_page|default('index') %}
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
from flask import current_app, Blueprint, render_template
|
||||
from flask import current_app, Blueprint, render_template, url_for, redirect
|
||||
|
||||
|
||||
default_views = Blueprint('default_views', __name__, url_prefix='/')
|
||||
|
||||
@default_views.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
return redirect(url_for('npc_views.generate_npc_party'))
|
||||
# return render_template('index.html')
|
||||
|
||||
@default_views.route('/handbook')
|
||||
def handbook():
|
||||
|
359
roll20/char_sheet.css
Normal file
359
roll20/char_sheet.css
Normal file
@ -0,0 +1,359 @@
|
||||
.sheet-main {
|
||||
display: grid;
|
||||
width: 800px;
|
||||
height: 900px;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 130px 1fr 1fr 1fr;
|
||||
|
||||
grid-template-areas:"header header"
|
||||
"attributes saves"
|
||||
"lactions ractions"
|
||||
"equipment loot"
|
||||
"spells spells";
|
||||
|
||||
}
|
||||
.sheet-npc {
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.sheet-section {
|
||||
padding: 5px;
|
||||
border-style: solid;
|
||||
}
|
||||
.sheet-inner-section {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/** SECTION LISTING **/
|
||||
.sheet-header {
|
||||
grid-area: header;
|
||||
/*background-color: olive;*/
|
||||
}
|
||||
.sheet-attributes {
|
||||
grid-area: attributes;
|
||||
/*background-color: orange;*/
|
||||
}
|
||||
.sheet-saves {
|
||||
grid-area: saves;
|
||||
/*background-color: brown;*/
|
||||
}
|
||||
.sheet-lactions {
|
||||
grid-area: lactions;
|
||||
/*background-color: coral;*/
|
||||
}
|
||||
.sheet-ractions {
|
||||
grid-area: ractions;
|
||||
/*background-color: green;*/
|
||||
}
|
||||
.sheet-spells {
|
||||
grid-area: spells;
|
||||
/*grid-column: 1 / span 2;*/
|
||||
}
|
||||
|
||||
/** INNER GRIDS **/
|
||||
div.sheet-grid-container-2col {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
div.sheet-header-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 30px 1fr;
|
||||
row-gap: 5px;
|
||||
column-gap: 5px;
|
||||
}
|
||||
div.sheet-header-field-grid {
|
||||
padding: 5px 15px;
|
||||
display: grid;
|
||||
grid-template-columns: 80px 1fr;
|
||||
}
|
||||
div.sheet-stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 50px 50px 50px 50px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
div.sheet-stat2-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
row-gap: 5px;
|
||||
column-gap: 5px;
|
||||
}
|
||||
div.sheet-movement-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
row-gap: 5px;
|
||||
}
|
||||
div.sheet-saves-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 100px auto auto auto;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
}
|
||||
div.sheet-attacks-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 50px 50px 50px;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
div.sheet-proficiencies-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 50px 50px;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
div.sheet-equipment-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 75px;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
div.sheet-currency-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
}
|
||||
div.sheet-spells-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
row-gap: 5px;
|
||||
column-gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/** GENERAL ELEMENT STYLE **/
|
||||
input.sheet-line-input {
|
||||
width: 214px!important;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
border-bottom: 1px solid black;
|
||||
background: rgba(0, 0, 0, 0.0);
|
||||
border-radius: 0px;
|
||||
}
|
||||
input.sheet-name-input {
|
||||
width: 100%;
|
||||
}
|
||||
div.sheet-spells-grid span {
|
||||
text-align: center;
|
||||
}
|
||||
div.sheet-spells.grid input {
|
||||
padding-left: 50px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
.sheet-stat-input {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.sheet-stat-span {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
font-family: "Candal";
|
||||
}
|
||||
.sheet-initiative-span {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
input.sheet-currency-input {
|
||||
font-size: 11px;
|
||||
width: 60px!important;
|
||||
}
|
||||
input.sheet-spells-input {
|
||||
width: 60px!important;
|
||||
margin-left: 22px;
|
||||
}
|
||||
.sheet-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.sheet-title-line {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
.sheet-textarea {
|
||||
outline: 0;
|
||||
border-radius: 0;
|
||||
box-sizing: content-box;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
overflow-y: hidden;
|
||||
padding: 10px;
|
||||
max-width: 90%;
|
||||
resize: none;
|
||||
}
|
||||
.sheet-roll-button {
|
||||
font-size: 1.6em;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
.sheet-header-field-grid select {
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.sheet-header-field-grid span {
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.sheet-hidden {
|
||||
display: none;
|
||||
}
|
||||
.sheet-margin-top {
|
||||
margin-top: 35px;
|
||||
}
|
||||
.sheet-grid-span-1-2 {
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
.sheet-grid-span-4-2 {
|
||||
grid-column: 4 / span 2;
|
||||
}
|
||||
input.sheet-input-big {
|
||||
width: 55px!important;
|
||||
}
|
||||
|
||||
/** INDIVIDUAL ELEMENT STYLES **/
|
||||
.sheet-system-title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.sheet-stat-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 4;
|
||||
}
|
||||
.sheet-movement-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
.sheet-saves-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 4;
|
||||
}
|
||||
.sheet-attack-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 4;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.sheet-proficiency-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 3;
|
||||
margin-top: 25px;
|
||||
}
|
||||
.sheet-equipment-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
.sheet-currency-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 5;
|
||||
margin-top: 25px;
|
||||
}
|
||||
.sheet-spells-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 7;
|
||||
}
|
||||
.sheet-exp-title {
|
||||
text-align: center;
|
||||
grid-column: 1 / span 5;
|
||||
}
|
||||
.sheet-hp-span {
|
||||
font-size: 28px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
vertical-align: bottom;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
.sheet-hp-input {
|
||||
width: 75%;
|
||||
float: right;
|
||||
}
|
||||
.sheet-ac-wrapper {
|
||||
grid-column: 1 / span 2;
|
||||
text-align: center;
|
||||
}
|
||||
.sheet-hp-mod-input {
|
||||
width: 75%;
|
||||
float: left;
|
||||
}
|
||||
.sheet-ac-input {
|
||||
width: 50%;
|
||||
}
|
||||
.sheet-exp-current {
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
.sheet-exp-needed {
|
||||
grid-column: 3 / span 2;
|
||||
}
|
||||
.sheet-exp-current > input, .sheet-exp-needed > input {
|
||||
width: 150px;
|
||||
}
|
||||
.sheet-lootbox {
|
||||
grid-column: 1 / span 5;
|
||||
}
|
||||
div.sheet-npc-rsection, div.sheet-npc-lsection {
|
||||
padding: 5px;
|
||||
border: 4px solid black;
|
||||
border-radius: 4px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.sheet-spells-grid-daily > input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/** Roll Templates **/
|
||||
.sheet-rolltemplate-acks {}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-container {
|
||||
border: 2px solid black;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-acks div.sheet-acks-header {
|
||||
background-color: rgba(112, 32, 130, 1);
|
||||
border-bottom: 1px solid black;
|
||||
padding: 5px;
|
||||
|
||||
line-height: 1.6em;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
color: #FFF;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-subheader {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: rgb(112, 32, 130);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-row {
|
||||
/*border-bottom: 1px solid black;*/
|
||||
padding: 10px;
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-row .inlinerollresult {
|
||||
float: right;
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-row .sheet-acks-target-value {
|
||||
float: right;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: green;
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-row:nth-child(odd) {
|
||||
background-color: rgba(217, 217, 214, 1);
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-acks-row:nth-child(even) {
|
||||
background-color: rgba(233, 233, 233, 1);
|
||||
}
|
||||
.sheet-rolltemplate-acks div.sheet-ask-desc {
|
||||
font-size: 0.7em;
|
||||
color: rgb(115, 115, 115);
|
||||
}
|
||||
|
||||
div.sheet-main { display: grid; }
|
||||
div.sheet-npc { display: none; }
|
||||
/*input.sheet-block-switch { display: none; };*/
|
||||
input.sheet-block-switch:checked ~ div.sheet-main { display: none; }
|
||||
input.sheet-block-switch:checked ~ div.sheet-npc { display: grid; }
|
714
roll20/char_sheet.html
Normal file
714
roll20/char_sheet.html
Normal file
@ -0,0 +1,714 @@
|
||||
<input type="checkbox" class="sheet-block-switch" name="attr_npcsheet" value="1"><span> NPC? (DO NOT CLICK)</span><br/><br/>
|
||||
<div class="main">
|
||||
<div class="header section">
|
||||
<div class="header-grid">
|
||||
<div class="header-field-grid">
|
||||
<span>Name</span>
|
||||
<input class="line-input" name="attr_character_name">
|
||||
</div>
|
||||
<div>
|
||||
<h2>Adventurer Conqueror King</h2>
|
||||
</div>
|
||||
<div class="header-field-grid">
|
||||
<span>Title</span>
|
||||
<input class="line-input" type="text" name="attr_classtitle" value="the Unknown">
|
||||
|
||||
<span>Class</span>
|
||||
<select name="attr_class">
|
||||
<option>Fighter</option>
|
||||
<option>Mage</option>
|
||||
<option>Cleric</option>
|
||||
<option>Thief</option>
|
||||
<option>Assassin</option>
|
||||
<option>Bard</option>
|
||||
<option>Bladedancer</option>
|
||||
<option>Explorer</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="header-field-grid">
|
||||
<span>Alignment</span>
|
||||
<input class="line-input" type="text" name="attr_align" value="">
|
||||
|
||||
<span>Level</span>
|
||||
<input class="line-input" type="text" name="attr_level" value="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attributes section">
|
||||
<div class="grid-container-2col">
|
||||
<div class="stat-grid">
|
||||
<div class="sheet-stat-title">
|
||||
<h3>Attributes</h3>
|
||||
</div>
|
||||
|
||||
<span>Value</span><span></span><span>Modifier</span><span></span>
|
||||
<!-- Strength -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_str" value="10">
|
||||
<span class="sheet-stat-span">Str</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_str_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Strength Throw}} {{roll=[[1d20 + @{str_mod}]]}}' name='roll_StrengthCheck'></button>
|
||||
|
||||
<!-- Intelligence -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_int" value="10">
|
||||
<span class="sheet-stat-span">Int</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_int_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Intelligence Throw}} {{roll=[[1d20 + @{int_mod}]]}}' name='roll_IntelligenceCheck'></button>
|
||||
|
||||
<!-- Wisdom -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_wis" value="10">
|
||||
<span class="sheet-stat-span">Wis</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_wis_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Wisdom Throw}} {{roll=[[1d20 + @{wis_mod}]]}}' name='roll_WisdomCheck'></button>
|
||||
|
||||
<!-- Dexterity -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_dex" value="10">
|
||||
<span class="sheet-stat-span">Dex</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_dex_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Dexterity Throw}} {{roll=[[1d20 + @{dex_mod}]]}}' name='roll_DexterityCheck'></button>
|
||||
|
||||
<!-- Constitution -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_con" value="10">
|
||||
<span class="sheet-stat-span">Con</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_con_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Constitution Throw}} {{roll=[[1d20 + @{con_mod}]]}}' name='roll_ConstitutionCheck'></button>
|
||||
|
||||
<!-- Charisma -->
|
||||
<input class="sheet-stat-input" type="text" name="attr_chr" value="10">
|
||||
<span class="sheet-stat-span">Chr</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_chr_mod" value="0">
|
||||
<button class="sheet-roll-button" type='roll' value='&{template:acks} {{name=Charisma Throw}} {{roll=[[1d20 + @{chr_mod}]]}}' name='roll_CharismaCheck'></button>
|
||||
</div>
|
||||
<div class="stat2-grid">
|
||||
<!-- Hit Points -->
|
||||
<span class="sheet-hp-span">HP</span>
|
||||
<span class="sheet-text-center">Current</span><span class="sheet-text-center">Maximum</span>
|
||||
<div><input class="sheet-stat-input sheet-hp-input" type="text" name="attr_hp"></div>
|
||||
<div><input class="sheet-stat-input sheet-hp-mod-input" type="text" name="attr_hp_max"></div>
|
||||
|
||||
<!-- Hit Points -->
|
||||
<span class="sheet-hp-span">AC</span>
|
||||
<div class="sheet-ac-wrapper"><input class="sheet-stat-input sheet-ac-input" type="text" name="attr_ac" value="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="saves section">
|
||||
<div class="grid-container-2col">
|
||||
<div class="movement-grid">
|
||||
<div class="sheet-movement-title">
|
||||
<h3>Movement</h3>
|
||||
</div>
|
||||
|
||||
<span class="sheet-save-span">Base</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_base_movement" value="120">
|
||||
|
||||
<span class="sheet-save-span">Combat</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_combat_movement" value="floor(@{base_movement} / 3)" disabled="true">
|
||||
|
||||
<span class="sheet-save-span">Chrg/Run</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_charge_movement" value="@{base_movement}" disabled="true">
|
||||
|
||||
<span class="sheet-save-span">Climb</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_climb_movement">
|
||||
|
||||
<span class="sheet-save-span">Stealth</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_stealth_movement" value="floor(@{base_movement} / 2)" disabled="true">
|
||||
|
||||
<span class="sheet-save-span">Daily</span>
|
||||
<input class="sheet-stat-input sheet-input-big" type="text" name="attr_daily_movement" value="floor(@{base_movement} * 2 / 10)" disabled="true">
|
||||
</div>
|
||||
<div class="saves-grid">
|
||||
<div class="sheet-saves-title">
|
||||
<h3>Saving Throws</h3>
|
||||
</div>
|
||||
|
||||
<span></span><span>Throw</span><span>Modifier</span><span></span>
|
||||
|
||||
<span class="sheet-save-span">Petrif. & Paral.</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pp"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pp_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Petrification & Paralysis}} {{roll=[[1d20 + @{save_pp_mod}]]}} {{target=[[@{save_pp}]]}}" name="roll_PPSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Poison & Death</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pd"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pd_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Poison & Death}} {{roll=[[1d20 + @{save_pd_mod}]]}} {{target=[[@{save_pd}]]}}" name="roll_PDSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Blast & Breath</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_bb"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_bb_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Blast & Breath}} {{roll=[[1d20 + @{save_bb_mod}]]}} {{target=[[@{save_bb}]]}}" name="roll_BBSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Staffs & Wands</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sw"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sw_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Staffs & Wands}} {{roll=[[1d20 + @{save_sw_mod}]]}} {{target=[[@{save_sw}]]}}" name="roll_SWSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Spells</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sp"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sp_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Spells}} {{roll=[[1d20 + @{save_sp_mod}]]}} {{target=[[@{save_sp}]]}}" name="roll_SPSave"></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lactions section">
|
||||
<div class="attacks-grid">
|
||||
<!-- Initiative -->
|
||||
<span class="sheet-initiative-span">Initiative</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_initiative_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Initiative Roll}} {{subheader=@{character_name}}} {{roll=[[1d6 + @{initiative_mod} &{tracker}]]}}" name="roll_Initiative"></button></div>
|
||||
|
||||
<span class="sheet-initiative-span">Attack Throw</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_attack_throw_vis" value="@{attack_throw}" disabled="true"></div>
|
||||
<div><input class="sheet-stat-input sheet-hidden" type="text" name="attr_attack_throw"></div>
|
||||
</div>
|
||||
<div class="attacks-grid">
|
||||
<div class="sheet-attack-title">
|
||||
<h3>Weapons & Attacks</h3>
|
||||
</div>
|
||||
<span>Attack Name</span><span>Th. Mod</span><span>Damage</span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_attacks">
|
||||
<div class="attacks-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_attack_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_attack_throw_mod"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_attack_dmg"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{attack_name} Attack}} {{roll=[[1d20 + @{attack_throw_mod}]]}} {{target=[[@{attack_throw}]]}} {{damage=[[@{attack_dmg}]]}}" name="roll_Attack"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="proficiencies-grid">
|
||||
<div class="sheet-proficiency-title">
|
||||
<h3>Proficiencies</h3>
|
||||
</div>
|
||||
<span>Proficiency</span><span>Desc</span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_proficiencies">
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_proficiency_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_proficiency_desc"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{proficiency_name} Proficency}} {{desc=@{proficiency_desc}}}" name="roll_Proficiency"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="ractions section">
|
||||
<div class="proficiencies-grid">
|
||||
<div class="sheet-proficiency-title">
|
||||
<h3>Class & Race Abilities</h3>
|
||||
</div>
|
||||
<span>Ability Name</span><span>Throw</span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_abilities">
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_ability_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_ability_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{ability_name}}} {{@subheader=Class/Race Ability}} {{roll=[[1d20]]}} {{target=@{ability_throw}}}" name="roll_Ability"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="proficiencies-grid">
|
||||
<div class="sheet-proficiency-title">
|
||||
<h3>Actions</h3>
|
||||
</div>
|
||||
<span>Action Name</span><span>Throw</span><span></span>
|
||||
</div>
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_opendoor_name" value="Open Door" disabled>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_opendoor_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="/roll 1d20" name="roll_OpenDoor"></button></div>
|
||||
</div>
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_detectdoor_name" value="Detect Secret Door" disabled>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_detectdoor_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="/roll 1d20" name="roll_DetectDoor"></button></div>
|
||||
</div>
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_hearnoise_name" value="Hear Noise" disabled>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_hearnoise_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="/roll 1d20" name="roll_HearNoise"></button></div>
|
||||
</div>
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_findtraps_name" value="Find Traps" disabled>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_findtraps_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="?{Spell|Cure Light Wounds, **Cure Light Wounds** Target Regains [[1d8+5]] HP. | Cure Moderate Wounds, **Cure Moderate Wounds** Target Regains [[2d8+8]] HP. | Cure Serious Wounds, **Cure Serious Wounds** Target Regains [[3d8+8]] HP.}" name="roll_FindTraps"></button></div>
|
||||
</div>
|
||||
<fieldset class="repeating_actions">
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_action_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_action_throw"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="/roll 1d20" name="roll_Action"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="equipment section">
|
||||
<div class="equipment-grid">
|
||||
<div class="sheet-equipment-title">
|
||||
<h3>Equipment & Magic Items</h3>
|
||||
</div>
|
||||
<span>Item Name</span><span>Encumb.</span>
|
||||
</div>
|
||||
<fieldset class="repeating_equipment">
|
||||
<div class="equipment-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_equipment_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_equipment_encumb"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="loot section">
|
||||
<div class="currency-grid">
|
||||
<div class="sheet-exp-title">
|
||||
<h3>Experience</h3>
|
||||
</div>
|
||||
<span>Current</span><span></span><span>Needed</span><span></span><span>Bonus</span>
|
||||
<div class="sheet-exp-current"><input type="text" name="attr_exp_current"></div>
|
||||
<div class="sheet-exp-needed"><input type="text" name="attr_exp_needed"></div>
|
||||
<div><input class="sheet-currency-input" type="text" name="attr_exp_bonus"></div>
|
||||
|
||||
|
||||
<div class="sheet-currency-title">
|
||||
<h3>Loot</h3>
|
||||
</div>
|
||||
<span>Copper</span><span>Silver</span><span>Electrum</span><span>Gold</span><span>Platinum</span>
|
||||
<input class="currency-input" type="text" name="attr_cp">
|
||||
<input class="currency-input" type="text" name="attr_sp">
|
||||
<input class="currency-input" type="text" name="attr_ep">
|
||||
<input class="currency-input" type="text" name="attr_gp">
|
||||
<input class="currency-input" type="text" name="attr_pp">
|
||||
|
||||
<div class="sheet-lootbox"><textarea class="sheet-textarea" wrap="off" name="attr_loot"></textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spells section">
|
||||
<div class="spells-grid spells-grid-daily">
|
||||
<div class="sheet-spells-title">
|
||||
<h3>Spells Per Day</h3>
|
||||
</div>
|
||||
<span></span><span>One</span><span>Two</span><span>Three</span><span>Four</span><span>Five</span><span>Six</span>
|
||||
<span>Used</span>
|
||||
<input class="spells-input" type="text" name="attr_spells_l1">
|
||||
<input class="spells-input" type="text" name="attr_spells_l2">
|
||||
<input class="spells-input" type="text" name="attr_spells_l3">
|
||||
<input class="spells-input" type="text" name="attr_spells_l4">
|
||||
<input class="spells-input" type="text" name="attr_spells_l5">
|
||||
<input class="spells-input" type="text" name="attr_spells_l6">
|
||||
|
||||
<span>Total</span>
|
||||
<input class="spells-input" type="text" name="attr_spells_l1_max">
|
||||
<input class="spells-input" type="text" name="attr_spells_l2_max">
|
||||
<input class="spells-input" type="text" name="attr_spells_l3_max">
|
||||
<input class="spells-input" type="text" name="attr_spells_l4_max">
|
||||
<input class="spells-input" type="text" name="attr_spells_l5_max">
|
||||
<input class="spells-input" type="text" name="attr_spells_l6_max">
|
||||
</div>
|
||||
<div class="spells-grid">
|
||||
<div class="sheet-spells-title sheet-margin-top">
|
||||
<h3>Spellbook</h3>
|
||||
</div>
|
||||
<span class="grid-span-1-2">Spell Name</span><span>Level</span><span class="grid-span-4-2">Effect</span><span>Damage</span><span></span><span></span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_spells">
|
||||
<div class="spells-grid">
|
||||
<input class="sheet-name-input grid-span-1-2" type="text" name="attr_spell_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_spell_level"></div>
|
||||
<div class="grid-span-4-2"><input class="sheet-stat-input" type="text" name="attr_spell_effect"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_spell_damage"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{spell_name}}} {{subheader=Level @{spell_level} Arcane Spell}} {{desc=@{spell_effect}}} {{damage=[[@{spell_damage}]]}}" name="roll_Spell"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="npc">
|
||||
<div class="npc-header sheet-grid-span-1-2 sheet-text-center">
|
||||
<h2>Adventurer Conqueror King System</h2>
|
||||
<h4>NPC/Monster Stat Block</h4>
|
||||
<br><br><br>
|
||||
</div>
|
||||
<div class="npc-lsection">
|
||||
<div class="attacks-grid">
|
||||
<span class="sheet-initiative-span">Attack Throw</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_npc_attack_throw_vis" value="@{npc_attack_throw}" disabled="true"></div>
|
||||
<div><input class="sheet-stat-input sheet-hidden" type="text" name="attr_npc_attack_throw"></div>
|
||||
|
||||
<span class="sheet-initiative-span">Armour Class</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_ac" value="0"></div>
|
||||
<div></div>
|
||||
|
||||
<!-- Initiative -->
|
||||
<span class="sheet-initiative-span">Initiative</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_initiative_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Initiative Roll}} {{subheader=@{character_name}}} {{roll=[[1d6 + @{initiative_mod} &{tracker}]]}}" name="roll_Initiative"></button></div>
|
||||
</div>
|
||||
<div class="attacks-grid">
|
||||
<div class="sheet-attack-title">
|
||||
<h3>Weapons & Attacks</h3>
|
||||
</div>
|
||||
<span>Attack Name</span><span>Th. Mod</span><span>Damage</span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_attacks">
|
||||
<div class="attacks-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_attack_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_attack_throw_mod"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_attack_dmg"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{attack_name} Attack}} {{roll=[[1d20 + @{attack_throw_mod}]]}} {{target=[[@{attack_throw}]]}} {{damage=[[@{attack_dmg}]]}}" name="roll_Attack"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="proficiencies-grid">
|
||||
<div class="sheet-proficiency-title">
|
||||
<h3>Proficiencies</h3>
|
||||
</div>
|
||||
<span>Proficiency</span><span>Desc</span><span></span>
|
||||
</div>
|
||||
<fieldset class="repeating_proficiencies">
|
||||
<div class="proficiencies-grid">
|
||||
<input class="sheet-name-input" type="text" name="attr_proficiency_name">
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_proficiency_desc"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=@{proficiency_name} Proficency}} {{desc=@{proficiency_desc}}}" name="roll_Proficiency"></button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="npc-rsection">
|
||||
<div class="attacks-grid">
|
||||
<span class="sheet-title-line">Hit Dice</span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_npc_hitdice">
|
||||
<input class="sheet-stat-input" type="text" name="attr_npc_hitdicemod" value="d8">
|
||||
<div><button class="sheet-roll-button" type="roll" value="/w gm &{template:acks}{{name=@{character_name} Hit Dice}}{{roll=[[@{npc_hitdice}@{npc_hitdicemod}]]}}" name="roll_NPCHitDice"></button></div>
|
||||
|
||||
|
||||
<span class="sheet-title-line">Hit Points/Max</span><input class="sheet-stat-input" type="text" name="attr_hp"><input class="sheet-stat-input" type="text" name="attr_hp_max"><span></span>
|
||||
|
||||
<span class="sheet-title-line">Class</span><span></span><span class="sheet-title-line">Level</span><span></span>
|
||||
|
||||
<select name="attr_class">
|
||||
<option>Fighter</option>
|
||||
<option>Mage</option>
|
||||
<option>Cleric</option>
|
||||
<option>Thief</option>
|
||||
<option>Assassin</option>
|
||||
<option>Bard</option>
|
||||
<option>Bladedancer</option>
|
||||
<option>Explorer</option>
|
||||
</select>
|
||||
<span></span>
|
||||
<input class="sheet-stat-input" type="text" name="attr_level">
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="saves-grid">
|
||||
<div class="sheet-saves-title">
|
||||
<h3>Saving Throws</h3>
|
||||
</div>
|
||||
|
||||
<span></span><span>Throw</span><span>Modifier</span><span></span>
|
||||
|
||||
<span class="sheet-save-span">Petrif. & Paral.</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pp"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pp_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Petrification & Paralysis}} {{roll=[[1d20 + @{save_pp_mod}]]}} {{target=[[@{save_pp}]]}}" name="roll_PPSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Poison & Death</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pd"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_pd_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Poison & Death}} {{roll=[[1d20 + @{save_pd_mod}]]}} {{target=[[@{save_pd}]]}}" name="roll_PDSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Blast & Breath</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_bb"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_bb_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Blast & Breath}} {{roll=[[1d20 + @{save_bb_mod}]]}} {{target=[[@{save_bb}]]}}" name="roll_BBSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Staffs & Wands</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sw"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sw_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Staffs & Wands}} {{roll=[[1d20 + @{save_sw_mod}]]}} {{target=[[@{save_sw}]]}}" name="roll_SWSave"></button></div>
|
||||
|
||||
<span class="sheet-save-span">Spells</span>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sp"></div>
|
||||
<div><input class="sheet-stat-input" type="text" name="attr_save_sp_mod" value="0"></div>
|
||||
<div><button class="sheet-roll-button" type="roll" value="&{template:acks} {{name=Saving Throw}} {{subheader=Spells}} {{roll=[[1d20 + @{save_sp_mod}]]}} {{target=[[@{save_sp}]]}}" name="roll_SPSave"></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Roll Templates -->
|
||||
<rolltemplate class="sheet-rolltemplate-acks">
|
||||
<div class="sheet-acks-container">
|
||||
<div class="sheet-acks-header">{{name}}</div>
|
||||
{{#subheader}}
|
||||
<div class="sheet-acks-subheader sheet-acks-row">{{subheader}}</div>
|
||||
{{/subheader}}
|
||||
{{#target}}
|
||||
<div class="sheet-acks-row">Target <span class="sheet-acks-target-value">{{target}}+</span></div>
|
||||
{{/target}}
|
||||
{{#roll}}
|
||||
<div class="sheet-acks-row">Roll {{roll}}</div>
|
||||
{{/roll}}
|
||||
{{#damage}}
|
||||
<div class="sheet-acks-row">Damage {{damage}}</div>
|
||||
{{/damage}}
|
||||
{{#desc}}
|
||||
<div class="sheet-acks-row sheet-acks-desc">{{desc}}</div>
|
||||
{{/desc}}
|
||||
</div>
|
||||
</rolltemplate>
|
||||
|
||||
|
||||
<!-- Sheet Workers -->
|
||||
<script type="text/worker">
|
||||
/*** ATTRIBUTE RELATED WORKERS ***/
|
||||
const calculateAttrMod = function(attr) {
|
||||
// Calculate the modifier for a given attribute value
|
||||
let atr = parseInt(attr);
|
||||
let mod = 0;
|
||||
|
||||
if(atr <= 3) mod = -3;
|
||||
if(atr >= 4 && atr <= 5) mod = -2;
|
||||
if(atr >= 6 && atr <= 8) mod = -1;
|
||||
if(atr >= 9 && atr <= 12) mod = 0;
|
||||
if(atr >= 13 && atr <= 15) mod = 1;
|
||||
if(atr >= 16 && atr <= 17) mod = 2;
|
||||
if(atr >= 18) mod = 3;
|
||||
|
||||
return mod;
|
||||
};
|
||||
|
||||
/** Calculate Modifiers when an Attribute value changes **/
|
||||
on("change:str", function(e) {
|
||||
console.log('calculate mod for str');
|
||||
setAttrs({str_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
on("change:int", function(e) {
|
||||
console.log('calculate mod for int');
|
||||
setAttrs({int_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
on("change:wis", function(e) {
|
||||
console.log('calculate mod for wis');
|
||||
setAttrs({wis_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
on("change:dex", function(e) {
|
||||
console.log('calculate mod for dex');
|
||||
setAttrs({dex_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
on("change:con", function(e) {
|
||||
console.log('calculate mod for con');
|
||||
setAttrs({con_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
on("change:chr", function(e) {
|
||||
console.log('calculate mod for chr');
|
||||
setAttrs({chr_mod: calculateAttrMod(e.newValue)});
|
||||
});
|
||||
</script>
|
||||
<script type="text/worker">
|
||||
/*** SAVES/THROWS RELATED WORKERS ***/
|
||||
const calculateSavesThrows = function(level, clas) {
|
||||
level = parseInt(level);
|
||||
var saves = [0, 0, 0, 0, 0, 0];
|
||||
switch(clas) {
|
||||
case 'Fighter':
|
||||
case 'Assassin':
|
||||
case 'Explorer':
|
||||
switch(level) {
|
||||
case 0:
|
||||
saves = [16, 15, 17, 17, 18];
|
||||
break;
|
||||
case 1:
|
||||
saves = [15, 14, 16, 16, 17, 10];
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
saves = [14, 13, 15, 15, 16, 9];
|
||||
break;
|
||||
case 4:
|
||||
saves = [13, 12, 14, 14, 15, 8];
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
saves = [12, 11, 13, 13, 14, 7];
|
||||
break;
|
||||
case 7:
|
||||
saves = [11, 10, 12, 12, 13, 6];
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
saves = [10, 9, 11, 11, 12, 5];
|
||||
break;
|
||||
case 10:
|
||||
saves = [9, 8, 10, 10, 11, 4];
|
||||
break;
|
||||
case 11:
|
||||
case 12:
|
||||
saves = [8, 7, 9, 9, 10, 3];
|
||||
break;
|
||||
case 13:
|
||||
saves = [7, 6, 8, 8, 9, 2];
|
||||
break;
|
||||
case 14:
|
||||
saves = [6, 5, 7, 7, 8, 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Mage':
|
||||
switch(level) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
saves = [13, 13, 15, 11, 12, 10];
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
saves = [12, 12, 14, 10, 11, 9];
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
saves = [11, 11, 13, 9, 10, 8];
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
saves = [10, 10, 12, 8, 9, 7];
|
||||
break;
|
||||
case 13:
|
||||
case 14:
|
||||
saves = [9, 9, 11, 7, 8, 6];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Cleric':
|
||||
case 'Bladedancer':
|
||||
switch(level) {
|
||||
case 1:
|
||||
case 2:
|
||||
saves = [13, 10, 16, 13, 15, 10];
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
saves = [12, 9, 15, 12, 14, 9];
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
saves = [11, 8, 14, 11, 13, 8];
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
saves = [10, 7, 13, 10, 12, 7];
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
saves = [9, 6, 12, 9, 11, 6];
|
||||
break;
|
||||
case 11:
|
||||
case 12:
|
||||
saves = [8, 5, 11, 8, 10, 5];
|
||||
break;
|
||||
case 13:
|
||||
case 14:
|
||||
saves = [7, 4, 10, 7, 9, 4];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Thief':
|
||||
case 'Bard':
|
||||
switch(level) {
|
||||
case 1:
|
||||
case 2:
|
||||
saves = [13, 13, 16, 14, 15, 10];
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
saves = [12, 12, 15, 13, 14, 9];
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
saves = [11, 11, 14, 12, 13, 8];
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
saves = [10, 10, 13, 11, 12, 7];
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
saves = [9, 9, 12, 10, 11, 6];
|
||||
break;
|
||||
case 11:
|
||||
case 12:
|
||||
saves = [8, 8, 11, 9, 10, 5];
|
||||
break;
|
||||
case 13:
|
||||
case 14:
|
||||
saves = [7, 7, 10, 8, 9, 4];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return saves;
|
||||
};
|
||||
|
||||
on("change:level change:class", function(e) {
|
||||
console.log('calculate saves and throws for class/level');
|
||||
getAttrs(["level", "class"], function(v) {
|
||||
let saves = calculateSavesThrows(v.level, v.class);
|
||||
setAttrs({
|
||||
save_pp: saves[0],
|
||||
save_pd: saves[1],
|
||||
save_bb: saves[2],
|
||||
save_sw: saves[3],
|
||||
save_sp: saves[4],
|
||||
attack_throw: saves[5]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* NPC Attack Throw */
|
||||
const calculateNpcHit = function(hd) {
|
||||
switch(hd.toString()) {
|
||||
case '1':
|
||||
return 10;
|
||||
case '2':
|
||||
return 9;
|
||||
case '3':
|
||||
return 8;
|
||||
case '4':
|
||||
return 7;
|
||||
case '5':
|
||||
return 6;
|
||||
case '6':
|
||||
return 5;
|
||||
case '7':
|
||||
return 4;
|
||||
case '8':
|
||||
case '9':
|
||||
return 3;
|
||||
case '10':
|
||||
case '11':
|
||||
return 2;
|
||||
case '12':
|
||||
case '13':
|
||||
return 1;
|
||||
case '14':
|
||||
case '15':
|
||||
return 0;
|
||||
case '16':
|
||||
case '17':
|
||||
return -1;
|
||||
case '18':
|
||||
case '19':
|
||||
return -2;
|
||||
case '20':
|
||||
case '21':
|
||||
return -3;
|
||||
default:
|
||||
return -4;
|
||||
}
|
||||
};
|
||||
on("change:npc_hitdice", function(e) {
|
||||
getAttrs(["npc_hitdice"], function(v) {
|
||||
let thrw = calculateNpcHit(v.npc_hitdice);
|
||||
setAttrs({npc_attack_throw: thrw});
|
||||
});
|
||||
});
|
||||
</script>
|
12
uwsgi.ini
12
uwsgi.ini
@ -4,8 +4,14 @@ module = wsgi:application
|
||||
master = true
|
||||
processes = 4
|
||||
|
||||
socket = ackstools.sock
|
||||
chmod-scoket = 660
|
||||
vacuum = true
|
||||
socket = acks.sock
|
||||
chmod-socket = 777
|
||||
uid = www-data
|
||||
gid = www-data
|
||||
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
|
||||
logger = file:/home/br4n/code/acks-tools/logs/uwsgi.log
|
||||
|
||||
env = FLASK_SETTINGS_FILE=/home/br4n/code/acks-tools/config/prod.cfg
|
||||
|
Loading…
x
Reference in New Issue
Block a user