├── aseprite ├── Title.ase ├── Intro Screen.ase └── User Interface.ase ├── app-core ├── assets │ ├── dig.png │ ├── dirt.png │ ├── fire.png │ ├── hurt.png │ ├── idle.png │ ├── move.png │ ├── sky.png │ ├── ground.png │ ├── title.png │ ├── topbar.png │ ├── aadigits.ttf │ ├── controls.png │ ├── dig_left.png │ ├── dithering.png │ ├── game_over.png │ ├── idle_left.png │ ├── move_left.png │ ├── scorpion.png │ ├── snd_dig.wav │ ├── snd_good.wav │ ├── snd_hurt.wav │ ├── snd_loose.wav │ ├── snd_start.wav │ ├── snd_step.wav │ ├── treasure0.png │ ├── treasure1.png │ ├── treasure2.png │ ├── treasure3.png │ ├── treasure4.png │ ├── treasure5.png │ ├── treasure6.png │ ├── dialog_back.png │ ├── instructions.png │ ├── level_prefix.png │ └── points_suffix.png ├── Utilities.js ├── World.js ├── AssetLoader.js ├── index.js ├── TransitionHelper.js ├── Game.js ├── Grid.js ├── CanvasHelper.js ├── EntityHelper.js ├── UserInterface.js ├── Player.js └── libs │ └── m4.js ├── app-simulator ├── icon.png ├── index.html ├── buttons │ ├── hash.svg │ ├── Enter.svg │ ├── 4.svg │ ├── Call.svg │ ├── SoftLeft.svg │ ├── Backspace.svg │ ├── SoftRight.svg │ ├── 2.svg │ ├── 1.svg │ ├── 7.svg │ ├── asterisk.svg │ ├── 0.svg │ ├── 5.svg │ ├── ArrowUp.svg │ ├── 8.svg │ ├── ArrowLeft.svg │ ├── ArrowDown.svg │ ├── ArrowRight.svg │ ├── 3.svg │ ├── 9.svg │ └── 6.svg └── build │ ├── bundle.css │ ├── bundle.css.map │ └── bundle.js ├── app-package ├── icons │ ├── icon_56.png │ ├── icon_112.png │ └── icon_128.png ├── index.html ├── manifest.webapp └── ads-sdk.v3.min.js ├── package.json ├── LICENSE.txt ├── webpack.config.js ├── .gitignore └── README.md /aseprite/Title.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/aseprite/Title.ase -------------------------------------------------------------------------------- /app-core/assets/dig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/dig.png -------------------------------------------------------------------------------- /app-core/assets/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/dirt.png -------------------------------------------------------------------------------- /app-core/assets/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/fire.png -------------------------------------------------------------------------------- /app-core/assets/hurt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/hurt.png -------------------------------------------------------------------------------- /app-core/assets/idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/idle.png -------------------------------------------------------------------------------- /app-core/assets/move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/move.png -------------------------------------------------------------------------------- /app-core/assets/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/sky.png -------------------------------------------------------------------------------- /app-simulator/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-simulator/icon.png -------------------------------------------------------------------------------- /app-core/assets/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/ground.png -------------------------------------------------------------------------------- /app-core/assets/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/title.png -------------------------------------------------------------------------------- /app-core/assets/topbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/topbar.png -------------------------------------------------------------------------------- /aseprite/Intro Screen.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/aseprite/Intro Screen.ase -------------------------------------------------------------------------------- /app-core/assets/aadigits.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/aadigits.ttf -------------------------------------------------------------------------------- /app-core/assets/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/controls.png -------------------------------------------------------------------------------- /app-core/assets/dig_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/dig_left.png -------------------------------------------------------------------------------- /app-core/assets/dithering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/dithering.png -------------------------------------------------------------------------------- /app-core/assets/game_over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/game_over.png -------------------------------------------------------------------------------- /app-core/assets/idle_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/idle_left.png -------------------------------------------------------------------------------- /app-core/assets/move_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/move_left.png -------------------------------------------------------------------------------- /app-core/assets/scorpion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/scorpion.png -------------------------------------------------------------------------------- /app-core/assets/snd_dig.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_dig.wav -------------------------------------------------------------------------------- /app-core/assets/snd_good.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_good.wav -------------------------------------------------------------------------------- /app-core/assets/snd_hurt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_hurt.wav -------------------------------------------------------------------------------- /app-core/assets/snd_loose.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_loose.wav -------------------------------------------------------------------------------- /app-core/assets/snd_start.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_start.wav -------------------------------------------------------------------------------- /app-core/assets/snd_step.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/snd_step.wav -------------------------------------------------------------------------------- /app-core/assets/treasure0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure0.png -------------------------------------------------------------------------------- /app-core/assets/treasure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure1.png -------------------------------------------------------------------------------- /app-core/assets/treasure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure2.png -------------------------------------------------------------------------------- /app-core/assets/treasure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure3.png -------------------------------------------------------------------------------- /app-core/assets/treasure4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure4.png -------------------------------------------------------------------------------- /app-core/assets/treasure5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure5.png -------------------------------------------------------------------------------- /app-core/assets/treasure6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/treasure6.png -------------------------------------------------------------------------------- /app-package/icons/icon_56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-package/icons/icon_56.png -------------------------------------------------------------------------------- /aseprite/User Interface.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/aseprite/User Interface.ase -------------------------------------------------------------------------------- /app-core/assets/dialog_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/dialog_back.png -------------------------------------------------------------------------------- /app-package/icons/icon_112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-package/icons/icon_112.png -------------------------------------------------------------------------------- /app-package/icons/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-package/icons/icon_128.png -------------------------------------------------------------------------------- /app-core/assets/instructions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/instructions.png -------------------------------------------------------------------------------- /app-core/assets/level_prefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/level_prefix.png -------------------------------------------------------------------------------- /app-core/assets/points_suffix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fergarram/amateur-archaeology/HEAD/app-core/assets/points_suffix.png -------------------------------------------------------------------------------- /app-core/Utilities.js: -------------------------------------------------------------------------------- 1 | export function easeInOutQuint (t, b, c, d) { 2 | if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; 3 | return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 4 | } 5 | 6 | export function easeLinear (t, b, c, d) { 7 | return c * t / d + b; 8 | } -------------------------------------------------------------------------------- /app-simulator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Banana Simulator 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app-simulator/buttons/hash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app-simulator/build/bundle.css: -------------------------------------------------------------------------------- 1 | body{background-color:#424242;padding-top:1rem}main.svelte-1i3izob{display:flex;justify-content:center;align-self:center;width:100%;height:100%}#device-io.svelte-1i3izob{margin-top:91px;margin-left:50px}#phone.svelte-1i3izob{width:340px;height:1180px;background:url('../nokia.svg')}#screen.svelte-1i3izob{width:240px;height:320px;background:black} 2 | #keyboard.svelte-xd431t{width:240px;margin-top:63px;position:absolute} 3 | button.svelte-623b3k{opacity:0.0;position:absolute;background:none;outline:none;border:none;margin:0;padding:0}button.svelte-623b3k:hover{opacity:0.25;cursor:pointer}button.svelte-623b3k:active{opacity:0.5} 4 | 5 | /*# sourceMappingURL=bundle.css.map */ -------------------------------------------------------------------------------- /app-package/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | Amateur Archaeology 11 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app-simulator/buttons/Enter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enter 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-package/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "Treasure Hunter", 4 | "description": "Small game about finding treasures in the sand.", 5 | "type": "web", 6 | "launch_path": "/index.html", 7 | "theme_color": "#323232", 8 | "fullscreen": "true", 9 | "orientation": [ "portrait-primary" ], 10 | "categories": [ "games" ], 11 | "icons": { 12 | "56": "/icons/icon_56.png", 13 | "112": "/icons/icon_112.png", 14 | "128": "/icons/icon_128.png" 15 | }, 16 | "developer": { 17 | "name": "Fernando Garcia", 18 | "url": "https://fernando.works" 19 | }, 20 | "locales": { 21 | "en-US": { 22 | "name": "Treasure Hunter", 23 | "subtitle": "Get all the treasures!", 24 | "description": "Go on an adventure to find valuable treasures in the desert. But beware! There are dangers beneath the sand!" 25 | } 26 | }, 27 | "default_locale": "en-US" 28 | } -------------------------------------------------------------------------------- /app-simulator/buttons/Call.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Call 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/SoftLeft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SoftLeft 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/Backspace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Backspace 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/SoftRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SoftRight 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/asterisk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amateur-archaeology", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "build:prod": "webpack --env production", 7 | "build:sim": "webpack --env simulator", 8 | "build": "webpack --env development", 9 | "dev": "webpack-dev-server --env simulator" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/Fergarram/amateur-archaeology.git" 14 | }, 15 | "keywords": [], 16 | "author": "Fernando Garcia", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/Fergarram/amateur-archaeology/issues" 20 | }, 21 | "homepage": "https://github.com/Fergarram/amateur-archaeology#readme", 22 | "devDependencies": { 23 | "@babel/core": "^7.9.0", 24 | "@babel/preset-env": "^7.9.5", 25 | "babel-loader": "^8.1.0", 26 | "copy-webpack-plugin": "^5.1.1", 27 | "webpack": "^4.42.1", 28 | "webpack-cli": "^3.3.11", 29 | "webpack-dev-server": "^3.10.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app-simulator/buttons/0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/ArrowUp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowUp 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/ArrowLeft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowLeft 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/ArrowDown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowDown 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 Fernando Garcia 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app-simulator/buttons/ArrowRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowRight 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-simulator/buttons/6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app-core/World.js: -------------------------------------------------------------------------------- 1 | import { easeInOutQuint } from './Utilities.js' 2 | 3 | class World { 4 | 5 | constructor() { 6 | this.sky_x = 0; 7 | this.skyDistance = 240; 8 | this.ground_length = 568; 9 | this.ground_y = 192; 10 | this.x = 0; 11 | this.y = 0; 12 | this.timePassed = 0; // Milliseconds 13 | this.titleAlpha = 1; 14 | } 15 | 16 | updateCamera(py) { 17 | this.y = -(py - 160); 18 | } 19 | 20 | update(delta) { 21 | 22 | // Move world 23 | if (this.x > -this.ground_length) { 24 | this.timePassed += delta; 25 | this.x = -easeInOutQuint(this.timePassed / 1000, 0, this.ground_length, 10); 26 | } 27 | 28 | // Move sky (parallax) 29 | if (this.sky_x > -this.skyDistance) { 30 | this.timePassed += delta; 31 | this.sky_x = -easeInOutQuint(this.timePassed / 1000, 0, this.skyDistance, 10); 32 | } 33 | 34 | // Update title alpha 35 | if (this.titleAlpha > 0) { 36 | this.titleAlpha = 1 - easeInOutQuint(this.timePassed / 1000, 0, 1, 10); 37 | } 38 | } 39 | 40 | draw(CanvasHelper) { 41 | 42 | // Sky 43 | CanvasHelper.drawImage('sky', this.sky_x, this.y); 44 | CanvasHelper.drawImage('dithering', 0, this.y); 45 | 46 | // Title 47 | if (this.titleAlpha > 0) { 48 | CanvasHelper.drawImage('title', 4, this.y + 14, this.titleAlpha); 49 | } 50 | 51 | // Decorative tiles 52 | CanvasHelper.drawImage('ground', this.x, this.y + this.ground_y); 53 | CanvasHelper.drawImage('dirt', this.x + 800, this.y + this.ground_y); 54 | } 55 | } 56 | 57 | export default new World(); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const CopyPlugin = require('copy-webpack-plugin'); 4 | 5 | module.exports = function (_env) { 6 | const isDevelopment = _env === 'development'; 7 | const isSimulator = _env === 'simulator'; 8 | 9 | return { 10 | devtool: (isDevelopment || isSimulator) && 'eval-source-map', 11 | entry: './app-core/index.js', 12 | output: { 13 | filename: 'index.js', 14 | path: path.resolve(__dirname, isSimulator ? 'app-simulator' : 'app-package') 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.js$/, 20 | exclude: /(node_modules)/, 21 | use: { 22 | loader: "babel-loader", 23 | options: { 24 | presets: ["@babel/preset-env"] 25 | } 26 | } 27 | } 28 | ] 29 | }, 30 | devServer: { 31 | contentBase: path.join(__dirname, 'app-simulator'), 32 | compress: true, 33 | port: 5000 34 | }, 35 | plugins: [ 36 | new webpack.DefinePlugin({ 37 | 'process.env.NODE_ENV': JSON.stringify( 38 | (isDevelopment || isSimulator) ? 'development' : 'production' 39 | ) 40 | }), 41 | new CopyPlugin([ 42 | { 43 | from: './app-core/assets', 44 | to: 'assets' 45 | } 46 | ]) 47 | ] 48 | } 49 | }; -------------------------------------------------------------------------------- /app-core/AssetLoader.js: -------------------------------------------------------------------------------- 1 | import { Howler } from './libs/howler.js'; 2 | 3 | class AssetLoader { 4 | constructor() { 5 | this.images = {}; 6 | this.sounds = {}; 7 | this.imageFiles = { 8 | dig: 'assets/dig.png', 9 | idle: 'assets/idle.png', 10 | move: 'assets/move.png', 11 | hurt: 'assets/hurt.png', 12 | dirt: 'assets/dirt.png', 13 | fire: 'assets/fire.png', 14 | scorpion: 'assets/scorpion.png', 15 | sky: 'assets/sky.png', 16 | dithering: 'assets/dithering.png', 17 | ground: 'assets/ground.png', 18 | treasure0: 'assets/treasure0.png', 19 | treasure1: 'assets/treasure1.png', 20 | treasure2: 'assets/treasure2.png', 21 | treasure3: 'assets/treasure3.png', 22 | treasure4: 'assets/treasure4.png', 23 | treasure5: 'assets/treasure5.png', 24 | treasure6: 'assets/treasure6.png', 25 | title: 'assets/title.png', 26 | topbar: 'assets/topbar.png', 27 | dialog_back: 'assets/dialog_back.png', 28 | points_suffix: 'assets/points_suffix.png', 29 | level_prefix: 'assets/level_prefix.png', 30 | controls: 'assets/controls.png' 31 | }; 32 | this.soundFiles = { 33 | start: 'assets/snd_start.wav', 34 | dig: 'assets/snd_dig.wav', 35 | good: 'assets/snd_good.wav', 36 | hurt: 'assets/snd_hurt.wav', 37 | step: 'assets/snd_step.wav', 38 | loose: 'assets/snd_loose.wav' 39 | }; 40 | } 41 | 42 | load() { 43 | const digitFont = new FontFace('AADigits', 'url(assets/aadigits.ttf)'); 44 | 45 | digitFont.load().then((loadedFont) => { 46 | document.fonts.add(loadedFont); 47 | }); 48 | 49 | let imagesLoaded = new Promise((resolve, reject) => { 50 | document.addEventListener('allimagesloaded', () => resolve()); 51 | }); 52 | 53 | let soundsLoaded = new Promise((resolve, reject) => { 54 | document.addEventListener('allsoundsloaded', () => resolve()); 55 | }); 56 | 57 | this.loadImages(); 58 | this.loadSounds(); 59 | 60 | return Promise.all([imagesLoaded, soundsLoaded]); 61 | } 62 | 63 | playSound(name, rate = 1) { 64 | const id = this.sounds[name].play(); 65 | if (rate !== 1) { 66 | this.sounds[name].rate(rate, id); 67 | } 68 | } 69 | 70 | loadSounds() { 71 | const noSounds = Object.keys(this.soundFiles).length; 72 | let loadedSoundsCount = 0; 73 | 74 | for (const name in this.soundFiles) { 75 | this.sounds[name] = new Howl({ 76 | src: [this.soundFiles[name]], 77 | volume: 0.8, 78 | }); 79 | this.sounds[name].once('load', () => { 80 | loadedSoundsCount += 1; 81 | if (loadedSoundsCount === noSounds) { 82 | document.dispatchEvent(new Event('allsoundsloaded')); 83 | } 84 | }); 85 | } 86 | } 87 | 88 | loadImages() { 89 | const noImages = Object.keys(this.imageFiles).length; 90 | let loadedImageCount = 0; 91 | 92 | for (const image in this.imageFiles) { 93 | this.images[image] = new Image(); 94 | this.images[image].src = this.imageFiles[image]; 95 | this.images[image].onload = () => { 96 | loadedImageCount += 1; 97 | if (loadedImageCount === noImages) { 98 | document.dispatchEvent(new Event('allimagesloaded')); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | export default new AssetLoader(); -------------------------------------------------------------------------------- /app-core/index.js: -------------------------------------------------------------------------------- 1 | import UserInterface from './UserInterface.js'; 2 | import AssetLoader from './AssetLoader.js'; 3 | import CanvasHelper from './CanvasHelper.js'; 4 | import TransitionHelper from './TransitionHelper.js'; 5 | import Grid from './Grid.js'; 6 | import World from './World.js'; 7 | import Player from './Player.js'; 8 | import EntityHelper from './EntityHelper.js'; 9 | import Game from './Game.js'; 10 | 11 | class AppCore { 12 | 13 | init() { 14 | window.onload = () => { 15 | 16 | if (navigator.userAgent.includes('KAIOS')) { 17 | navigator.requestWakeLock('screen'); 18 | } 19 | 20 | UserInterface.init(); 21 | 22 | AssetLoader.load().then(() => { 23 | 24 | // Initialize canvas helper 25 | CanvasHelper.init(AssetLoader); 26 | TransitionHelper.init(CanvasHelper.gl); 27 | 28 | // Initialize keyboard event handler 29 | document.addEventListener('keydown', this.keyboardEventHandler); 30 | 31 | // Randomize the grid 32 | Grid.reset(); 33 | 34 | // Initialing world 35 | Grid.world = World; 36 | Grid.entities = EntityHelper; 37 | EntityHelper.world = World; 38 | EntityHelper.grid = Grid; 39 | EntityHelper.assets = AssetLoader; 40 | EntityHelper.player = Player; 41 | Player.assets = AssetLoader; 42 | Player.world = World; 43 | Player.game = Game; 44 | Player.grid = Grid; 45 | Player.entities = EntityHelper; 46 | Game.ui = UserInterface; 47 | Game.player = Player; 48 | Game.grid = Grid; 49 | Game.entities = EntityHelper; 50 | Game.assets = AssetLoader; 51 | Game.transition = TransitionHelper; 52 | 53 | // Preapare the ad 54 | UserInterface.prepareAd(); 55 | 56 | if (process.env.NODE_ENV === 'development') { 57 | window.Grid = Grid; 58 | window.World = World; 59 | window.Player = Player; 60 | window.AssetLoader = AssetLoader; 61 | window.CanvasHelper = CanvasHelper; 62 | window.EntityHelper = EntityHelper; 63 | window.Game = Game; 64 | window.UserInterface = UserInterface; 65 | window.TransitionHelper = TransitionHelper; 66 | } 67 | 68 | AssetLoader.playSound('start'); 69 | setTimeout(() => { 70 | UserInterface.updateScore(Game.score, Game.goal); 71 | UserInterface.show(); 72 | }, 5000); 73 | 74 | // Start the main loop 75 | CanvasHelper.loop(this.update, this.draw); 76 | }); 77 | }; 78 | } 79 | 80 | update(delta) { 81 | if (Game.freeze) { 82 | return; 83 | } 84 | World.update(delta); 85 | Player.update(delta); 86 | EntityHelper.update(delta); 87 | Game.update(delta); 88 | } 89 | 90 | draw() { 91 | World.draw(CanvasHelper); 92 | Grid.draw(CanvasHelper); 93 | EntityHelper.draw(CanvasHelper); 94 | Player.draw(CanvasHelper); 95 | } 96 | 97 | keyboardEventHandler(event) { 98 | event.preventDefault(); 99 | 100 | if (UserInterface.isActive && !Player.canMove && !Game.started) { 101 | Player.canMove = true; 102 | Game.started = true; 103 | UserInterface.hideDialog(); 104 | return; 105 | } 106 | 107 | if (!Game.freeze) { 108 | Player.onKeyDown(event.key); 109 | } 110 | 111 | if (event.key === 'Backspace') { 112 | if (confirm("Are you sure you want to quit the game?")) { 113 | window.close(); 114 | } 115 | } 116 | 117 | if (event.key === '1') { 118 | AssetLoader.turnVolumeDown(); 119 | } 120 | 121 | if (event.key === '3') { 122 | AssetLoader.turnVolumeUp(); 123 | } 124 | 125 | } 126 | } 127 | 128 | const App = new AppCore(); 129 | App.init(); -------------------------------------------------------------------------------- /app-core/TransitionHelper.js: -------------------------------------------------------------------------------- 1 | class TransitionHelper { 2 | 3 | init(gl) { 4 | this.screen = document.getElementById('screen'); 5 | this.canvas = document.createElement('canvas'); 6 | this.canvas.id = 'TransitionCanvas'; 7 | this.width = 240; 8 | this.height = 320; 9 | this.canvas.width = this.width; 10 | this.canvas.height = this.height; 11 | this.canvas.style.zIndex = 99999999; 12 | this.canvas.style.position = 'absolute'; 13 | this.screen.appendChild(this.canvas); 14 | this.ctx = this.canvas.getContext('2d'); 15 | this.ctx.imageSmoothingEnabled = false; 16 | this.ctx.mozImageSmoothingEnabled = false; 17 | this.ctx.webkitImageSmoothingEnabled = false; 18 | this.speed = 75; 19 | this.ctx.transform(1, 0, 0, -1, 0, this.canvas.height); 20 | this.gl = gl; 21 | } 22 | 23 | enter(callback) { 24 | let count = 0; 25 | this.sampleSize = 4; 26 | this.inLoop = setInterval(() => { 27 | if (this.sampleSize > 40) { 28 | clearInterval(this.inLoop); 29 | callback(); 30 | return; 31 | } 32 | count += 1; 33 | this.pixelate(this.sampleSize); 34 | this.darken(count); 35 | this.sampleSize += 4; 36 | }, this.speed); 37 | } 38 | 39 | leave(callback) { 40 | if (!callback) { 41 | this.ctx.clearRect(0, 0, this.width, this.height); 42 | return; 43 | } 44 | let count = 10; 45 | this.sampleSize = 40; 46 | this.outLoop = setInterval(() => { 47 | if (this.sampleSize == 0) { 48 | clearInterval(this.outLoop); 49 | this.ctx.clearRect(0, 0, this.width, this.height); 50 | setTimeout(callback, this.speed); 51 | return; 52 | } 53 | this.pixelate(this.sampleSize); 54 | this.darken(count); 55 | count -= 1; 56 | this.sampleSize -= 4; 57 | }, this.speed); 58 | } 59 | 60 | darken(count) { 61 | if (count == 7) { 62 | this.ctx.fillStyle = 'rgba(0,0,0,0.25)'; 63 | this.ctx.fillRect(0, 0, this.width, this.height); 64 | } 65 | 66 | if (count == 8) { 67 | this.ctx.fillStyle = 'rgba(0,0,0,0.50)'; 68 | this.ctx.fillRect(0, 0, this.width, this.height); 69 | } 70 | 71 | if (count == 9) { 72 | this.ctx.fillStyle = 'rgba(0,0,0,0.75)'; 73 | this.ctx.fillRect(0, 0, this.width, this.height); 74 | } 75 | 76 | if (count == 10) { 77 | this.ctx.fillStyle = 'rgba(0,0,0,1)'; 78 | this.ctx.fillRect(0, 0, this.width, this.height); 79 | } 80 | } 81 | 82 | pixelate(sampleSize) { 83 | const width = this.gl.drawingBufferWidth; 84 | const height = this.gl.drawingBufferHeight; 85 | const size = width * height * 4; 86 | const pixels = new Uint8Array(size); 87 | this.gl.readPixels(0, 0, width, height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels); 88 | 89 | for (let y = 0; y < height + sampleSize; y += sampleSize) { 90 | for (let x = 0; x < width + sampleSize; x += sampleSize) { 91 | const offset = (y * width + x) * 4; 92 | const color = pixels.slice(offset, offset + 4); 93 | this.ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; 94 | this.ctx.fillRect(x - sampleSize / 2, y - sampleSize / 2, sampleSize, sampleSize); 95 | } 96 | } 97 | } 98 | 99 | clamp(value, min, max){ 100 | return Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max)); 101 | } 102 | } 103 | 104 | export default new TransitionHelper(); -------------------------------------------------------------------------------- /app-simulator/build/bundle.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "bundle.css", 4 | "sources": [ 5 | "../../Simulator.svelte", 6 | "../../Keyboard.svelte", 7 | "../../KeyButton.svelte" 8 | ], 9 | "sourcesContent": [ 10 | "\n\n
\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\n\t\t
\n\t
\n
\n\n", 11 | "\n\n
\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n
\n\n", 12 | "\n\n\n" 13 | ], 14 | "names": [], 15 | "mappings": "AAcS,IAAI,AAAE,CAAC,AACd,gBAAgB,CAAE,OAAO,CACzB,WAAW,CAAE,IAAI,AAClB,CAAC,AACD,IAAI,eAAC,CAAC,AACL,OAAO,CAAE,IAAI,CACb,eAAe,CAAE,MAAM,CACvB,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,AACb,CAAC,AACD,UAAU,eAAC,CAAC,AACX,UAAU,CAAE,IAAI,CAChB,WAAW,CAAE,IAAI,AAClB,CAAC,AACD,MAAM,eAAC,CAAC,AACP,KAAK,CAAE,KAAK,CACZ,MAAM,CAAE,MAAM,CACd,UAAU,CAAE,IAAI,cAAc,CAAC,AAChC,CAAC,AACD,OAAO,eAAC,CAAC,AACR,KAAK,CAAE,KAAK,CACZ,MAAM,CAAE,KAAK,CACb,UAAU,CAAE,KAAK,AAClB,CAAC;ACTD,SAAS,cAAC,CAAC,AACV,KAAK,CAAE,KAAK,CACZ,UAAU,CAAE,IAAI,CAChB,QAAQ,CAAE,QAAQ,AACnB,CAAC;ACdD,MAAM,cAAC,CAAC,AACP,OAAO,CAAE,GAAG,CACZ,QAAQ,CAAE,QAAQ,CAClB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CACb,MAAM,CAAE,IAAI,CACZ,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,AACX,CAAC,AACD,oBAAM,MAAM,AAAC,CAAC,AACb,OAAO,CAAE,IAAI,CACb,MAAM,CAAE,OAAO,AAChB,CAAC,AACD,oBAAM,OAAO,AAAC,CAAC,AACd,OAAO,CAAE,GAAG,AACb,CAAC" 16 | } -------------------------------------------------------------------------------- /app-core/Game.js: -------------------------------------------------------------------------------- 1 | class Game { 2 | 3 | constructor() { 4 | this.ui = null; 5 | this.player = null; 6 | this.assets = null; 7 | this.grid = null; 8 | this.transition = null; 9 | this.entities = null; 10 | this.level = 1; 11 | this.maxTime = 60; // Seconds 12 | this.remainingTime = this.maxTime; 13 | this.goal = 500; 14 | this.score = 0; 15 | this.started = false; 16 | this.elapsedTime = 0; 17 | this.freeze = false; 18 | } 19 | 20 | addPoints(points) { 21 | this.score += points; 22 | this.ui.updateScore(this.score, this.goal); 23 | } 24 | 25 | addTime(time) { 26 | if (this.remainingTime + time >= this.maxTime) { 27 | this.remainingTime = this.maxTime; 28 | } else { 29 | this.remainingTime += time; 30 | } 31 | } 32 | 33 | substractTime(time) { 34 | if (this.remainingTime - time <= 0) { 35 | this.remainingTime = 0; 36 | } else { 37 | this.remainingTime -= time; 38 | } 39 | } 40 | 41 | endLevel() { 42 | this.transition.enter(() => { 43 | this.player.reset(); 44 | this.grid.reset(); 45 | this.entities.reset(); 46 | this.freeze = false; 47 | 48 | 49 | // Lost, go to game over screen. 50 | if (this.goal > this.score) { 51 | this.ui.showGameOver(); 52 | this.transition.leave(); 53 | return; 54 | 55 | // Won, setup new level. 56 | } else if (this.score >= this.goal) { 57 | const extraPoints = this.score - this.goal; 58 | const bonusRatio = extraPoints / this.goal; 59 | this.maxTime = Math.floor(this.maxTime * (1 + (bonusRatio / 2))); 60 | this.level += 1; 61 | const newGoal = Math.floor(this.goal * 1.5); // I guess level will do something here? 62 | this.goal = newGoal - (newGoal % 5); // Only multiples of 5. 63 | } 64 | 65 | // Reset anyways 66 | this.remainingTime = this.maxTime; 67 | this.score = 0; 68 | 69 | // Show UI 70 | this.ui.isActive = false; 71 | this.ui.updateScore(this.score, this.goal); 72 | this.ui.updateTime(this.remainingTime, this.maxTime); 73 | this.ui.updateLevel(this.level); 74 | 75 | // Show level again. 76 | this.transition.leave(() => { 77 | this.ui.show(); 78 | }); 79 | }); 80 | } 81 | 82 | update(delta) { 83 | if (this.started) { 84 | this.elapsedTime += delta; 85 | 86 | if (this.elapsedTime >= 1000) { 87 | this.remainingTime -= 1; 88 | this.elapsedTime = 0; 89 | 90 | if (this.remainingTime <= 0) { 91 | this.started = false; 92 | this.freeze = true; 93 | let ringer = null; 94 | if (this.goal < this.score) { 95 | ringer = setInterval(() => this.assets.playSound('good'), 180); 96 | } 97 | setTimeout(() => { 98 | if (ringer !== null) { 99 | clearInterval(ringer); 100 | this.assets.playSound('start'); 101 | } else { 102 | this.assets.playSound('loose'); 103 | } 104 | this.endLevel(); 105 | }, 1500); 106 | } 107 | 108 | this.ui.updateTime(this.remainingTime, this.maxTime); 109 | } 110 | } 111 | } 112 | } 113 | 114 | export default new Game(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Banana App ### 2 | app-package/assets 3 | app-simulator/assets 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### Node ### 49 | # Logs 50 | logs 51 | *.log 52 | npm-debug.log* 53 | yarn-debug.log* 54 | yarn-error.log* 55 | lerna-debug.log* 56 | 57 | # Diagnostic reports (https://nodejs.org/api/report.html) 58 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 59 | 60 | # Runtime data 61 | pids 62 | *.pid 63 | *.seed 64 | *.pid.lock 65 | 66 | # Directory for instrumented libs generated by jscoverage/JSCover 67 | lib-cov 68 | 69 | # Coverage directory used by tools like istanbul 70 | coverage 71 | *.lcov 72 | 73 | # nyc test coverage 74 | .nyc_output 75 | 76 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 77 | .grunt 78 | 79 | # Bower dependency directory (https://bower.io/) 80 | bower_components 81 | 82 | # node-waf configuration 83 | .lock-wscript 84 | 85 | # Compiled binary addons (https://nodejs.org/api/addons.html) 86 | build/Release 87 | 88 | # Dependency directories 89 | node_modules/ 90 | jspm_packages/ 91 | 92 | # TypeScript v1 declaration files 93 | typings/ 94 | 95 | # TypeScript cache 96 | *.tsbuildinfo 97 | 98 | # Optional npm cache directory 99 | .npm 100 | 101 | # Optional eslint cache 102 | .eslintcache 103 | 104 | # Optional REPL history 105 | .node_repl_history 106 | 107 | # Output of 'npm pack' 108 | *.tgz 109 | 110 | # Yarn Integrity file 111 | .yarn-integrity 112 | 113 | # dotenv environment variables file 114 | .env 115 | .env.test 116 | 117 | # parcel-bundler cache (https://parceljs.org/) 118 | .cache 119 | 120 | # next.js build output 121 | .next 122 | 123 | # nuxt.js build output 124 | .nuxt 125 | 126 | # vuepress build output 127 | .vuepress/dist 128 | 129 | # Serverless directories 130 | .serverless/ 131 | 132 | # FuseBox cache 133 | .fusebox/ 134 | 135 | # DynamoDB Local files 136 | .dynamodb/ 137 | 138 | ### SublimeText ### 139 | # Cache files for Sublime Text 140 | *.tmlanguage.cache 141 | *.tmPreferences.cache 142 | *.stTheme.cache 143 | 144 | # Workspace files are user-specific 145 | *.sublime-workspace 146 | 147 | # Project files should be checked into the repository, unless a significant 148 | # proportion of contributors will probably not be using Sublime Text 149 | # *.sublime-project 150 | 151 | # SFTP configuration file 152 | sftp-config.json 153 | 154 | # Package control specific files 155 | Package Control.last-run 156 | Package Control.ca-list 157 | Package Control.ca-bundle 158 | Package Control.system-ca-bundle 159 | Package Control.cache/ 160 | Package Control.ca-certs/ 161 | Package Control.merged-ca-bundle 162 | Package Control.user-ca-bundle 163 | oscrypto-ca-bundle.crt 164 | bh_unicode_properties.cache 165 | 166 | # Sublime-github package stores a github token in this file 167 | # https://packagecontrol.io/packages/sublime-github 168 | GitHub.sublime-settings 169 | 170 | ### VisualStudioCode ### 171 | .vscode/* 172 | !.vscode/settings.json 173 | !.vscode/tasks.json 174 | !.vscode/launch.json 175 | !.vscode/extensions.json 176 | 177 | ### VisualStudioCode Patch ### 178 | # Ignore all local history of files 179 | .history 180 | 181 | ### Windows ### 182 | # Windows thumbnail cache files 183 | Thumbs.db 184 | Thumbs.db:encryptable 185 | ehthumbs.db 186 | ehthumbs_vista.db 187 | 188 | # Dump file 189 | *.stackdump 190 | 191 | # Folder config file 192 | [Dd]esktop.ini 193 | 194 | # Recycle Bin used on file shares 195 | $RECYCLE.BIN/ 196 | 197 | # Windows Installer files 198 | *.cab 199 | *.msi 200 | *.msix 201 | *.msm 202 | *.msp 203 | 204 | # Windows shortcuts 205 | *.lnk 206 | 207 | # End of https://www.gitignore.io/api/node,linux,macos,windows,sublimetext,visualstudiocode -------------------------------------------------------------------------------- /app-core/Grid.js: -------------------------------------------------------------------------------- 1 | class Grid { 2 | 3 | constructor() { 4 | this.world = null; 5 | this.entities = null; 6 | this.global_x = 576; 7 | this.global_y = 192; 8 | this.size = 32; 9 | this.gridList = []; 10 | this.timePassed = 0; // Milliseconds 11 | } 12 | 13 | reset() { 14 | this.gridList = []; 15 | this.addBlock(0, 0, 'dirt'); 16 | this.addBlock(1, 0, 'dirt'); 17 | this.addBlock(2, 0, 'dirt'); 18 | this.addBlock(3, 0, 'air'); 19 | this.addBlock(3, 1, 'dirt'); 20 | this.addBlock(4, 0, 'dirt'); 21 | this.addBlock(5, 0, 'dirt'); 22 | this.addBlock(6, 0, 'dirt'); 23 | this.randomizeLine(3); 24 | } 25 | 26 | addBlock(x, y, type) { 27 | if (x < 0 || x > 6) type = 'hard'; 28 | this.gridList.push({ 29 | type: type, 30 | x: x, 31 | y: y 32 | }); 33 | } 34 | 35 | getBlockType(x, y) { 36 | if (y < 0) 37 | return null; 38 | 39 | for (var i = 0; i < this.gridList.length; i++) { 40 | if (this.gridList[i].x === x && this.gridList[i].y === y) { 41 | return this.gridList[i].type; 42 | } 43 | } 44 | 45 | return 'empty'; 46 | } 47 | 48 | setBlock(x, y, type) { 49 | for (var i = 0; i < this.gridList.length; i++) { 50 | if (this.gridList[i].x === x && this.gridList[i].y === y) { 51 | this.gridList[i] = { 52 | type: type, 53 | x: x, 54 | y: y 55 | }; 56 | 57 | if (type === 'bad_dirt') { 58 | return; 59 | } 60 | 61 | const typeBelow = this.getBlockType(x, y + 1); 62 | const typeAbove = this.getBlockType(x, y - 1); 63 | const typeAtRight = this.getBlockType(x + 1, y); 64 | const typeAtLeft = this.getBlockType(x - 1, y); 65 | 66 | // Check if surrounding empty blocks need replacement 67 | if (typeBelow === 'empty') this.addBlock(x, y + 1, 'dirt'); 68 | if (typeAbove === 'empty') this.addBlock(x, y - 1, 'dirt'); 69 | if (typeAtRight === 'empty') this.addBlock(x + 1, y, 'dirt'); 70 | if (typeAtLeft === 'empty') this.addBlock(x - 1, y, 'dirt'); 71 | 72 | // Check if foe blocks need bad dirt replacement 73 | if (typeBelow === 'foe') this.setBlock(x, y + 1, 'bad_dirt'); 74 | if (typeAbove === 'foe') this.setBlock(x, y - 1, 'bad_dirt'); 75 | if (typeAtRight === 'foe') this.setBlock(x + 1, y, 'bad_dirt'); 76 | if (typeAtLeft === 'foe') this.setBlock(x - 1, y, 'bad_dirt'); 77 | 78 | if (type === 'air') { 79 | let treasureAbove = typeAbove ? typeAbove.indexOf('treasure') !== -1 : false; 80 | let treasureBelow = typeBelow ? typeBelow.indexOf('treasure') !== -1 : false; 81 | let treasureAtRight = typeAtRight ? typeAtRight.indexOf('treasure') !== -1 : false; 82 | let treasureAtLeft = typeAtLeft ? typeAtLeft.indexOf('treasure') !== -1 : false; 83 | 84 | if (treasureAbove) { 85 | this.setBlock(x, y - 1, 'air'); 86 | this.entities.create(x, y - 1, typeAbove); 87 | } 88 | 89 | if (treasureBelow) { 90 | this.setBlock(x, y + 1, 'air'); 91 | this.entities.create(x, y + 1, typeBelow); 92 | } 93 | 94 | if (treasureAtLeft) { 95 | this.setBlock(x - 1, y, 'air'); 96 | this.entities.create(x - 1, y, typeAtLeft); 97 | } 98 | 99 | if (treasureAtRight) { 100 | this.setBlock(x + 1, y, 'air'); 101 | this.entities.create(x + 1, y, typeAtRight); 102 | } 103 | } 104 | 105 | return; 106 | } 107 | } 108 | 109 | this.addBlock(x, y, type); 110 | } 111 | 112 | updateGrid(player_y) { 113 | this.gridList = this.gridList.filter(block => block.y >= player_y - 4); 114 | this.randomizeLine(player_y + 5); 115 | } 116 | 117 | randomizeLine(laneY) { 118 | for (let x = 0; x < 7; x++) { 119 | // Roll the dice for dirt 120 | let dice = Math.random() * 100; 121 | if (dice <= 7) { 122 | // Roll again for treasure 123 | dice = Math.floor(Math.random() * 100); 124 | 125 | if (dice <= 20) { 126 | this.setBlock(x, laneY, 'treasure2'); 127 | 128 | } else if (dice > 20 && dice <= 40) { 129 | this.setBlock(x, laneY, 'treasure4'); 130 | 131 | } else if (dice > 40 && dice <= 60) { 132 | this.setBlock(x, laneY, 'treasure0'); 133 | 134 | } else if (dice > 60 && dice <= 80) { 135 | this.setBlock(x, laneY, 'treasure1'); 136 | 137 | } else if (dice > 80 && dice <= 85) { 138 | this.setBlock(x, laneY, 'treasure5'); 139 | 140 | } else if (dice > 85 && dice <= 90) { 141 | this.setBlock(x, laneY, 'treasure3'); 142 | 143 | } else if (dice > 95 && dice <= 100) { 144 | this.setBlock(x, laneY, 'treasure6'); 145 | } 146 | } else if (dice > 7 && dice < 12) { 147 | this.setBlock(x, laneY, 'foe'); 148 | } 149 | } 150 | } 151 | 152 | draw(CanvasHelper) { 153 | if (this.gridList.length < 1) 154 | return; 155 | 156 | // Draw air first 157 | for (var i = 0; i < this.gridList.length; i++) { 158 | const isAir = this.gridList[i].type === 'air' 159 | const isTreasure = this.gridList[i].type.indexOf('treasure') !== -1; 160 | if (isAir || isTreasure) { 161 | 162 | const x = this.world.x + this.global_x + this.gridList[i].x * this.size; 163 | const y = this.world.y + this.global_y + this.gridList[i].y * this.size; 164 | 165 | if (isAir) { 166 | CanvasHelper.drawSquare([0.721, 0.768, 0.831, 1], x-1, y-1, 34); 167 | } else { 168 | CanvasHelper.drawSquare([0.721, 0.768, 0.831, 1], x, y, 32); 169 | } 170 | } 171 | } 172 | 173 | // Then everything else 174 | for (var i = 0; i < this.gridList.length; i++) { 175 | if (this.gridList[i].type === 'air') { 176 | continue; 177 | } 178 | 179 | const x = this.world.x + this.global_x + this.gridList[i].x * this.size; 180 | const y = this.world.y + this.global_y + this.gridList[i].y * this.size; 181 | 182 | if (this.gridList[i].type.indexOf('treasure') !== -1) { 183 | CanvasHelper.drawImage(this.gridList[i].type, x, y); 184 | } 185 | 186 | if (this.gridList[i].type === 'dirt' || this.gridList[i].type === 'hard') { 187 | CanvasHelper.drawImage('dirt', x, y); 188 | } 189 | 190 | if (this.gridList[i].type === 'bad_dirt') { 191 | CanvasHelper.drawImage('dirt', x, y); 192 | } 193 | } 194 | } 195 | } 196 | 197 | export default new Grid(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Contributors][contributors-shield]][contributors-url] 3 | [![Forks][forks-shield]][forks-url] 4 | [![Stargazers][stars-shield]][stars-url] 5 | [![Issues][issues-shield]][issues-url] 6 | [![MIT License][license-shield]][license-url] 7 | [![LinkedIn][linkedin-shield]][linkedin-url] 8 | 9 | 10 | 11 | 12 |
13 |

14 | 15 | Amateur Archaeology Logo 16 | 17 | 18 |

19 | A small game made for KaiOS devices, available in the KaiStore. 20 |
21 |
22 |
23 | View Demo 24 | · 25 | Report Bug 26 |

27 |

28 | 29 | 30 | 31 | 32 | ## Table of Contents 33 | 34 | * [About the Project](#about-the-project) 35 | * [Building The Project](#building-the-project) 36 | * [Simulator And Bundling](#simulator-and-bundling) 37 | * [Using KaiOS Devices](#using-kaios-devices) 38 | * [Contributing](#contributing) 39 | * [License](#license) 40 | * [Contact](#contact) 41 | 42 | 43 | 44 | 45 | ## About The Project 46 | 47 | I originally made this game for PC a few years ago for a game jam, you can take a look at it on [fergarram.itch.io](https://fergarram.itch.io/amateur-archaeology-iii). 48 | 49 | I also wrote a devlog for this project which you can read at [dev.to/fergarram](https://dev.to/fergarram/creating-a-game-for-feature-phones-using-javascript-3bnn). 50 | 51 | The game is written in ES6 using Canvas with a WebGL context. I didn't use any wrapper, library or framework for WebGL except for a math utility lib written by [webglfundamentals.org](https://webglfundamentals.org/). 52 | 53 | The simulator was made using [Svelte](svelte.dev), and the device graphics were traced by hand using a photo of my real Banana Phone using Sketch. 54 | 55 | I also created a [template](https://github.com/Fergarram/banana-app) for creating similarly-styled KaiOS apps which includes the source code for the simulator. 56 | 57 | 58 | ## Building The Project 59 | 60 | To get a local copy up and running follow these simple steps. 61 | 62 | ### Simulator And Bundling 63 | For using the simulator and packaging the app you only need NPM: 64 | 65 | ```sh 66 | git clone https://github.com/fergarram/amateur-archaeology.git 67 | 68 | npm install 69 | 70 | # Local server for the simulator (game included) 71 | npm run dev 72 | 73 | # Static files for the simulator (game included) 74 | npm run build:sim 75 | 76 | # KaiOS App Package (Develpment) 77 | npm run build 78 | 79 | # KaiOS App Package (Production) 80 | npm run build:prod 81 | ``` 82 | 83 | ### Using KaiOS Devices 84 | 85 | If you want to run this on your real KaiOS device, you need to first enable development mode on it by dialing `*#*#33284#*#*`. You should be able to see a small bug icon on the status bar of your device. 86 | 87 | Then, you need to make sure your device is detected by the adb tool: 88 | ``` 89 | > adb devices 90 | 91 | List of devices attached 92 | 4939400 device 93 | ``` 94 | 95 | After making sure your device is connected, you need to run the following command to forward your devices debugging TCP port: 96 | ``` 97 | adb forward tcp:6000 localfilesystem:/data/local/debugger-socket 98 | ``` 99 | 100 | Open up Firefox 49.0 (Version is very important!) WebIDE and click on the "Remote Runtime" and then click ok when the popup message appears. Lastly, you select the app-package folder and simply click on the play button. 101 | 102 | Setting up the environment for developing on for KaiOS can be tricky at first, you can take a look at this [article](https://nolanlawson.com/2019/09/22/the-joy-and-challenge-of-developing-for-kaios/) for more info on that, or read the official documentation at [developer.kaiostech.com](https://developer.kaiostech.com/). 103 | 104 | 105 | 106 | ## Contributing 107 | 108 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 109 | 110 | 1. Fork the Project 111 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 112 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 113 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 114 | 5. Open a Pull Request 115 | 116 | 117 | 118 | 119 | ## License 120 | 121 | Distributed under the MIT License. See `LICENSE` for more information. 122 | 123 | 124 | 125 | 126 | ## Contact 127 | 128 | You can reach out to me through [LinkedIn](https://linkedin.com/in/fergarram), [Dev.to](dev.to/fergarram), or [Twitter](https://twitter.com/_fergarram_). 129 | 130 | 131 | 132 | 133 | [contributors-shield]: https://img.shields.io/github/contributors/fergarram/amateur-archaeology.svg?style=flat-square 134 | [contributors-url]: https://github.com/fergarram/amateur-archaeology/graphs/contributors 135 | [forks-shield]: https://img.shields.io/github/forks/fergarram/amateur-archaeology.svg?style=flat-square 136 | [forks-url]: https://github.com/fergarram/amateur-archaeology/network/members 137 | [stars-shield]: https://img.shields.io/github/stars/fergarram/amateur-archaeology.svg?style=flat-square 138 | [stars-url]: https://github.com/fergarram/amateur-archaeology/stargazers 139 | [issues-shield]: https://img.shields.io/github/issues/fergarram/amateur-archaeology.svg?style=flat-square 140 | [issues-url]: https://github.com/fergarram/amateur-archaeology/issues 141 | [license-shield]: https://img.shields.io/github/license/fergarram/amateur-archaeology.svg?style=flat-square 142 | [license-url]: https://github.com/fergarram/amateur-archaeology/blob/master/LICENSE.txt 143 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555 144 | [linkedin-url]: https://linkedin.com/in/fergarram 145 | -------------------------------------------------------------------------------- /app-simulator/build/bundle.js: -------------------------------------------------------------------------------- 1 | var app=function(){"use strict";function t(){}function e(t){return t()}function n(){return Object.create(null)}function r(t){t.forEach(e)}function o(t){return"function"==typeof t}function l(t,e){return t!=t?e==e:t!==e||t&&"object"==typeof t||"function"==typeof t}function $(t,e){t.appendChild(e)}function f(t,e,n){t.insertBefore(e,n||null)}function s(t){t.parentNode.removeChild(t)}function a(t){return document.createElement(t)}function p(){return t=" ",document.createTextNode(t);var t}function u(t,e,n){null==n?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}let c;function m(t){c=t}const g=[],i=[],y=[],d=[],x=Promise.resolve();let h=!1;function k(t){y.push(t)}function w(){const t=new Set;do{for(;g.length;){const t=g.shift();m(t),b(t.$$)}for(;i.length;)i.pop()();for(let e=0;e{v.delete(t),r&&(n&&t.d(1),r())}),t.o(e)}}function z(t){t&&t.c()}function C(t,n,l){const{fragment:$,on_mount:f,on_destroy:s,after_update:a}=t.$$;$&&$.m(n,l),k(()=>{const n=f.map(e).filter(o);s?s.push(...n):r(n),t.$$.on_mount=[]}),a.forEach(k)}function L(t,e){const n=t.$$;null!==n.fragment&&(r(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function S(t,e){-1===t.$$.dirty[0]&&(g.push(t),h||(h=!0,x.then(w)),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<(g.ctx&&f(g.ctx[t],g.ctx[t]=r)&&(g.bound[t]&&g.bound[t](r),i&&S(e,t)),n)):[],g.update(),i=!0,r(g.before_update),g.fragment=!!$&&$(g.ctx),o.target&&(o.hydrate?g.fragment&&g.fragment.l(function(t){return Array.from(t.childNodes)}(o.target)):g.fragment&&g.fragment.c(),o.intro&&E(e.$$.fragment),C(e,o.target,o.anchor),w()),m(p)}class j{$destroy(){L(this,1),this.$destroy=t}$on(t,e){const n=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return n.push(e),()=>{const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}$set(){}}function B(e){let n,r,o,l;return{c(){var t,$,f,s;n=a("button"),r=a("img"),r.src!==(o=`buttons/${e[2]}.svg`)&&u(r,"src",o),u(r,"alt",e[0]),u(n,"style",e[1]),u(n,"class","svelte-623b3k"),t=n,$="click",f=e[3],t.addEventListener($,f,s),l=()=>t.removeEventListener($,f,s)},m(t,e){f(t,n,e),$(n,r)},p(t,[e]){4&e&&r.src!==(o=`buttons/${t[2]}.svg`)&&u(r,"src",o),1&e&&u(r,"alt",t[0]),2&e&&u(n,"style",t[1])},i:t,o:t,d(t){t&&s(n),l()}}}function O(t,e,n){let{key:r}=e,{style:o}=e,l=r;"*"===l&&(l="asterisk"),"#"===l&&(l="hash");return t.$set=t=>{"key"in t&&n(0,r=t.key),"style"in t&&n(1,o=t.style)},[r,o,l,()=>{const t=new CustomEvent("keydown");t.key=r,document.dispatchEvent(t)}]}class R extends j{constructor(t){super(),N(this,t,O,B,l,{key:0,style:1})}}function q(e){let n,r,o,l,c,m,g,i,y,d,x,h,k,w,b,v,_,S,N,j,B,O;const q=new R({props:{style:"left: 0;",key:"SoftLeft"}}),D=new R({props:{style:"left: 121px;",key:"SoftRight"}}),M=new R({props:{style:"left: 0px; top: 54px",key:"Call"}}),P=new R({props:{style:"left: 121px; top: 54px",key:"Backspace"}}),T=new R({props:{style:"left: 88px; top: 26px",key:"Arrowup"}}),F=new R({props:{style:"left: 79px; top: 33px",key:"ArrowLeft"}}),G=new R({props:{style:"left: 92px; top: 38px",key:"Enter"}}),H=new R({props:{style:"left: 146px; top: 36px",key:"ArrowRight"}}),I=new R({props:{style:"left: 89px; top: 89px",key:"ArrowDown"}}),J=new R({props:{style:"left: 0px; top: 104px",key:"1"}}),K=new R({props:{style:"left: 80px; top: 116px",key:"2"}}),Q=new R({props:{style:"left: 160px; top: 104px",key:"3"}}),U=new R({props:{style:"left: 0px; top: 140px",key:"4"}}),V=new R({props:{style:"left: 80px; top: 151px",key:"5"}}),W=new R({props:{style:"left: 160px; top: 140px",key:"6"}}),X=new R({props:{style:"left: 0px; top: 174px",key:"7"}}),Y=new R({props:{style:"left: 80px; top: 185px",key:"8"}}),Z=new R({props:{style:"left: 160px; top: 174px",key:"9"}}),tt=new R({props:{style:"left: 0px; top: 208px",key:"*"}}),et=new R({props:{style:"left: 80px; top: 219px",key:"0"}}),nt=new R({props:{style:"left: 160px; top: 208px",key:"#"}});return{c(){n=a("div"),z(q.$$.fragment),r=p(),z(D.$$.fragment),o=p(),z(M.$$.fragment),l=p(),z(P.$$.fragment),c=p(),z(T.$$.fragment),m=p(),z(F.$$.fragment),g=p(),z(G.$$.fragment),i=p(),z(H.$$.fragment),y=p(),z(I.$$.fragment),d=p(),z(J.$$.fragment),x=p(),z(K.$$.fragment),h=p(),z(Q.$$.fragment),k=p(),z(U.$$.fragment),w=p(),z(V.$$.fragment),b=p(),z(W.$$.fragment),v=p(),z(X.$$.fragment),_=p(),z(Y.$$.fragment),S=p(),z(Z.$$.fragment),N=p(),z(tt.$$.fragment),j=p(),z(et.$$.fragment),B=p(),z(nt.$$.fragment),u(n,"id","keyboard"),u(n,"class","svelte-xd431t")},m(t,e){f(t,n,e),C(q,n,null),$(n,r),C(D,n,null),$(n,o),C(M,n,null),$(n,l),C(P,n,null),$(n,c),C(T,n,null),$(n,m),C(F,n,null),$(n,g),C(G,n,null),$(n,i),C(H,n,null),$(n,y),C(I,n,null),$(n,d),C(J,n,null),$(n,x),C(K,n,null),$(n,h),C(Q,n,null),$(n,k),C(U,n,null),$(n,w),C(V,n,null),$(n,b),C(W,n,null),$(n,v),C(X,n,null),$(n,_),C(Y,n,null),$(n,S),C(Z,n,null),$(n,N),C(tt,n,null),$(n,j),C(et,n,null),$(n,B),C(nt,n,null),O=!0},p:t,i(t){O||(E(q.$$.fragment,t),E(D.$$.fragment,t),E(M.$$.fragment,t),E(P.$$.fragment,t),E(T.$$.fragment,t),E(F.$$.fragment,t),E(G.$$.fragment,t),E(H.$$.fragment,t),E(I.$$.fragment,t),E(J.$$.fragment,t),E(K.$$.fragment,t),E(Q.$$.fragment,t),E(U.$$.fragment,t),E(V.$$.fragment,t),E(W.$$.fragment,t),E(X.$$.fragment,t),E(Y.$$.fragment,t),E(Z.$$.fragment,t),E(tt.$$.fragment,t),E(et.$$.fragment,t),E(nt.$$.fragment,t),O=!0)},o(t){A(q.$$.fragment,t),A(D.$$.fragment,t),A(M.$$.fragment,t),A(P.$$.fragment,t),A(T.$$.fragment,t),A(F.$$.fragment,t),A(G.$$.fragment,t),A(H.$$.fragment,t),A(I.$$.fragment,t),A(J.$$.fragment,t),A(K.$$.fragment,t),A(Q.$$.fragment,t),A(U.$$.fragment,t),A(V.$$.fragment,t),A(W.$$.fragment,t),A(X.$$.fragment,t),A(Y.$$.fragment,t),A(Z.$$.fragment,t),A(tt.$$.fragment,t),A(et.$$.fragment,t),A(nt.$$.fragment,t),O=!1},d(t){t&&s(n),L(q),L(D),L(M),L(P),L(T),L(F),L(G),L(H),L(I),L(J),L(K),L(Q),L(U),L(V),L(W),L(X),L(Y),L(Z),L(tt),L(et),L(nt)}}}class D extends j{constructor(t){super(),N(this,t,null,q,l,{})}}function M(e){let n,r,o,l,c,m;const g=new D({});return{c(){n=a("main"),r=a("div"),o=a("div"),l=a("div"),c=p(),z(g.$$.fragment),u(l,"id","screen"),u(l,"class","svelte-1i3izob"),u(o,"id","device-io"),u(o,"class","svelte-1i3izob"),u(r,"id","phone"),u(r,"class","svelte-1i3izob"),u(n,"class","svelte-1i3izob")},m(t,e){f(t,n,e),$(n,r),$(r,o),$(o,l),$(o,c),C(g,o,null),m=!0},p:t,i(t){m||(E(g.$$.fragment,t),m=!0)},o(t){A(g.$$.fragment,t),m=!1},d(t){t&&s(n),L(g)}}}return new class extends j{constructor(t){super(),N(this,t,null,M,l,{})}}({target:document.body,props:{}})}(); 2 | //# sourceMappingURL=bundle.js.map 3 | -------------------------------------------------------------------------------- /app-core/CanvasHelper.js: -------------------------------------------------------------------------------- 1 | import m4 from './libs/m4.js'; 2 | import webglUtils from './libs/webgl-utils.js'; 3 | class CanvasHelper { 4 | 5 | constructor() { 6 | this.screen = null; 7 | this.canvas = null; 8 | this.assets = null; 9 | this.ctx = null; 10 | this.gl = null; 11 | this.imageProgram = null; 12 | this.squareProgram = null; 13 | this.textureInfoArray = {}; 14 | } 15 | 16 | init(AssetLoader) { 17 | this.screen = document.getElementById('screen'); 18 | this.canvas = document.createElement('canvas'); 19 | this.canvas.id = 'MainCanvas'; 20 | this.canvas.width = 240; 21 | this.canvas.height = 320; 22 | this.canvas.style.zIndex = 8; 23 | this.canvas.style.position = 'absolute'; 24 | this.screen.appendChild(this.canvas); 25 | this.gl = this.canvas.getContext('experimental-webgl', { preserveDrawingBuffer: true }); 26 | this.assets = AssetLoader; 27 | 28 | // Setup GLSL programs. 29 | this.imageProgram = webglUtils.createProgram(this.gl, this.createImageShaders()); 30 | this.squareProgram = webglUtils.createProgram(this.gl, this.createSquareShaders()); 31 | 32 | // Look up where the vertex data needs to go. 33 | this.positionLocation = this.gl.getAttribLocation(this.imageProgram, "a_position"); 34 | this.texcoordLocation = this.gl.getAttribLocation(this.imageProgram, "a_texcoord"); 35 | this.squarePositionLoc = this.gl.getAttribLocation(this.squareProgram, "a_position"); 36 | 37 | // Lookup uniforms. 38 | this.matrixLocation = this.gl.getUniformLocation(this.imageProgram, "u_matrix"); 39 | this.textureLocation = this.gl.getUniformLocation(this.imageProgram, "u_texture"); 40 | this.imageColorUniformLoc = this.gl.getUniformLocation(this.imageProgram, 'u_color'); 41 | this.squareMatrixLocation = this.gl.getUniformLocation(this.squareProgram, "u_matrix"); 42 | this.squareColorUniformLoc = this.gl.getUniformLocation(this.squareProgram, 'u_color'); 43 | this.suqareSizeUniformLoc = this.gl.getUniformLocation(this.squareProgram, 'u_size'); 44 | 45 | // Square point position buffer 46 | this.squarePositionBuffer = this.gl.createBuffer(); 47 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.squarePositionBuffer); 48 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([0, 0]), this.gl.STATIC_DRAW); 49 | 50 | // Create a buffer. 51 | this.positionBuffer = this.gl.createBuffer(); 52 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); 53 | 54 | // Put a unit quad in the buffer 55 | const positions = [ 56 | 0, 0, 57 | 0, 1, 58 | 1, 0, 59 | 1, 0, 60 | 0, 1, 61 | 1, 1, 62 | ]; 63 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW); 64 | 65 | // Create a buffer for texture coords 66 | this.texcoordBuffer = this.gl.createBuffer(); 67 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer); 68 | 69 | // Put texcoords in the buffer 70 | const texcoords = [ 71 | 0, 0, 72 | 0, 1, 73 | 1, 0, 74 | 1, 0, 75 | 0, 1, 76 | 1, 1, 77 | ]; 78 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(texcoords), this.gl.STATIC_DRAW); 79 | 80 | // Enable alpha for textures 81 | this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); 82 | this.gl.enable(this.gl.BLEND); 83 | 84 | for (let [name, img] of Object.entries(this.assets.images)) { 85 | // creates a texture info { width: w, height: h, texture: tex } 86 | // The texture will start with 1x1 pixels and be updated 87 | // when the image has loaded 88 | const texture = this.gl.createTexture(); 89 | this.gl.bindTexture(this.gl.TEXTURE_2D, texture); 90 | 91 | // let's assume all images are not a power of 2 92 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); 93 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); 94 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); 95 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); 96 | 97 | const textureInfo = { 98 | width: 1, 99 | height: 1, 100 | texture: texture 101 | }; 102 | 103 | textureInfo.width = img.width; 104 | textureInfo.height = img.height; 105 | 106 | this.gl.bindTexture(this.gl.TEXTURE_2D, textureInfo.texture); 107 | this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, img); 108 | 109 | this.textureInfoArray[name] = textureInfo; 110 | }; 111 | } 112 | 113 | loop(update, render) { 114 | let fps, delta, lastRender = Date.now() - 1; 115 | 116 | const animate = () => { 117 | delta = Date.now() - lastRender; 118 | fps = parseInt(1000 / delta); 119 | 120 | update(delta); 121 | 122 | this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); 123 | this.gl.clearColor(0, 0, 0, 1); 124 | this.gl.clear(this.gl.COLOR_BUFFER_BIT); 125 | 126 | render(); 127 | 128 | lastRender = Date.now(); 129 | window.requestAnimationFrame(animate); 130 | }; 131 | 132 | // Start the rendering loop 133 | animate(); 134 | } 135 | 136 | drawSquare(rgba, x, y, s) { 137 | this.gl.useProgram(this.squareProgram); 138 | 139 | // Setup the attributes to pull data from our buffers 140 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.squarePositionBuffer); 141 | this.gl.enableVertexAttribArray(this.squarePositionLoc); 142 | this.gl.vertexAttribPointer(this.squarePositionLoc, 2, this.gl.FLOAT, false, 0, 0); 143 | 144 | // this matirx will convert from pixels to clip space 145 | let matrix = m4.orthographic(0, this.canvas.width, this.canvas.height, 0, -1, 1); 146 | 147 | // this matrix will translate our quad to x, y 148 | matrix = m4.translate(matrix, x + (s/2), y + (s/2), 0); 149 | matrix = m4.scale(matrix, s, s, 1); 150 | this.gl.uniformMatrix4fv(this.squareMatrixLocation, false, matrix); 151 | this.gl.uniform1f(this.suqareSizeUniformLoc, s); 152 | this.gl.uniform4fv(this.squareColorUniformLoc, new Float32Array(rgba)); 153 | 154 | this.gl.drawArrays(this.gl.POINTS, 0, 1); 155 | } 156 | 157 | drawImage(imageName, x, y, a = 1, flipx = false, flipy = false) { 158 | const textureInfo = this.textureInfoArray[imageName]; 159 | this.gl.bindTexture(this.gl.TEXTURE_2D, textureInfo.texture); 160 | 161 | // Tell WebGL to use our shader program pair 162 | this.gl.useProgram(this.imageProgram); 163 | 164 | // Setup the attributes to pull data from our buffers 165 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); 166 | this.gl.enableVertexAttribArray(this.positionLocation); 167 | this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 0, 0); 168 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer); 169 | this.gl.enableVertexAttribArray(this.texcoordLocation); 170 | this.gl.vertexAttribPointer(this.texcoordLocation, 2, this.gl.FLOAT, false, 0, 0); 171 | 172 | // this matirx will convert from pixels to clip space 173 | let matrix = m4.orthographic(0, this.canvas.width, this.canvas.height, 0, -1, 1); 174 | 175 | // this matrix will translate our quad to x, y 176 | const xx = flipx ? x + (textureInfo.width * 2) : x; 177 | const yy = flipy ? y + (extureInfo.height * 2) : y; 178 | const sx = flipx ? -1 : 1; 179 | const sy = flipy ? -1 : 1; 180 | matrix = m4.translate(matrix, xx, yy, 0); 181 | matrix = m4.scale(matrix, (textureInfo.width * 2) * sx, (textureInfo.height * 2) * sy, 1); 182 | this.gl.uniformMatrix4fv(this.matrixLocation, false, matrix); 183 | this.gl.uniform1i(this.textureLocation, 0); 184 | this.gl.uniform4fv(this.imageColorUniformLoc, new Float32Array([1, 1, 1, a])); 185 | this.gl.drawArrays(this.gl.TRIANGLES, 0, 6); 186 | } 187 | 188 | createShader(sourceCode, type) { 189 | // Compiles either a shader of type gl.VERTEX_SHADER or gl.FRAGMENT_SHADER 190 | var shader = this.gl.createShader(type); 191 | this.gl.shaderSource(shader, sourceCode); 192 | this.gl.compileShader(shader); 193 | 194 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 195 | var info = this.gl.getShaderInfoLog(shader); 196 | throw 'Could not compile WebGL program. \n\n' + info; 197 | } 198 | return shader; 199 | } 200 | 201 | createSquareShaders() { 202 | const vertex = this.createShader(` 203 | attribute vec4 a_position; 204 | uniform mat4 u_matrix; 205 | uniform float u_size; 206 | 207 | void main() { 208 | gl_Position = u_matrix * a_position; 209 | gl_PointSize = u_size; 210 | } 211 | `, this.gl.VERTEX_SHADER); 212 | const frag = this.createShader(` 213 | precision mediump float; 214 | uniform vec4 u_color; 215 | 216 | void main() { 217 | gl_FragColor = u_color; 218 | } 219 | `, this.gl.FRAGMENT_SHADER); 220 | return [ vertex, frag ]; 221 | } 222 | 223 | createImageShaders() { 224 | const vertex = this.createShader(` 225 | attribute vec4 a_position; 226 | attribute vec2 a_texcoord; 227 | uniform mat4 u_matrix; 228 | varying vec2 v_texcoord; 229 | 230 | void main() { 231 | gl_Position = u_matrix * a_position; 232 | v_texcoord = a_texcoord; 233 | } 234 | `, this.gl.VERTEX_SHADER); 235 | const frag = this.createShader(` 236 | precision mediump float; 237 | varying vec2 v_texcoord; 238 | uniform vec4 u_color; 239 | uniform sampler2D u_texture; 240 | 241 | void main() { 242 | gl_FragColor = texture2D(u_texture, v_texcoord) * u_color; 243 | } 244 | `, this.gl.FRAGMENT_SHADER); 245 | return [ vertex, frag ]; 246 | } 247 | } 248 | 249 | export default new CanvasHelper(); -------------------------------------------------------------------------------- /app-core/EntityHelper.js: -------------------------------------------------------------------------------- 1 | import { easeLinear } from './Utilities.js'; 2 | 3 | class EntityHelper { 4 | constructor() { 5 | this.list = []; 6 | this.world = null; 7 | this.assets = null; 8 | this.player = null; 9 | this.grid = null; 10 | this.gravity = 0.1; 11 | this.speed = 2; 12 | this.elapsedTime = 0; 13 | this.shouldFlip = false; 14 | } 15 | 16 | create(gx, gy, type) { 17 | const x = this.world.x + this.grid.global_x + gx * this.grid.size; 18 | const y = this.world.y + this.grid.global_y + gy * this.grid.size; 19 | 20 | if (type === 'foe') { 21 | let dice = Math.random(); 22 | type = dice >= 0.5? 'scorpion' : 'fire'; 23 | } 24 | 25 | this.list.push({ 26 | type: type, 27 | grid_x: gx, 28 | grid_y: gy, 29 | x: x, 30 | y: y, 31 | dx: x, 32 | dy: y, 33 | isFalling: false, 34 | isMoving: false, 35 | dir: 'right', 36 | newborn: true, 37 | fallingTime: 0, // Milliseconds 38 | movingTime: 0, // Milliseconds 39 | }); 40 | } 41 | 42 | reset() { 43 | this.list = []; 44 | } 45 | 46 | clean(player_y) { 47 | this.list = this.list.filter(t => t.grid_y >= player_y - 4); 48 | } 49 | 50 | canBurnItem(x, y) { 51 | if (this.list < 1) { 52 | return false; 53 | } 54 | 55 | 56 | for (var i = 0; i < this.list.length; i++) { 57 | const isBurnable = this.list[i].type.indexOf('treasure') !== -1 || this.list[i].type === 'scorpion'; 58 | if (this.list[i].grid_x === x && this.list[i].grid_y === y && isBurnable) { 59 | return true; 60 | } 61 | } 62 | 63 | return false; 64 | } 65 | 66 | removeAtPos(x, y, callback) { 67 | if (this.list < 1) { 68 | return false; 69 | } 70 | 71 | const entitiesAtPos = []; 72 | for (var i = 0; i < this.list.length; i++) { 73 | if (this.list[i].grid_x === x && this.list[i].grid_y === y) { 74 | entitiesAtPos.push(this.list[i].type); 75 | this.list[i] = null; 76 | } 77 | } 78 | 79 | if (entitiesAtPos.length > 0) { 80 | this.list = this.list.filter(t => t !== null); 81 | callback(entitiesAtPos); 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | 88 | update(delta) { 89 | if (this.list.length < 1) 90 | return; 91 | 92 | this.elapsedTime += delta; 93 | if (this.elapsedTime >= 500) { 94 | this.shouldFlip = !this.shouldFlip; 95 | this.elapsedTime = 0; 96 | } 97 | 98 | for (var i = 0; i < this.list.length; i++) { 99 | const blockBelow = this.grid.getBlockType(this.list[i].grid_x, this.list[i].grid_y + 1); 100 | this.list[i].y = this.world.y + this.grid.global_y + this.list[i].grid_y * this.grid.size; 101 | 102 | if (this.list[i].type === 'fire') { 103 | 104 | const blockAtRight = this.grid.getBlockType(this.list[i].grid_x + 1, this.list[i].grid_y); 105 | const blockAtLeft = this.grid.getBlockType(this.list[i].grid_x - 1, this.list[i].grid_y); 106 | 107 | if (blockAtRight === 'air' && this.list[i].dir === 'right' && !this.list[i].isFalling) { 108 | 109 | if (this.list[i].newborn) { 110 | this.list[i].newborn = false; 111 | } 112 | 113 | if (!this.list[i].isMoving) { 114 | this.list[i].movingTime = 0; 115 | this.list[i].dx = this.list[i].x + this.grid.size; 116 | } 117 | 118 | if (this.list[i].x < this.list[i].dx) { 119 | this.list[i].isMoving = true; 120 | this.list[i].movingTime += delta; 121 | this.list[i].x += easeLinear(this.list[i].movingTime / 1000, 0, this.grid.size, this.speed); 122 | } 123 | 124 | if (this.list[i].x >= this.list[i].dx && this.list[i].isMoving) { 125 | this.list[i].x = this.list[i].dx; 126 | this.list[i].movingTime = 0; 127 | this.list[i].isMoving = false; 128 | this.list[i].grid_x += 1; 129 | 130 | if (this.list[i].grid_x === this.player.grid_x && this.list[i].grid_y === this.player.grid_y) { 131 | this.player.burn(); 132 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, () => {})) { 133 | continue; 134 | } 135 | } 136 | 137 | if (this.canBurnItem(this.list[i].grid_x, this.list[i].grid_y)) { 138 | const playSound = () => this.assets.playSound('hurt'); 139 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, playSound)) { 140 | continue; 141 | } 142 | } 143 | 144 | if (this.grid.getBlockType(this.list[i].grid_x + 1, this.list[i].grid_y) !== 'air') { 145 | this.list[i].dir = 'left'; 146 | } 147 | } 148 | } 149 | 150 | if (blockAtLeft === 'air' && (this.list[i].dir === 'left' || this.list[i].newborn) && !this.list[i].isFalling) { 151 | 152 | if (this.list[i].newborn) { 153 | this.list[i].newborn = false; 154 | this.list[i].dir = 'left'; 155 | } 156 | 157 | if (!this.list[i].isMoving) { 158 | this.list[i].movingTime = 0; 159 | this.list[i].dx = this.list[i].x - this.grid.size; 160 | } 161 | 162 | if (this.list[i].x > this.list[i].dx) { 163 | this.list[i].isMoving = true; 164 | this.list[i].movingTime += delta; 165 | this.list[i].x -= easeLinear(this.list[i].movingTime / 1000, 0, this.grid.size, this.speed); 166 | } 167 | 168 | if (this.list[i].x <= this.list[i].dx && this.list[i].isMoving) { 169 | this.list[i].x = this.list[i].dx; 170 | this.list[i].movingTime = 0; 171 | this.list[i].isMoving = false; 172 | this.list[i].grid_x -= 1; 173 | 174 | if (this.list[i].grid_x === this.player.grid_x && this.list[i].grid_y === this.player.grid_y) { 175 | this.player.burn(); 176 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, () => {})) { 177 | continue; 178 | } 179 | } 180 | 181 | if (this.canBurnItem(this.list[i].grid_x, this.list[i].grid_y)) { 182 | const playSound = () => this.assets.playSound('hurt'); 183 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, playSound)) { 184 | continue; 185 | } 186 | } 187 | 188 | if (this.grid.getBlockType(this.list[i].grid_x - 1, this.list[i].grid_y) !== 'air') { 189 | this.list[i].dir = 'right'; 190 | } 191 | } 192 | } 193 | } 194 | 195 | if (blockBelow === 'air') { 196 | if (!this.list[i].isFalling) { 197 | this.list[i].fallingTime = 0; 198 | this.list[i].dy = this.list[i].y + this.grid.size; 199 | } 200 | 201 | if (this.list[i].y < this.list[i].dy) { 202 | this.list[i].isFalling = true; 203 | this.list[i].fallingTime += delta; 204 | this.list[i].y += easeLinear(this.list[i].fallingTime / 1000, 0, this.grid.size, this.gravity); 205 | } 206 | 207 | if (this.list[i].y >= this.list[i].dy && this.list[i].isFalling) { 208 | this.list[i].y = this.list[i].dy; 209 | this.list[i].fallingTime = 0; 210 | this.list[i].isFalling = false; 211 | this.list[i].grid_y += 1; 212 | if (this.list[i].type === 'fire') { 213 | if (this.list[i].grid_x === this.player.grid_x && this.list[i].grid_y === this.player.grid_y) { 214 | this.player.burn(); 215 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, () => {})) { 216 | continue; 217 | } 218 | } 219 | 220 | if (this.canBurnItem(this.list[i].grid_x, this.list[i].grid_y)) { 221 | const playSound = () => this.assets.playSound('hurt'); 222 | if (this.removeAtPos(this.list[i].grid_x, this.list[i].grid_y, playSound)) { 223 | continue; 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | 232 | draw(CanvasHelper) { 233 | if (this.list.length < 1) 234 | return; 235 | 236 | for (var i = 0; i < this.list.length; i++) { 237 | if (this.list[i].type === 'scorpion' || this.list[i].type === 'fire') { 238 | CanvasHelper.drawImage(this.list[i].type, this.list[i].x, this.list[i].y, 1, this.shouldFlip); 239 | } else { 240 | CanvasHelper.drawImage(this.list[i].type, this.list[i].x, this.list[i].y); 241 | } 242 | } 243 | } 244 | } 245 | 246 | export default new EntityHelper(); -------------------------------------------------------------------------------- /app-core/UserInterface.js: -------------------------------------------------------------------------------- 1 | class UserInterface { 2 | 3 | constructor() { 4 | this.screen = null; 5 | this.topBarEl = null; 6 | this.timeBarEl = null; 7 | this.scoreEl = null; 8 | this.levelEl = null; 9 | this.levelNoEl = null; 10 | this.levelPrefixEl = null; 11 | this.dialogEl = null; 12 | this.dialogPointsEl = null; 13 | this.dialogPointsNoEl = null; 14 | this.dialogSuffixEl = null; 15 | this.controlsEl = null; 16 | this.gameOverEl = null; 17 | this.gameOverTextEl = null; 18 | this.isActive = false; 19 | this.ad = null; 20 | this.willExit = false; 21 | } 22 | 23 | init() { 24 | 25 | // 26 | // Top bar 27 | // 28 | 29 | this.topBarEl = document.createElement('div'); 30 | this.topBarEl.id = "topbar"; 31 | this.topBarEl.style.display = 'none'; 32 | this.topBarEl.style.background = "url(assets/topbar.png)"; 33 | this.topBarEl.style.zIndex = 9999; 34 | this.topBarEl.style.width = '240px'; 35 | this.topBarEl.style.height = '44px'; 36 | this.topBarEl.style.position = 'absolute'; 37 | this.topBarEl.style.backgroundRepeat = 'no-repeat'; 38 | 39 | this.timeBarEl = document.createElement('div'); 40 | this.timeBarEl.id = "timebar"; 41 | this.timeBarEl.style.position = 'absolute'; 42 | this.timeBarEl.style.left = '12px'; 43 | this.timeBarEl.style.top = '12px'; 44 | this.timeBarEl.style.width = '84px'; 45 | this.timeBarEl.style.height = '20px'; 46 | this.timeBarEl.style.backgroundColor = '#FFFFFF'; 47 | 48 | this.scoreEl = document.createElement('div'); 49 | this.scoreEl.id = 'score'; 50 | this.scoreEl.innerText = '0 / 500'; 51 | this.scoreEl.style.width = '126px'; 52 | this.scoreEl.style.position = 'relative'; 53 | this.scoreEl.style.textAlign = 'right'; 54 | this.scoreEl.style.left = `106px`; 55 | this.scoreEl.style.top = `12px`; 56 | this.scoreEl.style.letterSpacing = `2px`; 57 | this.scoreEl.style.fontSize = `17.5px`; 58 | this.scoreEl.style.color = "#FFFFFF"; 59 | this.scoreEl.style.fontFamily = "AADigits"; 60 | 61 | this.levelEl = document.createElement('div'); 62 | this.levelEl.id = 'level-bar' 63 | this.levelEl.style.position = 'relative'; 64 | this.levelEl.style.width = '228px'; 65 | this.levelEl.style.height = '25px'; 66 | this.levelEl.style.letterSpacing = `2px`; 67 | this.levelEl.style.fontSize = `17.5px`; 68 | this.levelEl.style.color = "#FFFFFF"; 69 | this.levelEl.style.backgroundColor = "#000000"; 70 | this.levelEl.style.fontFamily = "AADigits"; 71 | this.levelEl.style.textAlign = 'center'; 72 | this.levelEl.style.marginTop = '-10px'; 73 | this.levelEl.style.paddingTop = '4px'; 74 | this.levelEl.style.marginLeft = '6px'; 75 | this.levelEl.style.display = 'flex'; 76 | this.levelEl.style.justifyContent = 'center'; 77 | this.levelEl.style.lineHeight = '20px'; 78 | 79 | this.levelPrefixEl = document.createElement('img'); 80 | this.levelPrefixEl.id = "level-prefix"; 81 | this.levelPrefixEl.src = 'assets/level_prefix.png'; 82 | this.levelPrefixEl.style.paddingRight = '8px'; 83 | this.levelPrefixEl.style.height = '18px'; 84 | 85 | this.levelNoEl = document.createElement('span'); 86 | this.levelNoEl.id = 'level-number'; 87 | this.levelNoEl.innerText = '1'; 88 | 89 | this.levelEl.appendChild(this.levelPrefixEl); 90 | this.levelEl.appendChild(this.levelNoEl); 91 | 92 | this.topBarEl.appendChild(this.timeBarEl); 93 | this.topBarEl.appendChild(this.scoreEl); 94 | this.topBarEl.appendChild(this.levelEl); 95 | 96 | // 97 | // Dialog 98 | // 99 | 100 | this.dialogEl = document.createElement('div'); 101 | this.dialogEl.id = "dialog"; 102 | this.dialogEl.style.display = 'none'; 103 | this.dialogEl.style.position = 'absolute'; 104 | this.dialogEl.style.zIndex = 9999; 105 | this.dialogEl.style.width = '172px'; 106 | this.dialogEl.style.height = '192px'; 107 | this.dialogEl.style.marginLeft = '34px'; 108 | this.dialogEl.style.marginTop = '50px'; 109 | this.dialogEl.style.backgroundImage = 'url(assets/dialog_back.png)'; 110 | this.dialogEl.style.backgroundRepeat = 'no-repeat'; 111 | 112 | this.dialogPointsEl = document.createElement('div'); 113 | this.dialogPointsEl.style.width = `100%`; 114 | this.dialogPointsEl.style.letterSpacing = `2px`; 115 | this.dialogPointsEl.style.fontSize = `17.5px`; 116 | this.dialogPointsEl.style.color = "#FFFFFF"; 117 | this.dialogPointsEl.style.fontFamily = "AADigits"; 118 | this.dialogPointsEl.style.marginTop = '70px'; 119 | this.dialogPointsEl.style.textAlign = 'center'; 120 | this.dialogPointsEl.style.display = 'flex'; 121 | this.dialogPointsEl.style.justifyContent = 'center'; 122 | this.dialogPointsEl.style.lineHeight = '20px'; 123 | 124 | this.dialogPointsNoEl = document.createElement('span'); 125 | this.dialogPointsNoEl.id = 'dialog-points'; 126 | this.dialogPointsNoEl.innerText = '500'; 127 | 128 | this.dialogSuffixEl = document.createElement('img'); 129 | this.dialogSuffixEl.id = "dialog-sufix"; 130 | this.dialogSuffixEl.src = 'assets/points_suffix.png'; 131 | this.dialogSuffixEl.style.paddingLeft = '6px'; 132 | 133 | this.dialogPointsEl.appendChild(this.dialogPointsNoEl); 134 | this.dialogPointsEl.appendChild(this.dialogSuffixEl); 135 | this.dialogEl.appendChild(this.dialogPointsEl); 136 | 137 | 138 | // 139 | // Controls 140 | // 141 | 142 | this.controlsEl = document.createElement('img'); 143 | this.controlsEl.id = 'controls'; 144 | this.controlsEl.src = 'assets/controls.png'; 145 | this.controlsEl.style.display = 'none'; 146 | this.controlsEl.style.position = 'absolute'; 147 | this.controlsEl.style.zIndex = 9999; 148 | this.controlsEl.style.marginTop = '252px'; 149 | 150 | 151 | // 152 | // Game Over Screen 153 | // 154 | 155 | this.gameOverEl = document.createElement('div'); 156 | this.gameOverEl.style.width = '240px'; 157 | this.gameOverEl.style.height = '320px'; 158 | this.gameOverEl.style.position = 'absolute'; 159 | this.gameOverEl.style.backgroundColor = '#000000'; 160 | this.gameOverEl.style.display = 'none'; 161 | this.gameOverEl.style.justifyContent = 'center'; 162 | this.gameOverEl.style.alignItems = 'center'; 163 | this.gameOverTextEl = document.createElement('img'); 164 | this.gameOverTextEl.src = 'assets/game_over.png'; 165 | this.gameOverTextEl.style.transition = 'opacity ease 2s'; 166 | this.gameOverTextEl.style.opacity = '0.0'; 167 | this.gameOverEl.appendChild(this.gameOverTextEl); 168 | 169 | // 170 | // Appending to DOM 171 | // 172 | 173 | this.screen = document.getElementById('screen'); 174 | this.screen.appendChild(this.gameOverEl); 175 | this.screen.appendChild(this.topBarEl); 176 | this.screen.appendChild(this.dialogEl); 177 | this.screen.appendChild(this.controlsEl); 178 | } 179 | 180 | showGameOver() { 181 | this.topBarEl.style.visibility = 'hidden'; 182 | document.getElementById('MainCanvas').style.visibility = 'hidden'; 183 | this.topBarEl.style.visibility = 'hidden'; 184 | this.gameOverEl.style.display = 'flex'; 185 | setTimeout(() => this.gameOverTextEl.style.opacity = '1.0', 500); 186 | setTimeout(() => { 187 | if (confirm("Try again?")) { 188 | this.showAd(); 189 | } else { 190 | this.willExit = true; 191 | this.showAd(); 192 | } 193 | }, 3000); 194 | } 195 | 196 | showAd() { 197 | if (this.ad) { 198 | this.ad.call('display'); 199 | } else { 200 | this.redirectUser(); 201 | } 202 | } 203 | 204 | redirectUser() { 205 | if (this.willExit) { 206 | window.close(); 207 | } else { 208 | location.reload(); 209 | } 210 | } 211 | 212 | prepareAd() { 213 | if (window.getKaiAd) { 214 | window.getKaiAd({ 215 | publisher: '98ce6faa-00cf-4756-b191-f7019c715e51', 216 | app: 'Treasure Hunter', 217 | test: process.env.NODE_ENV === 'development' ? 1 : 0, 218 | slot: 'Game Over Screen', 219 | onerror: err => console.error('Got error when trying to fetch ad:', err), 220 | onready: ad => { 221 | this.ad = ad; 222 | this.ad.on('close', () => this.redirectUser()); 223 | } 224 | }); 225 | } 226 | } 227 | 228 | updateScore(score, goal) { 229 | this.scoreEl.innerText = `${score} / ${goal}`; 230 | if (!this.isActive) { 231 | this.dialogPointsNoEl.innerText = goal; 232 | } 233 | } 234 | 235 | updateLevel(level) { 236 | this.levelNoEl.innerText = level; 237 | this.levelEl.style.display = 'flex'; 238 | } 239 | 240 | updateTime(remainingTime, maxTime) { 241 | // The reason behind this crazy formula is that I want 242 | // it to unfill by scaled pixes (2 real pixels) at a time. 243 | if (remainingTime >= 0) { 244 | this.timeBarEl.style.width = `${Math.round((remainingTime * 42) / maxTime) * 2}px`; 245 | } 246 | } 247 | 248 | hideDialog() { 249 | this.dialogEl.style.display = 'none'; 250 | this.controlsEl.style.display = 'none'; 251 | this.levelEl.style.display = 'none'; 252 | } 253 | 254 | show() { 255 | this.topBarEl.style.removeProperty('display'); 256 | this.dialogEl.style.removeProperty('display'); 257 | this.controlsEl.style.removeProperty('display'); 258 | 259 | this.isActive = true; 260 | } 261 | } 262 | 263 | export default new UserInterface(); -------------------------------------------------------------------------------- /app-core/Player.js: -------------------------------------------------------------------------------- 1 | import { easeLinear } from './Utilities.js'; 2 | 3 | class Player { 4 | 5 | constructor() { 6 | this.world = null; 7 | this.assets = null; 8 | this.entities = null; 9 | this.game = null; 10 | this.grid = null; 11 | this.grid_x = 2; 12 | this.grid_y = -1; 13 | this.grid_size = 32; 14 | this.grid_startx = 576; 15 | this.grid_starty = 192; 16 | this.x = this.grid_startx + this.grid_x * this.grid_size; 17 | this.y = this.grid_starty + this.grid_y * this.grid_size; 18 | this.dx = this.x; 19 | this.dy = this.y; 20 | this.movingTime = 0; // Milliseconds 21 | this.dir = 'right'; 22 | this.speed = 0.5 // Seconds? - More means slower 23 | this.isMoving = false; 24 | this.isFalling = false; 25 | this.lastKeyPressed = false; 26 | this.sprite = 'idle'; 27 | this.last_sprite = 'idle'; 28 | this.canMove = false; 29 | } 30 | 31 | reset() { 32 | this.grid_x = 2; 33 | this.grid_y = -1; 34 | this.x = this.grid_startx + this.grid_x * this.grid_size; 35 | this.y = this.grid_starty + this.grid_y * this.grid_size; 36 | this.dx = this.x; 37 | this.dy = this.y; 38 | this.movingTime = 0; // Milliseconds 39 | this.dir = 'right'; 40 | this.isMoving = false; 41 | this.isFalling = false; 42 | this.lastKeyPressed = false; 43 | this.sprite = 'idle'; 44 | this.canMove = false; 45 | } 46 | 47 | update(delta) { 48 | 49 | // Gravity 50 | if (this.y < this.dy) { 51 | this.isFalling = true; 52 | this.sprite = 'move'; 53 | this.movingTime += delta; 54 | this.y += easeLinear(this.movingTime / 1000, 0, this.grid_size, this.speed); 55 | } 56 | 57 | if (this.y >= this.dy && this.isFalling) { 58 | this.y = this.dy; 59 | this.movingTime = 0; 60 | this.isFalling = false; 61 | this.sprite = 'idle'; 62 | this.assets.playSound('step'); 63 | this.calculateGridPosition(); 64 | 65 | // Updating the grid and treasure lists for clean up, etc. 66 | this.grid.updateGrid(this.grid_y); 67 | this.entities.clean(this.grid_y); 68 | } 69 | 70 | // Move right 71 | if (this.dir === 'right') { 72 | 73 | if (this.x < this.dx) { 74 | this.isMoving = true; 75 | this.sprite = 'move'; 76 | this.movingTime += delta; 77 | this.x += easeLinear(this.movingTime / 1000, 0, this.grid_size, this.speed); 78 | } 79 | 80 | if (this.x >= this.dx && this.isMoving) { 81 | this.x = this.dx; 82 | this.movingTime = 0; 83 | this.isMoving = false; 84 | this.sprite = 'idle'; 85 | 86 | this.calculateGridPosition(); 87 | 88 | if (this.lastKeyPressed === 'left') { 89 | this.dir = 'left'; 90 | } 91 | } 92 | 93 | // Move left 94 | } else if (this.dir === 'left') { 95 | 96 | if (this.x > this.dx) { 97 | this.isMoving = true; 98 | this.sprite = 'move'; 99 | this.movingTime += delta; 100 | this.x -= easeLinear(this.movingTime / 1000, 0, this.grid_size, this.speed); 101 | } 102 | 103 | if (this.x <= this.dx && this.isMoving) { 104 | this.x = this.dx; 105 | this.movingTime = 0; 106 | this.isMoving = false; 107 | this.sprite = 'idle'; 108 | 109 | this.calculateGridPosition(); 110 | 111 | if (this.lastKeyPressed === 'right') { 112 | this.dir = 'right'; 113 | } 114 | } 115 | } 116 | 117 | // Camera 118 | this.world.updateCamera(this.y); 119 | } 120 | 121 | draw(CanvasHelper) { 122 | CanvasHelper.drawImage(this.sprite, this.world.x + this.x, this.world.y + this.y, 1, this.dir === 'left'); 123 | } 124 | 125 | onKeyDown(key) { 126 | 127 | if (!this.canMove) { 128 | return; 129 | } 130 | 131 | // Move left 132 | if (key === '4' || key === 'ArrowLeft') { 133 | this.lastKeyPressed = 'left'; 134 | 135 | const canMoveLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y) === 'air' || 136 | this.grid.getBlockType(this.grid_x - 1, this.grid_y) === null; 137 | 138 | if (canMoveLeft) { 139 | this.assets.playSound('step'); 140 | } 141 | 142 | if (!this.isMoving && !this.isFalling && canMoveLeft) { 143 | this.dir = 'left'; 144 | this.movingTime = 0; 145 | this.dx = this.x - this.grid_size 146 | } 147 | 148 | if (!canMoveLeft || this.isFalling) { 149 | this.dir = 'left'; 150 | } 151 | } 152 | 153 | // Move right 154 | if (key === '6' || key === 'ArrowRight') { 155 | this.lastKeyPressed = 'right'; 156 | 157 | const canMoveRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y) === 'air' || 158 | this.grid.getBlockType(this.grid_x + 1, this.grid_y) === null; 159 | 160 | if (canMoveRight) { 161 | this.assets.playSound('step'); 162 | } 163 | 164 | if (!this.isMoving && !this.isFalling && canMoveRight) { 165 | this.dir = 'right'; 166 | this.movingTime = 0; 167 | this.dx = this.x + this.grid_size; 168 | } 169 | 170 | if (!canMoveRight || this.isFalling) { 171 | this.dir = 'right'; 172 | } 173 | } 174 | 175 | // Dig straight 176 | if (key === '5' || key === 'Enter') { 177 | this.assets.playSound('dig'); 178 | this.sprite = 'dig'; 179 | 180 | const dirtAtRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y) === 'dirt'; 181 | const dirtAtLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y) === 'dirt'; 182 | const foeAtRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y) === 'bad_dirt'; 183 | const foeAtLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y) === 'bad_dirt'; 184 | 185 | if (this.dir === 'right' && dirtAtRight) { 186 | this.grid.setBlock(this.grid_x + 1, this.grid_y, 'air'); 187 | } else if (this.dir === 'right' && foeAtRight) { 188 | this.grid.setBlock(this.grid_x + 1, this.grid_y, 'air'); 189 | this.entities.create(this.grid_x + 1, this.grid_y, 'foe'); 190 | } 191 | 192 | if (this.dir === 'left' && dirtAtLeft) { 193 | this.grid.setBlock(this.grid_x - 1, this.grid_y, 'air'); 194 | } else if (this.dir === 'left' && foeAtLeft) { 195 | this.grid.setBlock(this.grid_x - 1, this.grid_y, 'air'); 196 | this.entities.create(this.grid_x - 1, this.grid_y, 'foe'); 197 | } 198 | 199 | setTimeout(() => this.sprite = 'idle', 150); 200 | } 201 | 202 | // Dig down 203 | if (key === '8' || key === 'ArrowDown') { 204 | this.assets.playSound('dig'); 205 | this.sprite = 'dig'; 206 | 207 | const dirtAtRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y) === 'dirt'; 208 | const dirtAtLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y) === 'dirt'; 209 | const dirtAtBottomRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y + 1) === 'dirt' || 210 | this.grid.getBlockType(this.grid_x + 1, this.grid_y + 1) === 'empty'; 211 | const dirtAtBottomLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y + 1) === 'dirt' || 212 | this.grid.getBlockType(this.grid_x - 1, this.grid_y + 1) === 'empty'; 213 | const foeAtRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y) === 'bad_dirt'; 214 | const foeAtLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y) === 'bad_dirt'; 215 | const foeAtBottomRight = this.grid.getBlockType(this.grid_x + 1, this.grid_y + 1) === 'bad_dirt' || 216 | this.grid.getBlockType(this.grid_x + 1, this.grid_y + 1) === 'foe'; 217 | const foeAtBottomLeft = this.grid.getBlockType(this.grid_x - 1, this.grid_y + 1) === 'bad_dirt' || 218 | this.grid.getBlockType(this.grid_x - 1, this.grid_y + 1) === 'foe'; 219 | 220 | if (this.dir === 'right') { 221 | if (dirtAtRight) { 222 | this.grid.setBlock(this.grid_x + 1, this.grid_y, 'air'); 223 | } 224 | if (foeAtRight) { 225 | this.grid.setBlock(this.grid_x + 1, this.grid_y, 'air'); 226 | this.entities.create(this.grid_x + 1, this.grid_y, 'foe'); 227 | } 228 | if (dirtAtBottomRight) { 229 | this.grid.setBlock(this.grid_x + 1, this.grid_y + 1, 'air'); 230 | } 231 | if (foeAtBottomRight) { 232 | this.grid.setBlock(this.grid_x + 1, this.grid_y + 1, 'air'); 233 | this.entities.create(this.grid_x + 1, this.grid_y + 1, 'foe'); 234 | } 235 | } 236 | 237 | if (this.dir === 'left') { 238 | if (dirtAtLeft) { 239 | this.grid.setBlock(this.grid_x - 1, this.grid_y, 'air'); 240 | } 241 | if (foeAtLeft) { 242 | this.grid.setBlock(this.grid_x - 1, this.grid_y, 'air'); 243 | this.entities.create(this.grid_x - 1, this.grid_y, 'foe'); 244 | } 245 | if (dirtAtBottomLeft) { 246 | this.grid.setBlock(this.grid_x - 1, this.grid_y + 1, 'air'); 247 | } 248 | if (foeAtBottomLeft) { 249 | this.grid.setBlock(this.grid_x - 1, this.grid_y + 1, 'air'); 250 | this.entities.create(this.grid_x - 1, this.grid_y + 1, 'foe'); 251 | } 252 | } 253 | 254 | setTimeout(() => this.sprite = 'idle', 150); 255 | } 256 | } 257 | 258 | burn() { 259 | this.last_sprite = this.sprite; 260 | this.assets.playSound('hurt'); 261 | this.sprite = 'hurt'; 262 | setTimeout(() => { 263 | this.sprite = this.last_sprite; 264 | }, 500); 265 | this.game.substractTime(10); 266 | } 267 | 268 | calculateGridPosition() { 269 | this.grid_x = Math.floor((this.x - this.grid_startx) / this.grid_size); 270 | this.grid_y = Math.floor((this.y - this.grid_starty) / this.grid_size); 271 | if (this.grid.getBlockType(this.grid_x, this.grid_y + 1) === 'air') { 272 | if (!this.isFalling) { 273 | this.movingTime = 0; 274 | this.dy = this.y + this.grid_size; 275 | } 276 | } 277 | 278 | this.entities.removeAtPos(this.grid_x, this.grid_y, (entities) => { 279 | let playGoodSound = false; 280 | let playHurtSound = false; 281 | entities.forEach((t) => { 282 | switch(t) { 283 | case 'treasure2': 284 | this.game.addPoints(15); 285 | playGoodSound = true; 286 | break; 287 | case 'treasure4': 288 | this.game.addPoints(20); 289 | playGoodSound = true; 290 | break; 291 | case 'treasure0': 292 | this.game.addPoints(25); 293 | playGoodSound = true; 294 | break; 295 | case 'treasure1': 296 | this.game.addPoints(30); 297 | playGoodSound = true; 298 | break; 299 | case 'treasure5': 300 | this.game.addPoints(80); 301 | playGoodSound = true; 302 | break; 303 | case 'treasure3': 304 | this.game.addPoints(100); 305 | playGoodSound = true; 306 | break; 307 | case 'treasure6': 308 | this.game.addTime(15); 309 | playGoodSound = true; 310 | break; 311 | case 'scorpion': 312 | playHurtSound = true; 313 | this.last_sprite = this.sprite; 314 | this.sprite = 'hurt'; 315 | this.canMove = false; 316 | setTimeout(() => { 317 | this.sprite = this.last_sprite; 318 | this.canMove = true; 319 | }, 1000); 320 | break; 321 | case 'fire': 322 | this.burn(); 323 | break; 324 | } 325 | }); 326 | if (playGoodSound) { 327 | const rate = this.game.score >= this.game.goal ? 1.25 : 1; 328 | this.assets.playSound('good', rate); 329 | } 330 | if (playHurtSound) { 331 | this.assets.playSound('hurt'); 332 | } 333 | }); 334 | 335 | } 336 | } 337 | 338 | export default new Player(); -------------------------------------------------------------------------------- /app-package/ads-sdk.v3.min.js: -------------------------------------------------------------------------------- 1 | var _0x5f52=['style','position','absolute','left','-1000%','top','addEventListener','focus','blur','appendChild','dataset','success','mozApps','onsuccess','result','manifestURL','getPrototypeOf','target','getOwnPropertyNames','manifest','innerWidth','location','origin','getBoundingClientRect','right','bottom','height','innerHeight','open','vfsAdId','relative','setAttribute','cssText','tabindex','classList','navClass','display','background','width','keydownhandler','stopPropagation','keydown','key','parse','data','event','indexOf','apply','INVOKE_API_FAILED','activeElement','preventDefault','message','ONREADY_FUNC_MISSING','@babel/helpers\x20-\x20typeof','function','iterator','symbol','constructor','1.3.3','object','has','https://ssp.kaiads.com','/static/v3/frame.html?','body','0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ','map','join','adConfig','ignoreKeys','listeners','reject','onerror','error','destroy','wrap','remove','ready','timeout','onready','post','push','frame','AD_IFRAME_GONE','length','args','contentWindow','postMessage','stringify','AD_REQ_TIMED_OUT','container','isFullscreen','navigator','onLine','concat','&o=','createElement','iframe','div'];(function(_0x1196c6,_0x44e6fa){var _0x18d0d0=function(_0x300794){while(--_0x300794){_0x1196c6['push'](_0x1196c6['shift']());}};_0x18d0d0(++_0x44e6fa);}(_0x5f52,0xf9));var _0x44c0=function(_0x1cb19f,_0xca8227){_0x1cb19f=_0x1cb19f-0x0;var _0x1741b6=_0x5f52[_0x1cb19f];return _0x1741b6;};'use strict';function _typeof(_0x42782b){_0x44c0('0x0');if(typeof Symbol===_0x44c0('0x1')&&typeof Symbol[_0x44c0('0x2')]===_0x44c0('0x3')){_typeof=function _typeof(_0x42782b){return typeof _0x42782b;};}else{_typeof=function _typeof(_0x42782b){return _0x42782b&&typeof Symbol===_0x44c0('0x1')&&_0x42782b[_0x44c0('0x4')]===Symbol&&_0x42782b!==Symbol['prototype']?_0x44c0('0x3'):typeof _0x42782b;};}return _typeof(_0x42782b);}var getKaiAd=function(){var _0x94b928=_0x44c0('0x5');var _0x423dd9={'DOCBODY_NOT_READY':0x1,'ONREADY_FUNC_MISSING':0x2,'AD_DIMEN_TOO_SMALL':0x3,'AD_IFRAME_GONE':0x4,'AD_REQ_TIMED_OUT':0x5,'SERVER_SAID_NO_AD':0x6,'FREQ_CAPPING':0x7,'MISSING_W_H':0x8,'BAD_SERVER_RESPONSE':0x9,'INVOKE_API_FAILED':0xb,'CANNOT_PROCESS_RESPONSE':0xd,'NO_SERVER_RESPONSE':0xe,'INVALID_TEST_PARAM':0xf,'DISPLAY_CALLED_MULTIPLE_TIMES':0x10,'CANNOT_FETCH_SETTINGS':0x11,'UNKNOWN_API_CALLED':0x12,'SDK_CANNOT_LOAD':0x13,'UNSUPPORTED_SDK_VER':0x14};var _0x2dcf60=function _0x2dcf60(){var _0x1a7be1=new WeakSet();return function(_0x1172dd,_0x174bc7){if(_typeof(_0x174bc7)===_0x44c0('0x6')&&_0x174bc7!==null){if(_0x1a7be1[_0x44c0('0x7')](_0x174bc7)){return;}_0x1a7be1['add'](_0x174bc7);}return _0x174bc7;};};var _0x4854b6=_0x44c0('0x8');var _0x192215=_0x4854b6+_0x44c0('0x9');var _0x3eebf6={};var _0x3133bc=0xea60;var _0x473100={};var _0x5907aa=function _0x5907aa(){return document[_0x44c0('0xa')];};var _0x46cd9c=function _0x46cd9c(){var _0x2ec345=crypto['getRandomValues'](new Uint16Array(0x20));var _0x375da4=_0x44c0('0xb');var _0x218ae3=_0x375da4['length'];return[][_0x44c0('0xc')]['call'](_0x2ec345,function(_0x53fb33){return _0x375da4[_0x53fb33%_0x218ae3];})[_0x44c0('0xd')]('');};var _0x151aba=_0x46cd9c();var _0x4b78cd=function _0x4b78cd(_0x336f6b){var _0x14b51f={};_0x14b51f[_0x44c0('0xe')]=_0x336f6b;_0x14b51f['id']=_0x46cd9c();_0x14b51f[_0x44c0('0xf')]=[];_0x3eebf6[_0x14b51f['id']]=_0x14b51f;_0x14b51f[_0x44c0('0x10')]={};_0x14b51f[_0x44c0('0x11')]=function(_0x5c01b8){var _0x5850f4=_0x14b51f[_0x44c0('0xe')][_0x44c0('0x12')]||console[_0x44c0('0x13')];_0x5850f4(_0x5c01b8);_0x14b51f[_0x44c0('0x14')]();};_0x14b51f['destroy']=function(){if(_0x14b51f[_0x44c0('0x15')]){_0x14b51f[_0x44c0('0x15')][_0x44c0('0x16')]();}delete _0x3eebf6[_0x14b51f['id']];};_0x14b51f[_0x44c0('0x17')]=function(){clearTimeout(_0x14b51f[_0x44c0('0x18')]);_0x14b51f[_0x44c0('0xe')][_0x44c0('0x19')]({'call':function call(_0x395a5a,_0x11f702){_0x14b51f[_0x44c0('0x1a')](_0x395a5a,_0x11f702);},'on':function on(_0x20fccd,_0x28e097){if(!_0x14b51f[_0x44c0('0x10')][_0x20fccd]){_0x14b51f[_0x44c0('0x10')][_0x20fccd]=[];}_0x14b51f[_0x44c0('0x10')][_0x20fccd][_0x44c0('0x1b')](_0x28e097);}});};_0x14b51f[_0x44c0('0x1a')]=function(_0x56acc5){if(!_0x14b51f[_0x44c0('0x1c')]||!_0x14b51f[_0x44c0('0x1c')]['contentWindow']){_0x14b51f['reject'](_0x423dd9[_0x44c0('0x1d')]);}else{var _0x388e6c={};_0x388e6c['id']=_0x14b51f['id'];_0x388e6c['event']=_0x56acc5;for(var _0x45b74f=arguments[_0x44c0('0x1e')],_0x170042=new Array(_0x45b74f>0x1?_0x45b74f-0x1:0x0),_0x39e44a=0x1;_0x39e44a<_0x45b74f;_0x39e44a++){_0x170042[_0x39e44a-0x1]=arguments[_0x39e44a];}_0x388e6c[_0x44c0('0x1f')]=_0x170042;_0x14b51f['frame'][_0x44c0('0x20')][_0x44c0('0x21')](JSON[_0x44c0('0x22')](_0x388e6c,_0x2dcf60()),_0x4854b6);}};_0x14b51f[_0x44c0('0x18')]=function(){_0x14b51f[_0x44c0('0xe')]['timeout']=_0x14b51f['adConfig'][_0x44c0('0x18')]||_0x3133bc;return setTimeout(function(){_0x14b51f[_0x44c0('0x11')](_0x423dd9[_0x44c0('0x23')]);},_0x14b51f[_0x44c0('0xe')][_0x44c0('0x18')]);}();_0x14b51f[_0x44c0('0x24')]=function(){var _0x13a3be=_0x14b51f[_0x44c0('0xe')][_0x44c0('0x24')];if(!_0x13a3be){_0x13a3be=_0x5907aa();_0x14b51f['isFullscreen']=_0x14b51f[_0x44c0('0xe')][_0x44c0('0x25')]=0x1;}return _0x13a3be;}();_0x14b51f[_0x44c0('0x1c')]=function(){if(window[_0x44c0('0x26')][_0x44c0('0x27')]){var _0x477604=encodeURIComponent(document['location']['origin']);var _0x22c826=''[_0x44c0('0x28')](_0x192215,'i=')[_0x44c0('0x28')](_0x14b51f['id'],'&s=')['concat'](_0x151aba,_0x44c0('0x29'))[_0x44c0('0x28')](_0x477604);var _0x17394a=document[_0x44c0('0x2a')](_0x44c0('0x2b'));_0x17394a['setAttribute']('src',_0x22c826);var _0x2f4778=document['createElement'](_0x44c0('0x2c'));_0x2f4778['dataset'][_0x151aba]=_0x14b51f['id'];_0x2f4778[_0x44c0('0x2d')][_0x44c0('0x2e')]=_0x44c0('0x2f');_0x2f4778[_0x44c0('0x2d')][_0x44c0('0x30')]=_0x44c0('0x31');_0x2f4778[_0x44c0('0x2d')][_0x44c0('0x32')]='0px';_0x14b51f[_0x44c0('0x15')]=_0x2f4778;if(!_0x14b51f[_0x44c0('0x25')]){_0x2f4778[_0x44c0('0x33')](_0x44c0('0x34'),function(_0x1408b9){return _0x14b51f[_0x44c0('0x1a')](_0x44c0('0x34'));});_0x2f4778['addEventListener'](_0x44c0('0x35'),function(_0xf503e8){return _0x14b51f['post'](_0x44c0('0x35'));});}_0x2f4778['appendChild'](_0x17394a);_0x14b51f[_0x44c0('0x24')][_0x44c0('0x36')](_0x2f4778);_0x2f4778[_0x44c0('0x37')][_0x151aba]=_0x14b51f['id'];return _0x17394a;}return null;}();};var _0x2e3cca=function _0x2e3cca(_0x59a6dd){return{'___API___postGetSettings':function ___API___postGetSettings(_0x5a98d7){_0x59a6dd[_0x44c0('0x1a')](_0x5a98d7,_0x44c0('0x38'),_0x59a6dd[_0x44c0('0xe')]);},'___API___postGetManifestURL':function ___API___postGetManifestURL(_0x4ec8fb){navigator[_0x44c0('0x39')]['getSelf']()[_0x44c0('0x3a')]=function(_0x566c47){_0x59a6dd[_0x44c0('0x1a')](_0x4ec8fb,_0x44c0('0x38'),_0x566c47['target'][_0x44c0('0x3b')][_0x44c0('0x3c')]);};},'___API___postGetManifest':function ___API___postGetManifest(_0x5a4441){navigator[_0x44c0('0x39')]['getSelf']()['onsuccess']=function(_0x3d328f){var _0x140a11=Object[_0x44c0('0x3d')](_0x3d328f[_0x44c0('0x3e')][_0x44c0('0x3b')]);var _0xe62b57=Object[_0x44c0('0x3f')](_0x140a11);var _0x4c0ac7=JSON[_0x44c0('0x22')](_0x3d328f[_0x44c0('0x3e')]['result'],_0xe62b57);_0x59a6dd[_0x44c0('0x1a')](_0x5a4441,_0x44c0('0x38'),_0x4c0ac7,_0x3d328f[_0x44c0('0x3e')][_0x44c0('0x3b')][_0x44c0('0x40')]);};},'___API___postGetFullscreenDimension':function ___API___postGetFullscreenDimension(_0x3b6372){_0x59a6dd[_0x44c0('0x1a')](_0x3b6372,_0x44c0('0x38'),window['innerHeight'],window[_0x44c0('0x41')]);},'___API___postGetOrigin':function ___API___postGetOrigin(_0x34792f){_0x59a6dd[_0x44c0('0x1a')](_0x34792f,'success',document['location']['href'],document[_0x44c0('0x42')][_0x44c0('0x43')]);},'___API___postError':function ___API___postError(_0x4c8d8e){_0x59a6dd[_0x44c0('0xe')][_0x44c0('0x12')](_0x4c8d8e);},'___API___postReject':function ___API___postReject(_0x28e33f){_0x59a6dd[_0x44c0('0x11')](_0x28e33f);clearTimeout(_0x59a6dd[_0x44c0('0x18')]);},'___API___postGetVisibility':function ___API___postGetVisibility(_0x2b3b1c){var _0x1a6ec1=_0x59a6dd[_0x44c0('0x1c')][_0x44c0('0x44')]();_0x59a6dd[_0x44c0('0x1a')](_0x2b3b1c,_0x1a6ec1[_0x44c0('0x32')],_0x1a6ec1[_0x44c0('0x30')],_0x1a6ec1[_0x44c0('0x45')],_0x1a6ec1[_0x44c0('0x46')],_0x1a6ec1['width'],_0x1a6ec1[_0x44c0('0x47')],window['innerWidth'],window[_0x44c0('0x48')]);},'___API___postDestroyAd':function ___API___postDestroyAd(_0xc04f44){_0x59a6dd[_0x44c0('0x14')]();},'___API___postOpenWin':function ___API___postOpenWin(_0x36a4b0,_0x31bf0e){window[_0x44c0('0x49')](_0x31bf0e);},'__API__postAssignLocation':function __API__postAssignLocation(_0x55a633,_0x3e89e8){window[_0x44c0('0x42')]=_0x3e89e8;},'___API___postDisplayFullscreenAd':function ___API___postDisplayFullscreenAd(_0x1c1497,_0x29b108,_0x12124d){_0x473100[_0x44c0('0x4a')]=_0x29b108;_0x59a6dd[_0x44c0('0x24')][_0x44c0('0x2d')]['position']=_0x44c0('0x4b');_0x59a6dd[_0x44c0('0x15')][_0x44c0('0x4c')]('tabindex',0x0);_0x59a6dd[_0x44c0('0x15')][_0x44c0('0x2d')][_0x44c0('0x4d')]=_0x12124d;_0x59a6dd['frame']['style'][_0x44c0('0x4d')]=_0x12124d;},'___API___postDisplayBannerAd':function ___API___postDisplayBannerAd(_0x27c472,_0x5db519,_0x14aa81,_0x533cca,_0x740321,_0x497eb8){_0x59a6dd[_0x44c0('0x15')]['setAttribute'](_0x44c0('0x4e'),_0x5db519[_0x44c0('0x4e')]||0x0);_0x59a6dd[_0x44c0('0x15')][_0x44c0('0x4f')]['add'](_0x5db519[_0x44c0('0x50')]||'');_0x59a6dd[_0x44c0('0x24')]['style']['display']=_0x5db519[_0x44c0('0x51')];_0x59a6dd[_0x44c0('0x24')]['style'][_0x44c0('0x52')]=_0x533cca;_0x59a6dd[_0x44c0('0x24')][_0x44c0('0x2d')][_0x44c0('0x2e')]='relative';_0x59a6dd['wrap'][_0x44c0('0x2d')][_0x44c0('0x4d')]=_0x14aa81;_0x59a6dd['frame'][_0x44c0('0x2d')][_0x44c0('0x4d')]=_0x14aa81;if(_0x740321){_0x59a6dd[_0x44c0('0x24')][_0x44c0('0x2d')][_0x44c0('0x53')]=_0x740321+'px';}if(_0x497eb8){_0x59a6dd[_0x44c0('0x24')][_0x44c0('0x2d')]['height']=_0x497eb8+'px';}},'___API___postSetIgnoreKeys':function ___API___postSetIgnoreKeys(_0x59e8ef,_0x330730,_0x27665d){_0x59a6dd[_0x44c0('0xf')]=_0x330730;if(_0x27665d){_0x59a6dd[_0x44c0('0x17')]();}},'___API___postAdFocus':function ___API___postAdFocus(_0x21b0d3){_0x59a6dd['wrap'][_0x44c0('0x34')]();},'___API___postMaskGlobalFSListeners':function ___API___postMaskGlobalFSListeners(){_0x59a6dd[_0x44c0('0x15')][_0x44c0('0x54')]=function(_0x519c24){_0x519c24[_0x44c0('0x55')]();_0x59a6dd['post'](_0x44c0('0x56'),_0x519c24[_0x44c0('0x57')],-0x1);};_0x59a6dd[_0x44c0('0x15')][_0x44c0('0x33')]('keydown',_0x59a6dd['wrap'][_0x44c0('0x54')]);},'___API___postGetSDKVersion':function ___API___postGetSDKVersion(_0x4de303){_0x59a6dd[_0x44c0('0x1a')](_0x4de303,_0x44c0('0x38'),_0x94b928);}};};var _0x572ec1=function _0x572ec1(_0x3f0579){if(!_0x3f0579||!_0x3f0579[_0x44c0('0x43')]||_0x3f0579[_0x44c0('0x43')]!==_0x4854b6){return;}var _0x51ef09=JSON[_0x44c0('0x58')](_0x3f0579[_0x44c0('0x59')]);if(_0x3eebf6[_0x51ef09['id']]){var _0x42cc27=_0x3eebf6[_0x51ef09['id']];if(_0x51ef09[_0x44c0('0x5a')]&&_0x51ef09['event'][_0x44c0('0x5b')]('___API___')===0x0){var _0x2cbe1e=_0x2e3cca(_0x42cc27)[_0x51ef09[_0x44c0('0x5a')]];if(_0x2cbe1e){try{_0x2cbe1e[_0x44c0('0x5c')](_0x42cc27,_0x51ef09[_0x44c0('0x1f')]);}catch(_0x9f210e){_0x42cc27['post'](_0x51ef09[_0x44c0('0x5a')],_0x44c0('0x13'),_0x423dd9[_0x44c0('0x5d')]);}}else{_0x42cc27[_0x44c0('0xe')][_0x44c0('0x12')](_0x423dd9['UNKNOWN_API_CALLED']);}}if(_0x42cc27[_0x44c0('0x10')][_0x51ef09['event']]){_0x42cc27['listeners'][_0x51ef09[_0x44c0('0x5a')]]['forEach'](function(_0x10f2b8){return _0x10f2b8(_0x51ef09[_0x44c0('0x1f')]);});}}};var _0x420784=function _0x420784(_0x123b93){var _0x5767fc;if(_0x473100[_0x44c0('0x4a')]&&_0x3eebf6[_0x473100[_0x44c0('0x4a')]]){_0x5767fc=_0x3eebf6[_0x473100[_0x44c0('0x4a')]];}else{var _0x18c41f=document[_0x44c0('0x5e')];var _0x35d868=_0x18c41f[_0x44c0('0x37')][_0x151aba];if(!_0x18c41f||!_0x35d868){return;}_0x5767fc=_0x3eebf6[_0x35d868];}if(!_0x5767fc){return;}if(_0x5767fc['ignoreKeys'][_0x44c0('0x5b')](_0x123b93[_0x44c0('0x57')])>-0x1){return;}_0x5767fc[_0x44c0('0x1a')](_0x44c0('0x56'),_0x123b93['key'],-0x1);_0x123b93[_0x44c0('0x5f')]();_0x123b93['stopPropagation']();};window[_0x44c0('0x33')](_0x44c0('0x60'),_0x572ec1);window['addEventListener'](_0x44c0('0x56'),_0x420784);return function(_0x975bd8){var _0xa0c139=_0x975bd8[_0x44c0('0x12')]||console[_0x44c0('0x13')];if(!_0x5907aa()){_0xa0c139(_0x423dd9['DOCBODY_NOT_READY']);}else if(!_0x975bd8[_0x44c0('0x19')]){_0xa0c139(_0x423dd9[_0x44c0('0x61')]);}else{_0x4b78cd(_0x975bd8);}};}(); -------------------------------------------------------------------------------- /app-core/libs/m4.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014, Gregg Tavares. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Gregg Tavares. nor the names of his 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * Various 3d math functions. 34 | * 35 | * @module webgl-3d-math 36 | */ 37 | 38 | export default (function() { 39 | 40 | /** 41 | * An array or typed array with 3 values. 42 | * @typedef {number[]|TypedArray} Vector3 43 | * @memberOf module:webgl-3d-math 44 | */ 45 | 46 | /** 47 | * An array or typed array with 4 values. 48 | * @typedef {number[]|TypedArray} Vector4 49 | * @memberOf module:webgl-3d-math 50 | */ 51 | 52 | /** 53 | * An array or typed array with 16 values. 54 | * @typedef {number[]|TypedArray} Matrix4 55 | * @memberOf module:webgl-3d-math 56 | */ 57 | 58 | 59 | let MatType = Float32Array; 60 | 61 | /** 62 | * Sets the type this library creates for a Mat4 63 | * @param {constructor} Ctor the constructor for the type. Either `Float32Array` or `Array` 64 | * @return {constructor} previous constructor for Mat4 65 | */ 66 | function setDefaultType(Ctor) { 67 | const OldType = MatType; 68 | MatType = Ctor; 69 | return OldType; 70 | } 71 | 72 | /** 73 | * Takes two 4-by-4 matrices, a and b, and computes the product in the order 74 | * that pre-composes b with a. In other words, the matrix returned will 75 | * transform by b first and then a. Note this is subtly different from just 76 | * multiplying the matrices together. For given a and b, this function returns 77 | * the same object in both row-major and column-major mode. 78 | * @param {Matrix4} a A matrix. 79 | * @param {Matrix4} b A matrix. 80 | * @param {Matrix4} [dst] optional matrix to store result 81 | * @return {Matrix4} dst or a new matrix if none provided 82 | */ 83 | function multiply(a, b, dst) { 84 | dst = dst || new MatType(16); 85 | var b00 = b[0 * 4 + 0]; 86 | var b01 = b[0 * 4 + 1]; 87 | var b02 = b[0 * 4 + 2]; 88 | var b03 = b[0 * 4 + 3]; 89 | var b10 = b[1 * 4 + 0]; 90 | var b11 = b[1 * 4 + 1]; 91 | var b12 = b[1 * 4 + 2]; 92 | var b13 = b[1 * 4 + 3]; 93 | var b20 = b[2 * 4 + 0]; 94 | var b21 = b[2 * 4 + 1]; 95 | var b22 = b[2 * 4 + 2]; 96 | var b23 = b[2 * 4 + 3]; 97 | var b30 = b[3 * 4 + 0]; 98 | var b31 = b[3 * 4 + 1]; 99 | var b32 = b[3 * 4 + 2]; 100 | var b33 = b[3 * 4 + 3]; 101 | var a00 = a[0 * 4 + 0]; 102 | var a01 = a[0 * 4 + 1]; 103 | var a02 = a[0 * 4 + 2]; 104 | var a03 = a[0 * 4 + 3]; 105 | var a10 = a[1 * 4 + 0]; 106 | var a11 = a[1 * 4 + 1]; 107 | var a12 = a[1 * 4 + 2]; 108 | var a13 = a[1 * 4 + 3]; 109 | var a20 = a[2 * 4 + 0]; 110 | var a21 = a[2 * 4 + 1]; 111 | var a22 = a[2 * 4 + 2]; 112 | var a23 = a[2 * 4 + 3]; 113 | var a30 = a[3 * 4 + 0]; 114 | var a31 = a[3 * 4 + 1]; 115 | var a32 = a[3 * 4 + 2]; 116 | var a33 = a[3 * 4 + 3]; 117 | dst[ 0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; 118 | dst[ 1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; 119 | dst[ 2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; 120 | dst[ 3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; 121 | dst[ 4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; 122 | dst[ 5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; 123 | dst[ 6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; 124 | dst[ 7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; 125 | dst[ 8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; 126 | dst[ 9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; 127 | dst[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; 128 | dst[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; 129 | dst[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; 130 | dst[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; 131 | dst[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; 132 | dst[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; 133 | return dst; 134 | } 135 | 136 | 137 | /** 138 | * adds 2 vectors3s 139 | * @param {Vector3} a a 140 | * @param {Vector3} b b 141 | * @param {Vector3} dst optional vector3 to store result 142 | * @return {Vector3} dst or new Vector3 if not provided 143 | * @memberOf module:webgl-3d-math 144 | */ 145 | function addVectors(a, b, dst) { 146 | dst = dst || new MatType(3); 147 | dst[0] = a[0] + b[0]; 148 | dst[1] = a[1] + b[1]; 149 | dst[2] = a[2] + b[2]; 150 | return dst; 151 | } 152 | 153 | /** 154 | * subtracts 2 vectors3s 155 | * @param {Vector3} a a 156 | * @param {Vector3} b b 157 | * @param {Vector3} dst optional vector3 to store result 158 | * @return {Vector3} dst or new Vector3 if not provided 159 | * @memberOf module:webgl-3d-math 160 | */ 161 | function subtractVectors(a, b, dst) { 162 | dst = dst || new MatType(3); 163 | dst[0] = a[0] - b[0]; 164 | dst[1] = a[1] - b[1]; 165 | dst[2] = a[2] - b[2]; 166 | return dst; 167 | } 168 | 169 | /** 170 | * normalizes a vector. 171 | * @param {Vector3} v vector to normalize 172 | * @param {Vector3} dst optional vector3 to store result 173 | * @return {Vector3} dst or new Vector3 if not provided 174 | * @memberOf module:webgl-3d-math 175 | */ 176 | function normalize(v, dst) { 177 | dst = dst || new MatType(3); 178 | var length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); 179 | // make sure we don't divide by 0. 180 | if (length > 0.00001) { 181 | dst[0] = v[0] / length; 182 | dst[1] = v[1] / length; 183 | dst[2] = v[2] / length; 184 | } 185 | return dst; 186 | } 187 | 188 | /** 189 | * Computes the length of a vector 190 | * @param {Vector3} v vector to take length of 191 | * @return {number} length of vector 192 | */ 193 | function length(v) { 194 | return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); 195 | } 196 | 197 | /** 198 | * Computes the cross product of 2 vectors3s 199 | * @param {Vector3} a a 200 | * @param {Vector3} b b 201 | * @param {Vector3} dst optional vector3 to store result 202 | * @return {Vector3} dst or new Vector3 if not provided 203 | * @memberOf module:webgl-3d-math 204 | */ 205 | function cross(a, b, dst) { 206 | dst = dst || new MatType(3); 207 | dst[0] = a[1] * b[2] - a[2] * b[1]; 208 | dst[1] = a[2] * b[0] - a[0] * b[2]; 209 | dst[2] = a[0] * b[1] - a[1] * b[0]; 210 | return dst; 211 | } 212 | 213 | /** 214 | * Computes the dot product of two vectors; assumes both vectors have 215 | * three entries. 216 | * @param {Vector3} a Operand vector. 217 | * @param {Vector3} b Operand vector. 218 | * @return {number} dot product 219 | * @memberOf module:webgl-3d-math 220 | */ 221 | function dot(a, b) { 222 | return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); 223 | } 224 | 225 | /** 226 | * Computes the distance squared between 2 points 227 | * @param {Vector3} a 228 | * @param {Vector3} b 229 | * @return {number} distance squared between a and b 230 | */ 231 | function distanceSq(a, b) { 232 | const dx = a[0] - b[0]; 233 | const dy = a[1] - b[1]; 234 | const dz = a[2] - b[2]; 235 | return dx * dx + dy * dy + dz * dz; 236 | } 237 | 238 | /** 239 | * Computes the distance between 2 points 240 | * @param {Vector3} a 241 | * @param {Vector3} b 242 | * @return {number} distance between a and b 243 | */ 244 | function distance(a, b) { 245 | return Math.sqrt(distanceSq(a, b)); 246 | } 247 | 248 | /** 249 | * Makes an identity matrix. 250 | * @param {Matrix4} [dst] optional matrix to store result 251 | * @return {Matrix4} dst or a new matrix if none provided 252 | * @memberOf module:webgl-3d-math 253 | */ 254 | function identity(dst) { 255 | dst = dst || new MatType(16); 256 | 257 | dst[ 0] = 1; 258 | dst[ 1] = 0; 259 | dst[ 2] = 0; 260 | dst[ 3] = 0; 261 | dst[ 4] = 0; 262 | dst[ 5] = 1; 263 | dst[ 6] = 0; 264 | dst[ 7] = 0; 265 | dst[ 8] = 0; 266 | dst[ 9] = 0; 267 | dst[10] = 1; 268 | dst[11] = 0; 269 | dst[12] = 0; 270 | dst[13] = 0; 271 | dst[14] = 0; 272 | dst[15] = 1; 273 | 274 | return dst; 275 | } 276 | 277 | /** 278 | * Transposes a matrix. 279 | * @param {Matrix4} m matrix to transpose. 280 | * @param {Matrix4} [dst] optional matrix to store result 281 | * @return {Matrix4} dst or a new matrix if none provided 282 | * @memberOf module:webgl-3d-math 283 | */ 284 | function transpose(m, dst) { 285 | dst = dst || new MatType(16); 286 | 287 | dst[ 0] = m[0]; 288 | dst[ 1] = m[4]; 289 | dst[ 2] = m[8]; 290 | dst[ 3] = m[12]; 291 | dst[ 4] = m[1]; 292 | dst[ 5] = m[5]; 293 | dst[ 6] = m[9]; 294 | dst[ 7] = m[13]; 295 | dst[ 8] = m[2]; 296 | dst[ 9] = m[6]; 297 | dst[10] = m[10]; 298 | dst[11] = m[14]; 299 | dst[12] = m[3]; 300 | dst[13] = m[7]; 301 | dst[14] = m[11]; 302 | dst[15] = m[15]; 303 | 304 | return dst; 305 | } 306 | 307 | /** 308 | * Creates a lookAt matrix. 309 | * This is a world matrix for a camera. In other words it will transform 310 | * from the origin to a place and orientation in the world. For a view 311 | * matrix take the inverse of this. 312 | * @param {Vector3} cameraPosition position of the camera 313 | * @param {Vector3} target position of the target 314 | * @param {Vector3} up direction 315 | * @param {Matrix4} [dst] optional matrix to store result 316 | * @return {Matrix4} dst or a new matrix if none provided 317 | * @memberOf module:webgl-3d-math 318 | */ 319 | function lookAt(cameraPosition, target, up, dst) { 320 | dst = dst || new MatType(16); 321 | var zAxis = normalize( 322 | subtractVectors(cameraPosition, target)); 323 | var xAxis = normalize(cross(up, zAxis)); 324 | var yAxis = normalize(cross(zAxis, xAxis)); 325 | 326 | dst[ 0] = xAxis[0]; 327 | dst[ 1] = xAxis[1]; 328 | dst[ 2] = xAxis[2]; 329 | dst[ 3] = 0; 330 | dst[ 4] = yAxis[0]; 331 | dst[ 5] = yAxis[1]; 332 | dst[ 6] = yAxis[2]; 333 | dst[ 7] = 0; 334 | dst[ 8] = zAxis[0]; 335 | dst[ 9] = zAxis[1]; 336 | dst[10] = zAxis[2]; 337 | dst[11] = 0; 338 | dst[12] = cameraPosition[0]; 339 | dst[13] = cameraPosition[1]; 340 | dst[14] = cameraPosition[2]; 341 | dst[15] = 1; 342 | 343 | return dst; 344 | } 345 | 346 | /** 347 | * Computes a 4-by-4 perspective transformation matrix given the angular height 348 | * of the frustum, the aspect ratio, and the near and far clipping planes. The 349 | * arguments define a frustum extending in the negative z direction. The given 350 | * angle is the vertical angle of the frustum, and the horizontal angle is 351 | * determined to produce the given aspect ratio. The arguments near and far are 352 | * the distances to the near and far clipping planes. Note that near and far 353 | * are not z coordinates, but rather they are distances along the negative 354 | * z-axis. The matrix generated sends the viewing frustum to the unit box. 355 | * We assume a unit box extending from -1 to 1 in the x and y dimensions and 356 | * from -1 to 1 in the z dimension. 357 | * @param {number} fieldOfViewInRadians field of view in y axis. 358 | * @param {number} aspect aspect of viewport (width / height) 359 | * @param {number} near near Z clipping plane 360 | * @param {number} far far Z clipping plane 361 | * @param {Matrix4} [dst] optional matrix to store result 362 | * @return {Matrix4} dst or a new matrix if none provided 363 | * @memberOf module:webgl-3d-math 364 | */ 365 | function perspective(fieldOfViewInRadians, aspect, near, far, dst) { 366 | dst = dst || new MatType(16); 367 | var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians); 368 | var rangeInv = 1.0 / (near - far); 369 | 370 | dst[ 0] = f / aspect; 371 | dst[ 1] = 0; 372 | dst[ 2] = 0; 373 | dst[ 3] = 0; 374 | dst[ 4] = 0; 375 | dst[ 5] = f; 376 | dst[ 6] = 0; 377 | dst[ 7] = 0; 378 | dst[ 8] = 0; 379 | dst[ 9] = 0; 380 | dst[10] = (near + far) * rangeInv; 381 | dst[11] = -1; 382 | dst[12] = 0; 383 | dst[13] = 0; 384 | dst[14] = near * far * rangeInv * 2; 385 | dst[15] = 0; 386 | 387 | return dst; 388 | } 389 | 390 | /** 391 | * Computes a 4-by-4 orthographic projection matrix given the coordinates of the 392 | * planes defining the axis-aligned, box-shaped viewing volume. The matrix 393 | * generated sends that box to the unit box. Note that although left and right 394 | * are x coordinates and bottom and top are y coordinates, near and far 395 | * are not z coordinates, but rather they are distances along the negative 396 | * z-axis. We assume a unit box extending from -1 to 1 in the x and y 397 | * dimensions and from -1 to 1 in the z dimension. 398 | * @param {number} left The x coordinate of the left plane of the box. 399 | * @param {number} right The x coordinate of the right plane of the box. 400 | * @param {number} bottom The y coordinate of the bottom plane of the box. 401 | * @param {number} top The y coordinate of the right plane of the box. 402 | * @param {number} near The negative z coordinate of the near plane of the box. 403 | * @param {number} far The negative z coordinate of the far plane of the box. 404 | * @param {Matrix4} [dst] optional matrix to store result 405 | * @return {Matrix4} dst or a new matrix if none provided 406 | * @memberOf module:webgl-3d-math 407 | */ 408 | function orthographic(left, right, bottom, top, near, far, dst) { 409 | dst = dst || new MatType(16); 410 | 411 | dst[ 0] = 2 / (right - left); 412 | dst[ 1] = 0; 413 | dst[ 2] = 0; 414 | dst[ 3] = 0; 415 | dst[ 4] = 0; 416 | dst[ 5] = 2 / (top - bottom); 417 | dst[ 6] = 0; 418 | dst[ 7] = 0; 419 | dst[ 8] = 0; 420 | dst[ 9] = 0; 421 | dst[10] = 2 / (near - far); 422 | dst[11] = 0; 423 | dst[12] = (left + right) / (left - right); 424 | dst[13] = (bottom + top) / (bottom - top); 425 | dst[14] = (near + far) / (near - far); 426 | dst[15] = 1; 427 | 428 | return dst; 429 | } 430 | 431 | /** 432 | * Computes a 4-by-4 perspective transformation matrix given the left, right, 433 | * top, bottom, near and far clipping planes. The arguments define a frustum 434 | * extending in the negative z direction. The arguments near and far are the 435 | * distances to the near and far clipping planes. Note that near and far are not 436 | * z coordinates, but rather they are distances along the negative z-axis. The 437 | * matrix generated sends the viewing frustum to the unit box. We assume a unit 438 | * box extending from -1 to 1 in the x and y dimensions and from -1 to 1 in the z 439 | * dimension. 440 | * @param {number} left The x coordinate of the left plane of the box. 441 | * @param {number} right The x coordinate of the right plane of the box. 442 | * @param {number} bottom The y coordinate of the bottom plane of the box. 443 | * @param {number} top The y coordinate of the right plane of the box. 444 | * @param {number} near The negative z coordinate of the near plane of the box. 445 | * @param {number} far The negative z coordinate of the far plane of the box. 446 | * @param {Matrix4} [dst] optional matrix to store result 447 | * @return {Matrix4} dst or a new matrix if none provided 448 | * @memberOf module:webgl-3d-math 449 | */ 450 | function frustum(left, right, bottom, top, near, far, dst) { 451 | dst = dst || new MatType(16); 452 | 453 | var dx = right - left; 454 | var dy = top - bottom; 455 | var dz = far - near; 456 | 457 | dst[ 0] = 2 * near / dx; 458 | dst[ 1] = 0; 459 | dst[ 2] = 0; 460 | dst[ 3] = 0; 461 | dst[ 4] = 0; 462 | dst[ 5] = 2 * near / dy; 463 | dst[ 6] = 0; 464 | dst[ 7] = 0; 465 | dst[ 8] = (left + right) / dx; 466 | dst[ 9] = (top + bottom) / dy; 467 | dst[10] = -(far + near) / dz; 468 | dst[11] = -1; 469 | dst[12] = 0; 470 | dst[13] = 0; 471 | dst[14] = -2 * near * far / dz; 472 | dst[15] = 0; 473 | 474 | return dst; 475 | } 476 | 477 | /** 478 | * Makes a translation matrix 479 | * @param {number} tx x translation. 480 | * @param {number} ty y translation. 481 | * @param {number} tz z translation. 482 | * @param {Matrix4} [dst] optional matrix to store result 483 | * @return {Matrix4} dst or a new matrix if none provided 484 | * @memberOf module:webgl-3d-math 485 | */ 486 | function translation(tx, ty, tz, dst) { 487 | dst = dst || new MatType(16); 488 | 489 | dst[ 0] = 1; 490 | dst[ 1] = 0; 491 | dst[ 2] = 0; 492 | dst[ 3] = 0; 493 | dst[ 4] = 0; 494 | dst[ 5] = 1; 495 | dst[ 6] = 0; 496 | dst[ 7] = 0; 497 | dst[ 8] = 0; 498 | dst[ 9] = 0; 499 | dst[10] = 1; 500 | dst[11] = 0; 501 | dst[12] = tx; 502 | dst[13] = ty; 503 | dst[14] = tz; 504 | dst[15] = 1; 505 | 506 | return dst; 507 | } 508 | 509 | /** 510 | * Multiply by translation matrix. 511 | * @param {Matrix4} m matrix to multiply 512 | * @param {number} tx x translation. 513 | * @param {number} ty y translation. 514 | * @param {number} tz z translation. 515 | * @param {Matrix4} [dst] optional matrix to store result 516 | * @return {Matrix4} dst or a new matrix if none provided 517 | * @memberOf module:webgl-3d-math 518 | */ 519 | function translate(m, tx, ty, tz, dst) { 520 | // This is the optimized version of 521 | // return multiply(m, translation(tx, ty, tz), dst); 522 | dst = dst || new MatType(16); 523 | 524 | var m00 = m[0]; 525 | var m01 = m[1]; 526 | var m02 = m[2]; 527 | var m03 = m[3]; 528 | var m10 = m[1 * 4 + 0]; 529 | var m11 = m[1 * 4 + 1]; 530 | var m12 = m[1 * 4 + 2]; 531 | var m13 = m[1 * 4 + 3]; 532 | var m20 = m[2 * 4 + 0]; 533 | var m21 = m[2 * 4 + 1]; 534 | var m22 = m[2 * 4 + 2]; 535 | var m23 = m[2 * 4 + 3]; 536 | var m30 = m[3 * 4 + 0]; 537 | var m31 = m[3 * 4 + 1]; 538 | var m32 = m[3 * 4 + 2]; 539 | var m33 = m[3 * 4 + 3]; 540 | 541 | if (m !== dst) { 542 | dst[ 0] = m00; 543 | dst[ 1] = m01; 544 | dst[ 2] = m02; 545 | dst[ 3] = m03; 546 | dst[ 4] = m10; 547 | dst[ 5] = m11; 548 | dst[ 6] = m12; 549 | dst[ 7] = m13; 550 | dst[ 8] = m20; 551 | dst[ 9] = m21; 552 | dst[10] = m22; 553 | dst[11] = m23; 554 | } 555 | 556 | dst[12] = m00 * tx + m10 * ty + m20 * tz + m30; 557 | dst[13] = m01 * tx + m11 * ty + m21 * tz + m31; 558 | dst[14] = m02 * tx + m12 * ty + m22 * tz + m32; 559 | dst[15] = m03 * tx + m13 * ty + m23 * tz + m33; 560 | 561 | return dst; 562 | } 563 | 564 | /** 565 | * Makes an x rotation matrix 566 | * @param {number} angleInRadians amount to rotate 567 | * @param {Matrix4} [dst] optional matrix to store result 568 | * @return {Matrix4} dst or a new matrix if none provided 569 | * @memberOf module:webgl-3d-math 570 | */ 571 | function xRotation(angleInRadians, dst) { 572 | dst = dst || new MatType(16); 573 | var c = Math.cos(angleInRadians); 574 | var s = Math.sin(angleInRadians); 575 | 576 | dst[ 0] = 1; 577 | dst[ 1] = 0; 578 | dst[ 2] = 0; 579 | dst[ 3] = 0; 580 | dst[ 4] = 0; 581 | dst[ 5] = c; 582 | dst[ 6] = s; 583 | dst[ 7] = 0; 584 | dst[ 8] = 0; 585 | dst[ 9] = -s; 586 | dst[10] = c; 587 | dst[11] = 0; 588 | dst[12] = 0; 589 | dst[13] = 0; 590 | dst[14] = 0; 591 | dst[15] = 1; 592 | 593 | return dst; 594 | } 595 | 596 | /** 597 | * Multiply by an x rotation matrix 598 | * @param {Matrix4} m matrix to multiply 599 | * @param {number} angleInRadians amount to rotate 600 | * @param {Matrix4} [dst] optional matrix to store result 601 | * @return {Matrix4} dst or a new matrix if none provided 602 | * @memberOf module:webgl-3d-math 603 | */ 604 | function xRotate(m, angleInRadians, dst) { 605 | // this is the optimized version of 606 | // return multiply(m, xRotation(angleInRadians), dst); 607 | dst = dst || new MatType(16); 608 | 609 | var m10 = m[4]; 610 | var m11 = m[5]; 611 | var m12 = m[6]; 612 | var m13 = m[7]; 613 | var m20 = m[8]; 614 | var m21 = m[9]; 615 | var m22 = m[10]; 616 | var m23 = m[11]; 617 | var c = Math.cos(angleInRadians); 618 | var s = Math.sin(angleInRadians); 619 | 620 | dst[4] = c * m10 + s * m20; 621 | dst[5] = c * m11 + s * m21; 622 | dst[6] = c * m12 + s * m22; 623 | dst[7] = c * m13 + s * m23; 624 | dst[8] = c * m20 - s * m10; 625 | dst[9] = c * m21 - s * m11; 626 | dst[10] = c * m22 - s * m12; 627 | dst[11] = c * m23 - s * m13; 628 | 629 | if (m !== dst) { 630 | dst[ 0] = m[ 0]; 631 | dst[ 1] = m[ 1]; 632 | dst[ 2] = m[ 2]; 633 | dst[ 3] = m[ 3]; 634 | dst[12] = m[12]; 635 | dst[13] = m[13]; 636 | dst[14] = m[14]; 637 | dst[15] = m[15]; 638 | } 639 | 640 | return dst; 641 | } 642 | 643 | /** 644 | * Makes an y rotation matrix 645 | * @param {number} angleInRadians amount to rotate 646 | * @param {Matrix4} [dst] optional matrix to store result 647 | * @return {Matrix4} dst or a new matrix if none provided 648 | * @memberOf module:webgl-3d-math 649 | */ 650 | function yRotation(angleInRadians, dst) { 651 | dst = dst || new MatType(16); 652 | var c = Math.cos(angleInRadians); 653 | var s = Math.sin(angleInRadians); 654 | 655 | dst[ 0] = c; 656 | dst[ 1] = 0; 657 | dst[ 2] = -s; 658 | dst[ 3] = 0; 659 | dst[ 4] = 0; 660 | dst[ 5] = 1; 661 | dst[ 6] = 0; 662 | dst[ 7] = 0; 663 | dst[ 8] = s; 664 | dst[ 9] = 0; 665 | dst[10] = c; 666 | dst[11] = 0; 667 | dst[12] = 0; 668 | dst[13] = 0; 669 | dst[14] = 0; 670 | dst[15] = 1; 671 | 672 | return dst; 673 | } 674 | 675 | /** 676 | * Multiply by an y rotation matrix 677 | * @param {Matrix4} m matrix to multiply 678 | * @param {number} angleInRadians amount to rotate 679 | * @param {Matrix4} [dst] optional matrix to store result 680 | * @return {Matrix4} dst or a new matrix if none provided 681 | * @memberOf module:webgl-3d-math 682 | */ 683 | function yRotate(m, angleInRadians, dst) { 684 | // this is the optimized version of 685 | // return multiply(m, yRotation(angleInRadians), dst); 686 | dst = dst || new MatType(16); 687 | 688 | var m00 = m[0 * 4 + 0]; 689 | var m01 = m[0 * 4 + 1]; 690 | var m02 = m[0 * 4 + 2]; 691 | var m03 = m[0 * 4 + 3]; 692 | var m20 = m[2 * 4 + 0]; 693 | var m21 = m[2 * 4 + 1]; 694 | var m22 = m[2 * 4 + 2]; 695 | var m23 = m[2 * 4 + 3]; 696 | var c = Math.cos(angleInRadians); 697 | var s = Math.sin(angleInRadians); 698 | 699 | dst[ 0] = c * m00 - s * m20; 700 | dst[ 1] = c * m01 - s * m21; 701 | dst[ 2] = c * m02 - s * m22; 702 | dst[ 3] = c * m03 - s * m23; 703 | dst[ 8] = c * m20 + s * m00; 704 | dst[ 9] = c * m21 + s * m01; 705 | dst[10] = c * m22 + s * m02; 706 | dst[11] = c * m23 + s * m03; 707 | 708 | if (m !== dst) { 709 | dst[ 4] = m[ 4]; 710 | dst[ 5] = m[ 5]; 711 | dst[ 6] = m[ 6]; 712 | dst[ 7] = m[ 7]; 713 | dst[12] = m[12]; 714 | dst[13] = m[13]; 715 | dst[14] = m[14]; 716 | dst[15] = m[15]; 717 | } 718 | 719 | return dst; 720 | } 721 | 722 | /** 723 | * Makes an z rotation matrix 724 | * @param {number} angleInRadians amount to rotate 725 | * @param {Matrix4} [dst] optional matrix to store result 726 | * @return {Matrix4} dst or a new matrix if none provided 727 | * @memberOf module:webgl-3d-math 728 | */ 729 | function zRotation(angleInRadians, dst) { 730 | dst = dst || new MatType(16); 731 | var c = Math.cos(angleInRadians); 732 | var s = Math.sin(angleInRadians); 733 | 734 | dst[ 0] = c; 735 | dst[ 1] = s; 736 | dst[ 2] = 0; 737 | dst[ 3] = 0; 738 | dst[ 4] = -s; 739 | dst[ 5] = c; 740 | dst[ 6] = 0; 741 | dst[ 7] = 0; 742 | dst[ 8] = 0; 743 | dst[ 9] = 0; 744 | dst[10] = 1; 745 | dst[11] = 0; 746 | dst[12] = 0; 747 | dst[13] = 0; 748 | dst[14] = 0; 749 | dst[15] = 1; 750 | 751 | return dst; 752 | } 753 | 754 | /** 755 | * Multiply by an z rotation matrix 756 | * @param {Matrix4} m matrix to multiply 757 | * @param {number} angleInRadians amount to rotate 758 | * @param {Matrix4} [dst] optional matrix to store result 759 | * @return {Matrix4} dst or a new matrix if none provided 760 | * @memberOf module:webgl-3d-math 761 | */ 762 | function zRotate(m, angleInRadians, dst) { 763 | // This is the optimized version of 764 | // return multiply(m, zRotation(angleInRadians), dst); 765 | dst = dst || new MatType(16); 766 | 767 | var m00 = m[0 * 4 + 0]; 768 | var m01 = m[0 * 4 + 1]; 769 | var m02 = m[0 * 4 + 2]; 770 | var m03 = m[0 * 4 + 3]; 771 | var m10 = m[1 * 4 + 0]; 772 | var m11 = m[1 * 4 + 1]; 773 | var m12 = m[1 * 4 + 2]; 774 | var m13 = m[1 * 4 + 3]; 775 | var c = Math.cos(angleInRadians); 776 | var s = Math.sin(angleInRadians); 777 | 778 | dst[ 0] = c * m00 + s * m10; 779 | dst[ 1] = c * m01 + s * m11; 780 | dst[ 2] = c * m02 + s * m12; 781 | dst[ 3] = c * m03 + s * m13; 782 | dst[ 4] = c * m10 - s * m00; 783 | dst[ 5] = c * m11 - s * m01; 784 | dst[ 6] = c * m12 - s * m02; 785 | dst[ 7] = c * m13 - s * m03; 786 | 787 | if (m !== dst) { 788 | dst[ 8] = m[ 8]; 789 | dst[ 9] = m[ 9]; 790 | dst[10] = m[10]; 791 | dst[11] = m[11]; 792 | dst[12] = m[12]; 793 | dst[13] = m[13]; 794 | dst[14] = m[14]; 795 | dst[15] = m[15]; 796 | } 797 | 798 | return dst; 799 | } 800 | 801 | /** 802 | * Makes an rotation matrix around an arbitrary axis 803 | * @param {Vector3} axis axis to rotate around 804 | * @param {number} angleInRadians amount to rotate 805 | * @param {Matrix4} [dst] optional matrix to store result 806 | * @return {Matrix4} dst or a new matrix if none provided 807 | * @memberOf module:webgl-3d-math 808 | */ 809 | function axisRotation(axis, angleInRadians, dst) { 810 | dst = dst || new MatType(16); 811 | 812 | var x = axis[0]; 813 | var y = axis[1]; 814 | var z = axis[2]; 815 | var n = Math.sqrt(x * x + y * y + z * z); 816 | x /= n; 817 | y /= n; 818 | z /= n; 819 | var xx = x * x; 820 | var yy = y * y; 821 | var zz = z * z; 822 | var c = Math.cos(angleInRadians); 823 | var s = Math.sin(angleInRadians); 824 | var oneMinusCosine = 1 - c; 825 | 826 | dst[ 0] = xx + (1 - xx) * c; 827 | dst[ 1] = x * y * oneMinusCosine + z * s; 828 | dst[ 2] = x * z * oneMinusCosine - y * s; 829 | dst[ 3] = 0; 830 | dst[ 4] = x * y * oneMinusCosine - z * s; 831 | dst[ 5] = yy + (1 - yy) * c; 832 | dst[ 6] = y * z * oneMinusCosine + x * s; 833 | dst[ 7] = 0; 834 | dst[ 8] = x * z * oneMinusCosine + y * s; 835 | dst[ 9] = y * z * oneMinusCosine - x * s; 836 | dst[10] = zz + (1 - zz) * c; 837 | dst[11] = 0; 838 | dst[12] = 0; 839 | dst[13] = 0; 840 | dst[14] = 0; 841 | dst[15] = 1; 842 | 843 | return dst; 844 | } 845 | 846 | /** 847 | * Multiply by an axis rotation matrix 848 | * @param {Matrix4} m matrix to multiply 849 | * @param {Vector3} axis axis to rotate around 850 | * @param {number} angleInRadians amount to rotate 851 | * @param {Matrix4} [dst] optional matrix to store result 852 | * @return {Matrix4} dst or a new matrix if none provided 853 | * @memberOf module:webgl-3d-math 854 | */ 855 | function axisRotate(m, axis, angleInRadians, dst) { 856 | // This is the optimized version of 857 | // return multiply(m, axisRotation(axis, angleInRadians), dst); 858 | dst = dst || new MatType(16); 859 | 860 | var x = axis[0]; 861 | var y = axis[1]; 862 | var z = axis[2]; 863 | var n = Math.sqrt(x * x + y * y + z * z); 864 | x /= n; 865 | y /= n; 866 | z /= n; 867 | var xx = x * x; 868 | var yy = y * y; 869 | var zz = z * z; 870 | var c = Math.cos(angleInRadians); 871 | var s = Math.sin(angleInRadians); 872 | var oneMinusCosine = 1 - c; 873 | 874 | var r00 = xx + (1 - xx) * c; 875 | var r01 = x * y * oneMinusCosine + z * s; 876 | var r02 = x * z * oneMinusCosine - y * s; 877 | var r10 = x * y * oneMinusCosine - z * s; 878 | var r11 = yy + (1 - yy) * c; 879 | var r12 = y * z * oneMinusCosine + x * s; 880 | var r20 = x * z * oneMinusCosine + y * s; 881 | var r21 = y * z * oneMinusCosine - x * s; 882 | var r22 = zz + (1 - zz) * c; 883 | 884 | var m00 = m[0]; 885 | var m01 = m[1]; 886 | var m02 = m[2]; 887 | var m03 = m[3]; 888 | var m10 = m[4]; 889 | var m11 = m[5]; 890 | var m12 = m[6]; 891 | var m13 = m[7]; 892 | var m20 = m[8]; 893 | var m21 = m[9]; 894 | var m22 = m[10]; 895 | var m23 = m[11]; 896 | 897 | dst[ 0] = r00 * m00 + r01 * m10 + r02 * m20; 898 | dst[ 1] = r00 * m01 + r01 * m11 + r02 * m21; 899 | dst[ 2] = r00 * m02 + r01 * m12 + r02 * m22; 900 | dst[ 3] = r00 * m03 + r01 * m13 + r02 * m23; 901 | dst[ 4] = r10 * m00 + r11 * m10 + r12 * m20; 902 | dst[ 5] = r10 * m01 + r11 * m11 + r12 * m21; 903 | dst[ 6] = r10 * m02 + r11 * m12 + r12 * m22; 904 | dst[ 7] = r10 * m03 + r11 * m13 + r12 * m23; 905 | dst[ 8] = r20 * m00 + r21 * m10 + r22 * m20; 906 | dst[ 9] = r20 * m01 + r21 * m11 + r22 * m21; 907 | dst[10] = r20 * m02 + r21 * m12 + r22 * m22; 908 | dst[11] = r20 * m03 + r21 * m13 + r22 * m23; 909 | 910 | if (m !== dst) { 911 | dst[12] = m[12]; 912 | dst[13] = m[13]; 913 | dst[14] = m[14]; 914 | dst[15] = m[15]; 915 | } 916 | 917 | return dst; 918 | } 919 | 920 | /** 921 | * Makes a scale matrix 922 | * @param {number} sx x scale. 923 | * @param {number} sy y scale. 924 | * @param {number} sz z scale. 925 | * @param {Matrix4} [dst] optional matrix to store result 926 | * @return {Matrix4} dst or a new matrix if none provided 927 | * @memberOf module:webgl-3d-math 928 | */ 929 | function scaling(sx, sy, sz, dst) { 930 | dst = dst || new MatType(16); 931 | 932 | dst[ 0] = sx; 933 | dst[ 1] = 0; 934 | dst[ 2] = 0; 935 | dst[ 3] = 0; 936 | dst[ 4] = 0; 937 | dst[ 5] = sy; 938 | dst[ 6] = 0; 939 | dst[ 7] = 0; 940 | dst[ 8] = 0; 941 | dst[ 9] = 0; 942 | dst[10] = sz; 943 | dst[11] = 0; 944 | dst[12] = 0; 945 | dst[13] = 0; 946 | dst[14] = 0; 947 | dst[15] = 1; 948 | 949 | return dst; 950 | } 951 | 952 | /** 953 | * Multiply by a scaling matrix 954 | * @param {Matrix4} m matrix to multiply 955 | * @param {number} sx x scale. 956 | * @param {number} sy y scale. 957 | * @param {number} sz z scale. 958 | * @param {Matrix4} [dst] optional matrix to store result 959 | * @return {Matrix4} dst or a new matrix if none provided 960 | * @memberOf module:webgl-3d-math 961 | */ 962 | function scale(m, sx, sy, sz, dst) { 963 | // This is the optimized version of 964 | // return multiply(m, scaling(sx, sy, sz), dst); 965 | dst = dst || new MatType(16); 966 | 967 | dst[ 0] = sx * m[0 * 4 + 0]; 968 | dst[ 1] = sx * m[0 * 4 + 1]; 969 | dst[ 2] = sx * m[0 * 4 + 2]; 970 | dst[ 3] = sx * m[0 * 4 + 3]; 971 | dst[ 4] = sy * m[1 * 4 + 0]; 972 | dst[ 5] = sy * m[1 * 4 + 1]; 973 | dst[ 6] = sy * m[1 * 4 + 2]; 974 | dst[ 7] = sy * m[1 * 4 + 3]; 975 | dst[ 8] = sz * m[2 * 4 + 0]; 976 | dst[ 9] = sz * m[2 * 4 + 1]; 977 | dst[10] = sz * m[2 * 4 + 2]; 978 | dst[11] = sz * m[2 * 4 + 3]; 979 | 980 | if (m !== dst) { 981 | dst[12] = m[12]; 982 | dst[13] = m[13]; 983 | dst[14] = m[14]; 984 | dst[15] = m[15]; 985 | } 986 | 987 | return dst; 988 | } 989 | 990 | /** 991 | * creates a matrix from translation, quaternion, scale 992 | * @param {Number[]} translation [x, y, z] translation 993 | * @param {Number[]} quaternion [x, y, z, z] quaternion rotation 994 | * @param {Number[]} scale [x, y, z] scale 995 | * @param {Matrix4} [dst] optional matrix to store result 996 | * @return {Matrix4} dst or a new matrix if none provided 997 | */ 998 | function compose(translation, quaternion, scale, dst) { 999 | dst = dst || new MatType(16); 1000 | 1001 | const x = quaternion[0]; 1002 | const y = quaternion[1]; 1003 | const z = quaternion[2]; 1004 | const w = quaternion[3]; 1005 | 1006 | const x2 = x + x; 1007 | const y2 = y + y; 1008 | const z2 = z + z; 1009 | 1010 | const xx = x * x2; 1011 | const xy = x * y2; 1012 | const xz = x * z2; 1013 | 1014 | const yy = y * y2; 1015 | const yz = y * z2; 1016 | const zz = z * z2; 1017 | 1018 | const wx = w * x2; 1019 | const wy = w * y2; 1020 | const wz = w * z2; 1021 | 1022 | const sx = scale[0]; 1023 | const sy = scale[1]; 1024 | const sz = scale[2]; 1025 | 1026 | dst[0] = (1 - (yy + zz)) * sx; 1027 | dst[1] = (xy + wz) * sx; 1028 | dst[2] = (xz - wy) * sx; 1029 | dst[3] = 0; 1030 | 1031 | dst[4] = (xy - wz) * sy; 1032 | dst[5] = (1 - (xx + zz)) * sy; 1033 | dst[6] = (yz + wx) * sy; 1034 | dst[7] = 0; 1035 | 1036 | dst[ 8] = (xz + wy) * sz; 1037 | dst[ 9] = (yz - wx) * sz; 1038 | dst[10] = (1 - (xx + yy)) * sz; 1039 | dst[11] = 0; 1040 | 1041 | dst[12] = translation[0]; 1042 | dst[13] = translation[1]; 1043 | dst[14] = translation[2]; 1044 | dst[15] = 1; 1045 | 1046 | return dst; 1047 | } 1048 | 1049 | function quatFromRotationMatrix(m, dst) { 1050 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm 1051 | 1052 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) 1053 | const m11 = m[0]; 1054 | const m12 = m[4]; 1055 | const m13 = m[8]; 1056 | const m21 = m[1]; 1057 | const m22 = m[5]; 1058 | const m23 = m[9]; 1059 | const m31 = m[2]; 1060 | const m32 = m[6]; 1061 | const m33 = m[10]; 1062 | 1063 | const trace = m11 + m22 + m33; 1064 | 1065 | if (trace > 0) { 1066 | const s = 0.5 / Math.sqrt(trace + 1); 1067 | dst[3] = 0.25 / s; 1068 | dst[0] = (m32 - m23) * s; 1069 | dst[1] = (m13 - m31) * s; 1070 | dst[2] = (m21 - m12) * s; 1071 | } else if (m11 > m22 && m11 > m33) { 1072 | const s = 2 * Math.sqrt(1 + m11 - m22 - m33); 1073 | dst[3] = (m32 - m23) / s; 1074 | dst[0] = 0.25 * s; 1075 | dst[1] = (m12 + m21) / s; 1076 | dst[2] = (m13 + m31) / s; 1077 | } else if (m22 > m33) { 1078 | const s = 2 * Math.sqrt(1 + m22 - m11 - m33); 1079 | dst[3] = (m13 - m31) / s; 1080 | dst[0] = (m12 + m21) / s; 1081 | dst[1] = 0.25 * s; 1082 | dst[2] = (m23 + m32) / s; 1083 | } else { 1084 | const s = 2 * Math.sqrt(1 + m33 - m11 - m22); 1085 | dst[3] = (m21 - m12) / s; 1086 | dst[0] = (m13 + m31) / s; 1087 | dst[1] = (m23 + m32) / s; 1088 | dst[2] = 0.25 * s; 1089 | } 1090 | } 1091 | 1092 | function decompose(mat, translation, quaternion, scale) { 1093 | let sx = m4.length(mat.slice(0, 3)); 1094 | const sy = m4.length(mat.slice(4, 7)); 1095 | const sz = m4.length(mat.slice(8, 11)); 1096 | 1097 | // if determinate is negative, we need to invert one scale 1098 | const det = determinate(mat); 1099 | if (det < 0) { 1100 | sx = -sx; 1101 | } 1102 | 1103 | translation[0] = mat[12]; 1104 | translation[1] = mat[13]; 1105 | translation[2] = mat[14]; 1106 | 1107 | // scale the rotation part 1108 | const matrix = m4.copy(mat); 1109 | 1110 | const invSX = 1 / sx; 1111 | const invSY = 1 / sy; 1112 | const invSZ = 1 / sz; 1113 | 1114 | matrix[0] *= invSX; 1115 | matrix[1] *= invSX; 1116 | matrix[2] *= invSX; 1117 | 1118 | matrix[4] *= invSY; 1119 | matrix[5] *= invSY; 1120 | matrix[6] *= invSY; 1121 | 1122 | matrix[8] *= invSZ; 1123 | matrix[9] *= invSZ; 1124 | matrix[10] *= invSZ; 1125 | 1126 | quatFromRotationMatrix(matrix, quaternion); 1127 | 1128 | scale[0] = sx; 1129 | scale[1] = sy; 1130 | scale[2] = sz; 1131 | } 1132 | 1133 | function determinate(m) { 1134 | var m00 = m[0 * 4 + 0]; 1135 | var m01 = m[0 * 4 + 1]; 1136 | var m02 = m[0 * 4 + 2]; 1137 | var m03 = m[0 * 4 + 3]; 1138 | var m10 = m[1 * 4 + 0]; 1139 | var m11 = m[1 * 4 + 1]; 1140 | var m12 = m[1 * 4 + 2]; 1141 | var m13 = m[1 * 4 + 3]; 1142 | var m20 = m[2 * 4 + 0]; 1143 | var m21 = m[2 * 4 + 1]; 1144 | var m22 = m[2 * 4 + 2]; 1145 | var m23 = m[2 * 4 + 3]; 1146 | var m30 = m[3 * 4 + 0]; 1147 | var m31 = m[3 * 4 + 1]; 1148 | var m32 = m[3 * 4 + 2]; 1149 | var m33 = m[3 * 4 + 3]; 1150 | var tmp_0 = m22 * m33; 1151 | var tmp_1 = m32 * m23; 1152 | var tmp_2 = m12 * m33; 1153 | var tmp_3 = m32 * m13; 1154 | var tmp_4 = m12 * m23; 1155 | var tmp_5 = m22 * m13; 1156 | var tmp_6 = m02 * m33; 1157 | var tmp_7 = m32 * m03; 1158 | var tmp_8 = m02 * m23; 1159 | var tmp_9 = m22 * m03; 1160 | var tmp_10 = m02 * m13; 1161 | var tmp_11 = m12 * m03; 1162 | 1163 | var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - 1164 | (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); 1165 | var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - 1166 | (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); 1167 | var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - 1168 | (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); 1169 | var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - 1170 | (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); 1171 | 1172 | return 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); 1173 | } 1174 | 1175 | /** 1176 | * Computes the inverse of a matrix. 1177 | * @param {Matrix4} m matrix to compute inverse of 1178 | * @param {Matrix4} [dst] optional matrix to store result 1179 | * @return {Matrix4} dst or a new matrix if none provided 1180 | * @memberOf module:webgl-3d-math 1181 | */ 1182 | function inverse(m, dst) { 1183 | dst = dst || new MatType(16); 1184 | var m00 = m[0 * 4 + 0]; 1185 | var m01 = m[0 * 4 + 1]; 1186 | var m02 = m[0 * 4 + 2]; 1187 | var m03 = m[0 * 4 + 3]; 1188 | var m10 = m[1 * 4 + 0]; 1189 | var m11 = m[1 * 4 + 1]; 1190 | var m12 = m[1 * 4 + 2]; 1191 | var m13 = m[1 * 4 + 3]; 1192 | var m20 = m[2 * 4 + 0]; 1193 | var m21 = m[2 * 4 + 1]; 1194 | var m22 = m[2 * 4 + 2]; 1195 | var m23 = m[2 * 4 + 3]; 1196 | var m30 = m[3 * 4 + 0]; 1197 | var m31 = m[3 * 4 + 1]; 1198 | var m32 = m[3 * 4 + 2]; 1199 | var m33 = m[3 * 4 + 3]; 1200 | var tmp_0 = m22 * m33; 1201 | var tmp_1 = m32 * m23; 1202 | var tmp_2 = m12 * m33; 1203 | var tmp_3 = m32 * m13; 1204 | var tmp_4 = m12 * m23; 1205 | var tmp_5 = m22 * m13; 1206 | var tmp_6 = m02 * m33; 1207 | var tmp_7 = m32 * m03; 1208 | var tmp_8 = m02 * m23; 1209 | var tmp_9 = m22 * m03; 1210 | var tmp_10 = m02 * m13; 1211 | var tmp_11 = m12 * m03; 1212 | var tmp_12 = m20 * m31; 1213 | var tmp_13 = m30 * m21; 1214 | var tmp_14 = m10 * m31; 1215 | var tmp_15 = m30 * m11; 1216 | var tmp_16 = m10 * m21; 1217 | var tmp_17 = m20 * m11; 1218 | var tmp_18 = m00 * m31; 1219 | var tmp_19 = m30 * m01; 1220 | var tmp_20 = m00 * m21; 1221 | var tmp_21 = m20 * m01; 1222 | var tmp_22 = m00 * m11; 1223 | var tmp_23 = m10 * m01; 1224 | 1225 | var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - 1226 | (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); 1227 | var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - 1228 | (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); 1229 | var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - 1230 | (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); 1231 | var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - 1232 | (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); 1233 | 1234 | var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); 1235 | 1236 | dst[0] = d * t0; 1237 | dst[1] = d * t1; 1238 | dst[2] = d * t2; 1239 | dst[3] = d * t3; 1240 | dst[4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - 1241 | (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)); 1242 | dst[5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - 1243 | (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)); 1244 | dst[6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - 1245 | (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)); 1246 | dst[7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - 1247 | (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)); 1248 | dst[8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - 1249 | (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)); 1250 | dst[9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - 1251 | (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)); 1252 | dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - 1253 | (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)); 1254 | dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - 1255 | (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)); 1256 | dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - 1257 | (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)); 1258 | dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - 1259 | (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)); 1260 | dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - 1261 | (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)); 1262 | dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - 1263 | (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)); 1264 | 1265 | return dst; 1266 | } 1267 | 1268 | /** 1269 | * Takes a matrix and a vector with 4 entries, transforms that vector by 1270 | * the matrix, and returns the result as a vector with 4 entries. 1271 | * @param {Matrix4} m The matrix. 1272 | * @param {Vector4} v The point in homogenous coordinates. 1273 | * @param {Vector4} dst optional vector4 to store result 1274 | * @return {Vector4} dst or new Vector4 if not provided 1275 | * @memberOf module:webgl-3d-math 1276 | */ 1277 | function transformVector(m, v, dst) { 1278 | dst = dst || new MatType(4); 1279 | for (var i = 0; i < 4; ++i) { 1280 | dst[i] = 0.0; 1281 | for (var j = 0; j < 4; ++j) { 1282 | dst[i] += v[j] * m[j * 4 + i]; 1283 | } 1284 | } 1285 | return dst; 1286 | } 1287 | 1288 | /** 1289 | * Takes a 4-by-4 matrix and a vector with 3 entries, 1290 | * interprets the vector as a point, transforms that point by the matrix, and 1291 | * returns the result as a vector with 3 entries. 1292 | * @param {Matrix4} m The matrix. 1293 | * @param {Vector3} v The point. 1294 | * @param {Vector4} dst optional vector4 to store result 1295 | * @return {Vector4} dst or new Vector4 if not provided 1296 | * @memberOf module:webgl-3d-math 1297 | */ 1298 | function transformPoint(m, v, dst) { 1299 | dst = dst || new MatType(3); 1300 | var v0 = v[0]; 1301 | var v1 = v[1]; 1302 | var v2 = v[2]; 1303 | var d = v0 * m[0 * 4 + 3] + v1 * m[1 * 4 + 3] + v2 * m[2 * 4 + 3] + m[3 * 4 + 3]; 1304 | 1305 | dst[0] = (v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0] + m[3 * 4 + 0]) / d; 1306 | dst[1] = (v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1] + m[3 * 4 + 1]) / d; 1307 | dst[2] = (v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2] + m[3 * 4 + 2]) / d; 1308 | 1309 | return dst; 1310 | } 1311 | 1312 | /** 1313 | * Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a 1314 | * direction, transforms that direction by the matrix, and returns the result; 1315 | * assumes the transformation of 3-dimensional space represented by the matrix 1316 | * is parallel-preserving, i.e. any combination of rotation, scaling and 1317 | * translation, but not a perspective distortion. Returns a vector with 3 1318 | * entries. 1319 | * @param {Matrix4} m The matrix. 1320 | * @param {Vector3} v The direction. 1321 | * @param {Vector4} dst optional vector4 to store result 1322 | * @return {Vector4} dst or new Vector4 if not provided 1323 | * @memberOf module:webgl-3d-math 1324 | */ 1325 | function transformDirection(m, v, dst) { 1326 | dst = dst || new MatType(3); 1327 | 1328 | var v0 = v[0]; 1329 | var v1 = v[1]; 1330 | var v2 = v[2]; 1331 | 1332 | dst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0]; 1333 | dst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1]; 1334 | dst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2]; 1335 | 1336 | return dst; 1337 | } 1338 | 1339 | /** 1340 | * Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector 1341 | * as a normal to a surface, and computes a vector which is normal upon 1342 | * transforming that surface by the matrix. The effect of this function is the 1343 | * same as transforming v (as a direction) by the inverse-transpose of m. This 1344 | * function assumes the transformation of 3-dimensional space represented by the 1345 | * matrix is parallel-preserving, i.e. any combination of rotation, scaling and 1346 | * translation, but not a perspective distortion. Returns a vector with 3 1347 | * entries. 1348 | * @param {Matrix4} m The matrix. 1349 | * @param {Vector3} v The normal. 1350 | * @param {Vector3} [dst] The direction. 1351 | * @return {Vector3} The transformed direction. 1352 | * @memberOf module:webgl-3d-math 1353 | */ 1354 | function transformNormal(m, v, dst) { 1355 | dst = dst || new MatType(3); 1356 | var mi = inverse(m); 1357 | var v0 = v[0]; 1358 | var v1 = v[1]; 1359 | var v2 = v[2]; 1360 | 1361 | dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2]; 1362 | dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2]; 1363 | dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2]; 1364 | 1365 | return dst; 1366 | } 1367 | 1368 | function copy(src, dst) { 1369 | dst = dst || new MatType(16); 1370 | 1371 | dst[ 0] = src[ 0]; 1372 | dst[ 1] = src[ 1]; 1373 | dst[ 2] = src[ 2]; 1374 | dst[ 3] = src[ 3]; 1375 | dst[ 4] = src[ 4]; 1376 | dst[ 5] = src[ 5]; 1377 | dst[ 6] = src[ 6]; 1378 | dst[ 7] = src[ 7]; 1379 | dst[ 8] = src[ 8]; 1380 | dst[ 9] = src[ 9]; 1381 | dst[10] = src[10]; 1382 | dst[11] = src[11]; 1383 | dst[12] = src[12]; 1384 | dst[13] = src[13]; 1385 | dst[14] = src[14]; 1386 | dst[15] = src[15]; 1387 | 1388 | return dst; 1389 | } 1390 | 1391 | return { 1392 | copy: copy, 1393 | lookAt: lookAt, 1394 | addVectors: addVectors, 1395 | subtractVectors: subtractVectors, 1396 | distance: distance, 1397 | distanceSq: distanceSq, 1398 | normalize: normalize, 1399 | compose: compose, 1400 | cross: cross, 1401 | decompose: decompose, 1402 | dot: dot, 1403 | identity: identity, 1404 | transpose: transpose, 1405 | length: length, 1406 | orthographic: orthographic, 1407 | frustum: frustum, 1408 | perspective: perspective, 1409 | translation: translation, 1410 | translate: translate, 1411 | xRotation: xRotation, 1412 | yRotation: yRotation, 1413 | zRotation: zRotation, 1414 | xRotate: xRotate, 1415 | yRotate: yRotate, 1416 | zRotate: zRotate, 1417 | axisRotation: axisRotation, 1418 | axisRotate: axisRotate, 1419 | scaling: scaling, 1420 | scale: scale, 1421 | multiply: multiply, 1422 | inverse: inverse, 1423 | transformVector: transformVector, 1424 | transformPoint: transformPoint, 1425 | transformDirection: transformDirection, 1426 | transformNormal: transformNormal, 1427 | setDefaultType: setDefaultType, 1428 | }; 1429 | 1430 | })(); 1431 | 1432 | 1433 | --------------------------------------------------------------------------------