├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .karma.conf.js ├── .travis.yml ├── README.md ├── assets ├── Graphics │ ├── Bosses │ │ ├── Airman.psd │ │ ├── Crashman.psd │ │ ├── Flashman.psd │ │ ├── Heatman.psd │ │ └── Metalman.psd │ ├── Cog.ai │ ├── Deco │ │ ├── Headlight Lensflare prepared.psd │ │ └── Lens flare.psd │ ├── Defullscreen.ai │ ├── Enemies │ │ ├── ChangkeyMaker.psd │ │ ├── Sillycanon.psd │ │ ├── SniperArmor.psd │ │ ├── SniperJoe.psd │ │ └── Telly.psd │ ├── Explosions.psd │ ├── Font.psd │ ├── Fullscreen.ai │ ├── Hud.psd │ ├── Intro.psd │ ├── Levels │ │ ├── Airman.psd │ │ ├── Bubbleman.psd │ │ ├── Crashman.psd │ │ ├── DrWily1.psd │ │ ├── DrWily2.psd │ │ ├── DrWily3.psd │ │ ├── DrWily4.psd │ │ ├── DrWilyAlien.psd │ │ ├── DrWilyTeleport.psd │ │ ├── Flashman.psd │ │ ├── Heatman.psd │ │ ├── Metalman.psd │ │ ├── Quickman.psd │ │ └── Woodman.psd │ ├── Megaman │ │ ├── Megaman.act │ │ ├── Megaman.psd │ │ └── Metalman.act │ ├── Meters.psd │ ├── Nes Controller.svg │ ├── Obstacles.psd │ ├── Play.ai │ ├── PowerUp.psd │ ├── Projectiles.psd │ ├── StageSelect.psd │ └── Tool │ │ └── LevelEditor.psd └── jnes save states │ ├── Bubbleman-pre-boss.jst │ ├── Bubbleman-start.jst │ ├── Heatman-lower-high-jump.jst │ ├── Heatman-lower-start.jst │ ├── Heatman-vertical-1.jst │ ├── SniperArmor.jst │ └── Woodman before boss.jst ├── package.json ├── public ├── favicon.ico ├── index.html └── resource │ ├── Decorations.xml │ ├── Intro.xml │ ├── Megaman2.xml │ ├── Powerup.xml │ ├── Projectiles.xml │ ├── StageSelect.xml │ ├── characters │ ├── Airman.xml │ ├── ChangkeyMaker.xml │ ├── Crashman.xml │ ├── Flashman.xml │ ├── Heatman.xml │ ├── Megaman.xml │ ├── Metalman.xml │ ├── Shotman.xml │ ├── SniperArmor.xml │ ├── SniperJoe.xml │ ├── Telly.xml │ ├── airman.png │ ├── changkey.png │ ├── crashman.png │ ├── enemies │ │ ├── shotman.png │ │ ├── sniperarmor.png │ │ └── sniperjoe.png │ ├── flashman.png │ ├── heatman.png │ ├── megaman │ │ ├── airshot.ogg │ │ ├── damage.ogg │ │ ├── death.ogg │ │ ├── jump-land.ogg │ │ ├── megaman.png │ │ ├── metalblade.ogg │ │ ├── plasma.ogg │ │ ├── teleport-in.ogg │ │ └── teleport-out.ogg │ ├── metalman.png │ └── telly.png │ ├── cog.svg │ ├── crash-bomb-attach.ogg │ ├── deco │ └── headlight_lensflare.png │ ├── energy-fill.ogg │ ├── explosion.ogg │ ├── explosions.png │ ├── font.png │ ├── hit.ogg │ ├── intro │ ├── intro.ogg │ └── intro.png │ ├── levels │ ├── Airman.xml │ ├── Bubbleman.xml │ ├── Crashman.xml │ ├── DrWily1.xml │ ├── DrWily2.xml │ ├── DrWily3.xml │ ├── DrWily4.xml │ ├── DrWilyAlien.xml │ ├── DrWilyTeleport.xml │ ├── Flashman.xml │ ├── Heatman.xml │ ├── Metalman.xml │ ├── Quickman.xml │ ├── Woodman.xml │ ├── airman │ │ ├── music.ogg │ │ └── tiles.png │ ├── bubbleman │ │ ├── music.ogg │ │ └── tiles.png │ ├── crashman │ │ ├── music.ogg │ │ └── tiles.png │ ├── debug │ │ ├── DestructibleWall.xml │ │ ├── DisappearingBlock.xml │ │ ├── Doors.xml │ │ ├── Elevator.xml │ │ ├── Enemies.xml │ │ ├── JumpHeight.xml │ │ ├── Ladder.xml │ │ ├── OnePlatform.xml │ │ ├── Per.xml │ │ ├── Physics.xml │ │ ├── Powerups.xml │ │ └── sunset.png │ ├── drwily1 │ │ └── tiles.png │ ├── drwily2 │ │ └── tiles.png │ ├── drwily3 │ │ └── tiles.png │ ├── drwily4 │ │ └── tiles.png │ ├── drwilyalien │ │ └── tiles.png │ ├── drwilyteleport │ │ └── tiles.png │ ├── flashman │ │ ├── music.ogg │ │ └── tiles.png │ ├── heatman │ │ ├── music.ogg │ │ └── tiles.png │ ├── metalman │ │ ├── music.ogg │ │ └── tiles.png │ ├── quickman │ │ ├── music.ogg │ │ └── tiles.png │ └── woodman │ │ ├── music.ogg │ │ └── tiles.png │ ├── meters.png │ ├── nes-controller.svg │ ├── objects │ └── obstacles.png │ ├── powerup.png │ ├── projectiles.png │ └── stage-select │ ├── boss-reveal.ogg │ ├── change-selection.ogg │ ├── make-selection.ogg │ ├── stageselect.png │ └── wait-music.ogg ├── src ├── App.css ├── App.js ├── __tests__ │ └── bootstrap-test.js ├── bootstrap.js ├── index.css └── index.js ├── test ├── browser-env.js ├── browser-support │ ├── TestEnv.js │ ├── bootstrap.js │ ├── context.html │ ├── lib │ │ ├── expect.js │ │ ├── mocha.css │ │ ├── mocha.js │ │ └── sinon.js │ ├── style.css │ ├── ui.js │ └── webgl-mock.js ├── env.js ├── fixtures │ ├── animations.xml │ ├── character.xml │ ├── level.xml │ └── textures.xml ├── integration │ ├── fixtures │ │ ├── jump.xml │ │ └── ladder.xml │ ├── index.html │ └── tests │ │ ├── jump.js │ │ ├── ladder.js │ │ └── megaman-colors.js ├── system │ ├── index.html │ ├── input │ │ ├── level-bubbleman.json │ │ ├── level-crashman.json │ │ ├── level-flashman.json │ │ └── level-quickman.json │ └── tests │ │ ├── game.js │ │ ├── intro.js │ │ ├── level-1-airman.js │ │ ├── level-2-bubbleman.js │ │ ├── level-3-crashman.js │ │ ├── level-4-flashman.js │ │ ├── level-5-heatman.js │ │ ├── level-6-metalman.js │ │ ├── level-7-quickman.js │ │ ├── level-8-woodman.js │ │ ├── level-9-drwily1.js │ │ └── stage-select.js └── webpack.config.js └── yarn.lock /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 🛎️ 12 | uses: actions/checkout@v2 13 | 14 | - name: Install 15 | run: yarn install 16 | 17 | - name: Build 18 | run: yarn run build 19 | 20 | - name: Deploy 🚀 21 | uses: JamesIves/github-pages-deploy-action@v4.2.5 22 | with: 23 | branch: gh-pages 24 | folder: build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | /build 4 | /test/browser-test-build.js -------------------------------------------------------------------------------- /.karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(_config) { 2 | 3 | const dependencies = []; 4 | dependencies.push('./test/browser-test-build.js'); 5 | dependencies.push('./test/browser-support/webgl-mock.js'); 6 | 7 | const testFiles = [ 8 | {pattern: 'public/resource/**', watched: true, included: false}, 9 | {pattern: 'test/integration/fixtures/**', watched: true, included: false}, 10 | {pattern: 'test/system/input/**', watched: true, included: false}, 11 | 'test/browser-support/lib/expect.js', 12 | 'test/browser-support/lib/sinon.js', 13 | 'test/browser-support/TestEnv.js', 14 | 'test/browser-support/bootstrap.js', 15 | 'test/integration/tests/*.js', 16 | 'test/system/tests/*.js', 17 | ]; 18 | 19 | const config = { 20 | 21 | // base path that will be used to resolve all patterns (eg. files, exclude) 22 | basePath: '', 23 | 24 | browserNoActivityTimeout: 20000, 25 | 26 | // frameworks to use 27 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 28 | frameworks: [ 29 | 'mocha' 30 | ], 31 | 32 | // list of files / patterns to load in the browser 33 | files: dependencies.concat(testFiles), 34 | 35 | 36 | // list of files to exclude 37 | exclude: [ 38 | ], 39 | 40 | customLaunchers: { 41 | Chrome_travis_ci: { 42 | base: 'Chrome', 43 | flags: ['--no-sandbox'], 44 | } 45 | }, 46 | 47 | customContextFile: 'test/browser-support/context.html', 48 | 49 | proxies: { 50 | '/public/': '/base/public/', 51 | '/test/': '/base/test/', 52 | }, 53 | 54 | // test results reporter to use 55 | // possible values: 'dots', 'progress' 56 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 57 | reporters: [ 58 | 'mocha', 59 | ], 60 | 61 | // web server port 62 | port: 9876, 63 | 64 | // enable / disable colors in the output (reporters and logs) 65 | colors: true, 66 | 67 | // level of logging 68 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 69 | logLevel: _config.LOG_DEBUG, 70 | 71 | // enable / disable watching file and executing tests whenever any file changes 72 | autoWatch: true, 73 | 74 | // start these browsers 75 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 76 | browsers: [ 77 | 'Chrome', 78 | //'Firefox', 79 | ], 80 | 81 | // Continuous Integration mode 82 | // if true, Karma captures browsers, runs the tests and exits 83 | singleRun: false, 84 | 85 | // Concurrency level 86 | // how many browser should be started simultaneous 87 | concurrency: Infinity, 88 | 89 | coverageReporter: { 90 | dir : 'test/coverage/integration', 91 | reporters: [ 92 | {type: 'html', subdir: 'lcov-report'}, 93 | {type: 'json', subdir: './', file: 'coverage.json'} 94 | ] 95 | }, 96 | }; 97 | 98 | if (process.env.TRAVIS) { 99 | config.browsers = ['Chrome_travis_ci']; 100 | config.singleRun = true; 101 | /* 102 | config.files = [ 103 | 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js', 104 | './test/browser-support/webgl-mock.js', 105 | 'build/megaman.es5.js', 106 | ].concat(testFiles); 107 | */ 108 | } 109 | 110 | _config.set(config); 111 | } 112 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | before_install: 5 | - export CHROME_BIN=chromium-browser 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - 9 | - sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' 10 | - sudo apt-get update 11 | - sudo apt-get install google-chrome-stable 12 | branches: 13 | only: 14 | - master 15 | node_js: 16 | - '7' 17 | - '8' 18 | - '9' 19 | script: 20 | - yarn test 21 | - echo "megaman.pomle.com" > build/CNAME 22 | after_success: 23 | - yarn run upload-coverage 24 | deploy: 25 | provider: pages 26 | skip_cleanup: true 27 | github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard 28 | local_dir: build 29 | on: 30 | branch: master 31 | node: '8' 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # megamanjs 2 | [![Build Status](https://travis-ci.org/pomle/megamanjs.svg?branch=master)][1] 3 | [![codecov.io](https://codecov.io/github/pomle/megamanjs/coverage.svg?branch=master)](https://codecov.io/github/pomle/megamanjs?branch=master) 4 | 5 | Project that aims at creating a game engine in JavaScript using Megaman 2 on NES as guide. WebGL is used as renderer and despite Megaman 2 being a 2D game it is run in 3D space using [THREE.js](https://github.com/mrdoob/three.js/) as 3D lib. 6 | 7 | Deployed at http://megaman.pomle.com/ 8 | 9 | This project have generated the SnakeSilk Game Engine and the following libraries: 10 | * [snakesilk-engine](https://github.com/snakesilk/snakesilk-engine) 11 | Simple game engine providing game Entity, Timer, Scene, etc. 12 | 13 | * [snakesilk-xml-loader](https://github.com/snakesilk/snakesilk-xml-loader) 14 | Loader creating entities and scenes from XML. 15 | 16 | * [megaman-kit](https://github.com/snakesilk/megaman-kit) 17 | Components for creating Megaman games. 18 | 19 | Follow the project blog at https://medium.com/recreating-megaman-2-using-js-webgl 20 | 21 | ## Running 22 | 23 | * Clone repo. 24 | 25 | git clone https://github.com/pomle/megamanjs.git 26 | cd megamanjs 27 | 28 | * Install dependencies 29 | 30 | yarn install 31 | 32 | * Start; should open game in a browser. 33 | 34 | yarn start 35 | 36 | 37 | ## Developing 38 | 39 | ### Prerequisites 40 | 41 | * Make sure you are running Node.js `>= 7`. Installation instructions for your platform can be found at https://nodejs.org/en/download/package-manager/. 42 | 43 | node --version 44 | 45 | * Install dev dependencies. 46 | 47 | cd megamanjs 48 | yarn install 49 | 50 | * Run test suite. 51 | 52 | yarn test 53 | 54 | The test suite begins with running a Mocha unit test in Node. After that a Chrome window should open and run a browser test. 55 | 56 | 57 | ## Contributing 58 | 59 | Contributions are welcome. 60 | 61 | [1]: https://travis-ci.org/pomle/megamanjs 62 | -------------------------------------------------------------------------------- /assets/Graphics/Bosses/Airman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Bosses/Airman.psd -------------------------------------------------------------------------------- /assets/Graphics/Bosses/Crashman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Bosses/Crashman.psd -------------------------------------------------------------------------------- /assets/Graphics/Bosses/Flashman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Bosses/Flashman.psd -------------------------------------------------------------------------------- /assets/Graphics/Bosses/Heatman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Bosses/Heatman.psd -------------------------------------------------------------------------------- /assets/Graphics/Bosses/Metalman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Bosses/Metalman.psd -------------------------------------------------------------------------------- /assets/Graphics/Cog.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Cog.ai -------------------------------------------------------------------------------- /assets/Graphics/Deco/Headlight Lensflare prepared.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Deco/Headlight Lensflare prepared.psd -------------------------------------------------------------------------------- /assets/Graphics/Deco/Lens flare.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Deco/Lens flare.psd -------------------------------------------------------------------------------- /assets/Graphics/Defullscreen.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Defullscreen.ai -------------------------------------------------------------------------------- /assets/Graphics/Enemies/ChangkeyMaker.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Enemies/ChangkeyMaker.psd -------------------------------------------------------------------------------- /assets/Graphics/Enemies/Sillycanon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Enemies/Sillycanon.psd -------------------------------------------------------------------------------- /assets/Graphics/Enemies/SniperArmor.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Enemies/SniperArmor.psd -------------------------------------------------------------------------------- /assets/Graphics/Enemies/SniperJoe.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Enemies/SniperJoe.psd -------------------------------------------------------------------------------- /assets/Graphics/Enemies/Telly.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Enemies/Telly.psd -------------------------------------------------------------------------------- /assets/Graphics/Explosions.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Explosions.psd -------------------------------------------------------------------------------- /assets/Graphics/Font.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Font.psd -------------------------------------------------------------------------------- /assets/Graphics/Fullscreen.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Fullscreen.ai -------------------------------------------------------------------------------- /assets/Graphics/Hud.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Hud.psd -------------------------------------------------------------------------------- /assets/Graphics/Intro.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Intro.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Airman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Airman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Bubbleman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Bubbleman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Crashman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Crashman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWily1.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWily1.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWily2.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWily2.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWily3.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWily3.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWily4.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWily4.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWilyAlien.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWilyAlien.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/DrWilyTeleport.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/DrWilyTeleport.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Flashman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Flashman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Heatman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Heatman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Metalman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Metalman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Quickman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Quickman.psd -------------------------------------------------------------------------------- /assets/Graphics/Levels/Woodman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Levels/Woodman.psd -------------------------------------------------------------------------------- /assets/Graphics/Megaman/Megaman.act: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Megaman/Megaman.act -------------------------------------------------------------------------------- /assets/Graphics/Megaman/Megaman.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Megaman/Megaman.psd -------------------------------------------------------------------------------- /assets/Graphics/Megaman/Metalman.act: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Megaman/Metalman.act -------------------------------------------------------------------------------- /assets/Graphics/Meters.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Meters.psd -------------------------------------------------------------------------------- /assets/Graphics/Obstacles.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Obstacles.psd -------------------------------------------------------------------------------- /assets/Graphics/Play.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Play.ai -------------------------------------------------------------------------------- /assets/Graphics/PowerUp.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/PowerUp.psd -------------------------------------------------------------------------------- /assets/Graphics/Projectiles.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Projectiles.psd -------------------------------------------------------------------------------- /assets/Graphics/StageSelect.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/StageSelect.psd -------------------------------------------------------------------------------- /assets/Graphics/Tool/LevelEditor.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/Graphics/Tool/LevelEditor.psd -------------------------------------------------------------------------------- /assets/jnes save states/Bubbleman-pre-boss.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Bubbleman-pre-boss.jst -------------------------------------------------------------------------------- /assets/jnes save states/Bubbleman-start.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Bubbleman-start.jst -------------------------------------------------------------------------------- /assets/jnes save states/Heatman-lower-high-jump.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Heatman-lower-high-jump.jst -------------------------------------------------------------------------------- /assets/jnes save states/Heatman-lower-start.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Heatman-lower-start.jst -------------------------------------------------------------------------------- /assets/jnes save states/Heatman-vertical-1.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Heatman-vertical-1.jst -------------------------------------------------------------------------------- /assets/jnes save states/SniperArmor.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/SniperArmor.jst -------------------------------------------------------------------------------- /assets/jnes save states/Woodman before boss.jst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/assets/jnes save states/Woodman before boss.jst -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "megaman2-js", 3 | "version": "0.2.2", 4 | "license": "MIT", 5 | "repository": "https://github.com/pomle/megamanjs", 6 | "homepage": ".", 7 | "scripts": { 8 | "autoformat": "prettier --trailing-comma es5 --write './src/**/*.{js,jsx}'", 9 | "build": "react-scripts build", 10 | "build:test": "webpack --config ./test/webpack.config.js", 11 | "eject": "react-scripts eject", 12 | "heroku-postbuild": "yarn run build", 13 | "start": "react-scripts start", 14 | "start:prod": "serve ./build -C --port $PORT", 15 | "test": "yarn run test:unit && yarn run build && yarn run build:test && yarn run test:browser", 16 | "test:browser": "karma start .karma.conf --single-run --reporters mocha", 17 | "test:unit": "mocha 'src/**/*-test.js'" 18 | }, 19 | "dependencies": { 20 | "@snakesilk/engine": "^0.12.0", 21 | "@snakesilk/megaman-kit": "^0.7.2", 22 | "@snakesilk/platform-kit": "^0.2.1", 23 | "@snakesilk/react-loader-progress": "^0.0.1", 24 | "@snakesilk/react-presenter": "0.2.0", 25 | "@snex/react-connect": "0.3.2", 26 | "@snex/react-input-mapper": "^0.3.0", 27 | "desensitize-mousemove": "^0.1.3", 28 | "react": "^15.6.1", 29 | "react-dom": "^15.6.1", 30 | "react-scripts": "^5.0.0", 31 | "serve": "^6.0.6" 32 | }, 33 | "devDependencies": { 34 | "@snakesilk/testing": "^0.2.1", 35 | "babel-core": "^6.26.0", 36 | "babel-loader": "^7.1.2", 37 | "expect.js": "^0.3.1", 38 | "karma": "^1.7.0", 39 | "karma-chrome-launcher": "^2.2.0", 40 | "karma-coverage": "^1.1.1", 41 | "karma-mocha": "^1.3.0", 42 | "karma-mocha-reporter": "^2.2.3", 43 | "mocha": "^3.5.0", 44 | "prettier": "^1.5.3", 45 | "sinon": "^3.2.0", 46 | "webpack": "^3.5.4" 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | Megaman 2 JS 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/resource/Decorations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | READY 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /public/resource/Megaman2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~]]> 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /public/resource/Powerup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /public/resource/Projectiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /public/resource/StageSelect.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 241 | 242 | 243 | 247 | 248 | 249 | 253 | 254 | 255 | 259 | 260 | 261 | 265 | 266 | 267 | 271 | 272 | 273 | 277 | 278 | 279 | 283 | 284 | 285 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /public/resource/characters/Airman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /public/resource/characters/ChangkeyMaker.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/resource/characters/Crashman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/resource/characters/Flashman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /public/resource/characters/Heatman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /public/resource/characters/Megaman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | this.climber.bounds.climbable.top - 5) { 94 | return 'bent-over'; 95 | } 96 | if (this.velocity.y !== 0) { 97 | return 'climbing'; 98 | } 99 | return 'hang'; 100 | } 101 | 102 | if (!this.jump._ready) { 103 | if (this.weapon._firing) { 104 | return 'jump-fire'; 105 | } 106 | return 'jump'; 107 | } 108 | 109 | if (this.move._interimSpeed) { 110 | if (this.move._interimSpeed < this.move.speed * .8) { 111 | if (this.weapon._firing) { 112 | return 'fire'; 113 | } 114 | return 'lean'; 115 | } 116 | if (this.weapon._firing) { 117 | return 'run-fire'; 118 | } 119 | return 'run'; 120 | } 121 | 122 | if (this.weapon._firing) { 123 | return 'fire'; 124 | } 125 | 126 | return 'idle'; 127 | } 128 | ]]> 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /public/resource/characters/Metalman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/resource/characters/Shotman.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/resource/characters/SniperArmor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/resource/characters/SniperJoe.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /public/resource/characters/Telly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/resource/characters/airman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/airman.png -------------------------------------------------------------------------------- /public/resource/characters/changkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/changkey.png -------------------------------------------------------------------------------- /public/resource/characters/crashman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/crashman.png -------------------------------------------------------------------------------- /public/resource/characters/enemies/shotman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/enemies/shotman.png -------------------------------------------------------------------------------- /public/resource/characters/enemies/sniperarmor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/enemies/sniperarmor.png -------------------------------------------------------------------------------- /public/resource/characters/enemies/sniperjoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/enemies/sniperjoe.png -------------------------------------------------------------------------------- /public/resource/characters/flashman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/flashman.png -------------------------------------------------------------------------------- /public/resource/characters/heatman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/heatman.png -------------------------------------------------------------------------------- /public/resource/characters/megaman/airshot.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/airshot.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/damage.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/damage.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/death.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/death.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/jump-land.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/jump-land.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/megaman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/megaman.png -------------------------------------------------------------------------------- /public/resource/characters/megaman/metalblade.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/metalblade.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/plasma.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/plasma.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/teleport-in.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/teleport-in.ogg -------------------------------------------------------------------------------- /public/resource/characters/megaman/teleport-out.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/megaman/teleport-out.ogg -------------------------------------------------------------------------------- /public/resource/characters/metalman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/metalman.png -------------------------------------------------------------------------------- /public/resource/characters/telly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/characters/telly.png -------------------------------------------------------------------------------- /public/resource/cog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /public/resource/crash-bomb-attach.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/crash-bomb-attach.ogg -------------------------------------------------------------------------------- /public/resource/deco/headlight_lensflare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/deco/headlight_lensflare.png -------------------------------------------------------------------------------- /public/resource/energy-fill.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/energy-fill.ogg -------------------------------------------------------------------------------- /public/resource/explosion.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/explosion.ogg -------------------------------------------------------------------------------- /public/resource/explosions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/explosions.png -------------------------------------------------------------------------------- /public/resource/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/font.png -------------------------------------------------------------------------------- /public/resource/hit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/hit.ogg -------------------------------------------------------------------------------- /public/resource/intro/intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/intro/intro.ogg -------------------------------------------------------------------------------- /public/resource/intro/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/intro/intro.png -------------------------------------------------------------------------------- /public/resource/levels/DrWilyTeleport.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /public/resource/levels/airman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/airman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/airman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/airman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/bubbleman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/bubbleman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/bubbleman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/bubbleman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/crashman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/crashman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/crashman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/crashman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/debug/DestructibleWall.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/resource/levels/debug/DisappearingBlock.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Doors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Elevator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Enemies.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/resource/levels/debug/JumpHeight.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Ladder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /public/resource/levels/debug/OnePlatform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Per.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Physics.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/resource/levels/debug/Powerups.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/resource/levels/debug/sunset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/debug/sunset.png -------------------------------------------------------------------------------- /public/resource/levels/drwily1/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwily1/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/drwily2/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwily2/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/drwily3/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwily3/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/drwily4/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwily4/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/drwilyalien/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwilyalien/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/drwilyteleport/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/drwilyteleport/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/flashman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/flashman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/flashman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/flashman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/heatman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/heatman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/heatman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/heatman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/metalman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/metalman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/metalman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/metalman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/quickman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/quickman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/quickman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/quickman/tiles.png -------------------------------------------------------------------------------- /public/resource/levels/woodman/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/woodman/music.ogg -------------------------------------------------------------------------------- /public/resource/levels/woodman/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/levels/woodman/tiles.png -------------------------------------------------------------------------------- /public/resource/meters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/meters.png -------------------------------------------------------------------------------- /public/resource/objects/obstacles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/objects/obstacles.png -------------------------------------------------------------------------------- /public/resource/powerup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/powerup.png -------------------------------------------------------------------------------- /public/resource/projectiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/projectiles.png -------------------------------------------------------------------------------- /public/resource/stage-select/boss-reveal.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/stage-select/boss-reveal.ogg -------------------------------------------------------------------------------- /public/resource/stage-select/change-selection.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/stage-select/change-selection.ogg -------------------------------------------------------------------------------- /public/resource/stage-select/make-selection.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/stage-select/make-selection.ogg -------------------------------------------------------------------------------- /public/resource/stage-select/stageselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/stage-select/stageselect.png -------------------------------------------------------------------------------- /public/resource/stage-select/wait-music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomle/megamanjs/03392276d378922ddf56e086e895cf2da49567d6/public/resource/stage-select/wait-music.ogg -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | height: 100%; 3 | } 4 | 5 | .App .snakesilk-presenter .snex-connect { 6 | font-size: 2vh; 7 | height: 7vh; 8 | margin: 0 auto; 9 | width: 14vw; 10 | } 11 | 12 | .App .snakesilk-presenter .video .overlay { 13 | align-items: center; 14 | display: flex; 15 | flex-flow: column; 16 | justify-content: center; 17 | } 18 | 19 | .App .snakesilk-presenter .fullscreen { 20 | background: #000; 21 | height: 100%; 22 | } 23 | 24 | .App .snex-input-mapper { 25 | font-size: 2vh; 26 | margin: 0 auto; 27 | width: 30vw; 28 | } 29 | 30 | .App .snex-input-mapper .key-map { 31 | background: rgba(0,0,0,.5); 32 | border-radius: .2em; 33 | padding: 1em; 34 | text-align: left; 35 | } 36 | 37 | .App .snex-input-mapper button { 38 | background: transparent; 39 | border: none; 40 | color: inherit; 41 | margin-left: auto; 42 | } 43 | 44 | .App .snakesilk-progress-bar { 45 | border: solid 3px #fff; 46 | width: 50%; 47 | } 48 | 49 | .App .snakesilk-progress-bar.idle { 50 | display: none; 51 | } 52 | 53 | .App .snakesilk-progress-bar .progress { 54 | background: #fff; 55 | height: 10px; 56 | width: 50%; 57 | } 58 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Presenter from "@snakesilk/react-presenter"; 3 | import { ProgressBar } from "@snakesilk/react-loader-progress"; 4 | import SNEXConnect from "@snex/react-connect"; 5 | import SNEXMapper from "@snex/react-input-mapper"; 6 | import {createLoader} from './bootstrap'; 7 | import "./App.css"; 8 | 9 | class App extends Component { 10 | constructor(props) { 11 | super(props); 12 | this.loader = createLoader(); 13 | this.game = this.loader.game; 14 | window.megaman2 = this.loader; 15 | } 16 | 17 | componentDidMount() { 18 | this.loader.loadGame("./resource/Megaman2.xml"); 19 | } 20 | 21 | attachSNEXController(controller) { 22 | controller.on("data", ({ key, state }) => { 23 | this.routeInput(key, state); 24 | }); 25 | } 26 | 27 | routeInput(key, state) { 28 | this.game.input.trigger(key.toLowerCase(), state ? "keydown" : "keyup"); 29 | } 30 | 31 | render() { 32 | return ( 33 |
34 | } 39 | > 40 | this.routeInput(key, state)} 42 | svgURL="/resource/nes-controller.svg" 43 | /> 44 | 45 | this.attachSNEXController(cont)} 48 | /> 49 | 50 |
51 | ); 52 | } 53 | } 54 | 55 | export default App; 56 | -------------------------------------------------------------------------------- /src/__tests__/bootstrap-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | 3 | const mocks = require('@snakesilk/testing/mocks'); 4 | const {createNode} = require('@snakesilk/testing/xml'); 5 | const {createLoader} = require('../bootstrap'); 6 | const {Parser: {EntityParser}} = require('@snakesilk/xml-loader'); 7 | 8 | describe('createLoader()', () => { 9 | let loader; 10 | 11 | beforeEach(() => { 12 | mocks.AudioContext.mock(); 13 | mocks.requestAnimationFrame.mock(); 14 | mocks.THREE.WebGLRenderer.mock(); 15 | 16 | loader = createLoader(); 17 | }); 18 | 19 | afterEach(() => { 20 | mocks.AudioContext.restore(); 21 | mocks.requestAnimationFrame.restore(); 22 | mocks.THREE.WebGLRenderer.restore(); 23 | }); 24 | 25 | [ 26 | 'Airman', 27 | 'Crashman', 28 | 'Flashman', 29 | 'Heatman', 30 | 'Megaman', 31 | 'Metalman', 32 | 33 | 'ChangkeyMaker', 34 | 'Shotman', 35 | 'SniperArmor', 36 | 'SniperJoe', 37 | 'Telly', 38 | ].forEach(name => { 39 | const id = name + '-id'; 40 | const xmlString = ` 41 | 42 | `; 43 | 44 | describe(`when parsing ${xmlString}`, () => { 45 | let parser, Entity; 46 | 47 | beforeEach(() => { 48 | const node = createNode(xmlString); 49 | parser = new EntityParser(loader); 50 | return parser.getObjects(node) 51 | .then(objects => { 52 | Entity = objects[id].constructor; 53 | }); 54 | }); 55 | 56 | it(`produces a ${name} entity`, () => { 57 | expect(Entity).to.be.a(Function); 58 | }); 59 | 60 | it.skip(`produces a ${name} entity`, () => { 61 | expect(new Entity()).to.be.a(Objects[name]); 62 | }); 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | const {Game} = require('@snakesilk/engine'); 2 | const {Traits: PlatformTraits} = require('@snakesilk/platform-kit'); 3 | const { 4 | Entities, 5 | Loaders: {MegamanLoader}, 6 | Traits: MegamanTraits, 7 | } = require('@snakesilk/megaman-kit'); 8 | 9 | function createLoader() { 10 | const game = new Game(); 11 | const loader = new MegamanLoader(game); 12 | 13 | loader.entities.add(Entities); 14 | 15 | loader.traits.add(PlatformTraits); 16 | loader.traits.add(MegamanTraits); 17 | 18 | return loader; 19 | } 20 | 21 | module.exports = { 22 | createLoader, 23 | }; 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Saira", sans-serif; 3 | } 4 | 5 | body { 6 | background: #222; 7 | color: #fff; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | a { 13 | color: #fff; 14 | font-weight: bold; 15 | text-decoration: none; 16 | } 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /test/browser-env.js: -------------------------------------------------------------------------------- 1 | const THREE = require('three'); 2 | const Engine = require('@snakesilk/engine'); 3 | const {createLoader} = require('../src/bootstrap'); 4 | 5 | window.THREE = THREE; 6 | window.Engine = Engine; 7 | window.createLoader = createLoader; 8 | -------------------------------------------------------------------------------- /test/browser-support/TestEnv.js: -------------------------------------------------------------------------------- 1 | class TestEnv 2 | { 3 | constructor(gameXML) 4 | { 5 | this.renderInterval = 0; 6 | this.tickDelay = -1; 7 | 8 | this.paused = false; 9 | this._resume = null; 10 | 11 | this._running = false; 12 | this._screen = document.querySelector('#screen'); 13 | 14 | this.loader = createLoader(); 15 | this.game = this.loader.game; 16 | 17 | if (this._screen) { 18 | this.game.attachToElement(this._screen); 19 | this.game.setResolution(640, 480); 20 | this.game.adjustResolution(); 21 | } 22 | this.ready = this.loader.loadGame(gameXML); 23 | } 24 | destroy() 25 | { 26 | this.game.destroy(); 27 | this._screen.innerHTML = ''; 28 | } 29 | load(nameOrUrl) 30 | { 31 | const method = nameOrUrl.indexOf('.xml') === -1 ? 'loadSceneByName' : 'loadScene'; 32 | return this.loader[method](nameOrUrl).then(scene => { 33 | scene.camera.smoothing = 0; 34 | return scene; 35 | }); 36 | } 37 | loadInput(url) 38 | { 39 | return fetch(url).then(response => response.json()); 40 | } 41 | pauseToggle() 42 | { 43 | if (this.paused) { 44 | this.paused = false; 45 | if (this._resume) { 46 | this._resume(); 47 | } 48 | } else { 49 | this.paused = true; 50 | } 51 | } 52 | scene(scene) 53 | { 54 | this.game.setScene(scene); 55 | } 56 | tap(keys) 57 | { 58 | const inp = this.game.input; 59 | inp.enable(); 60 | keys.split(' ').forEach(key => { 61 | inp.trigger(key, inp.ENGAGE); 62 | inp.trigger(key, inp.RELEASE); 63 | }); 64 | } 65 | toggle(keys, state) 66 | { 67 | const inp = this.game.input; 68 | inp.enable(); 69 | keys.split(' ').forEach(key => { 70 | inp.trigger(key, state ? inp.ENGAGE : inp.RELEASE); 71 | }); 72 | } 73 | release() 74 | { 75 | this.game.input.release(); 76 | } 77 | settle() 78 | { 79 | this.game.scene.world.simulateTime(0); 80 | } 81 | playInput(log) 82 | { 83 | this.game.input.enable(); 84 | const player = new Engine.InputPlayer(this.game.scene.world, 85 | this.game.input); 86 | player.play(log); 87 | return player; 88 | } 89 | goToTick(tick) 90 | { 91 | /* When the game is paused and the tick retrieved 92 | current tick is returned but the simulation event 93 | for that tick will happen on the next iteration. */ 94 | const goal = (tick - 1) | 0; 95 | return this.waitUntil(data => data.tick === goal); 96 | } 97 | goToTime(time) 98 | { 99 | return this.waitUntil(data => data.totalTime > time); 100 | } 101 | waitTicks(ticks) 102 | { 103 | const tick = this.game.scene.world._tick + ticks; 104 | return this.waitUntil(data => data.tick >= tick); 105 | } 106 | waitTime(time) 107 | { 108 | const goal = this.game.scene.world._timeTotal + time; 109 | return this.waitUntil(data => data.totalTime >= goal); 110 | } 111 | waitUntil(condition) 112 | { 113 | if (this._running) { 114 | throw new Error('Progress already running. Make sure last wait*() call was waiting to be resolved.'); 115 | } 116 | this._running = true; 117 | 118 | return new Promise(resolve => { 119 | const scene = this.game.scene; 120 | const world = scene.world; 121 | 122 | const simCallback = (dt, t, tick) => { 123 | if (this.renderInterval > 0 && tick % this.renderInterval === 0) { 124 | this.game.render(); 125 | } 126 | 127 | const data = { 128 | scene, 129 | world, 130 | tick, 131 | deltaTime: dt, 132 | totalTime: t, 133 | }; 134 | 135 | if (condition(data)) { 136 | world.events.unbind(world.EVENT_SIMULATE, simCallback); 137 | this._running = false; 138 | this._resume = null; 139 | resolve(); 140 | } 141 | }; 142 | 143 | world.events.bind(world.EVENT_SIMULATE, simCallback); 144 | 145 | const next = this._resume = () => { 146 | if (!this.paused) { 147 | if (this.tickDelay >= 0) { 148 | scene.updateTime(scene.world.timeStep); 149 | } else { 150 | /* Relieve occasionally to let Async operations run. 151 | In the game environment everything is sync, but 152 | the loader is async when loading resources so in 153 | order to be able to test that a level is loaded 154 | at some time, we need to back off. */ 155 | let maxTicks = 1200; 156 | while (maxTicks-- && this._running) { 157 | scene.updateTime(scene.world.timeStep); 158 | } 159 | } 160 | 161 | if (this._running) { 162 | setTimeout(next, this.tickDelay); 163 | } 164 | } 165 | }; 166 | 167 | next(); 168 | }); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test/browser-support/bootstrap.js: -------------------------------------------------------------------------------- 1 | mocha.setup({ 2 | ui: 'bdd', 3 | enableTimeouts: false, 4 | }); 5 | 6 | const env = new TestEnv('/public/resource/Megaman2.xml'); 7 | env.renderInterval = 0; 8 | env.tickDelay = -1; 9 | -------------------------------------------------------------------------------- /test/browser-support/context.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 21 | 22 | 30 | 31 | %SCRIPTS% 32 | 35 | 36 | -------------------------------------------------------------------------------- /test/browser-support/lib/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /test/browser-support/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | font-family: sans-serif; 4 | } 5 | 6 | fieldset { 7 | border: none; 8 | font-size: .8em; 9 | } 10 | 11 | #control { 12 | margin: .5em; 13 | } 14 | 15 | #mocha { 16 | flex: 1; 17 | height: 100%; 18 | margin: 0; 19 | overflow: auto; 20 | } 21 | 22 | #mocha-report { 23 | margin: 1em 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/browser-support/ui.js: -------------------------------------------------------------------------------- 1 | ['tickDelay', 'renderInterval'].forEach(prop => { 2 | const element = document.querySelector('[name=' + prop + ']'); 3 | let value; 4 | if (value = localStorage.getItem(prop)) { 5 | element.value = value; 6 | } else { 7 | value = element.value; 8 | } 9 | 10 | if (env.hasOwnProperty(prop)) { 11 | env[prop] = value|0; 12 | } 13 | }); 14 | 15 | document.addEventListener('click', e => { 16 | const name = e.target.getAttribute('name'); 17 | if (name === 'pauseToggle') { 18 | env.pauseToggle(); 19 | } else if (name === 'exposeTick') { 20 | if (env.game.scene) { 21 | console.info('Tick', env.game.scene.world._tick); 22 | } else { 23 | console.error('No active scene'); 24 | } 25 | } 26 | }); 27 | 28 | document.addEventListener('change', e => { 29 | if (e.target.matches('[name=tickDelay]')) { 30 | env.tickDelay = e.target.value|0; 31 | localStorage.setItem('tickDelay', e.target.value); 32 | } else if (e.target.matches('[name=renderInterval]')) { 33 | env.renderInterval = e.target.value|0; 34 | localStorage.setItem('renderInterval', e.target.value); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /test/browser-support/webgl-mock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file overrides the renderer part of THREE.js's WebGLRenderer 3 | * and can be used to run THREE.js when no graphics card is available. 4 | * 5 | * Include after THREE.js and it will overwrite relevant parts of THREE.js. 6 | */ 7 | 8 | window.THREE.WebGLRenderer = function() { 9 | this.domElement = document.createElement('canvas'); 10 | this.render = function() {} 11 | this.setSize = function() {} 12 | } 13 | -------------------------------------------------------------------------------- /test/env.js: -------------------------------------------------------------------------------- 1 | global.self = global; 2 | -------------------------------------------------------------------------------- /test/fixtures/animations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/character.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /test/fixtures/textures.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/integration/fixtures/jump.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/integration/fixtures/ladder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /test/integration/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 | 20 |
21 | Playback 22 | 23 | 24 |
25 | 26 |
27 | Render Interval 28 | 29 | 35 |
36 | 37 |
38 | Tick Delay 39 | 40 | 49 |
50 |
51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | -------------------------------------------------------------------------------- /test/integration/tests/jump.js: -------------------------------------------------------------------------------- 1 | describe('Jump', () => { 2 | let player; 3 | 4 | before(done => { 5 | env.ready.then(() => { 6 | return env.load('/test/integration/fixtures/jump.xml'); 7 | }).then(scene => { 8 | env.scene(scene); 9 | player = env.game.player.character; 10 | done(); 11 | }).catch(done); 12 | }); 13 | 14 | after(() => { 15 | env.game.unsetScene(); 16 | }); 17 | 18 | beforeEach(() => { 19 | env.release(); 20 | player.reset(); 21 | }); 22 | 23 | describe('when jumping without obstruction', () => { 24 | let maxHeight = -Infinity; 25 | before(done => { 26 | player.position.x = -56; 27 | player.position.y = 0; 28 | env.settle(); 29 | player.jump.engage(); 30 | env.waitUntil(data => { 31 | if (player.position.y < maxHeight) { 32 | return true; 33 | } 34 | maxHeight = player.position.y; 35 | }).then(done); 36 | }); 37 | 38 | it('max height should be between 64 and 65', () => { 39 | expect(maxHeight).to.be.within(64, 65); 40 | }); 41 | }); 42 | 43 | describe('when jumping into obstruction', () => { 44 | let maxHeight = -Infinity; 45 | before(done => { 46 | player.position.x = 0; 47 | player.position.y = 0; 48 | env.settle(); 49 | player.jump.engage(); 50 | env.waitUntil(data => { 51 | if (player.position.y < maxHeight) { 52 | return true; 53 | } 54 | maxHeight = player.position.y; 55 | }).then(done); 56 | }); 57 | 58 | it('jump should be cancelled', () => { 59 | expect(maxHeight).to.be.within(30, 30.2); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/integration/tests/ladder.js: -------------------------------------------------------------------------------- 1 | describe('Ladder', () => { 2 | let player; 3 | 4 | before(done => { 5 | env.ready.then(() => { 6 | return env.load('/test/integration/fixtures/ladder.xml'); 7 | }).then(scene => { 8 | env.scene(scene); 9 | player = env.game.player.character; 10 | done(); 11 | }).catch(done); 12 | }); 13 | 14 | after(() => { 15 | env.game.unsetScene(); 16 | }); 17 | 18 | beforeEach(() => { 19 | env.release(); 20 | player.reset(); 21 | }); 22 | 23 | describe('when jumping against a ladder from below', () => { 24 | describe('and not aiming', () => { 25 | before(done => { 26 | player.position.x = -48; 27 | player.position.y = -69; 28 | env.waitTime(.1).then(() => { 29 | env.toggle('a', 1); 30 | return env.waitTime(.4); 31 | }).then(done); 32 | }); 33 | 34 | it('player passes thru ladder', () => { 35 | expect(player.position.y).to.be.within(-12, -11); 36 | }); 37 | 38 | it('player does not grab ladder', () => { 39 | expect(player.climber.attached).to.be(null); 40 | }); 41 | }); 42 | 43 | describe('while aiming up', () => { 44 | before(done => { 45 | player.position.x = -48; 46 | player.position.y = -69; 47 | env.toggle('a up', 1); 48 | env.waitTime(.4).then(done); 49 | }); 50 | 51 | it('player grabs ladder', () => { 52 | expect(player.position.x).to.be(-48); 53 | expect(player.position.y).to.be.within(-26, -25); 54 | }); 55 | }); 56 | }); 57 | 58 | describe('when falling on ladder from above', () => { 59 | describe('and not aiming', () => { 60 | before(done => { 61 | player.position.x = -48; 62 | player.position.y = 120; 63 | env.waitTime(.5).then(done); 64 | }); 65 | 66 | it('player is supported by ladder', () => { 67 | expect(player.position.x).to.be(-48); 68 | expect(player.position.y).to.be(107); 69 | }); 70 | }); 71 | 72 | describe('and aiming down', () => { 73 | before(done => { 74 | player.position.x = -48; 75 | player.position.y = 120; 76 | env.toggle('down', 1); 77 | env.waitTime(.4).then(done); 78 | }); 79 | 80 | it('player grabs ladder and climbs down', () => { 81 | expect(player.position.x).to.be(-48); 82 | expect(player.position.y).to.be.within(80, 82); 83 | }); 84 | }); 85 | }); 86 | 87 | describe('when walking past ladder from left', () => { 88 | describe('and not aiming', () => { 89 | before(done => { 90 | player.position.x = 80; 91 | player.position.y = -150; 92 | env.toggle('right', 1); 93 | env.waitTime(.5).then(done); 94 | }); 95 | 96 | it('player ignores ladder', () => { 97 | expect(player.position.x).to.be.within(117, 118); 98 | expect(player.position.y).to.be(-149); 99 | }); 100 | }); 101 | 102 | describe('while aiming up', () => { 103 | before(done => { 104 | player.position.x = 80; 105 | player.position.y = -150; 106 | env.toggle('right up', 1); 107 | env.waitTime(.5).then(done); 108 | }); 109 | 110 | it('player grabs ladder', () => { 111 | expect(player.position.x).to.be.within(96, 97); 112 | expect(player.position.y).to.be.within(-135, -134); 113 | }); 114 | }); 115 | }); 116 | 117 | describe('when walking past ladder from right', () => { 118 | describe('and not aiming', () => { 119 | before(done => { 120 | player.position.x = 112; 121 | player.position.y = -150; 122 | env.toggle('left', 1); 123 | env.waitTime(.5).then(done); 124 | }); 125 | 126 | it('player ignores ladder', () => { 127 | expect(player.position.x).to.be.within(66, 67); 128 | expect(player.position.y).to.be(-149); 129 | }); 130 | }); 131 | 132 | describe('while aiming up', () => { 133 | before(done => { 134 | player.position.x = 112; 135 | player.position.y = -150; 136 | env.toggle('left up', 1); 137 | env.waitTime(.5).then(done); 138 | }); 139 | 140 | it('player grabs ladder', () => { 141 | expect(player.position.x).to.be.within(95, 96); 142 | expect(player.position.y).to.be.within(-135, -134); 143 | }); 144 | }); 145 | }); 146 | 147 | describe('when falling along a ladder', () => { 148 | describe('and pressing down', () => { 149 | before(done => { 150 | player.position.x = -48; 151 | player.position.y = 60; 152 | env.toggle('down', 1); 153 | env.waitTime(.2).then(done); 154 | }); 155 | 156 | it('nothing happens', () => { 157 | expect(player.climber.attached).to.be(null); 158 | }); 159 | }); 160 | 161 | describe('and pressing up', () => { 162 | before(done => { 163 | player.position.x = -48; 164 | player.position.y = 0; 165 | env.toggle('up', 1); 166 | env.waitTime(.2).then(done); 167 | }); 168 | 169 | it('ladder is grabbed', () => { 170 | expect(player.position.y).to.be.within(9, 11); 171 | //expect(player.climber.attached).to.not.be(null); 172 | }); 173 | }); 174 | }); 175 | 176 | describe('when on ladder', () => { 177 | beforeEach(done => { 178 | player.position.x = -48; 179 | player.position.y = 0; 180 | env.toggle('up', 1); 181 | env.waitTicks(1).then(() => { 182 | env.release(); 183 | }).then(done); 184 | }); 185 | 186 | describe('and reaching bottom when climbing down', () => { 187 | beforeEach(done => { 188 | env.toggle('down', 1); 189 | env.waitTime(1).then(done); 190 | }); 191 | 192 | it('player releases ladder', () => { 193 | expect(player.position.y).to.be.within(-55, -52); 194 | expect(player.position.x).to.be(-48); 195 | }); 196 | 197 | /* The test above is contaminated by the full test suite 198 | when running on different speeds. I have not been able 199 | to figure out why. Hopefully this test is still valid 200 | but ideally the "it" from below should be used. */ 201 | it.skip('player releases ladder (exact)', () => { 202 | expect(player.position.y).to.be.within(-55, -54); 203 | expect(player.position.x).to.be(-48); 204 | }); 205 | }); 206 | 207 | describe('and player is reset', () => { 208 | beforeEach(done => { 209 | player.reset(); 210 | env.waitTime(.5).then(done); 211 | }); 212 | 213 | it('player releases ladder', () => { 214 | expect(player.position.y).to.be(-69); 215 | expect(player.position.x).to.be(-48); 216 | }); 217 | }); 218 | }); 219 | }); 220 | -------------------------------------------------------------------------------- /test/integration/tests/megaman-colors.js: -------------------------------------------------------------------------------- 1 | describe('Megaman Colors', () => { 2 | let player, weapons; 3 | 4 | before(done => { 5 | env.ready.then(() => { 6 | player = env.game.player.character; 7 | weapons = env.game.player.weapons; 8 | 9 | const scene = new Engine.Scene(); 10 | scene.camera.position.z = 100; 11 | scene.world.gravityForce.set(0, 0); 12 | scene.world.addObject(player); 13 | scene.world.updateTime(0.1); 14 | env.scene(scene); 15 | 16 | done(); 17 | }).catch(done); 18 | }); 19 | 20 | after(() => { 21 | player.weapon.equip(weapons.p); 22 | env.game.unsetScene(); 23 | }); 24 | 25 | function getPixel(x, y) { 26 | const scale = env.loader.textureScale; 27 | return player.model.material.map.image 28 | .getContext('2d') 29 | .getImageData(x * scale, y * scale, 1, 1); 30 | } 31 | 32 | describe('when changing weapon', () => { 33 | describe('to Plasma (default)', () => { 34 | before(() => { 35 | player.weapon.equip(weapons.p); 36 | env.game.render(); 37 | }); 38 | 39 | it('palette is blue shaded', () => { 40 | expect(getPixel(26, 26).data).to.eql([0, 255, 255, 255]); 41 | expect(getPixel(26, 29).data).to.eql([0, 115, 247, 255]); 42 | }); 43 | }); 44 | 45 | describe('to Air Shooter', () => { 46 | before(() => { 47 | player.weapon.equip(weapons.a); 48 | env.game.render(); 49 | }); 50 | 51 | it('palette is white and blue', () => { 52 | expect(getPixel(26, 26).data).to.eql([248, 248, 248, 255]); 53 | expect(getPixel(26, 29).data).to.eql([0, 120, 248, 255]); 54 | }); 55 | }); 56 | 57 | describe('to Crash Bomber', () => { 58 | before(() => { 59 | player.weapon.equip(weapons.c); 60 | env.game.render(); 61 | }); 62 | 63 | it('palette is white and red', () => { 64 | expect(getPixel(26, 26).data).to.eql([248, 248, 248, 255]); 65 | expect(getPixel(26, 29).data).to.eql([248, 120, 88, 255]); 66 | }); 67 | }); 68 | 69 | describe('to Metal Blade', () => { 70 | before(() => { 71 | player.weapon.equip(weapons.m); 72 | env.game.render(); 73 | }); 74 | 75 | it('palette is green shaded', () => { 76 | expect(getPixel(26, 26).data).to.eql([255, 224, 168, 255]); 77 | expect(getPixel(26, 29).data).to.eql([172, 175, 0, 255]); 78 | }); 79 | }); 80 | 81 | describe('to Time Stopper', () => { 82 | before(() => { 83 | player.weapon.equip(weapons.f); 84 | env.game.render(); 85 | }); 86 | 87 | it('palette is purple shaded', () => { 88 | expect(getPixel(26, 26).data).to.eql([248, 184, 248, 255]); 89 | expect(getPixel(26, 29).data).to.eql([216, 0, 204, 255]); 90 | }); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/system/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 | 20 |
21 | Playback 22 | 23 | 24 |
25 | 26 |
27 | Render Interval 28 | 29 | 35 |
36 | 37 |
38 | Tick Delay 39 | 40 | 49 |
50 |
51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | -------------------------------------------------------------------------------- /test/system/input/level-bubbleman.json: -------------------------------------------------------------------------------- 1 | [{"tick":320,"key":"right","type":"keydown"},{"tick":362,"key":"a","type":"keydown"},{"tick":376,"key":"a","type":"keyup"},{"tick":536,"key":"a","type":"keydown"},{"tick":584,"key":"a","type":"keyup"},{"tick":708,"key":"a","type":"keydown"},{"tick":760,"key":"a","type":"keyup"},{"tick":888,"key":"a","type":"keydown"},{"tick":938,"key":"a","type":"keyup"},{"tick":1100,"key":"a","type":"keydown"},{"tick":1120,"key":"a","type":"keyup"},{"tick":1168,"key":"right","type":"keyup"},{"tick":1174,"key":"right","type":"keydown"},{"tick":1194,"key":"a","type":"keydown"},{"tick":1212,"key":"a","type":"keyup"},{"tick":1266,"key":"a","type":"keydown"},{"tick":1294,"key":"a","type":"keyup"},{"tick":1348,"key":"a","type":"keydown"},{"tick":1370,"key":"a","type":"keyup"},{"tick":1444,"key":"a","type":"keydown"},{"tick":1462,"key":"a","type":"keyup"},{"tick":1516,"key":"a","type":"keydown"},{"tick":1538,"key":"a","type":"keyup"},{"tick":1830,"key":"right","type":"keyup"},{"tick":1834,"key":"left","type":"keydown"},{"tick":1900,"key":"left","type":"keyup"},{"tick":1904,"key":"right","type":"keydown"},{"tick":1980,"key":"right","type":"keyup"},{"tick":1982,"key":"left","type":"keydown"},{"tick":2096,"key":"right","type":"keydown"},{"tick":2096,"key":"left","type":"keyup"},{"tick":2218,"key":"left","type":"keydown"},{"tick":2220,"key":"right","type":"keyup"},{"tick":2428,"key":"left","type":"keyup"},{"tick":2430,"key":"right","type":"keydown"},{"tick":2442,"key":"right","type":"keyup"},{"tick":2510,"key":"right","type":"keydown"},{"tick":2666,"key":"right","type":"keyup"},{"tick":2668,"key":"left","type":"keydown"},{"tick":2872,"key":"left","type":"keyup"},{"tick":2876,"key":"right","type":"keydown"},{"tick":3744,"key":"a","type":"keydown"},{"tick":3766,"key":"a","type":"keyup"},{"tick":3832,"key":"a","type":"keydown"},{"tick":3848,"key":"a","type":"keyup"},{"tick":3912,"key":"a","type":"keydown"},{"tick":3932,"key":"a","type":"keyup"},{"tick":4008,"key":"right","type":"keyup"}] -------------------------------------------------------------------------------- /test/system/input/level-crashman.json: -------------------------------------------------------------------------------- 1 | [{"tick":316,"key":"left","type":"keydown"},{"tick":334,"key":"left","type":"keyup"},{"tick":334,"key":"a","type":"keydown"},{"tick":336,"key":"up","type":"keydown"},{"tick":368,"key":"a","type":"keyup"},{"tick":562,"key":"left","type":"keydown"},{"tick":568,"key":"up","type":"keyup"},{"tick":596,"key":"a","type":"keydown"},{"tick":632,"key":"up","type":"keydown"},{"tick":634,"key":"a","type":"keyup"},{"tick":650,"key":"left","type":"keyup"},{"tick":748,"key":"right","type":"keydown"},{"tick":752,"key":"up","type":"keyup"},{"tick":804,"key":"a","type":"keydown"},{"tick":824,"key":"a","type":"keyup"},{"tick":836,"key":"up","type":"keydown"},{"tick":848,"key":"right","type":"keyup"},{"tick":944,"key":"left","type":"keydown"},{"tick":946,"key":"up","type":"keyup"},{"tick":976,"key":"a","type":"keydown"},{"tick":1010,"key":"up","type":"keydown"},{"tick":1016,"key":"a","type":"keyup"},{"tick":1028,"key":"left","type":"keyup"},{"tick":1410,"key":"right","type":"keydown"},{"tick":1420,"key":"up","type":"keyup"},{"tick":1532,"key":"a","type":"keydown"},{"tick":1550,"key":"a","type":"keyup"},{"tick":1706,"key":"a","type":"keydown"},{"tick":1718,"key":"a","type":"keyup"},{"tick":1796,"key":"a","type":"keydown"},{"tick":1804,"key":"a","type":"keyup"},{"tick":1890,"key":"a","type":"keydown"},{"tick":1920,"key":"up","type":"keydown"},{"tick":1922,"key":"a","type":"keyup"},{"tick":1924,"key":"right","type":"keyup"},{"tick":2200,"key":"a","type":"keydown"},{"tick":2206,"key":"up","type":"keyup"},{"tick":2212,"key":"left","type":"keydown"},{"tick":2222,"key":"a","type":"keyup"},{"tick":2250,"key":"left","type":"keyup"},{"tick":2320,"key":"left","type":"keydown"},{"tick":2350,"key":"left","type":"keyup"},{"tick":2372,"key":"right","type":"keydown"},{"tick":2388,"key":"right","type":"keyup"},{"tick":2784,"key":"right","type":"keydown"},{"tick":2812,"key":"right","type":"keyup"},{"tick":3044,"key":"up","type":"keydown"},{"tick":3306,"key":"a","type":"keydown"},{"tick":3306,"key":"left","type":"keydown"},{"tick":3308,"key":"up","type":"keyup"},{"tick":3328,"key":"a","type":"keyup"},{"tick":3338,"key":"left","type":"keyup"},{"tick":3376,"key":"left","type":"keydown"},{"tick":3396,"key":"left","type":"keyup"},{"tick":3744,"key":"right","type":"keydown"},{"tick":3762,"key":"right","type":"keyup"},{"tick":4234,"key":"a","type":"keydown"},{"tick":4244,"key":"up","type":"keydown"},{"tick":4258,"key":"a","type":"keyup"},{"tick":4502,"key":"right","type":"keydown"},{"tick":4508,"key":"up","type":"keyup"},{"tick":4600,"key":"a","type":"keydown"},{"tick":4600,"key":"right","type":"keyup"},{"tick":4612,"key":"right","type":"keydown"},{"tick":4658,"key":"right","type":"keyup"},{"tick":4666,"key":"a","type":"keyup"},{"tick":4696,"key":"a","type":"keydown"},{"tick":4722,"key":"left","type":"keydown"},{"tick":4740,"key":"left","type":"keyup"},{"tick":4756,"key":"a","type":"keyup"},{"tick":6442,"key":"right","type":"keydown"},{"tick":6480,"key":"right","type":"keyup"},{"tick":6644,"key":"right","type":"keydown"},{"tick":6698,"key":"right","type":"keyup"},{"tick":6770,"key":"a","type":"keydown"},{"tick":6776,"key":"right","type":"keydown"},{"tick":6792,"key":"a","type":"keyup"},{"tick":6814,"key":"right","type":"keyup"},{"tick":6830,"key":"left","type":"keydown"},{"tick":6852,"key":"left","type":"keyup"},{"tick":8250,"key":"right","type":"keydown"},{"tick":8260,"key":"right","type":"keyup"},{"tick":8640,"key":"right","type":"keydown"},{"tick":8650,"key":"right","type":"keyup"},{"tick":9428,"key":"up","type":"keydown"},{"tick":9752,"key":"right","type":"keydown"},{"tick":9754,"key":"up","type":"keyup"},{"tick":10274,"key":"a","type":"keydown"},{"tick":10284,"key":"right","type":"keyup"},{"tick":10292,"key":"up","type":"keydown"},{"tick":10306,"key":"a","type":"keyup"},{"tick":10348,"key":"right","type":"keydown"},{"tick":10388,"key":"right","type":"keyup"},{"tick":11749,"key":"up","type":"keyup"},{"tick":11751,"key":"left","type":"keydown"},{"tick":11789,"key":"a","type":"keydown"},{"tick":11817,"key":"a","type":"keyup"},{"tick":11851,"key":"left","type":"keyup"},{"tick":11889,"key":"left","type":"keydown"},{"tick":11905,"key":"a","type":"keydown"},{"tick":11937,"key":"up","type":"keydown"},{"tick":11961,"key":"a","type":"keyup"},{"tick":11983,"key":"left","type":"keyup"},{"tick":12151,"key":"right","type":"keydown"},{"tick":12159,"key":"up","type":"keyup"},{"tick":12207,"key":"a","type":"keydown"},{"tick":12227,"key":"up","type":"keydown"},{"tick":12235,"key":"right","type":"keyup"},{"tick":12241,"key":"a","type":"keyup"},{"tick":12281,"key":"right","type":"keydown"},{"tick":12315,"key":"right","type":"keyup"},{"tick":13549,"key":"up","type":"keyup"},{"tick":13589,"key":"up","type":"keydown"},{"tick":14001,"key":"up","type":"keyup"},{"tick":14017,"key":"down","type":"keydown"},{"tick":14173,"key":"a","type":"keydown"},{"tick":14175,"key":"down","type":"keyup"},{"tick":14185,"key":"a","type":"keyup"},{"tick":14223,"key":"up","type":"keydown"},{"tick":14253,"key":"up","type":"keyup"},{"tick":14269,"key":"down","type":"keydown"},{"tick":14309,"key":"down","type":"keyup"},{"tick":14319,"key":"right","type":"keydown"},{"tick":14331,"key":"a","type":"keydown"},{"tick":14341,"key":"a","type":"keyup"},{"tick":14359,"key":"up","type":"keydown"},{"tick":14421,"key":"right","type":"keyup"},{"tick":15727,"key":"left","type":"keydown"},{"tick":15727,"key":"up","type":"keyup"},{"tick":15779,"key":"a","type":"keydown"},{"tick":15809,"key":"up","type":"keydown"},{"tick":15817,"key":"a","type":"keyup"},{"tick":15829,"key":"left","type":"keyup"},{"tick":16155,"key":"right","type":"keydown"},{"tick":16157,"key":"up","type":"keyup"},{"tick":16237,"key":"a","type":"keydown"},{"tick":16259,"key":"a","type":"keyup"},{"tick":16317,"key":"a","type":"keydown"},{"tick":16337,"key":"a","type":"keyup"},{"tick":16431,"key":"a","type":"keydown"},{"tick":16519,"key":"a","type":"keyup"},{"tick":16545,"key":"a","type":"keydown"},{"tick":16563,"key":"a","type":"keyup"},{"tick":16627,"key":"a","type":"keydown"},{"tick":16651,"key":"a","type":"keyup"},{"tick":16723,"key":"a","type":"keydown"},{"tick":16735,"key":"a","type":"keyup"},{"tick":16799,"key":"right","type":"keyup"},{"tick":17019,"key":"right","type":"keydown"},{"tick":17345,"key":"a","type":"keydown"},{"tick":17355,"key":"a","type":"keyup"},{"tick":17411,"key":"right","type":"keyup"},{"tick":17831,"key":"right","type":"keydown"},{"tick":17963,"key":"right","type":"keyup"}] -------------------------------------------------------------------------------- /test/system/input/level-flashman.json: -------------------------------------------------------------------------------- 1 | [{"tick":372,"key":"right","type":"keydown"},{"tick":1012,"key":"right","type":"keyup"},{"tick":1036,"key":"right","type":"keydown"},{"tick":1042,"key":"right","type":"keyup"},{"tick":1046,"key":"right","type":"keydown"},{"tick":1058,"key":"a","type":"keydown"},{"tick":1106,"key":"a","type":"keyup"},{"tick":1550,"key":"b","type":"keydown"},{"tick":1560,"key":"b","type":"keyup"},{"tick":1590,"key":"right","type":"keyup"},{"tick":1632,"key":"left","type":"keydown"},{"tick":2156,"key":"right","type":"keydown"},{"tick":2158,"key":"left","type":"keyup"},{"tick":2906,"key":"right","type":"keyup"},{"tick":2908,"key":"left","type":"keydown"},{"tick":2948,"key":"a","type":"keydown"},{"tick":2948,"key":"left","type":"keyup"},{"tick":2952,"key":"right","type":"keydown"},{"tick":2996,"key":"a","type":"keyup"},{"tick":3080,"key":"a","type":"keydown"},{"tick":3122,"key":"a","type":"keyup"},{"tick":3308,"key":"right","type":"keyup"},{"tick":3326,"key":"right","type":"keydown"},{"tick":3694,"key":"a","type":"keydown"},{"tick":3698,"key":"right","type":"keyup"},{"tick":3702,"key":"left","type":"keydown"},{"tick":3746,"key":"a","type":"keyup"},{"tick":3800,"key":"a","type":"keydown"},{"tick":3808,"key":"right","type":"keydown"},{"tick":3810,"key":"left","type":"keyup"},{"tick":3850,"key":"a","type":"keyup"},{"tick":3982,"key":"right","type":"keyup"},{"tick":3988,"key":"left","type":"keydown"},{"tick":4006,"key":"left","type":"keyup"},{"tick":4082,"key":"left","type":"keydown"},{"tick":4278,"key":"right","type":"keydown"},{"tick":4280,"key":"left","type":"keyup"},{"tick":4516,"key":"right","type":"keyup"},{"tick":4558,"key":"left","type":"keydown"},{"tick":4708,"key":"left","type":"keyup"},{"tick":4716,"key":"right","type":"keydown"},{"tick":4740,"key":"right","type":"keyup"},{"tick":4790,"key":"right","type":"keydown"},{"tick":5026,"key":"right","type":"keyup"},{"tick":5090,"key":"left","type":"keydown"},{"tick":5110,"key":"left","type":"keyup"},{"tick":5138,"key":"left","type":"keydown"},{"tick":5166,"key":"a","type":"keydown"},{"tick":5186,"key":"a","type":"keyup"},{"tick":5236,"key":"a","type":"keydown"},{"tick":5238,"key":"a","type":"keyup"},{"tick":5322,"key":"left","type":"keyup"},{"tick":5322,"key":"right","type":"keydown"},{"tick":5702,"key":"a","type":"keydown"},{"tick":5736,"key":"a","type":"keyup"},{"tick":5794,"key":"a","type":"keydown"},{"tick":5820,"key":"a","type":"keyup"},{"tick":5882,"key":"a","type":"keydown"},{"tick":5898,"key":"a","type":"keyup"},{"tick":5936,"key":"right","type":"keyup"},{"tick":5948,"key":"right","type":"keydown"},{"tick":5962,"key":"a","type":"keydown"},{"tick":5980,"key":"a","type":"keyup"},{"tick":6026,"key":"right","type":"keyup"},{"tick":6036,"key":"right","type":"keydown"},{"tick":6046,"key":"a","type":"keydown"},{"tick":6060,"key":"a","type":"keyup"},{"tick":6120,"key":"a","type":"keydown"},{"tick":6130,"key":"a","type":"keyup"},{"tick":6166,"key":"right","type":"keyup"},{"tick":6172,"key":"right","type":"keydown"},{"tick":6186,"key":"a","type":"keydown"},{"tick":6280,"key":"a","type":"keyup"},{"tick":6300,"key":"a","type":"keydown"},{"tick":6316,"key":"a","type":"keyup"},{"tick":6406,"key":"left","type":"keydown"},{"tick":6410,"key":"right","type":"keyup"},{"tick":6506,"key":"right","type":"keydown"},{"tick":6508,"key":"left","type":"keyup"},{"tick":6598,"key":"a","type":"keydown"},{"tick":6652,"key":"a","type":"keyup"},{"tick":6964,"key":"a","type":"keydown"},{"tick":7014,"key":"right","type":"keyup"},{"tick":7020,"key":"left","type":"keydown"},{"tick":7020,"key":"a","type":"keyup"},{"tick":7044,"key":"left","type":"keyup"},{"tick":7062,"key":"a","type":"keydown"},{"tick":7066,"key":"left","type":"keydown"},{"tick":7096,"key":"a","type":"keyup"},{"tick":7120,"key":"left","type":"keyup"},{"tick":7134,"key":"right","type":"keydown"},{"tick":7166,"key":"a","type":"keydown"},{"tick":7200,"key":"a","type":"keyup"},{"tick":7206,"key":"right","type":"keyup"},{"tick":7210,"key":"left","type":"keydown"},{"tick":7222,"key":"a","type":"keydown"},{"tick":7276,"key":"a","type":"keyup"},{"tick":7298,"key":"left","type":"keyup"},{"tick":7300,"key":"right","type":"keydown"},{"tick":7348,"key":"a","type":"keydown"},{"tick":7386,"key":"a","type":"keyup"},{"tick":7392,"key":"left","type":"keydown"},{"tick":7392,"key":"right","type":"keyup"},{"tick":7416,"key":"a","type":"keydown"},{"tick":7458,"key":"a","type":"keyup"},{"tick":7488,"key":"right","type":"keydown"},{"tick":7488,"key":"left","type":"keyup"},{"tick":7536,"key":"a","type":"keydown"},{"tick":7602,"key":"a","type":"keyup"},{"tick":7696,"key":"a","type":"keydown"},{"tick":7744,"key":"a","type":"keyup"},{"tick":7796,"key":"right","type":"keyup"},{"tick":8102,"key":"right","type":"keydown"},{"tick":8366,"key":"a","type":"keydown"},{"tick":8380,"key":"a","type":"keyup"},{"tick":8440,"key":"right","type":"keyup"},{"tick":8798,"key":"right","type":"keydown"},{"tick":8816,"key":"a","type":"keydown"},{"tick":8828,"key":"a","type":"keyup"},{"tick":8858,"key":"right","type":"keyup"},{"tick":8958,"key":"right","type":"keydown"},{"tick":8972,"key":"a","type":"keydown"},{"tick":8982,"key":"a","type":"keyup"},{"tick":9016,"key":"right","type":"keyup"},{"tick":9020,"key":"right","type":"keydown"},{"tick":9034,"key":"a","type":"keydown"},{"tick":9046,"key":"a","type":"keyup"},{"tick":9078,"key":"right","type":"keyup"},{"tick":9102,"key":"right","type":"keydown"},{"tick":9114,"key":"right","type":"keyup"}] -------------------------------------------------------------------------------- /test/system/input/level-quickman.json: -------------------------------------------------------------------------------- 1 | [{"tick":306,"key":"right","type":"keydown"},{"tick":380,"key":"right","type":"keyup"},{"tick":412,"key":"left","type":"keydown"},{"tick":442,"key":"left","type":"keyup"},{"tick":460,"key":"left","type":"keydown"},{"tick":582,"key":"left","type":"keyup"},{"tick":598,"key":"left","type":"keydown"},{"tick":656,"key":"left","type":"keyup"},{"tick":676,"key":"right","type":"keydown"},{"tick":906,"key":"right","type":"keyup"},{"tick":906,"key":"left","type":"keydown"},{"tick":1112,"key":"left","type":"keyup"},{"tick":1134,"key":"right","type":"keydown"},{"tick":1292,"key":"right","type":"keyup"},{"tick":1298,"key":"left","type":"keydown"},{"tick":1438,"key":"left","type":"keyup"},{"tick":1438,"key":"right","type":"keydown"},{"tick":1528,"key":"left","type":"keydown"},{"tick":1532,"key":"right","type":"keyup"},{"tick":1574,"key":"right","type":"keydown"},{"tick":1576,"key":"left","type":"keyup"},{"tick":1638,"key":"left","type":"keydown"},{"tick":1638,"key":"right","type":"keyup"},{"tick":1800,"key":"left","type":"keyup"},{"tick":1808,"key":"right","type":"keydown"},{"tick":1822,"key":"right","type":"keyup"},{"tick":1866,"key":"right","type":"keydown"},{"tick":2098,"key":"right","type":"keyup"},{"tick":2156,"key":"right","type":"keydown"},{"tick":2170,"key":"a","type":"keydown"},{"tick":2186,"key":"a","type":"keyup"},{"tick":2210,"key":"right","type":"keyup"},{"tick":2222,"key":"right","type":"keydown"},{"tick":2260,"key":"a","type":"keydown"},{"tick":2282,"key":"a","type":"keyup"},{"tick":2426,"key":"right","type":"keyup"},{"tick":2466,"key":"right","type":"keydown"},{"tick":2556,"key":"a","type":"keydown"},{"tick":2582,"key":"a","type":"keyup"},{"tick":2654,"key":"a","type":"keydown"},{"tick":2674,"key":"a","type":"keyup"},{"tick":2934,"key":"a","type":"keydown"},{"tick":2960,"key":"a","type":"keyup"},{"tick":3192,"key":"a","type":"keydown"},{"tick":3202,"key":"a","type":"keyup"},{"tick":3544,"key":"right","type":"keyup"},{"tick":3552,"key":"left","type":"keydown"},{"tick":3568,"key":"left","type":"keyup"},{"tick":3634,"key":"left","type":"keydown"},{"tick":3718,"key":"a","type":"keydown"},{"tick":3742,"key":"a","type":"keyup"},{"tick":3818,"key":"right","type":"keydown"},{"tick":3822,"key":"left","type":"keyup"},{"tick":3932,"key":"right","type":"keyup"},{"tick":3934,"key":"left","type":"keydown"},{"tick":4128,"key":"left","type":"keyup"},{"tick":4128,"key":"right","type":"keydown"},{"tick":4246,"key":"right","type":"keyup"},{"tick":4252,"key":"left","type":"keydown"},{"tick":4364,"key":"left","type":"keyup"},{"tick":4370,"key":"right","type":"keydown"},{"tick":4416,"key":"right","type":"keyup"},{"tick":4444,"key":"right","type":"keydown"},{"tick":4616,"key":"right","type":"keyup"},{"tick":4618,"key":"left","type":"keydown"},{"tick":4726,"key":"left","type":"keyup"},{"tick":4792,"key":"left","type":"keydown"},{"tick":4850,"key":"left","type":"keyup"},{"tick":4854,"key":"right","type":"keydown"},{"tick":4870,"key":"right","type":"keyup"},{"tick":4898,"key":"right","type":"keydown"},{"tick":4994,"key":"right","type":"keyup"},{"tick":5004,"key":"left","type":"keydown"},{"tick":5088,"key":"left","type":"keyup"},{"tick":5090,"key":"right","type":"keydown"},{"tick":5186,"key":"right","type":"keyup"},{"tick":5206,"key":"left","type":"keydown"},{"tick":5358,"key":"left","type":"keyup"},{"tick":5358,"key":"right","type":"keydown"},{"tick":5736,"key":"a","type":"keydown"},{"tick":5812,"key":"a","type":"keyup"},{"tick":5872,"key":"a","type":"keydown"},{"tick":5924,"key":"a","type":"keyup"},{"tick":5996,"key":"a","type":"keydown"},{"tick":6024,"key":"a","type":"keyup"},{"tick":6120,"key":"a","type":"keydown"},{"tick":6154,"key":"a","type":"keyup"},{"tick":6256,"key":"a","type":"keydown"},{"tick":6280,"key":"a","type":"keyup"},{"tick":6318,"key":"a","type":"keydown"},{"tick":6336,"key":"a","type":"keyup"},{"tick":6396,"key":"right","type":"keyup"},{"tick":6698,"key":"right","type":"keydown"},{"tick":6964,"key":"a","type":"keydown"},{"tick":6974,"key":"a","type":"keyup"},{"tick":7018,"key":"right","type":"keyup"},{"tick":7410,"key":"right","type":"keydown"},{"tick":7428,"key":"a","type":"keydown"},{"tick":7442,"key":"a","type":"keyup"},{"tick":7494,"key":"a","type":"keydown"},{"tick":7506,"key":"a","type":"keyup"},{"tick":7586,"key":"right","type":"keyup"}] -------------------------------------------------------------------------------- /test/system/tests/game.js: -------------------------------------------------------------------------------- 1 | describe('Megaman 2', function() { 2 | describe.skip('when loading game XML', () => { 3 | let game, loader, entrypoint; 4 | before(done => { 5 | game = new Engine.Game; 6 | loader = new XMLLoader(game); 7 | loader.loadGame('/resource/Megaman2.xml').then(_entrypoint => { 8 | entrypoint = _entrypoint; 9 | done(); 10 | }); 11 | }); 12 | 13 | it('entrypoint is Intro', () => { 14 | expect(entrypoint).to.be('Intro'); 15 | }); 16 | 17 | it('player has 28 health max', () => { 18 | expect(game.player.character.health.energy.max).to.be(28); 19 | }); 20 | 21 | it('player has 0 health min', () => { 22 | expect(game.player.character.health.energy.min).to.be(0); 23 | }); 24 | 25 | describe('Loader', () => { 26 | it('should load a scene by name', function(done) { 27 | const game = new Engine.Game; 28 | const loader = new XMLLoader(game); 29 | loader.loadGame('/dist/resource/Megaman2.xml') 30 | .then(() => { 31 | return loader.loadSceneByName('Intro'); 32 | }) 33 | .then(scene => { 34 | expect(scene.name).to.be('Intro'); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/system/tests/intro.js: -------------------------------------------------------------------------------- 1 | describe('Intro', function() { 2 | before(function(done) { 3 | env.load('Intro').then(scene => { 4 | env.scene(scene); 5 | done(); 6 | }); 7 | }); 8 | 9 | after(function() { 10 | env.game.unsetScene(); 11 | }); 12 | 13 | it('should have an invisible logo', function() { 14 | const logo = env.game.scene.world.getObject('logo'); 15 | expect(logo.model.material.opacity).to.be(0); 16 | expect(logo.position.z).to.be(-800); 17 | }); 18 | 19 | it('should have three invisible text layers', function() { 20 | const world = env.game.scene.world; 21 | const objects = [ 22 | world.getObject('story1'), 23 | world.getObject('story2'), 24 | world.getObject('story3'), 25 | ]; 26 | 27 | objects.forEach(o => { 28 | expect(o.model.material.opacity).to.be(0); 29 | }); 30 | 31 | this.objects = objects; 32 | }); 33 | 34 | it('layer 1 should be visible after 2.5 seconds', function(done) { 35 | env.goToTick(300).then(() => { 36 | expect(this.objects[0].model.material.opacity).to.be(1); 37 | expect(this.objects[1].model.material.opacity).to.be(0); 38 | done(); 39 | }); 40 | }); 41 | 42 | it('layer 2 should be visible after 8 seconds', function(done) { 43 | env.goToTick(1260).then(() => { 44 | expect(this.objects[0].model.material.opacity).to.be(0); 45 | expect(this.objects[1].model.material.opacity).to.be(1); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('layer 3 should be visible after 8 seconds', function(done) { 51 | env.goToTick(2220).then(() => { 52 | expect(this.objects[1].model.material.opacity).to.be(0); 53 | expect(this.objects[2].model.material.opacity).to.be(1); 54 | done(); 55 | }); 56 | }); 57 | 58 | it('layer 3 should be hidden after 5 seconds', function(done) { 59 | env.goToTick(2820).then(() => { 60 | expect(this.objects[2].model.material.opacity).to.be(0); 61 | done(); 62 | }); 63 | }); 64 | 65 | it('camera should be scrolled after 12 seconds', function(done) { 66 | env.goToTick(4260).then(() => { 67 | expect(env.game.scene.camera.position.y).to.be.within(400, 500); 68 | done(); 69 | }); 70 | }); 71 | 72 | it('camera should reach top of skyscraper', function(done) { 73 | env.goToTick(4500).then(() => { 74 | expect(env.game.scene.camera.position.y).to.be.within(570, 580); 75 | done(); 76 | }); 77 | }); 78 | 79 | it('logo should still be invisible', function() { 80 | const logo = env.game.scene.world.getObject('logo'); 81 | expect(logo.model.material.opacity).to.be(0); 82 | expect(logo.position.z).to.be(-800); 83 | }); 84 | 85 | it('logotype should be revealed', function(done) { 86 | env.goToTick(4800).then(() => { 87 | const logo = env.game.scene.world.getObject('logo'); 88 | expect(logo.model.material.opacity).to.be(1); 89 | expect(logo.position.z).to.be(0); 90 | done(); 91 | }); 92 | }); 93 | 94 | it('should go to scene select when pressing start', function(done) { 95 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 96 | done(); 97 | }); 98 | env.tap('start'); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/system/tests/level-1-airman.js: -------------------------------------------------------------------------------- 1 | describe('Airman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Airman stage when pressing up + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Airman'); 18 | started = true; 19 | }); 20 | env.tap('up start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | let player; 27 | 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Airman'), 31 | ]).then(([scene]) => { 32 | env.scene(scene); 33 | player = env.game.player.character; 34 | done(); 35 | }); 36 | }); 37 | 38 | after(function() { 39 | env.game.unsetScene(); 40 | }); 41 | 42 | describe('at start of stage', function() { 43 | it('player should be off screen', function() { 44 | expect(player.position) 45 | .to.eql({ x: 128, y: 80, z: 0}); 46 | }); 47 | }); 48 | 49 | describe('after ready text', function() { 50 | before(() => env.goToTime(2.5)); 51 | 52 | it('player should be at start position', function() { 53 | expect(player.position) 54 | .to.eql({x: 128, y: -120, z: 0}); 55 | }); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/system/tests/level-2-bubbleman.js: -------------------------------------------------------------------------------- 1 | describe('Bubbleman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Bubbleman stage when pressing up + left + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Bubbleman'); 18 | started = true; 19 | }); 20 | env.tap('up left start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | before(function(done) { 27 | Promise.all([ 28 | env.load('Bubbleman'), 29 | env.loadInput('/test/system/input/level-bubbleman.json'), 30 | ]).then(([scene, log]) => { 31 | env.scene(scene); 32 | env.playInput(log); 33 | done(); 34 | }); 35 | }); 36 | 37 | after(function() { 38 | env.game.unsetScene(); 39 | }); 40 | 41 | it('should hide player off screen on start', function() { 42 | expect(env.game.player.character.position.y) 43 | .to.be.above(env.game.scene.camera.position.y + 120); 44 | }); 45 | 46 | it('should teleport player to first checkpoint', function(done) { 47 | env.goToTime(2.5).then(() => { 48 | expect(env.game.player.character.position) 49 | .to.eql({x: 128, y: -101, z: 0}); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should have blocks that are solid and can be jumped from', function(done) { 55 | env.goToTick(1221).then(() => { 56 | expect(env.game.player.character.position) 57 | .to.eql({x: 772.7280559184165, y: -54.61095471377428, z: 0}); 58 | done(); 59 | }); 60 | }); 61 | 62 | it('enables player to get to other side of falling blocks', function(done) { 63 | env.goToTick(1609).then(() => { 64 | expect(env.game.player.character.position) 65 | .to.eql({x: 1054.7769542709652, y: -69, z: 0}); 66 | done(); 67 | }); 68 | }); 69 | 70 | it('falling blocks should have fallen away', function() { 71 | const blocks = env.game.scene.world.getObjects('falling-block'); 72 | expect(blocks[0].position.y).to.be.below(-1000); 73 | expect(blocks[4].position.y).to.be.below(-300); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/system/tests/level-3-crashman.js: -------------------------------------------------------------------------------- 1 | describe('Crashman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Crashman stage when pressing down + right + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Crashman'); 18 | started = true; 19 | }); 20 | env.tap('down right start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | let player; 27 | 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Crashman'), 31 | env.loadInput('/test/system/input/level-crashman.json'), 32 | ]).then(([scene, log]) => { 33 | env.scene(scene); 34 | env.playInput(log); 35 | player = env.game.player.character; 36 | done(); 37 | }).catch(done); 38 | }); 39 | 40 | describe('at start of level', () => { 41 | it('player is off screen', function() { 42 | expect(player.position).to.eql({x: 128, y: 256, z: 0}); 43 | }); 44 | 45 | it('camera is at start of level', function() { 46 | expect(env.game.scene.camera.position) 47 | .to.eql({x: 128, y: 120, z: 150}); 48 | }); 49 | }); 50 | 51 | describe('after 2.1 seconds', () => { 52 | before(done => { 53 | env.goToTime(2.2).then(done); 54 | }); 55 | 56 | it('player teleport is active', () => { 57 | expect(player.teleport.state).to.be(player.teleport.STATE_GO); 58 | }); 59 | 60 | it('player is teleporting down', () => { 61 | expect(player.position.y).to.be.within(218, 219); 62 | }); 63 | }); 64 | 65 | describe('after 2.5 seconds', () => { 66 | before(done => { 67 | env.goToTime(2.6).then(done); 68 | }); 69 | 70 | it('player is on the ground', () => { 71 | expect(player.position).to.eql({x: 128, y: 59, z: 0}); 72 | }); 73 | }); 74 | 75 | describe.skip('after 90 seconds', () => { 76 | before(done => { 77 | env.goToTime(90).then(done); 78 | }); 79 | 80 | it('player has reached end', () => { 81 | const pos = env.game.player.character.position; 82 | expect(pos.x).to.be.within(1288, 1536); 83 | expect(pos.y).to.be.within(3392, 3528); 84 | }); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/system/tests/level-4-flashman.js: -------------------------------------------------------------------------------- 1 | describe('Flashman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Flashman stage when pressing down + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Flashman'); 18 | started = true; 19 | }); 20 | env.tap('down start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | describe('Level', function() { 26 | before(function(done) { 27 | Promise.all([ 28 | env.load('Flashman'), 29 | env.loadInput('/test/system/input/level-flashman.json'), 30 | ]).then(([scene, log]) => { 31 | env.scene(scene); 32 | env.playInput(log); 33 | done(); 34 | }).catch(done); 35 | }); 36 | 37 | after(function() { 38 | env.game.unsetScene(); 39 | }); 40 | 41 | describe('at start of stage', function() { 42 | it('player should be off screen', function() { 43 | expect(env.game.player.character.position) 44 | .to.eql({x: 136, y: 48, z: 0}); 45 | }); 46 | }); 47 | 48 | describe('after ready text', function() { 49 | before(() => env.goToTime(2.5)); 50 | 51 | it('player should be at start position', function() { 52 | expect(env.game.player.character.position) 53 | .to.eql({x: 136, y: -152, z: 0}); 54 | }); 55 | }); 56 | 57 | describe.skip('after 78 seconds', function() { 58 | before(done => env.goToTime(80).then(done)); 59 | 60 | it('player should reach a position', function() { 61 | const pos = env.game.player.character.position; 62 | expect(pos.x).to.be.within(3100, 3300); 63 | expect(pos.y).to.be.within(-1840, -1800); 64 | }); 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/system/tests/level-5-heatman.js: -------------------------------------------------------------------------------- 1 | describe('Heatman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Heatman stage when pressing left + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Heatman'); 18 | started = true; 19 | }); 20 | env.tap('left start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | let player; 27 | 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Heatman'), 31 | ]).then(([scene]) => { 32 | env.scene(scene); 33 | player = env.game.player.character; 34 | done(); 35 | }); 36 | }); 37 | 38 | after(function() { 39 | env.game.unsetScene(); 40 | }); 41 | 42 | describe('at start of level', () => { 43 | it('player is off screen', function() { 44 | expect(player.position).to.eql({x: 136, y: 35, z: 0}); 45 | }); 46 | 47 | it('camera is at start of level', function() { 48 | expect(env.game.scene.camera.position) 49 | .to.eql({x: 180, y: -120, z: 150}); 50 | }); 51 | }); 52 | 53 | describe('after 2.2 seconds', () => { 54 | before(done => { 55 | env.goToTime(2.2).then(done); 56 | }); 57 | 58 | it('player teleport is active', () => { 59 | expect(player.teleport.state).to.be(player.teleport.STATE_GO); 60 | }); 61 | 62 | it('player is teleporting down', () => { 63 | expect(player.position.y).to.be.within(-3, -2); 64 | }); 65 | }); 66 | 67 | describe('after 2.6 seconds', () => { 68 | before(done => { 69 | env.goToTime(2.6).then(done); 70 | }); 71 | 72 | it('player is on the ground', () => { 73 | expect(player.position) 74 | .to.eql({ x: 136, y: -165, z: 0 }); 75 | }); 76 | }); 77 | 78 | describe('Level succession', function() { 79 | it.skip('disappearing blocks part 1 should be solvable', function() { 80 | }); 81 | 82 | it.skip('disappearing blocks part 2 should be solvable', function() { 83 | }); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/system/tests/level-6-metalman.js: -------------------------------------------------------------------------------- 1 | describe('Metalman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Metalman stage when pressing down + left + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Metalman'); 18 | started = true; 19 | }); 20 | env.tap('down left start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | let player; 27 | 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Metalman'), 31 | ]).then(([scene]) => { 32 | env.scene(scene); 33 | player = env.game.player.character; 34 | done(); 35 | }); 36 | }); 37 | 38 | after(function() { 39 | env.game.unsetScene(); 40 | }); 41 | 42 | describe('at start of stage', function() { 43 | it('player should be off screen', function() { 44 | expect(player.position) 45 | .to.eql({x: 128, y: 32, z: 0}); 46 | }); 47 | }); 48 | 49 | describe('after ready text', function() { 50 | before(() => env.goToTime(2.5)); 51 | 52 | it('player should be at start position', function() { 53 | expect(player.position) 54 | .to.eql({x: 128, y: -168, z: 0}); 55 | }); 56 | }); 57 | 58 | describe.skip('one second after start', function() { 59 | before(done => env.waitTime(1).then(done)); 60 | 61 | it('player should have been moved by conveyor', function() { 62 | expect(player.position) 63 | .to.eql({x: 128, y: -168, z: 0}); 64 | }); 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/system/tests/level-7-quickman.js: -------------------------------------------------------------------------------- 1 | describe('Quickman Level', function() { 2 | this.timeout(120000); 3 | 4 | context('StageSelect', function() { 5 | before(function(done) { 6 | env.load('StageSelect').then(scene => { 7 | env.scene(scene); 8 | done(); 9 | }); 10 | }); 11 | 12 | after(function() { 13 | env.game.unsetScene(); 14 | }); 15 | 16 | it('starts Quickman stage when pressing up + right + start', function(done) { 17 | let started = false; 18 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 19 | expect(scene.name).to.be('Quickman'); 20 | started = true; 21 | }); 22 | env.tap('up right start'); 23 | env.waitUntil(() => started).then(done); 24 | }); 25 | }); 26 | 27 | context('Level', function() { 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Quickman'), 31 | env.loadInput('/test/system/input/level-quickman.json'), 32 | ]).then(([scene, log]) => { 33 | env.scene(scene); 34 | env.playInput(log); 35 | done(); 36 | }); 37 | }); 38 | 39 | after(function() { 40 | env.game.unsetScene(); 41 | }); 42 | 43 | it('camera placed correctly', function() { 44 | expect(env.game.scene.camera.position) 45 | .to.eql({x: 128, y: -120, z: 150}); 46 | }); 47 | 48 | it('should hide player off screen on start', function() { 49 | expect(env.game.player.character.position) 50 | .to.eql({x: 128, y: 48, z: 0}); 51 | }); 52 | 53 | it('should teleport player to first checkpoint', function(done) { 54 | env.goToTime(2.5).then(() => { 55 | expect(env.game.player.character.position) 56 | .to.eql({x: 128, y: -152, z: 0}); 57 | done(); 58 | }); 59 | }); 60 | 61 | it('scene should go dark when entering dark area', function(done) { 62 | env.goToTime(19).then(() => { 63 | expect(env.game.scene.world.ambientLight.color) 64 | .to.eql({r: 0.1, g: 0.03, b: 0.03}); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('scene should go light when exiting dark area', function(done) { 70 | env.goToTime(31).then(() => { 71 | expect(env.game.scene.world.ambientLight.color) 72 | .to.eql({r: 1, g: 1, b: 1}); 73 | done(); 74 | }); 75 | }); 76 | 77 | it('player should reach end', function(done) { 78 | env.goToTime(64).then(() => { 79 | const pos = env.game.player.character.position; 80 | expect(pos.x).to.be.within(2120, 2180); 81 | expect(pos.y).to.be.within(-3780, -3720); 82 | done(); 83 | }); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/system/tests/level-8-woodman.js: -------------------------------------------------------------------------------- 1 | describe('Woodman Level', function() { 2 | context('StageSelect', function() { 3 | before(function(done) { 4 | env.load('StageSelect').then(scene => { 5 | env.scene(scene); 6 | done(); 7 | }); 8 | }); 9 | 10 | after(function() { 11 | env.game.unsetScene(); 12 | }); 13 | 14 | it('starts Woodman stage when pressing right + start', function(done) { 15 | let started = false; 16 | env.game.events.once(env.game.EVENT_SCENE_SET, scene => { 17 | expect(scene.name).to.be('Woodman'); 18 | started = true; 19 | }); 20 | env.tap('right start'); 21 | env.waitUntil(() => started).then(done); 22 | }); 23 | }); 24 | 25 | context('Level', function() { 26 | let player; 27 | 28 | before(function(done) { 29 | Promise.all([ 30 | env.load('Woodman'), 31 | ]).then(([scene]) => { 32 | env.scene(scene); 33 | player = env.game.player.character; 34 | done(); 35 | }); 36 | }); 37 | 38 | after(function() { 39 | env.game.unsetScene(); 40 | }); 41 | 42 | describe('at start of stage', function() { 43 | it('player should be off screen', function() { 44 | expect(player.position) 45 | .to.eql({x: 128, y: 16, z: 0}); 46 | }); 47 | }); 48 | 49 | describe('after ready text', function() { 50 | before(() => env.goToTime(2.5)); 51 | 52 | it('player should be at start position', function() { 53 | expect(player.position) 54 | .to.eql({x: 128, y: -184, z: 0}); 55 | }); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/system/tests/level-9-drwily1.js: -------------------------------------------------------------------------------- 1 | describe('Dr Wily Level 1', function() { 2 | let player; 3 | 4 | context('Level', function() { 5 | before(function(done) { 6 | Promise.all([ 7 | env.load('DrWily1'), 8 | ]).then(([scene]) => { 9 | env.scene(scene); 10 | player = env.game.player.character; 11 | done(); 12 | }); 13 | }); 14 | 15 | after(function() { 16 | env.game.unsetScene(); 17 | }); 18 | 19 | after(function() { 20 | env.game.unsetScene(); 21 | }); 22 | 23 | describe('at start of stage', function() { 24 | it('player should be off screen', function() { 25 | expect(player.position) 26 | .to.eql({x: 128, y: 248, z: 0}); 27 | }); 28 | 29 | it('camera is at start of level', function() { 30 | expect(env.game.scene.camera.position) 31 | .to.eql({x: 128, y: 112, z: 150}); 32 | }); 33 | }); 34 | 35 | describe('after ready text', function() { 36 | before(() => env.goToTime(2.5)); 37 | 38 | it('player should be at start position', function() { 39 | expect(player.position) 40 | .to.eql({x: 128, y: 48, z: 0}); 41 | }); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/system/tests/stage-select.js: -------------------------------------------------------------------------------- 1 | describe('Stage Select', function() { 2 | let camera, scene; 3 | 4 | beforeEach(done => { 5 | env.load('StageSelect').then(_scene => { 6 | scene = _scene; 7 | env.scene(_scene); 8 | camera = env.game.scene.camera; 9 | done(); 10 | }); 11 | }); 12 | 13 | afterEach(() => { 14 | env.game.unsetScene(); 15 | }); 16 | 17 | describe('At start', () => { 18 | it('is zoomed in', () => { 19 | expect(camera.position.z).to.be(40); 20 | }); 21 | 22 | it('indicator is in center', () => { 23 | expect(scene.world.getObject('indicator').position).to.eql({ 24 | x: 0, 25 | y: 8, 26 | z: 0.1 27 | }); 28 | }); 29 | 30 | describe('after 1 second', () => { 31 | beforeEach(() => env.waitTime(1)); 32 | 33 | it('is zoomed out', () => { 34 | expect(camera.position.z).to.be(140); 35 | }); 36 | }); 37 | }); 38 | 39 | describe('Indicator', () => { 40 | it('has blink trait', () => { 41 | const indicator = scene.world.getObject('indicator'); 42 | expect(indicator.blink.interval).to.be(0.25); 43 | }); 44 | }); 45 | 46 | const bosses = [ 47 | { 48 | keys: 'up', 49 | name: 'Airman', 50 | }, 51 | { 52 | keys: 'down', 53 | name: 'Flashman', 54 | }, 55 | { 56 | keys: 'left', 57 | name: 'Heatman', 58 | }, 59 | { 60 | keys: 'down left', 61 | name: 'Metalman', 62 | }, 63 | ]; 64 | 65 | bosses.forEach(boss => { 66 | describe(`when pressing ${boss.keys} and start`, () => { 67 | let spy = sinon.spy(); 68 | beforeEach(() => { 69 | const scene = env.game.scene; 70 | scene.events.once('stage-selected', spy); 71 | env.tap(boss.keys); 72 | env.tap('start'); 73 | }); 74 | 75 | it(`selects ${boss.name} stage`, () => { 76 | expect(spy.callCount).to.be(1); 77 | const stage = spy.lastCall.args[0]; 78 | expect(stage.scene).to.be(boss.name); 79 | }); 80 | 81 | describe('immediately after', () => { 82 | beforeEach(() => env.waitTime(0.1)); 83 | 84 | it('removes indicator', () => { 85 | expect(scene.world.getObject('indicator')).to.be(false); 86 | }); 87 | }); 88 | 89 | describe('after 2.5 seconds', () => { 90 | beforeEach(() => env.waitTime(2.5)); 91 | 92 | it('camera has moved to reveal boss', () => { 93 | expect(env.game.scene.camera.position).to.eql({ 94 | x: 0, 95 | y: 512, 96 | z: 140, 97 | }); 98 | }); 99 | 100 | it.skip('stars are behind podium'); 101 | 102 | it(`${boss.name} has been spawned in scene`, () => { 103 | const Boss = env.loader.resourceManager.get('entity', boss.name); 104 | const instance = env.game.scene.world.getObjects(boss.name)[0]; 105 | expect(instance).to.be.a(Boss); 106 | expect(instance.position.x).to.be(0); 107 | expect(instance.position.y).to.be.within(516, 518); 108 | }); 109 | 110 | describe('after 5 seconds', () => { 111 | let spy; 112 | 113 | beforeEach(done => { 114 | spy = sinon.spy(); 115 | env.game.events.once(env.game.EVENT_SCENE_SET, spy); 116 | env.waitUntil(() => spy.called).then(done); 117 | }); 118 | 119 | it(`has loaded ${boss.name} level`, () => { 120 | expect(spy.callCount).to.be(1); 121 | expect(spy.lastCall.args[0].name).to.be(boss.name); 122 | }); 123 | }); 124 | }); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /test/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | function resolve(...args) { 5 | return path.resolve(__dirname, ...args); 6 | } 7 | 8 | const config = { 9 | entry: { 10 | 'browser-env': resolve('browser-env.js'), 11 | }, 12 | output: { 13 | path: resolve('.'), 14 | filename: 'browser-test-build.js', 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.js$/, 20 | include: [ 21 | resolve('../src'), 22 | ], 23 | use: { 24 | loader: 'babel-loader', 25 | options: { 26 | presets: ['env'], 27 | /*plugins: [ 28 | require('transform-runtime'), 29 | require('transform-class-properties'), 30 | ],*/ 31 | }, 32 | }, 33 | }, 34 | ], 35 | }, 36 | }; 37 | 38 | module.exports = config; 39 | --------------------------------------------------------------------------------