Brandon Cornejo
4 years ago
11 changed files with 350 additions and 3 deletions
-
3acks/__init__.py
-
61acks/quest/commands.py
-
85acks/quest/quest_manager.py
-
86acks/quest/templates/quest_detail.html
-
39acks/quest/templates/quest_list.html
-
71acks/quest/views.py
-
BINacks/static/PalismaContinent.png
-
1acks/templates/base.html
-
2acks/templates/handbook.html
-
3acks/templates/index.html
-
2acks/templates/treasure.html
@ -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() |
@ -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 |
@ -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 %} |
@ -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 %} |
@ -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) |
|||
''' |
Before Width: 1385 | Height: 1603 | Size: 3.1 MiB After Width: 2969 | Height: 3436 | Size: 4.4 MiB |
Write
Preview
Loading…
Cancel
Save
Reference in new issue