Up-in-the-Air – commitdiff

You can use Git to clone the repository via the web URL. Download snapshot (zip)
Mock up complete options screen (mostly not functional yet)
authorJulian Fietkau <git@fietkau.software>
Mon, 23 Sep 2024 23:40:11 +0000 (01:40 +0200)
committerJulian Fietkau <git@fietkau.software>
Mon, 23 Sep 2024 23:40:11 +0000 (01:40 +0200)
index.html
main.js

index 8d7ea369518558e6f95303d67e3104c29e45d596..ea9c40a6272e9433f0a8a626e511c676516d4ed2 100644 (file)
@@ -57,6 +57,7 @@
   }
   .game-upintheair.font-atkinson * {
     font-family: 'Atkinson Hyperlegible' !important;
+    font-size-adjust: 0.45;
   }
   .game-upintheair.font-opendyslexic * {
     font-family: 'OpenDyslexic' !important;
     margin-top: 1em;
     text-align: center;
   }
+  .ui-page .area.twocol {
+    flex-grow: 1;
+    display: flex;
+    flex-direction: row;
+    gap: 2em;
+  }
+  .ui-page .area.twocol .column {
+    display: flex;
+    flex-direction: column;
+    flex-grow: 1;
+    justify-content: space-between;
+    gap: 1ex;
+  }
+  .ui-page .area.twocol .column:last-child {
+    width: 40%;
+    flex-grow: 0;
+    flex-shrink: 0;
+  }
+  .ui-page.options .feather {
+    flex-grow: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-evenly;
+    align-items: center;
+  }
+  .ui-page.options .feather input {
+    position: absolute;
+    left: -99999px;
+    width: 1px;
+    height: 1px;
+    opacity: 0;
+  }
+  .ui-page.options .feather img {
+    width: 6em;
+    margin: -1em 0;
+    transition: 150ms transform;
+  }
+  .ui-page.options .feather img {
+    transform: translateX(-1em);
+  }
+  .ui-page.options .feather input:checked + img {
+    transform: translateX(1em);
+  }
+  .ui-page.options .feather label:hover img, .ui-page.options .feather label:focus-within img {
+    filter: 
+        drop-shadow(-.1em -.1em 0 #fff) 
+        drop-shadow(.1em -.1em 0 #fff)
+        drop-shadow(.1em .1em 0 #fff) 
+        drop-shadow(-.1em .1em 0 #fff);
+  }
   .ui-page.loading {
     display: flex;
     flex-direction: column;
     align-items: center;
     gap: 1em;
   }
+  .ui-page.options .area.accessibility {
+    display: none;
+  }
+  .ui-page.options .areatabs {
+    margin-bottom: -1em;
+    display: flex;
+    align-items: center;
+    gap: 1ex;
+  }
+  .ui-page.options .areatabs button {
+    font-size: 1.6em;
+    background: #000b;
+    color: #fff;
+    font-family: 'Jersey 10';
+    border: none;
+    border-radius: 1em 1em 0 0;
+    cursor: pointer;
+  }
+  .ui-page.options .areatabs button.active {
+    padding-bottom: .7em;
+  }
   .ui-page.options button {
     width: 8em;
     padding: .45em .6em;
   }
   .ui-page input[type=range] {
     font-size: 1em;
-    accent-color: #f998a6;
+    accent-color: #c50031;
+  }
+  .ui-page input[type=radio], .ui-page input[type=checkbox] {
+    width: 1.3em;
+    height: 1.3em;
+    margin: .2em 0;
+    font-size: 1em;
+    vertical-align: sub;
+    accent-color: #c50031;
   }
   .ui-page.options .area button {
     padding: .3em;
     font-size: 1em;
     font-family: 'Jersey 10';
   }
+  .ui-page.options .controls p:last-child {
+    margin-top: 1em;
+    padding-right: 1em;
+    height: 4em;
+  }
+  .ui-page.options .controls p:last-child span:not(:first-child) {
+    display: none;
+  }
+  .ui-page.options .graphics *:not(.audio) label {
+    display: inline;
+    margin-left: 1ex;
+  }
+  .ui-page.options p.annotation {
+    margin-left: 1.6em;
+  }
+  .ui-page.options label.standardfont {
+    font-family: 'Jersey 10' !important;
+    font-size-adjust: 0.45;
+  }
+  .ui-page.options label.atkinson {
+    font-family: 'Atkinson Hyperlegible' !important;
+    font-size-adjust: 0.45;
+  }
+  .ui-page.options label.opendyslexic {
+    font-family: OpenDyslexic !important;
+    font-size-adjust: 0.4;
+  }
+  .ui-page.options div.difficulty p:nth-child(4) {
+    margin-top: 1ex;
+  }
   .ui-page .audio {
     display: grid;
     gap: .5ex 1ex;
   .ui-page.options .audio {
     grid-template-columns: auto auto auto;
   }
+  .ui-page.options .audio h3 {
+    grid-column: 1 / 4;
+  }
   .ui-page.pause .audio {
     grid-template-columns: auto auto;
     color: #fff;
     justify-content: end;
     gap: 1ex;
   }
+  .ui-page.options *:not(.audio) > label {
+    display: flex;
+    align-items: center;
+    justify-content: start;
+    gap: 1ex;
+    width: max-content;
+  }
+  .ui-page.options .keyboard {
+    display: flex;
+    flex-direction: column;
+    gap: 1ex;
+  }
+  .ui-page.options .keyboard label:not(:nth-child(8)) {
+    width: 8em;
+    margin: 0 auto;
+    display: flex;
+    justify-content: flex-end;
+  }
+  .ui-page.options .keyboard label button {
+    box-sizing: border-box;
+    width: 5em;
+  }
   .ui-page .audio input[type=range] {
     width: 8em;
   }
 </div>
 <div class="ui-page options options-general">
 <h2>Options</h2>
-<div class="area">
+<div class="areatabs">
+<button class="general active">General</button>
+<button class="accessibility">Accessibility</button>
+</div>
+<div class="area twocol general">
+<div class="column">
+<div class="controls">
+<h3>Controls</h3>
+<p><label><input type="radio" name="upInTheAirGame-controls" value="mouse" checked> Mouse movement</label></p>
+<p><label><input type="radio" name="upInTheAirGame-controls" value="touchpad"> Virtual touchpad</label></p>
+<p><label><input type="radio" name="upInTheAirGame-controls" value="thumbstick"> Virtual thumbstick</label></p>
+<p><label><input type="radio" name="upInTheAirGame-controls" value="keyboard"> Keyboard</label></p>
+<p><label><input type="radio" name="upInTheAirGame-controls" value="gamepad"> Gamepad</label></p>
+<p>
+<span class="mouse">The game is controlled by moving the mouse cursor around directly in the play area.</span>
+<span class="touchpad">The game is controlled through touch movements on an on-screen touchpad. Movements correspond directly to the play area.</span>
+<span class="thumbstick">The game is controlled through touch movements on an on-screen thumbstick. Directional movements steer the cursor.</span>
+<span class="keyboard">The game is controlled through arrow or WASD key presses. Key rebinding and additional accessibility options are available.</span>
+<span class="gamepad">The game is controlled with the thumbstick or d-pad of an attached gamepad accessory.</span>
+</p>
+</div>
+<div class="graphics">
+<h3>Graphics</h3>
+<p>Quality:
+<label><input type="radio" name="upInTheAirGame-font" value="standard" checked> Full</label>
+<label><input type="radio" name="upInTheAirGame-font" value="standard" checked> Reduced</label>
+<label><input type="radio" name="upInTheAirGame-font" value="standard" checked> Minimal</label>
+</div>
 <div class="audio">
+<h3>Audio</h3>
 <label>Music:
 <input type="range" min="0" max="100" value="50" step="1" class="music"></label>
 <span>50</span>
 <span>50</span>
 <button class="sounds">Test</button>
 </div>
-<p><label>Font: <select class="font">
+</div>
+
+<div class="column">
+<h3>Feather Customization</h3>
+<p>You can change the feather’s visual appearance. This is an aesthetic choice with no impact on the game mechanics.</p>
+<div class="feather">
+<label><input type="radio" name="upInTheAirGame-feather" value="blue" checked><img src="textures/feather.png" alt="Blue feather"></label>
+<label><input type="radio" name="upInTheAirGame-feather" value="red"><img src="textures/feather.png" alt="Red feather"></label>
+<label><input type="radio" name="upInTheAirGame-feather" value="green"><img src="textures/feather.png" alt="Green feather"></label>
+<label><input type="radio" name="upInTheAirGame-feather" value="black"><img src="textures/feather.png" alt="Black feather"></label>
+<label><input type="radio" name="upInTheAirGame-feather" value="brown"><img src="textures/feather.png" alt="Brown feather"></label>
+</div>
+</div>
+</div>
+
+
+<div class="area twocol accessibility">
+<div class="column">
+<div>
+<h3>Visuals</h3>
+<label><input type="checkbox" value="highcontrast"> High contrast mode</label>
+<p class="annotation">Render collectibles as bright green dots, replace clouds with dark background.</p>
+</div>
+<div>
+<h3>Font</h3>
+<p><label class="standardfont"><input type="radio" name="upInTheAirGame-font" value="standard" checked> Standard</label></p>
+<p><label class="atkinson"><input type="radio" name="upInTheAirGame-font" value="atkinson"> Atkinson Hyperlegible</label></p>
+<p><label class="opendyslexic"><input type="radio" name="upInTheAirGame-font" value="opendyslexic"> OpenDyslexic</label></p>
+</div>
+<div class="difficulty">
+<h3>Difficulty</h3>
+<p><label>Collecting radius:&nbsp;&nbsp;&nbsp;<select class="collectingradius">
 <option value="standard" selected>Standard</option>
-<option value="atkinson">Atkinson Hyperlegible</option>
-<option value="opendyslexic">OpenDyslexic</option>
+<option value="generous">Generous</option>
+<option value="eager">Eager</option>
+</select></label></p>
+<p class="annotation">This setting adjusts how close you need to get to a collectible in order to pick it up.</p>
+<p><label>Gameplay speed:&nbsp;&nbsp;&nbsp;<select class="gameplayspeed">
+<option value="100" selected>100 %</option>
+<option value="50">50 %</option>
+<option value="25">25 %</option>
+<option value="10">10 %</option>
 </select></label></p>
+<p class="annotation">This setting adjusts the speed of the gameplay clock – movement through the course and the feather’s physics will be slower.</p>
+</div>
+</div>
+
+<div class="column">
+<div class="keyboard">
+<h3>Keyboard Settings</h3>
+<p>These settings only have an effect if keyboard controls are enabled.</p>
+<label>Up: <button class="up" value="Arrow up|w">🠕 or W</button></label>
+<label>Right: <button class="right" value="Arrow right|d">🠖 or D</button></label>
+<label>Down: <button class="down" value="Arrow down|s">🠗 or S</button></label>
+<label>Left: <button class="left" value="Arrow left|a">🠔 or A</button></label>
+<button value="reset">Reset to default</button>
+<label><input type="checkbox" value="tapmode"> Tap mode</label>
+<p class="annotation">With this setting enabled, movement gets triggered by tapping the keys instead of holding them. Tap a direction multiple times to accelerate, tap the opposite direction to stop.</p>
+</div>
+</div>
 </div>
+
+
 <button class="goto title">Back</button>
 </div>
 <div class="ui-page credits">
diff --git a/main.js b/main.js
index 5d7c132f47f80831df61473a51906324aabe080a..047843f55dbb1aded5f158fe9082c334dff797ad 100644 (file)
--- a/main.js
+++ b/main.js
@@ -654,15 +654,39 @@ game.ui.root.querySelectorAll('.options .audio button').forEach((btn) => {
     }
   });
 });
-function checkFontSelection(select) {
+function checkFontSelection() {
+  const checked = document.querySelector('input[name=upInTheAirGame-font]:checked');
+  if(!checked) {
+    return;
+  }
+  const newFont = checked.value;
   game.ui.root.classList.remove('font-atkinson', 'font-opendyslexic');
-  const newFont = select.value;
   if(newFont != 'standard') {
     game.ui.root.classList.add('font-' + newFont);
   }
 }
-checkFontSelection(game.ui.root.querySelector('.options select.font'));
-game.ui.root.querySelector('.options select.font').addEventListener('change', e => checkFontSelection(e.target));
+checkFontSelection();
+game.ui.root.querySelectorAll('.options input[name=upInTheAirGame-font]').forEach((radio) => {
+  radio.addEventListener('change', e => checkFontSelection());
+});
+game.ui.root.querySelectorAll('.options .controls input').forEach((radio) => {
+  radio.addEventListener('change', (e) => {
+    e.target.closest('.controls').querySelectorAll('span:not(.' + e.target.value + ')').forEach(span => span.style.display = 'none');
+    e.target.closest('.controls').querySelector('span.' + e.target.value).style.display = 'block';
+  });
+});
+game.ui.root.querySelectorAll('.ui-page .areatabs button').forEach((btn) => {
+  btn.addEventListener('click', (e) => {
+    btn.parentNode.querySelectorAll('button').forEach((otherBtn) => {
+      otherBtn.classList.remove('active');
+      let val = otherBtn.classList[0];
+      otherBtn.closest('.ui-page').querySelector('div.' + val).style.display = 'none';
+    });
+    btn.classList.add('active');
+    let val = Array.from(btn.classList).filter(c => c != 'active')[0];
+    btn.closest('.ui-page').querySelector('div.' + val).style.display = 'flex';
+  });
+});
 game.ui.moveToPage = (target, skipFade = false) => {
   let fadeDuration = 250;
   if(skipFade) {