Up-in-the-Air – commitdiff

You can use Git to clone the repository via the web URL. Download snapshot (zip)
Introduce mute and fullscreen buttons
authorJulian Fietkau <git@fietkau.software>
Sun, 29 Sep 2024 18:44:54 +0000 (20:44 +0200)
committerJulian Fietkau <git@fietkau.software>
Sun, 29 Sep 2024 18:44:54 +0000 (20:44 +0200)
index.html
main.js

index d57bad92361d1145738631bdf518d311e5a66c1d..744dd75dac913ae19802bcb9258d16c902efe7f0 100644 (file)
@@ -61,8 +61,9 @@
   .game-upintheair {
     container-type: inline-size;
     box-sizing: border-box;
-    width: calc(100% - 2 * var(--game-margin));
-    height: calc(100svh - 2 * var(--game-margin));
+    width: 100cqw;
+    height: 100cqh;
+    padding: var(--game-margin);
     font-family: sans-serif;
     line-height: 1;
     display: flex;
     align-items: flex-start;
     --game-margin: 2rem;
   }
+  .game-upintheair:fullscreen {
+    background: #eee;
+  }
+  @media (prefers-color-scheme: dark) {
+    .game-upintheair:fullscreen {
+      background: #111;
+    }
+  }
   .game-upintheair .ui-container {
     position: relative;
     width: min(calc(100cqh - 2 * var(--game-margin)), calc(100cqw));
@@ -85,8 +94,8 @@
     width: min(4cm, 100cqw, calc(100cqh - 2 * var(--game-margin)));
     aspect-ratio: 1 / 1;
     position: absolute;
-    bottom: 0;
-    right: 0;
+    bottom: var(--game-margin);
+    right: var(--game-margin);
     z-index: 25;
   }
   .virtual-input-left .virtual-input-widget {
     justify-content: space-evenly;
     align-items: center;
   }
