├── .DS_Store ├── .github ├── pull_request_template.md └── workflows │ └── node.js.yml ├── .gitignore ├── .idea ├── SuperMarioBros.iml ├── modules.xml ├── vcs.xml └── workspace.xml ├── .js ├── .vs ├── Mario │ └── v16 │ │ └── .suo └── slnx.sqlite ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── README.md ├── TiledJSONConverter ├── convert_tiled.exe └── convert_tiled.py ├── animator.js ├── assetmanager.js ├── audio ├── 1-up.mp3 ├── block.mp3 ├── bump.wav ├── coin.mp3 ├── fireball.mp3 ├── fireworks.mp3 ├── flagpole.mp3 ├── game-over.mp3 ├── level-clear.mp3 ├── life-lost.mp3 ├── pipe.mp3 ├── power-up-appears.mp3 ├── power-up.mp3 ├── small-jump.mp3 ├── stomp.mp3 └── super-jump.mp3 ├── background.js ├── boundingbox.js ├── bricks.js ├── convert_tiled.py ├── enemies.js ├── gameengine.js ├── index.html ├── items.js ├── levels.js ├── main.js ├── mario.js ├── music ├── castle-hurry.mp3 ├── castle.mp3 ├── invincible.mp3 ├── overworld-hurry.mp3 ├── overworld.mp3 ├── underworld-hurry.mp3 ├── underworld.mp3 ├── waterworld-hurry.mp3 └── waterworld.mp3 ├── package-lock.json ├── package.json ├── sceneManager.js ├── sprites ├── bricks.png ├── bricksolod.png ├── castle_big.png ├── coins.png ├── enemies.png ├── firebar_fire.png ├── flag.png ├── ground.png ├── items.png ├── luigi.png ├── mario.png ├── mariodead.png ├── mouse_wheel.png ├── stuff.png ├── tiles.png ├── title.png ├── title_mushroom.png ├── underground_bricksolod.png └── underground_stuff.png ├── test └── test.js ├── timer.js ├── transition.js └── util.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/.DS_Store -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request Description Here 2 | 3 | This is for documentation and readability purposes so that reviewers and future developers can follow your PR easier. 4 | 5 | Feel free to delete this and any prompts you didn't need to fill out for the pull request (makes the document less cluttered) 6 | 7 | Also Emoji Pull Requests will net imaginary and worthless brownie points 🚀 8 | 9 | **Related GitHub Issue Number/Thread:** (Delete this line if not needed) 10 | 11 | ## Features 12 | 13 | > Did you fully complete the assigned features? 14 | 15 | - [x] Fully completed 16 | - [ ] Partially completed 17 | 18 | > What features did you Complete? 19 | 20 | > What features were you supposed to complete but didn't? (If any) 21 | 22 | > What features did you complete beyond your assigned features? (If any) 23 | 24 | > How did you go about implementing your features? 25 | 26 | _Feel free to not fill this out if you don't think the feature is complex enough. This part is just really helpful for the maintainers to have when analyzing the code._ 27 | 28 | ## Bugs 29 | 30 | > Did you introduce any bugs or potential ones? 31 | 32 | - [ ] Yes 33 | - [x] No 34 | 35 | > If so, what bugs did you introduce or what do you think might be a cause for bugs? 36 | 37 | If possible please put file and line numbers to aid in the debugging process. 38 | 39 | > Did you fix any bugs outside of what you introduced? 40 | 41 | Please give line numbers to aid in the checking process. 42 | 43 | ## Contributors to the Feature 44 | 45 | - *Indicate the name of participants in this PR* 46 | 47 | ## Final check 48 | 49 | Have you filled out this document and gave the pull request a descriptive name (Up on top of this document and the GitHub PR title)? Answer *Yes* and that means you are good to go! Thank you for your contribution. 50 | 51 | - [ ] Yes 52 | - [ ] No 53 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.x, 12.x, 14.x, 15.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v1 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm ci 29 | - run: npm run build --if-present 30 | - run: npm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code config 2 | jsconfig.json 3 | 4 | # VS config 5 | /.vs/ 6 | 7 | 8 | node_modules 9 | .vs/Mario/v16/.suo 10 | -------------------------------------------------------------------------------- /.idea/SuperMarioBros.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 1644046051624 28 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.js: -------------------------------------------------------------------------------- 1 | class SceneManager { 2 | constructor(game) { 3 | this.game = game; 4 | this.game.camera = this; 5 | this.x = 0; 6 | this.score = 0; 7 | this.coins = 0; 8 | this.lives = 3; 9 | 10 | this.coinAnimation = new Animator(ASSET_MANAGER.getAsset("./sprites/coins.png"), 0, 160, 8, 8, 4, 0.2, 0, false, true); 11 | 12 | this.minimap = new Minimap(this.game, 1.5 * PARAMS.BLOCKWIDTH, 3.5 * PARAMS.BLOCKWIDTH, 224 * PARAMS.SCALE); 13 | 14 | this.mario = new Mario(this.game, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH); 15 | 16 | this.loadLevelOne(2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH); 17 | }; 18 | 19 | loadLevelOne(x,y) { 20 | this.game.entities = []; 21 | this.x = 0; 22 | 23 | let background = new BigHill(this.game, 0, 11.5 * PARAMS.BLOCKWIDTH); 24 | this.game.addEntity(background); 25 | background = new Bush(this.game, 11.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 3); 26 | this.game.addEntity(background); 27 | background = new Hill(this.game, 16 * PARAMS.BLOCKWIDTH, 12.75 * PARAMS.BLOCKWIDTH); 28 | this.game.addEntity(background); 29 | background = new Bush(this.game, 23.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1); 30 | this.game.addEntity(background); 31 | background = new Cloud(this.game, 8.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 1); 32 | this.game.addEntity(background); 33 | background = new Cloud(this.game, 19.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 1); 34 | this.game.addEntity(background); 35 | background = new Cloud(this.game, 27.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 3); 36 | this.game.addEntity(background); 37 | background = new Cloud(this.game, 36.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 2); 38 | this.game.addEntity(background); 39 | background = new Bush(this.game, 41.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 2); 40 | this.game.addEntity(background); 41 | background = new BigHill(this.game, 48 * PARAMS.BLOCKWIDTH, 11.5 * PARAMS.BLOCKWIDTH); 42 | this.game.addEntity(background); 43 | background = new Cloud(this.game, 56.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 1); 44 | this.game.addEntity(background); 45 | background = new Bush(this.game, 59.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 3); 46 | this.game.addEntity(background); 47 | background = new Hill(this.game, 64 * PARAMS.BLOCKWIDTH, 12.75 * PARAMS.BLOCKWIDTH); 48 | this.game.addEntity(background); 49 | background = new Cloud(this.game, 67.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 1); 50 | this.game.addEntity(background); 51 | background = new Bush(this.game, 71.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1); 52 | this.game.addEntity(background); 53 | background = new Cloud(this.game, 75.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 3); 54 | this.game.addEntity(background); 55 | background = new Cloud(this.game, 83.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 2); 56 | this.game.addEntity(background); 57 | background = new Bush(this.game, 89.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 2); 58 | this.game.addEntity(background); 59 | background = new BigHill(this.game, 95 * PARAMS.BLOCKWIDTH, 11.5 * PARAMS.BLOCKWIDTH); 60 | this.game.addEntity(background); 61 | background = new Cloud(this.game, 103.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 1); 62 | this.game.addEntity(background); 63 | background = new Bush(this.game, 106.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 3); 64 | this.game.addEntity(background); 65 | background = new Hill(this.game, 111 * PARAMS.BLOCKWIDTH, 12.75 * PARAMS.BLOCKWIDTH); 66 | this.game.addEntity(background); 67 | background = new Cloud(this.game, 114.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 1); 68 | this.game.addEntity(background); 69 | background = new Bush(this.game, 118.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1); 70 | this.game.addEntity(background); 71 | background = new Cloud(this.game, 122.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 3); 72 | this.game.addEntity(background); 73 | background = new Cloud(this.game, 131.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 2); 74 | this.game.addEntity(background); 75 | background = new Bush(this.game, 136.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 2); 76 | this.game.addEntity(background); 77 | background = new BigHill(this.game, 143 * PARAMS.BLOCKWIDTH, 11.5 * PARAMS.BLOCKWIDTH); 78 | this.game.addEntity(background); 79 | background = new Cloud(this.game, 151.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 1); 80 | this.game.addEntity(background); 81 | background = new Bush(this.game, 156.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1); 82 | this.game.addEntity(background); 83 | background = new Hill(this.game, 159 * PARAMS.BLOCKWIDTH, 12.75 * PARAMS.BLOCKWIDTH); 84 | this.game.addEntity(background); 85 | background = new Cloud(this.game, 162.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 1); 86 | this.game.addEntity(background); 87 | background = new Bush(this.game, 166.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1); 88 | this.game.addEntity(background); 89 | background = new Cloud(this.game, 170.5 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 3); 90 | this.game.addEntity(background); 91 | background = new Cloud(this.game, 179.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 2); 92 | this.game.addEntity(background); 93 | 94 | let ground = new Ground(this.game, 0, 14 * PARAMS.BLOCKWIDTH, 69 * PARAMS.BLOCKWIDTH); 95 | this.game.addEntity(ground); 96 | ground = new Ground(this.game, 71 * PARAMS.BLOCKWIDTH, 14 * PARAMS.BLOCKWIDTH, 15 * PARAMS.BLOCKWIDTH); 97 | this.game.addEntity(ground); 98 | ground = new Ground(this.game, 89 * PARAMS.BLOCKWIDTH, 14 * PARAMS.BLOCKWIDTH, 63 * PARAMS.BLOCKWIDTH); 99 | this.game.addEntity(ground); 100 | ground = new Ground(this.game, 154 * PARAMS.BLOCKWIDTH, 14 * PARAMS.BLOCKWIDTH, 69 * PARAMS.BLOCKWIDTH); 101 | this.game.addEntity(ground); 102 | 103 | let brick = new Brick(this.game, 20 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 104 | this.game.addEntity(brick); 105 | brick = new Brick(this.game, 22 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 106 | this.game.addEntity(brick); 107 | brick = new Brick(this.game, 24 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 108 | this.game.addEntity(brick); 109 | brick = new Brick(this.game, 77 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 110 | this.game.addEntity(brick); 111 | brick = new Brick(this.game, 79 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 112 | this.game.addEntity(brick); 113 | brick = new Brick(this.game, 80 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 114 | this.game.addEntity(brick); 115 | brick = new Brick(this.game, 81 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 116 | this.game.addEntity(brick); 117 | brick = new Brick(this.game, 82 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 118 | this.game.addEntity(brick); 119 | brick = new Brick(this.game, 83 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 120 | this.game.addEntity(brick); 121 | brick = new Brick(this.game, 84 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 122 | this.game.addEntity(brick); 123 | brick = new Brick(this.game, 85 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 124 | this.game.addEntity(brick); 125 | brick = new Brick(this.game, 86 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 126 | this.game.addEntity(brick); 127 | brick = new Brick(this.game, 87 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 128 | this.game.addEntity(brick); 129 | brick = new Brick(this.game, 90 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 130 | this.game.addEntity(brick); 131 | brick = new Brick(this.game, 91 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 132 | this.game.addEntity(brick); 133 | brick = new Brick(this.game, 92 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 134 | this.game.addEntity(brick); 135 | brick = new Brick(this.game, 93 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "Coins"); 136 | this.game.addEntity(brick); 137 | brick = new Brick(this.game, 99 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 138 | this.game.addEntity(brick); 139 | brick = new Brick(this.game, 100 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "Star"); 140 | this.game.addEntity(brick); 141 | brick = new Brick(this.game, 117 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 142 | this.game.addEntity(brick); 143 | brick = new Brick(this.game, 120 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 144 | this.game.addEntity(brick); 145 | brick = new Brick(this.game, 121 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 146 | this.game.addEntity(brick) 147 | brick = new Brick(this.game, 122 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 148 | this.game.addEntity(brick); 149 | brick = new Brick(this.game, 127 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 150 | this.game.addEntity(brick); 151 | brick = new Brick(this.game, 128 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 152 | this.game.addEntity(brick); 153 | brick = new Brick(this.game, 129 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 154 | this.game.addEntity(brick); 155 | brick = new Brick(this.game, 130 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 156 | this.game.addEntity(brick); 157 | brick = new Brick(this.game, 167 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 158 | this.game.addEntity(brick); 159 | brick = new Brick(this.game, 168 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 160 | this.game.addEntity(brick); 161 | brick = new Brick(this.game, 170 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 162 | this.game.addEntity(brick); 163 | 164 | let tube = new Tube(this.game, 28 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1); 165 | this.game.addEntity(tube); 166 | tube = new Tube(this.game, 38 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 2); 167 | this.game.addEntity(tube); 168 | tube = new Tube(this.game, 46 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 3); 169 | this.game.addEntity(tube); 170 | tube = new Tube(this.game, 57 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 3, true); 171 | this.game.addEntity(tube); 172 | tube = new Tube(this.game, 162 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1); 173 | this.game.addEntity(tube); 174 | tube = new Tube(this.game, 176 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1); 175 | this.game.addEntity(tube); 176 | 177 | let box = new Brick(this.game, 16 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 178 | this.game.addEntity(box); 179 | box = new Brick(this.game, 21 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Growth"); 180 | this.game.addEntity(box); 181 | box = new Brick(this.game, 23 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 182 | this.game.addEntity(box); 183 | box = new Brick(this.game, 22 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2, "Coin"); 184 | this.game.addEntity(box); 185 | box = new Brick(this.game, 64 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH, 0, "1up"); 186 | this.game.addEntity(box); 187 | box = new Brick(this.game, 78 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Growth"); 188 | this.game.addEntity(box); 189 | box = new Brick(this.game, 93 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2, "Growth"); 190 | this.game.addEntity(box); 191 | box = new Brick(this.game, 105 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 192 | this.game.addEntity(box); 193 | box = new Brick(this.game, 108 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 194 | this.game.addEntity(box); 195 | box = new Brick(this.game, 108 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2, "Growth"); 196 | this.game.addEntity(box); 197 | box = new Brick(this.game, 111 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 198 | this.game.addEntity(box); 199 | box = new Brick(this.game, 128 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2, "Coin"); 200 | this.game.addEntity(box); 201 | box = new Brick(this.game, 129 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2, "Coin"); 202 | this.game.addEntity(box); 203 | box = new Brick(this.game, 169 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2, "Coin"); 204 | this.game.addEntity(box); 205 | 206 | let block = new Block(this.game, 133 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH); 207 | this.game.addEntity(block); 208 | block = new Block(this.game, 134 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 209 | this.game.addEntity(block); 210 | block = new Block(this.game, 135 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 211 | this.game.addEntity(block); 212 | block = new Block(this.game, 136 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 213 | this.game.addEntity(block); 214 | block = new Block(this.game, 139 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH); 215 | this.game.addEntity(block); 216 | block = new Block(this.game, 139 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 217 | this.game.addEntity(block); 218 | block = new Block(this.game, 139 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 219 | this.game.addEntity(block); 220 | block = new Block(this.game, 139 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 221 | this.game.addEntity(block); 222 | block = new Block(this.game, 147 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH); 223 | this.game.addEntity(block); 224 | block = new Block(this.game, 148 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH); 225 | this.game.addEntity(block); 226 | block = new Block(this.game, 149 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 227 | this.game.addEntity(block); 228 | block = new Block(this.game, 150 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 229 | this.game.addEntity(block); 230 | block = new Block(this.game, 154 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH); 231 | this.game.addEntity(block); 232 | block = new Block(this.game, 154 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 233 | this.game.addEntity(block); 234 | block = new Block(this.game, 154 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 235 | this.game.addEntity(block); 236 | block = new Block(this.game, 154 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 237 | this.game.addEntity(block); 238 | block = new Block(this.game, 178 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 239 | this.game.addEntity(block); 240 | block = new Block(this.game, 179 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 241 | this.game.addEntity(block); 242 | block = new Block(this.game, 180 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 7 * PARAMS.BLOCKWIDTH); 243 | this.game.addEntity(block); 244 | block = new Block(this.game, 181 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH); 245 | this.game.addEntity(block); 246 | block = new Block(this.game, 182 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH); 247 | this.game.addEntity(block); 248 | block = new Block(this.game, 183 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH); 249 | this.game.addEntity(block); 250 | block = new Block(this.game, 184 * PARAMS.BLOCKWIDTH, 7 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 251 | this.game.addEntity(block); 252 | block = new Block(this.game, 185 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 253 | this.game.addEntity(block); 254 | 255 | let goomba = new Goomba(this.game, 22 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 256 | this.game.addEntity(goomba); 257 | goomba = new Goomba(this.game, 40 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 258 | this.game.addEntity(goomba); 259 | goomba = new Goomba(this.game, 51 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 260 | this.game.addEntity(goomba); 261 | goomba = new Goomba(this.game, 52.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 262 | this.game.addEntity(goomba); 263 | goomba = new Goomba(this.game, 80 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH); 264 | this.game.addEntity(goomba); 265 | goomba = new Goomba(this.game, 82 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH); 266 | this.game.addEntity(goomba); 267 | goomba = new Goomba(this.game, 96 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 268 | this.game.addEntity(goomba); 269 | goomba = new Goomba(this.game, 97.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 270 | this.game.addEntity(goomba); 271 | goomba = new Koopa(this.game, 106 * PARAMS.BLOCKWIDTH, 12.5 * PARAMS.BLOCKWIDTH, 1); 272 | this.game.addEntity(goomba); 273 | goomba = new Goomba(this.game, 113 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 274 | this.game.addEntity(goomba); 275 | goomba = new Goomba(this.game, 114.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 276 | this.game.addEntity(goomba); 277 | goomba = new Goomba(this.game, 123 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 278 | this.game.addEntity(goomba); 279 | goomba = new Goomba(this.game, 124.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 280 | this.game.addEntity(goomba); 281 | goomba = new Goomba(this.game, 127 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 282 | this.game.addEntity(goomba); 283 | goomba = new Goomba(this.game, 128.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH); 284 | this.game.addEntity(goomba); 285 | 286 | this.mario.x = x; 287 | this.mario.y = this.mario.size ? y - PARAMS.BLOCKWIDTH : y; 288 | this.game.addEntity(this.mario); 289 | }; 290 | 291 | loadBonusLevelOne() { 292 | this.game.entities = []; 293 | this.x = 0; 294 | 295 | let ground = new Ground(this.game, 0, 14 * PARAMS.BLOCKWIDTH, 17 * PARAMS.BLOCKWIDTH); 296 | this.game.addEntity(ground); 297 | 298 | let box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 299 | this.game.addEntity(box); 300 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH, 1, "None"); 301 | this.game.addEntity(box); 302 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH, 1, "None"); 303 | this.game.addEntity(box); 304 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 1, "None"); 305 | this.game.addEntity(box); 306 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 4 * PARAMS.BLOCKWIDTH, 1, "None"); 307 | this.game.addEntity(box); 308 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH, 1, "None"); 309 | this.game.addEntity(box); 310 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 6 * PARAMS.BLOCKWIDTH, 1, "None"); 311 | this.game.addEntity(box); 312 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 7 * PARAMS.BLOCKWIDTH, 1, "None"); 313 | this.game.addEntity(box); 314 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH, 1, "None"); 315 | this.game.addEntity(box); 316 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH, 1, "None"); 317 | this.game.addEntity(box); 318 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH, 1, "None"); 319 | this.game.addEntity(box); 320 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 1, "None"); 321 | this.game.addEntity(box); 322 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 323 | this.game.addEntity(box); 324 | box = new Brick(this.game, 0 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 325 | this.game.addEntity(box); 326 | 327 | box = new Brick(this.game, 4 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 328 | this.game.addEntity(box); 329 | box = new Brick(this.game, 5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 330 | this.game.addEntity(box); 331 | box = new Brick(this.game, 6 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 332 | this.game.addEntity(box); 333 | box = new Brick(this.game, 7 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 334 | this.game.addEntity(box); 335 | box = new Brick(this.game, 8 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 336 | this.game.addEntity(box); 337 | box = new Brick(this.game, 9 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 338 | this.game.addEntity(box); 339 | box = new Brick(this.game, 10 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, 1, "None"); 340 | this.game.addEntity(box); 341 | 342 | box = new Brick(this.game, 4 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 1, "None"); 343 | this.game.addEntity(box); 344 | box = new Brick(this.game, 5 * PARAMS.BLOCKWIDTH, 11* PARAMS.BLOCKWIDTH, 1, "None"); 345 | this.game.addEntity(box); 346 | box = new Brick(this.game, 6 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 1, "None"); 347 | this.game.addEntity(box); 348 | box = new Brick(this.game, 7 * PARAMS.BLOCKWIDTH, 11* PARAMS.BLOCKWIDTH, 1, "None"); 349 | this.game.addEntity(box); 350 | box = new Brick(this.game, 8 * PARAMS.BLOCKWIDTH, 11* PARAMS.BLOCKWIDTH, 1, "None"); 351 | this.game.addEntity(box); 352 | box = new Brick(this.game, 9 * PARAMS.BLOCKWIDTH, 11* PARAMS.BLOCKWIDTH, 1, "None"); 353 | this.game.addEntity(box); 354 | box = new Brick(this.game, 10 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH, 1, "None"); 355 | this.game.addEntity(box); 356 | 357 | box = new Brick(this.game, 4 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 358 | this.game.addEntity(box); 359 | box = new Brick(this.game, 5 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 360 | this.game.addEntity(box); 361 | box = new Brick(this.game, 6 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 362 | this.game.addEntity(box); 363 | box = new Brick(this.game, 7 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 364 | this.game.addEntity(box); 365 | box = new Brick(this.game, 8 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 366 | this.game.addEntity(box); 367 | box = new Brick(this.game, 9 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 368 | this.game.addEntity(box); 369 | box = new Brick(this.game, 10 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH, 1, "None"); 370 | this.game.addEntity(box); 371 | 372 | box = new Brick(this.game, 4 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 373 | this.game.addEntity(box); 374 | box = new Brick(this.game, 5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 375 | this.game.addEntity(box); 376 | box = new Brick(this.game, 6 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 377 | this.game.addEntity(box); 378 | box = new Brick(this.game, 7 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 379 | this.game.addEntity(box); 380 | box = new Brick(this.game, 8 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 381 | this.game.addEntity(box); 382 | box = new Brick(this.game, 9 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 383 | this.game.addEntity(box); 384 | box = new Brick(this.game, 10 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, 1, "None"); 385 | this.game.addEntity(box); 386 | 387 | let coin = new Coin(this.game, 4 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 388 | this.game.addEntity(coin); 389 | coin = new Coin(this.game, 5 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 390 | this.game.addEntity(coin); 391 | coin = new Coin(this.game, 6 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 392 | this.game.addEntity(coin); 393 | coin = new Coin(this.game, 7 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 394 | this.game.addEntity(coin); 395 | coin = new Coin(this.game, 8 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 396 | this.game.addEntity(coin); 397 | coin = new Coin(this.game, 9 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 398 | this.game.addEntity(coin); 399 | coin = new Coin(this.game, 10 * PARAMS.BLOCKWIDTH, 10 * PARAMS.BLOCKWIDTH); 400 | this.game.addEntity(coin); 401 | 402 | coin = new Coin(this.game, 4 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 403 | this.game.addEntity(coin); 404 | coin = new Coin(this.game, 5 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 405 | this.game.addEntity(coin); 406 | coin = new Coin(this.game, 6 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 407 | this.game.addEntity(coin); 408 | coin = new Coin(this.game, 7 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 409 | this.game.addEntity(coin); 410 | coin = new Coin(this.game, 8 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 411 | this.game.addEntity(coin); 412 | coin = new Coin(this.game, 9 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 413 | this.game.addEntity(coin); 414 | coin = new Coin(this.game, 10 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 415 | this.game.addEntity(coin); 416 | 417 | coin = new Coin(this.game, 5 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 418 | this.game.addEntity(coin); 419 | coin = new Coin(this.game, 6 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 420 | this.game.addEntity(coin); 421 | coin = new Coin(this.game, 7 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 422 | this.game.addEntity(coin); 423 | coin = new Coin(this.game, 8 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 424 | this.game.addEntity(coin); 425 | coin = new Coin(this.game, 9 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH); 426 | this.game.addEntity(coin); 427 | 428 | let tube = new Tube(this.game, 15 * PARAMS.BLOCKWIDTH, -1 * PARAMS.BLOCKWIDTH, 14); 429 | this.game.addEntity(tube); 430 | tube = new SideTube(this.game, 13 * PARAMS.BLOCKWIDTH, 12 * PARAMS.BLOCKWIDTH); 431 | this.game.addEntity(tube); 432 | 433 | this.mario.x = 2.5 * PARAMS.BLOCKWIDTH; 434 | this.mario.y = 0 * PARAMS.BLOCKWIDTH; 435 | this.mario.state = 4; // falling 436 | this.game.addEntity(this.mario); 437 | }; 438 | 439 | update() { 440 | PARAMS.DEBUG = document.getElementById("debug").checked; 441 | 442 | let midpoint = PARAMS.CANVAS_WIDTH/2 - PARAMS.BLOCKWIDTH / 2; 443 | 444 | if (this.x < this.mario.x - midpoint) this.x = this.mario.x - midpoint; 445 | 446 | if (this.mario.dead && this.mario.y > PARAMS.BLOCKWIDTH * 16) { 447 | this.mario.dead = false; 448 | this.loadLevelOne(2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH); 449 | }; 450 | }; 451 | 452 | addCoin() { 453 | if (this.coins++ === 100) { 454 | this.coins = 0; 455 | this.lives++; 456 | } 457 | }; 458 | 459 | draw(ctx) { 460 | ctx.font = PARAMS.BLOCKWIDTH/2 + 'px "Press Start 2P"'; 461 | ctx.fillStyle = "White"; 462 | ctx.fillText("MARRIOTT", 1.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 463 | ctx.fillText((this.score + "").padStart(8,"0"), 1.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 464 | ctx.fillText("x" + (this.coins < 10 ? "0" : "") + this.coins, 6.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 465 | ctx.fillText("WORLD", 9 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 466 | ctx.fillText("1-1", 9.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 467 | ctx.fillText("TIME", 12.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 468 | ctx.fillText("400", 13 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 469 | 470 | this.coinAnimation.drawFrame(this.game.clockTick, ctx, 6 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH, 3); 471 | 472 | if (PARAMS.DEBUG) { 473 | let xV = "xV=" + Math.floor(this.game.mario.velocity.x); 474 | let yV = "yV=" + Math.floor(this.game.mario.velocity.y); 475 | ctx.fillText(xV, 1.5 * PARAMS.BLOCKWIDTH, 2.5 * PARAMS.BLOCKWIDTH); 476 | ctx.fillText(yV, 1.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 477 | 478 | ctx.translate(0, -10); // hack to move elements up by 10 pixels instead of adding -10 to all y coordinates below 479 | ctx.strokeStyle = "White"; 480 | ctx.lineWidth = 2; 481 | ctx.strokeStyle = this.game.left ? "White" : "Grey"; 482 | ctx.fillStyle = ctx.strokeStyle; 483 | ctx.strokeRect(6 * PARAMS.BLOCKWIDTH - 2, 2.5 * PARAMS.BLOCKWIDTH - 2, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 484 | ctx.fillText("L", 6 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 485 | ctx.strokeStyle = this.game.down ? "White" : "Grey"; 486 | ctx.fillStyle = ctx.strokeStyle; 487 | ctx.strokeRect(6.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 488 | ctx.fillText("D", 6.5 * PARAMS.BLOCKWIDTH + 2, 3.5 * PARAMS.BLOCKWIDTH + 2); 489 | ctx.strokeStyle = this.game.up ? "White" : "Grey"; 490 | ctx.fillStyle = ctx.strokeStyle; 491 | ctx.strokeRect(6.5 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH - 4, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 492 | ctx.fillText("U", 6.5 * PARAMS.BLOCKWIDTH + 2, 2.5 * PARAMS.BLOCKWIDTH - 2); 493 | ctx.strokeStyle = this.game.right ? "White" : "Grey"; 494 | ctx.fillStyle = ctx.strokeStyle; 495 | ctx.strokeRect(7 * PARAMS.BLOCKWIDTH + 2, 2.5 * PARAMS.BLOCKWIDTH - 2, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 496 | ctx.fillText("R", 7 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 497 | 498 | ctx.strokeStyle = this.game.A ? "White" : "Grey"; 499 | ctx.fillStyle = ctx.strokeStyle; 500 | ctx.beginPath(); 501 | ctx.arc(8.25 * PARAMS.BLOCKWIDTH + 2, 2.75 * PARAMS.BLOCKWIDTH, 0.25 * PARAMS.BLOCKWIDTH + 4, 0, 2 * Math.PI); 502 | ctx.stroke(); 503 | ctx.fillText("A", 8 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 504 | ctx.strokeStyle = this.game.B ? "White" : "Grey"; 505 | ctx.fillStyle = ctx.strokeStyle; 506 | ctx.beginPath(); 507 | ctx.arc(9 * PARAMS.BLOCKWIDTH + 2, 2.75 * PARAMS.BLOCKWIDTH, 0.25 * PARAMS.BLOCKWIDTH + 4, 0, 2 * Math.PI); 508 | ctx.stroke(); 509 | ctx.fillText("B", 8.75 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 510 | 511 | ctx.translate(0, 10); 512 | ctx.strokeStyle = "White"; 513 | ctx.fillStyle = ctx.strokeStyle; 514 | 515 | this.minimap.draw(ctx); 516 | } 517 | }; 518 | }; 519 | 520 | class Minimap { 521 | constructor(game, x, y, w) { 522 | Object.assign(this, { game, x, y, w }); 523 | }; 524 | 525 | update() { 526 | 527 | }; 528 | 529 | draw(ctx) { 530 | ctx.strokeStyle = "Black"; 531 | ctx.strokeRect(this.x, this.y, this.w, PARAMS.BLOCKWIDTH); 532 | for (var i = 0; i < this.game.entities.length; i++) { 533 | this.game.entities[i].drawMinimap(ctx, this.x, this.y); 534 | } 535 | }; 536 | }; 537 | -------------------------------------------------------------------------------- /.vs/Mario/v16/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/.vs/Mario/v16/.suo -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/.vs/slnx.sqlite -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5504 3 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING GUIDELINE 2 | 3 | ## Description 4 | 5 | Your contribution consists of three parts. 6 | 7 | 1. In **week** 4 you will attend the planning session in which we will 8 | select a project lead for the quarter. During this session you commit to implementing one feature of the class project. 9 | 2. Second, in **week 7** you will submit your implementation for code review. 10 | Next, in week 8 you will submit your code review of another student’s implementation. 11 | 3. Finally, in **week 10** you will submit your final code, after review, and your response to your code review. All code that passes review will be added to the project. 12 | 4. It is **not necessary** for your code to be merged to receive extra credits. However, it might be the case when you submit a very low quality code. 13 | 5. You can work individually or as a group on implementing an assigned feature. However, it is recommended to contribute as a group. 14 | 15 | ## Branch naming convention 16 | 17 | Please naming your branch using the following rule: 18 | 19 | - your-name/your-task 20 | - your-group-name/your-task 21 | 22 | Example: hungvu/contributing-guideline 23 | 24 | ## Task assinging and pull request 25 | 26 | 1. In **week 7**, you will make a pull request to submit your code. 27 | 2. Tasks will be divided into issues and put into project board. A story board will clearly indicate the task for each person and/or group. It is located in **Projects** tab. 28 | 3. All pull requests must pass an automated test run by Github action. As of now, it simply checks whether your code is compilable or not. There can be more tests added later on. 29 | 4. Please use .gitignore to exclude your environment or config files. 30 | 31 | ## Submitting pull request 32 | 33 | 1. You will not directly push the changes to a master branch. Master branch is protected. 34 | 2. Your code will stay on your own branch. You might create an upstream branch. Each branch is only accessible by contributors of that one. You can also work on a forked repository, but as discusssed in an introduction meeting, working on a separate branch is preferred. 35 | 3. At **week 7**, you will make a pull request and your code will be reviewed by Hung Vu (@hunghvu), Benjamin (@BenjaminDeJager), and professor Chris (@algorithm0r). 36 | 4. For the pull requests, please document what you changed, updated, what portion of an assigned task has been completed, not completed and any potential or discovered bugs which haven't been fixed. 37 | 5. For the pull requests, please link you pull request with an assigned issue. For more information, please visit [this site](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue). 38 | 6. For each pull request, we will restrict **Assignees** to the code author(s). Please also add a label **PR: NEED REVIEW** so the reviewer know it is a submission. The reviewer will then update labels with the followings. 39 | 40 | - **PR: REVIEWED**: This pull request is reviewed, but the decision has not been made. This tag will always stay on the pull request. 41 | - The labels below are more like secondary one, which indicates the review progress. 42 | - **PR: HAS CONFLICT**: This pull request has merge conflict that needs to be resolved. It does not indicate whether the PR can be merged or not. 43 | - **PR: DO NOT MERGE**: This pull request will not be merged. This is the final result. 44 | - **PR: READY TO MERGE**: This pull request passes all the tests, merge conflict are resolved and will be merged. This is a final result. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperMarioBros 2 | 3 | 1. Please read the [**contributing guideline**](https://github.com/algorithm0r/SuperMarioBros/blob/hungvu/contributing-guideline/CONTRIBUTING.md). 4 | 5 | ## Contributors 6 | 7 | - Chris Marriott 8 | -------------------------------------------------------------------------------- /TiledJSONConverter/convert_tiled.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/TiledJSONConverter/convert_tiled.exe -------------------------------------------------------------------------------- /TiledJSONConverter/convert_tiled.py: -------------------------------------------------------------------------------- 1 | __author__ = "Douglas Johnston" 2 | 3 | import json 4 | import math 5 | import ntpath 6 | import argparse 7 | from os import truncate 8 | import re 9 | import sys 10 | 11 | def main(): 12 | """ 13 | Converts a Tiled JSON file into a more usable file by finding all of the object layers. 14 | It assumes that the name of the object layer is the same as the object 15 | How to use: Run the file using 'python convert_tiled.py (PATH TO LEVEL FILE)' 16 | """ 17 | parser = argparse.ArgumentParser(description='Converts a Tiled JSON file into a more usable file by finding all of the object layers. It assumes that the name of the object layer is the same as the object How to use: Run the file using \'python convert_tiled.py (PATH TO LEVEL FILE)\'') 18 | parser.add_argument('filepath', help="The filepath of the Tiled JSON including the filename") 19 | parser.add_argument('-fp', '--filepath', help="The filepath of the Tiled JSON including the filename") 20 | parser.add_argument('-o', '--overwrite', action='store_true', help="Overwrites the file") 21 | parser.add_argument('-js', '--javascript', action='store_true', help="Saves the file to a JavaScript file instead of a JSON file") 22 | parser.add_argument('-i', '--indent', type=int, default=4, help="The ammount to indent the JSON") 23 | parser.add_argument('-wts', '--widthtosize', action='store_true', help="Replaces the width and height parameters to one size parameter using the blockwidth (default: 16), assumes that if the width of an object is less than the blockwidth, then the size is 1") 24 | parser.add_argument('-bw', '--blockwidth', type=int, help="The blockwidth to calculate the size parameter") 25 | 26 | args = parser.parse_args() 27 | 28 | if args.filepath is None and len(sys.argv) > 1: 29 | path = sys.argv[1] 30 | else: 31 | path = args.filepath 32 | 33 | if path is None or path in ['-fp', '--filepath', '-o', '--overwrite', '-js', '--javascript']: 34 | path = input("Enter the path including the filename: ") 35 | 36 | if args.blockwidth is None and args.widthtosize: 37 | args.blockwidth = 16 38 | elif args.blockwidth is not None: 39 | args.widthtosize = True 40 | 41 | 42 | file_name = ntpath.split(path) 43 | objects = {} 44 | with open(path, 'r+') as file: 45 | level_data = json.load(file) 46 | for layer in level_data["layers"]: 47 | if layer['type'] == "objectgroup": 48 | objects[layer['name']] = [] 49 | for obj in layer['objects']: 50 | if args.widthtosize: 51 | objects[layer['name']].append({ 52 | 'x': obj['x'], 53 | 'y': obj['y'], 54 | 'size': calculate_size(obj['width'], args.blockwidth) 55 | }) 56 | else: 57 | objects[layer['name']].append({ 58 | 'x': obj['x'], 59 | 'y': obj['y'], 60 | 'width': obj['width'], 61 | 'height': obj['height'] 62 | }) 63 | file.close() 64 | if args.javascript: 65 | extension = '.js' 66 | else: 67 | extension = '.json' 68 | if args.overwrite: 69 | out_file = open(path.replace('.json', extension), 'w') 70 | else: 71 | out_file = open(file_name[0][2:] + 'CONVERTED' + extension, 'w') 72 | out_file.write(re.sub(r'(?= this.totalTime); 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /assetmanager.js: -------------------------------------------------------------------------------- 1 | class AssetManager { 2 | constructor() { 3 | this.successCount = 0; 4 | this.errorCount = 0; 5 | this.cache = []; 6 | this.downloadQueue = []; 7 | }; 8 | 9 | queueDownload(path) { 10 | console.log("Queueing " + path); 11 | this.downloadQueue.push(path); 12 | }; 13 | 14 | isDone() { 15 | return this.downloadQueue.length === this.successCount + this.errorCount; 16 | }; 17 | 18 | downloadAll(callback) { 19 | if (this.downloadQueue.length === 0) setTimeout(callback, 10); 20 | for (var i = 0; i < this.downloadQueue.length; i++) { 21 | var that = this; 22 | 23 | var path = this.downloadQueue[i]; 24 | console.log(path); 25 | var ext = path.substring(path.length - 3); 26 | 27 | switch (ext) { 28 | case 'jpg': 29 | case 'png': 30 | var img = new Image(); 31 | img.addEventListener("load", function () { 32 | console.log("Loaded " + this.src); 33 | that.successCount++; 34 | if (that.isDone()) callback(); 35 | }); 36 | 37 | img.addEventListener("error", function () { 38 | console.log("Error loading " + this.src); 39 | that.errorCount++; 40 | if (that.isDone()) callback(); 41 | }); 42 | 43 | img.src = path; 44 | this.cache[path] = img; 45 | break; 46 | case 'wav': 47 | case 'mp3': 48 | case 'mp4': 49 | var aud = new Audio(); 50 | aud.addEventListener("loadeddata", function () { 51 | console.log("Loaded " + this.src); 52 | that.successCount++; 53 | if (that.isDone()) callback(); 54 | }); 55 | 56 | aud.addEventListener("error", function () { 57 | console.log("Error loading " + this.src); 58 | that.errorCount++; 59 | if (that.isDone()) callback(); 60 | }); 61 | 62 | aud.addEventListener("ended", function () { 63 | aud.pause(); 64 | aud.currentTime = 0; 65 | }); 66 | 67 | aud.src = path; 68 | aud.load(); 69 | 70 | this.cache[path] = aud; 71 | break; 72 | } 73 | } 74 | }; 75 | 76 | getAsset(path) { 77 | return this.cache[path]; 78 | }; 79 | 80 | playAsset(path) { 81 | let audio = this.cache[path]; 82 | if (audio.currentTime != 0) { 83 | let bak = audio.cloneNode(); 84 | bak.currentTime = 0; 85 | bak.volume = audio.volume; 86 | bak.play(); 87 | } else { 88 | audio.currentTime = 0; 89 | audio.play(); 90 | } 91 | }; 92 | 93 | muteAudio(mute) { 94 | for (var key in this.cache) { 95 | let asset = this.cache[key]; 96 | if (asset instanceof Audio) { 97 | asset.muted = mute; 98 | } 99 | } 100 | }; 101 | 102 | adjustVolume(volume) { 103 | for (var key in this.cache) { 104 | let asset = this.cache[key]; 105 | if (asset instanceof Audio) { 106 | asset.volume = volume; 107 | } 108 | } 109 | }; 110 | 111 | pauseBackgroundMusic() { 112 | for (var key in this.cache) { 113 | let asset = this.cache[key]; 114 | if (asset instanceof Audio) { 115 | asset.pause(); 116 | asset.currentTime = 0; 117 | } 118 | } 119 | }; 120 | 121 | autoRepeat(path) { 122 | var aud = this.cache[path]; 123 | aud.addEventListener("ended", function () { 124 | aud.play(); 125 | }); 126 | }; 127 | }; 128 | 129 | -------------------------------------------------------------------------------- /audio/1-up.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/1-up.mp3 -------------------------------------------------------------------------------- /audio/block.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/block.mp3 -------------------------------------------------------------------------------- /audio/bump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/bump.wav -------------------------------------------------------------------------------- /audio/coin.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/coin.mp3 -------------------------------------------------------------------------------- /audio/fireball.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/fireball.mp3 -------------------------------------------------------------------------------- /audio/fireworks.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/fireworks.mp3 -------------------------------------------------------------------------------- /audio/flagpole.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/flagpole.mp3 -------------------------------------------------------------------------------- /audio/game-over.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/game-over.mp3 -------------------------------------------------------------------------------- /audio/level-clear.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/level-clear.mp3 -------------------------------------------------------------------------------- /audio/life-lost.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/life-lost.mp3 -------------------------------------------------------------------------------- /audio/pipe.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/pipe.mp3 -------------------------------------------------------------------------------- /audio/power-up-appears.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/power-up-appears.mp3 -------------------------------------------------------------------------------- /audio/power-up.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/power-up.mp3 -------------------------------------------------------------------------------- /audio/small-jump.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/small-jump.mp3 -------------------------------------------------------------------------------- /audio/stomp.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/stomp.mp3 -------------------------------------------------------------------------------- /audio/super-jump.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/audio/super-jump.mp3 -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | class BigHill { 2 | constructor(game, x, y) { 3 | Object.assign(this, { game, x, y }); 4 | 5 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 6 | }; 7 | 8 | update() { 9 | 10 | }; 11 | 12 | drawMinimap(ctx, mmX, mmY) { 13 | } 14 | 15 | draw(ctx) { 16 | ctx.drawImage(this.spritesheet, 86, 0, 80, 40, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 5, PARAMS.BLOCKWIDTH * 2.5); 17 | }; 18 | }; 19 | 20 | class Hill { 21 | constructor(game, x, y) { 22 | Object.assign(this, { game, x, y }); 23 | 24 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 25 | }; 26 | 27 | update() { 28 | 29 | }; 30 | 31 | drawMinimap(ctx, mmX, mmY) { 32 | } 33 | 34 | draw(ctx) { 35 | ctx.drawImage(this.spritesheet, 169, 20, 48, 20, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 3, PARAMS.BLOCKWIDTH * 1.25); 36 | }; 37 | }; 38 | 39 | class Bush { 40 | constructor(game, x, y, size) { 41 | Object.assign(this, { game, x, y, size }); 42 | 43 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 44 | }; 45 | 46 | update() { 47 | 48 | }; 49 | 50 | drawMinimap(ctx, mmX, mmY) { 51 | } 52 | 53 | draw(ctx) { 54 | ctx.drawImage(this.spritesheet, 288, 24, 8, 24, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 0.5, PARAMS.BLOCKWIDTH * 1.5); 55 | let i = 0; 56 | for (; i < this.size; i++) { 57 | ctx.drawImage(this.spritesheet, 296, 24, 16, 24, this.x - this.game.camera.x + PARAMS.BLOCKWIDTH * (i + 0.5), this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 1.5); 58 | } 59 | ctx.drawImage(this.spritesheet, 312, 24, 8, 24, this.x - this.game.camera.x + PARAMS.BLOCKWIDTH * (i + 0.5), this.y, PARAMS.BLOCKWIDTH * 0.5, PARAMS.BLOCKWIDTH * 1.5); 60 | }; 61 | }; 62 | 63 | class Cloud { 64 | constructor(game, x, y, size) { 65 | Object.assign(this, { game, x, y, size }); 66 | 67 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 68 | }; 69 | 70 | update() { 71 | 72 | }; 73 | 74 | drawMinimap(ctx, mmX, mmY) { 75 | } 76 | 77 | draw(ctx) { 78 | ctx.drawImage(this.spritesheet, 211, 69, 8, 24, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 0.5, PARAMS.BLOCKWIDTH * 1.5); 79 | let i = 0; 80 | for (; i < this.size; i++) { 81 | ctx.drawImage(this.spritesheet, 219, 69, 16, 24, this.x - this.game.camera.x + PARAMS.BLOCKWIDTH * (i + 0.5), this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 1.5); 82 | } 83 | ctx.drawImage(this.spritesheet, 235, 69, 8, 24, this.x - this.game.camera.x + PARAMS.BLOCKWIDTH * (i + 0.5), this.y, PARAMS.BLOCKWIDTH * 0.5, PARAMS.BLOCKWIDTH * 1.5); 84 | }; 85 | }; 86 | 87 | class BigCastle { 88 | constructor(game, x, y) { 89 | Object.assign(this, { game, x, y }); 90 | 91 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/castle_big.png"); 92 | }; 93 | 94 | update() { 95 | 96 | }; 97 | 98 | drawMinimap(ctx, mmX, mmY) { 99 | } 100 | 101 | draw(ctx) { 102 | ctx.drawImage(this.spritesheet, 0, 0, 145, 175, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 9, PARAMS.BLOCKWIDTH * 11); 103 | }; 104 | }; -------------------------------------------------------------------------------- /boundingbox.js: -------------------------------------------------------------------------------- 1 | class BoundingBox { 2 | constructor(x, y, width, height) { 3 | Object.assign(this, { x, y, width, height }); 4 | 5 | this.left = x; 6 | this.top = y; 7 | this.right = this.left + this.width; 8 | this.bottom = this.top + this.height; 9 | }; 10 | 11 | collide(oth) { 12 | if (this.right > oth.left && this.left < oth.right && this.top < oth.bottom && this.bottom > oth.top) return true; 13 | return false; 14 | }; 15 | 16 | overlap(oth) { 17 | let a_half = {x: this.width / 2, y: this.height / 2}; 18 | let b_half = {x: oth.width / 2, y: oth.height / 2}; 19 | 20 | let a_center = {x: this.right - a_half.x, y: this.bottom - a_half.y}; 21 | let b_center = {x: oth.right - b_half.x, y: oth.bottom - b_half.y}; 22 | 23 | let ox = a_half.x + b_half.x - Math.abs(a_center.x - b_center.x); 24 | let oy = a_half.y + b_half.y - Math.abs(a_center.y - b_center.y); 25 | 26 | return {x: ox, y: oy}; 27 | }; 28 | }; -------------------------------------------------------------------------------- /bricks.js: -------------------------------------------------------------------------------- 1 | class Ground { 2 | constructor(game, x, y, w, underground) { 3 | Object.assign(this, { game, x, y, w }); 4 | 5 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/bricks.png"); 6 | 7 | if (underground) this.spritesheet = ASSET_MANAGER.getAsset("./sprites/underground_stuff.png"); 8 | 9 | 10 | this.BB = new BoundingBox(this.x, this.y, this.w, PARAMS.BLOCKWIDTH * 2); 11 | this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 2) 12 | this.rightBB = new BoundingBox(this.x + this.w - PARAMS.BLOCKWIDTH, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 2) 13 | }; 14 | 15 | update() { 16 | }; 17 | 18 | drawMinimap(ctx, mmX, mmY) { 19 | ctx.fillStyle = "Brown"; 20 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, this.w / PARAMS.BITWIDTH, PARAMS.SCALE * 2); 21 | }; 22 | 23 | draw(ctx) { 24 | let brickCount = this.w / PARAMS.BLOCKWIDTH; 25 | for (var i = 0; i < brickCount; i++) { 26 | ctx.drawImage(this.spritesheet,0,0, 16,16, this.x + i * PARAMS.BLOCKWIDTH - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 27 | ctx.drawImage(this.spritesheet, 0,0,16,16, this.x + i * PARAMS.BLOCKWIDTH - this.game.camera.x, this.y + PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 28 | } 29 | if (PARAMS.DEBUG) { 30 | ctx.strokeStyle = 'Red'; 31 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 32 | } 33 | }; 34 | }; 35 | 36 | class Brick { // type 0 = invis, 1 = brick, 2 = question, 3 = block 37 | constructor(game, x, y, type, prize, underground) { 38 | Object.assign(this, { game, x, y, prize, type }); 39 | 40 | this.bounce = false; 41 | 42 | this.velocity = 0; 43 | 44 | this.startTime = 0; 45 | 46 | this.animation = []; 47 | 48 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/bricks.png"); 49 | if (underground) this.spritesheet = ASSET_MANAGER.getAsset("./sprites/underground_stuff.png"); 50 | 51 | this.animation.push(null); 52 | this.animation.push(new Animator(this.spritesheet, 16, 0, 16, 16, 1, 0.33, 0, false, true)); 53 | this.animation.push(new Animator(ASSET_MANAGER.getAsset("./sprites/coins.png"), 0, 80, 16, 16, 4, 1/8, 0, false, true)); 54 | this.animation.push(new Animator(this.spritesheet, 48, 0, 16, 16, 1, 1, 0, false, true)); 55 | 56 | this.BB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH / 8, this.y, PARAMS.BLOCKWIDTH * 3 / 4, PARAMS.BLOCKWIDTH); 57 | this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH / 2, PARAMS.BLOCKWIDTH); 58 | this.rightBB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH / 2, this.y, PARAMS.BLOCKWIDTH / 2, PARAMS.BLOCKWIDTH); 59 | this.topBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH / 2); 60 | this.bottomBB = new BoundingBox(this.x, this.y + PARAMS.BLOCKWIDTH / 2, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH / 2); 61 | }; 62 | 63 | update() { 64 | const FALL_ACC = 562.5; 65 | 66 | this.velocity += FALL_ACC * this.game.clockTick; 67 | this.y += this.game.clockTick * this.velocity * PARAMS.SCALE; 68 | 69 | if (this.bounce && this.type < 3) { 70 | this.bounce = false; 71 | this.velocity = - 80; 72 | 73 | this.killEnemiesAbove(); 74 | 75 | switch (this.prize) { 76 | case 'Coins': 77 | if (this.startTime === 0) this.startTime = Date.now(); 78 | if (Date.now() - this.startTime < 3000) { 79 | this.game.addEntity(new CoinPop(this.game, this.x, this.BB.top - PARAMS.BLOCKWIDTH)); 80 | break; 81 | } 82 | case 'Coin': 83 | this.game.addEntity(new CoinPop(this.game, this.x, this.BB.top - PARAMS.BLOCKWIDTH)); 84 | this.type = 3; 85 | break; 86 | case 'Growth': 87 | if (this.game.mario.size === 1) { 88 | this.game.addEntity(new Flower(this.game, this.x, this.BB.top, this)); 89 | } 90 | if (this.game.mario.size === 0) { 91 | this.game.addEntity(new Mushroom(this.game, this.x, this.BB.top, this, 'Growth')); 92 | } 93 | this.type = 3; 94 | ASSET_MANAGER.playAsset("./audio/power-up-appears.mp3"); 95 | break; 96 | case '1up': 97 | this.game.addEntity(new Mushroom(this.game, this.x, this.BB.top, this, '1up')); 98 | this.type = 3; 99 | break; 100 | } 101 | if (this.type === 1) { 102 | if (this.game.mario.size === 0) { 103 | ASSET_MANAGER.playAsset("./audio/bump.wav"); 104 | } 105 | } 106 | } 107 | 108 | if (this.y > this.BB.top) this.y = this.BB.top; 109 | 110 | }; 111 | 112 | killEnemiesAbove() { 113 | const that = this; 114 | this.game.entities.forEach(entity => { 115 | if (entity.BB && entity.BB.bottom <= this.BB.top && entity.BB.right > this.BB.left && entity.BB.left < this.BB.right) { 116 | // Check if the entity is an enemy and kill it 117 | if (entity instanceof Goomba || entity instanceof Koopa || entity instanceof PirahnaPlant || entity instanceof KoopaParatroopaGreen || entity instanceof KoopaParatroopaRed) { 118 | entity.dead = true; 119 | that.game.addEntity(new Score(that.game, entity.x, entity.y, 100)); 120 | ASSET_MANAGER.playAsset("./audio/stomp.mp3"); 121 | } 122 | } 123 | }); 124 | } 125 | 126 | drawMinimap(ctx, mmX, mmY) { 127 | ctx.fillStyle = this.type === 2 ? "Gold" : "Brown"; 128 | if (this.type) ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, PARAMS.SCALE, PARAMS.SCALE); 129 | }; 130 | 131 | draw(ctx) { 132 | if (this.type) { 133 | this.animation[this.type].drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 134 | } 135 | 136 | if (PARAMS.DEBUG) { 137 | ctx.strokeStyle = 'Red'; 138 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 139 | ctx.strokeRect(this.leftBB.x - this.game.camera.x, this.leftBB.y, this.leftBB.width, this.leftBB.height); 140 | ctx.strokeRect(this.rightBB.x - this.game.camera.x, this.rightBB.y, this.rightBB.width, this.rightBB.height); 141 | ctx.strokeRect(this.topBB.x - this.game.camera.x, this.topBB.y, this.topBB.width, this.topBB.height); 142 | ctx.strokeRect(this.bottomBB.x - this.game.camera.x, this.bottomBB.y, this.bottomBB.width, this.bottomBB.height); 143 | } 144 | }; 145 | 146 | explode(){ 147 | this.game.addEntity(new Shard(this.game, this.x, this.y, -150)); 148 | this.game.addEntity(new Shard(this.game, this.x, this.y - PARAMS.BLOCKWIDTH * 1.5, -150)); 149 | this.game.addEntity(new Shard(this.game, this.x + PARAMS.BLOCKWIDTH, this.y - PARAMS.BLOCKWIDTH * 1.5, 150)); 150 | this.game.addEntity(new Shard(this.game, this.x + PARAMS.BLOCKWIDTH, this.y, 150)); 151 | this.removeFromWorld = true; 152 | ASSET_MANAGER.playAsset("./audio/block.mp3"); 153 | } 154 | }; 155 | 156 | class Shard{ 157 | constructor(game, x, y, xVelocity, yVelocity = -100){ 158 | Object.assign(this, {game, x, y}) 159 | 160 | this.animation = new Animator(ASSET_MANAGER.getAsset("./sprites/bricks.png"), 16, 0, 8, 8, 1, 0.33, 0, false, true); 161 | this.velocity = {x:xVelocity, y:yVelocity}; 162 | this.life = 90; //90 clock ticks -> 1.5 seconds at 60 fps 163 | 164 | } 165 | 166 | update(){ 167 | const FALL_ACC = 562.5; 168 | this.life -= 1; 169 | if(this.life <= 0){ 170 | this.removeFromWorld = true; 171 | } 172 | this.velocity.y += FALL_ACC * this.game.clockTick; 173 | this.y += this.game.clockTick * this.velocity.y * PARAMS.SCALE; 174 | this.x += this.game.clockTick * this.velocity.x; 175 | } 176 | 177 | draw(ctx) { 178 | this.animation.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 179 | }; 180 | 181 | drawMinimap(ctx, mmX, mmY) { 182 | ctx.fillStyle = "Brown"; 183 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, this.w / PARAMS.BITWIDTH, PARAMS.SCALE); 184 | }; 185 | 186 | }; 187 | 188 | class Block { 189 | constructor(game, x, y, w, underground) { 190 | Object.assign(this, { game, x, y, w }); 191 | 192 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/bricks.png"); 193 | if (underground) this.spritesheet = ASSET_MANAGER.getAsset("./sprites/underground_stuff.png"); 194 | 195 | this.BB = new BoundingBox(this.x, this.y, this.w, PARAMS.BLOCKWIDTH); 196 | this.leftBB = new BoundingBox(this.x, this.y, this.w / 2, PARAMS.BLOCKWIDTH); 197 | this.rightBB = new BoundingBox(this.x + this.w / 2, this.y, this.w / 2, PARAMS.BLOCKWIDTH); 198 | }; 199 | 200 | update() { 201 | }; 202 | 203 | drawMinimap(ctx, mmX, mmY) { 204 | ctx.fillStyle = "Brown"; 205 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, this.w / PARAMS.BITWIDTH, PARAMS.SCALE); 206 | }; 207 | 208 | draw(ctx) { 209 | let brickCount = this.w / PARAMS.BLOCKWIDTH; 210 | for (var i = 0; i < brickCount; i++) { 211 | ctx.drawImage(this.spritesheet, 64, 0, 16, 16, this.x + i * PARAMS.BLOCKWIDTH - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 212 | } 213 | }; 214 | }; 215 | 216 | class Tube { 217 | constructor(game, x, y, size, destination, enemyType) { 218 | Object.assign(this, { game, x, y, size, destination }); 219 | 220 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 221 | 222 | this.BB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH / 8, this.y, PARAMS.BLOCKWIDTH * 2 - PARAMS.BLOCKWIDTH * 2 / 8, PARAMS.BLOCKWIDTH * (size + 1)); 223 | this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * (size + 1)); 224 | this.rightBB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * (size + 1)); 225 | 226 | if (enemyType) { 227 | if (enemyType === "piranha") { 228 | this.game.addEntity(new PirahnaPlant(this.game, this.x, this.y, this)); 229 | } 230 | } 231 | }; 232 | 233 | update() { 234 | 235 | }; 236 | 237 | drawMinimap(ctx, mmX, mmY) { 238 | ctx.fillStyle = "Green"; 239 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, PARAMS.SCALE * 2, PARAMS.SCALE * (this.size + 1)); 240 | }; 241 | 242 | draw(ctx) { 243 | ctx.drawImage(this.spritesheet, 309, 417, 32, 16, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 2, PARAMS.BLOCKWIDTH); 244 | let i = 0; 245 | for (; i < this.size; i++) { 246 | ctx.drawImage(this.spritesheet, 309, 433.5, 32, 15, this.x - this.game.camera.x, this.y + PARAMS.BLOCKWIDTH * (i + 1), PARAMS.BLOCKWIDTH * 2, PARAMS.BLOCKWIDTH); 247 | } 248 | 249 | if (PARAMS.DEBUG) { 250 | ctx.strokeStyle = 'Red'; 251 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 252 | } 253 | }; 254 | }; 255 | 256 | class SideTube { 257 | constructor(game, x, y, size, destination) { 258 | Object.assign(this, { game, x, y, size, destination }); 259 | 260 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/tiles.png"); 261 | 262 | this.BB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH / 8, this.y, PARAMS.BLOCKWIDTH * 2, PARAMS.BLOCKWIDTH * 2); 263 | this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 2); 264 | this.rightBB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 2); 265 | }; 266 | 267 | update() { 268 | 269 | }; 270 | 271 | drawMinimap(ctx, mmX, mmY) { 272 | ctx.fillStyle = "Green"; 273 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, PARAMS.SCALE * 2, PARAMS.SCALE * 2); 274 | }; 275 | 276 | draw(ctx) { 277 | ctx.drawImage(this.spritesheet, 84, 417, 40, 32, this.x - this.game.camera.x, this.y, 2.5 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 278 | 279 | if (PARAMS.DEBUG) { 280 | ctx.strokeStyle = 'Red'; 281 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 282 | } 283 | }; 284 | }; 285 | 286 | class Lift { 287 | constructor(game, x, y, goingDown) { 288 | Object.assign(this, {game, x, y, goingDown}); 289 | 290 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/items.png"); 291 | 292 | this.velocity = 3 * PARAMS.BLOCKWIDTH; // pixels per second 293 | if (!goingDown) this.velocity = -this.velocity; 294 | 295 | this.updateBB(); 296 | }; 297 | 298 | update() { 299 | if (this.goingDown && this.y > 15 * PARAMS.BLOCKWIDTH) { 300 | let newLift = new Lift(this.game, this.x, 0, this.goingDown); 301 | this.game.addEntity(newLift); 302 | this.removeFromWorld = true; 303 | } else if (!this.goingDown && this.y < 0) { 304 | let newLift = new Lift(this.game, this.x, 15 * PARAMS.BLOCKWIDTH, this.goingDown); 305 | this.game.addEntity(newLift); 306 | this.removeFromWorld = true; 307 | } else { 308 | this.y += this.velocity * this.game.clockTick; 309 | this.updateBB(); 310 | } 311 | } 312 | 313 | drawMinimap(ctx, mmX, mmY) { 314 | ctx.fillStyle = 'Orange'; 315 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, PARAMS.SCALE * 3, PARAMS.SCALE); 316 | }; 317 | 318 | draw(ctx) { 319 | ctx.drawImage(this.spritesheet, 63, 38, 48, 8, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH * 3, PARAMS.BLOCKWIDTH * 0.5); 320 | 321 | if (PARAMS.DEBUG) { 322 | ctx.strokeStyle = 'Red'; 323 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 324 | } 325 | }; 326 | 327 | updateBB() { 328 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH * 3, PARAMS.BLOCKWIDTH * 0.5); 329 | this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 0.5); 330 | this.rightBB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH * 2, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 0.5); 331 | } 332 | } 333 | 334 | class Flag { 335 | constructor(game, x, y) { 336 | Object.assign(this, { game, x, y }); 337 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/flag.png"); 338 | this.flagX = x - 36; 339 | this.flagY = y + 27; 340 | this.updateBB(); 341 | this.win = false; 342 | } 343 | 344 | update() { 345 | } 346 | 347 | drawMinimap(ctx, mmX, mmY) { 348 | // TODO: add in minimap functionality 349 | }; 350 | 351 | draw(ctx) { 352 | let TICK = this.game.clockTick; 353 | 354 | // draw the pole 355 | ctx.drawImage(this.spritesheet, 20, 0, 8, 152, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH / 2, PARAMS.BLOCKWIDTH * 9.5); 356 | 357 | if (this.win) { 358 | let FLAG_SPEED_SCALE = 9; 359 | let BLOCK_TOP = 13 * PARAMS.BLOCKWIDTH; 360 | if (this.flagY === this.y + 27) { 361 | ASSET_MANAGER.playAsset("./audio/flagpole.mp3"); 362 | } 363 | // top of the block = 13 * blockwidth - blockwidth 364 | if (this.flagY < (BLOCK_TOP - PARAMS.BLOCKWIDTH)) { 365 | this.flagY += PARAMS.BLOCKWIDTH * TICK * FLAG_SPEED_SCALE; 366 | this.game.disableInput(); 367 | this.game.camera.paused = true; 368 | } 369 | } 370 | 371 | // draw the triangle flag part 372 | ctx.drawImage(this.spritesheet, 2, 1, 16, 16, this.flagX - this.game.camera.x, this.flagY, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 373 | 374 | 375 | if (PARAMS.DEBUG) { 376 | ctx.strokeStyle = 'Red'; 377 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 378 | } 379 | }; 380 | 381 | updateBB() { 382 | this.BB = new BoundingBox(this.x + 9, this.y, PARAMS.BLOCKWIDTH / 7, PARAMS.BLOCKWIDTH * 9.5); 383 | // this.leftBB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 0.5); 384 | // this.rightBB = new BoundingBox(this.x + PARAMS.BLOCKWIDTH * 2, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 0.5); 385 | } 386 | } -------------------------------------------------------------------------------- /convert_tiled.py: -------------------------------------------------------------------------------- 1 | __author__ = "Douglas Johnston" 2 | 3 | import json 4 | import math 5 | import ntpath 6 | import argparse 7 | from os import truncate 8 | import re 9 | import sys 10 | 11 | def main(): 12 | """ 13 | Converts a Tiled JSON file into a more usable file by finding all of the object layers. 14 | It assumes that the name of the object layer is the same as the object 15 | How to use: Run the file using 'python convert_tiled.py (PATH TO LEVEL FILE)' 16 | """ 17 | parser = argparse.ArgumentParser(description='Converts a Tiled JSON file into a more usable file by finding all of the object layers. It assumes that the name of the object layer is the same as the object How to use: Run the file using \'python convert_tiled.py (PATH TO LEVEL FILE)\'') 18 | parser.add_argument('filepath', help="The filepath of the Tiled JSON including the filename") 19 | parser.add_argument('-fp', '--filepath', help="The filepath of the Tiled JSON including the filename") 20 | parser.add_argument('-o', '--overwrite', action='store_true', help="Overwrites the file") 21 | parser.add_argument('-js', '--javascript', action='store_true', help="Saves the file to a JavaScript file instead of a JSON file") 22 | parser.add_argument('-i', '--indent', type=int, default=4, help="The ammount to indent the JSON") 23 | parser.add_argument('-wts', '--widthtosize', action='store_true', help="Replaces the width and height parameters to one size parameter using the blockwidth (default: 16), assumes that if the width of an object is less than the blockwidth, then the size is 1") 24 | parser.add_argument('-bw', '--blockwidth', type=int, help="The blockwidth to calculate the size parameter") 25 | 26 | args = parser.parse_args() 27 | 28 | if args.filepath is None and len(sys.argv) > 1: 29 | path = sys.argv[1] 30 | else: 31 | path = args.filepath 32 | 33 | if path is None or path in ['-fp', '--filepath', '-o', '--overwrite', '-js', '--javascript']: 34 | path = input("Enter the path including the filename: ") 35 | 36 | if args.blockwidth is None and args.widthtosize: 37 | args.blockwidth = 16 38 | elif args.blockwidth is not None: 39 | args.widthtosize = True 40 | 41 | 42 | file_name = ntpath.split(path) 43 | objects = {} 44 | with open(path, 'r+') as file: 45 | level_data = json.load(file) 46 | for layer in level_data["layers"]: 47 | if layer['type'] == "objectgroup": 48 | objects[layer['name']] = [] 49 | for obj in layer['objects']: 50 | if args.widthtosize: 51 | objects[layer['name']].append({ 52 | 'x': obj['x'], 53 | 'y': obj['y'], 54 | 'size': calculate_size(obj['width'], args.blockwidth) 55 | }) 56 | else: 57 | objects[layer['name']].append({ 58 | 'x': obj['x'], 59 | 'y': obj['y'], 60 | 'width': obj['width'], 61 | 'height': obj['height'] 62 | }) 63 | file.close() 64 | if args.javascript: 65 | extension = '.js' 66 | else: 67 | extension = '.json' 68 | if args.overwrite: 69 | out_file = open(path.replace('.json', extension), 'w') 70 | else: 71 | out_file = open(file_name[0][2:] + 'CONVERTED' + extension, 'w') 72 | out_file.write(re.sub(r'(? 0.3; 169 | this.up = gamepad.buttons[12].pressed || gamepad.axes[1] < -0.3; 170 | this.down = gamepad.buttons[13].pressed || gamepad.axes[1] > 0.3; 171 | } 172 | } 173 | 174 | update() { 175 | var entitiesCount = this.entities.length; 176 | 177 | this.gamepadUpdate(); 178 | 179 | for (var i = 0; i < entitiesCount; i++) { 180 | var entity = this.entities[i]; 181 | 182 | if (!entity.removeFromWorld) { 183 | entity.update(); 184 | } 185 | } 186 | 187 | this.camera.update(); 188 | 189 | for (var i = this.entities.length - 1; i >= 0; --i) { 190 | if (this.entities[i].removeFromWorld) { 191 | this.entities.splice(i, 1); 192 | } 193 | } 194 | this.wheel = 0; 195 | }; 196 | 197 | loop() { 198 | this.clockTick = this.timer.tick(); 199 | this.update(); 200 | this.draw(); 201 | 202 | this.click = null; 203 | }; 204 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Super Marriott Brothers 7 | 9 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 |
Super Marriott Brothers

45 |
46 | 47 | 48 |
49 |
50 | Debug Mute Volume 51 |
52 | 53 | -------------------------------------------------------------------------------- /items.js: -------------------------------------------------------------------------------- 1 | class Coin { 2 | constructor(game, x, y) { 3 | Object.assign(this, { game, x, y }); 4 | 5 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 6 | 7 | this.animation = new Animator(ASSET_MANAGER.getAsset("./sprites/coins.png"), 0, 97, 16, 16, 4, 0.1, 0, false, true); 8 | }; 9 | 10 | update() { 11 | 12 | }; 13 | 14 | drawMinimap(ctx, mmX, mmY) { 15 | 16 | } 17 | 18 | draw(ctx) { 19 | this.animation.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 20 | }; 21 | }; 22 | 23 | class CoinPop { 24 | constructor(game, x, y) { 25 | Object.assign(this, { game, x, y }); 26 | 27 | this.game.camera.addCoin(); 28 | 29 | this.velocity = -480; 30 | 31 | this.animation = new Animator(ASSET_MANAGER.getAsset("./sprites/coins.png"), 0, 112, 16, 16, 4, 0.1, 0, false, true); 32 | }; 33 | 34 | update() { 35 | const FALL_ACC = 2025; 36 | 37 | this.velocity += FALL_ACC * this.game.clockTick; 38 | this.y += this.game.clockTick * this.velocity * PARAMS.SCALE; 39 | 40 | if (this.velocity > 400) { 41 | this.removeFromWorld = true; 42 | this.game.addEntity(new Score(this.game, this.x, this.y + PARAMS.BLOCKWIDTH / 2, 200)); 43 | } 44 | }; 45 | 46 | drawMinimap(ctx, mmX, mmY) { 47 | } 48 | 49 | draw(ctx) { 50 | this.animation.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 51 | }; 52 | }; 53 | 54 | class Score { 55 | constructor(game, x, y, score) { 56 | Object.assign(this, { game, x, y, score }); 57 | 58 | this.game.camera.score += this.score; 59 | 60 | this.velocity = -2 * PARAMS.BITWIDTH; 61 | this.elapsed = 0; 62 | }; 63 | 64 | update() { 65 | this.elapsed += this.game.clockTick; 66 | if (this.elapsed > 1) this.removeFromWorld = true; 67 | 68 | this.y += this.game.clockTick * this.velocity * PARAMS.SCALE; 69 | }; 70 | 71 | drawMinimap(ctx, mmX, mmY) { 72 | } 73 | 74 | draw(ctx) { 75 | ctx.font = PARAMS.BLOCKWIDTH / 4 + 'px "Press Start 2P"'; 76 | ctx.fillStyle = "White"; 77 | ctx.fillText(this.score, this.x + (this.score < 1000 ? PARAMS.BLOCKWIDTH / 8 : 0) - this.game.camera.x, this.y); 78 | }; 79 | }; 80 | 81 | class Mushroom { 82 | constructor(game, x, y, brick, type) { 83 | Object.assign(this, { game, x, y, brick, type }); 84 | 85 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/items.png"); 86 | 87 | this.velocity = { x: 0, y: -PARAMS.BLOCKWIDTH }; 88 | 89 | this.emerging = true; 90 | 91 | this.updateBB(); 92 | }; 93 | 94 | updateBB() { 95 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 96 | }; 97 | 98 | update() { 99 | if (this.emerging) { 100 | this.y += this.game.clockTick * this.velocity.y * PARAMS.SCALE; 101 | 102 | if (this.y < this.BB.top - PARAMS.BLOCKWIDTH) { 103 | this.emerging = false; 104 | this.velocity.x = PARAMS.BLOCKWIDTH; 105 | this.velocity.y = 0; 106 | } 107 | } else { 108 | const FALL_ACC = 1800; 109 | 110 | this.velocity.y += FALL_ACC * this.game.clockTick; 111 | this.x += this.game.clockTick * this.velocity.x * PARAMS.SCALE; 112 | this.y += this.game.clockTick * this.velocity.y * PARAMS.SCALE; 113 | this.updateBB(); 114 | 115 | var that = this; 116 | this.game.entities.forEach(function (entity) { 117 | if (entity.BB && that.BB.collide(entity.BB)) { 118 | if (entity instanceof Mario) { 119 | 120 | } else if ((entity instanceof Ground || entity instanceof Brick || entity instanceof Block || entity instanceof Tube) 121 | && (that.BB.bottom - that.velocity.y * that.game.clockTick * PARAMS.SCALE) <= entity.BB.top) { 122 | that.y = entity.BB.top - PARAMS.BLOCKWIDTH; 123 | that.velocity.y = 0; 124 | that.updateBB(); 125 | } else if (entity !== that && !(entity instanceof Flower || entity instanceof Goomba 126 | || entity instanceof KoopaParatroopaRed || entity instanceof KoopaParatroopaGreen 127 | || entity instanceof Koopa || entity instanceof KoopaShell 128 | || entity instanceof PirahnaPlant || entity instanceof HammerBro 129 | || entity instanceof Hammer || entity instanceof FireBar 130 | || entity instanceof FireBar_Fire)) { 131 | that.velocity.x = -that.velocity.x; 132 | } 133 | }; 134 | }); 135 | } 136 | }; 137 | 138 | drawMinimap(ctx, mmX, mmY) { 139 | } 140 | 141 | draw(ctx) { 142 | if (this.type === 'Growth') { 143 | ctx.drawImage(this.spritesheet, 184, 34, 16, 16, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 144 | } else { 145 | ctx.drawImage(this.spritesheet, 214, 34, 16, 16, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 146 | } 147 | if (this.emerging) this.brick.draw(ctx); 148 | 149 | if (PARAMS.DEBUG) { 150 | ctx.strokeStyle = 'Red'; 151 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 152 | } 153 | }; 154 | }; 155 | 156 | class Flower { 157 | constructor(game, x, y, brick) { 158 | Object.assign(this, { game, x, y, brick }); 159 | this.velocity = { x: 0, y: -PARAMS.BLOCKWIDTH }; 160 | this.emerging = true; 161 | this.animation = new Animator(ASSET_MANAGER.getAsset("./sprites/items.png"), 4, 64, 16, 16, 4, 0.15, 14, false, true); 162 | ASSET_MANAGER.playAsset("./audio/power-up-appears.mp3"); 163 | this.updateBB(); 164 | }; 165 | 166 | updateBB() { 167 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 168 | }; 169 | 170 | update() { 171 | if (this.emerging) { 172 | this.y += this.game.clockTick * this.velocity.y * PARAMS.SCALE; 173 | 174 | if (this.y < this.BB.top - PARAMS.BLOCKWIDTH) { 175 | this.emerging = false; 176 | this.velocity.x = PARAMS.BLOCKWIDTH; 177 | this.velocity.y = 0; 178 | } 179 | } else { 180 | this.updateBB(); 181 | } 182 | }; 183 | 184 | drawMinimap(ctx, mmX, mmY) { 185 | } 186 | 187 | draw(ctx) { 188 | this.animation.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 189 | if (this.emerging) this.brick.draw(ctx); 190 | if (PARAMS.DEBUG) { 191 | ctx.strokeStyle = 'Red'; 192 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 193 | } 194 | }; 195 | }; 196 | 197 | class Fireball { 198 | constructor(game, x, y) { 199 | Object.assign(this, { game, x, y }); 200 | this.velocity = { x: 5 * PARAMS.BLOCKWIDTH, y: 2 * PARAMS.BLOCKWIDTH }; 201 | if (this.game.camera.mario.facing == 1) { 202 | this.velocity.x *= -1; 203 | } 204 | this.emerging = true; 205 | this.explode = false; 206 | this.elapsedTime = 0; 207 | this.animationTime = 0.05; 208 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/enemies.png"); 209 | //this.animation = new Animator(ASSET_MANAGER.getAsset("./sprites/enemies.png"), 26, 150, 8, 8, 1, 0.1, 0, false, true); 210 | 211 | // fireball explodes 212 | this.explodeAnimation = new Animator(this.spritesheet, 360, 184, 16, 16, 3, 0.1, 14, false, false); 213 | this.updateBB(); 214 | }; 215 | 216 | updateBB() { 217 | if (this.explode) { 218 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 219 | } else { 220 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH / 2, PARAMS.BLOCKWIDTH / 2); 221 | } 222 | }; 223 | 224 | update() { 225 | const FALL_ACC = 1000; // 1000/2; // 1800 226 | this.updateBB(); 227 | 228 | if (this.y > 768) { 229 | this.removeFromWorld = true; 230 | } 231 | 232 | this.elapsedTime += this.game.clockTick; 233 | if (this.elapsedTime > 4 * this.animationTime) { 234 | this.elapsedTime = 0; 235 | } 236 | 237 | if (!this.explode) { 238 | this.velocity.y += FALL_ACC * this.game.clockTick; 239 | this.x += this.game.clockTick * this.velocity.x * PARAMS.SCALE; 240 | this.y += this.game.clockTick * this.velocity.y * PARAMS.SCALE; 241 | this.updateBB(); 242 | } else { 243 | this.velocity.x = 0; 244 | this.velocity.y = 0; 245 | //this.velocity.y -= FALL_ACC * this.game.clockTick; 246 | } 247 | 248 | var that = this; 249 | this.game.entities.forEach(function (entity) { 250 | if (entity.BB && that.BB.collide(entity.BB)) { 251 | if ((entity instanceof Ground || entity instanceof Brick || entity instanceof Block) 252 | && (that.BB.bottom - that.velocity.y * that.game.clockTick * PARAMS.SCALE) <= entity.BB.top) { 253 | that.y = entity.BB.top - PARAMS.BLOCKWIDTH; 254 | that.velocity.y = -that.velocity.y / 2; 255 | that.updateBB(); 256 | } else if (entity instanceof Tube || entity instanceof SideTube || entity instanceof Block || 257 | (entity instanceof Brick && (that.BB.collide(entity.leftBB) || that.BB.collide(entity.rightBB)))) { 258 | that.explode = true; 259 | } else if ((entity instanceof Goomba || entity instanceof Koopa || entity instanceof PirahnaPlant || entity instanceof KoopaParatroopaGreen || entity instanceof KoopaParatroopaRed) && !entity.dead) { 260 | entity.dead = true; 261 | that.explode = true; 262 | 263 | if (entity instanceof PirahnaPlant && that.BB.collide(entity.tube.BB)) { 264 | entity.dead = false; 265 | } 266 | } 267 | }; 268 | }); 269 | }; 270 | 271 | drawMinimap(ctx, mmX, mmY) { 272 | } 273 | 274 | draw(ctx) { 275 | if (this.explode) { 276 | this.explodeAnimation.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 277 | if (this.explodeAnimation.isDone()) { 278 | this.removeFromWorld = true; 279 | } 280 | } else { 281 | if (this.elapsedTime < this.animationTime) { 282 | ctx.drawImage(this.spritesheet, 26, 150, 8, 8, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH/2, PARAMS.BLOCKWIDTH/2); 283 | } else if (this.elapsedTime < 2 * this.animationTime) { 284 | ctx.drawImage(this.spritesheet, 26, 165, 8, 8, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH/2, PARAMS.BLOCKWIDTH/2); 285 | } else if (this.elapsedTime < 3 * this.animationTime) { 286 | ctx.drawImage(this.spritesheet, 41, 150, 8, 8, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH/2, PARAMS.BLOCKWIDTH/2); 287 | } else { 288 | ctx.drawImage(this.spritesheet, 41, 165, 8, 8, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH/2, PARAMS.BLOCKWIDTH/2); 289 | } 290 | 291 | } 292 | 293 | if (PARAMS.DEBUG) { 294 | ctx.strokeStyle = 'Red'; 295 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 296 | } 297 | }; 298 | } -------------------------------------------------------------------------------- /levels.js: -------------------------------------------------------------------------------- 1 | var levelOne = { 2 | music: "./music/overworld.mp3", 3 | hurry_music: "./music/overworld-hurry.mp3", 4 | label: "1-1", 5 | underground: false, 6 | ground: [{ x: 0, y: 14, size: 69 }, { x: 71, y: 14, size: 15 }, { x: 89, y: 14, size: 63 }, { x: 154, y: 14, size: 69 }], 7 | bighills: [{ x: 0, y: 11.5 }, { x: 48, y: 11.5 }, { x: 95, y: 11.5 }, { x: 143, y: 11.5 }, { x: 189, y: 11.5 }], 8 | hills: [{ x: 16, y: 12.75 }, { x: 64, y: 12.75 }, { x: 111, y: 12.75 }, { x: 159, y: 12.75 }], 9 | bushes: [{ x: 11.5, y: 13, size: 3 }, { x: 23.5, y: 13, size: 1 }, { x: 41.5, y: 13, size: 2 }, 10 | { x: 59.5, y: 13, size: 3 }, { x: 71.5, y: 13, size: 1 }, { x: 89.5, y: 13, size: 2 }, 11 | { x: 106.5, y: 13, size: 3 }, { x: 118.5, y: 13, size: 1 }, { x: 136.5, y: 13, size: 2 }, 12 | { x: 156.5, y: 13, size: 1 }, { x: 166.5, y: 13, size: 1 }], 13 | clouds: [{ x: 8.5, y: 4, size: 1 }, { x: 19.5, y: 3, size: 1 }, { x: 27.5, y: 4, size: 3 }, 14 | { x: 36.5, y: 3, size: 2 }, { x: 56.5, y: 4, size: 1 }, { x: 67.5, y: 3, size: 1 }, 15 | { x: 75.5, y: 4, size: 3 }, { x: 83.5, y: 3, size: 2 }, { x: 103.5, y: 4, size: 1 }, 16 | { x: 114.5, y: 3, size: 1 }, { x: 122.5, y: 4, size: 3 }, { x: 131.5, y: 3, size: 2 }, 17 | { x: 151.5, y: 4, size: 1 }, { x: 162.5, y: 3, size: 1 }, { x: 170.5, y: 4, size: 3 }, 18 | { x: 179.5, y: 3, size: 2 }, { x: 197.5, y: 4, size: 1 }], 19 | bigcastles: [{ x: 199, y: 3, size: 1 }], 20 | bricks: [{ x: 20, y: 10, type: 1, prize: "None" }, 21 | { x: 22, y: 10, type: 1, prize: "None" }, 22 | { x: 24, y: 10, type: 1, prize: "None" }, 23 | { x: 77, y: 10, type: 1, prize: "None" }, 24 | { x: 79, y: 10, type: 1, prize: "None" }, 25 | { x: 80, y: 6, type: 1, prize: "None" }, 26 | { x: 81, y: 6, type: 1, prize: "None" }, 27 | { x: 82, y: 6, type: 1, prize: "None" }, 28 | { x: 83, y: 6, type: 1, prize: "None" }, 29 | { x: 84, y: 6, type: 1, prize: "None" }, 30 | { x: 85, y: 6, type: 1, prize: "None" }, 31 | { x: 86, y: 6, type: 1, prize: "None" }, 32 | { x: 87, y: 6, type: 1, prize: "None" }, 33 | { x: 90, y: 6, type: 1, prize: "None" }, 34 | { x: 91, y: 6, type: 1, prize: "None" }, 35 | { x: 92, y: 6, type: 1, prize: "None" }, 36 | { x: 93, y: 10, type: 1, prize: "Coins" }, 37 | { x: 99, y: 10, type: 1, prize: "None" }, 38 | { x: 100, y: 10, type: 1, prize: "Star" }, 39 | { x: 117, y: 10, type: 1, prize: "None" }, 40 | { x: 120, y: 6, type: 1, prize: "None" }, 41 | { x: 121, y: 6, type: 1, prize: "None" }, 42 | { x: 122, y: 6, type: 1, prize: "None" }, 43 | { x: 127, y: 6, type: 1, prize: "None" }, 44 | { x: 128, y: 10, type: 1, prize: "None" }, 45 | { x: 129, y: 10, type: 1, prize: "None" }, 46 | { x: 130, y: 6, type: 1, prize: "None" }, 47 | { x: 167, y: 10, type: 1, prize: "None" }, 48 | { x: 168, y: 10, type: 1, prize: "None" }, 49 | { x: 170, y: 10, type: 1, prize: "None" }, 50 | { x: 16, y: 10, type: 2, prize: "Coin" }, 51 | { x: 21, y: 10, type: 2, prize: "Growth" }, 52 | { x: 23, y: 10, type: 2, prize: "Coin" }, 53 | { x: 22, y: 6, type: 2, prize: "Coin" }, 54 | { x: 64, y: 9, type: 0, prize: "1up" }, 55 | { x: 78, y: 10, type: 2, prize: "Growth" }, 56 | { x: 93, y: 6, type: 2, prize: "Growth" }, 57 | { x: 105, y: 10, type: 2, prize: "Coin" }, 58 | { x: 108, y: 10, type: 2, prize: "Coin" }, 59 | { x: 108, y: 6, type: 2, prize: "Growth" }, 60 | { x: 111, y: 10, type: 2, prize: "Coin" }, 61 | { x: 128, y: 6, type: 2, prize: "Coin" }, 62 | { x: 129, y: 6, type: 2, prize: "Coin" }, 63 | { x: 169, y: 10, type: 2, prize: "Coin" }], 64 | tubes: [{ x: 28, y: 12, size: 1, destination: false }, 65 | { x: 38, y: 11, size: 2, destination: false }, 66 | { x: 46, y: 10, size: 3, destination: false }, 67 | { x: 57, y: 10, size: 3, destination: true }, 68 | { x: 162, y: 12, size: 1, destination: false }, 69 | { x: 176, y: 12, size: 1, destination: false }], 70 | flags: [{ x: 195.3, y: 3.55, size: 1 }], 71 | // flags: [{ x: 15, y: 3.55, size: 1 }], 72 | blocks: [{ x: 133, y: 13, size: 4 }, 73 | { x: 134, y: 12, size: 3 }, 74 | { x: 135, y: 11, size: 2 }, 75 | { x: 136, y: 10, size: 1 }, 76 | { x: 139, y: 10, size: 1 }, 77 | { x: 139, y: 11, size: 2 }, 78 | { x: 139, y: 12, size: 3 }, 79 | { x: 139, y: 13, size: 4 }, 80 | { x: 147, y: 13, size: 5 }, 81 | { x: 148, y: 12, size: 4 }, 82 | { x: 149, y: 11, size: 3 }, 83 | { x: 150, y: 10, size: 2 }, 84 | { x: 154, y: 13, size: 4 }, 85 | { x: 154, y: 12, size: 3 }, 86 | { x: 154, y: 11, size: 2 }, 87 | { x: 154, y: 10, size: 1 }, 88 | { x: 178, y: 13, size: 9 }, 89 | { x: 179, y: 12, size: 8 }, 90 | { x: 180, y: 11, size: 7 }, 91 | { x: 181, y: 10, size: 6 }, 92 | { x: 182, y: 9, size: 5 }, 93 | { x: 183, y: 8, size: 4 }, 94 | { x: 184, y: 7, size: 3 }, 95 | { x: 185, y: 6, size: 2 }, 96 | { x: 195, y: 13, size: 1 }], 97 | goombas: [{ x: 22, y:13 }, 98 | { x: 40, y: 13 }, 99 | { x: 51, y: 13 }, 100 | { x: 52.5, y: 13 }, 101 | { x: 80, y: 5 }, 102 | { x: 82, y: 5 }, 103 | { x: 96, y: 13 }, 104 | { x: 97.5, y: 13 }, 105 | { x: 113, y: 13 }, 106 | { x: 114.5, y: 13 }, 107 | { x: 123, y: 13 }, 108 | { x: 124.5, y: 13 }, 109 | { x: 127, y: 13 }, 110 | { x: 128.5, y: 13 }], 111 | koopas: [{ x: 106, y: 12.5, facing: 1}], 112 | // Un-comment to test paratroopas 113 | // koopaParatroopaGreen: [{x: 30, y: 12.5, facing: 1}], 114 | // koopaParatroopaRed: [{x: 20, y: 12.5, facing: 1}] 115 | }; 116 | 117 | var bonusLevelOne = { 118 | music: "./music/underworld.mp3", 119 | hurry_music: "./music/underworld-hurry.mp3", 120 | label: "1-1", 121 | underground: false, 122 | bricks: [{ x: 0, y: 0, type: 1, prize: "None" }, 123 | { x: 0, y: 1, type: 1, prize: "None" }, 124 | { x: 0, y: 2, type: 1, prize: "None" }, 125 | { x: 0, y: 3, type: 1, prize: "None" }, 126 | { x: 0, y: 4, type: 1, prize: "None" }, 127 | { x: 0, y: 5, type: 1, prize: "None" }, 128 | { x: 0, y: 6, type: 1, prize: "None" }, 129 | { x: 0, y: 7, type: 1, prize: "None" }, 130 | { x: 0, y: 8, type: 1, prize: "None" }, 131 | { x: 0, y: 9, type: 1, prize: "None" }, 132 | { x: 0, y: 10, type: 1, prize: "None" }, 133 | { x: 0, y: 11, type: 1, prize: "None" }, 134 | { x: 0, y: 12, type: 1, prize: "None" }, 135 | { x: 0, y: 13, type: 1, prize: "None" }, 136 | { x: 4, y: 0, type: 1, prize: "None" }, 137 | { x: 5, y: 0, type: 1, prize: "None" }, 138 | { x: 6, y: 0, type: 1, prize: "None" }, 139 | { x: 7, y: 0, type: 1, prize: "None" }, 140 | { x: 8, y: 0, type: 1, prize: "None" }, 141 | { x: 9, y: 0, type: 1, prize: "None" }, 142 | { x: 10, y: 0, type: 1, prize: "None" }, 143 | { x: 4, y: 11, type: 1, prize: "None" }, 144 | { x: 5, y: 11, type: 1, prize: "None" }, 145 | { x: 6, y: 11, type: 1, prize: "None" }, 146 | { x: 7, y: 11, type: 1, prize: "None" }, 147 | { x: 8, y: 11, type: 1, prize: "None" }, 148 | { x: 9, y: 11, type: 1, prize: "None" }, 149 | { x: 10, y: 11, type: 1, prize: "None" }, 150 | { x: 4, y: 12, type: 1, prize: "None" }, 151 | { x: 5, y: 12, type: 1, prize: "None" }, 152 | { x: 6, y: 12, type: 1, prize: "None" }, 153 | { x: 7, y: 12, type: 1, prize: "None" }, 154 | { x: 8, y: 12, type: 1, prize: "None" }, 155 | { x: 9, y: 12, type: 1, prize: "None" }, 156 | { x: 10, y: 12, type: 1, prize: "None" }, 157 | { x: 4, y: 13, type: 1, prize: "None" }, 158 | { x: 5, y: 13, type: 1, prize: "None" }, 159 | { x: 6, y: 13, type: 1, prize: "None" }, 160 | { x: 7, y: 13, type: 1, prize: "None" }, 161 | { x: 8, y: 13, type: 1, prize: "None" }, 162 | { x: 9, y: 13, type: 1, prize: "None" }, 163 | { x: 10, y: 13, type: 1, prize: "None" }], 164 | tubes: [{ x: 13, y: 12, size: 1, destination: true, side: true }, 165 | { x: 15, y: -1, size: 14, destination: false }], 166 | coins: [{ x: 4, y: 10 }, 167 | { x: 5, y: 10 }, 168 | { x: 6, y: 10 }, 169 | { x: 7, y: 10 }, 170 | { x: 8, y: 10 }, 171 | { x: 9, y: 10 }, 172 | { x: 10, y: 10 }, 173 | { x: 4, y: 9 }, 174 | { x: 5, y: 9 }, 175 | { x: 6, y: 9 }, 176 | { x: 7, y: 9 }, 177 | { x: 8, y: 9 }, 178 | { x: 9, y: 9 }, 179 | { x: 10, y: 9 }, 180 | { x: 5, y: 8 }, 181 | { x: 6, y: 8 }, 182 | { x: 7, y: 8 }, 183 | { x: 8, y: 8 }, 184 | { x: 9, y: 8 }], 185 | ground: [{ x: 0, y: 14, size: 34 }] 186 | 187 | }; 188 | 189 | var levelTwo = { 190 | underground: true, // NOTE: PLEASE ADD THIS BOOLEAN PROPERTY WHEN TESTING LEVEL 1-2 191 | music: "./music/underworld.mp3", 192 | // hurry_music: "./music/underworld-hurry.mp3", 193 | label: "1-2", 194 | ground: [{ x: 0, y: 13, size: 80 }, { x:83, y: 13, size: 37}, {x: 122, y: 13, size: 2}, 195 | {x: 126, y: 13, size: 12}, {x: 145, y: 13, size: 7}, {x: 159, y: 13, size: 32}], 196 | bricks: [], 197 | blocks: [{ x: 17, y: 12, size: 1 }, 198 | { x: 19, y: 12, size: 1 }, { x: 19, y: 11, size: 1 }, 199 | { x: 21, y: 12, size: 1 }, { x: 21, y: 11, size: 1 }, { x: 21, y: 10, size: 1 }, 200 | { x: 23, y: 12, size: 1 }, { x: 23, y: 11, size: 1 }, { x: 23, y: 10, size: 1 }, { x: 23, y: 9, size: 1 }, 201 | { x: 25, y: 12, size: 1 }, { x: 25, y: 11, size: 1 }, { x: 25, y: 10, size: 1 }, { x: 25, y: 9, size: 1 }, 202 | { x: 27, y: 12, size: 1 }, { x: 27, y: 11, size: 1 }, { x: 27, y: 10, size: 1 }, 203 | { x: 31, y: 12, size: 1 }, { x: 31, y: 11, size: 1 }, { x: 31, y: 10, size: 1 }, 204 | { x: 33, y: 12, size: 1 }, { x: 33, y: 11, size: 1 }, 205 | { x: 133, y: 12, size: 5 }, 206 | { x: 134, y: 11, size: 4 }, 207 | { x: 135, y: 10, size: 3 }, 208 | { x: 136, y: 9, size: 2 }], 209 | coins: [{ x: 40, y: 8 }, { x: 45, y: 8 }, { x: 41, y: 5 }, { x: 42, y: 5 }, { x: 43, y: 5 }, { x: 44, y: 5 }, 210 | { x: 58, y: 8 }, { x: 59, y: 8 }, { x: 60, y: 8 }, { x: 61, y: 8 }, 211 | { x: 68, y: 8 }, 212 | { x: 84, y: 5 }, { x: 85, y: 5 }, { x: 86, y: 5 }, { x: 87, y: 5 }, { x: 88, y: 5 }, { x: 89, y: 5 }], 213 | goombas: [{ x: 16, y:12 }, { x: 17, y:11 }, 214 | { x: 29, y:12 }, 215 | { x: 62, y:12 }, { x: 64, y:12 }, 216 | { x: 73, y:4 }, { x: 76, y:8 }, { x: 77.5, y:8 }, 217 | { x: 99, y:12 }, { x: 100.5, y:12 }, { x: 102, y:12 }, 218 | { x: 113, y:12 }, 219 | { x: 135, y:9 }, { x: 136.5, y:8 }], 220 | koopas: [{ x: 44, y:11.5, facing: 1}, { x: 45, y:11.5, facing: 1 }, 221 | { x: 59, y:11.5, facing: 1 }, 222 | { x: 146, y:11.5, facing: 1 }], 223 | tubes: [{ x: 103, y: 10, size: 2, destination: true }, 224 | { x: 109, y: 9, size: 3, destination: false }, 225 | { x: 115, y: 11, size: 1, destination: false }, 226 | { x: 166, y: 8, size: 1, destination: false, side: true}, 227 | { x: 168, y: 2, size: 7, destination: false }, 228 | { x: 178, y: 10, size: 2, destination: false }, 229 | { x: 182, y: 10, size: 2, destination: false }, 230 | { x: 186, y: 10, size: 2, destination: false }], 231 | lifts: [{ x: 140, y: 7, goingDown: true }, {x: 155, y: 9, goingDown: false }] 232 | }; 233 | 234 | var bonusLevelTwo = { 235 | label: "1-2", 236 | ground: [{ x: 0, y: 11, size: 17 }], 237 | coins: [{ x: 4, y: 6 }, { x: 5, y: 6 }, { x: 6, y: 6 }, { x: 7, y: 6 }, { x: 8, y: 6 }, { x: 9, y: 6 }, { x: 10, y: 6 }, { x: 11, y: 6 }, 238 | { x: 3, y: 10 }, { x: 4, y: 10 }, { x: 5, y: 10 }, { x: 6, y: 10 }, { x: 7, y: 10 }, { x: 8, y: 10 }, { x: 9, y: 10 }, { x: 10, y: 10 }, { x: 11, y: 10 }], 239 | tubes: [{ x: 13, y: 9, size: 1, destination: true, side: true }, 240 | { x: 15, y: -1, size: 11, destination: false }], 241 | bricks: [{ x: 0, y: 0, type: 1, prize: "None" }, 242 | { x: 0, y: 1, type: 1, prize: "None" }, 243 | { x: 0, y: 2, type: 1, prize: "None" }, 244 | { x: 0, y: 3, type: 1, prize: "None" }, 245 | { x: 0, y: 4, type: 1, prize: "None" }, 246 | { x: 0, y: 5, type: 1, prize: "None" }, 247 | { x: 0, y: 6, type: 1, prize: "None" }, 248 | { x: 0, y: 7, type: 1, prize: "None" }, 249 | { x: 0, y: 8, type: 1, prize: "None" }, 250 | { x: 0, y: 9, type: 1, prize: "None" }, 251 | { x: 0, y: 10, type: 1, prize: "None" }, 252 | { x: 3, y: 0, type: 1, prize: "None" }, 253 | { x: 4, y: 0, type: 1, prize: "None" }, 254 | { x: 5, y: 0, type: 1, prize: "None" }, 255 | { x: 6, y: 0, type: 1, prize: "None" }, 256 | { x: 7, y: 0, type: 1, prize: "None" }, 257 | { x: 8, y: 0, type: 1, prize: "None" }, 258 | { x: 9, y: 0, type: 1, prize: "None" }, 259 | { x: 10, y: 0, type: 1, prize: "None" }, 260 | { x: 11, y: 0, type: 1, prize: "None" }, 261 | { x: 12, y: 0, type: 1, prize: "None" }, 262 | { x: 13, y: 0, type: 1, prize: "None" }, 263 | { x: 14, y: 0, type: 1, prize: "None" }, 264 | { x: 3, y: 1, type: 1, prize: "None" }, 265 | { x: 4, y: 1, type: 1, prize: "None" }, 266 | { x: 5, y: 1, type: 1, prize: "None" }, 267 | { x: 6, y: 1, type: 1, prize: "None" }, 268 | { x: 7, y: 1, type: 1, prize: "None" }, 269 | { x: 8, y: 1, type: 1, prize: "None" }, 270 | { x: 9, y: 1, type: 1, prize: "None" }, 271 | { x: 10, y: 1, type: 1, prize: "None" }, 272 | { x: 11, y: 1, type: 1, prize: "None" }, 273 | { x: 12, y: 1, type: 1, prize: "None" }, 274 | { x: 13, y: 1, type: 1, prize: "None" }, 275 | { x: 14, y: 1, type: 1, prize: "None" }, 276 | { x: 3, y: 2, type: 1, prize: "None" }, 277 | { x: 4, y: 2, type: 1, prize: "None" }, 278 | { x: 5, y: 2, type: 1, prize: "None" }, 279 | { x: 6, y: 2, type: 1, prize: "None" }, 280 | { x: 7, y: 2, type: 1, prize: "None" }, 281 | { x: 8, y: 2, type: 1, prize: "None" }, 282 | { x: 9, y: 2, type: 1, prize: "None" }, 283 | { x: 10, y: 2, type: 1, prize: "None" }, 284 | { x: 11, y: 2, type: 1, prize: "None" }, 285 | { x: 12, y: 2, type: 1, prize: "None" }, 286 | { x: 13, y: 2, type: 1, prize: "None" }, 287 | { x: 14, y: 2, type: 1, prize: "None" }, 288 | { x: 3, y: 3, type: 1, prize: "None" }, 289 | { x: 4, y: 3, type: 1, prize: "None" }, 290 | { x: 5, y: 3, type: 1, prize: "None" }, 291 | { x: 6, y: 3, type: 1, prize: "None" }, 292 | { x: 7, y: 3, type: 1, prize: "None" }, 293 | { x: 8, y: 3, type: 1, prize: "None" }, 294 | { x: 9, y: 3, type: 1, prize: "None" }, 295 | { x: 10, y: 3, type: 1, prize: "None" }, 296 | { x: 11, y: 3, type: 1, prize: "None" }, 297 | { x: 12, y: 3, type: 1, prize: "None" }, 298 | { x: 13, y: 3, type: 1, prize: "None" }, 299 | { x: 14, y: 3, type: 1, prize: "None" }, 300 | { x: 3, y: 7, type: 1, prize: "None" }, 301 | { x: 4, y: 7, type: 1, prize: "None" }, 302 | { x: 5, y: 7, type: 1, prize: "None" }, 303 | { x: 6, y: 7, type: 1, prize: "None" }, 304 | { x: 7, y: 7, type: 1, prize: "None" }, 305 | { x: 8, y: 7, type: 1, prize: "None" }, 306 | { x: 9, y: 7, type: 1, prize: "None" }, 307 | { x: 10, y: 7, type: 1, prize: "None" }, 308 | { x: 11, y: 7, type: 1, prize: "None" }, 309 | { x: 12, y: 7, type: 1, prize: "Coins" }, 310 | { x: 13, y: 7, type: 1, prize: "None" }, 311 | { x: 14, y: 7, type: 1, prize: "None" }, 312 | { x: 13, y: 4, type: 1, prize: "None" }, 313 | { x: 14, y: 4, type: 1, prize: "None" }, 314 | { x: 13, y: 5, type: 1, prize: "None" }, 315 | { x: 14, y: 5, type: 1, prize: "None" }, 316 | { x: 13, y: 6, type: 1, prize: "None" }, 317 | { x: 14, y: 6, type: 1, prize: "None" }, 318 | { x: 13, y: 8, type: 1, prize: "None" }, 319 | { x: 14, y: 8, type: 1, prize: "None" }] 320 | } 321 | 322 | function loadLevel2Bricks(e) { 323 | for (var i = 0; i < 11; i++) { 324 | e.push({ x: 0, y: i+2, type: 1, prize: "None"}); 325 | } 326 | 327 | for (var i = 0; i < 83; i++) { 328 | e.push({ x: i + 6, y: 2, type: 1, prize: "None"}); 329 | } 330 | 331 | e.push({ x: 89, y: 2, type: 1, prize: "1up"}); 332 | 333 | for (var i = 0; i < 48; i++) { 334 | e.push({ x: i + 90, y: 2, type: 1, prize: "None"}); 335 | } 336 | 337 | for (var i = 0; i < 2; i++) { 338 | e.push({ x: 54, y : i + 3, type: 1, prize: "None"}); 339 | e.push({ x: 55, y : i + 3, type: 1, prize: "None"}); 340 | 341 | e.push({ x: 58, y : i + 3, type: 1, prize: "None"}); 342 | e.push({ x: 59, y : i + 3, type: 1, prize: "None"}); 343 | e.push({ x: 60, y : i + 3, type: 1, prize: "None"}); 344 | e.push({ x: 61, y : i + 3, type: 1, prize: "None"}); 345 | e.push({ x: 62, y : i + 3, type: 1, prize: "None"}); 346 | e.push({ x: 63, y : i + 3, type: 1, prize: "None"}); 347 | 348 | e.push({ x: 66, y : i + 3, type: 1, prize: "None"}); 349 | e.push({ x: 67, y : i + 3, type: 1, prize: "None"}); 350 | e.push({ x: 68, y : i + 3, type: 1, prize: "None"}); 351 | e.push({ x: 69, y : i + 3, type: 1, prize: "None"}); 352 | 353 | e.push({ x: 76, y : i + 3, type: 1, prize: "None"}); 354 | e.push({ x: 77, y : i + 3, type: 1, prize: "None"}); 355 | e.push({ x: 78, y : i + 3, type: 1, prize: "None"}); 356 | e.push({ x: 79, y : i + 3, type: 1, prize: "None"}); 357 | } 358 | 359 | for (var i = 0; i < 5; i++) { 360 | e.push({ x: 52, y : i + 5, type: 1, prize: "None"}); 361 | e.push({ x: 53, y : i + 5, type: 1, prize: "None"}); 362 | 363 | e.push({ x: 62, y : i + 5, type: 1, prize: "None"}); 364 | e.push({ x: 63, y : i + 5, type: 1, prize: "None"}); 365 | 366 | e.push({ x: 67, y : i + 5, type: 1, prize: "None"}); 367 | 368 | e.push({ x: 72, y : i + 5, type: 1, prize: "None"}); 369 | if(i === 3) 370 | e.push({ x: 73, y : i + 5, type: 1, prize: "Coins"}); 371 | else 372 | e.push({ x: 73, y : i + 5, type: 1, prize: "None"}); 373 | } 374 | 375 | e.push({ x: 54, y : 9, type: 1, prize: "None"}); 376 | e.push({ x: 55, y : 9, type: 1, prize: "None"}); 377 | 378 | e.push({ x: 58, y : 9, type: 1, prize: "None"}); 379 | e.push({ x: 59, y : 9, type: 1, prize: "None"}); 380 | e.push({ x: 60, y : 9, type: 1, prize: "None"}); 381 | e.push({ x: 61, y : 9, type: 1, prize: "None"}); 382 | 383 | e.push({ x: 68, y : 9, type: 1, prize: "None"}); 384 | e.push({ x: 69, y : 9, type: 1, prize: "None"}); 385 | 386 | e.push({ x: 76, y : 9, type: 1, prize: "None"}); 387 | e.push({ x: 77, y : 9, type: 1, prize: "None"}); 388 | e.push({ x: 78, y : 9, type: 1, prize: "None"}); 389 | e.push({ x: 79, y : 9, type: 1, prize: "None"}); 390 | 391 | e.push({ x: 54, y : 10, type: 1, prize: "None"}); 392 | e.push({ x: 55, y : 10, type: 1, prize: "None"}); 393 | e.push({ x: 54, y : 11, type: 1, prize: "None"}); 394 | e.push({ x: 55, y : 11, type: 1, prize: "None"}); 395 | 396 | e.push({ x: 69, y : 8, type: 1, prize: "Growth"}); 397 | 398 | for (var i = 0; i < 2; i++) { 399 | for (var j = 0; j < 6; j++) { 400 | e.push({ x: 84 + j, y: 7 + i, type: 1, prize: "None"}); 401 | } 402 | } 403 | 404 | for (var i = 0; i < 3; i++) { 405 | e.push({ x: 39, y : 7 + i, type: 1, prize: "None"}); 406 | 407 | e.push({ x: 41, y : 7 + i, type: 1, prize: "None"}); 408 | 409 | e.push({ x: 44, y : 7 + i, type: 1, prize: "None"}); 410 | 411 | if (i === 0) 412 | e.push({ x: 46, y : 7 + i, type: 1, prize: "Growth"}); 413 | else 414 | e.push({ x: 46, y : 7 + i, type: 1, prize: "None"}); 415 | } 416 | 417 | e.push({ x: 42, y : 7, type: 1, prize: "None"}); 418 | e.push({ x: 43, y : 7, type: 1, prize: "None"}); 419 | 420 | e.push({ x: 40, y : 9, type: 1, prize: "None"}); 421 | e.push({ x: 45, y : 9, type: 1, prize: "None"}); 422 | 423 | e.push({ x: 29, y : 8, type: 1, prize: "Coins"}); 424 | 425 | e.push({ x: 10, y : 9, type: 2, prize: "Growth"}); 426 | e.push({ x: 11, y : 9, type: 2, prize: "Coin"}); 427 | e.push({ x: 12, y : 9, type: 2, prize: "Coin"}); 428 | e.push({ x: 13, y : 9, type: 2, prize: "Coin"}); 429 | e.push({ x: 14, y : 9, type: 2, prize: "Coin"}); 430 | 431 | e.push({ x: 122, y : 10, type: 1, prize: "None"}); 432 | e.push({ x: 123, y : 10, type: 1, prize: "None"}); 433 | e.push({ x: 122, y : 11, type: 1, prize: "None"}); 434 | e.push({ x: 123, y : 11, type: 1, prize: "None"}); 435 | e.push({ x: 122, y : 12, type: 1, prize: "None"}); 436 | e.push({ x: 123, y : 12, type: 1, prize: "None"}); 437 | 438 | e.push({ x: 145, y : 8, type: 1, prize: "None"}); 439 | e.push({ x: 146, y : 8, type: 1, prize: "None"}); 440 | e.push({ x: 147, y : 8, type: 1, prize: "None"}); 441 | e.push({ x: 148, y : 8, type: 1, prize: "None"}); 442 | e.push({ x: 149, y : 8, type: 1, prize: "Growth"}); 443 | 444 | // after platform 445 | for (var i = 0; i < 7; i++) { 446 | e.push({ x: 160 + i, y : 2, type: 1, prize: "None"}); 447 | } 448 | 449 | for (var i = 0; i < 17; i++) { 450 | e.push({ x: 169 + i, y : 2, type: 1, prize: "None"}); 451 | } 452 | 453 | for (var i = 0; i < 7; i++) { 454 | for (var j = 0; j < 7; j++) { 455 | e.push({ x: 169 + i, y : 3 + j, type: 1, prize: "None"}); 456 | } 457 | } 458 | 459 | for (var i = 0; i < 3; i++) { 460 | for (var j = 0; j < 17; j++) { 461 | e.push({ x: 159 + j, y : 10 + i, type: 1, prize: "None"}); 462 | } 463 | } 464 | 465 | for (var i = 0; i < 11; i++) { 466 | for (var j = 0; j < 2; j++) { 467 | e.push({ x: 189 + j, y : 2 + i, type: 1, prize: "None"}); 468 | } 469 | } 470 | } 471 | 472 | loadLevel2Bricks(levelTwo.bricks); 473 | 474 | var credits = { 475 | text: [ 476 | " 2022 Students", 477 | "• KV", 478 | " Merge requests", 479 | "• Raz", 480 | " Gamepad Support", 481 | " Credits", 482 | " ", 483 | " 2021 Students", 484 | "• Seoungdeok Jeon", 485 | " Fire Bar", 486 | "• Albert Lin", 487 | " Fire Mario", 488 | "• Darryl James", 489 | " Piranha Plant", 490 | "• Victor Chau", 491 | " Level 1-2", 492 | "• Daniel Machen", 493 | " Koopa Shell", 494 | "• James Wedum", 495 | " Brick Breaking", 496 | "• Colin Chan/Khue Nguyen", 497 | " Palette Switching", 498 | "• Timmy Roma", 499 | " Hammer Bros", 500 | "• Kyle Oslin", 501 | " Moving Platforms", 502 | " ", 503 | "• v1.0 - Chris Marriott", 504 | "• OG Engine - Seth Ladd" 505 | ] 506 | } -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | var ASSET_MANAGER = new AssetManager(); 3 | 4 | // spritesheets 5 | ASSET_MANAGER.queueDownload("./sprites/mario.png"); 6 | ASSET_MANAGER.queueDownload("./sprites/luigi.png"); 7 | ASSET_MANAGER.queueDownload("./sprites/enemies.png"); 8 | ASSET_MANAGER.queueDownload("./sprites/tiles.png"); 9 | ASSET_MANAGER.queueDownload("./sprites/ground.png"); 10 | ASSET_MANAGER.queueDownload("./sprites/bricks.png"); 11 | ASSET_MANAGER.queueDownload("./sprites/items.png"); 12 | ASSET_MANAGER.queueDownload("./sprites/coins.png"); 13 | ASSET_MANAGER.queueDownload("./sprites/firebar_fire.png"); 14 | ASSET_MANAGER.queueDownload("./sprites/title.png"); 15 | ASSET_MANAGER.queueDownload("./sprites/underground_stuff.png"); 16 | ASSET_MANAGER.queueDownload("./sprites/title_mushroom.png"); 17 | ASSET_MANAGER.queueDownload("./sprites/mouse_wheel.png"); 18 | ASSET_MANAGER.queueDownload("./sprites/castle_big.png"); 19 | ASSET_MANAGER.queueDownload("./sprites/flag.png"); 20 | 21 | // music 22 | ASSET_MANAGER.queueDownload("./music/overworld.mp3"); 23 | ASSET_MANAGER.queueDownload("./music/underworld.mp3"); 24 | ASSET_MANAGER.queueDownload("./music/overworld-hurry.mp3"); 25 | ASSET_MANAGER.queueDownload("./music/underworld-hurry.mp3"); 26 | ASSET_MANAGER.queueDownload("./audio/level-clear.mp3"); 27 | 28 | // sound effects 29 | ASSET_MANAGER.queueDownload("./audio/small-jump.mp3"); 30 | ASSET_MANAGER.queueDownload("./audio/super-jump.mp3"); 31 | ASSET_MANAGER.queueDownload("./audio/stomp.mp3"); 32 | ASSET_MANAGER.queueDownload("./audio/block.mp3"); 33 | ASSET_MANAGER.queueDownload("./audio/bump.wav"); 34 | ASSET_MANAGER.queueDownload("./audio/coin.mp3"); 35 | ASSET_MANAGER.queueDownload("./audio/power-up-appears.mp3"); 36 | ASSET_MANAGER.queueDownload("./audio/power-up.mp3"); 37 | ASSET_MANAGER.queueDownload("./audio/flagpole.mp3"); 38 | ASSET_MANAGER.queueDownload("./audio/fireball.mp3"); 39 | 40 | ASSET_MANAGER.downloadAll(function () { 41 | var gameEngine = new GameEngine(); 42 | 43 | ASSET_MANAGER.autoRepeat("./music/overworld.mp3"); 44 | ASSET_MANAGER.autoRepeat("./music/underworld.mp3"); 45 | ASSET_MANAGER.autoRepeat("./music/overworld-hurry.mp3"); 46 | ASSET_MANAGER.autoRepeat("./music/underworld-hurry.mp3"); 47 | 48 | PARAMS.BLOCKWIDTH = PARAMS.BITWIDTH * PARAMS.SCALE; 49 | 50 | var canvas = document.getElementById('gameWorld'); 51 | var ctx = canvas.getContext('2d'); 52 | 53 | PARAMS.CANVAS_WIDTH = canvas.width; 54 | PARAMS.CANVAS_HEIGHT = canvas.height; 55 | 56 | gameEngine.init(ctx); 57 | 58 | new SceneManager(gameEngine); 59 | 60 | gameEngine.start(); 61 | }); 62 | -------------------------------------------------------------------------------- /mario.js: -------------------------------------------------------------------------------- 1 | class Mario { 2 | constructor(game, x, y, luigi) { 3 | Object.assign(this, { game, x, y }); 4 | 5 | this.game.mario = this; 6 | 7 | // spritesheet 8 | this.spritesheet = ASSET_MANAGER.getAsset("./sprites/mario.png"); 9 | if (luigi) this.spritesheet = ASSET_MANAGER.getAsset("./sprites/luigi.png"); 10 | 11 | // mario's state variables 12 | this.size = 0; // 0 = little, 1 = big, 2 = super, 3 = little invincible, 4 = big invincible, 5 = super invincible 13 | this.facing = 0; // 0 = right, 1 = left 14 | this.state = 0; // 0 = idle, 1 = walking, 2 = running, 3 = skidding, 4 = jumping/falling, 5 = ducking, 6 = pulling flag 15 | this.dead = false; 16 | 17 | // fire mario's state variables 18 | this.canThrow = true; 19 | this.throwFireballTimeElapsed = 0; 20 | this.fireballsThrown = 0; 21 | 22 | this.velocity = { x: 0, y: 0 }; 23 | this.fallAcc = 562.5; 24 | 25 | this.updateBB(); 26 | 27 | // mario's animations 28 | this.animations = []; 29 | this.loadAnimations(); 30 | }; 31 | 32 | loadAnimations() { 33 | for (var i = 0; i < 7; i++) { // six states 34 | this.animations.push([]); 35 | for (var j = 0; j < 3; j++) { // three sizes (star-power not implemented yet) 36 | this.animations[i].push([]); 37 | for (var k = 0; k < 2; k++) { // two directions 38 | this.animations[i][j].push([]); 39 | } 40 | } 41 | } 42 | 43 | // idle animation for state = 0 44 | // facing right = 0 45 | this.animations[0][0][0] = new Animator(this.spritesheet, 210, 0, 16, 16, 1, 0.33, 14, false, true); 46 | this.animations[0][1][0] = new Animator(this.spritesheet, 209, 52, 16, 32, 1, 0.33, 14, false, true); 47 | this.animations[0][2][0] = new Animator(this.spritesheet, 209, 122, 16, 32, 1, 0.33, 14, false, true); 48 | 49 | // facing left = 1 50 | this.animations[0][0][1] = new Animator(this.spritesheet, 179, 0, 16, 16, 1, 0.33, 14, false, true); 51 | this.animations[0][1][1] = new Animator(this.spritesheet, 180, 52, 16, 32, 1, 0.33, 14, false, true); 52 | this.animations[0][2][1] = new Animator(this.spritesheet, 180, 122, 16, 32, 1, 0.33, 14, false, true); 53 | 54 | // walk animation 55 | // facing right 56 | this.animations[1][0][0] = new Animator(this.spritesheet, 239, 0, 16, 16, 3, 0.10, 14, false, true); 57 | this.animations[1][1][0] = new Animator(this.spritesheet, 239, 52, 16, 32, 3, 0.10, 14, true, true); 58 | this.animations[1][2][0] = new Animator(this.spritesheet, 237, 122, 16, 32, 3, 0.10, 9, true, true); 59 | 60 | // facing left 61 | this.animations[1][0][1] = new Animator(this.spritesheet, 89, 0, 16, 16, 3, 0.10, 14, true, true); 62 | this.animations[1][1][1] = new Animator(this.spritesheet, 90, 52, 16, 32, 3, 0.10, 14, false, true); 63 | this.animations[1][2][1] = new Animator(this.spritesheet, 102, 122, 16, 32, 3, 0.10, 9, false, true); 64 | 65 | // run animation 66 | // facing right 67 | this.animations[2][0][0] = new Animator(this.spritesheet, 239, 0, 16, 16, 3, 0.05, 14, false, true); 68 | this.animations[2][1][0] = new Animator(this.spritesheet, 239, 52, 16, 32, 3, 0.05, 14, true, true); 69 | this.animations[2][2][0] = new Animator(this.spritesheet, 237, 122, 16, 32, 3, 0.05, 9, true, true); 70 | 71 | // facing left 72 | this.animations[2][0][1] = new Animator(this.spritesheet, 89, 0, 16, 16, 3, 0.05, 14, true, true); 73 | this.animations[2][1][1] = new Animator(this.spritesheet, 90, 52, 16, 32, 3, 0.05, 14, false, true); 74 | this.animations[2][2][1] = new Animator(this.spritesheet, 102, 122, 16, 32, 3, 0.05, 9, false, true); 75 | 76 | // slide animation 77 | // facing right 78 | this.animations[3][0][0] = new Animator(this.spritesheet, 59, 0, 16, 16, 1, 0.33, 14, false, true); 79 | this.animations[3][1][0] = new Animator(this.spritesheet, 329, 52, 16, 32, 1, 0.33, 14, false, true); 80 | this.animations[3][2][0] = new Animator(this.spritesheet, 337, 122, 16, 32, 1, 0.33, 14, false, true); 81 | 82 | // facing left 83 | this.animations[3][0][1] = new Animator(this.spritesheet, 330, 0, 16, 16, 1, 0.33, 14, false, true); 84 | this.animations[3][1][1] = new Animator(this.spritesheet, 60, 52, 16, 32, 1, 0.33, 14, false, true); 85 | this.animations[3][2][1] = new Animator(this.spritesheet, 52, 122, 16, 32, 1, 0.33, 14, false, true); 86 | 87 | // jump animation 88 | // facing right 89 | this.animations[4][0][0] = new Animator(this.spritesheet, 359, 0, 16, 16, 1, 0.33, 14, false, true); 90 | this.animations[4][1][0] = new Animator(this.spritesheet, 359, 52, 16, 32, 1, 0.33, 14, true, true); 91 | this.animations[4][2][0] = new Animator(this.spritesheet, 362, 122, 16, 32, 1, 0.33, 9, true, true); 92 | 93 | // facing left 94 | this.animations[4][0][1] = new Animator(this.spritesheet, 29, 0, 16, 16, 1, 0.33, 14, true, true); 95 | this.animations[4][1][1] = new Animator(this.spritesheet, 30, 52, 16, 32, 1, 0.33, 14, false, true); 96 | this.animations[4][2][1] = new Animator(this.spritesheet, 27, 122, 16, 32, 1, 0.33, 9, false, true); 97 | 98 | // duck animation 99 | // facing right 100 | this.animations[5][0][0] = new Animator(this.spritesheet, 209, 0, 16, 16, 1, 0.33, 14, false, true); 101 | this.animations[5][1][0] = new Animator(this.spritesheet, 389, 47, 16, 32, 1, 0.33, 14, false, true); 102 | this.animations[5][2][0] = new Animator(this.spritesheet, 389, 117, 16, 32, 1, 0.33, 14, false, true); 103 | 104 | // facing left 105 | this.animations[5][0][1] = new Animator(this.spritesheet, 180, 0, 16, 16, 1, 0.33, 14, false, true); 106 | this.animations[5][1][1] = new Animator(this.spritesheet, 0, 48, 16, 32, 1, 0.33, 14, false, true); 107 | this.animations[5][2][1] = new Animator(this.spritesheet, 0, 118, 16, 32, 1, 0.33, 14, false, true); 108 | 109 | // pulling animation 110 | // facing right 111 | this.animations[6][0][0] = new Animator(this.spritesheet, 329, 29, 16, 16, 2, 0.12, 14, false, true); 112 | this.animations[6][1][0] = new Animator(this.spritesheet, 361, 87, 15, 32, 2, 0.12, 14, false, true); 113 | this.animations[6][2][0] = new Animator(this.spritesheet, 361, 157, 15, 32, 2, 0.12, 14, false, true); 114 | 115 | // facing left 116 | this.animations[6][0][1] = new Animator(this.spritesheet, 29, 29, 16, 16, 2, 0.12, 14, false, true); 117 | this.animations[6][1][1] = new Animator(this.spritesheet, 0, 87, 15, 32, 2, 0.12, 14, false, true); 118 | this.animations[6][2][1] = new Animator(this.spritesheet, 0, 157, 15, 32, 2, 0.12, 14, false, true); 119 | 120 | this.deadAnim = new Animator(this.spritesheet, 0, 16, 16, 16, 1, 0.33, 0, false, true); 121 | }; 122 | 123 | updateBB() { 124 | if (this.size === 0 || this.size === 3) { 125 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 126 | } 127 | else { 128 | if (this.game.down) // big mario is crouching 129 | this.BB = new BoundingBox(this.x, this.y + PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 130 | else 131 | this.BB = new BoundingBox(this.x, this.y, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH * 2); 132 | } 133 | }; 134 | 135 | updateLastBB() { 136 | this.lastBB = this.BB; 137 | }; 138 | 139 | die() { 140 | this.velocity.y = -640; 141 | this.dead = true; 142 | ASSET_MANAGER.pauseBackgroundMusic(); 143 | }; 144 | 145 | update() { 146 | const TICK = this.game.clockTick; 147 | 148 | // I used this page to approximate my constants 149 | // https://web.archive.org/web/20130807122227/http://i276.photobucket.com/albums/kk21/jdaster64/smb_playerphysics.png 150 | // I converted these values from hex and into units of pixels and seconds. 151 | 152 | const MIN_WALK = 4.453125; 153 | const MAX_WALK = 93.75; 154 | const MAX_RUN = 153.75; 155 | const ACC_WALK = 133.59375; 156 | const ACC_RUN = 200.390625; 157 | const DEC_REL = 182.8125; 158 | const DEC_SKID = 365.625; 159 | const MIN_SKID = 33.75; 160 | 161 | const STOP_FALL = 1575; 162 | const WALK_FALL = 1800; 163 | const RUN_FALL = 2025; 164 | const STOP_FALL_A = 450; 165 | const WALK_FALL_A = 421.875; 166 | const RUN_FALL_A = 562.5; 167 | 168 | const MAX_FALL = 270; 169 | 170 | this.fireballsThrown = 0; 171 | 172 | if (this.dead) { 173 | this.velocity.y += RUN_FALL * TICK; 174 | this.y += this.velocity.y * TICK * PARAMS.SCALE; 175 | this.size = 0; 176 | 177 | if (this.y > PARAMS.BLOCKWIDTH * 16) { 178 | this.dead = false; 179 | this.game.camera.lives--; 180 | if (this.game.camera.lives < 0) 181 | this.game.camera.gameOver = true; 182 | else this.game.camera.loadLevel(levelOne, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, true, false); 183 | } 184 | 185 | } else if (this.state === 6) { 186 | let BLOCK_TOP = 13 * PARAMS.BLOCKWIDTH; 187 | let MARIO_SLIDE_SPEED = 12; 188 | let FLAG_WIDTH = PARAMS.BLOCKWIDTH / 7; 189 | let MARIO_FLIP_DELAY = 1.75; 190 | let MARIO_JUMP_DELAY = 0.5; 191 | let BASE_FLAG_HEIGHT = BLOCK_TOP - PARAMS.BLOCKWIDTH; 192 | /* 193 | Flag touch scoring from: https://mario.fandom.com/wiki/Goal_Pole 194 | NOTE: We're using bitwidth estimates, this is not accurate 195 | 196 | 0-17 pixels high: 100 extra points --- 1 BLOCKWIDTH up from floor + blockwidth 197 | 18-57 pixels high: 400 extra points --- 2-3 BLOCKWIDTH ^^ 198 | 58-81 pixels high: 800 extra points -- 3-4 BLOCKWIDTH ^^ 199 | 82-127 pixels high: 2000 extra points -- 4-5 BLOCKWIDTH ^^ 200 | 128-153 pixels high: 4000 extra points -- above 201 | */ 202 | let scoreAdd = 0; 203 | 204 | if (this.size === 0) { 205 | if (this.y < (BLOCK_TOP - PARAMS.BLOCKWIDTH)) { 206 | this.y += PARAMS.BLOCKWIDTH * TICK * MARIO_SLIDE_SPEED; 207 | } else { 208 | if (!this.scored) { 209 | 210 | if (this.flagTouchedY > BASE_FLAG_HEIGHT - PARAMS.BLOCKWIDTH) { 211 | scoreAdd = 100; 212 | } else if (this.flagTouchedY > BASE_FLAG_HEIGHT - 3 * PARAMS.BLOCKWIDTH) { 213 | scoreAdd = 400; 214 | } else if (this.flagTouchedY > BASE_FLAG_HEIGHT - 5 * PARAMS.BLOCKWIDTH) { 215 | scoreAdd = 800; 216 | } else { 217 | scoreAdd = 2000; 218 | } 219 | this.game.addEntity(new Score(this.game, this.x, this.y, scoreAdd)); 220 | this.scored = true; 221 | } 222 | this.doneSliding = true; 223 | } 224 | } else { 225 | if (this.y < (BLOCK_TOP - 2 * PARAMS.BLOCKWIDTH)) { 226 | this.y += PARAMS.BLOCKWIDTH * TICK * MARIO_SLIDE_SPEED; 227 | } else { 228 | if (!this.scored) { 229 | // this.game.addEntity(new Score(this.game, this.x, this.y, 100)); 230 | if (this.flagTouchedY > BASE_FLAG_HEIGHT - PARAMS.BLOCKWIDTH) { 231 | scoreAdd = 100; 232 | } else if (this.flagTouchedY > BASE_FLAG_HEIGHT - 3 * PARAMS.BLOCKWIDTH) { 233 | scoreAdd = 400; 234 | } else if (this.flagTouchedY > BASE_FLAG_HEIGHT - 5 * PARAMS.BLOCKWIDTH) { 235 | scoreAdd = 800; 236 | } else { 237 | scoreAdd = 2000; 238 | } 239 | this.game.addEntity(new Score(this.game, this.x, this.y, scoreAdd)); 240 | this.scored = true; 241 | } 242 | this.doneSliding = true; 243 | } 244 | } 245 | if (this.doneSliding) { 246 | if (!this.flipped) { 247 | if (this.timer === undefined) { 248 | this.timer = 0; 249 | 250 | } else { 251 | this.timer += TICK; 252 | } 253 | if (this.timer && this.timer > MARIO_FLIP_DELAY) { 254 | this.facing = 1; 255 | this.x += FLAG_WIDTH + PARAMS.BLOCKWIDTH; 256 | this.flipped = true; 257 | this.timer = undefined; 258 | } 259 | } else { 260 | if (this.timer === undefined) { 261 | this.timer = 0; 262 | 263 | } else { 264 | this.timer += TICK; 265 | } 266 | if (this.timer && this.timer > MARIO_JUMP_DELAY) { 267 | this.state = 4; 268 | this.velocity.x = 240; // need to figure out how to make him jump more 269 | this.velocity.y = -50; 270 | this.facing = 1; 271 | ASSET_MANAGER.pauseBackgroundMusic(); 272 | ASSET_MANAGER.playAsset("./audio/level-clear.mp3"); 273 | } 274 | 275 | } 276 | } 277 | 278 | 279 | } else { 280 | // update velocity 281 | 282 | if (!this.game.camera.title) { 283 | if (this.state !== 4) { // not jumping 284 | // ground physics 285 | if (Math.abs(this.velocity.x) < MIN_WALK) { // slower than a walk // starting, stopping or turning around 286 | this.velocity.x = 0; 287 | this.state = 0; 288 | if (this.game.left && !this.game.down) { 289 | this.velocity.x -= MIN_WALK; 290 | } 291 | if (this.game.right && !this.game.down) { 292 | this.velocity.x += MIN_WALK; 293 | } 294 | } 295 | else if (Math.abs(this.velocity.x) >= MIN_WALK) { // faster than a walk // accelerating or decelerating 296 | if (this.facing === 0) { 297 | if (this.game.right && !this.game.left && !this.game.down) { 298 | if (this.game.B) { 299 | this.velocity.x += ACC_RUN * TICK; 300 | } else this.velocity.x += ACC_WALK * TICK; 301 | } else if (this.game.left && !this.game.right && !this.game.down) { 302 | this.velocity.x -= DEC_SKID * TICK; 303 | this.state = 3; 304 | } else { 305 | this.velocity.x -= DEC_REL * TICK; 306 | } 307 | } 308 | if (this.facing === 1) { 309 | if (this.game.left && !this.game.right && !this.game.down) { 310 | if (this.game.B) { 311 | this.velocity.x -= ACC_RUN * TICK; 312 | } else this.velocity.x -= ACC_WALK * TICK; 313 | } else if (this.game.right && !this.game.left && !this.game.down) { 314 | this.velocity.x += DEC_SKID * TICK; 315 | this.state = 3; 316 | } else { 317 | this.velocity.x += DEC_REL * TICK; 318 | } 319 | } 320 | } 321 | 322 | this.velocity.y += this.fallAcc * TICK; 323 | 324 | if (this.game.A) { // jump 325 | if (Math.abs(this.velocity.x) < 16) { 326 | this.velocity.y = -280; 327 | this.fallAcc = STOP_FALL; 328 | } 329 | else if (Math.abs(this.velocity.x) < 40) { 330 | this.velocity.y = -280; 331 | this.fallAcc = WALK_FALL; 332 | } 333 | else { 334 | this.velocity.y = -300; 335 | this.fallAcc = RUN_FALL; 336 | } 337 | this.state = 4; 338 | 339 | if (this.size === 0) { 340 | ASSET_MANAGER.playAsset("./audio/small-jump.mp3"); 341 | } else { 342 | ASSET_MANAGER.playAsset("./audio/super-jump.mp3"); 343 | } 344 | } 345 | 346 | if (this.win) { 347 | this.velocity.x = 50; 348 | 349 | // if (this.x === ??) delay for .5 secs, disappear 350 | // when he disappears, timer goes down, score increases 351 | // let TARGET_POS = 700 * PARAMS.BLOCKWIDTH; 352 | // console.log("current x " + this.x); 353 | // console.log("target x " + 9706); 354 | let DOOR_POSITION = 9645; 355 | 356 | if (this.x >= DOOR_POSITION) { 357 | this.disappear = true; 358 | this.velocity.x = 0; 359 | 360 | if (this.game.camera.time > 0) { 361 | if (this.timer === undefined) { 362 | this.timer = 0; 363 | } else { 364 | this.timer += this.game.clockTick; 365 | } 366 | 367 | if (this.timer > .02) { 368 | this.game.camera.time -= 1; 369 | this.game.camera.score += 50; 370 | this.timer = undefined; 371 | } 372 | 373 | } else { 374 | this.doneScoring = true; 375 | } 376 | 377 | if (this.doneScoring) { 378 | this.win = false; 379 | this.game.camera.loadLevel(levelTwo, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, true, false); 380 | this.disappear = false; 381 | this.state = 0; 382 | this.game.startInput(); 383 | } 384 | 385 | } 386 | } 387 | } else { 388 | // air physics 389 | // vertical physics 390 | if (this.velocity.y < 0 && this.game.A) { // holding A while jumping jumps higher 391 | if (this.fallAcc === STOP_FALL) this.velocity.y -= (STOP_FALL - STOP_FALL_A) * TICK; 392 | if (this.fallAcc === WALK_FALL) this.velocity.y -= (WALK_FALL - WALK_FALL_A) * TICK; 393 | if (this.fallAcc === RUN_FALL) this.velocity.y -= (RUN_FALL - RUN_FALL_A) * TICK; 394 | } 395 | 396 | // horizontal physics 397 | if (this.game.right && !this.game.left) { 398 | if (Math.abs(this.velocity.x) > MAX_WALK) { 399 | this.velocity.x += ACC_RUN * TICK; 400 | } else this.velocity.x += ACC_WALK * TICK; 401 | } else if (this.game.left && !this.game.right) { 402 | if (Math.abs(this.velocity.x) > MAX_WALK) { 403 | this.velocity.x -= ACC_RUN * TICK; 404 | } else this.velocity.x -= ACC_WALK * TICK; 405 | } else { 406 | // do nothing 407 | } 408 | } 409 | } 410 | 411 | this.velocity.y += this.fallAcc * TICK; 412 | 413 | // max speed calculation 414 | if (this.velocity.y >= MAX_FALL) this.velocity.y = MAX_FALL; 415 | if (this.velocity.y <= -MAX_FALL) this.velocity.y = -MAX_FALL; 416 | 417 | if (this.velocity.x >= MAX_RUN) this.velocity.x = MAX_RUN; 418 | if (this.velocity.x <= -MAX_RUN) this.velocity.x = -MAX_RUN; 419 | if (this.velocity.x >= MAX_WALK && !this.game.B) this.velocity.x = MAX_WALK; 420 | if (this.velocity.x <= -MAX_WALK && !this.game.B) this.velocity.x = -MAX_WALK; 421 | 422 | // Collision detection 423 | // update X position 424 | this.x += this.velocity.x * TICK * PARAMS.SCALE; 425 | this.updateLastBB(); 426 | this.updateBB(); 427 | // Handle horizontal collisions 428 | this.handleHorizontalCollisions(); 429 | 430 | // Update Y position 431 | this.y += this.velocity.y * TICK * PARAMS.SCALE; 432 | 433 | // Apply left boundary constraint 434 | if (this.x < this.game.camera.x) { 435 | this.x = this.game.camera.x; 436 | this.velocity.x = Math.max(0, this.velocity.x); 437 | } 438 | 439 | this.updateLastBB(); 440 | this.updateBB(); 441 | // Handle vertical collisions 442 | this.handleVerticalCollisions(); 443 | 444 | // if mario fell of the map he's dead 445 | if (this.y > PARAMS.BLOCKWIDTH * 16) this.die(); 446 | 447 | // mario throws fireballs 448 | if (this.size == 2) { 449 | if (this.fireballsThrown >= 2) { 450 | this.canThrow = false; 451 | } 452 | if (this.game.B) { 453 | this.throwFireballTimeElapsed += TICK; 454 | if (this.canThrow) { 455 | ASSET_MANAGER.playAsset("./audio/fireball.mp3"); 456 | if (this.facing == 0) { 457 | this.game.addEntity(new Fireball(this.game, this.x + 14 * PARAMS.SCALE, this.y + 12 * PARAMS.SCALE)); 458 | } else { 459 | this.game.addEntity(new Fireball(this.game, this.x - 6 * PARAMS.SCALE, this.y + 12 * PARAMS.SCALE)); 460 | } 461 | this.canThrow = false; 462 | } 463 | } else if (!this.game.B && this.fireballsThrown < 2) { 464 | this.throwFireballTimeElapsed = 0; 465 | this.canThrow = true; 466 | } 467 | } 468 | 469 | // update state 470 | if (this.state !== 4 && this.state !== 6) { 471 | if (this.game.down) this.state = 5; 472 | else if (Math.abs(this.velocity.x) > MAX_WALK) this.state = 2; 473 | else if (Math.abs(this.velocity.x) >= MIN_WALK) this.state = 1; 474 | else this.state = 0; 475 | } 476 | 477 | // update direction 478 | if (this.velocity.x < 0) this.facing = 1; 479 | if (this.velocity.x > 0) this.facing = 0; 480 | } 481 | }; 482 | 483 | handleHorizontalCollisions() { 484 | const that = this; 485 | this.game.entities.forEach(function (entity) { 486 | if (entity.BB && that.BB.collide(entity.BB)) { 487 | if ((entity instanceof Brick && entity.type) // hit a visible brick 488 | && that.BB.collide(entity.BB)) { 489 | let overlap = that.BB.overlap(entity.BB); 490 | if (that.BB.collide(entity.leftBB) && that.lastBB.right <= entity.BB.left) { 491 | that.x = entity.BB.left - PARAMS.BLOCKWIDTH; 492 | if (that.velocity.x > 0) that.velocity.x = 0; 493 | } else if (that.BB.collide(entity.rightBB) && that.lastBB.left >= entity.BB.right) { 494 | that.x = entity.BB.right; 495 | if (that.velocity.x < 0) that.velocity.x = 0; 496 | } 497 | that.updateBB(); 498 | } 499 | else if ((entity instanceof Tube || entity instanceof SideTube || entity instanceof Ground || entity instanceof Block)) { 500 | if (that.lastBB.right <= entity.BB.left) { // Collided with the left 501 | that.x = entity.BB.left - PARAMS.BLOCKWIDTH; 502 | if (that.velocity.x > 0) that.velocity.x = 0; 503 | if (entity instanceof SideTube && that.game.right) { 504 | that.game.camera.loadLevel(levelOne, 162.5 * PARAMS.BLOCKWIDTH, 11 * PARAMS.BLOCKWIDTH) 505 | } 506 | } else if (that.lastBB.left >= entity.BB.right) { // Collided with the right 507 | that.x = entity.BB.right; 508 | if (that.velocity.x < 0) that.velocity.x = 0; 509 | } 510 | that.updateBB(); 511 | } 512 | else if (entity instanceof Mushroom && !entity.emerging) { 513 | entity.removeFromWorld = true; 514 | ASSET_MANAGER.playAsset("./audio/power-up.mp3"); 515 | if (entity.type === 'Growth') { 516 | that.y -= PARAMS.BLOCKWIDTH; 517 | that.size = 1; 518 | that.game.addEntity(new Score(that.game, that.x, that.y, 1000)); 519 | } else { 520 | that.game.camera.lives++; 521 | } 522 | } 523 | else if (entity instanceof Flower && !entity.emerging) { 524 | entity.removeFromWorld = true; 525 | ASSET_MANAGER.playAsset("./audio/power-up.mp3"); 526 | if (that.size == 1) { 527 | that.size = 2; 528 | } else if (that.size == 0) { 529 | that.size = 1; 530 | } 531 | } 532 | else if (entity instanceof Coin) { 533 | entity.removeFromWorld = true; 534 | that.game.camera.score += 200; 535 | that.game.camera.addCoin(); 536 | } 537 | else if (entity instanceof FireBar_Fire) { 538 | that.die(); 539 | } 540 | else if (entity instanceof Flag) { 541 | that.x = entity.BB.left - PARAMS.BLOCKWIDTH; 542 | that.velocity.x = 0; 543 | entity.win = true; 544 | that.state = 6; 545 | that.win = true; 546 | if (!that.flagTouchedY) { 547 | that.flagTouchedY = that.y; 548 | } 549 | } 550 | } 551 | 552 | // counting the number of fireballs currently in play 553 | if (entity instanceof Fireball) { 554 | that.fireballsThrown++; 555 | } 556 | 557 | that.updateBB(); 558 | }); 559 | } 560 | 561 | handleVerticalCollisions() { 562 | const that = this; 563 | this.game.entities.forEach(function (entity) { 564 | if (entity.BB && that.BB.collide(entity.BB)) { 565 | if (that.velocity.y > 0) { // falling 566 | if ((entity instanceof Ground || entity instanceof Brick || entity instanceof Block || entity instanceof Tube || entity instanceof SideTube) // landing 567 | && (that.lastBB.bottom) <= entity.BB.top) { // was above last tick 568 | if (that.size === 0 || that.size === 3) { // small 569 | that.y = entity.BB.top - PARAMS.BLOCKWIDTH; 570 | } else { // big 571 | that.y = entity.BB.top - 2 * PARAMS.BLOCKWIDTH; 572 | } 573 | that.velocity.y = 0; 574 | 575 | if(that.state === 4) that.state = 0; // set state to idle 576 | that.updateBB(); 577 | 578 | if (entity instanceof Tube && entity.destination && that.game.down) { 579 | that.game.camera.loadLevel(bonusLevelOne, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, false, false); 580 | } 581 | } 582 | else if (entity instanceof Lift && that.lastBB.bottom <= entity.BB.top + PARAMS.SCALE * 3) { 583 | if (that.size === 0 || that.size === 3) { // small 584 | that.y = entity.BB.top - PARAMS.BLOCKWIDTH; 585 | } else { // big 586 | that.y = entity.BB.top - 2 * PARAMS.BLOCKWIDTH; 587 | } 588 | that.velocity.y = 0; 589 | 590 | if(that.state === 4) that.state = 0; // set state to idle 591 | } 592 | else if ((entity instanceof Goomba || entity instanceof Koopa || entity instanceof KoopaParatroopaGreen || entity instanceof KoopaParatroopaRed || entity instanceof KoopaShell) // squish Goomba 593 | && (that.lastBB.bottom) <= entity.BB.top // was above last tick 594 | && !entity.dead) { // can't squish an already squished Goomba 595 | entity.dead = true; 596 | that.velocity.y = -240; // bounce 597 | ASSET_MANAGER.playAsset("./audio/stomp.mp3"); 598 | } 599 | } 600 | else if (that.velocity.y < 0) { // jumping 601 | if ((entity instanceof Brick) // hit ceiling 602 | && (that.lastBB.top) >= entity.BB.bottom) { // was below last tick 603 | 604 | // Check for center collision with brick 605 | if (that.BB.collide(entity.leftBB) && that.BB.collide(entity.rightBB)) { 606 | entity.bounce = true; 607 | that.velocity.y = 0; 608 | 609 | if(entity.type == 1 && that.size != 0 && that.size != 3){ // if it's a regular brick, and mario is big 610 | entity.explode(); 611 | } 612 | } 613 | // Check for side collisions 614 | else if (that.BB.collide(entity.leftBB)) { 615 | that.x = entity.BB.left - PARAMS.BLOCKWIDTH; 616 | if (that.velocity.x > 0) that.velocity.x = 0; 617 | } 618 | else if (that.BB.collide(entity.rightBB)) { 619 | that.x = entity.BB.right; 620 | if (that.velocity.x < 0) that.velocity.x = 0; 621 | } 622 | 623 | } 624 | else if (entity instanceof Lift && that.lastBB.bottom <= entity.BB.top + PARAMS.SCALE * 3) { 625 | if (that.size === 0 || that.size === 3) { // small 626 | that.y = entity.BB.top - PARAMS.BLOCKWIDTH; 627 | } else { // big 628 | that.y = entity.BB.top - 2 * PARAMS.BLOCKWIDTH; 629 | } 630 | that.velocity.y = 0; 631 | that.updateBB(); 632 | } 633 | } 634 | } 635 | that.updateBB(); 636 | }); 637 | } 638 | 639 | drawMinimap(ctx, mmX, mmY) { 640 | ctx.fillStyle = "Red"; 641 | ctx.fillRect(mmX + this.x / PARAMS.BITWIDTH, mmY + this.y / PARAMS.BITWIDTH, PARAMS.SCALE, PARAMS.SCALE * Math.min(this.size + 1, 2)); 642 | } 643 | 644 | draw(ctx) { 645 | if (this.dead) { 646 | this.deadAnim.drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 647 | } else if (this.size == 2 && this.game.B && this.throwFireballTimeElapsed < 0.1) { // throwing fireballs 648 | if (this.facing == 0) { 649 | ctx.drawImage(this.spritesheet, 287, 122, 16, 32, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 650 | } else { 651 | ctx.drawImage(this.spritesheet, 102, 122, 16, 32, this.x - this.game.camera.x, this.y, PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH); 652 | } 653 | } else if (this.disappear) { 654 | 655 | } else { 656 | this.animations[this.state][this.size][this.facing].drawFrame(this.game.clockTick, ctx, this.x - this.game.camera.x, this.y, PARAMS.SCALE); 657 | } 658 | if (PARAMS.DEBUG) { 659 | ctx.strokeStyle = 'Red'; 660 | ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); 661 | } 662 | }; 663 | }; 664 | -------------------------------------------------------------------------------- /music/castle-hurry.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/castle-hurry.mp3 -------------------------------------------------------------------------------- /music/castle.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/castle.mp3 -------------------------------------------------------------------------------- /music/invincible.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/invincible.mp3 -------------------------------------------------------------------------------- /music/overworld-hurry.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/overworld-hurry.mp3 -------------------------------------------------------------------------------- /music/overworld.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/overworld.mp3 -------------------------------------------------------------------------------- /music/underworld-hurry.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/underworld-hurry.mp3 -------------------------------------------------------------------------------- /music/underworld.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/underworld.mp3 -------------------------------------------------------------------------------- /music/waterworld-hurry.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/waterworld-hurry.mp3 -------------------------------------------------------------------------------- /music/waterworld.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/music/waterworld.mp3 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SuperMarioBros", 3 | "description": "A web based clone of the classic NES Super Mario Brothers.", 4 | "author": "Chris Marriott", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/algorithm0r/SuperMarioBros.git" 12 | }, 13 | "homepage": "https://github.com/algorithm0r/SuperMarioBros", 14 | "bugs": { 15 | "url": "https://github.com/algorithm0r/SuperMarioBros/issues" 16 | }, 17 | "dependencies": { 18 | "mocha": "^8.2.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sceneManager.js: -------------------------------------------------------------------------------- 1 | class SceneManager { 2 | constructor(game) { 3 | this.game = game; 4 | this.game.camera = this; 5 | this.x = 0; 6 | this.score = 0; 7 | this.coins = 0; 8 | this.lives = 3; 9 | 10 | this.gameOver = false; 11 | 12 | this.title = true; 13 | this.credits = false; 14 | this.level = null; 15 | 16 | this.menuSelect = { 17 | mario: false, 18 | luigi: false, 19 | credits: false 20 | } 21 | this.menuSelectIndex = -10; 22 | this.creditsLineIndex = 0; 23 | this.menuButtonTimer = 0.15; 24 | this.menuButtonCooldown = 0.15; 25 | 26 | this.coinAnimation = new Animator(ASSET_MANAGER.getAsset("./sprites/coins.png"), 0, 160, 8, 8, 4, 0.2, 0, false, true); 27 | 28 | this.minimap = new Minimap(this.game, 1.5 * PARAMS.BLOCKWIDTH, 3.5 * PARAMS.BLOCKWIDTH, 224 * PARAMS.SCALE); 29 | 30 | this.mario = new Mario(this.game, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH); 31 | 32 | this.loadLevel(levelOne, 2.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, false, true); 33 | 34 | // NOTE: PLEASE USE THE FOLLOWING LINE TO TEST. 35 | // this.loadLevel(levelTwo, 2.5 * PARAMS.BLOCKWIDTH, 13 * PARAMS.BLOCKWIDTH, false, true); 36 | }; 37 | 38 | clearEntities() { 39 | this.game.entities.forEach(function (entity) { 40 | entity.removeFromWorld = true; 41 | }); 42 | }; 43 | 44 | loadLevel(level, x, y, transition, title) { 45 | this.title = title; 46 | this.level = level; 47 | this.clearEntities(); 48 | this.x = 0; 49 | this.underground = level.underground; 50 | 51 | if (transition) { 52 | this.game.addEntity(new TransitionScreen(this.game, level, x, y, title)); 53 | } else { 54 | if (level.bighills) { 55 | for (var i = 0; i < level.bighills.length; i++) { 56 | let hill = level.bighills[i]; 57 | this.game.addEntity(new BigHill(this.game, hill.x * PARAMS.BLOCKWIDTH, hill.y * PARAMS.BLOCKWIDTH)); 58 | } 59 | } 60 | if (level.hills) { 61 | for (var i = 0; i < level.hills.length; i++) { 62 | let hill = level.hills[i]; 63 | this.game.addEntity(new Hill(this.game, hill.x * PARAMS.BLOCKWIDTH, hill.y * PARAMS.BLOCKWIDTH)); 64 | } 65 | } 66 | if (level.bushes) { 67 | for (var i = 0; i < level.bushes.length; i++) { 68 | let bush = level.bushes[i]; 69 | this.game.addEntity(new Bush(this.game, bush.x * PARAMS.BLOCKWIDTH, bush.y * PARAMS.BLOCKWIDTH, bush.size)); 70 | } 71 | } 72 | if (level.clouds) { 73 | for (var i = 0; i < level.clouds.length; i++) { 74 | let cloud = level.clouds[i]; 75 | this.game.addEntity(new Cloud(this.game, cloud.x * PARAMS.BLOCKWIDTH, cloud.y * PARAMS.BLOCKWIDTH, cloud.size)); 76 | } 77 | } 78 | if (level.bigcastles) { 79 | for (var i = 0; i < level.bigcastles.length; i++) { 80 | let castle = level.bigcastles[i]; 81 | this.game.addEntity(new BigCastle(this.game, castle.x * PARAMS.BLOCKWIDTH, castle.y * PARAMS.BLOCKWIDTH, castle.size)); 82 | } 83 | } 84 | if (level.ground) { 85 | for (var i = 0; i < level.ground.length; i++) { 86 | let ground = level.ground[i]; 87 | this.game.addEntity(new Ground(this.game, ground.x * PARAMS.BLOCKWIDTH, ground.y * PARAMS.BLOCKWIDTH, ground.size * PARAMS.BLOCKWIDTH, level.underground)); 88 | } 89 | } 90 | if (level.bricks) { 91 | for (var i = 0; i < level.bricks.length; i++) { 92 | let brick = level.bricks[i]; 93 | this.game.addEntity(new Brick(this.game, brick.x * PARAMS.BLOCKWIDTH, brick.y * PARAMS.BLOCKWIDTH, brick.type, brick.prize, level.underground)); 94 | } 95 | } 96 | if (level.flags) { 97 | for (var i = 0; i < level.flags.length; i++) { 98 | let flag = level.flags[i]; 99 | this.game.addEntity(new Flag(this.game, flag.x * PARAMS.BLOCKWIDTH, flag.y * PARAMS.BLOCKWIDTH, flag.size * PARAMS.BLOCKWIDTH)); 100 | } 101 | } 102 | if (level.blocks) { 103 | for (var i = 0; i < level.blocks.length; i++) { 104 | let block = level.blocks[i]; 105 | this.game.addEntity(new Block(this.game, block.x * PARAMS.BLOCKWIDTH, block.y * PARAMS.BLOCKWIDTH, block.size * PARAMS.BLOCKWIDTH, level.underground)); 106 | } 107 | } 108 | if (level.tubes) { 109 | for (var i = 0; i < level.tubes.length; i++) { 110 | let tube = level.tubes[i]; 111 | if (!tube.side) { 112 | this.game.addEntity(new Tube(this.game, tube.x * PARAMS.BLOCKWIDTH, tube.y * PARAMS.BLOCKWIDTH, tube.size, tube.destination, tube.enemyType)); 113 | } else { 114 | this.game.addEntity(new SideTube(this.game, tube.x * PARAMS.BLOCKWIDTH, tube.y * PARAMS.BLOCKWIDTH)); 115 | } 116 | } 117 | } 118 | if (level.goombas) { 119 | for (var i = 0; i < level.goombas.length; i++) { 120 | let goomba = level.goombas[i]; 121 | this.game.addEntity(new Goomba(this.game, goomba.x * PARAMS.BLOCKWIDTH, goomba.y * PARAMS.BLOCKWIDTH)); 122 | } 123 | } 124 | if (level.koopaParatroopaGreen) { 125 | for (var i = 0; i < level.koopaParatroopaGreen.length; i++) { 126 | let koopaParatroopaGreen = level.koopaParatroopaGreen[i]; 127 | this.game.addEntity(new KoopaParatroopaGreen(this.game, koopaParatroopaGreen.x * PARAMS.BLOCKWIDTH, koopaParatroopaGreen.y * PARAMS.BLOCKWIDTH, koopaParatroopaGreen.facing)); 128 | } 129 | } 130 | if (level.koopaParatroopaRed) { 131 | for (var i = 0; i < level.koopaParatroopaRed.length; i++) { 132 | let koopaParatroopaRed = level.koopaParatroopaRed[i]; 133 | this.game.addEntity(new KoopaParatroopaRed(this.game, koopaParatroopaRed.x * PARAMS.BLOCKWIDTH, koopaParatroopaRed.y * PARAMS.BLOCKWIDTH, koopaParatroopaRed.facing)); 134 | } 135 | } 136 | if (level.koopas) { 137 | for (var i = 0; i < level.koopas.length; i++) { 138 | let koopa = level.koopas[i]; 139 | this.game.addEntity(new Koopa(this.game, koopa.x * PARAMS.BLOCKWIDTH, koopa.y * PARAMS.BLOCKWIDTH, koopa.facing)); 140 | } 141 | } 142 | if (level.coins) { 143 | for (var i = 0; i < level.coins.length; i++) { 144 | let coin = level.coins[i]; 145 | this.game.addEntity(new Coin(this.game, coin.x * PARAMS.BLOCKWIDTH, coin.y * PARAMS.BLOCKWIDTH)); 146 | } 147 | } 148 | this.mario.x = x; 149 | this.mario.y = y; 150 | this.mario.removeFromWorld = false; 151 | this.mario.velocity = { x: 0, y: 0 }; 152 | this.mario.state = 4 // mario enters level in falling state; 153 | 154 | if (level.music && !this.title) { 155 | ASSET_MANAGER.pauseBackgroundMusic(); 156 | ASSET_MANAGER.playAsset(level.music); 157 | } 158 | 159 | var that = this; 160 | var mario = false; 161 | this.game.entities.forEach(function(entity) { 162 | if(that.mario === entity) mario = true; 163 | }); 164 | if(!mario) this.game.addEntity(this.mario); 165 | 166 | this.time = 400; 167 | this.game.camera.paused = false; 168 | } 169 | 170 | if (level.lifts) { 171 | for (var i = 0; i < level.lifts.length; i++) { 172 | let lift = level.lifts[i]; 173 | this.game.addEntity(new Lift(this.game, lift.x * PARAMS.BLOCKWIDTH, lift.y * PARAMS.BLOCKWIDTH, lift.goingDown)); 174 | } 175 | } 176 | this.mario.x = x; 177 | this.mario.y = y; 178 | //this.game.addEntity(this.mario); 179 | 180 | }; 181 | 182 | updateAudio() { 183 | var mute = document.getElementById("mute").checked; 184 | var volume = document.getElementById("volume").value; 185 | 186 | ASSET_MANAGER.muteAudio(mute); 187 | ASSET_MANAGER.adjustVolume(volume); 188 | 189 | }; 190 | 191 | update() { 192 | this.menuButtonTimer += this.game.clockTick; 193 | if (this.time === 99) { 194 | ASSET_MANAGER.pauseBackgroundMusic(); 195 | ASSET_MANAGER.playAsset(this.level.hurry_music); 196 | } 197 | if (!this.title && !this.transition && !this.paused) { 198 | if (this.timer === undefined) { 199 | this.timer = 0; 200 | } else { 201 | this.timer += this.game.clockTick; 202 | } 203 | 204 | if (this.timer > 0.4) { 205 | this.time -= 1; 206 | this.timer = undefined; 207 | } 208 | } 209 | 210 | // Gamepad/keyboard title/credits menu select 211 | if (this.credits && (this.game.A || this.game.B)) { 212 | this.menuSelectIndex = 0; 213 | } if (!this.credits && this.menuButtonTimer > this.menuButtonCooldown) { 214 | if (!this.menuSelect.mario && !this.menuSelect.luigi && !this.menuSelect.credits) { 215 | if (this.game.up || this.game.down) { 216 | this.menuButtonTimer = 0; 217 | this.menuSelect.mario = true; 218 | } 219 | } else if (this.menuSelect.mario) { 220 | if (this.game.up) { 221 | this.menuButtonTimer = 0; 222 | this.menuSelect.mario = false; 223 | this.menuSelect.credits = true; 224 | } else if (this.game.down) { 225 | this.menuButtonTimer = 0; 226 | this.menuSelect.mario = false; 227 | this.menuSelect.luigi = true; 228 | } 229 | } else if (this.menuSelect.luigi) { 230 | if (this.game.up) { 231 | this.menuButtonTimer = 0; 232 | this.menuSelect.luigi = false; 233 | this.menuSelect.mario = true; 234 | } else if (this.game.down) { 235 | this.menuButtonTimer = 0; 236 | this.menuSelect.luigi = false; 237 | this.menuSelect.credits = true; 238 | } 239 | } else if (this.menuSelect.credits) { 240 | if (this.game.up) { 241 | this.menuButtonTimer = 0; 242 | this.menuSelect.credits = false; 243 | this.menuSelect.luigi = true; 244 | } else if (this.game.down) { 245 | this.menuButtonTimer = 0; 246 | this.menuSelect.credits = false; 247 | this.menuSelect.mario = true; 248 | } 249 | } 250 | 251 | } 252 | 253 | // Credits scroll with gamepad/keyboard 254 | if (this.credits && this.menuButtonTimer > this.menuButtonCooldown) { 255 | if (this.game.wheel < 0 || this.game.up) { 256 | if (this.creditsLineIndex <= 0) { 257 | this.creditsLineIndex = 0; 258 | } else { 259 | this.creditsLineIndex--; 260 | } 261 | this.menuSelectIndex = 0; 262 | this.menuButtonTimer = 0; 263 | } 264 | else if (this.game.wheel > 0 || this.game.down) { 265 | if (this.creditsLineIndex >= credits.text.length - 12) { 266 | this.creditsLineIndex = credits.text.length - 11; 267 | } else { 268 | this.creditsLineIndex++; 269 | } 270 | this.menuSelectIndex = 0; 271 | this.menuButtonTimer = 0; 272 | } 273 | } 274 | 275 | // Selecting with mouse overrides gamepad select. 276 | if (this.game.mouse && this.game.mouse.y > 9 * PARAMS.BLOCKWIDTH && this.game.mouse.y < 11.5 * PARAMS.BLOCKWIDTH) { 277 | this.menuSelectIndex = -10; 278 | this.menuSelect.mario = false; 279 | this.menuSelect.luigi = false; 280 | this.menuSelect.credits = false; 281 | } 282 | 283 | // Gamepad control of debug 284 | if (this.game.gamepad != null && this.game.gamepad.buttons[8].pressed && this.menuButtonTimer > this.menuButtonCooldown) { 285 | if (document.getElementById("debug").checked) { 286 | document.getElementById("debug").checked = false; 287 | } else { 288 | document.getElementById("debug").checked = true; 289 | } 290 | this.menuButtonTimer = 0; 291 | } 292 | 293 | // Gamepad control of debug 294 | if (this.game.gamepad != null && this.game.gamepad.buttons[9].pressed && this.menuButtonTimer > this.menuButtonCooldown) { 295 | if (document.getElementById("mute").checked) { 296 | document.getElementById("mute").checked = false; 297 | } else { 298 | document.getElementById("mute").checked = true; 299 | } 300 | this.menuButtonTimer = 0; 301 | } 302 | 303 | // Gamepad control of volume slider 304 | if (this.game.gamepad != null && Math.abs(this.game.gamepad.axes[2]) > 0.3 && this.menuButtonTimer > this.menuButtonCooldown) { 305 | if (this.game.gamepad.axes[2] > 0.3) { 306 | document.getElementById("volume").value = parseFloat(document.getElementById("volume").value, 10) + 0.05; 307 | } 308 | if (this.game.gamepad.axes[2] < -0.3) { 309 | document.getElementById("volume").value -= 0.05; 310 | } 311 | this.menuButtonTimer = 0; 312 | } 313 | 314 | this.updateAudio(); 315 | PARAMS.DEBUG = document.getElementById("debug").checked; 316 | 317 | if (this.title && !this.credits && (this.game.click || this.game.A) && (this.menuButtonTimer > this.menuButtonCooldown)) { 318 | if (this.menuSelect.mario || (this.game.click && this.game.click.y > 9 * PARAMS.BLOCKWIDTH && this.game.click.y < 9.5 * PARAMS.BLOCKWIDTH)) { 319 | this.title = false; 320 | this.mario = new Mario(this.game, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH); 321 | this.loadLevel(levelOne, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, true); 322 | } 323 | if (this.menuSelect.luigi || (this.game.click && this.game.click.y > 10 * PARAMS.BLOCKWIDTH && this.game.click.y < 10.5 * PARAMS.BLOCKWIDTH)) { 324 | this.title = false; 325 | this.mario = new Mario(this.game, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, true); 326 | this.loadLevel(levelOne, 2.5 * PARAMS.BLOCKWIDTH, 0 * PARAMS.BLOCKWIDTH, true); 327 | } 328 | if (this.menuSelect.credits || (this.game.click && this.game.click.y > 11 * PARAMS.BLOCKWIDTH && this.game.click.y < 11.5 * PARAMS.BLOCKWIDTH)) { 329 | this.credits = true; 330 | this.menuButtonTimer = 0; 331 | this.menuSelect.credits = false; 332 | } 333 | } else if (this.title && this.credits && (this.game.click || this.game.A || this.game.B) && (this.menuButtonTimer > this.menuButtonCooldown)) { 334 | if (this.game.A || this.game.B || (this.game.click && this.game.click.y > 13.25 * PARAMS.BLOCKWIDTH && this.game.click.y < 13.75 * PARAMS.BLOCKWIDTH)) { 335 | this.credits = false; 336 | this.menuButtonTimer = 0; 337 | this.menuSelect.mario = true; 338 | } 339 | } 340 | 341 | if (this.gameOver) { 342 | this.gameOver = false; 343 | this.lives = 3; 344 | this.score = 0; 345 | this.coins = 0; 346 | var x = 2.5 * PARAMS.BLOCKWIDTH; 347 | var y = 13 * PARAMS.BLOCKWIDTH; 348 | this.mario = new Mario(this.game, x, y); 349 | 350 | this.clearEntities(); 351 | 352 | this.game.addEntity(new TransitionScreen(this.game, levelOne, x, y, true)); 353 | } 354 | 355 | let midpoint = PARAMS.CANVAS_WIDTH/2 - PARAMS.BLOCKWIDTH / 2; 356 | 357 | if (this.x < this.mario.x - midpoint) this.x = this.mario.x - midpoint; 358 | 359 | // NOTE: THIS FOLLOWING CODE HAS A BUG WHERE CANVAS COLOR WON'T CHANGE BACK TO BLUE. 360 | var canvas = document.getElementById("gameWorld"); 361 | if (this.underground) { 362 | canvas.style.backgroundColor = "black"; 363 | } else { 364 | canvas.style.backgroundColor = "#049cd8"; 365 | } 366 | }; 367 | 368 | addCoin() { 369 | ASSET_MANAGER.playAsset("./audio/coin.mp3"); 370 | if (this.coins++ === 100) { 371 | this.coins = 0; 372 | this.lives++; 373 | } 374 | }; 375 | 376 | draw(ctx) { 377 | ctx.font = PARAMS.BLOCKWIDTH / 2 + 'px "Press Start 2P"'; 378 | ctx.fillStyle = "White"; 379 | 380 | ctx.fillStyle = "White"; 381 | ctx.fillText("MARRIOTT", 1.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 382 | ctx.fillText((this.score + "").padStart(8,"0"), 1.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 383 | ctx.fillText("x" + (this.coins < 10 ? "0" : "") + this.coins, 6.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 384 | ctx.fillText("WORLD", 9 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 385 | ctx.fillText(this.level.label, 9.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 386 | ctx.fillText("TIME", 12.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 387 | ctx.fillText(this.time, 13 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 388 | 389 | if (this.title && !this.credits) { // Title Screen 390 | var width = 176; 391 | var height = 88; 392 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title.png"), 2.5 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH, width * PARAMS.SCALE, height * PARAMS.SCALE); 393 | if ((this.game.mouse && this.game.mouse.y > 9 * PARAMS.BLOCKWIDTH && this.game.mouse.y < 9.5 * PARAMS.BLOCKWIDTH) || this.menuSelect.mario) { 394 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title_mushroom.png"), 6 * PARAMS.BLOCKWIDTH, 8.9 * PARAMS.BLOCKWIDTH, 30, 30); 395 | } 396 | ctx.fillText("MARIO", 6.75 * PARAMS.BLOCKWIDTH, 9.5 * PARAMS.BLOCKWIDTH); 397 | if ((this.game.mouse && this.game.mouse.y > 10 * PARAMS.BLOCKWIDTH && this.game.mouse.y < 10.5 * PARAMS.BLOCKWIDTH) || this.menuSelect.luigi) { 398 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title_mushroom.png"), 6 * PARAMS.BLOCKWIDTH, 9.9 * PARAMS.BLOCKWIDTH, 30, 30); 399 | } 400 | ctx.fillText("LUIGI", 6.75 * PARAMS.BLOCKWIDTH, 10.5 * PARAMS.BLOCKWIDTH); 401 | if ((this.game.mouse && this.game.mouse.y > 11 * PARAMS.BLOCKWIDTH && this.game.mouse.y < 11.5 * PARAMS.BLOCKWIDTH) || this.menuSelect.credits) { 402 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title_mushroom.png"), 6 * PARAMS.BLOCKWIDTH, 10.9 * PARAMS.BLOCKWIDTH, 30, 30); 403 | } 404 | ctx.fillText("CREDITS", 6.75 * PARAMS.BLOCKWIDTH, 11.5 * PARAMS.BLOCKWIDTH); 405 | } else if (this.title && this.credits) { // Credits Screen 406 | var width = 176; 407 | var height = 88; 408 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title.png"), 4.5 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH, width * PARAMS.SCALE / 1.55, height * PARAMS.SCALE / 1.55); 409 | 410 | if ((this.game.mouse && this.game.mouse.y > 13.25 * PARAMS.BLOCKWIDTH && this.game.mouse.y < 13.75 * PARAMS.BLOCKWIDTH) || this.menuSelectIndex === 0) { 411 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/title_mushroom.png"), 6.25 * PARAMS.BLOCKWIDTH, 13.1 * PARAMS.BLOCKWIDTH, 30, 30); 412 | } 413 | ctx.fillText("BACK", 7 * PARAMS.BLOCKWIDTH, 13.75 * PARAMS.BLOCKWIDTH); 414 | 415 | ctx.strokeStyle = "Black"; 416 | ctx.lineWidth = 5; 417 | ctx.strokeRect(4 * PARAMS.BLOCKWIDTH, 5.7 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH, 7.25 * PARAMS.BLOCKWIDTH); 418 | ctx.lineWidth = 3; 419 | ctx.strokeRect(4 * PARAMS.BLOCKWIDTH, 6.7 * PARAMS.BLOCKWIDTH, 8 * PARAMS.BLOCKWIDTH, 1) 420 | ctx.fillText("CREDITS ↑↓", 5.5 * PARAMS.BLOCKWIDTH, 6.5 * PARAMS.BLOCKWIDTH); 421 | ctx.drawImage(ASSET_MANAGER.getAsset("./sprites/mouse_wheel.png"), 10.75 * PARAMS.BLOCKWIDTH, 5.9 * PARAMS.BLOCKWIDTH, 30 , 30) 422 | 423 | // Credits box text output 424 | ctx.font = '15px "Press Start 2P"'; 425 | let j = 0; 426 | for (let i = this.creditsLineIndex; i < credits.text.length && i < this.creditsLineIndex + 12; i++) { 427 | let temp = ctx.fillStyle; 428 | if (credits.text[i].length > 0) { 429 | if (credits.text[i].includes("Students")) { 430 | ctx.fillStyle = "Black"; 431 | } 432 | ctx.fillText(credits.text[i], 4.25 * PARAMS.BLOCKWIDTH, (7.25 + 0.5 * j) * PARAMS.BLOCKWIDTH); 433 | ctx.fillStyle = temp; 434 | j++; 435 | } 436 | } 437 | } 438 | 439 | this.coinAnimation.drawFrame(this.game.clockTick, ctx, 6 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH, 3); 440 | 441 | if (PARAMS.DEBUG) { 442 | let xV = "xV=" + Math.floor(this.game.mario.velocity.x); 443 | let yV = "yV=" + Math.floor(this.game.mario.velocity.y); 444 | ctx.fillText(xV, 1.5 * PARAMS.BLOCKWIDTH, 2.5 * PARAMS.BLOCKWIDTH); 445 | ctx.fillText(yV, 1.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 446 | 447 | ctx.fillText(`FPS ${this.game.timer.ticks.length}`, 12.5 * PARAMS.BLOCKWIDTH, 2.5 * PARAMS.BLOCKWIDTH); 448 | 449 | ctx.translate(0, -10); // hack to move elements up by 10 pixels instead of adding -10 to all y coordinates below 450 | ctx.strokeStyle = "White"; 451 | ctx.lineWidth = 2; 452 | ctx.strokeStyle = this.game.left ? "White" : "Grey"; 453 | ctx.fillStyle = ctx.strokeStyle; 454 | ctx.strokeRect(6 * PARAMS.BLOCKWIDTH - 2, 2.5 * PARAMS.BLOCKWIDTH - 2, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 455 | ctx.fillText("L", 6 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH); 456 | ctx.strokeStyle = this.game.down ? "White" : "Grey"; 457 | ctx.fillStyle = ctx.strokeStyle; 458 | ctx.strokeRect(6.5 * PARAMS.BLOCKWIDTH, 3 * PARAMS.BLOCKWIDTH, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 459 | ctx.fillText("D", 6.5 * PARAMS.BLOCKWIDTH + 2, 3.5 * PARAMS.BLOCKWIDTH + 2); 460 | ctx.strokeStyle = this.game.up ? "White" : "Grey"; 461 | ctx.fillStyle = ctx.strokeStyle; 462 | ctx.strokeRect(6.5 * PARAMS.BLOCKWIDTH, 2 * PARAMS.BLOCKWIDTH - 4, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 463 | ctx.fillText("U", 6.5 * PARAMS.BLOCKWIDTH + 2, 2.5 * PARAMS.BLOCKWIDTH - 2); 464 | ctx.strokeStyle = this.game.right ? "White" : "Grey"; 465 | ctx.fillStyle = ctx.strokeStyle; 466 | ctx.strokeRect(7 * PARAMS.BLOCKWIDTH + 2, 2.5 * PARAMS.BLOCKWIDTH - 2, 0.5 * PARAMS.BLOCKWIDTH + 2, 0.5 * PARAMS.BLOCKWIDTH + 2); 467 | ctx.fillText("R", 7 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 468 | 469 | ctx.strokeStyle = this.game.A ? "White" : "Grey"; 470 | ctx.fillStyle = ctx.strokeStyle; 471 | ctx.beginPath(); 472 | ctx.arc(8.25 * PARAMS.BLOCKWIDTH + 2, 2.75 * PARAMS.BLOCKWIDTH, 0.25 * PARAMS.BLOCKWIDTH + 4, 0, 2 * Math.PI); 473 | ctx.stroke(); 474 | ctx.fillText("A", 8 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 475 | ctx.strokeStyle = this.game.B ? "White" : "Grey"; 476 | ctx.fillStyle = ctx.strokeStyle; 477 | ctx.beginPath(); 478 | ctx.arc(9 * PARAMS.BLOCKWIDTH + 2, 2.75 * PARAMS.BLOCKWIDTH, 0.25 * PARAMS.BLOCKWIDTH + 4, 0, 2 * Math.PI); 479 | ctx.stroke(); 480 | ctx.fillText("B", 8.75 * PARAMS.BLOCKWIDTH + 4, 3 * PARAMS.BLOCKWIDTH); 481 | 482 | ctx.translate(0, 10); 483 | ctx.strokeStyle = "White"; 484 | ctx.fillStyle = ctx.strokeStyle; 485 | 486 | this.minimap.draw(ctx); 487 | } 488 | }; 489 | }; 490 | 491 | 492 | class Minimap { 493 | constructor(game, x, y, w) { 494 | Object.assign(this, { game, x, y, w }); 495 | }; 496 | 497 | update() { 498 | 499 | }; 500 | 501 | draw(ctx) { 502 | ctx.strokeStyle = "Black"; 503 | ctx.strokeRect(this.x, this.y, this.w, PARAMS.BLOCKWIDTH); 504 | for (var i = 0; i < this.game.entities.length; i++) { 505 | this.game.entities[i].drawMinimap(ctx, this.x, this.y); 506 | } 507 | }; 508 | }; 509 | -------------------------------------------------------------------------------- /sprites/bricks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/bricks.png -------------------------------------------------------------------------------- /sprites/bricksolod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/bricksolod.png -------------------------------------------------------------------------------- /sprites/castle_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/castle_big.png -------------------------------------------------------------------------------- /sprites/coins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/coins.png -------------------------------------------------------------------------------- /sprites/enemies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/enemies.png -------------------------------------------------------------------------------- /sprites/firebar_fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/firebar_fire.png -------------------------------------------------------------------------------- /sprites/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/flag.png -------------------------------------------------------------------------------- /sprites/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/ground.png -------------------------------------------------------------------------------- /sprites/items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/items.png -------------------------------------------------------------------------------- /sprites/luigi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/luigi.png -------------------------------------------------------------------------------- /sprites/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/mario.png -------------------------------------------------------------------------------- /sprites/mariodead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/mariodead.png -------------------------------------------------------------------------------- /sprites/mouse_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/mouse_wheel.png -------------------------------------------------------------------------------- /sprites/stuff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/stuff.png -------------------------------------------------------------------------------- /sprites/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/tiles.png -------------------------------------------------------------------------------- /sprites/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/title.png -------------------------------------------------------------------------------- /sprites/title_mushroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/title_mushroom.png -------------------------------------------------------------------------------- /sprites/underground_bricksolod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/underground_bricksolod.png -------------------------------------------------------------------------------- /sprites/underground_stuff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithm0r/SuperMarioBros/ce3d3acdd75462347263be8bbfa555fc3699ffc5/sprites/underground_stuff.png -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | let assert = require('assert'); 2 | // This test case exists to fullfill the requirement of Github Action. 3 | describe('Place holder', function() { 4 | it('This returns true', function() { 5 | assert.strictEqual(1, 1) 6 | }); 7 | }); -------------------------------------------------------------------------------- /timer.js: -------------------------------------------------------------------------------- 1 | // This game shell was happily modified from Googler Seth Ladd's "Bad Aliens" game and his Google IO talk in 2011 2 | 3 | class Timer { 4 | constructor() { 5 | this.gameTime = 0; 6 | this.maxStep = 0.05; 7 | this.lastTimestamp = 0; 8 | this.ticks = []; 9 | }; 10 | 11 | tick() { 12 | var current = Date.now(); 13 | var delta = (current - this.lastTimestamp) / 1000; // convert milliseconds to seconds 14 | this.lastTimestamp = current; 15 | 16 | var gameDelta = Math.min(delta, this.maxStep); 17 | this.gameTime += gameDelta; 18 | 19 | this.ticks.push(delta); 20 | 21 | let index = this.ticks.length - 1; 22 | let sum = 0; 23 | while(sum <= 1 && index >= 0) { 24 | sum += this.ticks[index--]; 25 | } 26 | index++; 27 | 28 | this.ticks.splice(0,index); 29 | 30 | return gameDelta; 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /transition.js: -------------------------------------------------------------------------------- 1 | class TransitionScreen { 2 | constructor(game, level, x, y, gameOver) { 3 | Object.assign(this, { game, level, x, y, gameOver }); 4 | 5 | this.elapsed = 0; 6 | }; 7 | 8 | update() { 9 | this.elapsed += this.game.clockTick; 10 | 11 | if (this.elapsed > 2) this.game.camera.loadLevel(this.level, this.x, this.y, false, this.gameOver); 12 | }; 13 | 14 | draw(ctx) { 15 | ctx.fillStyle = "Black"; 16 | ctx.fillRect(0, 0, PARAMS.CANVAS_WIDTH, PARAMS.CANVAS_HEIGHT); 17 | 18 | ctx.font = PARAMS.BLOCKWIDTH / 2 + 'px "Press Start 2P"'; 19 | ctx.fillStyle = "White"; 20 | 21 | ctx.fillStyle = "White"; 22 | ctx.fillText("MARRIOTT", 1.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 23 | ctx.fillText((this.game.camera.score + "").padStart(8, "0"), 1.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 24 | ctx.fillText("x" + (this.game.camera.coins < 10 ? "0" : "") + this.game.camera.coins, 6.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 25 | ctx.fillText("WORLD", 9 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 26 | ctx.fillText(this.level.label, 9.5 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 27 | ctx.fillText("TIME", 12.5 * PARAMS.BLOCKWIDTH, 1 * PARAMS.BLOCKWIDTH); 28 | // ctx.fillText(this.game.time, 13 * PARAMS.BLOCKWIDTH, 1.5 * PARAMS.BLOCKWIDTH); 29 | 30 | if (this.gameOver) { 31 | ctx.fillText("GAME OVER", 6 * PARAMS.BLOCKWIDTH, 9 * PARAMS.BLOCKWIDTH); 32 | } 33 | else { 34 | ctx.fillText("WORLD " + this.level.label, 5.5 * PARAMS.BLOCKWIDTH, 5 * PARAMS.BLOCKWIDTH); 35 | ctx.fillText("x " + this.game.camera.lives, 7.5 * PARAMS.BLOCKWIDTH, 7.5 * PARAMS.BLOCKWIDTH); 36 | ctx.drawImage(this.game.mario.spritesheet, 210, 0, 16, 16, 6 * PARAMS.BLOCKWIDTH, 6.5 * PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH, PARAMS.BLOCKWIDTH); 37 | } 38 | }; 39 | 40 | drawMinimap() { 41 | 42 | }; 43 | }; 44 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | // returns a random integer between 0 and n-1 2 | function randomInt(n) { 3 | return Math.floor(Math.random() * n); 4 | }; 5 | 6 | // returns a string that can be used as a rgb web color 7 | function rgb(r, g, b) { 8 | return "rgb(" + r + "," + g + "," + b + ")"; 9 | }; 10 | 11 | // returns a string that can be used as a hsl web color 12 | function hsl(h, s, l) { 13 | return "hsl(" + h + "," + s + "%," + l + "%)"; 14 | }; 15 | 16 | // creates an alias for requestAnimationFrame for backwards compatibility 17 | window.requestAnimFrame = (function () { 18 | return window.requestAnimationFrame || 19 | window.webkitRequestAnimationFrame || 20 | window.mozRequestAnimationFrame || 21 | window.oRequestAnimationFrame || 22 | window.msRequestAnimationFrame || 23 | function (/* function */ callback, /* DOMElement */ element) { 24 | window.setTimeout(callback, 1000 / 60); 25 | }; 26 | })(); 27 | 28 | // add global parameters here 29 | 30 | const PARAMS = { 31 | DEBUG: true, 32 | SCALE: 3, 33 | BITWIDTH: 16 34 | }; 35 | --------------------------------------------------------------------------------