diff --git a/index.html b/index.html
index 9ab3afc..d0a2b26 100644
--- a/index.html
+++ b/index.html
@@ -36,12 +36,17 @@
</div>
</div>
<div id="game">
+ <h2 id="time-trial-heading">TIME TRIAL <span id="time-trial-time">30.00s</span> TIME TRIAL</h2>
<div id="choice-container"></div>
<div id="results" class="hide">
<p id="correct"><span>Correct!</span></p>
<p id="incorrect"><span>Incorrect.</span><br>The correct choice was <span id="correct-character">Noelle</span>.</p>
<p><a href="#" id="next-link">Next</a></p>
</div>
+ <div id="final-results">
+ <p>Good job! You got <span id="final-correct">0</span> <span>correct</span> and <span id="final-incorrect">0</span> <span>incorrect</span>.</p>
+ <p><a href="#" id="play-again-link">Play again?</a></p>
+ </div>
<div id="scorekeeper"></div>
</div>
</div>
diff --git a/main.js b/main.js
index 97fc007..2129149 100644
--- a/main.js
+++ b/main.js
@@ -269,6 +269,7 @@ function waitForEvent(el, eventName) {
function setupScorekeeper(numCharacters) {
const container = document.getElementById('scorekeeper');
+ container.innerHTML = '';
for (let i = 0; i < numCharacters; i++) {
const el = document.createElement('div')
el.classList.add('marker');
@@ -290,6 +291,8 @@ function markScorekeeper(cls, character) {
async function mainLol() {
const testChar = 'Noellef';
+ // one-time initialization .............................................uuh
+
document.body.addEventListener('keydown', evt => {
const choiceContainer = document.getElementById('choice-container');
if (!choiceContainer.classList.contains('hide')) {
@@ -322,10 +325,6 @@ async function mainLol() {
}
});
- // pre-game ------------------------------------------------------------...
-
- let mode;
-
const modeLinks = document.querySelectorAll('#mode-picker a[data-mode]');
const modeParas = document.querySelectorAll('#mode-descriptions p[data-mode]');
@@ -344,110 +343,181 @@ async function mainLol() {
el.addEventListener('click', evt => pickMode(evt.target));
}
+ let mode;
+
pickMode(modeLinks[0]);
modeLinks[0].focus();
- // you can pick game options before resources are loaded
await loadResources();
document.body.classList.add('resources-loaded');
- const gameEl = document.getElementById('game');
+ // pre-game ------------------------------------------------------------...
- const playLink = document.getElementById('play-link');
- await waitForEvent(playLink, 'click');
+ while (true) {
+ const gameEl = document.getElementById('game');
- document.body.classList.add('game');
+ const playLink = document.getElementById('play-link');
+ await waitForEvent(playLink, 'click');
- const deckGames = ['deltarune'];
- const hardMode = false;
+ document.body.classList.add('game');
+ document.body.classList.add('mode-' + mode);
- const characterDeck = gameData.characters.filter(chr => chr.games.some(g => deckGames.includes(g)));
+ const deckGames = ['deltarune'];
+ const hardMode = false;
- // game loop -----------------------------------------------------------nym
+ const characterDeck = gameData.characters.filter(chr => chr.games.some(g => deckGames.includes(g)));
- setupScorekeeper(characterDeck.length);
+ // time trial display ----------------------------------------------TT!
- while (characterDeck.length) {
- gameEl.classList.remove('correct');
- gameEl.classList.remove('incorrect');
+ const startTime = Date.now();
+ const timeTrialDuration = 30;
+ let timeTrialOver = false;
- const correctCharacter = characterWithName(testChar) || spliceRandom(characterDeck);
+ let resolveTimeTrial;
+ const timeTrialPromise = new Promise(res => {
+ resolveTimeTrial = res;
+ });
- const numChoices = 6;
- const potentialOptions = characterDeck.slice().filter(chr => chr !== correctCharacter);
- const correctIndex = Math.min(Math.floor(Math.random() * numChoices), potentialOptions.length);
- const allCharacters = [];
- for (let i = 0; i < numChoices; i++) {
- if (i === correctIndex) {
- allCharacters.push(correctCharacter);
- } else if (potentialOptions.length) {
- let choice;
- allCharacters.push(spliceRandom(potentialOptions));
- } else {
- break
+ const updateTimeTrialDisplay = () => {
+ const remaining = Math.max(0, startTime + 1000 * timeTrialDuration - Date.now());
+ const str = (Math.floor(remaining / 10) / 100).toString();
+ const int = str.split('.')[0];
+ const frac = str.split('.')[1] || '';
+ document.getElementById('time-trial-time').innerHTML = `${int}.${frac.padEnd(2, '0')}s`;
+
+ if (remaining === 0) {
+ resolveTimeTrial();
+ timeTrialOver = true;
}
- }
- let msg;
- if (hardMode) {
- msg = pickRandom(['Oh?', 'Hello!']);
- } else {
- msg = pickRandom(correctCharacter.messages);
- }
+ requestAnimationFrame(updateTimeTrialDisplay);
+ };
if (mode === 'time-trial') {
- doSpeech(correctCharacter, msg);
- await delay(100);
- } else {
- await doSpeech(correctCharacter, msg);
- await delay(500);
+ updateTimeTrialDisplay();
}
- const choice = await displayChoices(allCharacters, mode === 'time-trial');
+ // game loop -----------------------------------------------------------nym
+
+ let numCorrect = 0;
+ let numIncorrect = 0;
+
+ setupScorekeeper(characterDeck.length);
+
+ while (characterDeck.length) {
+ gameEl.classList.remove('correct');
+ gameEl.classList.remove('incorrect');
+
+ // heck sorry I don't know how to code I know this is bad
+ const status = await Promise.race([timeTrialPromise, (async () => {
+ const correctCharacter = characterWithName(testChar) || spliceRandom(characterDeck);
+
+ const numChoices = 6;
+ const potentialOptions = characterDeck.slice().filter(chr => chr !== correctCharacter);
+ const correctIndex = Math.min(Math.floor(Math.random() * numChoices), potentialOptions.length);
+ const allCharacters = [];
+ for (let i = 0; i < numChoices; i++) {
+ if (i === correctIndex) {
+ allCharacters.push(correctCharacter);
+ } else if (potentialOptions.length) {
+ let choice;
+ allCharacters.push(spliceRandom(potentialOptions));
+ } else {
+ break
+ }
+ }
- const container = document.getElementById('choice-container');
- for (let i = 0; i < allCharacters.length; i++) {
- const el = container.children[i];
- if (i === correctIndex) {
- el.classList.add('correct');
- } else {
- el.classList.add('incorrect');
- }
- if (i === allCharacters.indexOf(choice)) {
- el.classList.add('chosen');
- } else {
- el.classList.add('not-chosen');
- }
- }
+ let msg;
+ if (hardMode) {
+ msg = pickRandom(['Oh?', 'Hello!']);
+ } else {
+ msg = pickRandom(correctCharacter.messages);
+ }
- if (choice === correctCharacter) {
- markScorekeeper('correct', correctCharacter);
- } else {
- markScorekeeper('incorrect', correctCharacter);
- }
+ if (mode === 'time-trial') {
+ doSpeech(correctCharacter, msg.slice(0, 10));
+ await delay(100);
+ } else {
+ await doSpeech(correctCharacter, msg);
+ await delay(500);
+ }
- await delay(200);
+ const choice = await displayChoices(allCharacters, mode === 'time-trial');
+
+ if (timeTrialOver) return;
+
+ const container = document.getElementById('choice-container');
+ for (let i = 0; i < allCharacters.length; i++) {
+ const el = container.children[i];
+ if (i === correctIndex) {
+ el.classList.add('correct');
+ } else {
+ el.classList.add('incorrect');
+ }
+ if (i === allCharacters.indexOf(choice)) {
+ el.classList.add('chosen');
+ } else {
+ el.classList.add('not-chosen');
+ }
+ }
- if (choice === correctCharacter) {
- gameEl.classList.add('correct');
- } else {
- gameEl.classList.add('incorrect');
- const el = document.getElementById('correct-character');
- el.innerHTML = '';
- el.appendChild(document.createTextNode(correctCharacter.name));
+ if (choice === correctCharacter) {
+ markScorekeeper('correct', correctCharacter);
+ numCorrect++;
+ } else {
+ markScorekeeper('incorrect', correctCharacter);
+ numIncorrect++;
+ }
+
+ await delay(200);
+
+ if (choice === correctCharacter) {
+ gameEl.classList.add('correct');
+ } else {
+ gameEl.classList.add('incorrect');
+ const el = document.getElementById('correct-character');
+ el.innerHTML = '';
+ el.appendChild(document.createTextNode(correctCharacter.name));
+ }
+
+ if (timeTrialOver) return;
+
+ document.getElementById('results').classList.remove('hide');
+
+ const nextLink = document.getElementById('next-link');
+ nextLink.focus();
+ await waitForEvent(nextLink, 'click');
+ })()]);
+
+ document.getElementById('choice-container').classList.add('hide');
+ document.getElementById('results').classList.add('hide');
+
+ await delay(400);
+
+ if (timeTrialOver) {
+ break;
+ }
}
- document.getElementById('results').classList.remove('hide');
+ // results screen ------------------------------------------------------gg-
- const nextLink = document.getElementById('next-link');
- nextLink.focus();
- await waitForEvent(nextLink, 'click');
+ document.body.classList.add('game-over');
+ document.getElementById('final-correct').innerHTML = numCorrect;
+ document.getElementById('final-incorrect').innerHTML = numIncorrect;
- document.getElementById('choice-container').classList.add('hide');
- document.getElementById('results').classList.add('hide');
+ const playAgain = document.getElementById('play-again-link');
+ playAgain.focus();
+ await waitForEvent(playAgain, 'click');
- await delay(400);
+ document.getElementById('choice-container').innerHTML = '';
+ document.getElementById('scorekeeper').innerHTML = '';
+ document.body.classList.remove('game-over');
+ await delay(1000);
+ document.body.classList.remove('game');
+ for (const el of modeLinks) {
+ document.body.classList.remove('mode-' + el.dataset.mode);
+ }
}
}
diff --git a/style.css b/style.css
index cc09a9a..7d48241 100644
--- a/style.css
+++ b/style.css
@@ -46,6 +46,19 @@ body:not(.resources-loaded) #play-paragraph {
display: none;
}
+body:not(.game-over) #final-results {
+ opacity: 0;
+}
+
+#final-results {
+ transition: opacity 1s;
+}
+
+/* We have to hide these, or else they messes up layouting of elements below it. */
+body.game-over #results, body.game-over #choice-container {
+ display: none;
+}
+
#crash-trace {
text-align: start;
border: 1pt dotted white;
@@ -205,6 +218,26 @@ body:not(.resources-loaded) #play-paragraph {
background-color: red;
}
+body:not(.mode-time-trial) #time-trial-heading {
+ display: none;
+}
+
+#time-trial-heading {
+ color: rgba(255, 255, 255, 0.3);
+}
+
+#time-trial-time {
+ color: red;
+}
+
+#final-correct, #final-correct + span {
+ color: green;
+}
+
+#final-incorrect, #final-incorrect + span {
+ color: red;
+}
+
body, html {
width: 100%;
height: 100%;
|