Update WebHost to include new tutorial landing page.
- Added MSU-1 setup guide - Updated header and landing page for title change - Re-structured tutorial files - Added tutorials.json, which is used when constructing the new /tutorial page
This commit is contained in:
parent
eb58ee2422
commit
b063407d2b
|
@ -79,10 +79,14 @@ def register_session():
|
||||||
session["_id"] = uuid4() # uniquely identify each session without needing a login
|
session["_id"] = uuid4() # uniquely identify each session without needing a login
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/tutorial/<string:game>/<string:file>/<string:lang>')
|
||||||
|
def tutorial(game, file, lang):
|
||||||
|
return render_template("tutorial.html", game=game, file=file, lang=lang)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tutorial')
|
@app.route('/tutorial')
|
||||||
@app.route('/tutorial/<string:lang>')
|
def tutorial_landing():
|
||||||
def tutorial(lang='en'):
|
return render_template("tutorialLanding.html")
|
||||||
return render_template(f"tutorial.html", lang=lang)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/player-settings')
|
@app.route('/player-settings')
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
const availableLanguages = {
|
|
||||||
de: 'Deutsch',
|
|
||||||
en: 'English',
|
|
||||||
es: 'Español',
|
|
||||||
fr: 'Français',
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
const tutorialWrapper = document.getElementById('tutorial-wrapper');
|
const tutorialWrapper = document.getElementById('tutorial-wrapper');
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
|
@ -21,27 +14,11 @@ window.addEventListener('load', () => {
|
||||||
}
|
}
|
||||||
resolve(ajax.responseText);
|
resolve(ajax.responseText);
|
||||||
};
|
};
|
||||||
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/tutorial_` +
|
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/` +
|
||||||
`${tutorialWrapper.getAttribute('data-language')}.md`, true);
|
`${tutorialWrapper.getAttribute('data-game')}/${tutorialWrapper.getAttribute('data-file')}_` +
|
||||||
|
`${tutorialWrapper.getAttribute('data-lang')}.md`, true);
|
||||||
ajax.send();
|
ajax.send();
|
||||||
}).then((results) => {
|
}).then((results) => {
|
||||||
// Build the language selector
|
|
||||||
let currentLanguage = window.location.href.split('/').pop();
|
|
||||||
if (Object.keys(availableLanguages).indexOf(currentLanguage) === -1) { currentLanguage = 'en' }
|
|
||||||
const languageSelectorWrapper = document.createElement('div');
|
|
||||||
languageSelectorWrapper.setAttribute('id', 'language-selector-wrapper')
|
|
||||||
const languageSelector = document.createElement('select');
|
|
||||||
languageSelector.setAttribute('id', 'language-selector');
|
|
||||||
for (const lang of Object.keys(availableLanguages)) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.innerText = availableLanguages[lang];
|
|
||||||
if (lang === currentLanguage) { option.setAttribute('selected', '1'); }
|
|
||||||
languageSelector.appendChild(option);
|
|
||||||
}
|
|
||||||
languageSelectorWrapper.appendChild(languageSelector);
|
|
||||||
tutorialWrapper.appendChild(languageSelectorWrapper);
|
|
||||||
|
|
||||||
// Populate page with HTML generated from markdown
|
// Populate page with HTML generated from markdown
|
||||||
tutorialWrapper.innerHTML += (new showdown.Converter()).makeHtml(results);
|
tutorialWrapper.innerHTML += (new showdown.Converter()).makeHtml(results);
|
||||||
adjustHeaderWidth();
|
adjustHeaderWidth();
|
||||||
|
@ -65,14 +42,10 @@ window.addEventListener('load', () => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('language-selector').addEventListener('change', (event) => {
|
|
||||||
console.info(window.location.hostname);
|
|
||||||
window.location.href = `http://${window.location.hostname}/tutorial/${event.target.value}`;
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
tutorialWrapper.innerHTML =
|
tutorialWrapper.innerHTML =
|
||||||
`<h2>${error}</h2>
|
`<h2>This page is out of logic!</h2>
|
||||||
<h3>Click <a href="${window.location.origin}/tutorial">here</a> to return to safety.</h3>`;
|
<h3>Click <a href="${window.location.origin}/tutorial">here</a> to return to safety.</h3>`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"gameTitle": "The Legend of Zelda: A Link to the Past",
|
||||||
|
"tutorials": [
|
||||||
|
{
|
||||||
|
"name": "Multiworld Setup Tutorial",
|
||||||
|
"description": "A guide to setting up the Archipelago ALttP software on your computer. This guide covers single-player, multiworld, and related software.",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "English",
|
||||||
|
"filename": "zelda3/multiworld_en.md",
|
||||||
|
"link": "zelda3/multiworld/en",
|
||||||
|
"authors": [
|
||||||
|
"Farrak Kilhn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "Deutsch",
|
||||||
|
"filename": "zelda3/multiworld_de.md",
|
||||||
|
"link": "zelda3/multiworld/de",
|
||||||
|
"authors": [
|
||||||
|
"Fischfilet"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "Español",
|
||||||
|
"filename": "zelda3/multiworld_es.md",
|
||||||
|
"link": "zelda3/multiworld/es",
|
||||||
|
"authors": [
|
||||||
|
"Edos"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "Français",
|
||||||
|
"filename": "zelda3/multiworld_fr.md",
|
||||||
|
"link": "zelda3/multiworld/fr",
|
||||||
|
"authors": [
|
||||||
|
"Coxla"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MSU-1 Setup Tutorial",
|
||||||
|
"description": "A guide to setting up MSU-1, which allows for custom in-game music.",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "English",
|
||||||
|
"filename": "zelda3/msu1_en.md",
|
||||||
|
"link": "zelda3/msu1/en",
|
||||||
|
"authors": [
|
||||||
|
"Farrak Kilhn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,68 @@
|
||||||
|
# MSU-1 Setup Guide
|
||||||
|
|
||||||
|
## What is MSU-1?
|
||||||
|
MSU-1 allows for the use of custom in-game music. It works on original hardware, the SuperNT, and certain emulators.
|
||||||
|
This guide will explain how to find custom music packages, often called MSU packs, and how to configure
|
||||||
|
them for use with original hardware, the SuperNT, and the snes9x emulator.
|
||||||
|
|
||||||
|
## Where to find MSU Packs
|
||||||
|
MSU packs are constantly in development. You can find a list of completed packs, as well as in-development packs on
|
||||||
|
[this Google Spreadsheet](https://docs.google.com/spreadsheets/d/1XRkR4Xy6S24UzYkYBAOv-VYWPKZIoUKgX04RbjF128Q).
|
||||||
|
|
||||||
|
## What an MSU pack should look like
|
||||||
|
MSU packs contain many files, most of which are the music files which will be used when playing the game. These files
|
||||||
|
should be named similarly, with a hyphenated number at the end, and with a `.pcm` extension. It does not matter what
|
||||||
|
each music file is named, so long as they all follow the same pattern. The most popular filename you will find is
|
||||||
|
`alttp_msu-X.pcm`, where X is replaced by a number.
|
||||||
|
|
||||||
|
There is one other type of file you should find inside an MSU pack's folder. This file indicates to the hardware or
|
||||||
|
to the emulator that MSU should be enabled for this game. This file should be named similarly to the other files in
|
||||||
|
the folder, but will have a `.msu` extension and be 0 KB in size.
|
||||||
|
|
||||||
|
A short example of the contents of an MSU pack folder are as follows:
|
||||||
|
```
|
||||||
|
List of files inside an MSU pack folder:
|
||||||
|
alttp_msu.msu
|
||||||
|
alttp_msu-1.pcm
|
||||||
|
alttp_msu-2.pcm
|
||||||
|
...
|
||||||
|
alttp_msu-34.pcm
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to use an MSU Pack
|
||||||
|
In all cases, you must rename your ROM file to match the pattern of names inside your MSU pack's folder, then place
|
||||||
|
your ROM file inside that folder.
|
||||||
|
|
||||||
|
This will cause the folder contents to look like the following:
|
||||||
|
```
|
||||||
|
List of files inside an MSU pack folder:
|
||||||
|
alttp_msu.msu
|
||||||
|
alttp_msu.sfc <-- Add your ROM file
|
||||||
|
alttp_msu-1.pcm
|
||||||
|
alttp_msu-2.pcm
|
||||||
|
...
|
||||||
|
alttp_msu-34.pcm
|
||||||
|
```
|
||||||
|
|
||||||
|
### With snes9x
|
||||||
|
1. Load the ROM file from snes9x.
|
||||||
|
|
||||||
|
### With SD2SNES / FXPak on original hardware
|
||||||
|
1. Load the MSU pack folder onto your SD2SNES / FXPak.
|
||||||
|
2. Navigate into the MSU pack folder and load your ROM.
|
||||||
|
|
||||||
|
### With SD2SNES / FXPak on SuperNT
|
||||||
|
1. Load the MSU pack folder onto your SD2SNES / FXPak.
|
||||||
|
2. Power on your SuperNT and navigate to the `Settings` menu.
|
||||||
|
3. Enter the `Audio` settings.
|
||||||
|
4. Check the box marked `Cartridge Audio Enable.`
|
||||||
|
5. Navigate back to the previous menu.
|
||||||
|
6. Choose `Save/Clear Settings`.
|
||||||
|
7. Choose `Save Settings`.
|
||||||
|
8. Choose `Run Cartridge` from the main menu.
|
||||||
|
9. Navigate into your MSU pack folder and load your ROM.
|
||||||
|
|
||||||
|
## A word of caution to streamers
|
||||||
|
Many MSU packs use copyrighted music which is not permitted for use on platforms like Twitch and YouTube.
|
||||||
|
If you choose to stream copyrighted music, your VOD may be muted. In the worst scenario, you may receive a DMCA
|
||||||
|
take-down notice. Please be careful to only stream music for which you have the rights to do so.
|
|
@ -0,0 +1,71 @@
|
||||||
|
const showError = () => {
|
||||||
|
const tutorial = document.getElementById('tutorial-landing');
|
||||||
|
document.getElementById('page-title').innerText = 'This page is out of logic!';
|
||||||
|
tutorial.removeChild(document.getElementById('loading'));
|
||||||
|
const userMessage = document.createElement('h3');
|
||||||
|
const homepageLink = document.createElement('a');
|
||||||
|
homepageLink.innerText = 'Click here';
|
||||||
|
homepageLink.setAttribute('href', '/');
|
||||||
|
userMessage.append(homepageLink);
|
||||||
|
userMessage.append(' to go back to safety!');
|
||||||
|
tutorial.append(userMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const ajax = new XMLHttpRequest();
|
||||||
|
ajax.onreadystatechange = () => {
|
||||||
|
if (ajax.readyState !== 4) { return; }
|
||||||
|
const tutorialDiv = document.getElementById('tutorial-landing');
|
||||||
|
if (ajax.status !== 200) { return showError(); }
|
||||||
|
|
||||||
|
try {
|
||||||
|
const games = JSON.parse(ajax.responseText);
|
||||||
|
games.forEach((game) => {
|
||||||
|
const gameTitle = document.createElement('h2');
|
||||||
|
gameTitle.innerText = game.gameTitle;
|
||||||
|
tutorialDiv.appendChild(gameTitle);
|
||||||
|
|
||||||
|
game.tutorials.forEach((tutorial) => {
|
||||||
|
const tutorialName = document.createElement('h3');
|
||||||
|
tutorialName.innerText = tutorial.name;
|
||||||
|
tutorialDiv.appendChild(tutorialName);
|
||||||
|
|
||||||
|
const tutorialDescription = document.createElement('p');
|
||||||
|
tutorialDescription.innerText = tutorial.description;
|
||||||
|
tutorialDiv.appendChild(tutorialDescription);
|
||||||
|
|
||||||
|
const intro = document.createElement('p');
|
||||||
|
intro.innerText = 'This guide is available in the following languages:';
|
||||||
|
tutorialDiv.appendChild(intro);
|
||||||
|
|
||||||
|
const fileList = document.createElement('ul');
|
||||||
|
tutorial.files.forEach((file) => {
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
const anchor = document.createElement('a');
|
||||||
|
anchor.innerText = file.language;
|
||||||
|
anchor.setAttribute('href', `${window.location.origin}/tutorial/${file.link}`);
|
||||||
|
listItem.appendChild(anchor);
|
||||||
|
|
||||||
|
listItem.append(' by ');
|
||||||
|
for (let author of file.authors) {
|
||||||
|
listItem.append(author);
|
||||||
|
if (file.authors.indexOf(author) !== (file.authors.length -1)) {
|
||||||
|
listItem.append(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
tutorialDiv.appendChild(fileList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tutorialDiv.removeChild(document.getElementById('loading'));
|
||||||
|
} catch (error) {
|
||||||
|
showError();
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/tutorials.json`, true);
|
||||||
|
ajax.send();
|
||||||
|
});
|
|
@ -16,6 +16,10 @@ html{
|
||||||
color: #eeffeb;
|
color: #eeffeb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tutorial-wrapper p{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#tutorial-wrapper a{
|
#tutorial-wrapper a{
|
||||||
color: #ffef00;
|
color: #ffef00;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +94,7 @@ html{
|
||||||
}
|
}
|
||||||
|
|
||||||
#tutorial-wrapper pre{
|
#tutorial-wrapper pre{
|
||||||
|
margin-top: 0;
|
||||||
padding: 0.5rem 0.25rem;
|
padding: 0.5rem 0.25rem;
|
||||||
background-color: #ffeeab;
|
background-color: #ffeeab;
|
||||||
border: 1px solid #9f916a;
|
border: 1px solid #9f916a;
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
html{
|
||||||
|
background-image: url('../static/backgrounds/grass/grass-0007-large.png');
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-size: 650px 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 70rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem 1rem 3rem;
|
||||||
|
color: #eeffeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing p{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing a{
|
||||||
|
color: #ffef00;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h1{
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: normal;
|
||||||
|
border-bottom: 1px solid #ffffff;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 1px 1px 4px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h2{
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: normal;
|
||||||
|
border-bottom: 1px solid #ffffff;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: #ffe993;
|
||||||
|
text-transform: lowercase;
|
||||||
|
text-shadow: 1px 1px 2px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h3{
|
||||||
|
font-size: 1.70rem;
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h4{
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h5{
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h6{
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #434343;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing h3, #tutorial-landing h4, #tutorial-landing h5,#tutorial-landing h6{
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing ul{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing ol{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing li{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing pre{
|
||||||
|
margin-top: 0;
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
background-color: #ffeeab;
|
||||||
|
border: 1px solid #9f916a;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tutorial-landing code{
|
||||||
|
background-color: #ffeeab;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
padding-right: 0.25rem;
|
||||||
|
color: #000000;
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
<div id="base-header-right">
|
<div id="base-header-right">
|
||||||
<a href="/player-settings">start game</a>
|
<a href="/player-settings">start game</a>
|
||||||
<a href="/uploads">host game</a>
|
<a href="/uploads">host game</a>
|
||||||
<a href="/tutorial">setup guide</a>
|
<a href="/tutorial">setup guides</a>
|
||||||
<a href="/generate">upload config</a>
|
<a href="/generate">upload config</a>
|
||||||
<a href="https://discord.gg/8Z65BR2">discord</a>
|
<a href="https://discord.gg/8Z65BR2">discord</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div id="landing-links">
|
<div id="landing-links">
|
||||||
<a href="/player-settings" id="player-settings-button">start<br />playing</a>
|
<a href="/player-settings" id="player-settings-button">start<br />playing</a>
|
||||||
<a href="/uploads" id="uploads-button">host<br />game</a>
|
<a href="/uploads" id="uploads-button">host<br />game</a>
|
||||||
<a href="/tutorial" id="setup-guide-button">setup guide</a>
|
<a href="/tutorial" id="setup-guide-button">setup guides</a>
|
||||||
<a href="/generate" id="generate-button">upload config</a>
|
<a href="/generate" id="generate-button">upload config</a>
|
||||||
<a href="https://discord.gg/8Z65BR2" id="discord-button">discord</a>
|
<a href="https://discord.gg/8Z65BR2" id="discord-button">discord</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% include 'header/grassHeader.html' %}
|
{% include 'header/grassHeader.html' %}
|
||||||
<title>Setup Tutorial</title>
|
<title>Archipelago</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/tutorial.css") }}" />
|
<link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/tutorial.css") }}" />
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div id="tutorial-wrapper" class="main-content" data-language="{{ lang }}">
|
<div id="tutorial-wrapper" data-game="{{ game }}" data-file="{{ file }}" data-lang="{{ lang }}">
|
||||||
<!-- Content generated by JavaScript -->
|
<!-- Content generated by JavaScript -->
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'pageWrapper.html' %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
{% include 'header/grassHeader.html' %}
|
||||||
|
<title>Archipelago Guides</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/tutorialLanding.css") }}" />
|
||||||
|
<script type="application/ecmascript" src="{{ static_autoversion("assets/tutorialLanding.js") }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div id="tutorial-landing" data-game="{{ game }}" data-file="{{ file }}" data-lang="{{ lang }}">
|
||||||
|
<h1 id="page-title">Archipelago Guides</h1>
|
||||||
|
<p id="loading">Loading...</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue