import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
-function playRandomSound(game) {
+
+window['startUpInTheAirGame'] = (game) => {
+
+game['fn'] = {};
+
+game['fn'].playRandomSound = () => {
if(!game.view || !game.view.audioListener) {
return;
}
}
}
-function easeInOut(val) {
+game['fn'].easeInOut = (val) => {
return -0.5 * Math.cos(val * Math.PI) + 0.5;
}
-function lerp(start, end, progress) {
+game['fn'].lerp = (start, end, progress) => {
return (1.0 - progress) * start + progress * end;
}
-function loadAllAssets(game, renderProgressCallback) {
+game['fn'].loadAllAssets = (renderProgressCallback) => {
game.assets = {};
game.assets.words = {
'thanks': ['thank you', 'thanks'],
} else if(unlockable == 'ghost') {
todoList['textures/feather-ghost.png'] = 1023;
} else {
- let unlock = unlockWithKey(game, 'NIbp2kW5' + unlockable + 'e2ZDFl5Y');
+ let unlock = game['fn'].unlockWithKey('NIbp2kW5' + unlockable + 'e2ZDFl5Y');
if(unlock && unlock['type'] == 'feather') {
todoList['data:textures/feather-' + unlock['name']] = unlock['url'];
}
});
}
-function applyForceToFeather(game, vector) {
+game['fn'].applyForceToFeather = (vector) => {
game.objects.feather.speed.add(vector);
}
-function initializeGame(game, canvas) {
-
+game['fn'].initializeGame = (canvas) => {
game.timeProgress = 0;
game.timeTotal = 258;
game.courseRadius = 50;
game.view.camera.position.set(-5, -game.courseRadius, game.view.camera.position.z);
game.view.scene = scene;
- createMeshes(game);
- createFeather(game);
- reset(game);
+ game['fn'].createMeshes();
+ game['fn'].createFeather();
+ game['fn'].reset();
function pinwheelPositionUpdate(game, viewportX, viewportY) {
const vFOV = THREE.MathUtils.degToRad(game.view.camera.fov);
});
document.body.addEventListener('touchend', e => {
if(e.target.closest('.ui-container') && game.settings['controls'] != 'mouse' && ['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
- moveToPage(game, 'pause', true);
+ game['fn'].moveToPage('pause', true);
e.preventDefault();
}
if(game.settings['controls'] == 'touchpad' || game.settings['controls'] == 'thumbstick') {
game.var.endingExitTrajectory = new THREE.Vector3();
game.var.endingEntryRotation = new THREE.Vector3();
game.var.endingExitRotation = new THREE.Vector3();
- game.view.renderer.setAnimationLoop(() => { animate(game, scene); });
+ game.view.renderer.setAnimationLoop(() => { game['fn'].animate(scene); });
}
-function prepareWordMesh(game, word) {
+game['fn'].prepareWordMesh = (word) => {
while(word.children.length > 0) {
word.remove(word.children[0]);
}
}
}
-function reset(game) {
+game['fn'].reset = () => {
game.controls = {};
game.controls.positionX = 0;
game.controls.positionY = 0;
}
let wordIndex = Math.floor(Math.random() * wordList.length);
word.text = wordList.splice(wordIndex, 1)[0];
- prepareWordMesh(game, word);
+ game['fn'].prepareWordMesh(word);
word.randomAnimOffset = Math.random();
const vFOV = THREE.MathUtils.degToRad(game.view.camera.fov);
let attempts = 0;
}
}
-function animate(game, scene) {
+game['fn'].animate = ( scene) => {
if(!('startTime' in game)) {
game.startTime = game.view.clock.getElapsedTime();
}
});
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));
+ cameraX += Math.max(0.0, 1 - game['fn'].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) => {
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)));
+ btn.style.left = 10 * game['fn'].easeInOut(Math.max(0.0, Math.min(1.0, 0.3 * timeOffset + 2.5 - game.timeProgress))).toFixed(2) + 'em';
+ let opacity = game['fn'].easeInOut(Math.max(0.0, Math.min(1.0, -0.3 * timeOffset + game.timeProgress - 1.5)));
btn.style.opacity = opacity.toFixed(2);
if(opacity == 1.0) {
btn.disabled = false;
});
}
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);
+ game.ui.root.querySelector('.ui-page.title .footer').style.opacity = game['fn'].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 = game['fn'].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');
}
} else if(game.ui.currentPage == 'openingcutscene') {
if(game.ui.reachedEnd) {
- reset(game);
+ game['fn'].reset();
}
if(game.timeProgress < 0.1 && !game.view.windSound.isPlaying) {
game.view.windSound.stop();
if(game.timeProgress >= 3.0) {
let windStrength = Math.max(0.5 * (1 + Math.cos((game.timeProgress - 4.0) * Math.PI)), Math.min(2 * (game.timeProgress - 4.2), 1.2));
game.objects.feather.position.x = cameraX - 8.45 + 0.15 * windStrength + 13.45 * Math.min(1.0, Math.max(0.0, 1 - Math.pow(Math.max(0.0, 6.0 - game.timeProgress), 2)));
- game.objects.feather.position.y = -game.courseRadius - 6.4 + 6.4 * easeInOut(Math.min(1,Math.max(0, game.timeProgress - 5.0) / 2));
+ game.objects.feather.position.y = -game.courseRadius - 6.4 + 6.4 * game['fn'].easeInOut(Math.min(1,Math.max(0, game.timeProgress - 5.0) / 2));
game.objects.feather.position.y += (Math.cos((Math.max(5.0, Math.min(8.0, game.timeProgress)) - 5.0) * 2 * Math.PI / 3) - 1) * 2 * Math.sin(game.timeProgress * 2);
game.objects.feather.position.z = -9.9 + 9.9 * Math.min(1.0, Math.max(0.0, 1 - Math.pow(Math.max(0.0, 6.0 - game.timeProgress), 2)));
game.objects.feather.rotation.z = Math.PI / 2.1 - 0.2 * windStrength + 1.45 * Math.max(0.0, game.timeProgress - 5.0);
game.controls.positionX = 0;
game.controls.positionY = -3;
}
- game.objects.pinwheel.material[4].opacity = easeInOut(Math.max(0, (game.timeProgress - 7)));
+ game.objects.pinwheel.material[4].opacity = game['fn'].easeInOut(Math.max(0, (game.timeProgress - 7)));
if(game.timeProgress >= 8.0) {
game.ui.root.querySelectorAll('.ui-page.gameplay p').forEach((elem) => {
let opacity = Math.max(0.0, Math.min(1.0, 8.0 - game.timeProgress));
if(!game.view.muted) {
game.view.music.play();
}
- moveToPage(game, 'gameplay', true);
+ game['fn'].moveToPage('gameplay', true);
}
} else if(game.ui.currentPage == 'endingcutscene') {
cameraSwayFactor = cameraSwayFactor * game.timeProgress / 8;
cameraX = 5 - Math.pow(Math.max(0, 5 - game.timeProgress) / 5, 1.6) * 5;
- let trajectoryLerpValue = easeInOut(Math.min(6.0, game.timeProgress) / 6);
+ let trajectoryLerpValue = game['fn'].easeInOut(Math.min(6.0, game.timeProgress) / 6);
game.var.endingEntryTrajectory.addScaledVector(game.objects.feather.speed, delta);
- game.var.endingExitTrajectory.setX(11.2 * easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
- game.var.endingExitTrajectory.setY(-game.courseRadius - 7.6 * easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
- game.var.endingExitTrajectory.setZ(-8.9 * easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
- game.objects.feather.rotation.x = lerp(game.var.endingEntryRotation.x, game.var.endingExitRotation.x, trajectoryLerpValue);
- game.objects.feather.rotation.y = lerp(game.var.endingEntryRotation.y, game.var.endingExitRotation.y, trajectoryLerpValue);
- game.objects.feather.rotation.z = lerp(game.var.endingEntryRotation.z, game.var.endingExitRotation.z, trajectoryLerpValue);
+ game.var.endingExitTrajectory.setX(11.2 * game['fn'].easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
+ game.var.endingExitTrajectory.setY(-game.courseRadius - 7.6 * game['fn'].easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
+ game.var.endingExitTrajectory.setZ(-8.9 * game['fn'].easeInOut(Math.min(1, 1 - Math.max(0, 4 - game.timeProgress) / 4)));
+ game.objects.feather.rotation.x = game['fn'].lerp(game.var.endingEntryRotation.x, game.var.endingExitRotation.x, trajectoryLerpValue);
+ game.objects.feather.rotation.y = game['fn'].lerp(game.var.endingEntryRotation.y, game.var.endingExitRotation.y, trajectoryLerpValue);
+ game.objects.feather.rotation.z = game['fn'].lerp(game.var.endingEntryRotation.z, game.var.endingExitRotation.z, trajectoryLerpValue);
game.objects.feather.position.lerpVectors(game.var.endingEntryTrajectory, game.var.endingExitTrajectory, trajectoryLerpValue);
- game.objects.pinwheel.material[4].opacity = easeInOut(Math.max(0, (1 - game.timeProgress)));
+ game.objects.pinwheel.material[4].opacity = game['fn'].easeInOut(Math.max(0, (1 - game.timeProgress)));
if(!game.settings['highcontrast']) {
- let letterScale = lerp(0.3, 0.0, easeInOut(Math.max(0, Math.min(1, game.timeProgress - 6))));
+ let letterScale = game['fn'].lerp(0.3, 0.0, game['fn'].easeInOut(Math.max(0, Math.min(1, game.timeProgress - 6))));
for(let i = 0; i < game.objects.words.length; i++) {
let word = game.objects.words[i];
if(!word.collected) {
}
if(game.timeProgress >= 8) {
game.ui.root.querySelector('.ui-page.title').classList.add('end');
- moveToPage(game, 'outro', true);
+ game['fn'].moveToPage('outro', true);
}
} else if(game.ui.reachedEnd) {
cameraX = 5;
game.var.endingEntryRotation.y = game.objects.feather.rotation.y;
game.var.endingEntryRotation.z = game.objects.feather.rotation.z;
game.var.endingExitRotation.set(-0.2, 0, -0.2);
- moveToPage(game, 'endingcutscene', true);
+ game['fn'].moveToPage('endingcutscene', true);
}
if(game.settings['audio']['music'] > 0.0 && game.view.music && !game.view.music.isPlaying) {
if(!game.settings['highcontrast']) {
let sunsetValue = 2.0;
if(!game.ui.reachedEnd) {
- sunsetValue = sunsetValue * easeInOut(Math.min(1, Math.max(0, ((game.timeProgress / game.timeTotal) - 0.3) / 0.6)));
+ sunsetValue = sunsetValue * game['fn'].easeInOut(Math.min(1, Math.max(0, ((game.timeProgress / game.timeTotal) - 0.3) / 0.6)));
}
if(game.settings['graphics'] <= 2) {
for(let i = 0; i < 6; i++) {
game.var.featherBorderForce.setComponent(coord, 3 * Math.sign(game.var.featherLocalPos.getComponent(coord)) - game.var.featherLocalPos.getComponent(coord));
}
}
- applyForceToFeather(game, game.var.featherBorderForce);
+ game['fn'].applyForceToFeather(game.var.featherBorderForce);
const tiltedGravity = game.gravity.clone();
game.var.pinwheelDistance.subVectors(game.objects.feather.position, game.objects.pinwheel.position).setZ(0);
const pinwheelForce = 0.5 * Math.max(0, Math.pow(game.var.pinwheelDistance.length(), - 0.5) - 0.5);
- applyForceToFeather(game, game.var.pinwheelDistance.normalize().multiplyScalar(pinwheelForce));
+ game['fn'].applyForceToFeather(game.var.pinwheelDistance.normalize().multiplyScalar(pinwheelForce));
if(pinwheelForce < 0.2) {
if(game.objects.feather.swayDirection > 0 && game.objects.feather.speed.x > 1.5) {
game.objects.feather.swayDirection *= -1;
tiltedGravity.x += game.objects.feather.swayDirection;
}
if(game.objects.feather.speed.y > -1) {
- applyForceToFeather(game, tiltedGravity);
+ game['fn'].applyForceToFeather(tiltedGravity);
}
game.objects.feather.rotation.z = -0.1 * game.objects.feather.speed.x * game.settings['difficulty']['speed'] / 100;
game.objects.feather.position.addScaledVector(game.objects.feather.speed, delta * game.settings['difficulty']['speed'] / 100);
let collectedScale = 0.0;
if(!game.settings['highcontrast'] && game.settings['graphics'] <= 2) {
- collectedScale = lerp(0.6, 0.3, 1 - Math.pow(1 - game.objects.words.collectedCount / game.objects.words.length, 2));
+ collectedScale = game['fn'].lerp(0.6, 0.3, 1 - Math.pow(1 - game.objects.words.collectedCount / game.objects.words.length, 2));
} else if(!game.settings['highcontrast'] && game.settings['graphics'] == 3) {
collectedScale = 0.3;
}
if(!word.collected && new THREE.Vector3().subVectors(word.position, game.objects.feather.position).length() < collectingRadius) {
word.collected = game.view.clock.getElapsedTime();
game.objects.words.collectedCount += 1;
- playRandomSound(game);
+ game['fn'].playRandomSound();
}
if(word.parent != game.view.scene) {
// All that happens in here is the positional animation for the word, which
game.var.collectedPos.set(x, y, z);
}
if(game.var.notCollectedPos.length() > 0 && game.var.collectedPos.length() > 0) {
- let collectingProgress = easeInOut(Math.max(0.0, Math.min(1.0, (game.view.clock.getElapsedTime() - word.collected) / collectionAnimationDuration)));
+ let collectingProgress = game['fn'].easeInOut(Math.max(0.0, Math.min(1.0, (game.view.clock.getElapsedTime() - word.collected) / collectionAnimationDuration)));
letter.position.lerpVectors(game.var.notCollectedPos, game.var.collectedPos, collectingProgress);
- let scale = lerp(1.0, collectedScale, collectingProgress);
+ let scale = game['fn'].lerp(1.0, collectedScale, collectingProgress);
letter.scale.set(scale, scale, scale);
} else if(game.var.notCollectedPos.length() > 0) {
letter.position.set(game.var.notCollectedPos.x, game.var.notCollectedPos.y, game.var.notCollectedPos.z);
game.view.renderer.render(scene, game.view.camera);
}
-function loadSettings(game) {
+game['fn'].loadSettings = () => {
let settings = {
'controls': null, // set during first time launch depending on device
'virtualinputleft': false,
if(unlockedFeather == 'golden' || unlockedFeather == 'ghost') {
img.src = 'textures/feather-' + unlockedFeather + '.png';
} else {
- let unlock = unlockWithKey(game, 'NIbp2kW5' + unlockedFeather + 'e2ZDFl5Y');
+ let unlock = game['fn'].unlockWithKey('NIbp2kW5' + unlockedFeather + 'e2ZDFl5Y');
if(unlock && unlock['type'] == 'feather') {
img.src = unlock['url'];
} else {
game.settings = settings;
}
-function applySettings(game) {
+game['fn'].applySettings = () => {
const ui = game.ui.root.querySelector('.ui-page.options');
if(ui.querySelector('input[name="upInTheAirGame-controls"]:checked')) {
game.settings['controls'] = ui.querySelector('input[name="upInTheAirGame-controls"]:checked').value;
}
}
-function createFeather(game) {
+game['fn'].createFeather = () => {
let position, rotation;
if(game.objects.feather) {
position = game.objects.feather.position;
game.objects.feather.twistSpeed = 0.1;
}
-function createMeshes(game) {
+game['fn'].createMeshes = () => {
if(game.objects.clouds && game.objects.clouds.parent == game.view.scene) {
game.view.scene.remove(game.objects.clouds);
if(game.objects.clouds.children.length > 0) {
}
if(game.objects.words) {
for(let word of game.objects.words) {
- prepareWordMesh(game, word);
+ game['fn'].prepareWordMesh(word);
}
}
game.view.scene.add(game.objects.backdrop);
}
-function unlockFeather(game, feather, url) {
+game['fn'].unlockFeather = (feather, url) => {
if(game.settings['unlocks'].includes(feather)) {
return false;
}
radio.name = 'upInTheAirGame-feather';
radio.value = feather;
radio.addEventListener('change', () => {
- applySettings(game);
- createFeather(game);
+ game['fn'].applySettings();
+ game['fn'].createFeather();
});
let img = document.createElement('img');
img.src = url;
label.appendChild(radio);
label.appendChild(img);
game.ui.root.querySelector('.ui-page.options .feather input[value="' + insertAfterFeather + '"]').parentNode.after(label);
- applySettings(game);
+ game['fn'].applySettings();
let ui = game.ui.root.querySelector('.ui-page.unlock');
let img2 = ui.querySelector('img');
img2.src = img.src;
return true;
}
-function moveToPage(game, target, skipFade = false) {
+game['fn'].moveToPage = (target, skipFade = false) => {
let fadeDuration = 250;
if(skipFade) {
fadeDuration = 0;
game.ui.cheatBuffer = '';
}
if(target == 'title' && (!game.ui.currentPage || ['loading', 'controls'].includes(game.ui.currentPage))) {
- initializeGame(game, game.ui.root.querySelector('canvas'));
+ game['fn'].initializeGame(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());
finalParagraph.style.display = 'none';
let neededUnlocking = false;
if(game.objects.words.collectedCount == 100) {
- neededUnlocking = unlockFeather(game, 'golden');
+ neededUnlocking = game['fn'].unlockFeather('golden');
} else {
- neededUnlocking = unlockFeather(game, 'ghost');
+ neededUnlocking = game['fn'].unlockFeather('ghost');
}
if(neededUnlocking) {
returnButton.innerText = 'Continue';
}
}
-function unlockWithKey(game, input) {
+game['fn'].unlockWithKey = (input) => {
input = 'aBYPmb2xCwF2ilfD'+ input + 'PNHFwI2zKZejUv6c';
let hash = (input) => {
// Adapted with appreciation from bryc:
data = new TextDecoder().decode(data);
let result = JSON.parse(data);
if(result['type'] == 'redirect') {
- return unlockWithKey(game, result['target']);
+ return game['fn'].unlockWithKey(result['target']);
} else {
return result;
}
return null;
};
-window['game'] = {
- state: 'loadingAssets',
- ui: {
+game['fn'].start = () => {
+ game.ui = {
root: document.querySelector('.upInTheAirGame .ui-container'),
gamepads: [],
- },
- settings: {},
+ };
+ game.settings = {};
// If you're looking at the source code and the following seems scary, don't worry, it's just a few
// unlockable feather textures. I liked easter eggs and cheat codes when I was young, and I didn't
// want these to be trivially bypassable for people who can read the code.
- unlockables: [
+ game.unlockables = [
{
'accessKey': '5b32eb7ad08488f4',
'payload': 'k4JPu3sWfEhcgleieVGghixKSI10qdRRC5tAl39Tzy1U7Rx9EEEYbLx9wCcxAf7wC8r9mJZCOj8bNa7grMbUmTeCeWWPAg==',
'accessKey': 'd8e8dd84f4b0c103',
'payload': 'EGKsJYSjVVaxCBWPRUGjWuLMl3k7fB/7uKYp8wz28r/5XTaOJF7LnbPMpBwysAR8IR/whArG',
},
- ],
-};
+ ];
-loadSettings(window['game']);
-applySettings(window['game']);
-
-if(game.ui.root.querySelectorAll('.ui-page.credits .area h3').length > 3) {
- // If the credits have more than three third-level headers, that means we are
- // in the freeware version and can make the CSS adjustments it needs.
- let css = document.styleSheets[0];
- css.insertRule('.upInTheAirGame .ui-page.credits .person { position: relative; height: 4em; padding-left: calc(4em + 1ex); display: flex; flex-direction: column; justify-content: center; }');
- css.insertRule('.upInTheAirGame .ui-page.credits .person::before { content: " "; position: absolute; left: 0; box-sizing: border-box; width: 4em; height: 4em; background-size: contain; border-radius: .6em; border: .1em solid #d53c59; }');
- game.ui.root.querySelectorAll('.ui-page.credits .area .person').forEach((person) => {
- let personName = Array.from(person.classList).filter(c => c != 'person')[0];
- let imageFormat = (personName == 'nina') ? 'png' : 'jpg';
- css.insertRule('.upInTheAirGame .ui-page.credits .person.' + personName + '::before { background-image: url("textures/person-' + personName + '.' + imageFormat + '"); }');
+ game['fn'].loadSettings();
+ game['fn'].applySettings();
+
+ if(game.ui.root.querySelectorAll('.ui-page.credits .area h3').length > 3) {
+ // If the credits have more than three third-level headers, that means we are
+ // in the freeware version and can make the CSS adjustments it needs.
+ let css = document.styleSheets[0];
+ css.insertRule('.upInTheAirGame .ui-page.credits .person { position: relative; height: 4em; padding-left: calc(4em + 1ex); display: flex; flex-direction: column; justify-content: center; }');
+ css.insertRule('.upInTheAirGame .ui-page.credits .person::before { content: " "; position: absolute; left: 0; box-sizing: border-box; width: 4em; height: 4em; background-size: contain; border-radius: .6em; border: .1em solid #d53c59; }');
+ game.ui.root.querySelectorAll('.ui-page.credits .area .person').forEach((person) => {
+ let personName = Array.from(person.classList).filter(c => c != 'person')[0];
+ let imageFormat = (personName == 'nina') ? 'png' : 'jpg';
+ css.insertRule('.upInTheAirGame .ui-page.credits .person.' + personName + '::before { background-image: url("textures/person-' + personName + '.' + imageFormat + '"); }');
+ });
+ }
+
+ game.ui.root.querySelectorAll('button.goto').forEach((btn) => {
+ btn.addEventListener('click', (e) => {
+ 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);
+ game.view.music.setBuffer(game.assets['audio']['music-' + game.settings['audio']['theme']]);
+ game.view.music.setVolume(game.settings['audio']['music']);
+ game.view.windSound = new THREE.Audio(game.view.audioListener);
+ game.view.windSound.setBuffer(game.assets['audio']['wind']);
+ game.view.windSound.setVolume(game.settings['audio']['sounds']);
+ game.view.windSound.setLoop(false);
+ }
+ let btn = e.target.closest('button');
+ let target = Array.from(btn.classList).filter(c => c != 'goto')[0];
+ if(target == 'previous') {
+ target = game.ui.previousPage;
+ }
+ game['fn'].moveToPage(target);
+ });
});
-}
-game.ui.root.querySelectorAll('button.goto').forEach((btn) => {
- btn.addEventListener('click', (e) => {
- 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);
- game.view.music.setBuffer(game.assets['audio']['music-' + game.settings['audio']['theme']]);
- game.view.music.setVolume(game.settings['audio']['music']);
- game.view.windSound = new THREE.Audio(game.view.audioListener);
- game.view.windSound.setBuffer(game.assets['audio']['wind']);
- game.view.windSound.setVolume(game.settings['audio']['sounds']);
- game.view.windSound.setLoop(false);
- }
- let btn = e.target.closest('button');
- let target = Array.from(btn.classList).filter(c => c != 'goto')[0];
- if(target == 'previous') {
- target = game.ui.previousPage;
+ game.ui.root.querySelectorAll('.options .controls input, .options .graphics input, .options .feather input, .options .accessibility input, .options .accessibility select').forEach((elem) => {
+ elem.addEventListener('change', () => {
+ game['fn'].applySettings();
+ if(elem.name == 'upInTheAirGame-controls') {
+ game.ui.root.querySelector('.controls .leftside').style.display = (['touchpad', 'thumbstick'].includes(game.settings['controls'])) ? 'block' : 'none';
+ game.ui.root.querySelectorAll('.options .controls p span:not(.' + game.settings['controls'] + ')').forEach(span => span.style.display = 'none');
+ game.ui.root.querySelector('.options .controls span.' + game.settings['controls']).style.display = 'block';
+ } else if(elem.value == 'highcontrast' || elem.name == 'upInTheAirGame-graphics') {
+ game['fn'].createMeshes();
+ } else if(elem.name == 'upInTheAirGame-feather') {
+ game['fn'].createFeather();
+ }
+ });
+ });
+
+ 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();
}
- moveToPage(game, target);
});
-});
-game.ui.root.querySelectorAll('.options .controls input, .options .graphics input, .options .feather input, .options .accessibility input, .options .accessibility select').forEach((elem) => {
- elem.addEventListener('change', () => {
- applySettings(game);
- if(elem.name == 'upInTheAirGame-controls') {
- game.ui.root.querySelector('.controls .leftside').style.display = (['touchpad', 'thumbstick'].includes(game.settings['controls'])) ? 'block' : 'none';
- game.ui.root.querySelectorAll('.options .controls p span:not(.' + game.settings['controls'] + ')').forEach(span => span.style.display = 'none');
- game.ui.root.querySelector('.options .controls span.' + game.settings['controls']).style.display = 'block';
- } else if(elem.value == 'highcontrast' || elem.name == 'upInTheAirGame-graphics') {
- createMeshes(game);
- } else if(elem.name == 'upInTheAirGame-feather') {
- createFeather(game);
+
+ 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];
+ game.ui.root.querySelector('.ui-page.options .audio input[type=range].' + audioCategory).value = e.target.value;
+ game['fn'].applySettings();
+ });
+ });
+
+ game.ui.root.querySelectorAll('.options .audio button').forEach((btn) => {
+ btn.addEventListener('click', (e) => {
+ if(e.target.classList.contains('music')) {
+ if(game.view.music.isPlaying) {
+ game.view.music.stop();
+ if(game.view.music.timeoutID) {
+ clearTimeout(game.view.music.timeoutID);
+ delete game.view.music.timeoutID;
+ }
+ } else {
+ game.view.music.offset = 36;
+ if(!game.view.muted) {
+ game.view.music.play();
+ }
+ game.view.music.timeoutID = setTimeout(() => {
+ game.view.music.stop();
+ }, 6000);
+ }
+ } else if(e.target.classList.contains('sounds')) {
+ game['fn'].playRandomSound();
+ }
+ });
+ });
+
+ game.ui.root.querySelectorAll('.options .keyboard label button').forEach((btn) => {
+ btn.addEventListener('click', () => {
+ if(game.ui.root.querySelector('.ui-page.keyboard-modal')) {
+ return;
+ }
+ const keyboardModal = document.createElement('div');
+ keyboardModal.classList.add('ui-page', 'keyboard-modal');
+ const instruction = document.createElement('span');
+ const direction = btn.classList[0];
+ keyboardModal.classList.add(direction);
+ instruction.innerText = 'Please press the key for “' + direction[0].toUpperCase() + direction.slice(1) + '”';
+ keyboardModal.appendChild(instruction);
+ game.ui.root.appendChild(keyboardModal);
+ });
+ });
+
+ game.ui.root.querySelector('.options .keyboard button[value="reset"]').addEventListener('click', (e) => {
+ const container = e.target.parentNode;
+ container.querySelector('button.up').value = 'ArrowUp|w';
+ container.querySelector('button.right').value = 'ArrowRight|d';
+ container.querySelector('button.down').value = 'ArrowDown|s';
+ container.querySelector('button.left').value = 'ArrowLeft|a';
+ game['fn'].applySettings();
+ game['fn'].loadSettings();
+ });
+
+ 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.root.style.fontSize = (game.ui.root.clientWidth / 48) + 'px';
+ window.addEventListener('resize', () => {
+ game.ui.root.style.fontSize = (game.ui.root.clientWidth / 48) + 'px';
+ });
+
+ window.addEventListener('scroll', () => {
+ if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
+ return;
+ }
+ let bbox = game.ui.root.querySelector('canvas').getBoundingClientRect();
+ if(bbox.bottom < -100 || bbox.top - bbox.height > 100 || bbox.left + bbox.width < -100 || bbox.left - window.innerWidth > 100) {
+ game['fn'].moveToPage('pause', true);
}
});
-});
-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];
- game.ui.root.querySelector('.ui-page.options .audio input[type=range].' + audioCategory).value = e.target.value;
- applySettings(game);
+ window.addEventListener('blur', () => {
+ if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
+ game['fn'].moveToPage('pause', true);
+ }
});
-});
-game.ui.root.querySelectorAll('.options .audio button').forEach((btn) => {
- btn.addEventListener('click', (e) => {
- if(e.target.classList.contains('music')) {
- if(game.view.music.isPlaying) {
- game.view.music.stop();
- if(game.view.music.timeoutID) {
- clearTimeout(game.view.music.timeoutID);
- delete game.view.music.timeoutID;
+
+ document.addEventListener('keydown', (e) => {
+ const keyboardModal = game.ui.root.querySelector('.keyboard-modal');
+ if(keyboardModal) {
+ const direction = [...keyboardModal.classList].filter(c => c != 'ui-page' && c != 'keyboard-modal')[0];
+ if(e.key != 'Escape') {
+ game.ui.root.querySelector('.options .keyboard label button.' + direction).value = e.key;
+ game['fn'].applySettings();
+ game['fn'].loadSettings();
+ }
+ keyboardModal.remove();
+ e.preventDefault();
+ e.stopPropagation();
+ return;
+ }
+ if(game.ui.currentPage == 'title' && e.key.match(/[a-z]/)) {
+ game.ui.cheatBuffer = (game.ui.cheatBuffer + e.key).slice(-25);
+ for(let len = 10; len <= 25; len++) {
+ if(game.ui.cheatBuffer.length < len) {
+ break;
}
- } else {
- game.view.music.offset = 36;
- if(!game.view.muted) {
- game.view.music.play();
+ let unlock = game['fn'].unlockWithKey(game.ui.cheatBuffer.slice(-len));
+ if(unlock && unlock['type'] == 'feather' && !game.settings['unlocks'].includes(unlock['name'])) {
+ game['fn'].playRandomSound();
+ game['fn'].unlockFeather(unlock['name'], unlock['url']);
+ game['fn'].moveToPage('unlock');
}
- game.view.music.timeoutID = setTimeout(() => {
- game.view.music.stop();
- }, 6000);
}
- } else if(e.target.classList.contains('sounds')) {
- playRandomSound(game);
+ return;
+ }
+ if(e.key == 'Escape') {
+ if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
+ game['fn'].moveToPage('pause', true);
+ } else if(game.ui.currentPage == 'pause') {
+ game['fn'].moveToPage(game.ui.previousPage, true);
+ }
}
});
-});
-game.ui.root.querySelectorAll('.options .keyboard label button').forEach((btn) => {
- btn.addEventListener('click', () => {
- if(game.ui.root.querySelector('.ui-page.keyboard-modal')) {
- return;
+
+ window.addEventListener('gamepadconnected', (e) => {
+ game.ui.gamepads.push(e.gamepad);
+ });
+ window.addEventListener('gamepaddisconnected', (e) => {
+ if(game.ui.gamepads.includes(e.gamepad)) {
+ game.ui.gamepads.splice(game.ui.gamepads.indexOf(e.gamepad), 1);
}
- const keyboardModal = document.createElement('div');
- keyboardModal.classList.add('ui-page', 'keyboard-modal');
- const instruction = document.createElement('span');
- const direction = btn.classList[0];
- keyboardModal.classList.add(direction);
- instruction.innerText = 'Please press the key for “' + direction[0].toUpperCase() + direction.slice(1) + '”';
- keyboardModal.appendChild(instruction);
- game.ui.root.appendChild(keyboardModal);
});
-});
-game.ui.root.querySelector('.options .keyboard button[value="reset"]').addEventListener('click', (e) => {
- const container = e.target.parentNode;
- container.querySelector('button.up').value = 'ArrowUp|w';
- container.querySelector('button.right').value = 'ArrowRight|d';
- container.querySelector('button.down').value = 'ArrowDown|s';
- container.querySelector('button.left').value = 'ArrowLeft|a';
- applySettings(game);
- loadSettings(game);
-});
-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.root.querySelector('.ui-page.pause button.title').addEventListener('click', () => {
+ game['fn'].reset();
});
-});
-game.ui.root.style.fontSize = (game.ui.root.clientWidth / 48) + 'px';
-window.addEventListener('resize', () => {
- game.ui.root.style.fontSize = (game.ui.root.clientWidth / 48) + 'px';
-});
-window.addEventListener('scroll', () => {
- if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
- return;
- }
- let bbox = game.ui.root.querySelector('canvas').getBoundingClientRect();
- if(bbox.bottom < -100 || bbox.top - bbox.height > 100 || bbox.left + bbox.width < -100 || bbox.left - window.innerWidth > 100) {
- moveToPage(game, 'pause', true);
- }
-});
-window.addEventListener('blur', () => {
- if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
- moveToPage(game, 'pause', true);
- }
-});
-document.addEventListener('keydown', (e) => {
- const keyboardModal = game.ui.root.querySelector('.keyboard-modal');
- if(keyboardModal) {
- const direction = [...keyboardModal.classList].filter(c => c != 'ui-page' && c != 'keyboard-modal')[0];
- if(e.key != 'Escape') {
- game.ui.root.querySelector('.options .keyboard label button.' + direction).value = e.key;
- applySettings(game);
- loadSettings(game);
- }
- keyboardModal.remove();
- e.preventDefault();
- e.stopPropagation();
- return;
- }
- if(game.ui.currentPage == 'title' && e.key.match(/[a-z]/)) {
- game.ui.cheatBuffer = (game.ui.cheatBuffer + e.key).slice(-25);
- for(let len = 10; len <= 25; len++) {
- if(game.ui.cheatBuffer.length < len) {
- break;
+
+ game['fn'].loadAllAssets((progress) => {
+ let percentage = Math.floor(100 * progress);
+ game.ui.root.querySelector('.ui-page.loading progress').value = percentage;
+ game.ui.root.querySelector('.ui-page.loading span').innerText = percentage;
+ }).then(() => {
+ 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';
}
- 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']);
- moveToPage(game, 'unlock');
+ game.ui.root.querySelector('.controls input[value="' + control + '"]').checked = true;
+ game['fn'].applySettings();
+ game['fn'].loadSettings();
+ 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];
+ }
+ if(game.assets.audiothemes.length == 1) {
+ game.ui.root.querySelector('.ui-page.options .audiotheme').style.display = 'none';
+ }
+ let container = game.ui.root.querySelector('.ui-page.options .audiotheme');
+ for(let audioTheme of game.assets.audiothemes) {
+ let snippet = container.children[0].content.cloneNode(true).children[0];
+ snippet.children[0].value = audioTheme;
+ if(audioTheme == game.settings['audio']['theme']) {
+ snippet.children[0].checked = true;
}
+ snippet.children[0].addEventListener('change', () => {
+ game['fn'].applySettings();
+ game.view.music.setBuffer(game.assets['audio']['music-' + game.settings['audio']['theme']]);
+ });
+ snippet.childNodes[1].textContent = ' ' + audioTheme[0].toUpperCase() + audioTheme.slice(1);
+ container.appendChild(snippet);
}
- return;
- }
- if(e.key == 'Escape') {
- if(['gameplay', 'openingcutscene', 'endingcutscene'].includes(game.ui.currentPage)) {
- moveToPage(game, 'pause', true);
- } else if(game.ui.currentPage == 'pause') {
- moveToPage(game, game.ui.previousPage, true);
- }
- }
-});
-window.addEventListener('gamepadconnected', (e) => {
- game.ui.gamepads.push(e.gamepad);
-});
-window.addEventListener('gamepaddisconnected', (e) => {
- if(game.ui.gamepads.includes(e.gamepad)) {
- game.ui.gamepads.splice(game.ui.gamepads.indexOf(e.gamepad), 1);
- }
-});
-game.ui.root.querySelector('.ui-page.pause button.title').addEventListener('click', () => {
- reset(game);
-});
-
-loadAllAssets(window['game'], (progress) => {
- let percentage = Math.floor(100 * progress);
- game.ui.root.querySelector('.ui-page.loading progress').value = percentage;
- game.ui.root.querySelector('.ui-page.loading span').innerText = percentage;
-}).then(() => {
- 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';
+ if(controlsInterstitial) {
+ game['fn'].moveToPage('controls');
} else {
- control = 'touchpad';
+ game['fn'].moveToPage('title');
}
- 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];
- }
- if(game.assets.audiothemes.length == 1) {
- game.ui.root.querySelector('.ui-page.options .audiotheme').style.display = 'none';
- }
- let container = game.ui.root.querySelector('.ui-page.options .audiotheme');
- for(let audioTheme of game.assets.audiothemes) {
- let snippet = container.children[0].content.cloneNode(true).children[0];
- snippet.children[0].value = audioTheme;
- if(audioTheme == game.settings['audio']['theme']) {
- snippet.children[0].checked = true;
- }
- snippet.children[0].addEventListener('change', () => {
- applySettings(game);
- game.view.music.setBuffer(game.assets['audio']['music-' + game.settings['audio']['theme']]);
- });
- snippet.childNodes[1].textContent = ' ' + audioTheme[0].toUpperCase() + audioTheme.slice(1);
- container.appendChild(snippet);
- }
- if(controlsInterstitial) {
- moveToPage(game, 'controls');
- } else {
- moveToPage(game, 'title');
- }
-}, (err) => {
- console.error(err);
-});
+ }, (err) => {
+ console.error(err);
+ });
+};
+
+// Set up name mirrors for each function that should survive most minifiers and transpilers
+game['fn']['animate'] = game['fn'].animate;
+game['fn']['applyForceToFeather'] = game['fn'].applyForceToFeather;
+game['fn']['applySettings'] = game['fn'].applySettings;
+game['fn']['createFeather'] = game['fn'].createFeather;
+game['fn']['createMeshes'] = game['fn'].createMeshes;
+game['fn']['easeInOut'] = game['fn'].easeInOut;
+game['fn']['initializeGame'] = game['fn'].initializeGame;
+game['fn']['lerp'] = game['fn'].lerp;
+game['fn']['loadAllAssets'] = game['fn'].loadAllAssets;
+game['fn']['loadSettings'] = game['fn'].loadSettings;
+game['fn']['moveToPage'] = game['fn'].moveToPage;
+game['fn']['playRandomSound'] = game['fn'].playRandomSound;
+game['fn']['prepareWordMesh'] = game['fn'].prepareWordMesh;
+game['fn']['reset'] = game['fn'].reset;
+game['fn']['start'] = game['fn'].start;
+game['fn']['unlockFeather'] = game['fn'].unlockFeather;
+game['fn']['unlockWithKey'] = game['fn'].unlockWithKey;
+
+game['fn'].start();
+
+}
+