Browse Source

Quest management start

master
Brandon Cornejo 2 years ago
parent
commit
c91ed2f213
  1. 3
      acks/__init__.py
  2. 61
      acks/quest/commands.py
  3. 85
      acks/quest/quest_manager.py
  4. 86
      acks/quest/templates/quest_detail.html
  5. 39
      acks/quest/templates/quest_list.html
  6. 71
      acks/quest/views.py
  7. BIN
      acks/static/PalismaContinent.png
  8. 1
      acks/templates/base.html
  9. 2
      acks/templates/handbook.html
  10. 3
      acks/templates/index.html
  11. 2
      acks/templates/treasure.html

3
acks/__init__.py

@ -20,6 +20,9 @@ def create_app():
from acks.npc.views import npc_views
app.register_blueprint(npc_views)
from acks.quest.views import quest_views
app.register_blueprint(quest_views)
# Load our CLI commands
from acks.commands import default_cli
app.cli.add_command(default_cli)

61
acks/quest/commands.py

@ -0,0 +1,61 @@
import click
from flask.cli import AppGroup
from ..models import db
npc_cli = AppGroup('npc')
@npc_cli.command('populate')
def populate_npc_database():
import csv
from .models import (
CharacterClass,
ClassLevelProgression,
EquipmentArmour,
EquipmentRangedWeapon,
EquipmentMeleeWeapon,
Spell
)
def load_csv_data(file_name, cls):
rows = []
with open('acks/npc/data/{}'.format(file_name), newline='') as data:
reader = csv.DictReader(data)
for row in reader:
rows.append(cls(**row))
return rows
# Character Classes
db.session.bulk_save_objects(load_csv_data('default_classes.csv', CharacterClass))
# Equipment Armour
db.session.bulk_save_objects(load_csv_data('default_armours.csv', EquipmentArmour))
# Ranged Weapons
db.session.bulk_save_objects(load_csv_data('default_ranged.csv', EquipmentRangedWeapon))
# Melee Weapons
melee_weps = load_csv_data('default_melee.csv', EquipmentMeleeWeapon)
for wep in melee_weps:
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)
# 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
spell.description = spell.description.strip()
db.session.bulk_save_objects(spells)
db.session.commit()

85
acks/quest/quest_manager.py

@ -0,0 +1,85 @@
from io import BytesIO
from pathlib import Path
from zipfile import ZipFile
FS_ROOT = '/srv/www/atr0phy.net/acks/quests'
URL_ROOT = 'https://atr0phy.net/acks/quests'
IMG_EXTENSIONS = ('png', 'jpg', 'jpeg')
def load_quests():
quest_list = {}
fs = Path(FS_ROOT)
for e in fs.iterdir():
if not e.is_dir():
continue
# Each directory correlates to a level group
quest_level_url_name = e.name.replace('Level', '').replace('-', '').replace(' ', '')
# Gather quests under each level
quest_list[quest_level_url_name] = load_quest_level_directory(e)
return quest_list
def load_quest_level_directory(qdir):
this_level = []
for e in qdir.iterdir():
if not e.is_dir():
continue
# Each directory is a quest for this level
this_level.append(e.name)
return this_level
def get_quest_details(level, quest_name):
level_range = str(level)
folder_name = "Level {0} - {1}".format(level_range[0], level_range[1])
quest_path = "{}/{}/{}".format(FS_ROOT, folder_name, quest_name)
fs = Path(quest_path)
if not fs.exists() or not fs.is_dir():
print('EXITING {}'.format(fs))
return None
quest = {}
quest['name'] = fs.name
quest['download'] = (level, quest_name)
quest['assets'] = []
def urlify(path):
return {'display': path.name, 'url': path.as_posix().replace(FS_ROOT, URL_ROOT)}
for e in fs.iterdir():
if e.name.endswith(IMG_EXTENSIONS):
quest['assets'].append(urlify(e))
elif e.name == 'info.html':
quest['info'] = urlify(e)
elif e.name == 'tsv.txt':
quest['tsv'] = urlify(e)
print("Quest: {}".format(quest))
return quest
def get_quest_archive(level, quest_name):
level_range = str(level)
folder_name = "Level {0} - {1}".format(level_range[0], level_range[1])
quest_path = "{}/{}/{}".format(FS_ROOT, folder_name, quest_name)
fs = Path(quest_path)
if not fs.exists() or not fs.is_dir():
print('EXITING {}'.format(fs))
return None
file_path_list = []
for e in fs.iterdir():
file_path_list.append((e.as_posix(), e.name))
archive_mem = BytesIO()
with ZipFile(archive_mem, 'w') as archive:
for f in file_path_list:
archive.write(f[0], f[1])
archive_mem.seek(0)
return archive_mem

