position: relative;
width: min(calc(100cqh - 2 * var(--game-margin)), calc(100cqw));
aspect-ratio: 1 / 1;
+ background: #fff;
font-size-adjust: 0.4;
image-rendering: pixelated;
box-shadow: 1px 1px 3px #000a;
flex-grow: 0;
flex-shrink: 0;
}
+ .ui-page.controls {
+ background-color: #fff;
+ color: #444;
+ padding: 1em;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.5em;
+ text-align: center;
+ }
+ .ui-page.controls p {
+ margin: .15em auto;
+ max-width: 23em;
+ }
+ .ui-page.controls p:last-child {
+ font-size: .85;
+ }
+ .ui-page.controls svg {
+ margin: 1em;
+ max-height: 8em;
+ }
+ .ui-page.controls svg path {
+ fill: currentColor;
+ }
+ .ui-page.controls button {
+ margin: 1em;
+ border: 1px solid #444;
+ box-shadow: 0 2px #444;
+ padding: .6em 1em;
+ background: #fff;
+ color: #444;
+ font-family: unset;
+ font-size: 1em;
+ }
+ .ui-page.controls button:hover {
+ background: #eee;
+ border: 1px solid #444;
+ }
+ .ui-page.controls button:active {
+ background: #ddd;
+ border: 1px solid #444;
+ }
.ui-page.options .feather {
flex-grow: 1;
display: flex;
<progress value="0" max="100"></progress>
<div><span>0</span> %</div>
</div>
+<div class="ui-page controls">
+<p class="mouse">You appear to be using a device with a <strong>mouse or touchpad</strong> and an on-screen cursor.</p>
+<p class="touchpad">You appear to be using a device with a <strong>touch screen</strong>.</p>
+<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
+<path class="mouse" d="m16 2c-1.654 0-3 1.346-3 3v23c0 1.654 1.346 3 3 3h11.96v3h-1.955c-0.553 0-1 0.447-1 1v4c0 0.553 0.447 1 1 1h12c0.553 0 1-0.447 1-1v-4c0-0.553-0.447-1-1-1h-1.955v-3h11.96c1.654 0 3-1.346 3-3v-23c0-1.654-1.346-3-3-3h-32zm0 2h32c0.552 0 1 0.448 1 1v18h-2v-15c0-1.103-0.897-2-2-2h-26c-1.103 0-2 0.897-2 2v15h-2v-18c0-0.552 0.448-1 1-1zm3 4h26v15h-6v2h10v3c0 0.552-0.448 1-1 1h-32c-0.552 0-1-0.448-1-1v-3h18v-2h-14v-15zm16 15v2h2v-2h-2zm-5.045 8h4.09v3h-4.09v-3zm-2.955 5h10v2h-10v-2zm-22 6c-1.654 0-3 1.346-3 3v14c0 1.654 1.346 3 3 3h36c1.654 0 3-1.346 3-3v-14c0-1.654-1.346-3-3-3h-23v2h23c0.552 0 1 0.448 1 1v14c0 0.552-0.448 1-1 1h-36c-0.552 0-1-0.448-1-1v-14c0-0.552 0.448-1 1-1h7v-2h-7zm9 0v2h2v-2h-2zm40.94 0c-3.827 0-6.939 3.112-6.939 6.939v6.121c0 3.827 3.112 6.939 6.939 6.939h0.1211c3.827 0 6.938-3.113 6.938-6.941v-6.119c0-3.827-3.112-6.939-6.939-6.939h-0.1191zm0 2h0.1191c2.724 0 4.939 2.214 4.939 4.939v6.119c0 2.724-2.214 4.939-4.939 4.939h-0.1191c-2.724 0-4.939-2.214-4.939-4.939v-6.119c0-2.724 2.215-4.939 4.939-4.939zm-48.94 2v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm17 0c-0.553 0-1 0.447-1 1v2c0 0.553 0.447 1 1 1s1-0.447 1-1v-2c0-0.553-0.447-1-1-1zm-49 5v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h2v-2h-2zm-32 5v2h2v-2h-2zm4 0v2h2v-2h-2zm4 0v2h18v-2h-18zm20 0v2h2v-2h-2zm4 0v2h2v-2h-2z"/>
+<path class="touchpad" d="m16 2c-1.654 0-3 1.346-3 3v54c0 1.654 1.346 3 3 3h30c1.654 0 3-1.346 3-3v-33h2c1.654 0 3-1.346 3-3v-12c0-1.654-1.346-3-3-3h-2v-3c0-1.654-1.346-3-3-3zm0 2h30c0.551 0 1 0.448 1 1v3h-14c-1.654 0-3 1.346-3 3h-15v-6c0-0.552 0.449-1 1-1zm17 6h18c0.551 0 1 0.448 1 1v12c0 0.552-0.449 1-1 1h-9c-0.265 0-0.519 0.105-0.707 0.293l-4.293 4.293v-3.586c0-0.553-0.448-1-1-1h-3c-0.551 0-1-0.448-1-1v-12c0-0.552 0.449-1 1-1zm1 2v2h8v-2zm10 0v2h2v-2zm4 0v2h2v-2zm-33 1h15v10c0 1.654 1.346 3 3 3h2v5c0 0.404 0.2442 0.7698 0.6172 0.9238 0.124 0.052 0.2538 0.07617 0.3828 0.07617 0.261 0 0.516-0.102 0.707-0.293l5.707-5.707h4.586v18h-19v2h19v13c0 0.552-0.449 1-1 1h-30c-0.551 0-1-0.448-1-1v-13h7v-2h-7zm19 3v2h16v-2zm0 4v2h16v-2zm-10 24v2h2v-2zm7 4c-2.757 0-5 2.243-5 5s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5zm0 2c1.654 0 3 1.346 3 3s-1.346 3-3 3-3-1.346-3-3 1.346-3 3-3zm-1 2v2h2v-2z"/>
+</svg>
+<p>Setting gameplay controls to: <strong><span class="mouse">Mouse movement</span><span class="touchpad">Virtual touchpad</span></strong></p>
+<p>If this is incorrect or you have other preferences, different game control settings are available in the options menu.</p>
+<button class="goto title">Continue</button>
+<p>This message will only be shown on first launch.</p>
+</div>
<div class="ui-page title">
<h1>Up in the Air</h1>
<button class="goto openingcutscene">Start Game</button>
<p>“<a href="https://www.brailleinstitute.org/freefont/" target="_blank">Atkinson Hyperlegible</a>” by <a href="https://www.brailleinstitute.org/" target="_blank">Braille Institute of America, Inc.</a></p>
<p>“<a href="https://opendyslexic.org/" target="_blank">OpenDyslexic</a>” by <a href="https://abbiegonzalez.com/" target="_blank">Abbie Gonzalez</a> (<a href="https://hackers.town/@antijingoist" target="_blank">@antijingoist@hackers.town</a>)</p>
<p>Logo: “<a href="https://www.fontspace.com/precious-font-f7252" target="_blank">Precious</a>” by Bolt Cutter Design</p>
+<h4>Icons</h4>
+<p><a href="https://www.iconfinder.com/icons/2941610/computer_desktop_keyboard_monitor_mouse_icon" target="_blank">Computer</a> and <a href="https://www.iconfinder.com/icons/2990368/chat_communication_mobile_phone_smartphone_icon" target="_blank">Mobile</a> by <a href="https://www.iconfinder.com/zirconicusso" target="_blank">Zirsolostudio</a> (via <a href="https://www.iconfinder.com/" target="_blank">Iconfinder</a>)</p>
<h4>Engine</h4>
<p><a href="https://threejs.org/" target="_blank">three.js</a> v169 by mrdoob and contributors</p>
<h4>Inspiration</h4>
function loadSettings(game) {
let settings = {
- 'controls': 'mouse',
+ 'controls': null, // set during first time launch depending on device
'virtualinputleft': false,
'graphics': 1,
'audio': {
if(stored) {
let merge = (source, target) => {
for(let k of Object.keys(source)) {
- if(typeof(source[k]) == 'object' && typeof(target[k]) == 'object' && !Array.isArray(target[k])) {
+ if(source[k] != null && typeof(source[k]) == 'object' && typeof(target[k]) == 'object' && !Array.isArray(target[k])) {
merge(source[k], target[k]);
} else if(k in target) {
target[k] = source[k];
merge(stored, settings);
}
const ui = game.ui.root.querySelector('.ui-page.options');
- ui.querySelector('.controls input[value="' + settings['controls'] + '"]').checked = true;
- ui.querySelector('.controls .leftside input').checked = settings['virtualinputleft'];
- ui.querySelector('.controls .leftside').style.display = (['touchpad', 'thumbstick'].includes(settings['controls'])) ? 'block' : 'none';
- ui.querySelectorAll('.controls p span:not(.' + settings['controls'] + ')').forEach(span => span.style.display = 'none');
- ui.querySelector('.controls span.' + settings['controls']).style.display = 'block';
+ if(settings['controls']) {
+ ui.querySelector('.controls input[value="' + settings['controls'] + '"]').checked = true;
+ ui.querySelector('.controls .leftside input').checked = settings['virtualinputleft'];
+ ui.querySelector('.controls .leftside').style.display = (['touchpad', 'thumbstick'].includes(settings['controls'])) ? 'block' : 'none';
+ ui.querySelectorAll('.controls p span:not(.' + settings['controls'] + ')').forEach(span => span.style.display = 'none');
+ ui.querySelector('.controls span.' + settings['controls']).style.display = 'block';
+ }
ui.querySelector('.graphics input[value="' + settings['graphics'] + '"]').checked = true;
for(let audioCategory of ['music', 'sounds']) {
let newValue = Math.max(0, Math.min(100, Math.round(100 * settings['audio'][audioCategory])));
function applySettings(game) {
const ui = game.ui.root.querySelector('.ui-page.options');
- game.settings['controls'] = ui.querySelector('input[name="upInTheAirGame-controls"]:checked').value;
+ if(ui.querySelector('input[name="upInTheAirGame-controls"]:checked')) {
+ game.settings['controls'] = ui.querySelector('input[name="upInTheAirGame-controls"]:checked').value;
+ }
game.settings['virtualinputleft'] = ui.querySelector('.controls .leftside input').checked;
if(game.settings['virtualinputleft']) {
game.ui.root.parentNode.classList.add('virtual-input-left');
}, fadeDuration);
}
if(target == 'title') {
- game.cheatBuffer = '';
+ game.ui.cheatBuffer = '';
+ }
+ if(target == 'title' && (!game.ui.currentPage || ['loading', 'controls'].includes(game.ui.currentPage))) {
+ initializeGame(game, game.ui.root.querySelector('canvas'));
}
if(target == 'title' && game.ui.root.querySelector('.ui-page.gameplay p')) {
game.ui.root.querySelectorAll('.ui-page.gameplay p').forEach(elem => elem.remove());
}
- if(target == 'title' && game.view && game.view.windSound.isPlaying) {
+ if(target == 'title' && game.view && game.view.windSound && game.view.windSound.isPlaying) {
game.view.windSound.stop();
}
- if(target == 'title' && game.view && game.view.music.isPlaying) {
+ if(target == 'title' && game.view && game.view.music && game.view.music.isPlaying) {
game.view.music.stop();
if(game.view.music.timeoutID) {
clearTimeout(game.view.music.timeoutID);
});
}
const targetElems = [game.ui.root.querySelector('.ui-page.' + target + '')];
- if(game.ui.root.querySelector('.ui-page.gameplay').style.opacity != '1') {
+ if(game.ui.root.querySelector('.ui-page.gameplay').style.opacity != '1' && target == 'title') {
targetElems.push(game.ui.root.querySelector('.ui-page.gameplay'));
}
for(let targetElem of targetElems) {
game.ui.root.querySelectorAll('button.goto').forEach((btn) => {
btn.addEventListener('click', (e) => {
- if(!game.view.music) {
+ if(game.view && !game.view.music) {
game.view.audioListener = new THREE.AudioListener();
game.view.camera.add(game.view.audioListener);
game.view.music = new THREE.Audio(game.view.audioListener);
return;
}
if(game.ui.currentPage == 'title' && e.key.match(/[a-z]/)) {
- game.cheatBuffer = (game.cheatBuffer + e.key).slice(-25);
+ game.ui.cheatBuffer = (game.ui.cheatBuffer + e.key).slice(-25);
for(let len = 10; len <= 25; len++) {
- if(game.cheatBuffer.length < len) {
+ if(game.ui.cheatBuffer.length < len) {
break;
}
- let unlock = unlockWithKey(game, game.cheatBuffer.slice(-len));
+ let unlock = unlockWithKey(game, game.ui.cheatBuffer.slice(-len));
if(unlock && unlock['type'] == 'feather' && !game.settings['unlocks'].includes(unlock['name'])) {
playRandomSound(game);
unlockFeather(game, unlock['name'], unlock['url']);
if(window.location.hostname == 'fietkau.media' && window.location.pathname == '/up_in_the_air') {
game.ui.root.querySelector('.ui-page.title .footer span:last-child').remove();
}
+ let controlsInterstitial = false;
+ if(!game.settings['controls']) {
+ controlsInterstitial = true;
+ let control;
+ if(matchMedia('(hover: hover)').matches) {
+ control = 'mouse';
+ } else {
+ control = 'touchpad';
+ }
+ game.ui.root.querySelector('.controls input[value="' + control + '"]').checked = true;
+ applySettings(game);
+ loadSettings(game);
+ game.ui.root.querySelectorAll('.ui-page.controls .' + ((control == 'mouse') ? 'touchpad' : 'mouse')).forEach(elem => elem.remove());
+ }
if(!game.assets.audiothemes.includes(game.settings['audio']['theme'])) {
game.settings['audio']['theme'] = game.assets.audiothemes[0];
}
snippet.childNodes[1].textContent = ' ' + audioTheme[0].toUpperCase() + audioTheme.slice(1);
container.appendChild(snippet);
}
- moveToPage(game, 'title');
- initializeGame(window['game'], game.ui.root.querySelector('canvas'));
+ if(controlsInterstitial) {
+ moveToPage(game, 'controls');
+ } else {
+ moveToPage(game, 'title');
+ }
}, (err) => {
console.error(err);
});