├── .gitattributes ├── src ├── FontData │ ├── small.ase │ ├── small.png │ ├── standard.ase │ ├── standard.png │ ├── small.config.json │ ├── FontData.js │ ├── Font.js │ ├── standard.config.json │ ├── small.font.json │ └── standard.font.json ├── index.js ├── lz-string │ ├── bower.json │ ├── bin │ │ └── bin.js │ ├── package.json │ ├── LICENSE │ ├── README.md │ ├── libs │ │ ├── lz-string.min.js │ │ ├── base64-string.js │ │ └── lz-string.js │ └── reference │ │ └── lz-string-1.0.2.js ├── Screen │ └── standardPalette.js ├── libIndex.js ├── MapData │ ├── MapData.js │ └── TileMap.js ├── ConvertData │ ├── ConvertData.js │ └── ConvertData.test.js ├── Builder │ └── Builder.js ├── Audio │ ├── Notes.js │ ├── Frequencies.js │ ├── Sound.js │ └── Audio.js ├── Input │ ├── Keys.js │ └── Input.js ├── ConvertProject │ └── ConvertProject.js ├── TileData │ └── TileData.js └── Engine │ └── Engine.js ├── .eslintignore ├── .esdoc.json ├── .editorconfig ├── demo ├── config │ ├── webpack.demo.prod.js │ ├── webpack.demo.dev.js │ └── webpack.demo.common.js ├── dist │ └── index.html ├── src │ ├── style.css │ ├── index.js │ └── createGameSource.js └── data │ ├── tilebug.project.json │ └── WelcomeTransfer.json ├── README.md ├── utils ├── renameTxtLib.js └── cleanLib.js ├── config ├── webpack.config.lib.txt.js ├── webpack.config.lib.common.js ├── webpack.config.lib.js └── webpack.config.lib.min.js ├── .eslintrc.js ├── license.txt ├── .gitignore ├── .npmignore └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | -------------------------------------------------------------------------------- /src/FontData/small.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byersdz/bitmelo/HEAD/src/FontData/small.ase -------------------------------------------------------------------------------- /src/FontData/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byersdz/bitmelo/HEAD/src/FontData/small.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /node_modules 3 | /config 4 | /demo/config 5 | /demo/dist 6 | /src/lz-string 7 | -------------------------------------------------------------------------------- /src/FontData/standard.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byersdz/bitmelo/HEAD/src/FontData/standard.ase -------------------------------------------------------------------------------- /src/FontData/standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byersdz/bitmelo/HEAD/src/FontData/standard.png -------------------------------------------------------------------------------- /.esdoc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "source": "./src", 4 | "destination": "./docs", 5 | "plugins": [ 6 | {"name": "esdoc-standard-plugin"} 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/FontData/small.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tileSize": 8, 3 | "originX" : 2, 4 | "originY": 2, 5 | "standardWidth": 3, 6 | "letterSpacing": 1 7 | } 8 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | export * from './libIndex'; 3 | 4 | export { default as Builder } from './Builder/Builder'; 5 | export { default as libText } from '../lib/bitmelo.min.txt'; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /demo/config/webpack.demo.prod.js: -------------------------------------------------------------------------------- 1 | 2 | var merge = require( 'webpack-merge' ); 3 | var common = require( './webpack.demo.common.js' ); 4 | 5 | module.exports = merge( common, { 6 | mode: 'production', 7 | devtool: 'source-map' 8 | } ); 9 | -------------------------------------------------------------------------------- /demo/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bitmelo Demo 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lz-string/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lz-string", 3 | "version": "1.4.4", 4 | "main": "libs/lz-string.min.js", 5 | "ignore": [ 6 | "bin/", 7 | "reference/", 8 | "tests/" 9 | ], 10 | "keywords": ["compression", "lzw", "string"] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Bitmelo Engine 2 | Bitmelo is a game engine for making small pixel art games. 3 | 4 | [Click here to view the API docs](https://bitmelo.com/api) 5 | 6 | ### License 7 | Copyright 2019 David Byers. 8 | 9 | This software is licensed under the MIT license, located in license.txt 10 | -------------------------------------------------------------------------------- /demo/config/webpack.demo.dev.js: -------------------------------------------------------------------------------- 1 | 2 | var merge = require( 'webpack-merge' ); 3 | var common = require( './webpack.demo.common.js' ); 4 | 5 | module.exports = merge( common, { 6 | mode: 'development', 7 | devtool: 'inline-source-map', 8 | devServer: { 9 | contentBase: './demo/dist', 10 | port: 9000, 11 | }, 12 | } ); 13 | -------------------------------------------------------------------------------- /utils/renameTxtLib.js: -------------------------------------------------------------------------------- 1 | 2 | // renaming is required because webpack wont minify a txt lib 3 | const fs = require( 'fs' ); 4 | 5 | const oldName = 'lib/bitmelo.min.txt.js'; 6 | const newName = 'lib/bitmelo.min.txt'; 7 | 8 | fs.rename( oldName, newName, ( err ) => { 9 | if ( err ) { 10 | console.log( err ); 11 | } 12 | } ); 13 | -------------------------------------------------------------------------------- /src/Screen/standardPalette.js: -------------------------------------------------------------------------------- 1 | 2 | export default [ 3 | '000000', 4 | '0b0711', 5 | 'eaf2de', 6 | '766e76', 7 | '561f6e', 8 | '7d4f31', 9 | 'e98c49', 10 | 'fbc0a0', 11 | 'f681b2', 12 | 'd83232', 13 | 'e3e962', 14 | '65cf57', 15 | '2ba957', 16 | '187575', 17 | '1e2cB0', 18 | '2379e5', 19 | '95cae5', 20 | ]; 21 | -------------------------------------------------------------------------------- /src/lz-string/bin/bin.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | var lzString = require('../libs/lz-string.js'); 3 | var fs = require('fs'); 4 | 5 | if (process.argv.length < 3) { 6 | console.error('Usage: lz-string '); 7 | process.exit(1); 8 | } 9 | 10 | console.log(lzString.compress(fs.readFileSync(process.argv[2], { 11 | encoding: 'utf8', 12 | }))); 13 | 14 | -------------------------------------------------------------------------------- /demo/src/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | min-height: 100%; 7 | margin: 0; 8 | } 9 | 10 | #main-container { 11 | display: flex; 12 | flex-direction: column; 13 | height: 100%; 14 | } 15 | 16 | #play-iframe { 17 | width: 100%; 18 | height: 100%; 19 | border: none; 20 | border-width: 0; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | -------------------------------------------------------------------------------- /config/webpack.config.lib.txt.js: -------------------------------------------------------------------------------- 1 | var merge = require( 'webpack-merge' ); 2 | var common = require( './webpack.config.lib.common' ); 3 | var path = require( 'path' ); 4 | 5 | module.exports = merge( common, { 6 | mode: 'production', 7 | output: { 8 | path: path.resolve(__dirname, '../lib'), 9 | filename: `bitmelo.min.txt.js`, 10 | library: 'bitmelo', 11 | libraryTarget: 'umd' 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /config/webpack.config.lib.common.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/libIndex.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.js$/, 9 | exclude: /node_modules/, 10 | use: ['eslint-loader'] 11 | }, 12 | { 13 | test: /\.txt$/i, 14 | use: [ 15 | 'raw-loader' 16 | ] 17 | } 18 | ] 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /config/webpack.config.lib.js: -------------------------------------------------------------------------------- 1 | var merge = require( 'webpack-merge' ); 2 | var common = require( './webpack.config.lib.common' ); 3 | var path = require( 'path' ); 4 | 5 | module.exports = merge( common, { 6 | mode: 'development', 7 | output: { 8 | path: path.resolve(__dirname, '../lib'), 9 | filename: `bitmelo.${ process.env.npm_package_version }.js`, 10 | library: 'bitmelo', 11 | libraryTarget: 'umd' 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /config/webpack.config.lib.min.js: -------------------------------------------------------------------------------- 1 | var merge = require( 'webpack-merge' ); 2 | var common = require( './webpack.config.lib.common' ); 3 | var path = require( 'path' ); 4 | 5 | module.exports = merge( common, { 6 | mode: 'production', 7 | output: { 8 | path: path.resolve(__dirname, '../lib'), 9 | filename: `bitmelo.${ process.env.npm_package_version }.min.js`, 10 | library: 'bitmelo', 11 | libraryTarget: 'umd' 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /src/FontData/FontData.js: -------------------------------------------------------------------------------- 1 | 2 | import Font from './Font'; 3 | 4 | /** 5 | * Holds all of the font data 6 | */ 7 | class FontData { 8 | constructor() { 9 | /** 10 | * Array of Font objects 11 | */ 12 | this.fonts = []; 13 | } 14 | 15 | /** 16 | * Add a Font to this.fonts from font data 17 | * @param {*} fontData 18 | */ 19 | addFont( fontData ) { 20 | this.fonts.push( new Font( fontData ) ); 21 | } 22 | } 23 | 24 | export default FontData; 25 | -------------------------------------------------------------------------------- /utils/cleanLib.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require( 'fs' ); 3 | const path = require( 'path' ); 4 | 5 | const libDirectory = 'lib'; 6 | 7 | fs.readdir( libDirectory, ( err, files ) => { 8 | if ( err ) { 9 | console.log( err ); 10 | return; 11 | } 12 | 13 | files.forEach( ( file ) => { 14 | const filePath = path.join( libDirectory, file ); 15 | fs.unlink( filePath, ( unlinkErr ) => { 16 | if ( unlinkErr ) { 17 | console.log( unlinkErr ); 18 | } 19 | } ); 20 | } ); 21 | } ); 22 | -------------------------------------------------------------------------------- /demo/config/webpack.demo.common.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require( 'path' ); 3 | 4 | module.exports = { 5 | entry: './demo/src/index.js', 6 | output: { 7 | filename: 'bundle.js', 8 | path: path.resolve(__dirname, '../dist') 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.css$/, 14 | use: [ 15 | 'style-loader', 16 | 'css-loader' 17 | ] 18 | }, 19 | { 20 | test: /\.js$/, 21 | exclude: /node_modules/, 22 | use: ['eslint-loader'] 23 | }, 24 | { 25 | test: /\.txt$/i, 26 | use: [ 27 | 'raw-loader' 28 | ] 29 | } 30 | ] 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // import { Engine, Notes } from '../../src/index'; 4 | // // import testProject from '../data/WelcomeDemo.project.json'; 5 | import testProject from '../data/WelcomeTransfer.json'; 6 | import createGameSource from './createGameSource'; 7 | import './style.css'; 8 | 9 | // const testScript = ` 10 | // while(true) { 11 | // console.log(engine.shouldBreak()); 12 | // } 13 | // `; 14 | 15 | const iframe = document.createElement( 'iframe' ); 16 | iframe.id = 'play-iframe'; 17 | iframe.srcdoc = createGameSource( testProject ); 18 | 19 | const container = document.getElementById( 'main-container' ); 20 | container.appendChild( iframe ); 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | "extends": "airbnb-base", 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "jest": true 8 | }, 9 | "rules": { 10 | "no-underscore-dangle": "off", 11 | "no-continue": "off", 12 | "class-methods-use-this": "off", 13 | "space-in-parens": ["error", "always"], 14 | "template-curly-spacing": ["error", "never"], 15 | "no-bitwise": "off", 16 | "template-curly-spacing": ["error", "always"], 17 | "arrow-body-style": "off", 18 | "brace-style": ["error", "stroustrup"], 19 | "max-len": ["error", { "code": 120, "ignoreComments": true, "ignoreTrailingComments": true }], 20 | "operator-assignment": "off", 21 | "no-lonely-if": "off", 22 | "no-console": 'off', 23 | "prefer-destructuring": ["error", { 24 | "array": false, 25 | "object": true 26 | }, { 27 | "enforceForRenamedProperties": false 28 | }] 29 | }, 30 | "globals": { 31 | "bitmelo": "readonly" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/libIndex.js: -------------------------------------------------------------------------------- 1 | 2 | export { default as Audio } from './Audio/Audio'; 3 | export { default as Frequencies } from './Audio/Frequencies'; 4 | export { default as Notes } from './Audio/Notes'; 5 | export { default as Sound } from './Audio/Sound'; 6 | 7 | export { default as ConvertProject } from './ConvertProject/ConvertProject'; 8 | 9 | export { default as ConvertData } from './ConvertData/ConvertData'; 10 | 11 | export { default as Font } from './FontData/Font'; 12 | export { default as FontData } from './FontData/FontData'; 13 | 14 | export { default as Input } from './Input/Input'; 15 | export { default as Keys } from './Input/Keys'; 16 | 17 | export { default as MapData } from './MapData/MapData'; 18 | export { default as TileMap } from './MapData/TileMap'; 19 | 20 | export { default as Engine } from './Engine/Engine'; 21 | 22 | export { default as Screen } from './Screen/Screen'; 23 | export { default as standardPalette } from './Screen/standardPalette'; 24 | 25 | export { default as TileData } from './TileData/TileData'; 26 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 David Byers 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | 11 | -------------------------------------------------------------------------------- /src/lz-string/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lz-string", 3 | "version": "1.4.4", 4 | "license": "MIT", 5 | "filename": "lz-string.js", 6 | "description": "LZ-based compression algorithm", 7 | "homepage": "http://pieroxy.net/blog/pages/lz-string/index.html", 8 | "keywords": [ 9 | "lz", 10 | "compression", 11 | "string" 12 | ], 13 | "main": "libs/lz-string.js", 14 | "typings": "typings/lz-string.d.ts", 15 | "bin": { 16 | "lz-string": "bin/bin.js" 17 | }, 18 | "scripts": {}, 19 | "dependencies": {}, 20 | "devDependencies": {}, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/pieroxy/lz-string.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/pieroxy/lz-string/issues" 27 | }, 28 | "directories": { 29 | "test": "tests" 30 | }, 31 | "author": "pieroxy ", 32 | "autoupdate": { 33 | "source": "git", 34 | "target": "git://github.com/pieroxy/lz-string.git", 35 | "basePath": "libs/", 36 | "files": [ 37 | "lz-string.js", 38 | "lz-string.min.js", 39 | "base64-string.js" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lz-string/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013 pieroxy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | docs/ 83 | demo/dist/bundle.js 84 | demo/dist/bundle.js.map 85 | out/ 86 | -------------------------------------------------------------------------------- /src/MapData/MapData.js: -------------------------------------------------------------------------------- 1 | import TileMap from './TileMap'; 2 | 3 | /** 4 | * Holds all of the Tile Map data. 5 | */ 6 | class MapData { 7 | constructor() { 8 | /** 9 | * Array of TileMap objects 10 | */ 11 | this.tileMaps = []; 12 | } 13 | 14 | /** 15 | * Add a tilemap from a data object 16 | * @param {Object} tileMap - The tile map data 17 | */ 18 | addTileMap( tileMap ) { 19 | this.tileMaps.push( new TileMap( tileMap ) ); 20 | return this.tileMaps.length - 1; 21 | } 22 | 23 | /** 24 | * Get the tile GID at a given position 25 | * @param {number} x - The x position 26 | * @param {number} y - The y position 27 | * @param {number} tileMap - The index of the tile map you are checking 28 | * @param {number} layer - The index of the layer on the tile map you are checking 29 | */ 30 | getTile( x, y, tileMap = 0, layer = 0 ) { 31 | if ( tileMap < 0 || tileMap >= this.tileMaps.length ) { 32 | return -1; 33 | } 34 | 35 | return this.tileMaps[tileMap].getTile( x, y, layer ); 36 | } 37 | 38 | /** 39 | * Set the tile GID at a given position 40 | * @param {*} gid - The gid 41 | * @param {*} x - The x position 42 | * @param {*} y - The y position 43 | * @param {*} tileMap - The index of the tile map you are setting 44 | * @param {*} layer - The index of the layer on the tile map you are setting 45 | */ 46 | setTile( gid, x, y, tileMap = 0, layer = 0 ) { 47 | if ( tileMap < 0 || tileMap >= this.tileMaps.length ) { 48 | return; 49 | } 50 | 51 | this.tileMaps[tileMap].setTile( gid, x, y, layer ); 52 | } 53 | } 54 | 55 | export default MapData; 56 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | # bitmelo specific 83 | demo/ 84 | docs/ 85 | config/ 86 | 87 | .editorconfig 88 | .esdoc.json 89 | .eslintignore 90 | .eslintrc.js 91 | .gitattributes 92 | .gitignore 93 | 94 | -------------------------------------------------------------------------------- /src/ConvertData/ConvertData.js: -------------------------------------------------------------------------------- 1 | 2 | import LZString from '../lz-string/libs/lz-string'; 3 | 4 | class ConvertData { 5 | static arrayToRun( array ) { 6 | const result = []; 7 | let currentValue = array[0]; 8 | let runNumber = 0; 9 | 10 | for ( let i = 0; i < array.length; i += 1 ) { 11 | if ( array[i] === currentValue ) { 12 | runNumber += 1; 13 | } 14 | else { 15 | result.push( runNumber ); 16 | result.push( currentValue ); 17 | runNumber = 1; 18 | currentValue = array[i]; 19 | } 20 | } 21 | // add the last item 22 | result.push( runNumber ); 23 | result.push( currentValue ); 24 | return result; 25 | } 26 | 27 | static runToArray( runArray ) { 28 | const result = []; 29 | let runPosition = 0; 30 | while ( runPosition < runArray.length ) { 31 | const runLength = runArray[runPosition]; 32 | const item = runArray[runPosition + 1]; 33 | for ( let j = 0; j < runLength; j += 1 ) { 34 | result.push( item ); 35 | } 36 | runPosition += 2; 37 | } 38 | 39 | return result; 40 | } 41 | 42 | static arrayToCompressedString( array ) { 43 | const arrayString = array.join( ',' ); 44 | const compressed = LZString.compressToEncodedURIComponent( arrayString ); 45 | return compressed; 46 | } 47 | 48 | static compressedStringToArray( compressedString ) { 49 | const decompressedString = LZString.decompressFromEncodedURIComponent( compressedString ); 50 | const stringArray = decompressedString.split( ',' ); 51 | const result = []; 52 | for ( let i = 0; i < stringArray.length; i += 1 ) { 53 | result.push( Number.parseInt( stringArray[i], 10 ) ); 54 | } 55 | return result; 56 | } 57 | } 58 | 59 | export default ConvertData; 60 | -------------------------------------------------------------------------------- /demo/src/createGameSource.js: -------------------------------------------------------------------------------- 1 | import cloneDeep from 'lodash/cloneDeep'; 2 | import { libText, Builder } from '../../src/index'; 3 | 4 | function createGameSource( projectData, testScript = '' ) { 5 | const projectCopy = cloneDeep( projectData ); 6 | 7 | if ( projectCopy.tileset && projectCopy.tileset.present ) { 8 | projectCopy.tileset = projectCopy.tileset.present; 9 | } 10 | if ( projectCopy.tilemap && projectCopy.tilemap.present ) { 11 | projectCopy.tilemap = projectCopy.tilemap.present; 12 | } 13 | 14 | const { scripts } = projectCopy.code; 15 | delete projectCopy.code; 16 | let scriptsString = ''; 17 | 18 | for ( let i = 0; i < scripts.length; i += 1 ) { 19 | scriptsString += scripts[i].text; 20 | } 21 | 22 | if ( testScript ) { 23 | scriptsString = testScript; 24 | } 25 | 26 | scriptsString = Builder.instrumentScript( scriptsString ); 27 | 28 | const style = ` 29 | 48 | `; 49 | 50 | const result = ` 51 | 52 | 53 | ${ style } 54 | 55 | 56 |
57 |
58 |
59 | 68 | 69 | 70 | `; 71 | 72 | return result; 73 | } 74 | 75 | export default createGameSource; 76 | -------------------------------------------------------------------------------- /src/Builder/Builder.js: -------------------------------------------------------------------------------- 1 | // esprima requires a require for some reason 2 | const esprima = require( 'esprima' ); 3 | 4 | class Builder { 5 | /** 6 | * Instrument a script string to add calls that can break out of loops. 7 | * The instrumented code will assume that there is a global bitmelo.Engine variable 8 | * named 'engine'. 9 | * @param {string} script 10 | */ 11 | static instrumentScript( script ) { 12 | try { 13 | const patches = []; 14 | 15 | esprima.parseScript( 16 | script, 17 | { 18 | range: true, 19 | tolerant: false, 20 | }, 21 | ( node ) => { 22 | switch ( node.type ) { 23 | case 'DoWhileStatement': 24 | case 'ForStatement': 25 | case 'ForInStatement': 26 | case 'ForOfStatement': 27 | case 'WhileStatement': { 28 | let start = 1 + node.body.range[0]; 29 | const end = node.body.range[1]; 30 | 31 | let startCode = 'if (engine.shouldBreak()) { break; }'; 32 | let endCode = ''; 33 | 34 | if ( node.body.type !== 'BlockStatement' ) { 35 | startCode = `{${ startCode }`; 36 | endCode = '}'; 37 | start -= 1; 38 | } 39 | 40 | patches.push( { position: start, code: startCode } ); 41 | patches.push( { position: end, code: endCode } ); 42 | break; 43 | } 44 | default: break; 45 | } 46 | }, 47 | ); 48 | 49 | let result = script; 50 | 51 | patches.sort( ( a, b ) => { 52 | return b.position - a.position; 53 | } ).forEach( ( patch ) => { 54 | result = result.slice( 0, patch.position ) + patch.code + result.slice( patch.position ); 55 | } ); 56 | 57 | return result; 58 | } 59 | catch ( err ) { 60 | console.error( err ); 61 | return script; 62 | } 63 | } 64 | } 65 | 66 | export default Builder; 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitmelo", 3 | "version": "1.7.0", 4 | "description": "A game engine for making small pixel art games.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "clean-builds": "node utils/cleanlib.js", 9 | "build": "webpack --config config/webpack.config.lib.js", 10 | "build-min": "webpack --config config/webpack.config.lib.min.js", 11 | "build-txt": "webpack --config config/webpack.config.lib.txt.js", 12 | "rename-txt": "node utils/renameTxtLib.js", 13 | "build-all": "npm run clean-builds && npm run build-txt && npm run rename-txt && npm run build && npm run build-min", 14 | "build-docs": "jsdoc -r ./src", 15 | "demo-start": "webpack-dev-server --open --config demo/config/webpack.demo.dev.js", 16 | "demo-build": "webpack --config demo/config/webpack.demo.prod.js", 17 | "prepublishOnly": "npm run build-all" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/byersdz/bitmelo.git" 22 | }, 23 | "keywords": [], 24 | "author": "David Byers", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/byersdz/bitmelo/issues" 28 | }, 29 | "homepage": "https://github.com/byersdz/bitmelo#readme", 30 | "devDependencies": { 31 | "babel-jest": "^24.9.0", 32 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 33 | "css-loader": "^2.1.0", 34 | "eslint": "^5.14.0", 35 | "eslint-config-airbnb-base": "^13.1.0", 36 | "eslint-loader": "^2.1.2", 37 | "eslint-plugin-import": "^2.16.0", 38 | "jest": "^24.1.0", 39 | "style-loader": "^0.23.1", 40 | "wait-on": "^3.3.0", 41 | "webpack": "^4.29.3", 42 | "webpack-cli": "^3.2.3", 43 | "webpack-dev-server": "^3.1.14", 44 | "webpack-merge": "^4.2.1" 45 | }, 46 | "babel": { 47 | "plugins": [ 48 | "transform-es2015-modules-commonjs" 49 | ] 50 | }, 51 | "jest": { 52 | "moduleNameMapper": { 53 | "^SRC(.*)$": "/src$1" 54 | } 55 | }, 56 | "dependencies": { 57 | "esprima": "^4.0.1", 58 | "lodash": "^4.17.21", 59 | "raw-loader": "^3.1.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/ConvertData/ConvertData.test.js: -------------------------------------------------------------------------------- 1 | 2 | import ConvertData from './ConvertData'; 3 | 4 | test( 'array to run min', () => { 5 | expect( ConvertData.arrayToRun( [1, 2, 3] ) ).toEqual( [1, 1, 1, 2, 1, 3] ); 6 | } ); 7 | 8 | test( 'array to run large', () => { 9 | const source = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 6, 6, 6, 6, 2, 2]; 10 | const target = [10, 5, 1, 2, 4, 6, 2, 2]; 11 | expect( ConvertData.arrayToRun( source ) ).toEqual( target ); 12 | } ); 13 | 14 | test( 'array to run mono', () => { 15 | const size = 10000; 16 | const source = new Array( size ); 17 | source.fill( 99 ); 18 | const target = [size, 99]; 19 | expect( ConvertData.arrayToRun( source ) ).toEqual( target ); 20 | } ); 21 | 22 | test( 'run to array min', () => { 23 | expect( ConvertData.runToArray( [1, 1, 1, 2, 1, 3] ) ).toEqual( [1, 2, 3] ); 24 | } ); 25 | 26 | test( 'run to array large', () => { 27 | const source = [10, 5, 1, 2, 4, 6, 2, 2]; 28 | const target = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 6, 6, 6, 6, 2, 2]; 29 | expect( ConvertData.runToArray( source ) ).toEqual( target ); 30 | } ); 31 | 32 | test( 'run to array mono', () => { 33 | const size = 10000; 34 | const source = [size, 99]; 35 | const target = new Array( size ); 36 | target.fill( 99 ); 37 | expect( ConvertData.runToArray( source ) ).toEqual( target ); 38 | } ); 39 | 40 | test( 'array to compressed min', () => { 41 | const testArray = [4, 5, 6]; 42 | const compressed = ConvertData.arrayToCompressedString( testArray ); 43 | const decompressed = ConvertData.compressedStringToArray( compressed ); 44 | 45 | expect( decompressed ).toEqual( testArray ); 46 | } ); 47 | 48 | test( 'array to match known compressed string', () => { 49 | const testArray = [255, 0, 1, 5, 6, 7, 99, 128, 22, 37]; 50 | const compressed = ConvertData.arrayToCompressedString( testArray ); 51 | expect( compressed ).toEqual( 'EwVhBoAZwRnCBs4Ds4CcbbABzmMcAZmSA' ); 52 | } ); 53 | 54 | test( 'compressed string to match known array', () => { 55 | const compressed = 'EwVhBoAZwRnCBs4Ds4CcbbABzmMcAZmSA'; 56 | const decompressed = ConvertData.compressedStringToArray( compressed ); 57 | 58 | expect( decompressed ).toEqual( [255, 0, 1, 5, 6, 7, 99, 128, 22, 37] ); 59 | } ); 60 | -------------------------------------------------------------------------------- /src/lz-string/README.md: -------------------------------------------------------------------------------- 1 | lz-string 2 | ========= 3 | LZ-based compression algorithm for JavaScript 4 | 5 | ## Warning (migrating from version 1.3.4 - nov 2014) 6 | Files have changed locations and name since a recent release. The new release file is in `libs/lz-string.min.js` (or in `libs/lz-string.js` if you don't care for the minified version) 7 | 8 | Sorry about the mess in other repos. This will not happen again. 9 | 10 | ## Note on server side 11 | 12 | If you are using one of the ports of lz-string to decode on the server what was encoded in the browser, you might want to use version 1.3.7 as the version 1.3.8 introduced a slight change in the encoding. While the JS versions are completely cross-compatible, the PHP, Go, ... versions might not be as forgiving. 13 | 14 | ## Install via [npm](https://npmjs.org/) 15 | 16 | ```shell 17 | $ npm install -g lz-string 18 | $ lz-string input.js > output.txt 19 | ``` 20 | 21 | ## Home page 22 | Home page for this program with examples, documentation and a live demo: http://pieroxy.net/blog/pages/lz-string/index.html 23 | 24 | ## Other languages 25 | This lib has numerous ports to other languages, for server side processing, mostly. Here they are: 26 | 27 | 28 | * **Java:** [by Diogo Duailibe](https://github.com/diogoduailibe/lzstring4j) 29 | * **Java:** [by rufushuang, with base64 support and better performances](https://github.com/rufushuang/lz-string4java) 30 | * **C#:** [by Jawa-the-Hutt](https://github.com/jawa-the-hutt/lz-string-csharp) 31 | * **C#:** [by kreudom, another implementation in C#, more up to date](https://github.com/kreudom/lz-string-csharp) 32 | * **PHP:** [by nullpunkt](https://github.com/nullpunkt/lz-string-php) 33 | * **Python3:** [by eduardtomasek](https://github.com/eduardtomasek/lz-string-python) 34 | * **Go** [I helped a friend to write a Go implementation of the decompression algorithm](https://github.com/pieroxy/lz-string-go) 35 | * **Go** [Austin wrote the decompression part as well](https://github.com/austinh115/lz-string-go) 36 | * **Elixir** [by Michael Shapiro](https://github.com/koudelka/elixir-lz-string) 37 | * **C++/QT** [by AmiArt](https://github.com/AmiArt/qt-lzstring) 38 | * **VB.NET** [by gsemac](https://github.com/gsemac/lz-string-vb) 39 | * **Salesforce Apex** (Java like language): [bilal did the port](https://github.com/bilalfastian/LZ4String) 40 | * **Kotlin:** [from Zen Liu](https://github.com/ZenLiuCN/lz-string4k) 41 | * **Dart:** [from skipness](https://github.com/ZenLiuCN/lz-string4k) 42 | * **Haxe:** [from markknol](https://github.com/markknol/hx-lzstring) 43 | -------------------------------------------------------------------------------- /src/Audio/Notes.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Object mapping Node identifiers to numbers. AKA Notes.C4 = 48 4 | */ 5 | const Notes = {}; 6 | 7 | Notes.C0 = 0; 8 | Notes.Cs0 = 1; 9 | Notes.Db0 = 1; 10 | Notes.D0 = 2; 11 | Notes.Ds0 = 3; 12 | Notes.Eb0 = 3; 13 | Notes.E0 = 4; 14 | Notes.F0 = 5; 15 | Notes.Fs0 = 6; 16 | Notes.Gb0 = 6; 17 | Notes.G0 = 7; 18 | Notes.Gs0 = 8; 19 | Notes.Ab0 = 8; 20 | Notes.A0 = 9; 21 | Notes.As0 = 10; 22 | Notes.Bb0 = 10; 23 | Notes.B0 = 11; 24 | 25 | Notes.C1 = 12; 26 | Notes.Cs1 = 13; 27 | Notes.Db1 = 13; 28 | Notes.D1 = 14; 29 | Notes.Ds1 = 15; 30 | Notes.Eb1 = 15; 31 | Notes.E1 = 16; 32 | Notes.F1 = 17; 33 | Notes.Fs1 = 18; 34 | Notes.Gb1 = 18; 35 | Notes.G1 = 19; 36 | Notes.Gs1 = 20; 37 | Notes.Ab1 = 20; 38 | Notes.A1 = 21; 39 | Notes.As1 = 22; 40 | Notes.Bb1 = 22; 41 | Notes.B1 = 23; 42 | 43 | Notes.C2 = 24; 44 | Notes.Cs2 = 25; 45 | Notes.Db2 = 25; 46 | Notes.D2 = 26; 47 | Notes.Ds2 = 27; 48 | Notes.Eb2 = 27; 49 | Notes.E2 = 28; 50 | Notes.F2 = 29; 51 | Notes.Fs2 = 30; 52 | Notes.Gb2 = 30; 53 | Notes.G2 = 31; 54 | Notes.Gs2 = 32; 55 | Notes.Ab2 = 32; 56 | Notes.A2 = 33; 57 | Notes.As2 = 34; 58 | Notes.Bb2 = 34; 59 | Notes.B2 = 35; 60 | 61 | Notes.C3 = 36; 62 | Notes.Cs3 = 37; 63 | Notes.Db3 = 37; 64 | Notes.D3 = 38; 65 | Notes.Ds3 = 39; 66 | Notes.Eb3 = 39; 67 | Notes.E3 = 40; 68 | Notes.F3 = 41; 69 | Notes.Fs3 = 42; 70 | Notes.Gb3 = 42; 71 | Notes.G3 = 43; 72 | Notes.Gs3 = 44; 73 | Notes.Ab3 = 44; 74 | Notes.A3 = 45; 75 | Notes.As3 = 46; 76 | Notes.Bb3 = 46; 77 | Notes.B3 = 47; 78 | 79 | Notes.C4 = 48; 80 | Notes.Cs4 = 49; 81 | Notes.Db4 = 49; 82 | Notes.D4 = 50; 83 | Notes.Ds4 = 51; 84 | Notes.Eb4 = 51; 85 | Notes.E4 = 52; 86 | Notes.F4 = 53; 87 | Notes.Fs4 = 54; 88 | Notes.Gb4 = 54; 89 | Notes.G4 = 55; 90 | Notes.Gs4 = 56; 91 | Notes.Ab4 = 56; 92 | Notes.A4 = 57; 93 | Notes.As4 = 58; 94 | Notes.Bb4 = 58; 95 | Notes.B4 = 59; 96 | 97 | Notes.C5 = 60; 98 | Notes.Cs5 = 61; 99 | Notes.Db5 = 61; 100 | Notes.D5 = 62; 101 | Notes.Ds5 = 63; 102 | Notes.Eb5 = 63; 103 | Notes.E5 = 64; 104 | Notes.F5 = 65; 105 | Notes.Fs5 = 66; 106 | Notes.Gb5 = 66; 107 | Notes.G5 = 67; 108 | Notes.Gs5 = 68; 109 | Notes.Ab5 = 68; 110 | Notes.A5 = 69; 111 | Notes.As5 = 70; 112 | Notes.Bb5 = 70; 113 | Notes.B5 = 71; 114 | 115 | Notes.C6 = 72; 116 | Notes.Cs6 = 73; 117 | Notes.Db6 = 73; 118 | Notes.D6 = 74; 119 | Notes.Ds6 = 75; 120 | Notes.Eb6 = 75; 121 | Notes.E6 = 76; 122 | Notes.F6 = 77; 123 | Notes.Fs6 = 78; 124 | Notes.Gb6 = 78; 125 | Notes.G6 = 79; 126 | Notes.Gs6 = 80; 127 | Notes.Ab6 = 80; 128 | Notes.A6 = 81; 129 | Notes.As6 = 82; 130 | Notes.Bb6 = 82; 131 | Notes.B6 = 83; 132 | 133 | Notes.C7 = 84; 134 | Notes.Cs7 = 85; 135 | Notes.Db7 = 85; 136 | Notes.D7 = 86; 137 | Notes.Ds7 = 87; 138 | Notes.Eb7 = 87; 139 | Notes.E7 = 88; 140 | Notes.F7 = 89; 141 | Notes.Fs7 = 90; 142 | Notes.Gb7 = 90; 143 | Notes.G7 = 91; 144 | Notes.Gs7 = 92; 145 | Notes.Ab7 = 92; 146 | Notes.A7 = 93; 147 | Notes.As7 = 94; 148 | Notes.Bb7 = 94; 149 | Notes.B7 = 95; 150 | 151 | Notes.C8 = 96; 152 | Notes.Cs8 = 97; 153 | Notes.Db8 = 97; 154 | Notes.D8 = 98; 155 | Notes.Ds8 = 99; 156 | Notes.Eb8 = 99; 157 | Notes.E8 = 100; 158 | Notes.F8 = 101; 159 | Notes.Fs8 = 102; 160 | Notes.Gb8 = 102; 161 | Notes.G8 = 103; 162 | Notes.Gs8 = 104; 163 | Notes.Ab8 = 104; 164 | Notes.A8 = 105; 165 | Notes.As8 = 106; 166 | Notes.Bb8 = 106; 167 | Notes.B8 = 107; 168 | 169 | export default Notes; 170 | -------------------------------------------------------------------------------- /src/FontData/Font.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Represents a font. Data is stored similarly to tilesets in an array, with the ability to remap characters 4 | * that have a unicode point larger than the array can hold. 5 | */ 6 | class Font { 7 | constructor( fontData ) { 8 | /** 9 | * The standard width for a character. Used as the default if none is specified for a character. 10 | */ 11 | this.standardWidth = 5; 12 | 13 | /** 14 | * Number of pixels between each character 15 | */ 16 | this.letterSpacing = 1; 17 | 18 | /** 19 | * The tile size of the tile sheet where the characters are drawn 20 | */ 21 | this.tileSize = 16; 22 | 23 | /** 24 | * The number of columns in the tile sheet 25 | */ 26 | this.width = 16; 27 | 28 | /** 29 | * The number of rows in the tile sheet 30 | */ 31 | this.height = 16; 32 | 33 | /** 34 | * The x origin in pixels of the character relative to the bottom left. 35 | */ 36 | this.originX = 1; 37 | 38 | /** 39 | * The y origin in pixels of the character relative to the bottom left. 40 | */ 41 | this.originY = 3; 42 | 43 | /** 44 | * Object mapping unicode points to character information. 45 | * Used for changing a characters size or remapping unicode points larger than what fits on the tile sheet. 46 | */ 47 | this.charData = null; 48 | 49 | /** 50 | * Uint8ClampedArray of the tile sheet data. 51 | * Generated by the constructor. 52 | */ 53 | this.data = null; 54 | 55 | if ( fontData ) { 56 | this.standardWidth = fontData.standardWidth; 57 | this.letterSpacing = fontData.letterSpacing; 58 | 59 | this.tileSize = fontData.tileSize; 60 | this.width = fontData.width; 61 | this.height = fontData.height; 62 | this.originX = fontData.originX; 63 | this.originY = fontData.originY; 64 | this.charData = fontData.charData; 65 | 66 | this.data = new Uint8ClampedArray( this.width * this.height * this.tileSize * this.tileSize ); 67 | const { data } = fontData; 68 | let runPosition = 0; 69 | let dataPosition = 0; 70 | while ( runPosition < data.length ) { 71 | const runLength = data[runPosition]; 72 | const paletteId = parseInt( data[runPosition + 1], 10 ); 73 | for ( let j = 0; j < runLength; j += 1 ) { 74 | this.data[dataPosition] = paletteId; 75 | dataPosition += 1; 76 | } 77 | runPosition += 2; 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Get the base index in the data array for the character 84 | * @param {number} charCode - the unicode point for the character 85 | */ 86 | baseIndexForChar( charCode ) { 87 | let codePoint = charCode; 88 | if ( charCode >= this.width * this.height ) { 89 | const key = charCode.toString(); 90 | 91 | if ( this.charData && this.charData[key] ) { 92 | if ( this.charData[key] !== undefined ) { 93 | codePoint = this.charData[key].remap; 94 | } 95 | } 96 | } 97 | return codePoint * this.tileSize * this.tileSize; 98 | } 99 | 100 | /** 101 | * Get the width of a character 102 | * @param {number} charCode - the unicode point for the character 103 | */ 104 | widthForChar( charCode ) { 105 | const charKey = charCode.toString(); 106 | if ( this.charData && this.charData[charKey] ) { 107 | if ( this.charData[charKey].width !== undefined ) { 108 | return this.charData[charKey].width; 109 | } 110 | } 111 | return this.standardWidth; 112 | } 113 | } 114 | 115 | export default Font; 116 | -------------------------------------------------------------------------------- /src/MapData/TileMap.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Represents a Tile Map. 4 | */ 5 | class TileMap { 6 | constructor( data ) { 7 | /** 8 | * The name of the Tile Map 9 | */ 10 | this.name = ''; 11 | /** 12 | * The number of columns in the Tile Map 13 | */ 14 | this.width = 0; 15 | /** 16 | * The number of rows in the Tile Map 17 | */ 18 | this.height = 0; 19 | 20 | /** 21 | * Array of layer data 22 | */ 23 | this.layers = []; 24 | 25 | if ( data ) { 26 | this.name = data.name; 27 | this.width = data.width; 28 | this.height = data.height; 29 | 30 | if ( data.layers ) { 31 | for ( let i = 0; i < data.layers.length; i += 1 ) { 32 | this.layers.push( this._getTypedArrayFromDataLayer( data.layers[i] ) ); 33 | } 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Convert an array of numbers into a Uint16Array 40 | * @param {number[]} dataLayer 41 | */ 42 | _getTypedArrayFromDataLayer( dataLayer ) { 43 | const result = new Uint16Array( this.width * this.height ); 44 | for ( let i = 0; i < dataLayer.length; i += 1 ) { 45 | result[i] = dataLayer[i]; 46 | } 47 | return result; 48 | } 49 | 50 | /** 51 | * Get the tile GID at a given position 52 | * @param {number} x - The x position 53 | * @param {number} y - The y position 54 | * @param {number} layer - The index of the layer on the tile map you are checking 55 | */ 56 | getTile( x, y, layer = 0 ) { 57 | if ( layer < 0 || layer >= this.layers.length ) { 58 | return -1; 59 | } 60 | 61 | if ( x < 0 || x >= this.width ) { 62 | return -1; 63 | } 64 | 65 | if ( y < 0 || y >= this.height ) { 66 | return -1; 67 | } 68 | 69 | const currentLayer = this.layers[layer]; 70 | const index = y * this.width + x; 71 | return currentLayer[index]; 72 | } 73 | 74 | /** 75 | * Set the tile GID at a given position 76 | * @param {*} gid - The gid 77 | * @param {*} x - The x position 78 | * @param {*} y - The y position 79 | * @param {*} layer - The index of the layer on the tile map you are setting 80 | */ 81 | setTile( gid, x, y, layer = 0 ) { 82 | if ( layer < 0 || layer >= this.layers.length ) { 83 | return; 84 | } 85 | 86 | if ( x < 0 || x >= this.width ) { 87 | return; 88 | } 89 | 90 | if ( y < 0 || y >= this.height ) { 91 | return; 92 | } 93 | 94 | const currentLayer = this.layers[layer]; 95 | const index = y * this.width + x; 96 | currentLayer[index] = gid; 97 | } 98 | 99 | /** 100 | * get an array of the layer data, optionally only getting a subsection of the layer 101 | * @param {number} layer - The layer 102 | * @param {number} startX - The bottom left x position 103 | * @param {number} startY - The bottom left y position 104 | * @param {number} endX - The top right x position 105 | * @param {number} endY - The top right y position 106 | */ 107 | getLayerData( layer = 0, startX = 0, startY = 0, endX = 0, endY = 0 ) { 108 | const finishX = endX || this.width; 109 | const finishY = endY || this.height; 110 | 111 | const width = finishX - startX; 112 | const height = finishY - startY; 113 | 114 | const data = new Array( width * height ); 115 | 116 | for ( let y = 0; y < height; y += 1 ) { 117 | for ( let x = 0; x < width; x += 1 ) { 118 | const targetX = x + startX; 119 | const targetY = y + startY; 120 | 121 | if ( targetX < 0 || targetY < 0 || targetX >= this.width || targetY >= this.height ) { 122 | data[y * width + x] = 0; 123 | continue; 124 | } 125 | 126 | data[y * width + x] = this.getTile( targetX, targetY, layer ); 127 | } 128 | } 129 | 130 | return { data, width, height }; 131 | } 132 | } 133 | 134 | export default TileMap; 135 | -------------------------------------------------------------------------------- /src/FontData/standard.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tileSize": 16, 3 | "originX" : 2, 4 | "originY": 4, 5 | "standardWidth": 5, 6 | "letterSpacing": 1, 7 | "charData": { 8 | "8364": { 9 | "name": "euro", 10 | "remap": 1, 11 | "width": 6 12 | }, 13 | "33": { 14 | "name": "!", 15 | "width": 3 16 | }, 17 | "34": { 18 | "name": "double quote", 19 | "width": 4 20 | }, 21 | "37": { 22 | "name": "percent", 23 | "width": 7 24 | }, 25 | "38": { 26 | "name": "and", 27 | "width": 7 28 | }, 29 | "39": { 30 | "name": "single quote", 31 | "width": 1 32 | }, 33 | "40": { 34 | "name": "(", 35 | "width": 3 36 | }, 37 | "41": { 38 | "name": ")", 39 | "width": 3 40 | }, 41 | "44": { 42 | "name": ",", 43 | "width": 2 44 | }, 45 | "46": { 46 | "name": ".", 47 | "width": 1 48 | }, 49 | "47": { 50 | "name": "f slash", 51 | "width": 7 52 | }, 53 | "49": { 54 | "name": "1", 55 | "width": 3 56 | }, 57 | "58": { 58 | "name": ":", 59 | "width": 1 60 | }, 61 | "59": { 62 | "name": ";", 63 | "width": 2 64 | }, 65 | "60": { 66 | "name": "<", 67 | "width": 4 68 | }, 69 | "62": { 70 | "name": ">", 71 | "width": 4 72 | }, 73 | "64": { 74 | "name": "@", 75 | "width": 7 76 | }, 77 | "91": { 78 | "name": "[", 79 | "width": 3 80 | }, 81 | "92": { 82 | "name": "backslash", 83 | "width": 7 84 | }, 85 | "93": { 86 | "name": "]", 87 | "width": 3 88 | }, 89 | "96": { 90 | "name": "grave", 91 | "width": 2 92 | }, 93 | "105": { 94 | "name": "i", 95 | "width": 1 96 | }, 97 | "107": { 98 | "name": "k", 99 | "width": 4 100 | }, 101 | "108": { 102 | "name": "l", 103 | "width": 1 104 | }, 105 | "123": { 106 | "name": "{", 107 | "width": 3 108 | }, 109 | "124": { 110 | "name": "|", 111 | "width": 1 112 | }, 113 | "125": { 114 | "name": "}", 115 | "width": 3 116 | }, 117 | "161": { 118 | "name": "upside down !", 119 | "width": 3 120 | }, 121 | "164": { 122 | "name": "currency", 123 | "width": 6 124 | }, 125 | "165": { 126 | "name": "yen", 127 | "width": 7 128 | }, 129 | "166": { 130 | "name": "broken bar", 131 | "width": 1 132 | }, 133 | "167": { 134 | "name": "section", 135 | "width": 6 136 | }, 137 | "169": { 138 | "name": "copyright", 139 | "width": 7 140 | }, 141 | "170": { 142 | "name": "f ordinal", 143 | "width": 4 144 | }, 145 | "171": { 146 | "name": "<<", 147 | "width": 6 148 | }, 149 | "172": { 150 | "name": "not", 151 | "width": 6 152 | }, 153 | "174": { 154 | "name": "(r)", 155 | "width": 8 156 | }, 157 | "176": { 158 | "name": "degree", 159 | "width": 4 160 | }, 161 | "178": { 162 | "name": "^2", 163 | "width": 3 164 | }, 165 | "179": { 166 | "name": "^3", 167 | "width": 3 168 | }, 169 | "180": { 170 | "name": "acute", 171 | "width": 2 172 | }, 173 | "182": { 174 | "name": "pilcrow", 175 | "width": 3 176 | }, 177 | "183": { 178 | "name": "middle dot", 179 | "width": 1 180 | }, 181 | "185": { 182 | "name": "^1", 183 | "width": 4 184 | }, 185 | "186": { 186 | "name": "m ordinal", 187 | "width": 4 188 | }, 189 | "187": { 190 | "name": ">>", 191 | "width": 6 192 | }, 193 | "188": { 194 | "name": "1/4", 195 | "width": 7 196 | }, 197 | "189": { 198 | "name": "1/2", 199 | "width": 7 200 | }, 201 | "190": { 202 | "name": "3/4", 203 | "width": 7 204 | }, 205 | "236": { 206 | "name": "i", 207 | "width": 3 208 | }, 209 | "237": { 210 | "name": "i", 211 | "width": 3 212 | }, 213 | "238": { 214 | "name": "i", 215 | "width": 3 216 | }, 217 | "239": { 218 | "name": "i", 219 | "width": 3 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Audio/Frequencies.js: -------------------------------------------------------------------------------- 1 | 2 | import Notes from './Notes'; 3 | 4 | /** 5 | * Array of Frequency values for notes. 6 | */ 7 | const Frequencies = new Array( 108 ); 8 | 9 | Frequencies[Notes.C0] = 16.35160; 10 | Frequencies[Notes.Cs0] = 17.32391; 11 | Frequencies[Notes.D0] = 18.35405; 12 | Frequencies[Notes.Ds0] = 19.44544; 13 | Frequencies[Notes.E0] = 20.60172; 14 | Frequencies[Notes.F0] = 21.82676; 15 | Frequencies[Notes.Fs0] = 23.12465; 16 | Frequencies[Notes.G0] = 24.49971; 17 | Frequencies[Notes.Gs0] = 25.95654; 18 | Frequencies[Notes.A0] = 27.50000; 19 | Frequencies[Notes.As0] = 29.13524; 20 | Frequencies[Notes.B0] = 30.86771; 21 | 22 | Frequencies[Notes.C1] = 32.70320; 23 | Frequencies[Notes.Cs1] = 34.64783; 24 | Frequencies[Notes.D1] = 36.70810; 25 | Frequencies[Notes.Ds1] = 38.89087; 26 | Frequencies[Notes.E1] = 41.20344; 27 | Frequencies[Notes.F1] = 43.65353; 28 | Frequencies[Notes.Fs1] = 46.24930; 29 | Frequencies[Notes.G1] = 48.99943; 30 | Frequencies[Notes.Gs1] = 51.91309; 31 | Frequencies[Notes.A1] = 55.00000; 32 | Frequencies[Notes.As1] = 58.27047; 33 | Frequencies[Notes.B1] = 61.73541; 34 | 35 | Frequencies[Notes.C2] = 65.40639; 36 | Frequencies[Notes.Cs2] = 69.29566; 37 | Frequencies[Notes.D2] = 73.41619; 38 | Frequencies[Notes.Ds2] = 77.78175; 39 | Frequencies[Notes.E2] = 82.40689; 40 | Frequencies[Notes.F2] = 87.30706; 41 | Frequencies[Notes.Fs2] = 92.49861; 42 | Frequencies[Notes.G2] = 97.99886; 43 | Frequencies[Notes.Gs2] = 103.8262; 44 | Frequencies[Notes.A2] = 110.0000; 45 | Frequencies[Notes.As2] = 116.5409; 46 | Frequencies[Notes.B2] = 123.4708; 47 | 48 | Frequencies[Notes.C3] = 130.8128; 49 | Frequencies[Notes.Cs3] = 138.5913; 50 | Frequencies[Notes.D3] = 146.8324; 51 | Frequencies[Notes.Ds3] = 155.5635; 52 | Frequencies[Notes.E3] = 164.8138; 53 | Frequencies[Notes.F3] = 174.6141; 54 | Frequencies[Notes.Fs3] = 184.9972; 55 | Frequencies[Notes.G3] = 195.9977; 56 | Frequencies[Notes.Gs3] = 207.6523; 57 | Frequencies[Notes.A3] = 220.0000; 58 | Frequencies[Notes.As3] = 233.0819; 59 | Frequencies[Notes.B3] = 246.9417; 60 | 61 | Frequencies[Notes.C4] = 261.6256; 62 | Frequencies[Notes.Cs4] = 277.1826; 63 | Frequencies[Notes.D4] = 293.6648; 64 | Frequencies[Notes.Ds4] = 311.1270; 65 | Frequencies[Notes.E4] = 329.6276; 66 | Frequencies[Notes.F4] = 349.2282; 67 | Frequencies[Notes.Fs4] = 369.9944; 68 | Frequencies[Notes.G4] = 391.9954; 69 | Frequencies[Notes.Gs4] = 415.3047; 70 | Frequencies[Notes.A4] = 440.0000; 71 | Frequencies[Notes.As4] = 466.1638; 72 | Frequencies[Notes.B4] = 493.8833; 73 | 74 | Frequencies[Notes.C5] = 523.2511; 75 | Frequencies[Notes.Cs5] = 554.3653; 76 | Frequencies[Notes.D5] = 587.3295; 77 | Frequencies[Notes.Ds5] = 622.2540; 78 | Frequencies[Notes.E5] = 659.2551; 79 | Frequencies[Notes.F5] = 698.4565; 80 | Frequencies[Notes.Fs5] = 739.9888; 81 | Frequencies[Notes.G5] = 783.9909; 82 | Frequencies[Notes.Gs5] = 830.6094; 83 | Frequencies[Notes.A5] = 880.0000; 84 | Frequencies[Notes.As5] = 932.3275; 85 | Frequencies[Notes.B5] = 987.7666; 86 | 87 | Frequencies[Notes.C6] = 1046.502; 88 | Frequencies[Notes.Cs6] = 1108.731; 89 | Frequencies[Notes.D6] = 1174.659; 90 | Frequencies[Notes.Ds6] = 1244.508; 91 | Frequencies[Notes.E6] = 1318.510; 92 | Frequencies[Notes.F6] = 1396.913; 93 | Frequencies[Notes.Fs6] = 1479.978; 94 | Frequencies[Notes.G6] = 1567.982; 95 | Frequencies[Notes.Gs6] = 1661.219; 96 | Frequencies[Notes.A6] = 1760.000; 97 | Frequencies[Notes.As6] = 1864.655; 98 | Frequencies[Notes.B6] = 1975.533; 99 | 100 | Frequencies[Notes.C7] = 2093.005; 101 | Frequencies[Notes.Cs7] = 2217.461; 102 | Frequencies[Notes.D7] = 2349.318; 103 | Frequencies[Notes.Ds7] = 2489.016; 104 | Frequencies[Notes.E7] = 2637.020; 105 | Frequencies[Notes.F7] = 2793.826; 106 | Frequencies[Notes.Fs7] = 2959.955; 107 | Frequencies[Notes.G7] = 3135.963; 108 | Frequencies[Notes.Gs7] = 3322.438; 109 | Frequencies[Notes.A7] = 3520.000; 110 | Frequencies[Notes.As7] = 3729.310; 111 | Frequencies[Notes.B7] = 3951.066; 112 | 113 | Frequencies[Notes.C8] = 4186.009; 114 | Frequencies[Notes.Cs8] = 4434.922; 115 | Frequencies[Notes.D8] = 4698.636; 116 | Frequencies[Notes.Ds8] = 4978.032; 117 | Frequencies[Notes.E8] = 5274.041; 118 | Frequencies[Notes.F8] = 5587.652; 119 | Frequencies[Notes.Fs8] = 5919.911; 120 | Frequencies[Notes.G8] = 6271.927; 121 | Frequencies[Notes.Gs8] = 6644.875; 122 | Frequencies[Notes.A8] = 7040.000; 123 | Frequencies[Notes.As8] = 7458.620; 124 | Frequencies[Notes.B8] = 7902.133; 125 | 126 | export default Frequencies; 127 | -------------------------------------------------------------------------------- /src/lz-string/libs/lz-string.min.js: -------------------------------------------------------------------------------- 1 | var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString); 2 | -------------------------------------------------------------------------------- /src/Input/Keys.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Object mapping KeyCodes to key numbers. 4 | * Keys.codesToKeyCodes maps key numbers to KeyCodes. 5 | */ 6 | const Keys = {}; 7 | 8 | Keys.NONE = 0; 9 | Keys.BACKSPACE = 8; 10 | Keys.TAB = 9; 11 | Keys.ENTER = 13; 12 | Keys.SHIFT = 16; 13 | Keys.CTRL = 17; 14 | Keys.ALT = 18; 15 | Keys.PAUSE_BREAK = 19; 16 | Keys.CAPSLOCK = 20; 17 | Keys.ESCAPE = 27; 18 | Keys.SPACE = 32; 19 | Keys.PAGE_UP = 33; 20 | Keys.PAGE_DOWN = 34; 21 | Keys.END = 35; 22 | Keys.HOME = 36; 23 | Keys.LEFT_ARROW = 37; 24 | Keys.UP_ARROW = 38; 25 | Keys.RIGHT_ARROW = 39; 26 | Keys.DOWN_ARROW = 40; 27 | Keys.INSERT = 45; 28 | Keys.DELETE = 46; 29 | Keys.ZERO = 48; 30 | Keys.ONE = 49; 31 | Keys.TWO = 50; 32 | Keys.THREE = 51; 33 | Keys.FOUR = 52; 34 | Keys.FIVE = 53; 35 | Keys.SIX = 54; 36 | Keys.SEVEN = 55; 37 | Keys.EIGHT = 56; 38 | Keys.NINE = 57; 39 | Keys.A_KEY = 65; 40 | Keys.B_KEY = 66; 41 | Keys.C_KEY = 67; 42 | Keys.D_KEY = 68; 43 | Keys.E_KEY = 69; 44 | Keys.F_KEY = 70; 45 | Keys.G_KEY = 71; 46 | Keys.H_KEY = 72; 47 | Keys.I_KEY = 73; 48 | Keys.J_KEY = 74; 49 | Keys.K_KEY = 75; 50 | Keys.L_KEY = 76; 51 | Keys.M_KEY = 77; 52 | Keys.N_KEY = 78; 53 | Keys.O_KEY = 79; 54 | Keys.P_KEY = 80; 55 | Keys.Q_KEY = 81; 56 | Keys.R_KEY = 82; 57 | Keys.S_KEY = 83; 58 | Keys.T_KEY = 84; 59 | Keys.U_KEY = 85; 60 | Keys.V_KEY = 86; 61 | Keys.W_KEY = 87; 62 | Keys.X_KEY = 88; 63 | Keys.Y_KEY = 89; 64 | Keys.Z_KEY = 90; 65 | Keys.LEFT_WINDOW = 91; 66 | Keys.RIGHT_WINDOW = 92; 67 | Keys.SELECT = 93; 68 | Keys.NUM_ZERO = 96; 69 | Keys.NUM_ONE = 97; 70 | Keys.NUM_TWO = 98; 71 | Keys.NUM_THREE = 99; 72 | Keys.NUM_FOUR = 100; 73 | Keys.NUM_FIVE = 101; 74 | Keys.NUM_SIX = 102; 75 | Keys.NUM_SEVEN = 103; 76 | Keys.NUM_EIGHT = 104; 77 | Keys.NUM_NINE = 105; 78 | Keys.MULTIPLY = 106; 79 | Keys.ADD = 107; 80 | Keys.SUBTRACT = 109; 81 | Keys.DECIMAL_POINT = 110; 82 | Keys.DIVIDE = 111; 83 | Keys.F1 = 112; 84 | Keys.F2 = 113; 85 | Keys.F3 = 114; 86 | Keys.F4 = 115; 87 | Keys.F5 = 116; 88 | Keys.F6 = 117; 89 | Keys.F7 = 118; 90 | Keys.F8 = 119; 91 | Keys.F9 = 120; 92 | Keys.F10 = 121; 93 | Keys.F11 = 122; 94 | Keys.F12 = 123; 95 | Keys.NUM_LOCK = 144; 96 | Keys.SCROLL_LOCK = 145; 97 | Keys.SEMI_COLON = 186; 98 | Keys.EQUAL_SIGN = 187; 99 | Keys.COMMA = 188; 100 | Keys.DASH = 189; 101 | Keys.PERIOD = 190; 102 | Keys.FORWARD_SLASH = 191; 103 | Keys.GRAVE_ACCENT = 192; 104 | Keys.OPEN_BRACKET = 219; 105 | Keys.BACK_SLASH = 220; 106 | Keys.CLOSE_BRACKET = 221; 107 | Keys.SINGLE_QUOTE = 222; 108 | 109 | Keys.codesToKeyCodes = { 110 | Backspace: Keys.BACKSPACE, 111 | Tab: Keys.TAB, 112 | Enter: Keys.ENTER, 113 | ShiftLeft: Keys.SHIFT, 114 | ShiftRight: Keys.SHIFT, 115 | ControlLeft: Keys.CTRL, 116 | ControlRight: Keys.CTRL, 117 | AltLeft: Keys.ALT, 118 | AltRight: Keys.ALT, 119 | CapsLock: Keys.CAPSLOCK, 120 | Escape: Keys.ESCAPE, 121 | Space: Keys.SPACE, 122 | PageUp: Keys.PAGE_UP, 123 | PageDown: Keys.PAGE_DOWN, 124 | End: Keys.END, 125 | Home: Keys.HOME, 126 | ArrowLeft: Keys.LEFT_ARROW, 127 | ArrowUp: Keys.UP_ARROW, 128 | ArrowRight: Keys.RIGHT_ARROW, 129 | ArrowDown: Keys.DOWN_ARROW, 130 | Insert: Keys.INSERT, 131 | Delete: Keys.DELETE, 132 | Digit0: Keys.ZERO, 133 | Digit1: Keys.ONE, 134 | Digit2: Keys.TWO, 135 | Digit3: Keys.THREE, 136 | Digit4: Keys.FOUR, 137 | Digit5: Keys.FIVE, 138 | Digit6: Keys.SIX, 139 | Digit7: Keys.SEVEN, 140 | Digit8: Keys.EIGHT, 141 | Digit9: Keys.NINE, 142 | KeyA: Keys.A_KEY, 143 | KeyB: Keys.B_KEY, 144 | KeyC: Keys.C_KEY, 145 | KeyD: Keys.D_KEY, 146 | KeyE: Keys.E_KEY, 147 | KeyF: Keys.F_KEY, 148 | KeyG: Keys.G_KEY, 149 | KeyH: Keys.H_KEY, 150 | KeyI: Keys.I_KEY, 151 | KeyJ: Keys.J_KEY, 152 | KeyK: Keys.K_KEY, 153 | KeyL: Keys.L_KEY, 154 | KeyM: Keys.M_KEY, 155 | KeyN: Keys.N_KEY, 156 | KeyO: Keys.O_KEY, 157 | KeyP: Keys.P_KEY, 158 | KeyQ: Keys.Q_KEY, 159 | KeyR: Keys.R_KEY, 160 | KeyS: Keys.S_KEY, 161 | KeyT: Keys.T_KEY, 162 | KeyU: Keys.U_KEY, 163 | KeyV: Keys.V_KEY, 164 | KeyW: Keys.W_KEY, 165 | KeyX: Keys.X_KEY, 166 | KeyY: Keys.Y_KEY, 167 | KeyZ: Keys.Z_KEY, 168 | Select: Keys.SELECT, 169 | Numpad0: Keys.NUM_ZERO, 170 | Numpad1: Keys.NUM_ONE, 171 | Numpad2: Keys.NUM_TWO, 172 | Numpad3: Keys.NUM_THREE, 173 | Numpad4: Keys.NUM_FOUR, 174 | Numpad5: Keys.NUM_FIVE, 175 | Numpad6: Keys.NUM_SIX, 176 | Numpad7: Keys.NUM_SEVEN, 177 | Numpad8: Keys.NUM_EIGHT, 178 | Numpad9: Keys.NUM_NINE, 179 | NumpadMultiply: Keys.MULTIPLY, 180 | NumpadAdd: Keys.ADD, 181 | NumpadSubtract: Keys.SUBTRACT, 182 | NumpadDecimal: Keys.DECIMAL_POINT, 183 | NumpadDivide: Keys.DIVIDE, 184 | F1: Keys.F1, 185 | F2: Keys.F2, 186 | F3: Keys.F3, 187 | F4: Keys.F4, 188 | F5: Keys.F5, 189 | F6: Keys.F6, 190 | F7: Keys.F7, 191 | F8: Keys.F8, 192 | F9: Keys.F9, 193 | F10: Keys.F10, 194 | F11: Keys.F11, 195 | F12: Keys.F12, 196 | NumLock: Keys.NUM_LOCK, 197 | ScrollLock: Keys.SCROLL_LOCK, 198 | Semicolon: Keys.SEMI_COLON, 199 | Equal: Keys.EQUAL_SIGN, 200 | NumpadEqual: Keys.EQUAL_SIGN, 201 | Comma: Keys.COMMA, 202 | NumpadComma: Keys.COMMA, 203 | Period: Keys.PERIOD, 204 | Slash: Keys.FORWARD_SLASH, 205 | Backquote: Keys.GRAVE_ACCENT, 206 | BracketLeft: Keys.OPEN_BRACKET, 207 | Backslash: Keys.BACK_SLASH, 208 | BracketRight: Keys.CLOSE_BRACKET, 209 | Quote: Keys.SINGLE_QUOTE, 210 | Minus: Keys.DASH, 211 | NumpadEnter: Keys.ENTER, 212 | }; 213 | 214 | export default Keys; 215 | -------------------------------------------------------------------------------- /src/Audio/Sound.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Represents a sound that can be played. 4 | */ 5 | class Sound { 6 | constructor( data ) { 7 | /** 8 | * Array of volume values for the duration of the sound. 9 | * Each value must be 0 to 15. 10 | * Length of the array must always be 32. 11 | */ 12 | this.volumeTics = new Int8Array( 32 ); 13 | 14 | /** 15 | * Array of pitch values for the duration of the sound. 16 | * Each value must be -10 to 10. 17 | * Length of the array must always be 32. 18 | */ 19 | this.pitchTics = new Int8Array( 32 ); 20 | 21 | /** 22 | * Array of arp values for the duration of the sound. 23 | * Each value is the number of notes above or below the note of the sound. 24 | * Length of the array must always be 32. 25 | */ 26 | this.arpTics = new Int8Array( 32 ); 27 | 28 | /** 29 | * Should we loop over the volumeTics when playing a sound? 30 | * If false the last volume tic will be played infinitely once reaching it. 31 | */ 32 | this.useVolumeLoop = false; 33 | 34 | /** 35 | * The volumeTics index we will start looping at if useVolumeLoop is true. 36 | */ 37 | this.volumeLoopStart = -1; 38 | 39 | /** 40 | * The volumeTics index we will stop looping at if useVolumeLoop is true. 41 | */ 42 | this.volumeLoopEnd = -1; 43 | 44 | /** 45 | * Should we loop over the pitchTics when playing a sound? 46 | * If false the last pitch tic will be played infinitely once reaching it. 47 | */ 48 | this.usePitchLoop = false; 49 | 50 | /** 51 | * The pitchTics index we will start looping at if usePitchLoop is true. 52 | */ 53 | this.pitchLoopStart = -1; 54 | 55 | /** 56 | * The pitchTics index we will stop looping at if usePitchLoop is true. 57 | */ 58 | this.pitchLoopEnd = -1; 59 | 60 | /** 61 | * Should we loop over the arpTics when playing a sound? 62 | * If false the last arp tic will be played infinitely once reaching it. 63 | */ 64 | this.useArpLoop = false; 65 | 66 | /** 67 | * The arpTics index we will start looping at if useArpLoop is true. 68 | */ 69 | this.arpLoopStart = -1; 70 | 71 | /** 72 | * The arpTics index we will stop looping at id useArpLoop is true. 73 | */ 74 | this.arpLoopEnd = -1; 75 | 76 | /** 77 | * The Wave index used by this sound. 78 | * 0 is Sine. 79 | * 1 is Triangle. 80 | * 2 is Square. 81 | * 3 is Sawtooth. 82 | */ 83 | this.wave = 0; 84 | 85 | /** 86 | * The pitch scale used by the pitchTics. Value is in cents. 87 | */ 88 | this.pitchScale = 10; 89 | 90 | /** 91 | * Number of tics to ramp down playing of the sound. 92 | * Must always be at least 1. 93 | */ 94 | this.releaseLength = 1; 95 | 96 | /** 97 | * The ramp type used when ramping down after stopping a sound. 98 | * 'linear' uses a linear ramp 99 | * 'expo' uses an exponential ramp 100 | */ 101 | this.releaseMode = Sound.RELEASE_LINEAR; 102 | 103 | /** 104 | * Is an infinite sound currently playing on this sound? 105 | */ 106 | this.isPlayingInfiniteSound = false; 107 | 108 | /** 109 | * The start time of the current infinite sound. 110 | */ 111 | this.infiniteStartTime = 0; 112 | 113 | /** 114 | * The oscillator node of the current infinite sound. 115 | */ 116 | this.infiniteOsc = null; 117 | 118 | /** 119 | * The gain node of the current infinite sound. 120 | */ 121 | this.infiniteGain = null; 122 | 123 | /** 124 | * The duration of a tic in the current infinite sound. 125 | */ 126 | this.infiniteTicDuration = 0; 127 | 128 | /** 129 | * The number of tics played in the current infinite sound. 130 | */ 131 | this.infiniteTicsPlayed = 0; 132 | 133 | /** 134 | * The volume of the current infinite sound. 135 | */ 136 | this.infiniteVolume = 0; 137 | 138 | /** 139 | * The note of the current infinite sound. 140 | */ 141 | this.infiniteNote = 0; 142 | 143 | /** 144 | * The last volume tic played. 145 | */ 146 | this.lastVolumeTic = 0; 147 | 148 | /** 149 | * The last pitch tic played. 150 | */ 151 | this.lastPitchTic = 0; 152 | 153 | /** 154 | * The last arp tic played. 155 | */ 156 | this.lastArpTic = 0; 157 | 158 | if ( data ) { 159 | const { 160 | volumeTics, 161 | pitchTics, 162 | arpTics, 163 | wave, 164 | pitchScale, 165 | releaseLength, 166 | releaseMode, 167 | useVolumeLoop, 168 | volumeLoopStart, 169 | volumeLoopEnd, 170 | usePitchLoop, 171 | pitchLoopStart, 172 | pitchLoopEnd, 173 | useArpLoop, 174 | arpLoopStart, 175 | arpLoopEnd, 176 | } = data; 177 | 178 | if ( volumeTics && Array.isArray( volumeTics ) ) { 179 | for ( let i = 0; i < volumeTics.length; i += 1 ) { 180 | this.volumeTics[i] = volumeTics[i]; 181 | } 182 | } 183 | 184 | if ( pitchTics && Array.isArray( pitchTics ) ) { 185 | for ( let i = 0; i < pitchTics.length; i += 1 ) { 186 | this.pitchTics[i] = pitchTics[i]; 187 | } 188 | } 189 | 190 | if ( arpTics && Array.isArray( arpTics ) ) { 191 | for ( let i = 0; i < arpTics.length; i += 1 ) { 192 | this.arpTics[i] = arpTics[i]; 193 | } 194 | } 195 | 196 | this.wave = wave; 197 | this.pitchScale = pitchScale; 198 | this.releaseLength = releaseLength; 199 | this.releaseMode = releaseMode; 200 | this.useVolumeLoop = useVolumeLoop; 201 | this.volumeLoopStart = volumeLoopStart; 202 | this.volumeLoopEnd = volumeLoopEnd; 203 | this.usePitchLoop = usePitchLoop; 204 | this.pitchLoopStart = pitchLoopStart; 205 | this.pitchLoopEnd = pitchLoopEnd; 206 | this.useArpLoop = useArpLoop; 207 | this.arpLoopStart = arpLoopStart; 208 | this.arpLoopEnd = arpLoopEnd; 209 | } 210 | } 211 | } 212 | 213 | Sound.RELEASE_LINEAR = 'linear'; 214 | Sound.RELEASE_EXPO = 'expo'; 215 | 216 | export default Sound; 217 | -------------------------------------------------------------------------------- /src/lz-string/reference/lz-string-1.0.2.js: -------------------------------------------------------------------------------- 1 | // Copyright © 2013 Pieroxy 2 | // This work is free. You can redistribute it and/or modify it 3 | // under the terms of the WTFPL, Version 2 4 | // For more information see LICENSE.txt or http://www.wtfpl.net/ 5 | // 6 | // LZ-based compression algorithm, version 1.0.2-rc1 7 | var LZString = { 8 | 9 | writeBit : function(value, data) { 10 | data.val = (data.val << 1) | value; 11 | if (data.position == 15) { 12 | data.position = 0; 13 | data.string += String.fromCharCode(data.val); 14 | data.val = 0; 15 | } else { 16 | data.position++; 17 | } 18 | }, 19 | 20 | writeBits : function(numBits, value, data) { 21 | if (typeof(value)=="string") 22 | value = value.charCodeAt(0); 23 | for (var i=0 ; i> 1; 26 | } 27 | }, 28 | 29 | produceW : function (context) { 30 | if (Object.prototype.hasOwnProperty.call(context.dictionaryToCreate,context.w)) { 31 | if (context.w.charCodeAt(0)<256) { 32 | this.writeBits(context.numBits, 0, context.data); 33 | this.writeBits(8, context.w, context.data); 34 | } else { 35 | this.writeBits(context.numBits, 1, context.data); 36 | this.writeBits(16, context.w, context.data); 37 | } 38 | this.decrementEnlargeIn(context); 39 | delete context.dictionaryToCreate[context.w]; 40 | } else { 41 | this.writeBits(context.numBits, context.dictionary[context.w], context.data); 42 | } 43 | this.decrementEnlargeIn(context); 44 | }, 45 | 46 | decrementEnlargeIn : function(context) { 47 | context.enlargeIn--; 48 | if (context.enlargeIn == 0) { 49 | context.enlargeIn = Math.pow(2, context.numBits); 50 | context.numBits++; 51 | } 52 | }, 53 | 54 | compress: function (uncompressed) { 55 | var context = { 56 | dictionary: {}, 57 | dictionaryToCreate: {}, 58 | c:"", 59 | wc:"", 60 | w:"", 61 | enlargeIn: 2, // Compensate for the first entry which should not count 62 | dictSize: 3, 63 | numBits: 2, 64 | result: "", 65 | data: {string:"", val:0, position:0} 66 | }, i; 67 | 68 | for (i = 0; i < uncompressed.length; i += 1) { 69 | context.c = uncompressed.charAt(i); 70 | if (!Object.prototype.hasOwnProperty.call(context.dictionary,context.c)) { 71 | context.dictionary[context.c] = context.dictSize++; 72 | context.dictionaryToCreate[context.c] = true; 73 | } 74 | 75 | context.wc = context.w + context.c; 76 | if (Object.prototype.hasOwnProperty.call(context.dictionary,context.wc)) { 77 | context.w = context.wc; 78 | } else { 79 | this.produceW(context); 80 | // Add wc to the dictionary. 81 | context.dictionary[context.wc] = context.dictSize++; 82 | context.w = String(context.c); 83 | } 84 | } 85 | 86 | // Output the code for w. 87 | if (context.w !== "") { 88 | this.produceW(context); 89 | } 90 | 91 | // Mark the end of the stream 92 | this.writeBits(context.numBits, 2, context.data); 93 | 94 | // Flush the last char 95 | while (context.data.val>0) this.writeBit(0,context.data) 96 | return context.data.string; 97 | }, 98 | 99 | readBit : function(data) { 100 | var res = data.val & data.position; 101 | data.position >>= 1; 102 | if (data.position == 0) { 103 | data.position = 32768; 104 | data.val = data.string.charCodeAt(data.index++); 105 | } 106 | //data.val = (data.val << 1); 107 | return res>0 ? 1 : 0; 108 | }, 109 | 110 | readBits : function(numBits, data) { 111 | var res = 0; 112 | var maxpower = Math.pow(2,numBits); 113 | var power=1; 114 | while (power!=maxpower) { 115 | res |= this.readBit(data) * power; 116 | power <<= 1; 117 | } 118 | return res; 119 | }, 120 | 121 | decompress: function (compressed) { 122 | var dictionary = {}, 123 | next, 124 | enlargeIn = 4, 125 | dictSize = 4, 126 | numBits = 3, 127 | entry = "", 128 | result = "", 129 | i, 130 | w, 131 | c, 132 | errorCount=0, 133 | literal, 134 | data = {string:compressed, val:compressed.charCodeAt(0), position:32768, index:1}; 135 | 136 | for (i = 0; i < 3; i += 1) { 137 | dictionary[i] = i; 138 | } 139 | 140 | next = this.readBits(2, data); 141 | switch (next) { 142 | case 0: 143 | c = String.fromCharCode(this.readBits(8, data)); 144 | break; 145 | case 1: 146 | c = String.fromCharCode(this.readBits(16, data)); 147 | break; 148 | case 2: 149 | return ""; 150 | } 151 | dictionary[3] = c; 152 | w = result = c; 153 | while (true) { 154 | c = this.readBits(numBits, data); 155 | 156 | switch (c) { 157 | case 0: 158 | if (errorCount++ > 10000) return "Error"; 159 | c = String.fromCharCode(this.readBits(8, data)); 160 | dictionary[dictSize++] = c; 161 | c = dictSize-1; 162 | enlargeIn--; 163 | break; 164 | case 1: 165 | c = String.fromCharCode(this.readBits(16, data)); 166 | dictionary[dictSize++] = c; 167 | c = dictSize-1; 168 | enlargeIn--; 169 | break; 170 | case 2: 171 | return result; 172 | } 173 | 174 | if (enlargeIn == 0) { 175 | enlargeIn = Math.pow(2, numBits); 176 | numBits++; 177 | } 178 | 179 | if (dictionary[c]) { 180 | entry = dictionary[c]; 181 | } else { 182 | if (c === dictSize) { 183 | entry = w + w.charAt(0); 184 | } else { 185 | return null; 186 | } 187 | } 188 | result += entry; 189 | 190 | // Add w+entry[0] to the dictionary. 191 | dictionary[dictSize++] = w + entry.charAt(0); 192 | enlargeIn--; 193 | 194 | w = entry; 195 | 196 | if (enlargeIn == 0) { 197 | enlargeIn = Math.pow(2, numBits); 198 | numBits++; 199 | } 200 | 201 | } 202 | return result; 203 | } 204 | }; 205 | -------------------------------------------------------------------------------- /src/ConvertProject/ConvertProject.js: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get'; 2 | 3 | import ConvertData from '../ConvertData/ConvertData'; 4 | 5 | class ConvertProject { 6 | static convertProjectTilemaps( projectTilemaps ) { 7 | const tilemaps = []; 8 | 9 | for ( let i = 0; i < projectTilemaps.length; i += 1 ) { 10 | const tilemap = {}; 11 | tilemap.width = projectTilemaps[i].width; 12 | tilemap.height = projectTilemaps[i].height; 13 | tilemap.name = projectTilemaps[i].name; 14 | tilemap.layers = []; 15 | 16 | for ( let j = 0; j < projectTilemaps[i].layers.length; j += 1 ) { 17 | const currentLayer = projectTilemaps[i].layers[j]; 18 | const format = currentLayer.format ? currentLayer.format : 'array'; 19 | 20 | if ( format === 'rc' ) { 21 | // run and compressed string 22 | const runData = ConvertData.compressedStringToArray( currentLayer.data ); 23 | tilemap.layers.push( ConvertData.runToArray( runData ) ); 24 | } 25 | else if ( format === 'c' ) { 26 | // compressed string 27 | tilemap.layers.push( ConvertData.compressedStringToArray( currentLayer.data ) ); 28 | } 29 | else if ( format === 'run' ) { 30 | // run length encoding 31 | tilemap.layers.push( ConvertData.runToArray( currentLayer.data ) ); 32 | } 33 | else { 34 | // array 35 | tilemap.layers.push( ...[currentLayer.data] ); 36 | } 37 | } 38 | 39 | tilemaps.push( tilemap ); 40 | } 41 | 42 | return tilemaps; 43 | } 44 | 45 | static convertProjectTilesets( projectTilesets, tileSize ) { 46 | const tilesets = []; 47 | 48 | for ( let i = 0; i < projectTilesets.length; i += 1 ) { 49 | const currentProjectTileset = projectTilesets[i]; 50 | const tileset = {}; 51 | const layerData = []; 52 | 53 | for ( let j = 0; j < currentProjectTileset.layers.length; j += 1 ) { 54 | const currentLayer = currentProjectTileset.layers[j]; 55 | if ( currentLayer.isVisible ) { 56 | let dataArray = null; 57 | 58 | const format = currentLayer.format ? currentLayer.format : 'array'; 59 | 60 | if ( format === 'rc' ) { 61 | // run and compressed string 62 | const runArray = ConvertData.compressedStringToArray( currentLayer.data ); 63 | dataArray = ConvertProject.convertToTilesetArray( 64 | ConvertData.runToArray( runArray ), 65 | currentProjectTileset.width * tileSize, 66 | currentProjectTileset.height * tileSize, 67 | tileSize, 68 | ); 69 | } 70 | else if ( format === 'c' ) { 71 | // compressed string 72 | dataArray = ConvertProject.convertToTilesetArray( 73 | ConvertData.compressedStringToArray( currentLayer.data ), 74 | currentProjectTileset.width * tileSize, 75 | currentProjectTileset.height * tileSize, 76 | tileSize, 77 | ); 78 | } 79 | else if ( format === 'run' ) { 80 | // run length encoding 81 | dataArray = ConvertProject.convertToTilesetArray( 82 | ConvertData.runToArray( currentLayer.data ), 83 | currentProjectTileset.width * tileSize, 84 | currentProjectTileset.height * tileSize, 85 | tileSize, 86 | ); 87 | } 88 | else { 89 | // array 90 | dataArray = ConvertProject.convertToTilesetArray( 91 | currentLayer.data, 92 | currentProjectTileset.width * tileSize, 93 | currentProjectTileset.height * tileSize, 94 | tileSize, 95 | ); 96 | } 97 | 98 | layerData.push( dataArray ); 99 | } 100 | } 101 | 102 | // flatten the layer data into 1 layer, ignoring transparent pixels 103 | if ( layerData.length > 0 ) { 104 | tileset.data = new Array( layerData[0].length ); 105 | tileset.data.fill( 0 ); 106 | for ( let position = 0; position < layerData[0].length; position += 1 ) { 107 | for ( let layer = 0; layer < layerData.length; layer += 1 ) { 108 | const currentValue = layerData[layer][position]; 109 | if ( currentValue ) { 110 | tileset.data[position] = currentValue; 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | else { 117 | tileset.data = new Array( currentProjectTileset.width * currentProjectTileset.height * tileSize * tileSize ); 118 | tileset.data.fill( 0 ); 119 | } 120 | 121 | // add the flags 122 | let flags = get( currentProjectTileset, 'flags' ); 123 | if ( !flags || flags.length !== currentProjectTileset.width * currentProjectTileset.height ) { 124 | flags = new Array( currentProjectTileset.width * currentProjectTileset.height ); 125 | flags.fill( 0 ); 126 | } 127 | 128 | tileset.width = currentProjectTileset.width; 129 | tileset.height = currentProjectTileset.height; 130 | tileset.format = 'array'; 131 | tileset.name = 'test'; 132 | tileset.tileSize = tileSize; 133 | tileset.flags = flags; 134 | 135 | tilesets.push( tileset ); 136 | } 137 | return tilesets; 138 | } 139 | 140 | static convertToTilesetArray( sourceArray, width, height, tileSize ) { 141 | const imageData = new Array( width * height ); 142 | const tileColumns = width / tileSize; 143 | 144 | for ( let i = 0; i < sourceArray.length; i += 1 ) { 145 | const x = i % width; 146 | const y = Math.floor( i / width ); 147 | const tileX = Math.floor( x / tileSize ); 148 | const tileY = Math.floor( y / tileSize ); 149 | 150 | const iPerTile = tileSize * tileSize; 151 | const startIndex = tileY * iPerTile * tileColumns + ( tileX * iPerTile ); // the starting index of the current tile 152 | 153 | // relative x and y from the tile origin 154 | const relativeX = x - ( tileX * tileSize ); 155 | const relativeY = y - ( tileY * tileSize ); 156 | 157 | const destinationIndex = startIndex + ( relativeY * tileSize ) + relativeX; 158 | imageData[destinationIndex] = sourceArray[i]; 159 | } 160 | 161 | return imageData; 162 | } 163 | } 164 | 165 | export default ConvertProject; 166 | -------------------------------------------------------------------------------- /demo/data/tilebug.project.json: -------------------------------------------------------------------------------- 1 | {"layout":{"activeNavigationTab":"project","navigationPanelIsOpen":true,"referencePanelIsOpen":true,"activeSoundTicTab":"volume","tileSelectorIsOpen":true,"tileEditor":{"showGrid":false},"tilemapEditor":{"tilemapSelectorIsOpen":true,"tileSelectorIsOpen":true,"cursorX":-11,"cursorY":18,"showGrid":true},"soundEditor":{"pianoOctave":4,"lastVolumeTic":-1,"lastPitchTic":-1,"lastArpTic":-1},"referenceTabTitle":"Motivation","play":{"stickConsoleToBottom":true},"referenceRoutes":{"about":{"current":["REFERENCE_ABOUT"],"cached":{"REFERENCE_ABOUT":["REFERENCE_ABOUT"]}},"project":{"current":["MOTIVATION"],"cached":{"MOTIVATION":["MOTIVATION"]}},"play":{"current":["CONSOLE"],"cached":{"CONSOLE":["CONSOLE"]}},"code":{"current":["API","API_SCREEN_SNIPPETS"],"cached":{"API":["API","API_SCREEN_SNIPPETS"]}},"tile":{"current":["ARTICLES"],"cached":{"ARTICLES":["ARTICLES"]}},"tilemap":{"current":["ARTICLES"],"cached":{"ARTICLES":["ARTICLES"]}},"sound":{"current":["HOTKEYS"],"cached":{"HOTKEYS":["HOTKEYS"]}}},"colorPickerIsOpen":false},"sound":{"sounds":[{"volumeTics":[15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15],"pitchTics":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"arpTics":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"pitchScale":100,"wave":0,"useVolumeLoop":false,"volumeLoopStart":0,"volumeLoopEnd":31,"usePitchLoop":false,"pitchLoopStart":0,"pitchLoopEnd":31,"useArpLoop":false,"arpLoopStart":0,"arpLoopEnd":31,"name":"","releaseLength":1,"releaseMode":"linear","needToAddToAudioEngine":false}],"activeSound":0,"audioEvents":[],"piano":{"speed":0,"volume":0.75}},"palette":{"colors":["000000","0b0711","eaf2de","766e76","561f6e","7d4f31","e98c49","fbc0a0","f681b2","d83232","e3e962","65cf57","2ba957","187575","1e2cB0","2379e5","95cae5"],"selectedIndex":6,"altIndex":0},"pixelTools":{"selectedTool":"PENCIL_TOOL","selectedTileTool":"TILE_DRAW_TOOL","pixelToolSettings":{"pencilSize":1,"eraserSize":1}},"tileset":{"tilesets":[{"width":2,"height":3,"selectedTile":5,"selectionWidth":1,"selectionHeight":1,"mapSelectedTile":0,"mapSelectionWidth":2,"mapSelectionHeight":3,"activeLayer":0,"layers":[{"isVisible":true,"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,6,0,0,0,0,0,0,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,6,0,0,0,0,0,0,6,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,6,6,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,6,6,6,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]}],"activeIndex":0,"editorSelection":{"width":0,"height":0,"offsetX":0,"offsetY":0,"data":[],"isActive":false}},"tilemap":{"activeIndex":0,"tilemaps":[{"name":"untitled","width":24,"height":16,"activeLayer":0,"layers":[{"isVisible":true,"data":[1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]}]},"project":{"tileSize":16,"name":"tilebug","screen":{"width":192,"height":128,"scaleMode":1,"scale":3,"minScale":1,"maxScale":4,"horizontalScaleCushion":10,"verticalScaleCushion":10,"rescaleOnWindowResize":true},"misc":{"hideCursor":false,"clickToBegin":false,"startTransitionFrames":60,"useNegativeMotivation":false},"editorVersion":"1.2.0"},"code":{"scripts":[{"text":"\nengine.onInit = () => {\n\n};\n\nengine.onUpdate = () => {\n\tengine.screen.drawMap(\n\t 0, // originX on map\n\t 0, // originY on map\n\t -1, // width\n\t -1, // height\n\t 0, // screenX\n\t 0, // screenY\n\t 0 // tilemap index\n\t);\n};\n\n","cursorRow":14,"cursorColumn":3,"scrollTop":0}],"activeIndex":0,"playLogs":[]},"clipboard":{"pixels":{"width":0,"height":0,"offsetX":0,"offsetY":0,"data":[],"isActive":false}}} -------------------------------------------------------------------------------- /src/TileData/TileData.js: -------------------------------------------------------------------------------- 1 | 2 | import isInteger from 'lodash/isInteger'; 3 | 4 | const flagValues = []; 5 | let f = 1; 6 | for ( let i = 0; i < 16; i += 1 ) { 7 | flagValues.push( f ); 8 | f = f * 2; 9 | } 10 | 11 | /** 12 | * Holds all of the tile data. 13 | */ 14 | class TileData { 15 | constructor() { 16 | /** 17 | * The size of each tile in pixels. 18 | * Used for both width and height. 19 | */ 20 | this.tileSize = 16; 21 | /** 22 | * Array of tileset data. 23 | * All tiledata is added to this.data when init is called. 24 | * More tilesets can not be added after this. 25 | */ 26 | this.tilesets = []; 27 | /** 28 | * All of the tile data in a single Uint8ClampedArray. 29 | * This is whats used by Screen to draw tiles. 30 | */ 31 | this.data = null; 32 | /** 33 | * All of the flag data in a single Uint16Array. 34 | * Each item is a flag value for each tile. 35 | */ 36 | this.flagData = null; 37 | } 38 | 39 | /** 40 | * Parse all of the tilesets and add them to the data array 41 | */ 42 | init() { 43 | let numberOfTiles = 0; 44 | for ( let i = 0; i < this.tilesets.length; i += 1 ) { 45 | const currentTileset = this.tilesets[i]; 46 | numberOfTiles += currentTileset.width * currentTileset.height; 47 | } 48 | this.data = new Uint8ClampedArray( numberOfTiles * this.tileSize * this.tileSize ); 49 | this.flagData = new Uint16Array( numberOfTiles + 1 ); 50 | this.flagData.fill( 0 ); 51 | 52 | let startPosition = 0; 53 | let firstGid = 1; 54 | for ( let i = 0; i < this.tilesets.length; i += 1 ) { 55 | const currentTileset = this.tilesets[i]; 56 | currentTileset.firstGid = firstGid; 57 | const { data } = currentTileset; 58 | if ( currentTileset.format === 'array' ) { 59 | for ( let j = 0; j < data.length; j += 1 ) { 60 | this.data[startPosition + j] = parseInt( data[j], 10 ); 61 | } 62 | } 63 | else if ( currentTileset.format === 'run' ) { 64 | let runPosition = 0; 65 | let dataPosition = 0; 66 | while ( runPosition < data.length ) { 67 | const runLength = data[runPosition]; 68 | const paletteId = parseInt( data[runPosition + 1], 10 ); 69 | for ( let j = 0; j < runLength; j += 1 ) { 70 | this.data[startPosition + dataPosition] = paletteId; 71 | dataPosition += 1; 72 | } 73 | runPosition += 2; 74 | } 75 | } 76 | 77 | const { flags } = currentTileset; 78 | for ( let j = 0; j < flags.length; j += 1 ) { 79 | this.flagData[firstGid + j] = parseInt( flags[j], 10 ); 80 | } 81 | 82 | firstGid += currentTileset.width * currentTileset.height; 83 | startPosition += currentTileset.width * currentTileset.height * this.tileSize * this.tileSize; 84 | 85 | // data is no longer needed in the current tileset as it has been added to this.data. 86 | delete currentTileset.data; 87 | 88 | // flags are no longer needed 89 | delete currentTileset.flags; 90 | } 91 | } 92 | 93 | /** 94 | * Add a tileset. 95 | * All tilesets must be added before init is called. 96 | * @param {Object} tileset - the tileset data 97 | */ 98 | addTileset( tileset ) { 99 | this.tilesets.push( tileset ); 100 | } 101 | 102 | /** 103 | * Get the GID for a tile 104 | * @param {number} x - x position of the tile 105 | * @param {number} y - y position of the tile 106 | * @param {number} tileset - the index of the tileset 107 | */ 108 | getGid( x, y, tileset = 0 ) { 109 | if ( x < 0 || y < 0 ) { 110 | return -1; 111 | } 112 | const currentTileset = this.tilesets[tileset]; 113 | const { width, height, firstGid } = currentTileset; 114 | if ( x >= width || y >= height ) { 115 | return -1; 116 | } 117 | return y * width + x + firstGid; 118 | } 119 | 120 | /** 121 | * Get the tileset info for a given gid 122 | * @param {number} gid - the gid of the tile 123 | */ 124 | getTilesetInfoForGid( gid ) { 125 | if ( !this.tilesets || this.tilesets.length === 0 || !gid ) { 126 | return null; 127 | } 128 | 129 | let tilesetIndex = 0; 130 | for ( let i = 0; i < this.tilesets.length; i += 1 ) { 131 | const tileset = this.tilesets[i]; 132 | if ( tileset.firstGid <= gid ) { 133 | tilesetIndex = i; 134 | } 135 | else { 136 | break; 137 | } 138 | } 139 | 140 | return { 141 | firstGid: this.tilesets[tilesetIndex].firstGid, 142 | width: this.tilesets[tilesetIndex].width, 143 | height: this.tilesets[tilesetIndex].height, 144 | index: tilesetIndex, 145 | }; 146 | } 147 | 148 | /** 149 | * Get the array data for a tile section 150 | * @param {number} gid - the gid of the bottom left tile 151 | * @param {number} tileWidth - the width in tiles of the tile section 152 | * @param {number} tileHeight - the height in tiles of the tile section 153 | */ 154 | getTileSectionData( gid, tileWidth = 1, tileHeight = 1 ) { 155 | const tilesetInfo = this.getTilesetInfoForGid( gid ); 156 | 157 | if ( !tilesetInfo ) { 158 | return null; 159 | } 160 | 161 | const { tileSize } = this; 162 | 163 | const localGid = gid - tilesetInfo.firstGid; 164 | const localY = Math.floor( localGid / tilesetInfo.width ); 165 | const localX = localGid - localY * tilesetInfo.width; 166 | 167 | const data = new Uint8ClampedArray( tileWidth * tileHeight * tileSize * tileSize ); 168 | const width = tileWidth * tileSize; 169 | const height = tileHeight * tileSize; 170 | 171 | for ( let tileY = 0; tileY < tileHeight; tileY += 1 ) { 172 | for ( let tileX = 0; tileX < tileWidth; tileX += 1 ) { 173 | if ( tileX + localX >= tilesetInfo.width || tileY + localY >= tilesetInfo.height ) { 174 | // tile is outside of tileset 175 | break; 176 | } 177 | 178 | const tileGid = localGid + tileY * tilesetInfo.width + tileX + tilesetInfo.firstGid; 179 | const basePosition = ( tileGid - 1 ) * tileSize * tileSize; 180 | for ( let y = 0; y < tileSize; y += 1 ) { 181 | for ( let x = 0; x < tileSize; x += 1 ) { 182 | const paletteId = this.data[basePosition + y * tileSize + x]; 183 | const targetX = tileX * tileSize + x; 184 | const targetY = tileY * tileSize + y; 185 | data[targetY * width + targetX] = paletteId; 186 | } 187 | } 188 | } 189 | } 190 | 191 | return { data, width, height }; 192 | } 193 | 194 | /** 195 | * Get flag for a gid. If the flagNumber is not specified or negative, returns the bit field for a gid 196 | * @param {number} gid - the gid of the tile 197 | * @param {number} flagNumber - the flag number, 0 - 15. If negative the number of the bit field will be returned 198 | */ 199 | getFlag( gid, flagNumber = -1 ) { 200 | if ( gid < 0 || gid >= this.flagData.length ) { 201 | return 0; 202 | } 203 | 204 | const gidFlags = this.flagData[gid]; 205 | 206 | if ( flagNumber < 0 || flagNumber >= 16 ) { 207 | return gidFlags; 208 | } 209 | 210 | if ( flagValues[flagNumber] & gidFlags ) { 211 | return true; 212 | } 213 | 214 | return false; 215 | } 216 | 217 | /** 218 | * Set flag for a gid. If the flagNumber is negative, set the bit field for a gid 219 | * @param {number} gid - the gid of the tile 220 | * @param {number} flagNumber - the flag number, 0 - 15. Set negative to set the entire bit field for the flags 221 | * @param {number | boolean} value - true or false if setting a flag, number if setting the bit field 222 | */ 223 | setFlag( gid, flagNumber, value ) { 224 | if ( gid < 0 || gid >= this.flagData.length ) { 225 | return; 226 | } 227 | 228 | if ( flagNumber < 0 ) { 229 | // set the entire bitfield 230 | if ( isInteger( value ) && value >= 0 && value <= 65535 ) { 231 | this.flagData[gid] = value; 232 | } 233 | } 234 | else if ( isInteger( flagNumber ) && flagNumber >= 0 && flagNumber < 16 ) { 235 | const flagValue = flagValues[flagNumber]; 236 | if ( value ) { 237 | // add the flag 238 | this.flagData[gid] = this.flagData[gid] | flagValue; 239 | } 240 | else { 241 | // remove the flag 242 | this.flagData[gid] = this.flagData[gid] & ~flagValue; 243 | } 244 | } 245 | } 246 | } 247 | 248 | export default TileData; 249 | -------------------------------------------------------------------------------- /src/FontData/small.font.json: -------------------------------------------------------------------------------- 1 | {"name":"small","tileSize":8,"width":16,"height":8,"originX":2,"originY":2,"standardWidth":3,"letterSpacing":1,"data":[2122,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,36,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,2,2,2,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,14,0,3,2,3,0,3,2,1,1,1,2,3,0,1,2,1,1,3,2,3,0,2,2,1,1,2,2,3,0,3,2,1,1,1,2,3,0,1,2,1,1,3,2,3,0,3,2,13,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,2,1,2,2,3,0,2,2,1,1,1,2,5,0,3,2,37,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,13,0,3,2,4,0,2,2,1,1,1,2,4,0,1,2,1,1,2,2,4,0,1,2,1,1,1,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,3,2,13,0,3,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,1,2,4,0,1,2,1,1,2,2,4,0,3,2,28,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,20,0,3,2,4,0,2,2,1,1,2,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,20,0,3,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,3,2,52,0,5,2,3,0,1,2,3,1,1,2,3,0,5,2,28,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,52,0,3,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,3,2,19,0,4,2,4,0,1,2,2,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,2,1,1,2,4,0,4,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,1,2,1,1,1,2,4,0,2,2,1,1,1,2,4,0,1,2,2,1,1,2,4,0,4,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,13,0,3,2,5,0,1,2,1,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,12,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,20,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,20,0,3,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,22,0,3,2,4,0,2,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,2,2,4,0,2,2,1,1,2,2,4,0,2,2,1,1,1,2,5,0,3,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,5,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,3,2,5,0,1,2,1,1,2,2,4,0,2,2,1,1,2,2,4,0,2,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,2,2,4,0,3,2,14,0,3,2,5,0,1,2,1,1,1,2,5,0,4,2,4,0,1,2,2,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,12,0,4,2,3,0,2,2,2,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,12,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,4,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,1,2,5,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,4,2,4,0,1,2,2,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,4,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,1,2,1,1,1,2,4,0,2,2,1,1,2,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,3,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,13,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,12,0,4,2,3,0,2,2,2,1,1,2,3,0,1,2,2,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,4,2,4,0,1,2,2,1,2,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,2,2,2,1,1,2,4,0,4,2,12,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,2,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,12,0,3,2,4,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,12,0,3,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,2,2,1,1,2,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,11,0,4,2,4,0,1,2,2,1,1,2,4,0,1,2,1,1,2,2,4,0,1,2,1,1,1,2,5,0,1,2,1,1,2,2,4,0,1,2,2,1,1,2,4,0,4,2,22,0,3,2,4,0,2,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,2,2,4,0,3,2,22,0,4,2,4,0,1,2,2,1,1,2,4,0,2,2,1,1,1,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,1,2,4,0,1,2,2,1,1,2,4,0,4,2,35,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,5,2,68,0,3,2,4,0,2,2,1,1,1,2,4,0,1,2,1,1,2,2,4,0,3,2,14,0,4,2,3,0,2,2,2,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,2,1,1,2,4,0,4,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,3,2,13,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,5,0,3,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,2,2,4,0,1,2,2,1,1,2,4,0,1,2,1,1,3,2,3,0,2,2,2,1,1,2,4,0,4,2,11,0,5,2,3,0,1,2,3,1,1,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,3,2,3,0,3,2,14,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,3,2,1,1,1,2,5,0,3,2,5,0,1,2,1,1,1,2,5,0,3,2,11,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,3,2,3,0,1,2,1,1,1,2,5,0,3,2,14,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,12,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,3,2,5,0,1,2,1,1,3,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,21,0,3,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,19,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,3,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,2,1,2,2,3,0,4,2,20,0,4,2,4,0,1,2,2,1,2,2,3,0,3,2,1,1,1,2,3,0,1,2,1,1,3,2,3,0,2,2,2,1,1,2,4,0,4,2,20,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,2,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,12,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,20,0,3,2,4,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,1,2,3,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,20,0,3,2,5,0,1,2,1,1,1,2,4,0,2,2,1,1,2,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,5,2,19,0,5,2,3,0,1,2,3,1,1,2,3,0,2,2,1,1,2,2,3,0,3,2,1,1,1,2,3,0,1,2,3,1,1,2,3,0,5,2,20,0,4,2,4,0,1,2,2,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,2,1,1,2,4,0,2,2,1,1,2,2,4,0,1,2,2,1,1,2,4,0,4,2,12,0,3,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,1,2,1,1,1,2,5,0,3,2,12,0,4,2,4,0,1,2,2,1,1,2,4,0,2,2,1,1,2,2,4,0,1,2,2,1,1,2,3,0,2,2,1,1,2,2,3,0,1,2,2,1,1,2,4,0,4,2,28,0,5,2,3,0,1,2,1,1,1,2,1,1,1,2,3,0,2,2,1,1,2,2,4,0,3,2,75,0]} -------------------------------------------------------------------------------- /src/lz-string/libs/base64-string.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Pieroxy 2 | // This work is free. You can redistribute it and/or modify it 3 | // under the terms of the WTFPL, Version 2 4 | // For more information see LICENSE.txt or http://www.wtfpl.net/ 5 | // 6 | // This lib is part of the lz-string project. 7 | // For more information, the home page: 8 | // http://pieroxy.net/blog/pages/lz-string/index.html 9 | // 10 | // Base64 compression / decompression for already compressed content (gif, png, jpg, mp3, ...) 11 | // version 1.4.1 12 | var Base64String = { 13 | 14 | compressToUTF16 : function (input) { 15 | var output = [], 16 | i,c, 17 | current, 18 | status = 0; 19 | 20 | input = this.compress(input); 21 | 22 | for (i=0 ; i> 1)+32)); 27 | current = (c & 1) << 14; 28 | break; 29 | case 1: 30 | output.push(String.fromCharCode((current + (c >> 2))+32)); 31 | current = (c & 3) << 13; 32 | break; 33 | case 2: 34 | output.push(String.fromCharCode((current + (c >> 3))+32)); 35 | current = (c & 7) << 12; 36 | break; 37 | case 3: 38 | output.push(String.fromCharCode((current + (c >> 4))+32)); 39 | current = (c & 15) << 11; 40 | break; 41 | case 4: 42 | output.push(String.fromCharCode((current + (c >> 5))+32)); 43 | current = (c & 31) << 10; 44 | break; 45 | case 5: 46 | output.push(String.fromCharCode((current + (c >> 6))+32)); 47 | current = (c & 63) << 9; 48 | break; 49 | case 6: 50 | output.push(String.fromCharCode((current + (c >> 7))+32)); 51 | current = (c & 127) << 8; 52 | break; 53 | case 7: 54 | output.push(String.fromCharCode((current + (c >> 8))+32)); 55 | current = (c & 255) << 7; 56 | break; 57 | case 8: 58 | output.push(String.fromCharCode((current + (c >> 9))+32)); 59 | current = (c & 511) << 6; 60 | break; 61 | case 9: 62 | output.push(String.fromCharCode((current + (c >> 10))+32)); 63 | current = (c & 1023) << 5; 64 | break; 65 | case 10: 66 | output.push(String.fromCharCode((current + (c >> 11))+32)); 67 | current = (c & 2047) << 4; 68 | break; 69 | case 11: 70 | output.push(String.fromCharCode((current + (c >> 12))+32)); 71 | current = (c & 4095) << 3; 72 | break; 73 | case 12: 74 | output.push(String.fromCharCode((current + (c >> 13))+32)); 75 | current = (c & 8191) << 2; 76 | break; 77 | case 13: 78 | output.push(String.fromCharCode((current + (c >> 14))+32)); 79 | current = (c & 16383) << 1; 80 | break; 81 | case 14: 82 | output.push(String.fromCharCode((current + (c >> 15))+32, (c & 32767)+32)); 83 | status = 0; 84 | break; 85 | } 86 | } 87 | output.push(String.fromCharCode(current + 32)); 88 | return output.join(''); 89 | }, 90 | 91 | 92 | decompressFromUTF16 : function (input) { 93 | var output = [], 94 | current,c, 95 | status=0, 96 | i = 0; 97 | 98 | while (i < input.length) { 99 | c = input.charCodeAt(i) - 32; 100 | 101 | switch (status++) { 102 | case 0: 103 | current = c << 1; 104 | break; 105 | case 1: 106 | output.push(String.fromCharCode(current | (c >> 14))); 107 | current = (c&16383) << 2; 108 | break; 109 | case 2: 110 | output.push(String.fromCharCode(current | (c >> 13))); 111 | current = (c&8191) << 3; 112 | break; 113 | case 3: 114 | output.push(String.fromCharCode(current | (c >> 12))); 115 | current = (c&4095) << 4; 116 | break; 117 | case 4: 118 | output.push(String.fromCharCode(current | (c >> 11))); 119 | current = (c&2047) << 5; 120 | break; 121 | case 5: 122 | output.push(String.fromCharCode(current | (c >> 10))); 123 | current = (c&1023) << 6; 124 | break; 125 | case 6: 126 | output.push(String.fromCharCode(current | (c >> 9))); 127 | current = (c&511) << 7; 128 | break; 129 | case 7: 130 | output.push(String.fromCharCode(current | (c >> 8))); 131 | current = (c&255) << 8; 132 | break; 133 | case 8: 134 | output.push(String.fromCharCode(current | (c >> 7))); 135 | current = (c&127) << 9; 136 | break; 137 | case 9: 138 | output.push(String.fromCharCode(current | (c >> 6))); 139 | current = (c&63) << 10; 140 | break; 141 | case 10: 142 | output.push(String.fromCharCode(current | (c >> 5))); 143 | current = (c&31) << 11; 144 | break; 145 | case 11: 146 | output.push(String.fromCharCode(current | (c >> 4))); 147 | current = (c&15) << 12; 148 | break; 149 | case 12: 150 | output.push(String.fromCharCode(current | (c >> 3))); 151 | current = (c&7) << 13; 152 | break; 153 | case 13: 154 | output.push(String.fromCharCode(current | (c >> 2))); 155 | current = (c&3) << 14; 156 | break; 157 | case 14: 158 | output.push(String.fromCharCode(current | (c >> 1))); 159 | current = (c&1) << 15; 160 | break; 161 | case 15: 162 | output.push(String.fromCharCode(current | c)); 163 | status=0; 164 | break; 165 | } 166 | 167 | 168 | i++; 169 | } 170 | 171 | return this.decompress(output.join('')); 172 | //return output; 173 | 174 | }, 175 | 176 | 177 | // private property 178 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 179 | 180 | decompress : function (input) { 181 | var output = []; 182 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 183 | var i = 1; 184 | var odd = input.charCodeAt(0) >> 8; 185 | 186 | while (i < input.length*2 && (i < input.length*2-1 || odd==0)) { 187 | 188 | if (i%2==0) { 189 | chr1 = input.charCodeAt(i/2) >> 8; 190 | chr2 = input.charCodeAt(i/2) & 255; 191 | if (i/2+1 < input.length) 192 | chr3 = input.charCodeAt(i/2+1) >> 8; 193 | else 194 | chr3 = NaN; 195 | } else { 196 | chr1 = input.charCodeAt((i-1)/2) & 255; 197 | if ((i+1)/2 < input.length) { 198 | chr2 = input.charCodeAt((i+1)/2) >> 8; 199 | chr3 = input.charCodeAt((i+1)/2) & 255; 200 | } else 201 | chr2=chr3=NaN; 202 | } 203 | i+=3; 204 | 205 | enc1 = chr1 >> 2; 206 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 207 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 208 | enc4 = chr3 & 63; 209 | 210 | if (isNaN(chr2) || (i==input.length*2+1 && odd)) { 211 | enc3 = enc4 = 64; 212 | } else if (isNaN(chr3) || (i==input.length*2 && odd)) { 213 | enc4 = 64; 214 | } 215 | 216 | output.push(this._keyStr.charAt(enc1)); 217 | output.push(this._keyStr.charAt(enc2)); 218 | output.push(this._keyStr.charAt(enc3)); 219 | output.push(this._keyStr.charAt(enc4)); 220 | } 221 | 222 | return output.join(''); 223 | }, 224 | 225 | compress : function (input) { 226 | var output = [], 227 | ol = 1, 228 | output_, 229 | chr1, chr2, chr3, 230 | enc1, enc2, enc3, enc4, 231 | i = 0, flush=false; 232 | 233 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 234 | 235 | while (i < input.length) { 236 | 237 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 238 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 239 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 240 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 241 | 242 | chr1 = (enc1 << 2) | (enc2 >> 4); 243 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 244 | chr3 = ((enc3 & 3) << 6) | enc4; 245 | 246 | if (ol%2==0) { 247 | output_ = chr1 << 8; 248 | flush = true; 249 | 250 | if (enc3 != 64) { 251 | output.push(String.fromCharCode(output_ | chr2)); 252 | flush = false; 253 | } 254 | if (enc4 != 64) { 255 | output_ = chr3 << 8; 256 | flush = true; 257 | } 258 | } else { 259 | output.push(String.fromCharCode(output_ | chr1)); 260 | flush = false; 261 | 262 | if (enc3 != 64) { 263 | output_ = chr2 << 8; 264 | flush = true; 265 | } 266 | if (enc4 != 64) { 267 | output.push(String.fromCharCode(output_ | chr3)); 268 | flush = false; 269 | } 270 | } 271 | ol+=3; 272 | } 273 | 274 | if (flush) { 275 | output.push(String.fromCharCode(output_)); 276 | output = output.join(''); 277 | output = String.fromCharCode(output.charCodeAt(0)|256) + output.substring(1); 278 | } else { 279 | output = output.join(''); 280 | } 281 | 282 | return output; 283 | 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/Engine/Engine.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-destructuring */ 2 | import Screen from '../Screen/Screen'; 3 | import Input from '../Input/Input'; 4 | import TileData from '../TileData/TileData'; 5 | import MapData from '../MapData/MapData'; 6 | import FontData from '../FontData/FontData'; 7 | import Audio from '../Audio/Audio'; 8 | import ConvertProject from '../ConvertProject/ConvertProject'; 9 | 10 | import standardFont from '../FontData/standard.font.json'; 11 | import smallFont from '../FontData/small.font.json'; 12 | 13 | class Engine { 14 | constructor() { 15 | /** 16 | * The id of the div that the engine will be contained by 17 | */ 18 | this.containerId = 'bitmelo-container'; 19 | 20 | /** 21 | * Function to be called to add assets and configuration during initialization 22 | */ 23 | this.onConfigure = null; 24 | 25 | /** 26 | * Function to be called when the engine is initialized 27 | */ 28 | this.onInit = null; 29 | 30 | /** 31 | * Function to draw the initial screen when the engine loads. 32 | * Only seen when clickToBegin is true 33 | */ 34 | this.onDrawStartScreen = null; 35 | 36 | /** 37 | * Function to draw the transition frames after start click. 38 | * Only seen when clickToBegin is true 39 | */ 40 | this.onUpdateStartTransition = null; 41 | 42 | /** 43 | * Number of frames to show after begin click before the game starts. 44 | * Only relevant when clickToBegin is true. 45 | */ 46 | this.startTransitionFrames = 60; 47 | 48 | /** 49 | * Function to be called every update of the engine. 50 | * Perform game logic and rendering here. 51 | */ 52 | this.onUpdate = null; 53 | 54 | /** 55 | * Should we require the user to click the screen before the game starts? 56 | * This stops a game from automatically starring in a web page which can be annoying. 57 | */ 58 | this.clickToBegin = true; 59 | 60 | /** 61 | * Instance of the Screen class used by the Engine. Automatically created by the engine. 62 | */ 63 | this.screen = new Screen(); 64 | 65 | /** 66 | * Instance of the Input class used by the Engine. Automatically created by the engine. 67 | */ 68 | this.input = new Input(); 69 | 70 | /** 71 | * Instance of the TileData class used by the Engine. Automatically created by the engine 72 | */ 73 | this.tileData = new TileData(); 74 | 75 | /** 76 | * Instance of the MapData class used by the Engine. Automatically created by the engine 77 | */ 78 | this.mapData = new MapData(); 79 | 80 | /** 81 | * Instance of the FontData class used by the Engine. Automatically created by the engine. 82 | * The Standard font is automatically added at index 0. 83 | * The Small font is automatically added at index 1. 84 | */ 85 | this.fontData = new FontData(); 86 | 87 | /** 88 | * Instance of the Audio class used by the Engine. Automatically created by the engine. 89 | */ 90 | this.audio = new Audio(); 91 | 92 | /** 93 | * The number of seconds since init was called 94 | */ 95 | this.realTimeSinceInit = 0; 96 | 97 | /** 98 | * The number of seconds since the game was started 99 | */ 100 | this.realTimeSinceGameStart = 0; 101 | 102 | /** 103 | * This mode runs the engine without drawing to a canvas or playing audio. 104 | * This is useful to use the engine to generate image data. 105 | */ 106 | this.dataOnlyMode = false; 107 | 108 | this.fontData.addFont( standardFont ); 109 | this.fontData.addFont( smallFont ); 110 | 111 | this._hasBegun = false; 112 | this._update = this._update.bind( this ); 113 | this._initTime = 0; 114 | this._gameStartTime = 0; 115 | this._frameStartTime = new Date().getTime(); 116 | this._paused = false; 117 | 118 | /** 119 | * Whether we have detected a condition that should stop the games execution. 120 | * Likely from an infinite loop. 121 | */ 122 | this._didCrash = false; 123 | 124 | /** 125 | * How many milliseconds an update frame can run until the game crashes. 126 | * This is to stop an infinite loop from locking up the browser forever. 127 | */ 128 | this._msToFrameCrash = 5000; 129 | } 130 | 131 | /** 132 | * Add project data from the Bitmelo Editor to the engine 133 | */ 134 | addProjectData( projectData ) { 135 | let format = 'project'; 136 | if ( projectData.format ) { 137 | format = projectData.format; 138 | } 139 | 140 | let project = null; 141 | let sounds = null; 142 | let tilesets = null; 143 | let tilemaps = null; 144 | let palette = null; 145 | 146 | if ( format === 'transfer' ) { 147 | project = projectData.project; 148 | sounds = projectData.sounds; 149 | tilesets = projectData.tilesets; 150 | tilemaps = projectData.tilemaps; 151 | palette = projectData.palette; 152 | } 153 | else { 154 | project = projectData.project; 155 | sounds = projectData.sound.sounds; 156 | tilesets = projectData.tileset.tilesets; 157 | tilemaps = projectData.tilemap.tilemaps; 158 | palette = projectData.palette; 159 | } 160 | 161 | // engine settings 162 | this.clickToBegin = project.misc.clickToBegin; 163 | this.startTransitionFrames = project.misc.startTransitionFrames; 164 | 165 | // screen settings 166 | this.screen.width = project.screen.width; 167 | this.screen.height = project.screen.height; 168 | this.screen.scaleMode = project.screen.scaleMode; 169 | this.screen.scale = project.screen.scale; 170 | this.screen.minScale = project.screen.minScale; 171 | this.screen.maxScale = project.screen.maxScale; 172 | this.screen.horizontalScaleCushion = project.screen.horizontalScaleCushion; 173 | this.screen.verticalScaleCushion = project.screen.verticalScaleCushion; 174 | this.screen.rescaleOnWindowResize = project.screen.rescaleOnWindowResize; 175 | this.screen.hideCursor = project.misc.hideCursor; 176 | this.screen.setPalette( palette.colors ); 177 | 178 | // tilesets 179 | this.tileData.tileSize = project.tileSize; 180 | const convertedTilesets = ConvertProject.convertProjectTilesets( tilesets, project.tileSize ); 181 | for ( let i = 0; i < convertedTilesets.length; i += 1 ) { 182 | this.tileData.addTileset( convertedTilesets[i] ); 183 | } 184 | 185 | // tilemaps 186 | const convertedTilemaps = ConvertProject.convertProjectTilemaps( tilemaps ); 187 | for ( let i = 0; i < convertedTilemaps.length; i += 1 ) { 188 | this.mapData.addTileMap( convertedTilemaps[i] ); 189 | } 190 | 191 | // sounds 192 | for ( let i = 0; i < sounds.length; i += 1 ) { 193 | this.audio.addSound( sounds[i] ); 194 | } 195 | } 196 | 197 | /** 198 | * Begin running the engine. This will perform initial setup, call the onInit function, and begin the game loop 199 | */ 200 | begin() { 201 | const date = new Date(); 202 | this._initTime = date.getTime(); 203 | if ( this.onConfigure ) { 204 | this.onConfigure(); 205 | } 206 | 207 | this.screen.dataOnlyMode = this.dataOnlyMode; 208 | this.screen.conainerId = this.containerId; 209 | this.screen.init(); 210 | this.screen.onScaleChange = ( scale ) => { 211 | this.input.canvasScale = scale; 212 | }; 213 | 214 | this.input.dataOnlyMode = this.dataOnlyMode; 215 | this.input.canvas = this.screen.canvas; 216 | this.input.canvasScale = this.screen.scale; 217 | this.input.screenWidth = this.screen.width; 218 | this.input.screenHeight = this.screen.height; 219 | this.input.init(); 220 | this.tileData.init(); 221 | this.screen.tileData = this.tileData; 222 | this.screen.mapData = this.mapData; 223 | this.screen.fontData = this.fontData; 224 | this.audio.dataOnlyMode = this.dataOnlyMode; 225 | this.audio.init(); 226 | 227 | if ( this.onInit ) { 228 | this.onInit(); 229 | } 230 | 231 | if ( this._didCrash ) { 232 | this.screen.clear( 1 ); 233 | this.screen.drawText( 'Game Crashed', 10, 10, 2, 1, 0 ); 234 | this.screen._setCanvasStyle(); 235 | this.screen.drawScreen(); 236 | return; 237 | } 238 | 239 | if ( this.clickToBegin && !this.dataOnlyMode ) { 240 | if ( this.onDrawStartScreen ) { 241 | this.onDrawStartScreen(); 242 | } 243 | else { 244 | this.screen.clear( 1 ); 245 | this.screen.drawText( 'Click to begin...', 10, 10, 2, 1, 0 ); 246 | } 247 | const screenHidesCursor = this.screen.hideCursor; 248 | this.screen.hideCursor = false; 249 | this.screen._setCanvasStyle(); 250 | this.screen.drawScreen(); 251 | this.screen.canvas.addEventListener( 'click', () => { 252 | if ( !this._hasBegun ) { 253 | this._hasBegun = true; 254 | this.screen.hideCursor = screenHidesCursor; 255 | this.screen._setCanvasStyle(); 256 | this.input.clearInput(); 257 | requestAnimationFrame( this._update ); 258 | } 259 | } ); 260 | } 261 | else { 262 | this._gameStartTime = date.getTime(); 263 | this._hasBegun = true; 264 | requestAnimationFrame( this._update ); 265 | } 266 | this._didCrash = false; 267 | } 268 | 269 | pause() { 270 | this._paused = true; 271 | } 272 | 273 | unpause() { 274 | this._paused = false; 275 | requestAnimationFrame( this._update ); 276 | } 277 | 278 | isPaused() { 279 | return this._paused; 280 | } 281 | 282 | /** 283 | * Game loop 284 | */ 285 | _update() { 286 | if ( this._paused ) { 287 | return; 288 | } 289 | 290 | this.advanceFrame(); 291 | 292 | if ( this._didCrash ) { 293 | this.screen.clear( 1 ); 294 | this.screen.drawText( 'Game Crashed', 10, 10, 2, 1, 0 ); 295 | this.screen.drawScreen(); 296 | } 297 | else { 298 | requestAnimationFrame( this._update ); 299 | } 300 | } 301 | 302 | advanceFrame() { 303 | if ( this._didCrash ) { 304 | return; 305 | } 306 | 307 | const date = new Date(); 308 | this._frameStartTime = date.getTime(); 309 | const msSinceInit = date.getTime() - this._initTime; 310 | this.realTimeSinceInit = msSinceInit / 1000; 311 | 312 | this.input.pollInput(); 313 | 314 | // the first game frame after transition 315 | if ( this.clickToBegin && this.startTransitionFrames === 0 ) { 316 | this._gameStartTime = date.getTime(); 317 | this.startTransitionFrames -= 1; 318 | } 319 | 320 | if ( this.clickToBegin && this.startTransitionFrames > 0 ) { 321 | this.startTransitionFrames -= 1; 322 | 323 | if ( this.onUpdateStartTransition ) { 324 | this.clearInput(); 325 | this.onUpdateStartTransition(); 326 | } 327 | else { 328 | this.screen.clear( 1 ); 329 | } 330 | } 331 | else if ( this.onUpdate ) { 332 | this.realTimeSinceGameStart = ( date.getTime() - this._gameStartTime ) / 1000; 333 | this.onUpdate(); 334 | this.audio.update(); 335 | } 336 | 337 | this.screen.drawScreen(); 338 | } 339 | 340 | /** 341 | * Should we break out of a loop? 342 | * This is used by instrumented code in case a frame is taking long enough that 343 | * the game should crash instead of locking up the browser. 344 | */ 345 | shouldBreak() { 346 | if ( this._didCrash ) { 347 | return true; 348 | } 349 | 350 | const currentTime = new Date().getTime(); 351 | const msSinceFrameStart = currentTime - this._frameStartTime; 352 | if ( msSinceFrameStart > this._msToFrameCrash ) { 353 | this._didCrash = true; 354 | return true; 355 | } 356 | 357 | return false; 358 | } 359 | } 360 | 361 | export default Engine; 362 | -------------------------------------------------------------------------------- /src/Audio/Audio.js: -------------------------------------------------------------------------------- 1 | import Sound from './Sound'; 2 | import Frequencies from './Frequencies'; 3 | 4 | /** 5 | * Handles playing of audio and adding audio data. 6 | */ 7 | class Audio { 8 | constructor() { 9 | /** 10 | * The AudioContext used. Created in init. 11 | */ 12 | this.context = null; 13 | 14 | /** 15 | * Array of sounds used, of the Sound class type. 16 | * Add a sound from data using the addSound method. 17 | */ 18 | this.sounds = []; 19 | 20 | /** 21 | * Time in second we should look ahead during update to add audio events to the context. 22 | */ 23 | this.lookAheadTime = 0.05; // in seconds 24 | 25 | /** 26 | * This mode runs the engine without drawing to a canvas or playing audio. 27 | * This is useful to use the engine to generate image data. 28 | */ 29 | this.dataOnlyMode = false; 30 | } 31 | 32 | /** 33 | * Initialize the audio context. Called automatically by the Engine. 34 | */ 35 | init() { 36 | if ( this.dataOnlyMode ) { 37 | return; 38 | } 39 | 40 | const AudioContext = window.AudioContext || window.webkitAudioContext; 41 | this.context = new AudioContext(); 42 | } 43 | 44 | /** 45 | * Update audio events. Called automatically by the Engine in the update loop. 46 | */ 47 | update() { 48 | if ( this.dataOnlyMode ) { 49 | return; 50 | } 51 | 52 | let sound = null; 53 | for ( let i = 0; i < this.sounds.length; i += 1 ) { 54 | sound = this.sounds[i]; 55 | if ( sound.isPlayingInfiniteSound ) { 56 | const lastScheduledTime = this.context.currentTime - sound.infiniteStartTime + this.lookAheadTime; 57 | const totalNumberOfTics = Math.floor( lastScheduledTime / sound.infiniteTicDuration ); 58 | if ( totalNumberOfTics > sound.infiniteTicsPlayed ) { 59 | let volumeTicIndex = 0; 60 | let pitchTicIndex = 0; 61 | let arpTicIndex = 0; 62 | for ( let tic = sound.infiniteTicsPlayed + 1; tic <= totalNumberOfTics; tic += 1 ) { 63 | const time = sound.infiniteStartTime + tic * sound.infiniteTicDuration; 64 | volumeTicIndex = Audio.indexAtTic( 65 | tic, sound.useVolumeLoop, 66 | sound.volumeLoopStart, 67 | sound.volumeLoopEnd, 68 | ); 69 | pitchTicIndex = Audio.indexAtTic( tic, sound.usePitchLoop, sound.pitchLoopStart, sound.pitchLoopEnd ); 70 | arpTicIndex = Audio.indexAtTic( tic, sound.useArpLoop, sound.arpLoopStart, sound.arpLoopEnd ); 71 | 72 | const currentVolume = Audio.valueForVolume( sound.volumeTics[volumeTicIndex] ) 73 | * Audio.linearToAdjustedVolume( sound.infiniteVolume ); 74 | 75 | sound.infiniteGain.gain.linearRampToValueAtTime( currentVolume, time ); 76 | sound.infiniteOsc.detune.linearRampToValueAtTime( sound.pitchTics[pitchTicIndex] * sound.pitchScale, time ); 77 | const currentNote = sound.infiniteNote + sound.arpTics[arpTicIndex]; 78 | const currentFrequency = Audio.frequencyForNote( currentNote ); 79 | sound.infiniteOsc.frequency.setValueAtTime( currentFrequency, time ); 80 | 81 | sound.infiniteTicsPlayed = tic; 82 | } 83 | 84 | sound.lastVolumeTic = volumeTicIndex; 85 | sound.lastPitchTic = pitchTicIndex; 86 | sound.lastArpTic = arpTicIndex; 87 | } 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * Add a Sound instance to the sounds array from data. 94 | * @param {*} soundData 95 | */ 96 | addSound( soundData ) { 97 | this.sounds.push( new Sound( soundData ) ); 98 | return this.sounds.length - 1; 99 | } 100 | 101 | /** 102 | * Play a sound 103 | * @param {*} soundIndex 104 | * @param {*} note 105 | * @param {*} duration 106 | * @param {*} volume 107 | * @param {*} speed 108 | */ 109 | playSound( soundIndex, note, duration = 32, volume = 1, speed = 0 ) { 110 | if ( this.dataOnlyMode ) { 111 | return; 112 | } 113 | 114 | if ( soundIndex >= this.sounds.length || soundIndex < 0 ) { 115 | console.error( 'Invalid sound index' ); 116 | return; 117 | } 118 | 119 | if ( duration < 0 ) { 120 | this.playInfiniteSound( soundIndex, note, volume, speed ); 121 | return; 122 | } 123 | 124 | const sound = this.sounds[soundIndex]; 125 | 126 | const osc = this.context.createOscillator(); 127 | osc.type = Audio.oscTypeForWaveValue( sound.wave ); 128 | 129 | const ticDuration = Audio.ticDurationForSpeedValue( speed ); 130 | 131 | osc.frequency.value = Audio.frequencyForNote( note ); 132 | 133 | const gainNode = this.context.createGain(); 134 | 135 | const initialVolume = Audio.valueForVolume( sound.volumeTics[0] ) * Audio.linearToAdjustedVolume( volume ); 136 | gainNode.gain.setValueAtTime( initialVolume, this.context.currentTime ); 137 | 138 | osc.detune.setValueAtTime( sound.pitchTics[0] * sound.pitchScale, this.context.currentTime ); 139 | 140 | for ( let tic = 1; tic < duration; tic += 1 ) { 141 | const time = this.context.currentTime + tic * ticDuration; 142 | const volumeTicIndex = Audio.indexAtTic( tic, sound.useVolumeLoop, sound.volumeLoopStart, sound.volumeLoopEnd ); 143 | const pitchTicIndex = Audio.indexAtTic( tic, sound.usePitchLoop, sound.pitchLoopStart, sound.pitchLoopEnd ); 144 | const arpTicIndex = Audio.indexAtTic( tic, sound.useArpLoop, sound.arpLoopStart, sound.arpLoopEnd ); 145 | 146 | const currentVolume = Audio.valueForVolume( sound.volumeTics[volumeTicIndex] ) 147 | * Audio.linearToAdjustedVolume( volume ); 148 | 149 | gainNode.gain.linearRampToValueAtTime( currentVolume, time ); 150 | osc.detune.linearRampToValueAtTime( sound.pitchTics[pitchTicIndex] * sound.pitchScale, time ); 151 | const currentNote = note + sound.arpTics[arpTicIndex]; 152 | const currentFrequency = Audio.frequencyForNote( currentNote ); 153 | osc.frequency.setValueAtTime( currentFrequency, time ); 154 | } 155 | const stopTime = this.context.currentTime + ( duration * ticDuration ) + ( sound.releaseLength * ticDuration ); 156 | 157 | if ( sound.releaseMode === Sound.RELEASE_EXPO ) { 158 | gainNode.gain.exponentialRampToValueAtTime( 0, stopTime ); 159 | } 160 | else { 161 | // default to linear 162 | gainNode.gain.linearRampToValueAtTime( 0, stopTime ); 163 | } 164 | osc.connect( gainNode ).connect( this.context.destination ); 165 | osc.start(); 166 | 167 | osc.stop( stopTime ); 168 | } 169 | 170 | /** 171 | * Stop a sound that is being played infinitely 172 | * @param {*} soundIndex 173 | */ 174 | stopInfiniteSound( soundIndex ) { 175 | const sound = this.sounds[soundIndex]; 176 | if ( sound.isPlayingInfiniteSound ) { 177 | const stopTime = this.context.currentTime + ( sound.releaseLength * sound.infiniteTicDuration ); 178 | if ( sound.releaseMode === Sound.RELEASE_EXPO ) { 179 | try { 180 | sound.infiniteGain.gain.exponentialRampToValueAtTime( 0.01, stopTime ); 181 | } 182 | catch ( err ) { 183 | sound.infiniteGain.gain.linearRampToValueAtTime( 0, stopTime ); 184 | } 185 | } 186 | else { 187 | // default to linear 188 | sound.infiniteGain.gain.linearRampToValueAtTime( 0, stopTime ); 189 | } 190 | sound.infiniteTicsPlayed = 0; 191 | sound.infiniteOsc.stop( stopTime ); 192 | sound.isPlayingInfiniteSound = false; 193 | } 194 | } 195 | 196 | /** 197 | * Stop all infinitely playing sounds 198 | */ 199 | stopAllInfiniteSounds() { 200 | for ( let i = 0; i < this.sounds.length; i += 1 ) { 201 | this.stopInfiniteSound( i ); 202 | } 203 | } 204 | 205 | /** 206 | * Play a sound infinitely. Only one instance of a sound at each index can be played at a time. 207 | * @param {*} soundIndex 208 | * @param {*} note 209 | * @param {*} volume 210 | * @param {*} speed 211 | */ 212 | playInfiniteSound( soundIndex, note, volume, speed ) { 213 | if ( this.dataOnlyMode ) { 214 | return; 215 | } 216 | 217 | const sound = this.sounds[soundIndex]; 218 | if ( sound.isPlayingInfiniteSound ) { 219 | this.stopInfiniteSound( soundIndex ); 220 | } 221 | 222 | sound.isPlayingInfiniteSound = true; 223 | sound.infiniteStartTime = this.context.currentTime; 224 | sound.infiniteOsc = this.context.createOscillator(); 225 | sound.infiniteTicDuration = Audio.ticDurationForSpeedValue( speed ); 226 | sound.infiniteGain = this.context.createGain(); 227 | sound.infiniteVolume = volume; 228 | sound.infiniteNote = note; 229 | 230 | sound.infiniteOsc.frequency.value = Audio.frequencyForNote( note ); 231 | sound.infiniteOsc.type = Audio.oscTypeForWaveValue( sound.wave ); 232 | 233 | const initialVolume = Audio.valueForVolume( sound.volumeTics[0] ) * volume; 234 | sound.infiniteGain.gain.setValueAtTime( initialVolume, this.context.currentTime ); 235 | 236 | sound.infiniteOsc.connect( sound.infiniteGain ).connect( this.context.destination ); 237 | sound.infiniteOsc.start(); 238 | } 239 | 240 | /** 241 | * Get the frequency in hertz for a note number. 242 | * @param {*} note 243 | */ 244 | static frequencyForNote( note ) { 245 | let trimmedNote = note; 246 | if ( trimmedNote < 0 ) { 247 | trimmedNote = 0; 248 | } 249 | else if ( trimmedNote >= Frequencies.length ) { 250 | trimmedNote = Frequencies.length - 1; 251 | } 252 | return Frequencies[trimmedNote]; 253 | } 254 | 255 | /** 256 | * Get the duration in seconds for a tic at a given speed number. 257 | * @param {*} speed 258 | */ 259 | static ticDurationForSpeedValue( speed ) { 260 | return Audio.fullDurationForSpeedValue( speed ) / Audio.TICS_PER_SOUND; 261 | } 262 | 263 | /** 264 | * Get the total length of a sound in seconds for a given speed number 265 | * @param {*} speed 266 | */ 267 | static fullDurationForSpeedValue( speed ) { 268 | switch ( speed ) { 269 | case -1: 270 | return 1.5; 271 | case -2: 272 | return 2; 273 | case -3: 274 | return 2.5; 275 | case -4: 276 | return 3; 277 | case 1: 278 | return 0.75; 279 | case 2: 280 | return 0.5; 281 | case 3: 282 | return 0.25; 283 | default: 284 | return 1; 285 | } 286 | } 287 | 288 | /** 289 | * Get the wave type for a wave index 290 | * @param {*} waveValue 291 | */ 292 | static oscTypeForWaveValue( waveValue ) { 293 | switch ( waveValue ) { 294 | case 0: 295 | return 'sine'; 296 | case 1: 297 | return 'triangle'; 298 | case 2: 299 | return 'square'; 300 | case 3: 301 | return 'sawtooth'; 302 | default: 303 | return 'sine'; 304 | } 305 | } 306 | 307 | /** 308 | * Get a 0 - 1 volume value for a 0 - 15 value used by the Sound class. 309 | * @param {*} volume 310 | */ 311 | static valueForVolume( volume ) { 312 | const normalizedValue = volume / 15; 313 | return normalizedValue ** 2.5; 314 | } 315 | 316 | /** 317 | * Adjust a linear volume value to account for human hearing. 318 | * @param {*} volume 319 | */ 320 | static linearToAdjustedVolume( volume ) { 321 | return volume ** 2.5; 322 | } 323 | 324 | /** 325 | * Get the current tic index, taking into account looping. 326 | * @param {*} tic 327 | * @param {*} useLoop 328 | * @param {*} loopStart 329 | * @param {*} loopEnd 330 | */ 331 | static indexAtTic( tic, useLoop, loopStart, loopEnd ) { 332 | if ( !useLoop || loopStart < 0 || loopEnd < loopStart ) { 333 | // no looping 334 | if ( tic < 0 ) { 335 | return 0; 336 | } 337 | 338 | if ( tic >= Audio.TICS_PER_SOUND ) { 339 | return Audio.TICS_PER_SOUND - 1; 340 | } 341 | 342 | return tic; 343 | } 344 | 345 | if ( tic <= loopEnd ) { 346 | // not looping yet 347 | return tic; 348 | } 349 | 350 | const loopLength = loopEnd - loopStart + 1; 351 | const loopAdd = ( tic - loopStart ) % loopLength; 352 | 353 | return loopStart + loopAdd; 354 | } 355 | } 356 | 357 | Audio.TICS_PER_SOUND = 32; 358 | 359 | export default Audio; 360 | -------------------------------------------------------------------------------- /demo/data/WelcomeTransfer.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "5e6ec15503e08617f0babdd7", 3 | "sounds": [ 4 | { 5 | "volumeTics": [ 6 | 9, 7 | 9, 8 | 0, 9 | 0, 10 | 14, 11 | 14, 12 | 0, 13 | 0, 14 | 0, 15 | 0, 16 | 0, 17 | 0, 18 | 0, 19 | 0, 20 | 0, 21 | 0, 22 | 14, 23 | 14, 24 | 0, 25 | 0, 26 | 10, 27 | 10, 28 | 0, 29 | 0, 30 | 0, 31 | 0, 32 | 0, 33 | 0, 34 | 0, 35 | 0, 36 | 0, 37 | 0 38 | ], 39 | "pitchTics": [ 40 | 0, 41 | 0, 42 | 0, 43 | 0, 44 | 0, 45 | 0, 46 | 0, 47 | 0, 48 | 0, 49 | 0, 50 | 0, 51 | 0, 52 | 0, 53 | 0, 54 | 0, 55 | 0, 56 | 0, 57 | 0, 58 | 0, 59 | 0, 60 | 0, 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 0, 66 | 0, 67 | 0, 68 | 0, 69 | 0, 70 | 0, 71 | 0 72 | ], 73 | "arpTics": [ 74 | 0, 75 | 0, 76 | 0, 77 | 0, 78 | 0, 79 | 0, 80 | 0, 81 | 0, 82 | 0, 83 | 0, 84 | 0, 85 | 0, 86 | 0, 87 | 0, 88 | 0, 89 | -2, 90 | -2, 91 | -2, 92 | -2, 93 | -2, 94 | -2, 95 | -2, 96 | -2, 97 | -2, 98 | -2, 99 | -2, 100 | -2, 101 | -2, 102 | -2, 103 | -2, 104 | -2, 105 | -2 106 | ], 107 | "_id": "5e6ec18803e08617f0babe21", 108 | "pitchScale": 100, 109 | "wave": 2, 110 | "useVolumeLoop": true, 111 | "volumeLoopStart": 0, 112 | "volumeLoopEnd": 31, 113 | "usePitchLoop": false, 114 | "pitchLoopStart": 0, 115 | "pitchLoopEnd": 31, 116 | "useArpLoop": true, 117 | "arpLoopStart": 0, 118 | "arpLoopEnd": 31, 119 | "name": "walk", 120 | "releaseLength": 1, 121 | "releaseMode": "linear" 122 | }, 123 | { 124 | "volumeTics": [ 125 | 15, 126 | 15, 127 | 15, 128 | 15, 129 | 15, 130 | 15, 131 | 15, 132 | 15, 133 | 15, 134 | 15, 135 | 15, 136 | 15, 137 | 15, 138 | 15, 139 | 15, 140 | 15, 141 | 15, 142 | 15, 143 | 15, 144 | 15, 145 | 15, 146 | 15, 147 | 15, 148 | 15, 149 | 15, 150 | 15, 151 | 15, 152 | 15, 153 | 15, 154 | 15, 155 | 15, 156 | 15 157 | ], 158 | "pitchTics": [ 159 | 0, 160 | 0, 161 | 0, 162 | 0, 163 | 0, 164 | 0, 165 | 0, 166 | 0, 167 | 0, 168 | 0, 169 | 0, 170 | 0, 171 | 0, 172 | 0, 173 | 0, 174 | 0, 175 | 0, 176 | 0, 177 | 0, 178 | 0, 179 | 0, 180 | 0, 181 | 0, 182 | 0, 183 | 0, 184 | 0, 185 | 0, 186 | 0, 187 | 0, 188 | 0, 189 | 0, 190 | 0 191 | ], 192 | "arpTics": [ 193 | 0, 194 | 0, 195 | 0, 196 | 0, 197 | 1, 198 | 1, 199 | 1, 200 | 1, 201 | 3, 202 | 3, 203 | 3, 204 | 3, 205 | 5, 206 | 5, 207 | 5, 208 | 5, 209 | 7, 210 | 7, 211 | 7, 212 | 7, 213 | 9, 214 | 9, 215 | 9, 216 | 9, 217 | 11, 218 | 11, 219 | 11, 220 | 11, 221 | 8, 222 | 8, 223 | 8, 224 | 8 225 | ], 226 | "_id": "5e6ec18803e08617f0babe20", 227 | "pitchScale": 100, 228 | "wave": 2, 229 | "useVolumeLoop": false, 230 | "volumeLoopStart": 0, 231 | "volumeLoopEnd": 31, 232 | "usePitchLoop": false, 233 | "pitchLoopStart": 0, 234 | "pitchLoopEnd": 31, 235 | "useArpLoop": false, 236 | "arpLoopStart": 0, 237 | "arpLoopEnd": 31, 238 | "name": "pickup", 239 | "releaseLength": 10, 240 | "releaseMode": "linear" 241 | } 242 | ], 243 | "tilesets": [ 244 | { 245 | "layers": [ 246 | { 247 | "_id": "5e6ec18803e08617f0babe23", 248 | "isVisible": true, 249 | "data": "CwGgDCoIwjHSA7OStbPmgHFlBmNOEAmATn0JQQ1RiJJFIggCZaU3ojO0IHy+lVqlLd2wrh3EgeUMQgmNmMkADYRK9TTbqC5HjpC5h6mKD1p1WAZbTJQ5GKaQuITuyACsjWzGTHfFwcPaxR1chtkR2VwpTCfKBiEpIjlZFiIdJ9MtSMAw2RCqS0XbVyirBp3NmQ6Yhc6v1dbGsoCWsoit1zWuvbKJqx1brr1XrR+xpdhgZ6XXDMUKbGGidW6-xnFuTRQbuhu733YOqw9+tAFryXFo4ujbBnyUBpn5recFHtsoJ-v4BYLACwAWbCwyhBsDBENBRmUsMk0Fk22RtFRSNoKLhFCgbG8O1A3kR12JRJOqDJOwI+NgBAx4JxbDoTW8BDG5BpUDpUOxglxhBZ9XU3hpY2phG5-IZfKZ5OQrNyOwVsWV9U5kuhVB5zJkEFVHKFFIl9SlEO15Jg3kMUHlhsS10MhONJE1mH5OstmmIwudPOlaIFsBFXsB1zoBBdvJNsp2UB91vF7lt4YD-sjOp97jwesC6tTynT5LwBpT9IL5qpJdpAbwbD1TJjMGLYerkdr11qeOrLAx+JIdEbLituWDhkzivHXOjsAN1s9pjYTO5HSnvdbPL7YcX1Zexvt7rlDtyTtApnFbJHvv5Tov-OHpk9+O3U6aoFlK4jFeX09Lv35S48N8PBTRxBynMkzyPEo80jG8emDJ892mFRXx5ICMXQr8PBWQYKxgLs5ygmQu1-a9rgvFgEKwppkFkAwA0wg8X1KFRYR1LsLzwJ1SLYdtgyYhgijo1BhMYmNBJcME8JUe8ZyIpUWhbVceR7IifQXDxaIYlQMJk80hIeJj8PFa0c1vJSNRkJ0x2uDSaJQySHO5d8VFlZyPAINzNJUUDAzwUc5hg7Uc0MFgczs1YeC0lzn3uGKnJEjyEqafDUGihJB2bIL3RCnpwpU34XNS3TRL0-9CuAnzpKgYMKM9FM2y7CKshckrtJIlycJcXyMyI206os6c+MVTqHO+MjSo67CxsMwdbUMXBRUvQwAvUSdLJI3IDVqBgdkW2xxU7OYNO3E6VCOlYevJVTZPHNbBoxO9jQi2MuwRVzEKA-cwO4npfuU91NqnF6B2uNj6n6SjzU-Wo3EyqsAYbQaQY3EaUIYTyZpYSMAKbVzgNBnUsqvJG8xRp6zuYzGYpx6s8di2pCYtKtINk-7P0B5Hqq7I6UyaanHrplxMdewNNtMWq0bZwb0yB28wLe-T6kXRD+ZQpnY1nRV9oa7VFZ2O7rm8cHkGp1XhZQuHA320KEcs4aTeVmRzdNy3pJtnpxcvHijQo4NwepjwoYPQOUqtom7e01Uoe8SmMfqWjaaphPYHDotyMVXKxnqndUdkmO0dxp2aYK5OGYtQcOMz+Sves3JE3Xf8s9TjtqOLoPBbLmbRaJltTBZlvZIbqdZb7ocvXtY2lbN6f1ek4ndc52SoH2x2Z4PV2mQ1vHKW5XejWgPfaCPw+AxYH1OLB2eVeviu-NzsjEeP7Sn9P2FDbCojxTXj7b88RCHbQ3xk0YOXly7-x5IAkOKdQFrH6BA-kUCwFY3NqHFipovhtzQbRVBwD0GukLkLTeW4wwcxIomZ8ElMZHX9IhACrQRZ-nEmlH8udxqUPOk7JoGlaFtw6K1GagtiFCKdp3CSqVUIuUQrwgSlUvLDhEZjRROk+FoHkWVAhzU1FrHLtuZRwk1ZTRSglXRGioxaO3AwlRG9RF-yVpYi2U1-RaK0iA5Kjig7uOIYYtY9knFJBscVRy8UvI+LcaEjxYSTSRRYduaRATtzfCKtYlySi7HlSSXIkhzilZdXYUlAxHiXZzEFBgiAWi8kBh8fokh3CZrxMwfyFYZ1rQOIuhPAh70b4TQaAQyEZTICwmxvEcCRB+mLgICMbkOFqQQg1vLKySwhYrA6M4cCyhP5GV1FhE6dwsxDjGaDEgG1zj7PlHWSASMAKPRsKABAughyTOILzOm4cAiLGCPReUTy8AvJfG8ig5BzL6ApE8lgfz3CPxIACwQbxLoqM8PQOhdNCb5DmEC48CQKQQiRimVWaKHkgp9I4VAwBblXIfkyAl5JnjkrQt2bUNhYinjQByZQUAro7GGeVEeKllDeDhayrwMIVIXIsBeTwFzxm8iAA", 250 | "format": "rc" 251 | } 252 | ], 253 | "_id": "5e6ec18803e08617f0babe22", 254 | "width": 8, 255 | "height": 8 256 | } 257 | ], 258 | "tilemaps": [ 259 | { 260 | "layers": [ 261 | { 262 | "_id": "5e6ec18803e08617f0babe25", 263 | "isVisible": true, 264 | "data": "IwGgzGLADOAsVwFZF1AJhEg7CTSAOcLXUJATjxNUlDRADZqyjIcbVQzStKn2MRUGFh1aIeoKrpKw2AgESEdJmM6Iwq8DzCyJTNj0KIKJnnHGSlqFCpNDeWZhMuzYSUVmBA", 265 | "format": "rc" 266 | } 267 | ], 268 | "_id": "5e6ec18803e08617f0babe24", 269 | "name": "untitled", 270 | "width": 12, 271 | "height": 8 272 | } 273 | ], 274 | "dateCreated": "2020-03-15T23:59:17.018Z", 275 | "dateUpdated": "2020-03-16T00:00:08.532Z", 276 | "connectedProject": "5e6ec14503e08617f0babdba", 277 | "user": "5e4c88490450551f7c7262e6", 278 | "urlName": "welcome", 279 | "palette": { 280 | "colors": [ 281 | "000000", 282 | "0b0711", 283 | "eaf2de", 284 | "766e76", 285 | "561f6e", 286 | "7d4f31", 287 | "e98c49", 288 | "fbc0a0", 289 | "f681b2", 290 | "d83232", 291 | "e3e962", 292 | "65cf57", 293 | "2ba957", 294 | "187575", 295 | "1e2cB0", 296 | "2379e5", 297 | "95cae5" 298 | ], 299 | "_id": "5e6ec18803e08617f0babe26" 300 | }, 301 | "code": { 302 | "_id": "5e6ec18803e08617f0babe27", 303 | "scripts": [ 304 | { 305 | "_id": "5e6ec18803e08617f0babe1f", 306 | "text": "\n// Globals\nlet inp = null; // input\nlet scr = null; // screen\nlet aud = null; // audio\n\nconst player = {\n\tx: 90,\n\ty: 30,\n\tspeed: 0.5,\n\tisWalking: false,\n\tflip: 0,\n\tframesSinceWalkStart: 0\n}\n\nconst mushrooms = [\n\t{ \n\t\tx: 36,\n\t\ty: 30,\n\t\twasGrabbed: false\n\t},\n\t{ \n\t\tx: 130,\n\t\ty: 70,\n\t\twasGrabbed: false\n\t}\n];\n\nlet numberOfGrabbedMushrooms = 0;\n\nlet randomColor = 1;\n\n// initialization\nengine.onInit = () => {\n\tinp = engine.input;\n\tscr = engine.screen;\n\taud = engine.audio;\n\t\n\tupdateColors();\n};\n\n\n// update loop\nengine.onUpdate = () => {\n scr.clear( 1 );\n\n\tscr.drawMap(\n\t 0, // originX on map\n\t 0, // originY on map\n\t -1, // width\n\t -1, // height\n\t 0, // screenX\n\t 0, // screenY\n\t 0 // tilemap index\n\t);\n\t\n\tdrawMushrooms();\n\t\n\tupdatePlayer();\n\t\n\tlet textMainColor = 2;\n\tif ( numberOfGrabbedMushrooms > 0 ) {\n\t\ttextMainColor = randomColor;\n\t}\n\t\n\tlet textPositionOffset = 0;\n\tif ( numberOfGrabbedMushrooms > 1 ) {\n\t\ttextPositionOffset = Math.sin( engine.realTimeSinceGameStart * 10 ) * 8;\n\t}\n\t\n\tscr.drawText(\n\t\t'Welcome to Bitmelo!',\n\t\t50,\n\t\t90 + Math.floor( textPositionOffset ),\n\t\ttextMainColor,\n\t\t1,\n\t\t0\n\t);\n};\n\nfunction drawMushrooms() {\n\tmushrooms.forEach( mushroom => {\n\t\tif ( !mushroom.wasGrabbed ) {\n\t\t\tscr.drawTile(\n\t\t\t\t61,\n\t\t\t\tmushroom.x - 8, // center on the position\n\t\t\t\tmushroom.y - 8, // center on the position\n\t\t\t\t0\n\t\t\t);\n\t\t}\n\t} );\n}\n\nfunction updatePlayer() {\n\tlet newX = player.x;\n\tlet newY = player.y;\n\t\n\tlet isWalking = false;\n\tif ( inp.left.pressed ) {\n\t\tnewX -= player.speed;\n\t\tisWalking = true;\n\t\tplayer.flip = 1;\n\t}\n\telse if ( inp.right.pressed ) {\n\t\tnewX += player.speed;\n\t\tisWalking = true;\n\t\tplayer.flip = 0;\n\t}\n\t\n\tif ( inp.down.pressed ) {\n\t\tnewY -= player.speed;\n\t\tisWalking = true;\n\t}\n\telse if ( inp.up.pressed ) {\n\t\tnewY += player.speed;\n\t\tisWalking = true;\n\t}\n\t\n\tif ( isWalking ) {\n\t\tplayer.framesSinceWalkStart += 1;\n\t}\n\t\n\t// play or stop audio\n\tif ( isWalking && !player.isWalking ) {\n\t\t// started walking\n\t\tplayer.framesSinceWalkStart = 0;\n\t\t\n\t\tlet note = bitmelo.Notes.C4;\n\t\tif ( numberOfGrabbedMushrooms > 1 ) {\n\t\t\tnote = bitmelo.Notes.C2;\n\t\t}\n\t\telse if ( numberOfGrabbedMushrooms > 0 ) {\n\t\t\tnote = bitmelo.Notes.C3;\n\t\t}\n\t\t\n\t\taud.playInfiniteSound(\n\t\t\t0,\n\t\t\tnote,\n\t\t\t0.5,\n\t\t\t2\n\t\t);\n\t}\n\telse if ( !isWalking && player.isWalking ) {\n\t\t// stopped walking\n\t\taud.stopInfiniteSound( 0 );\n\t}\n\t\n\tplayer.isWalking = isWalking;\n\t\n\t// make sure we are not colliding with the fence\n\tif ( \n\t\tnewX >= 16\n\t\t&& newX < scr.width - 16\n\t\t&& newY >= 24\n\t\t&& newY < scr.height - 16\n\t) {\n\t\tplayer.x = newX;\n\t\tplayer.y = newY;\n\t}\n\t\n\t// check mushroom collisions\n\tfor ( let i = 0; i < mushrooms.length; i += 1 ) {\n\t\tconst mushroom = mushrooms[i];\n\t\tif ( !mushroom.wasGrabbed ) {\n\t\t\tconst deltaX = Math.abs( player.x - mushroom.x );\n\t\t\tconst deltaY= Math.abs( player.y - mushroom.y );\n\t\t\tconst distance = Math.sqrt( deltaX * deltaX + deltaY * deltaY );\n\t\t\t\n\t\t\t// player has grabbed a mushroom\n\t\t\tif ( distance <= 12 ) {\n\t\t\t\tmushroom.wasGrabbed = true;\n\t\t\t\tnumberOfGrabbedMushrooms += 1;\n\t\t\t\t\n\t\t\t\taud.playSound(\n\t\t\t\t\t1,\n\t\t\t\t\tbitmelo.Notes.E3,\n\t\t\t\t\t48,\n\t\t\t\t\t0.25,\n\t\t\t\t\t1\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// draw the player\n\tlet frameGID = 1;\n\tif ( player.isWalking ) {\n\t\tif ( player.framesSinceWalkStart % 16 < 8 ) {\n\t\t\tframeGID = 2;\n\t\t}\n\t\telse {\n\t\t\tframeGID = 3;\n\t\t}\n\t}\n\t\n\tscr.drawTile(\n\t\tframeGID,\n\t\tMath.floor( player.x ) - 8, // center the tile on the position\n\t\tMath.floor( player.y ) - 8, // center the tile on the position\n\t\tplayer.flip\n\t);\n}\n\nfunction updateColors() {\n\trandomColor = Math.floor( Math.random() * 16 ) + 1;\n\tsetTimeout( updateColors, 100 );\n}\n\n\n\n\n\n\n\n\n\n\n\n" 307 | } 308 | ] 309 | }, 310 | "project": { 311 | "_id": "5e6ec18803e08617f0babe28", 312 | "tileSize": 16, 313 | "name": "Welcome", 314 | "screen": { 315 | "_id": "5e6ec18803e08617f0babe29", 316 | "width": 192, 317 | "height": 128, 318 | "scaleMode": 1, 319 | "scale": 3, 320 | "minScale": 1, 321 | "maxScale": 4, 322 | "horizontalScaleCushion": 10, 323 | "verticalScaleCushion": 10, 324 | "rescaleOnWindowResize": true 325 | }, 326 | "misc": { 327 | "_id": "5e6ec18803e08617f0babe2a", 328 | "hideCursor": false, 329 | "clickToBegin": true, 330 | "startTransitionFrames": 60 331 | }, 332 | "editorVersion": "1.3.1" 333 | }, 334 | "__v": 2, 335 | "format": "transfer" 336 | } 337 | -------------------------------------------------------------------------------- /src/lz-string/libs/lz-string.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Pieroxy 2 | // This work is free. You can redistribute it and/or modify it 3 | // under the terms of the WTFPL, Version 2 4 | // For more information see LICENSE.txt or http://www.wtfpl.net/ 5 | // 6 | // For more information, the home page: 7 | // http://pieroxy.net/blog/pages/lz-string/testing.html 8 | // 9 | // LZ-based compression algorithm, version 1.4.4 10 | var LZString = (function() { 11 | 12 | // private property 13 | var f = String.fromCharCode; 14 | var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 15 | var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; 16 | var baseReverseDic = {}; 17 | 18 | function getBaseValue(alphabet, character) { 19 | if (!baseReverseDic[alphabet]) { 20 | baseReverseDic[alphabet] = {}; 21 | for (var i=0 ; i>> 8; 66 | buf[i*2+1] = current_value % 256; 67 | } 68 | return buf; 69 | }, 70 | 71 | //decompress from uint8array (UCS-2 big endian format) 72 | decompressFromUint8Array:function (compressed) { 73 | if (compressed===null || compressed===undefined){ 74 | return LZString.decompress(compressed); 75 | } else { 76 | var buf=new Array(compressed.length/2); // 2 bytes per character 77 | for (var i=0, TotalLen=buf.length; i> 1; 159 | } 160 | } else { 161 | value = 1; 162 | for (i=0 ; i> 1; 184 | } 185 | } 186 | context_enlargeIn--; 187 | if (context_enlargeIn == 0) { 188 | context_enlargeIn = Math.pow(2, context_numBits); 189 | context_numBits++; 190 | } 191 | delete context_dictionaryToCreate[context_w]; 192 | } else { 193 | value = context_dictionary[context_w]; 194 | for (i=0 ; i> 1; 204 | } 205 | 206 | 207 | } 208 | context_enlargeIn--; 209 | if (context_enlargeIn == 0) { 210 | context_enlargeIn = Math.pow(2, context_numBits); 211 | context_numBits++; 212 | } 213 | // Add wc to the dictionary. 214 | context_dictionary[context_wc] = context_dictSize++; 215 | context_w = String(context_c); 216 | } 217 | } 218 | 219 | // Output the code for w. 220 | if (context_w !== "") { 221 | if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { 222 | if (context_w.charCodeAt(0)<256) { 223 | for (i=0 ; i> 1; 244 | } 245 | } else { 246 | value = 1; 247 | for (i=0 ; i> 1; 269 | } 270 | } 271 | context_enlargeIn--; 272 | if (context_enlargeIn == 0) { 273 | context_enlargeIn = Math.pow(2, context_numBits); 274 | context_numBits++; 275 | } 276 | delete context_dictionaryToCreate[context_w]; 277 | } else { 278 | value = context_dictionary[context_w]; 279 | for (i=0 ; i> 1; 289 | } 290 | 291 | 292 | } 293 | context_enlargeIn--; 294 | if (context_enlargeIn == 0) { 295 | context_enlargeIn = Math.pow(2, context_numBits); 296 | context_numBits++; 297 | } 298 | } 299 | 300 | // Mark the end of the stream 301 | value = 2; 302 | for (i=0 ; i> 1; 312 | } 313 | 314 | // Flush the last char 315 | while (true) { 316 | context_data_val = (context_data_val << 1); 317 | if (context_data_position == bitsPerChar-1) { 318 | context_data.push(getCharFromInt(context_data_val)); 319 | break; 320 | } 321 | else context_data_position++; 322 | } 323 | return context_data.join(''); 324 | }, 325 | 326 | decompress: function (compressed) { 327 | if (compressed == null) return ""; 328 | if (compressed == "") return null; 329 | return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); 330 | }, 331 | 332 | _decompress: function (length, resetValue, getNextValue) { 333 | var dictionary = [], 334 | next, 335 | enlargeIn = 4, 336 | dictSize = 4, 337 | numBits = 3, 338 | entry = "", 339 | result = [], 340 | i, 341 | w, 342 | bits, resb, maxpower, power, 343 | c, 344 | data = {val:getNextValue(0), position:resetValue, index:1}; 345 | 346 | for (i = 0; i < 3; i += 1) { 347 | dictionary[i] = i; 348 | } 349 | 350 | bits = 0; 351 | maxpower = Math.pow(2,2); 352 | power=1; 353 | while (power!=maxpower) { 354 | resb = data.val & data.position; 355 | data.position >>= 1; 356 | if (data.position == 0) { 357 | data.position = resetValue; 358 | data.val = getNextValue(data.index++); 359 | } 360 | bits |= (resb>0 ? 1 : 0) * power; 361 | power <<= 1; 362 | } 363 | 364 | switch (next = bits) { 365 | case 0: 366 | bits = 0; 367 | maxpower = Math.pow(2,8); 368 | power=1; 369 | while (power!=maxpower) { 370 | resb = data.val & data.position; 371 | data.position >>= 1; 372 | if (data.position == 0) { 373 | data.position = resetValue; 374 | data.val = getNextValue(data.index++); 375 | } 376 | bits |= (resb>0 ? 1 : 0) * power; 377 | power <<= 1; 378 | } 379 | c = f(bits); 380 | break; 381 | case 1: 382 | bits = 0; 383 | maxpower = Math.pow(2,16); 384 | power=1; 385 | while (power!=maxpower) { 386 | resb = data.val & data.position; 387 | data.position >>= 1; 388 | if (data.position == 0) { 389 | data.position = resetValue; 390 | data.val = getNextValue(data.index++); 391 | } 392 | bits |= (resb>0 ? 1 : 0) * power; 393 | power <<= 1; 394 | } 395 | c = f(bits); 396 | break; 397 | case 2: 398 | return ""; 399 | } 400 | dictionary[3] = c; 401 | w = c; 402 | result.push(c); 403 | while (true) { 404 | if (data.index > length) { 405 | return ""; 406 | } 407 | 408 | bits = 0; 409 | maxpower = Math.pow(2,numBits); 410 | power=1; 411 | while (power!=maxpower) { 412 | resb = data.val & data.position; 413 | data.position >>= 1; 414 | if (data.position == 0) { 415 | data.position = resetValue; 416 | data.val = getNextValue(data.index++); 417 | } 418 | bits |= (resb>0 ? 1 : 0) * power; 419 | power <<= 1; 420 | } 421 | 422 | switch (c = bits) { 423 | case 0: 424 | bits = 0; 425 | maxpower = Math.pow(2,8); 426 | power=1; 427 | while (power!=maxpower) { 428 | resb = data.val & data.position; 429 | data.position >>= 1; 430 | if (data.position == 0) { 431 | data.position = resetValue; 432 | data.val = getNextValue(data.index++); 433 | } 434 | bits |= (resb>0 ? 1 : 0) * power; 435 | power <<= 1; 436 | } 437 | 438 | dictionary[dictSize++] = f(bits); 439 | c = dictSize-1; 440 | enlargeIn--; 441 | break; 442 | case 1: 443 | bits = 0; 444 | maxpower = Math.pow(2,16); 445 | power=1; 446 | while (power!=maxpower) { 447 | resb = data.val & data.position; 448 | data.position >>= 1; 449 | if (data.position == 0) { 450 | data.position = resetValue; 451 | data.val = getNextValue(data.index++); 452 | } 453 | bits |= (resb>0 ? 1 : 0) * power; 454 | power <<= 1; 455 | } 456 | dictionary[dictSize++] = f(bits); 457 | c = dictSize-1; 458 | enlargeIn--; 459 | break; 460 | case 2: 461 | return result.join(''); 462 | } 463 | 464 | if (enlargeIn == 0) { 465 | enlargeIn = Math.pow(2, numBits); 466 | numBits++; 467 | } 468 | 469 | if (dictionary[c]) { 470 | entry = dictionary[c]; 471 | } else { 472 | if (c === dictSize) { 473 | entry = w + w.charAt(0); 474 | } else { 475 | return null; 476 | } 477 | } 478 | result.push(entry); 479 | 480 | // Add w+entry[0] to the dictionary. 481 | dictionary[dictSize++] = w + entry.charAt(0); 482 | enlargeIn--; 483 | 484 | w = entry; 485 | 486 | if (enlargeIn == 0) { 487 | enlargeIn = Math.pow(2, numBits); 488 | numBits++; 489 | } 490 | 491 | } 492 | } 493 | }; 494 | return LZString; 495 | })(); 496 | 497 | if (typeof define === 'function' && define.amd) { 498 | define(function () { return LZString; }); 499 | } else if( typeof module !== 'undefined' && module != null ) { 500 | module.exports = LZString 501 | } else if( typeof angular !== 'undefined' && angular != null ) { 502 | angular.module('LZString', []) 503 | .factory('LZString', function () { 504 | return LZString; 505 | }); 506 | } 507 | -------------------------------------------------------------------------------- /src/Input/Input.js: -------------------------------------------------------------------------------- 1 | import Keys from './Keys'; 2 | 3 | 4 | /** 5 | * Handle game input 6 | */ 7 | class Input { 8 | constructor() { 9 | /** 10 | * Reference to the canvas used for mouse input. 11 | * Automatically added by the Engine. 12 | */ 13 | this.canvas = null; 14 | 15 | /** 16 | * The scale of the canvas, aka the pixel size. 17 | * Added automatically by the Engine 18 | */ 19 | this.canvasScale = 1; 20 | 21 | /** 22 | * The width of the game screen. 23 | * Not affected by this.canvasScale. 24 | * Added automatically by the Engine. 25 | */ 26 | this.screenWidth = 1; 27 | 28 | /** 29 | * The height of the game screen. 30 | * Not affected by this.canvasScale. 31 | * Added automatically by the Engine. 32 | */ 33 | this.screenHeight = 1; 34 | 35 | /** 36 | * Object containing input state of the mouse. 37 | * mouse.isOffScreen, 38 | * mouse.position.x, 39 | * mouse.position.y, 40 | * mouse.left.pressed, 41 | * mouse.left.down, 42 | * mouse.left.up, 43 | * mouse.right.pressed, 44 | * mouse.right.down, 45 | * mouse.right.up 46 | */ 47 | this.mouse = {}; 48 | this.mouse.isOffScreen = true; 49 | this.mouse.position = { 50 | x: -1, 51 | y: -1, 52 | }; 53 | 54 | this.mouse.left = { 55 | pressed: false, 56 | down: false, 57 | up: false, 58 | }; 59 | 60 | this.mouse.right = { 61 | pressed: false, 62 | down: false, 63 | up: false, 64 | }; 65 | 66 | /** 67 | * Caches keyboard key states. 68 | */ 69 | this._keysRaw = new Uint8ClampedArray( 256 ); 70 | 71 | /** 72 | * Keyboard states for the current frame 73 | */ 74 | this._currentKeys = new Uint8ClampedArray( 256 ); 75 | 76 | /** 77 | * Keyboard states for the last frame 78 | */ 79 | this._lastKeys = new Uint8ClampedArray( 256 ); 80 | 81 | /** 82 | * Maps standard game buttons to keyboard keys. 83 | */ 84 | this._buttonsToKeys = new Uint8ClampedArray( 32 ); 85 | 86 | /** 87 | * Maps standard game buttons to alternate keyboard keys 88 | */ 89 | this._buttonsToAltKeys = new Uint8ClampedArray( 32 ); 90 | 91 | /** 92 | * Maps standard game buttons to joypad buttons 93 | */ 94 | this._buttonsToJoyButtons = new Int8Array( 32 ); 95 | 96 | /** 97 | * Maps standard game buttons to alternate joypad buttons 98 | */ 99 | this._buttonsToAltJoyButtons = new Int8Array( 32 ); 100 | this._buttonsToAltJoyButtons.fill( -1 ); 101 | 102 | /** 103 | * Maps standard game buttons to joypad axes 104 | */ 105 | this._buttonsToJoyAxes = new Int8Array( 32 ); 106 | 107 | /** 108 | * gamepad button states for the current frame 109 | */ 110 | this._currentJoyButtons = new Uint8ClampedArray( 20 ); 111 | 112 | /** 113 | * gamepad button states for the last frame 114 | */ 115 | this._lastJoyButtons = new Uint8ClampedArray( 20 ); 116 | 117 | /** 118 | * gamepad axes states for the current frame 119 | */ 120 | this._currentJoyAxes = new Array( 9 ); 121 | 122 | /** 123 | * gamepad axes states for the last frame 124 | */ 125 | this._lastJoyAxes = new Array( 9 ); 126 | 127 | // default button mappings 128 | this._buttonsToKeys[Input.GAME_LEFT] = Keys.LEFT_ARROW; 129 | this._buttonsToKeys[Input.GAME_RIGHT] = Keys.RIGHT_ARROW; 130 | this._buttonsToKeys[Input.GAME_UP] = Keys.UP_ARROW; 131 | this._buttonsToKeys[Input.GAME_DOWN] = Keys.DOWN_ARROW; 132 | 133 | this._buttonsToKeys[Input.GAME_ACTION_ONE] = Keys.Z_KEY; 134 | this._buttonsToKeys[Input.GAME_ACTION_TWO] = Keys.X_KEY; 135 | this._buttonsToKeys[Input.GAME_ACTION_THREE] = Keys.A_KEY; 136 | this._buttonsToKeys[Input.GAME_ACTION_FOUR] = Keys.S_KEY; 137 | this._buttonsToKeys[Input.GAME_LEFT_TRIGGER] = Keys.Q_KEY; 138 | this._buttonsToKeys[Input.GAME_RIGHT_TRIGGER] = Keys.W_KEY; 139 | 140 | this._buttonsToKeys[Input.GAME_PAUSE] = Keys.P_KEY; 141 | 142 | this._buttonsToKeys[Input.MENU_LEFT] = Keys.LEFT_ARROW; 143 | this._buttonsToKeys[Input.MENU_RIGHT] = Keys.RIGHT_ARROW; 144 | this._buttonsToKeys[Input.MENU_UP] = Keys.UP_ARROW; 145 | this._buttonsToKeys[Input.MENU_DOWN] = Keys.DOWN_ARROW; 146 | 147 | this._buttonsToKeys[Input.MENU_CONFIRM] = Keys.Z_KEY; 148 | this._buttonsToKeys[Input.MENU_BACK] = Keys.X_KEY; 149 | 150 | // default alt button mappings 151 | this._buttonsToAltKeys[Input.GAME_LEFT] = Keys.J_KEY; 152 | this._buttonsToAltKeys[Input.GAME_RIGHT] = Keys.L_KEY; 153 | this._buttonsToAltKeys[Input.GAME_UP] = Keys.I_KEY; 154 | this._buttonsToAltKeys[Input.GAME_DOWN] = Keys.K_KEY; 155 | 156 | this._buttonsToAltKeys[Input.GAME_ACTION_ONE] = Keys.SPACE; 157 | this._buttonsToAltKeys[Input.GAME_ACTION_TWO] = Keys.D_KEY; 158 | this._buttonsToAltKeys[Input.GAME_ACTION_THREE] = Keys.C_KEY; 159 | this._buttonsToAltKeys[Input.GAME_ACTION_FOUR] = Keys.V_KEY; 160 | this._buttonsToAltKeys[Input.GAME_LEFT_TRIGGER] = Keys.SHIFT; 161 | this._buttonsToAltKeys[Input.GAME_RIGHT_TRIGGER] = Keys.ALT; 162 | 163 | this._buttonsToAltKeys[Input.GAME_PAUSE] = Keys.ENTER; 164 | 165 | this._buttonsToAltKeys[Input.MENU_LEFT] = Keys.J_KEY; 166 | this._buttonsToAltKeys[Input.MENU_RIGHT] = Keys.L_KEY; 167 | this._buttonsToAltKeys[Input.MENU_UP] = Keys.I_KEY; 168 | this._buttonsToAltKeys[Input.MENU_DOWN] = Keys.K_KEY; 169 | 170 | this._buttonsToAltKeys[Input.MENU_CONFIRM] = Keys.SPACE; 171 | this._buttonsToAltKeys[Input.MENU_BACK] = Keys.D_KEY; 172 | 173 | // default joypad button mappings 174 | this._buttonsToJoyButtons[Input.GAME_LEFT] = 14; 175 | this._buttonsToJoyButtons[Input.GAME_RIGHT] = 15; 176 | this._buttonsToJoyButtons[Input.GAME_UP] = 12; 177 | this._buttonsToJoyButtons[Input.GAME_DOWN] = 13; 178 | 179 | this._buttonsToJoyButtons[Input.GAME_ACTION_ONE] = 0; 180 | this._buttonsToJoyButtons[Input.GAME_ACTION_TWO] = 1; 181 | this._buttonsToJoyButtons[Input.GAME_ACTION_THREE] = 2; 182 | this._buttonsToJoyButtons[Input.GAME_ACTION_FOUR] = 3; 183 | this._buttonsToJoyButtons[Input.GAME_LEFT_TRIGGER] = 4; 184 | this._buttonsToJoyButtons[Input.GAME_RIGHT_TRIGGER] = 5; 185 | 186 | this._buttonsToJoyButtons[Input.GAME_PAUSE] = 9; 187 | 188 | this._buttonsToJoyButtons[Input.MENU_LEFT] = 14; 189 | this._buttonsToJoyButtons[Input.MENU_RIGHT] = 15; 190 | this._buttonsToJoyButtons[Input.MENU_UP] = 12; 191 | this._buttonsToJoyButtons[Input.MENU_DOWN] = 13; 192 | 193 | this._buttonsToJoyButtons[Input.MENU_CONFIRM] = 0; 194 | this._buttonsToJoyButtons[Input.MENU_BACK] = 1; 195 | 196 | // default joypad alt button mappings 197 | this._buttonsToAltJoyButtons[Input.GAME_LEFT_TRIGGER] = 6; 198 | this._buttonsToAltJoyButtons[Input.GAME_RIGHT_TRIGGER] = 7; 199 | 200 | this._buttonsToAltJoyButtons[Input.GAME_PAUSE] = 16; 201 | 202 | // default joypad axis mappings 203 | this._buttonsToJoyAxes[Input.GAME_LEFT] = -1; 204 | this._buttonsToJoyAxes[Input.GAME_RIGHT] = 1; 205 | this._buttonsToJoyAxes[Input.GAME_UP] = -2; 206 | this._buttonsToJoyAxes[Input.GAME_DOWN] = 2; 207 | 208 | this._buttonsToJoyAxes[Input.MENU_LEFT] = -1; 209 | this._buttonsToJoyAxes[Input.MENU_RIGHT] = 1; 210 | this._buttonsToJoyAxes[Input.MENU_UP] = -2; 211 | this._buttonsToJoyAxes[Input.MENU_DOWN] = 2; 212 | 213 | /** 214 | * Is the left mouse button down this frame? 215 | */ 216 | this._currentMouseLeft = false; 217 | /** 218 | * Was the left mouse button down last frame? 219 | */ 220 | this._lastMouseLeft = false; 221 | 222 | /** 223 | * Force the left mouse button state to be down this frame. 224 | * Used for the edge case in which a mouse button is clicked up and down all in the span of one frame. 225 | */ 226 | this._forceMouseLeftDown = false; 227 | 228 | /** 229 | * Is the right mouse button down this frame? 230 | */ 231 | this._currentMouseRight = false; 232 | 233 | /** 234 | * Was the right mouse button down last frame? 235 | */ 236 | this._lastMouseRight = false; 237 | 238 | /** 239 | * Force the right mouse button state to be down this frame. 240 | * Used for the edge case in which a mouse button is clicked up and down all in the span of one frame. 241 | */ 242 | this._forceMouseRightDown = false; 243 | 244 | /** 245 | * How far from center does an axis need to be to count as pressed? 246 | */ 247 | this._axisThreshold = 0.4; 248 | 249 | /** 250 | * Which keys do not cause event.preventDefault to be called on keydown events. 251 | * By default this is the escape key and all function keys 252 | */ 253 | this._allowDefaultKeys = [ 254 | 27, 255 | 112, 256 | 113, 257 | 114, 258 | 115, 259 | 116, 260 | 117, 261 | 118, 262 | 119, 263 | 120, 264 | 121, 265 | 122, 266 | 123, 267 | ]; 268 | 269 | /** 270 | * This mode runs the engine without drawing to a canvas or playing audio. 271 | * This is useful to use the engine to generate image data. 272 | */ 273 | this.dataOnlyMode = false; 274 | } 275 | 276 | /** 277 | * Do initial setup. Add event listeners. 278 | */ 279 | init() { 280 | if ( this.dataOnlyMode ) { 281 | return; 282 | } 283 | 284 | window.addEventListener( 'keydown', this._keyDown.bind( this ), false ); 285 | window.addEventListener( 'keyup', this._keyUp.bind( this ), false ); 286 | 287 | window.onfocus = () => { 288 | this.clearInput(); 289 | }; 290 | 291 | window.onblur = () => { 292 | this.clearInput(); 293 | }; 294 | 295 | if ( this.canvas ) { 296 | this.canvas.oncontextmenu = ( e ) => { 297 | e.preventDefault(); 298 | }; 299 | 300 | this.canvas.addEventListener( 'pointerenter', this._pointerEnter.bind( this ), false ); 301 | this.canvas.addEventListener( 'pointermove', this._pointerMove.bind( this ), false ); 302 | this.canvas.addEventListener( 'pointerdown', this._pointerDown.bind( this ), false ); 303 | this.canvas.addEventListener( 'pointerup', this._pointerUp.bind( this ), false ); 304 | this.canvas.addEventListener( 'pointerleave', this._pointerLeave.bind( this ), false ); 305 | } 306 | } 307 | 308 | /** 309 | * handle window keydown events 310 | * @param {*} e 311 | */ 312 | _keyDown( e ) { 313 | // prevent default on all keys but escape and function keys 314 | if ( !this._allowDefaultKeys.includes( e.which ) ) { 315 | e.preventDefault(); 316 | } 317 | 318 | if ( e.code ) { 319 | this._keysRaw[Keys.codesToKeyCodes[e.code]] = 1; 320 | } 321 | else { 322 | this._keysRaw[e.keyCode] = 1; 323 | } 324 | } 325 | 326 | /** 327 | * handle window keyup events 328 | * @param {*} e 329 | */ 330 | _keyUp( e ) { 331 | if ( e.code ) { 332 | this._keysRaw[Keys.codesToKeyCodes[e.code]] = 0; 333 | } 334 | else { 335 | this._keysRaw[e.keyCode] = 0; 336 | } 337 | } 338 | 339 | /** 340 | * Handle pointerenter event 341 | */ 342 | _pointerEnter() { 343 | this.mouse.isOffScreen = false; 344 | this._currentMouseLeft = false; 345 | this._currentMouseRight = false; 346 | } 347 | 348 | /** 349 | * Handle pointermove event 350 | * @param {*} e 351 | */ 352 | _pointerMove( e ) { 353 | const canvasRect = this.canvas.getBoundingClientRect(); 354 | this.mouse.position = { 355 | x: Math.floor( ( e.clientX - canvasRect.left ) / this.canvasScale ), 356 | y: this.screenHeight - Math.floor( ( e.clientY - canvasRect.top ) / this.canvasScale ) - 1, 357 | }; 358 | } 359 | 360 | /** 361 | * Handle pointerdown event 362 | * @param {*} e 363 | */ 364 | _pointerDown( e ) { 365 | if ( e.button === 0 ) { 366 | // left button 367 | this._currentMouseLeft = true; 368 | this._forceMouseLeftDown = true; 369 | } 370 | else if ( e.button === 2 ) { 371 | // right button 372 | this._currentMouseRight = true; 373 | this._forceMouseRightDown = true; 374 | } 375 | } 376 | 377 | /** 378 | * handle pointerup event 379 | * @param {*} e 380 | */ 381 | _pointerUp( e ) { 382 | if ( e.button === 0 ) { 383 | // left button 384 | this._currentMouseLeft = false; 385 | } 386 | else if ( e.button === 2 ) { 387 | // right button 388 | this._currentMouseRight = false; 389 | } 390 | } 391 | 392 | /** 393 | * handle pointerleave event 394 | */ 395 | _pointerLeave() { 396 | this.mouse.isOffScreen = true; 397 | } 398 | 399 | /** 400 | * clear out all of the input 401 | */ 402 | clearInput() { 403 | for ( let i = 0; i < 256; i += 1 ) { 404 | this._keysRaw[i] = 0; 405 | this._lastKeys[i] = 0; 406 | this._currentKeys[i] = 0; 407 | } 408 | } 409 | 410 | /** 411 | * Update the input, should be done first thing in the game loop. 412 | * Called automatically by the Engine. 413 | */ 414 | pollInput() { 415 | for ( let i = 0; i < 20; i += 1 ) { 416 | this._lastJoyButtons[i] = this._currentJoyButtons[i]; 417 | this._currentJoyButtons[i] = 0; 418 | } 419 | 420 | for ( let i = 0; i < 9; i += 1 ) { 421 | this._lastJoyAxes[i] = this._currentJoyAxes[i]; 422 | this._currentJoyAxes[i] = 0; 423 | } 424 | 425 | try { 426 | const gamePads = navigator.getGamepads(); 427 | for ( let i = 0; i < gamePads.length; i += 1 ) { 428 | const gamePad = gamePads[i]; 429 | if ( gamePad ) { 430 | for ( let b = 0; b < gamePad.buttons.length; b += 1 ) { 431 | if ( b < 20 && gamePad.buttons[b].pressed ) { 432 | this._currentJoyButtons[b] = 1; 433 | } 434 | } 435 | for ( let a = 0; a < gamePad.axes.length; a += 1 ) { 436 | if ( a < 8 ) { 437 | this._currentJoyAxes[a + 1] = gamePad.axes[a]; 438 | } 439 | } 440 | } 441 | } 442 | } 443 | // eslint-disable-next-line no-empty 444 | catch ( err ) {} 445 | 446 | for ( let i = 0; i < 256; i += 1 ) { 447 | this._lastKeys[i] = this._currentKeys[i]; 448 | this._currentKeys[i] = this._keysRaw[i]; 449 | } 450 | // index 0 is always off 451 | this._lastKeys[0] = 0; 452 | this._currentKeys[0] = 0; 453 | 454 | this._updateButtons(); 455 | 456 | this.mouse.left.pressed = this._forceMouseLeftDown ? true : this._currentMouseLeft; 457 | this.mouse.left.down = this._forceMouseLeftDown ? true : this._currentMouseLeft && !this._lastMouseLeft; 458 | this.mouse.left.up = !this._currentMouseLeft && this._lastMouseLeft; 459 | 460 | this.mouse.right.pressed = this._forceMouseRightDown ? true : this._currentMouseRight; 461 | this.mouse.right.down = this._forceMouseRightDown ? true : this._currentMouseRight && !this._lastMouseRight; 462 | this.mouse.right.up = !this._currentMouseRight && this._lastMouseRight; 463 | 464 | this._forceMouseLeftDown = false; 465 | this._forceMouseRightDown = false; 466 | 467 | this._lastMouseLeft = this._currentMouseLeft; 468 | this._lastMouseRight = this._currentMouseRight; 469 | } 470 | 471 | /** 472 | * return true if the key is currently held down 473 | * @param {number} keyCode 474 | */ 475 | getKeyPressed( keyCode ) { 476 | if ( keyCode < 0 || keyCode >= 256 ) { 477 | return false; 478 | } 479 | 480 | return this._currentKeys[keyCode] > 0; 481 | } 482 | 483 | /** 484 | * return true if the key was pressed down this frame 485 | * @param {number} keyCode 486 | */ 487 | getKeyDown( keyCode ) { 488 | if ( keyCode < 0 || keyCode >= 256 ) { 489 | return false; 490 | } 491 | 492 | const current = this._currentKeys[keyCode] > 0; 493 | const last = this._lastKeys[keyCode] > 0; 494 | return current && !last; 495 | } 496 | 497 | /** 498 | * return true if the key was released this frame 499 | * @param {number} keyCode 500 | */ 501 | getKeyUp( keyCode ) { 502 | if ( keyCode < 0 || keyCode >= 256 ) { 503 | return false; 504 | } 505 | 506 | const current = this._currentKeys[keyCode] > 0; 507 | const last = this._lastKeys[keyCode] > 0; 508 | return !current && last; 509 | } 510 | 511 | /** 512 | * return true if the joypad button is currently held down 513 | * This is the joypad from the Gamepad API, and generally should only 514 | * be used when detecting inputs to remap controls. For normal 515 | * gameplay use Input.getButtonPressed 516 | * @param {number} buttonIndex 517 | */ 518 | getJoyButtonPressed( buttonIndex ) { 519 | if ( buttonIndex < 0 || buttonIndex >= 20 ) { 520 | return false; 521 | } 522 | 523 | return this._currentJoyButtons[buttonIndex] > 0; 524 | } 525 | 526 | /** 527 | * return true if the joypad button was pressed down this frame. 528 | * This is the joypad from the Gamepad API, and generally should only 529 | * be used when detecting inputs to remap controls. For normal 530 | * gameplay use Input.getButtonDown 531 | * @param {number} buttonIndex 532 | */ 533 | getJoyButtonDown( buttonIndex ) { 534 | if ( buttonIndex < 0 || buttonIndex >= 20 ) { 535 | return false; 536 | } 537 | 538 | const current = this._currentJoyButtons[buttonIndex] > 0; 539 | const last = this._lastJoyButtons[buttonIndex] > 0; 540 | return current && !last; 541 | } 542 | 543 | /** 544 | * return true if the joypad button was released this frame. 545 | * This is the joypad from the Gamepad API, and generally should only 546 | * be used when detecting inputs to remap controls. For normal 547 | * gameplay use Input.getButtonUp 548 | * @param {number} buttonIndex 549 | */ 550 | getJoyButtonUp( buttonIndex ) { 551 | if ( buttonIndex < 0 || buttonIndex >= 20 ) { 552 | return false; 553 | } 554 | 555 | const current = this._currentJoyButtons[buttonIndex] > 0; 556 | const last = this._lastJoyButtons[buttonIndex] > 0; 557 | return !current && last; 558 | } 559 | 560 | /** 561 | * Helper function for joypad axes 562 | * @param {*} axisIndex 563 | * @param {*} axisArray 564 | */ 565 | _axisFromArrayIsPressed( axisIndex, axisArray ) { 566 | if ( axisIndex === 0 || axisIndex > 8 || axisIndex < -8 ) { 567 | return 0; 568 | } 569 | 570 | const isNegative = axisIndex < 0; 571 | const value = axisArray[Math.abs( axisIndex )]; 572 | 573 | return isNegative ? value <= -this._axisThreshold : value >= this._axisThreshold; 574 | } 575 | 576 | /** 577 | * return true if the joypad axis is currently held down 578 | * This is the joypad from the Gamepad API, and generally should only 579 | * be used when detecting inputs to remap controls. For normal 580 | * gameplay use Input.getButtonPressed 581 | * @param {number} axisIndex 582 | */ 583 | getJoyAxisPressed( axisIndex ) { 584 | return this._axisFromArrayIsPressed( axisIndex, this._currentJoyAxes ); 585 | } 586 | 587 | /** 588 | * return true if the joypad axis was pressed down this frame 589 | * This is the joypad from the Gamepad API, and generally should only 590 | * be used when detecting inputs to remap controls. For normal 591 | * gameplay use Input.getButtonDown 592 | * @param {number} axisIndex 593 | */ 594 | getJoyAxisDown( axisIndex ) { 595 | const current = this.getJoyAxisPressed( axisIndex ); 596 | const last = this._axisFromArrayIsPressed( axisIndex, this._lastJoyAxes ); 597 | 598 | return current && !last; 599 | } 600 | 601 | /** 602 | * return true if the joypad axis was released this frame 603 | * This is the joypad from the Gamepad API, and generally should only 604 | * be used when detecting inputs to remap controls. For normal 605 | * gameplay use Input.getButtonUp 606 | * @param {number} axisIndex 607 | */ 608 | getJoyAxisUp( axisIndex ) { 609 | const current = this.getJoyAxisPressed( axisIndex ); 610 | const last = this._axisFromArrayIsPressed( axisIndex, this._lastJoyAxes ); 611 | 612 | return !current && last; 613 | } 614 | 615 | /** 616 | * return true if the standard game button is currently held down 617 | * @param {number} buttonCode 618 | */ 619 | getButtonPressed( buttonCode ) { 620 | const key = this._buttonsToKeys[buttonCode]; 621 | const altKey = this._buttonsToAltKeys[buttonCode]; 622 | const joyButton = this._buttonsToJoyButtons[buttonCode]; 623 | const altJoyButton = this._buttonsToAltJoyButtons[buttonCode]; 624 | const joyAxis = this._buttonsToJoyAxes[buttonCode]; 625 | 626 | const keyPressed = this._currentKeys[key]; 627 | const altKeyPressed = this._currentKeys[altKey]; 628 | const joyButtonPressed = this._currentJoyButtons[joyButton]; 629 | const altJoyButtonPressed = this._currentJoyButtons[altJoyButton]; 630 | const joyAxisPressed = this._axisFromArrayIsPressed( joyAxis, this._currentJoyAxes ); 631 | return keyPressed || altKeyPressed || joyButtonPressed || altJoyButtonPressed || joyAxisPressed; 632 | } 633 | 634 | /** 635 | * return true if the standard game button was pressed down this frame 636 | * @param {number} buttonCode 637 | */ 638 | getButtonDown( buttonCode ) { 639 | const currentPressed = this.getButtonPressed( buttonCode ); 640 | 641 | if ( currentPressed ) { 642 | const key = this._buttonsToKeys[buttonCode]; 643 | const altKey = this._buttonsToAltKeys[buttonCode]; 644 | const joyButton = this._buttonsToJoyButtons[buttonCode]; 645 | const altJoyButton = this._buttonsToAltJoyButtons[buttonCode]; 646 | const joyAxis = this._buttonsToJoyAxes[buttonCode]; 647 | 648 | const lastKeyPressed = this._lastKeys[key]; 649 | const lastAltKeyPressed = this._lastKeys[altKey]; 650 | const lastJoyButtonPressed = this._lastJoyButtons[joyButton]; 651 | const lastAltJoyButtonPressed = this._lastJoyButtons[altJoyButton]; 652 | const lastJoyAxisPressed = this._axisFromArrayIsPressed( joyAxis, this._lastJoyAxes ); 653 | 654 | if ( 655 | !lastKeyPressed 656 | && !lastAltKeyPressed 657 | && !lastJoyButtonPressed 658 | && !lastAltJoyButtonPressed 659 | && !lastJoyAxisPressed 660 | ) { 661 | return true; 662 | } 663 | } 664 | 665 | return false; 666 | } 667 | 668 | /** 669 | * return true if the standard game button was released this frame 670 | * @param {number} buttonCode 671 | */ 672 | getButtonUp( buttonCode ) { 673 | const currentPressed = this.getButtonPressed( buttonCode ); 674 | 675 | if ( !currentPressed ) { 676 | const key = this._buttonsToKeys[buttonCode]; 677 | const altKey = this._buttonsToAltKeys[buttonCode]; 678 | const joyButton = this._buttonsToJoyButtons[buttonCode]; 679 | const altJoyButton = this._buttonsToAltJoyButtons[buttonCode]; 680 | const joyAxis = this._buttonsToJoyAxes[buttonCode]; 681 | 682 | const lastKeyPressed = this._lastKeys[key]; 683 | const lastAltKeyPressed = this._lastKeys[altKey]; 684 | const lastJoyButtonPressed = this._lastJoyButtons[joyButton]; 685 | const lastAltJoyButtonPressed = this._lastJoyButtons[altJoyButton]; 686 | const lastJoyAxisPressed = this._axisFromArrayIsPressed( joyAxis, this._lastJoyAxes ); 687 | 688 | if ( 689 | lastKeyPressed 690 | || lastAltKeyPressed 691 | || lastJoyButtonPressed 692 | || lastAltJoyButtonPressed 693 | || lastJoyAxisPressed 694 | ) { 695 | return true; 696 | } 697 | } 698 | 699 | return false; 700 | } 701 | 702 | /** 703 | * update standard game button states. 704 | */ 705 | _updateButtons() { 706 | this._updateButton( 'left', Input.GAME_LEFT ); 707 | this._updateButton( 'right', Input.GAME_RIGHT ); 708 | this._updateButton( 'up', Input.GAME_UP ); 709 | this._updateButton( 'down', Input.GAME_DOWN ); 710 | 711 | this._updateButton( 'action1', Input.GAME_ACTION_ONE ); 712 | this._updateButton( 'action2', Input.GAME_ACTION_TWO ); 713 | this._updateButton( 'action3', Input.GAME_ACTION_THREE ); 714 | this._updateButton( 'action4', Input.GAME_ACTION_FOUR ); 715 | this._updateButton( 'leftTrigger', Input.GAME_LEFT_TRIGGER ); 716 | this._updateButton( 'rightTrigger', Input.GAME_RIGHT_TRIGGER ); 717 | 718 | this._updateButton( 'pause', Input.GAME_PAUSE ); 719 | 720 | this._updateButton( 'menuLeft', Input.MENU_LEFT ); 721 | this._updateButton( 'menuRight', Input.MENU_RIGHT ); 722 | this._updateButton( 'menuUp', Input.MENU_UP ); 723 | this._updateButton( 'menuDown', Input.MENU_DOWN ); 724 | 725 | this._updateButton( 'menuConfirm', Input.MENU_CONFIRM ); 726 | this._updateButton( 'menuBack', Input.MENU_BACK ); 727 | } 728 | 729 | /** 730 | * Update the state for a standard game button. 731 | * @param {*} name 732 | * @param {*} index 733 | */ 734 | _updateButton( name, index ) { 735 | const pressed = this.getButtonPressed( index ); 736 | const down = this.getButtonDown( index ); 737 | const up = this.getButtonUp( index ); 738 | 739 | this[name] = { pressed, down, up }; 740 | } 741 | } 742 | 743 | Input.GAME_LEFT = 0; 744 | Input.GAME_RIGHT = 1; 745 | Input.GAME_UP = 2; 746 | Input.GAME_DOWN = 3; 747 | Input.GAME_ACTION_ONE = 4; 748 | Input.GAME_ACTION_TWO = 5; 749 | Input.GAME_ACTION_THREE = 6; 750 | Input.GAME_ACTION_FOUR = 7; 751 | Input.GAME_PAUSE = 8; 752 | Input.GAME_LEFT_TRIGGER = 9; 753 | Input.GAME_RIGHT_TRIGGER = 10; 754 | 755 | Input.MENU_LEFT = 11; 756 | Input.MENU_RIGHT = 12; 757 | Input.MENU_UP = 13; 758 | Input.MENU_DOWN = 14; 759 | Input.MENU_CONFIRM = 15; 760 | Input.MENU_BACK = 16; 761 | 762 | export default Input; 763 | -------------------------------------------------------------------------------- /src/FontData/standard.font.json: -------------------------------------------------------------------------------- 1 | {"name":"standard","tileSize":16,"width":16,"height":16,"originX":2,"originY":4,"standardWidth":5,"letterSpacing":1,"charData":{"33":{"name":"!","width":3},"34":{"name":"double quote","width":4},"37":{"name":"percent","width":7},"38":{"name":"and","width":7},"39":{"name":"single quote","width":1},"40":{"name":"(","width":3},"41":{"name":")","width":3},"44":{"name":",","width":2},"46":{"name":".","width":1},"47":{"name":"f slash","width":7},"49":{"name":"1","width":3},"58":{"name":":","width":1},"59":{"name":";","width":2},"60":{"name":"<","width":4},"62":{"name":">","width":4},"64":{"name":"@","width":7},"91":{"name":"[","width":3},"92":{"name":"backslash","width":7},"93":{"name":"]","width":3},"96":{"name":"grave","width":2},"105":{"name":"i","width":1},"107":{"name":"k","width":4},"108":{"name":"l","width":1},"123":{"name":"{","width":3},"124":{"name":"|","width":1},"125":{"name":"}","width":3},"161":{"name":"upside down !","width":3},"164":{"name":"currency","width":6},"165":{"name":"yen","width":7},"166":{"name":"broken bar","width":1},"167":{"name":"section","width":6},"169":{"name":"copyright","width":7},"170":{"name":"f ordinal","width":4},"171":{"name":"<<","width":6},"172":{"name":"not","width":6},"174":{"name":"(r)","width":8},"176":{"name":"degree","width":4},"178":{"name":"^2","width":3},"179":{"name":"^3","width":3},"180":{"name":"acute","width":2},"182":{"name":"pilcrow","width":3},"183":{"name":"middle dot","width":1},"185":{"name":"^1","width":4},"186":{"name":"m ordinal","width":4},"187":{"name":">>","width":6},"188":{"name":"1/4","width":7},"189":{"name":"1/2","width":7},"190":{"name":"3/4","width":7},"236":{"name":"i","width":3},"237":{"name":"i","width":3},"238":{"name":"i","width":3},"239":{"name":"i","width":3},"8364":{"name":"euro","remap":1,"width":6}},"data":[307,0,5,2,10,0,2,2,3,1,2,2,8,0,2,2,1,1,3,2,1,1,1,2,8,0,1,2,4,1,3,2,8,0,2,2,1,1,4,2,9,0,1,2,4,1,3,2,8,0,2,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,8058,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,12,0,2,2,1,1,2,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,5,2,219,0,6,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,6,2,107,0,5,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,5,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,5,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,5,2,140,0,3,2,11,0,3,2,1,1,2,2,10,0,1,2,4,1,2,2,9,0,3,2,1,1,1,2,1,1,1,2,10,0,2,2,1,1,1,2,1,1,1,2,9,0,2,2,3,1,2,2,9,0,1,2,1,1,1,2,1,1,2,2,10,0,1,2,1,1,1,2,1,1,3,2,9,0,2,2,4,1,1,2,10,0,2,2,1,1,3,2,11,0,3,2,107,0,3,2,2,0,3,2,8,0,1,2,1,1,4,2,1,1,2,2,7,0,2,2,1,1,2,2,1,1,1,2,1,1,1,2,8,0,2,2,1,1,2,2,1,1,2,2,8,0,3,2,1,1,3,2,8,0,2,2,1,1,2,2,1,1,2,2,8,0,1,2,1,1,1,2,1,1,2,2,1,1,2,2,7,0,2,2,1,1,4,2,1,1,1,2,8,0,3,2,2,0,3,2,120,0,8,2,7,0,2,2,4,1,1,2,1,1,1,2,7,0,1,2,1,1,4,2,1,1,2,2,7,0,1,2,1,1,3,2,1,1,1,2,1,1,1,2,7,0,1,2,1,1,2,2,2,1,3,2,7,0,2,2,2,1,2,2,1,1,1,2,9,0,2,2,1,1,2,2,1,1,1,2,10,0,2,2,2,1,2,2,11,0,4,2,217,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,111,0,3,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,123,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,189,0,6,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,2,2,2,1,2,2,10,0,2,2,2,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,6,2,108,0,3,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,3,2,139,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,236,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,185,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,221,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,120,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,5,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,12,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,2,2,1,1,1,2,12,0,1,2,2,1,1,2,12,0,2,2,1,1,1,2,13,0,3,2,124,0,7,2,9,0,1,2,5,1,1,2,9,0,2,2,1,1,4,2,10,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,9,0,5,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,123,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,5,2,1,1,1,2,10,0,1,2,3,1,2,2,9,0,5,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,126,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,9,0,5,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,6,2,10,0,1,2,4,1,2,2,9,0,5,2,1,1,1,2,9,0,5,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,122,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,124,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,9,0,5,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,7,2,122,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,123,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,5,2,1,1,1,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,138,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,157,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,175,0,3,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,138,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,169,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,127,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,2,2,12,0,1,2,2,1,2,2,9,0,5,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,124,0,7,2,8,0,2,2,5,1,1,2,7,0,2,2,1,1,6,2,7,0,1,2,1,1,1,2,2,1,1,2,1,1,2,2,7,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,7,0,1,2,1,1,1,2,3,1,1,2,1,1,1,2,7,0,2,2,1,1,4,2,1,1,1,2,8,0,2,2,4,1,2,2,9,0,6,2,120,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,12,0,3,2,123,0,6,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,123,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,3,2,9,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,6,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,122,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,121,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,122,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,3,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,7,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,121,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,121,0,3,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,2,2,1,1,2,2,9,0,1,2,1,1,1,2,1,1,2,2,10,0,1,2,2,1,2,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,1,2,1,1,2,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,125,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,2,1,1,2,2,1,1,2,9,0,7,2,121,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,3,2,122,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,4,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,123,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,2,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,122,0,6,2,10,0,1,2,4,1,2,2,9,0,5,2,1,1,1,2,10,0,4,2,1,1,1,2,9,0,2,2,3,1,2,2,9,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,2,2,4,1,1,2,10,0,6,2,123,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,122,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,123,0,3,2,12,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,7,2,9,0,1,2,2,1,1,2,2,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,123,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,121,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,2,2,9,0,5,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,7,2,121,0,4,2,12,0,1,2,2,1,1,2,12,0,1,2,1,1,2,2,12,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,1,2,2,1,1,2,12,0,4,2,130,0,3,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,125,0,4,2,12,0,1,2,2,1,1,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,2,2,1,1,1,2,12,0,1,2,2,1,1,2,12,0,4,2,188,0,3,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,12,0,3,2,107,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,314,0,3,2,12,0,2,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,1,2,1,1,1,2,13,0,3,2,110,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,154,0,6,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,126,0,6,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,1,2,13,0,1,2,1,1,5,2,9,0,2,2,4,1,1,2,10,0,6,2,154,0,6,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,4,1,1,2,10,0,4,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,122,0,5,2,10,0,2,2,3,1,1,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,154,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,3,2,11,0,1,2,3,1,1,2,11,0,1,2,1,1,5,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,91,0,5,2,11,0,1,2,3,1,2,2,10,0,4,2,1,1,1,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,154,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,125,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,94,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,121,0,6,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,1,2,1,1,2,2,10,0,1,2,2,1,2,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,125,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,125,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,154,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,155,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,4,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,126,0,4,2,12,0,1,2,2,1,1,2,9,0,4,2,1,1,2,2,8,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,154,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,1,2,2,1,4,2,9,0,1,2,1,1,1,2,3,1,1,2,9,0,7,2,153,0,6,2,10,0,1,2,4,1,2,2,9,0,5,2,1,1,1,2,9,0,2,2,3,1,2,2,9,0,1,2,1,1,5,2,9,0,2,2,4,1,1,2,10,0,6,2,155,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,3,2,124,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,155,0,3,2,12,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,154,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,153,0,3,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,3,2,122,0,5,2,11,0,1,2,3,1,2,2,10,0,4,2,1,1,1,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,153,0,7,2,9,0,1,2,5,1,1,2,9,0,2,2,1,1,4,2,10,0,2,2,1,1,2,2,10,0,4,2,1,1,2,2,9,0,1,2,5,1,1,2,9,0,7,2,139,0,3,2,12,0,2,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,1,2,1,1,1,2,12,0,2,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,91,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,93,0,3,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,2,2,12,0,2,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,1,2,1,1,1,2,12,0,2,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,3,2,157,0,6,2,10,0,1,2,1,1,1,2,2,1,2,2,9,0,2,2,2,1,1,2,1,1,1,2,10,0,6,2,8857,0,5,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,126,0,3,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,1,2,1,1,3,2,9,0,1,2,1,1,1,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,3,2,139,0,7,2,9,0,1,2,5,1,1,2,9,0,2,2,1,1,4,2,9,0,2,2,1,1,2,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,5,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,3,2,3,0,3,2,7,0,1,2,1,1,5,2,1,1,1,2,7,0,2,2,5,1,2,2,8,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,8,0,2,2,5,1,2,2,7,0,1,2,1,1,5,2,1,1,1,2,7,0,3,2,3,0,3,2,106,0,3,2,12,0,2,2,1,1,2,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,8,0,2,2,1,1,3,2,1,1,2,2,7,0,1,2,1,1,2,2,1,0,2,2,1,1,1,2,7,0,3,2,3,0,3,2,103,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,3,2,110,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,5,2,1,1,1,2,9,0,2,2,4,1,2,2,8,0,1,2,1,1,4,2,1,1,1,2,8,0,1,2,1,1,4,2,1,1,1,2,8,0,2,2,4,1,2,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,218,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,92,0,5,2,10,0,2,2,3,1,2,2,8,0,2,2,1,1,3,2,1,1,2,2,7,0,1,2,1,1,1,2,3,1,1,2,1,1,1,2,7,0,1,2,1,1,1,2,1,1,3,2,1,1,1,2,7,0,1,2,1,1,1,2,3,1,1,2,1,1,1,2,7,0,2,2,1,1,3,2,1,1,2,2,8,0,2,2,3,1,2,2,10,0,5,2,169,0,6,2,10,0,1,2,4,1,1,2,10,0,6,2,10,0,2,2,1,1,1,2,1,1,1,2,10,0,1,2,1,1,1,2,2,1,1,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,2,2,2,1,2,2,11,0,4,2,109,0,6,2,9,0,2,2,1,1,2,2,1,1,1,2,8,0,2,2,1,1,2,2,1,1,2,2,8,0,1,2,1,1,2,2,1,1,2,2,9,0,2,2,1,1,2,2,1,1,2,2,9,0,2,2,1,1,2,2,1,1,1,2,10,0,6,2,173,0,3,2,13,0,1,2,1,1,1,2,8,0,6,2,1,1,1,2,8,0,1,2,6,1,1,2,8,0,8,2,410,0,6,2,9,0,2,2,4,1,2,2,7,0,2,2,2,1,2,2,2,1,2,2,6,0,1,2,1,1,1,2,1,1,2,2,1,1,1,2,1,1,1,2,6,0,1,2,1,1,1,2,3,1,2,2,1,1,1,2,6,0,1,2,1,1,1,2,1,1,2,2,1,1,1,2,1,1,1,2,6,0,1,2,1,1,1,2,4,1,1,2,1,1,1,2,6,0,2,2,1,1,4,2,1,1,2,2,7,0,2,2,4,1,2,2,9,0,6,2,200,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,186,0,4,2,11,0,2,2,2,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,2,2,2,1,2,2,11,0,4,2,107,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,11,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,3,2,187,0,5,2,11,0,1,2,3,1,1,2,11,0,1,2,2,1,2,2,11,0,3,2,1,1,1,2,11,0,1,2,2,1,2,2,11,0,4,2,172,0,5,2,11,0,1,2,3,1,1,2,11,0,2,2,2,1,1,2,11,0,3,2,1,1,1,2,11,0,1,2,3,1,1,2,11,0,5,2,187,0,3,2,13,0,1,2,1,1,2,2,12,0,1,2,2,1,1,2,12,0,2,2,1,1,1,2,13,0,3,2,76,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,5,2,9,0,1,2,3,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,3,2,1,0,3,2,153,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,1,2,1,1,1,2,1,1,1,2,10,0,2,2,1,1,1,2,1,1,1,2,10,0,1,2,2,1,1,2,1,1,1,2,10,0,1,2,2,1,1,2,1,1,1,2,10,0,1,2,2,1,1,2,1,1,1,2,10,0,2,2,3,1,1,2,11,0,5,2,139,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,142,0,5,2,11,0,1,2,3,1,1,2,11,0,3,2,1,1,1,2,12,0,1,2,1,1,2,2,12,0,3,2,283,0,5,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,2,2,11,0,2,2,1,1,1,2,12,0,1,2,2,1,1,2,12,0,4,2,156,0,6,2,10,0,1,2,4,1,1,2,10,0,6,2,10,0,2,2,2,1,2,2,10,0,1,2,1,1,2,0,1,1,1,2,10,0,1,2,1,1,2,0,1,1,1,2,10,0,2,2,2,1,2,2,11,0,4,2,107,0,6,2,10,0,1,2,1,1,2,2,1,1,2,2,9,0,2,2,1,1,2,2,1,1,2,2,9,0,2,2,1,1,2,2,1,1,1,2,8,0,2,2,1,1,2,2,1,1,2,2,8,0,1,2,1,1,2,2,1,1,2,2,9,0,6,2,144,0,3,2,11,0,3,2,1,1,1,2,8,0,4,2,3,1,1,2,8,0,1,2,1,1,2,2,1,1,1,2,1,1,1,2,8,0,2,2,1,1,5,2,8,0,3,2,1,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,112,0,6,2,10,0,1,2,4,1,1,2,7,0,6,2,1,1,2,2,7,0,1,2,1,1,2,2,3,1,1,2,8,0,2,2,1,1,5,2,8,0,3,2,1,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,114,0,3,2,11,0,3,2,1,1,1,2,8,0,4,2,3,1,1,2,8,0,1,2,1,1,2,2,1,1,1,2,1,1,1,2,8,0,2,2,1,1,5,2,7,0,4,2,1,1,2,2,9,0,1,2,3,1,1,2,1,1,1,2,9,0,2,2,2,1,3,2,9,0,1,2,3,1,1,2,11,0,5,2,108,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,5,2,9,0,2,2,2,1,1,2,12,0,2,2,1,1,1,2,13,0,3,2,13,0,1,2,1,1,1,2,13,0,3,2,123,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,12,0,4,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,60,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,4,2,12,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,58,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,75,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,5,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,73,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,11,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,90,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,12,0,3,2,12,0,2,2,1,1,2,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,59,0,3,2,1,0,6,2,6,0,1,2,1,1,1,2,1,0,1,2,4,1,1,2,6,0,1,2,1,1,3,2,1,1,4,2,6,0,1,2,5,1,3,2,7,0,2,2,1,1,2,2,3,1,1,2,8,0,2,2,1,1,1,2,1,1,3,2,9,0,2,2,2,1,4,2,9,0,2,2,4,1,1,2,10,0,6,2,71,0,5,2,11,0,1,2,3,1,1,2,11,0,3,2,1,1,1,2,11,0,2,2,1,1,2,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,3,2,9,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,122,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,11,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,60,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,58,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,75,0,7,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,4,2,10,0,1,2,4,1,1,2,10,0,1,2,1,1,4,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,90,0,7,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,11,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,60,0,7,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,58,0,7,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,75,0,7,2,9,0,1,2,5,1,1,2,9,0,3,2,1,1,3,2,11,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,11,0,3,2,1,1,3,2,9,0,1,2,5,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,90,0,6,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,8,0,2,2,1,1,3,2,1,1,1,2,8,0,1,2,3,1,2,2,1,1,1,2,8,0,2,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,122,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,74,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,61,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,59,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,76,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,74,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,90,0,3,2,1,0,3,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,3,2,1,0,3,2,136,0,3,2,13,0,1,2,1,1,5,2,9,0,2,2,4,1,2,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,2,2,4,1,2,2,9,0,5,2,1,1,1,2,13,0,3,2,105,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,11,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,61,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,59,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,76,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,92,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,2,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,58,0,5,2,11,0,1,2,3,1,1,2,11,0,2,2,1,1,3,2,11,0,1,2,3,1,2,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,1,1,2,2,1,1,1,2,10,0,1,2,3,1,2,2,9,0,2,2,1,1,3,2,10,0,1,2,3,1,1,2,11,0,5,2,91,0,6,2,10,0,1,2,1,1,1,2,2,1,2,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,2,2,1,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,6,2,91,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,93,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,91,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,108,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,106,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,123,0,6,2,9,0,2,2,2,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,92,0,8,2,7,0,2,2,1,1,1,2,4,1,1,2,7,0,1,2,1,1,1,2,2,1,5,2,6,0,1,2,1,1,2,2,5,1,1,2,6,0,1,2,1,1,2,2,1,1,3,2,1,1,1,2,6,0,2,2,6,1,2,2,7,0,8,2,104,0,5,2,11,0,1,2,3,1,1,2,11,0,3,2,1,1,1,2,11,0,2,2,1,1,3,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,5,2,9,0,1,2,1,1,1,2,13,0,1,2,1,1,5,2,9,0,2,2,4,1,1,2,10,0,6,2,154,0,5,2,10,0,2,2,3,1,1,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,93,0,5,2,10,0,2,2,3,1,1,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,91,0,5,2,10,0,2,2,3,1,1,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,108,0,5,2,10,0,2,2,3,1,1,2,10,0,1,2,1,1,5,2,9,0,1,2,5,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,123,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,4,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,94,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,4,2,12,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,92,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,109,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,1,2,12,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,124,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,4,1,1,2,10,0,3,2,1,1,2,2,9,0,2,2,2,1,2,2,10,0,1,2,2,1,1,2,1,1,1,2,10,0,6,2,106,0,3,2,1,0,3,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,106,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,12,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,93,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,91,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,108,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,2,2,10,0,2,2,1,1,1,2,1,1,1,2,11,0,5,2,106,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,2,2,3,1,2,2,10,0,5,2,11,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,124,0,3,2,13,0,1,2,1,1,1,2,11,0,7,2,9,0,1,2,5,1,1,2,9,0,7,2,11,0,1,2,1,1,1,2,13,0,3,2,138,0,3,2,13,0,1,2,1,1,5,2,9,0,2,2,4,1,2,2,9,0,1,2,2,1,2,2,1,1,1,2,9,0,1,2,1,1,1,2,1,1,1,2,1,1,1,2,9,0,1,2,1,1,2,2,2,1,1,2,9,0,2,2,4,1,2,2,9,0,5,2,1,1,1,2,13,0,3,2,137,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,11,0,2,2,1,1,1,2,11,0,2,2,1,1,2,2,11,0,1,2,1,1,2,2,12,0,3,2,93,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,91,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,2,2,1,1,2,2,12,0,3,2,108,0,5,2,10,0,2,2,3,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,91,0,5,2,11,0,1,2,3,1,2,2,10,0,4,2,1,1,1,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,2,2,12,0,2,2,1,1,2,2,12,0,2,2,1,1,1,2,13,0,3,2,58,0,3,2,13,0,1,2,1,1,1,2,13,0,1,2,1,1,4,2,10,0,1,2,4,1,2,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,4,1,2,2,9,0,1,2,1,1,4,2,10,0,1,2,1,1,1,2,13,0,3,2,94,0,5,2,11,0,1,2,3,1,2,2,10,0,4,2,1,1,1,2,9,0,2,2,4,1,1,2,9,0,1,2,1,1,3,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,1,2,1,1,1,2,1,0,1,2,1,1,1,2,9,0,7,2,10,0,1,2,1,1,1,2,1,1,1,2,11,0,5,2,73,0]} --------------------------------------------------------------------------------