86
acks/quest/templates/quest_detail.html

@ -0,0 +1,86 @@
{% extends "base.html" %}
{% set active_page = "quest_detail" %}
{% block title %}ACKS Quest Detail{% endblock %}
{% block content %}
<div class="uk-flex uk-flex-center uk-margin-bottom uk-margin-top">
<h1 class="uk-text-center"><strong>Adventurer Conqueror King</strong>Quest Detail</h1>
</div>
{% if quest %}
<div class="uk-container uk-container-small">
<div class="uk-text-center uk-margin-large">
<h1 class="uk-heading-xlarge">{{ quest['name'] }}</h1>
</div>
<div class="uk-flex uk-flex-center uk-margin-large-bottom">
<a href="{{ quest['info']['url'] }}" class="uk-button uk-button-default" target="_blank">Info Window</a>
{% if quest['tsv'] %}
<a href="{{ quest['tsv']['url'] }}" class="uk-button uk-button-default">TSV File</a>
{% endif %}
<a href="{{ url_for('quest_manager.quest_download', level=quest['download'][0], quest_name=quest['download'][1]) }}" class="uk-button uk-button-default" target="_blank">Download</a>
</div>
<ul uk-accordion="multiple: true">
<li>
<a class="uk-accordion-title" href="#">Information</a>
<div class="uk-accordion-content">
<h3 class="uk-text-center">Quest Info</h3>
<div id="frame-container">
<iframe id="quest-frame" src="{{ quest['info']['url'] }}"></iframe>
</div>
</div>
</li>
<li class="uk-open">
<a class="uk-accordion-title" href="#">Assets</a>
<div class="uk-accordion-content">
<h3 class="uk-text-center">Quest Assets</h3>
<div uk-slideshow="animation: push">
<ul class="uk-slideshow-nav uk-dotnav uk-flex-center uk-margin"></ul>
<div class="uk-position-relative uk-visible-toggle uk-light" tabindex="-1">
<ul class="uk-slideshow-items" uk-lightbox>
{% for a in quest['assets'] %}
<li>
<a href="{{ a['url'] }}"><img src="{{ a['url'] }}" alt="" width="800" uk-cover></a>
<div class="uk-overlay uk-overlay-primary uk-position-bottom uk-text-center uk-transition-slide-bottom">
<h3 class="uk-margin-remove">{{ a['display'] }}</h3>
</div>
</li>
{% endfor %}
</ul>
</div>
<ul class="uk-slideshow-nav uk-dotnav uk-flex-center uk-margin"></ul>
</div>
</div>
</li>
</ul>
</div>
{% endif %}
{% endblock %}
{% block head %}
<style>
#asset-view img {
max-width: 300px;
max-height: 500px;
}
#frame-container {
display: flex;
width: 100%;
height: 85vh;
flex-direction: column:
}
#quest-frame {
flex-grow: 1;
border: none;
margin: 0;
padding: 0;
}
h1 strong {
display: block;
font-size: 50%;
opacity: 0.65;
}
</style>
<script>
</script>
{% endblock %}

39
acks/quest/templates/quest_list.html

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% set active_page = "quest_list" %}
{% block title %}ACKS Quest List{% endblock %}
{% block content %}
<div class="uk-flex uk-flex-center uk-margin-bottom uk-margin-top">
<h1 class="uk-text-center"><strong>Adventurer Conqueror King</strong>Quest Board (Internal)</h1>
</div>
{% if quest_map %}
<div class="uk-flex uk-flex-bottom uk-flex-center uk-margin-large-bottom">
<ul class="uk-list">
{% for level, quests in quest_map.items() | sort(attribute=0) %}
<li>
<strong>Level {{ level[0] }} - {{ level[1] }}</strong>
<ul class="uk-list">
{% for quest in quests %}
<li><a href="{{ url_for('quest_manager.quest_detail', level=level, quest_name=quest) }}">{{ quest }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
{% block head %}
<style>
h1 strong {
display: block;
font-size: 50%;
opacity: 0.65;
}
</style>
<script>
</script>
{% endblock %}

71
acks/quest/views.py

@ -0,0 +1,71 @@
from flask import (
request,
jsonify,
current_app,
render_template,
send_file,
Blueprint
)
from .quest_manager import (
load_quests,
get_quest_details,
get_quest_archive
)
quest_views = Blueprint(
'quest_manager',
__name__,
template_folder='templates',
url_prefix='/quest'
)
@quest_views.route('/list')
def quest_list():
quests = load_quests()
return render_template('quest_list.html', quest_map=quests)
@quest_views.route('/detail/<int:level>/<string:quest_name>')
def quest_detail(level, quest_name):
quest = get_quest_details(level, quest_name)
return render_template('quest_detail.html', quest=quest)
@quest_views.route('/detail/<int:level>/<string:quest_name>/download')
def quest_download(level, quest_name):
archive = get_quest_archive(level, quest_name)
return send_file(archive, attachment_filename="acks_{0}.zip".format(quest_name), as_attachment=True)
'''
@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)
# 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)
@npc_views.route('/single')
@npc_views.route('/single/<int:base_level>/<int:guild_id>')
def generate_single_npc(base_level=None, guild_id=0):
guilds = CharacterClass.query.filter(CharacterClass.bucket.notin_(['Demi-Human'])).all()
npc = None
if base_level:
npc = create_npc(base_level, guild_id)
# If asked for JSON, return the npc, otherwise render HTML template
if request.args.get('format', 'html') == 'json':
return jsonify(npc)
return render_template('generate_single_npc.html', npc=npc, base_level=base_level, guilds=guilds, guild_id=guild_id)
@npc_views.route('/spells')
def spell_list():
spells = Spell.query.all()
return render_template('spell_list.html', spells=spells)
'''

BIN
acks/static/PalismaContinent.png

Before

Width: 1385  |  Height: 1603  |  Size: 3.1 MiB

After

Width: 2969  |  Height: 3436  |  Size: 4.4 MiB

1
acks/templates/base.html

@ -5,6 +5,7 @@
('.header', 'generate', 'Generate'),
('.header', 'other', 'Other'),
('/npc/spells', 'spells', 'Spells'),
('/quest/list', 'questlist', 'Quests'),
('/api/schema', 'api', 'API'),
] %}
{% set generation_bar = [

2
acks/templates/handbook.html

@ -7,7 +7,7 @@
<h1 class="uk-text-center"><strong>Adventurer Conqueror King</strong>Handbook</h1>
</div>
<div id="frame-container">
<iframe id="handbook-frame" src="https://atr0phy.net/acks/handbook"/>
<iframe id="handbook-frame" src="https://atr0phy.net/acks/handbook"></iframe>
</div>
{% endblock %}

3
acks/templates/index.html

@ -11,7 +11,8 @@
<p>
Welcome to the Adventurer Conqueror King toolkit for the Legends of Palisma campaign.
You've had a long journey... three months aboard a ship to reach the new continent,
many thousands of miles across the Immortal Sea from Branborne and any sense of familiarity.
many thousands of miles across the Immortal Sea from Branborne and away from any sense
of familiarity.
</p>
<p>
What adventures await you in an age when empires totter on the brink of war, and terrible

2
acks/templates/treasure.html

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% set active_page = "handbook" %}
{% set active_page = "treasure" %}
{% set treasure_letters = [
("A", "Incidental 275gp"),

Loading…
Cancel
Save