-  .ui-page.options .feather input {
+  .ui-page.options .feather input, .ui-page.title .system-buttons input {
     position: absolute;
     left: -99999px;
     width: 1px;
     background: #f9c8d5;
     border-image-source: url('textures/button-pressed.png');
   }
+  .ui-page.title .system-buttons {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    padding: .7ex;
+    display: flex;
+    flex-direction: column;
+    align-items: start;
+    gap: 1ex;
+  }
+  .ui-page.title.end .system-buttons {
+    left: unset;
+    right: 0;
+    align-items: end;
+  }
+  @media not (hover: hover) {
+    .ui-page.title .system-buttons {
+      font-size: 2em;
+    }
+  }
+  .ui-page.title .system-buttons svg {
+    width: 2em;
+    height: 2em;
+  }
+  .ui-page.title .system-buttons svg:hover {
+    filter: drop-shadow(0 0 1px #000);
+  }
+  .ui-page.title .system-buttons label input:not(:checked) + svg g {
+    display: none;
+  }
+  .ui-page.title .system-buttons button {
+    appearance: none;
+    display: block;
+    margin: 0;
+    padding: 0;
+    border: none;
+    background: none;
+    font-size: 1em;
+  }
   .ui-page.title .footer {
     position: absolute;
     right: 0;
 <button class="goto openingcutscene">Start Game</button>
 <button class="goto options">Options</button>
 <button class="goto credits">Credits</button>
+<div class="system-buttons">
+<label>
+<input type="checkbox" name="mute">
+<svg version="1.1" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg" aria-label="Mute audio">
+<path d="m14 4v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-5v1h-1v1h-1v8h1v1h1v1h5v1h1v1h1v1h1v1h1v1h1v1h1v1h4v-1h1v-6h1v1h1v1h3v2h1v1h4v-1h1v-2h1v-2h1v-4h1v-4h-1v-5h-1v-1h-1v-2h-1v-1h-4v1h-1v2h-3v1h-1v1h-1v-6h-1v-1h-3z" fill="#fff"/>
+<path d="m15 6v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-5v1h-1v6h1v1h5v1h1v1h1v1h1v1h1v1h1v1h1v1h2v-22h-1zm11 2v3h1v2h1v8h-1v2h-1v3h2v-2h1v-2h1v-4h1v-2h-1v-5h-1v-1h-1v-2h-1zm-4 3v1h1v2h1v6h-1v2h-1v1h3v-1h1v-4h1v-2h-1v-4h-1v-1h-2zm-2 2v1h-1v1h1v4h-1v1h1v1h1v-1h1v-6h-1v-1z" fill="#000"/>
+<g>
+<path d="m27 0v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v4h1v1h1v1h1v1h4v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-4h-1v-1h-1v-1h-1v-1h-3z" fill="#fff"/>
+<path d="m28 2v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v2h1v1h1v1h2v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-2h-1v-1h-1v-1h-1z" fill="#000"/>
+</g>
+</svg>
+</label>
+<button>
+<svg version="1.1" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg">
+<path d="m0 0v11h7v-4h4v-7h-11zm23 0v7h4v4h7v-11h-11zm-23 23v11h11v-7h-4v-4h-7zm27 0v4h-4v7h11v-11h-7z" fill="#fff"/>
+<path d="m2 2v7h3v-4h4v-3h-7zm23 0v3h4v4h3v-7h-7zm-23 23v7h7v-3h-4v-4h-3zm27 0v4h-4v3h7v-7h-3z" fill="#000"/>
+</svg>
+</button>
+</div>
 <div class="footer">
 <span>Version: dev</span>
 <span>A game for <a href="https://itch.io/jam/fedi-jam" target="_blank" class="fediverse">FediJam 2024</a></span>
diff --git a/main.js b/main.js
index 71fb04100d4e7537944caa7304ff2c534cf0712c..3916670ad21c185a57c1ed36a215332f57b3fcfc 100644 (file)
--- a/main.js
+++ b/main.js
@@ -23,7 +23,7 @@ function playRandomSound(game) {
   let sound = new THREE.Audio(game.view.audioListener);
   sound.setBuffer(game.assets['audio']['sound' + index + '-' + game.settings['audio']['theme']]);
   sound.setVolume(game.settings['audio']['sounds']);
-  if(game.settings['audio']['sounds'] > 0) {
+  if(!game.view.muted && game.settings['audio']['sounds'] > 0) {
     sound.play();
   }
 }
@@ -235,6 +235,7 @@ function initializeGame(game, canvas) {
 
   game.objects = {};
   game.view = {};
+  game.view.muted = false;
   game.view.canvas = canvas;
   game.ui.virtualInput = canvas.closest('.game-upintheair').querySelector('.virtual-input-widget');
 
@@ -730,20 +731,21 @@ function animate(game, scene) {
       if(!game.ui.reachedStart) {
         if(game.timeProgress < 1) {
           game.ui.root.querySelector('.ui-page.title h1').style.opacity = '0';
-          game.ui.root.querySelectorAll('.ui-page.title button').forEach((btn) => {
+          game.ui.root.querySelectorAll('.ui-page.title button').forEach((btn) => {
             btn.disabled = true;
             btn.style.position = 'relative';
             btn.style.left = '10em';
             btn.style.opacity = '0';
           });
           game.ui.root.querySelector('.ui-page.title .footer').style.opacity = '0';
+          game.ui.root.querySelector('.ui-page.title .system-buttons').style.opacity = '0';
           cameraX += Math.max(0.0, 1 - easeInOut(0.5 + game.timeProgress / 2));
           cameraY += Math.max(0.0, 10 * Math.pow(0.5 - game.timeProgress / 2, 2));
         } else if(game.timeProgress >= 1.0 && game.timeProgress <= 2.1) {
           game.ui.root.querySelector('.ui-page.title h1').style.opacity = Math.min(1.0, game.timeProgress - 1.0).toFixed(2);
         }
         if(game.timeProgress >= 1.5 && game.timeProgress <= 3.0) {
-          game.ui.root.querySelectorAll('.ui-page.title button').forEach((btn) => {
+          game.ui.root.querySelectorAll('.ui-page.title button').forEach((btn) => {
             let timeOffset = Array.from(btn.parentNode.children).indexOf(btn) - 2;
             btn.style.left = 10 * easeInOut(Math.max(0.0, Math.min(1.0, 0.3 * timeOffset + 2.5 - game.timeProgress))).toFixed(2) + 'em';
             let opacity = easeInOut(Math.max(0.0, Math.min(1.0, -0.3 * timeOffset + game.timeProgress - 1.5)));
@@ -755,11 +757,13 @@ function animate(game, scene) {
         }
         if(game.timeProgress >= 3.0 && game.timeProgress <= 4.0) {
           game.ui.root.querySelector('.ui-page.title .footer').style.opacity = easeInOut(Math.max(0.0, Math.min(1.0, game.timeProgress - 3.0))).toFixed(2);
+          game.ui.root.querySelector('.ui-page.title .system-buttons').style.opacity = easeInOut(Math.max(0.0, Math.min(1.0, game.timeProgress - 3.0))).toFixed(2);
         }
         if(game.timeProgress > 4.0 && !game.ui.reachedStart) {
           game.ui.root.querySelector('.ui-page.title h1').removeAttribute('style');
-          game.ui.root.querySelectorAll('.ui-page.title button').forEach(btn => { btn.disabled = false; btn.removeAttribute('style'); });
+          game.ui.root.querySelectorAll('.ui-page.title button').forEach(btn => { btn.disabled = false; btn.removeAttribute('style'); });
           game.ui.root.querySelector('.ui-page.title .footer').removeAttribute('style');
+          game.ui.root.querySelector('.ui-page.title .system-buttons').removeAttribute('style');
           game.ui.reachedStart = true;
         }
       }
@@ -785,7 +789,9 @@ function animate(game, scene) {
         if(!game.view.windSound.isPlaying) {
           game.view.windSound.setVolume(game.settings['audio']['sounds']);
           game.view.windSound.offset = 0;
-          game.view.windSound.play();
+          if(!game.view.muted) {
+            game.view.windSound.play();
+          }
         }
       }
       if(game.timeProgress > 1.0 && game.timeProgress <= 5.0) {
@@ -829,7 +835,9 @@ function animate(game, scene) {
           elem.style.opacity = opacity.toFixed(2);
         });
         game.view.music.offset = 0;
-        game.view.music.play();
+        if(!game.view.muted) {
+          game.view.music.play();
+        }
         moveToPage(game, 'gameplay', true);
       }
     } else if(game.ui.currentPage == 'endingcutscene') {
@@ -906,7 +914,9 @@ function animate(game, scene) {
     const remainingRealTime = (game.timeTotal - game.timeProgress) / (game.settings['difficulty']['speed'] / 100);
     if(remainingRealTime >= game.assets['audio']['music-' + game.settings['audio']['theme']].duration - 2) {
       game.view.music.offset = 0;
-      game.view.music.play();
+      if(!game.view.muted) {
+        game.view.music.play();
+      }
     }
   }
 
@@ -1636,6 +1646,7 @@ function moveToPage(game, target, skipFade = false) {
       game.ui.root.querySelector('.ui-page.title h1').removeAttribute('style');
       game.ui.root.querySelectorAll('.ui-page.title button').forEach(btn => { btn.disabled = false; btn.removeAttribute('style'); });
       game.ui.root.querySelector('.ui-page.title .footer').removeAttribute('style');
+      game.ui.root.querySelector('.ui-page.title .system-buttons').removeAttribute('style');
       game.ui.reachedStart = true;
     }, fadeDuration);
   }
@@ -1808,11 +1819,15 @@ function moveToPage(game, target, skipFade = false) {
     if(game.timeProgress >= 1.0) {
       game.view.windSound.offset = game.timeProgress - 1.0;
       game.view.windSound.setVolume(game.settings['audio']['sounds']);
-      game.view.windSound.play();
+      if(!game.view.muted) {
+        game.view.windSound.play();
+      }
     }
   } else if(game.ui.currentPage == 'pause' && target == 'gameplay') {
     game.view.music.offset = (game.timeProgress / (game.settings['difficulty']['speed'] / 100)) % game.assets['audio']['music-' + game.settings['audio']['theme']].duration;
-    game.view.music.play();
+    if(!game.view.muted) {
+      game.view.music.play();
+    }
   }
   game.ui.previousPage = game.ui.currentPage;
   game.ui.currentPage = target;
@@ -1956,6 +1971,16 @@ game.ui.root.querySelectorAll('.options .controls input, .options .graphics inpu
     }
   });
 });
+game.ui.root.querySelector('.ui-page.title .system-buttons input').addEventListener('change', (e) => {
+  game.view.muted = e.target.checked;
+});
+game.ui.root.querySelector('.ui-page.title .system-buttons button').addEventListener('click', (e) => {
+  if(document.fullscreenElement == game.ui.root.parentNode) {
+    document.exitFullscreen();
+  } else {
+    game.ui.root.parentNode.requestFullscreen();
+  }
+});
 game.ui.root.querySelectorAll('.ui-page .audio input[type=range]').forEach((elem) => {
   elem.addEventListener('input', (e) => {
     let audioCategory = Array.from(e.target.classList).filter(v => ['music', 'sounds'].includes(v))[0];
@@ -1974,7 +1999,9 @@ game.ui.root.querySelectorAll('.options .audio button').forEach((btn) => {
         }
       } else {
         game.view.music.offset = 36;
-        game.view.music.play();
+        if(!game.view.muted) {
+          game.view.music.play();
+        }
         game.view.music.timeoutID = setTimeout(() => {
           game.view.music.stop();
         }, 6000);