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:
Chris Wilson 2021-01-03 17:46:47 -05:00
parent eb58ee2422
commit b063407d2b
15 changed files with 340 additions and 39 deletions

View File

@ -79,10 +79,14 @@ def register_session():
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/<string:lang>')
def tutorial(lang='en'):
return render_template(f"tutorial.html", lang=lang)
def tutorial_landing():
return render_template("tutorialLanding.html")
@app.route('/player-settings')

View File

@ -1,10 +1,3 @@
const availableLanguages = {
de: 'Deutsch',
en: 'English',
es: 'Español',
fr: 'Français',
};
window.addEventListener('load', () => {
const tutorialWrapper = document.getElementById('tutorial-wrapper');
new Promise((resolve, reject) => {
@ -21,27 +14,11 @@ window.addEventListener('load', () => {
}
resolve(ajax.responseText);
};
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/tutorial_` +
`${tutorialWrapper.getAttribute('data-language')}.md`, true);
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/` +
`${tutorialWrapper.getAttribute('data-game')}/${tutorialWrapper.getAttribute('data-file')}_` +
`${tutorialWrapper.getAttribute('data-lang')}.md`, true);
ajax.send();
}).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
tutorialWrapper.innerHTML += (new showdown.Converter()).makeHtml(results);
adjustHeaderWidth();
@ -65,14 +42,10 @@ window.addEventListener('load', () => {
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) => {
console.error(error);
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>`;
});
});

View File

@ -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"
]
}
]
}
]
}
]

View File

@ -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.

View File

@ -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();
});

View File

@ -16,6 +16,10 @@ html{
color: #eeffeb;
}
#tutorial-wrapper p{
margin-top: 0;
}
#tutorial-wrapper a{
color: #ffef00;
}
@ -90,6 +94,7 @@ html{
}
#tutorial-wrapper pre{
margin-top: 0;
padding: 0.5rem 0.25rem;
background-color: #ffeeab;
border: 1px solid #9f916a;

View File

@ -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;
}

View File

@ -13,7 +13,7 @@
<div id="base-header-right">
<a href="/player-settings">start 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="https://discord.gg/8Z65BR2">discord</a>
</div>

View File

@ -14,7 +14,7 @@
<div id="landing-links">
<a href="/player-settings" id="player-settings-button">start<br />playing</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="https://discord.gg/8Z65BR2" id="discord-button">discord</a>
</div>

View File

@ -2,7 +2,7 @@
{% block head %}
{% include 'header/grassHeader.html' %}
<title>Setup Tutorial</title>
<title>Archipelago</title>
<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"
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
@ -11,7 +11,7 @@
{% endblock %}
{% 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 -->
</div>
{% endblock %}

View File

@ -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 %}