├── 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 |
--------------------------------------------------------------------------------
/app-simulator/buttons/4.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app-simulator/buttons/SoftLeft.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/Backspace.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/SoftRight.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/2.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/1.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/7.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/asterisk.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app-simulator/buttons/5.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/ArrowUp.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/8.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/ArrowLeft.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/ArrowDown.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app-simulator/buttons/3.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/9.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-simulator/buttons/6.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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\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 |
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 |
--------------------------------------------------------------------------------