Up-in-the-Air – commitdiff

You can use Git to clone the repository via the web URL. Download snapshot (zip)
Working unlocks for skill-based secret feathers
authorJulian Fietkau <git@fietkau.software>
Sat, 28 Sep 2024 22:59:30 +0000 (00:59 +0200)
committerJulian Fietkau <git@fietkau.software>
Sat, 28 Sep 2024 23:01:14 +0000 (01:01 +0200)
index.html
main.js

index 423819c604840a7e6c720b2185433ff46d877a24..ac16169fa3aa4e8cc8fc1803260f299fe4591eb0 100644 (file)
   }
   .ui-page.options .feather img {
     width: 6em;
-    margin: -1em 0;
+    margin: -2em 0;
     transition: 150ms transform;
     transform: scaleY(-1) translateX(-1em);
   }
     border-width: .3em;
     font-size: 1em;
   }
+  .ui-page.unlock {
+    padding: 1em;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    gap: 1em;
+  }
+  .ui-page.unlock .area {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    font-size: 2em;
+    text-align: center;
+    gap: 1em;
+  }
+  .ui-page.unlock .area img {
+    margin-top: 1em;
+    transform: scale(2, -2);
+  }
   .ui-page.pause {
     background: #000d;
     padding: 1em;
 </div>
 <button class="goto title">Return to Title Screen</button>
 </div>
+<div class="ui-page unlock">
+<div class="area">
+<p>You just unlocked a new feather:</p>
+<img src="textures/feather-blue.png" alt="Blue feather">
+<p class="name">Blue feather</p>
+</div>
+<button class="goto title">Return to Title Screen</button>
+</div>
 <div class="ui-page pause">
 <h2>Game Paused</h2>
 <div class="audio">
diff --git a/main.js b/main.js
index 2dce5f1a434fd988f7caf81371d5862c25c51088..98e831289920e67005a152bb8ad38f63c5c09d41 100644 (file)
--- a/main.js
+++ b/main.js
@@ -1115,17 +1115,19 @@ function loadSettings(game) {
     audioThemeRadio.checked = true;
   }
   for(let unlockedFeather of settings['unlocks']) {
-    let label = document.createElement('label');
-    let radio = document.createElement('input');
-    radio.type = 'radio';
-    radio.name = 'upInTheAirGame-feather';
-    radio.value = unlockedFeather.toLowerCase().replace(/ /g, '');
-    label.appendChild(radio);
-    let img = document.createElement('img');
-    img.src = 'textures/feather-' + radio.value + '.png';
-    img.alt = unlockedFeather + ' feather';
-    label.appendChild(img);
-    ui.querySelector('.feather').appendChild(label);
+    if(!game.ui.root.querySelector('.ui-page.options .feather input[value="' + unlockedFeather + '"]')) {
+      let radio = document.createElement('input');
+      radio.type = 'radio';
+      radio.name = 'upInTheAirGame-feather';
+      radio.value = unlockedFeather;
+      let img = document.createElement('img');
+      img.src = 'textures/feather-' + unlockedFeather + '.png';
+      img.alt = unlockedFeather[0].toUpperCase() + unlockedFeather.slice(1) + ' feather';
+      let label = document.createElement('label');
+      label.appendChild(radio);
+      label.appendChild(img);
+      game.ui.root.querySelector('.ui-page.options .feather').appendChild(label);
+    }
   }
   if(!ui.querySelector('.feather input[value="' + settings['feather'] + '"]')) {
     settings['feather'] = 'blue';
@@ -1528,6 +1530,56 @@ function createMeshes(game) {
   game.view.scene.add(game.objects.backdrop);
 }
 
+function unlockFeather(game, feather, url) {
+  if(game.settings['unlocks'].includes(feather)) {
+    return false;
+  }
+  game.settings['unlocks'].push(feather);
+  if(!url) {
+    url = 'textures/feather-' + feather + '.png';
+  }
+  if(!game.assets['textures']['feather-' + feather]) {
+    (new THREE.TextureLoader()).load(url, (result) => {
+      result.colorSpace = THREE.SRGBColorSpace;
+      result.minFilter = THREE.NearestFilter;
+      result.magFilter = THREE.NearestFilter;
+      result.repeat = new THREE.Vector2(1, -1);
+      result.wrapT = THREE.RepeatWrapping;
+      game.assets['textures']['feather-' + feather] = result;
+    }, () => {}, (err) => {
+      console.error('Error while loading ' + feather + ' feather texture: ' + err);
+    });
+  }
+  // Custom hash function that ensures our unlockables get stored in the same order,
+  // regardless of the order in which they get unlocked.
+  let miniHash = (input) => {
+    return 4 * input.charCodeAt(0) + 3 * input.charCodeAt(2) + 2 * input.charCodeAt(3);
+  }
+  game.settings['unlocks'].sort((u1, u2) => miniHash(u1) > miniHash(u2));
+  let insertAfterFeather = 'purple';
+  if(game.settings['unlocks'].indexOf(feather) >= 1) {
+    insertAfterFeather = game.settings['unlocks'][game.settings['unlocks'].indexOf(feather) - 1];
+  }
+  let radio = document.createElement('input');
+  radio.type = 'radio';
+  radio.name = 'upInTheAirGame-feather';
+  radio.value = feather;
+  let img = document.createElement('img');
+  img.src = url;
+  img.alt = feather[0].toUpperCase() + feather.slice(1) + ' feather';
+  let label = document.createElement('label');
+  label.appendChild(radio);
+  label.appendChild(img);
+  game.ui.root.querySelector('.ui-page.options .feather input[value="' + insertAfterFeather + '"]').parentNode.after(label);
+  applySettings(game);
+  let ui = game.ui.root.querySelector('.ui-page.unlock');
+  let img2 = ui.querySelector('img');
+  img2.src = img.src;
+  img2.alt = img.alt;
+  ui.querySelector('p.name').innerText = img2.alt;
+  return true;
+}
+
 window['game'] = {
   state: 'loadingAssets',
   ui: {
@@ -1680,8 +1732,33 @@ game.ui.moveToPage = (target, skipFade = false) => {
     game.ui.root.querySelector('.ui-page.outro .optionalPlural').innerText = 'word' + ((game.objects.words.collectedCount == 1) ? '' : 's') + '.';
     let ratingElem = game.ui.root.querySelector('.ui-page.outro .rating');
     let exampleElems = game.ui.root.querySelectorAll('.ui-page.outro .examples');
+    let finalParagraph = game.ui.root.querySelector('.ui-page.outro .area > p:last-child');
     ratingElem.style.display = 'none';
     exampleElems.forEach(elem => { elem.style.display = 'none'; });
+    let returnButton = game.ui.root.querySelector('.ui-page.outro button.goto');
+    if(game.objects.words.collectedCount == 100 || game.objects.words.collectedCount == 0) {
+      finalParagraph.style.display = 'none';
+      let neededUnlocking = false;
+      if(game.objects.words.collectedCount == 100) {
+        neededUnlocking = unlockFeather(game, 'golden');
+      } else {
+        neededUnlocking = unlockFeather(game, 'ghost');
+      }
+      if(neededUnlocking) {
+        returnButton.innerText = 'Continue';
+        returnButton.classList.remove('title');
+        returnButton.classList.add('unlock');
+      } else {
+        returnButton.innerText = 'Return to Title Screen';
+        returnButton.classList.remove('unlock');
+        returnButton.classList.add('title');
+      }
+    } else {
+      finalParagraph.style.display = 'block';
+      returnButton.innerText = 'Return to Title Screen';
+      returnButton.classList.remove('unlock');
+      returnButton.classList.add('title');
+    }
     if(game.objects.words.collectedCount > 0) {
       if(game.objects.words.collectedCount == 100) {
         ratingElem.style.display = 'block';
@@ -1751,7 +1828,7 @@ game.ui.moveToPage = (target, skipFade = false) => {
       }
     } else {
       ratingElem.style.display = 'block';
-      ratingElem.innerText = 'You completed the course while dodging every word. That’s an achievement on its own. Respect!';
+      ratingElem.innerText = 'You completed the course while dodging every word. That’s an achievement all on its own. Respect!';
     }
   }
   if(target == 'options') {