├── src
├── index.ts
├── Assets
│ ├── audio
│ │ ├── coin.mp3
│ │ ├── coin.ogg
│ │ ├── die.mp3
│ │ ├── die.ogg
│ │ ├── game.mp3
│ │ ├── game.ogg
│ │ ├── grow.mp3
│ │ ├── grow.ogg
│ │ ├── hurt.mp3
│ │ ├── hurt.ogg
│ │ ├── jump.mp3
│ │ ├── jump.ogg
│ │ ├── menu.mp3
│ │ ├── menu.ogg
│ │ ├── editor.mp3
│ │ ├── editor.ogg
│ │ ├── ending.mp3
│ │ ├── ending.ogg
│ │ ├── peach.mp3
│ │ ├── peach.ogg
│ │ ├── shell.mp3
│ │ ├── shell.ogg
│ │ ├── shoot.mp3
│ │ ├── shoot.ogg
│ │ ├── success.mp3
│ │ ├── success.ogg
│ │ ├── enemy_die.mp3
│ │ ├── enemy_die.ogg
│ │ ├── gameover.mp3
│ │ ├── gameover.ogg
│ │ ├── mushroom.mp3
│ │ ├── mushroom.ogg
│ │ ├── invincible.mp3
│ │ ├── invincible.ogg
│ │ ├── lifeupgrade.mp3
│ │ └── lifeupgrade.ogg
│ ├── wallpaper.jpg
│ ├── mario-enemies.png
│ ├── mario-objects.png
│ ├── mario-peach.png
│ ├── mario-sprites.png
│ ├── backgrounds
│ │ ├── 01.png
│ │ ├── 02.png
│ │ ├── 03.png
│ │ ├── 04.png
│ │ ├── 05.png
│ │ ├── 06.png
│ │ ├── 07.png
│ │ └── 08.png
│ └── fonts
│ │ └── SuperMarioBros.ttf
├── Styles
│ ├── app.scss
│ └── mario.scss
├── engine
│ ├── index.ts
│ ├── Gauge.ts
│ ├── constants.ts
│ ├── Base.ts
│ └── Level.ts
├── index.html
├── items
│ ├── MultipleCoinBox.ts
│ ├── ItemFigure.ts
│ ├── index.ts
│ ├── Coin.ts
│ ├── StarBox.ts
│ ├── CoinBoxCoin.ts
│ ├── MushroomBox.ts
│ ├── CoinBox.ts
│ ├── Star.ts
│ ├── Item.ts
│ └── Mushroom.ts
├── modules.d.ts
├── app.ts
├── matter
│ ├── Ground.ts
│ ├── Soil.ts
│ ├── LeftBush.ts
│ ├── LeftSoil.ts
│ ├── RightBush.ts
│ ├── RightSoil.ts
│ ├── LeftPipeSoil.ts
│ ├── MiddleBush.ts
│ ├── LeftMiddleBush.ts
│ ├── LeftPipeGrass.ts
│ ├── RightPipeGrass.ts
│ ├── RightPipeSoil.ts
│ ├── LeftPlantedSoil.ts
│ ├── RightMiddleBush.ts
│ ├── RightPlantedSoil.ts
│ ├── TopLeftGrassSoil.ts
│ ├── MiddlePlantedSoil.ts
│ ├── Stone.ts
│ ├── TopLeftCornerGrass.ts
│ ├── TopRightCornerGrass.ts
│ ├── TopRightGrassSoil.ts
│ ├── LeftGrass.ts
│ ├── TopGrass.ts
│ ├── BrownBlock.ts
│ ├── LeftTopPipe.ts
│ ├── RightGrass.ts
│ ├── RightTopPipe.ts
│ ├── LeftPipe.ts
│ ├── TopLeftRoundedGrass.ts
│ ├── RightPipe.ts
│ ├── TopRightRoundedGrass.ts
│ ├── TopLeftGrass.ts
│ ├── TopRightGrass.ts
│ ├── Decoration.ts
│ ├── Matter.ts
│ └── index.ts
├── figures
│ ├── Turtle.ts
│ ├── index.ts
│ ├── Plant.ts
│ ├── StaticPlant.ts
│ ├── Bullet.ts
│ ├── SpikedTurtle.ts
│ ├── Gumpa.ts
│ ├── TurtleShell.ts
│ ├── Enemy.ts
│ ├── PipePlant.ts
│ ├── GreenTurtle.ts
│ ├── Figure.ts
│ └── Mario.ts
├── effects.codegen
├── mario.ts
├── utils
│ └── index.ts
├── keys.ts
├── types
│ └── index.ts
├── assets.ts
├── audio.ts
└── levels.ts
├── prettier.config.js
├── .editorconfig
├── .gitignore
├── tsconfig.json
├── package.json
├── tslint.json
├── LICENSE
└── README.md
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './mario';
2 |
--------------------------------------------------------------------------------
/src/Assets/audio/coin.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/coin.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/coin.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/die.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/die.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/die.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/die.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/game.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/game.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/game.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/game.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/grow.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/grow.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/grow.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/grow.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/hurt.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/hurt.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/hurt.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/hurt.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/jump.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/jump.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/jump.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/menu.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/menu.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/menu.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/menu.ogg
--------------------------------------------------------------------------------
/src/Assets/wallpaper.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/wallpaper.jpg
--------------------------------------------------------------------------------
/src/Assets/audio/editor.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/editor.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/editor.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/editor.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/ending.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/ending.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/ending.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/ending.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/peach.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/peach.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/peach.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/peach.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/shell.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/shell.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/shell.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/shell.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/shoot.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/shoot.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/shoot.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/success.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/success.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/success.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/success.ogg
--------------------------------------------------------------------------------
/src/Assets/mario-enemies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/mario-enemies.png
--------------------------------------------------------------------------------
/src/Assets/mario-objects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/mario-objects.png
--------------------------------------------------------------------------------
/src/Assets/mario-peach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/mario-peach.png
--------------------------------------------------------------------------------
/src/Assets/mario-sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/mario-sprites.png
--------------------------------------------------------------------------------
/src/Assets/audio/enemy_die.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/enemy_die.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/enemy_die.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/enemy_die.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/gameover.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/gameover.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/gameover.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/gameover.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/mushroom.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/mushroom.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/mushroom.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/mushroom.ogg
--------------------------------------------------------------------------------
/src/Assets/backgrounds/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/01.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/02.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/03.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/04.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/05.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/06.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/07.png
--------------------------------------------------------------------------------
/src/Assets/backgrounds/08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/backgrounds/08.png
--------------------------------------------------------------------------------
/src/Styles/app.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background: url('../Assets/wallpaper.jpg');
3 | background-size: cover;
4 | }
5 |
--------------------------------------------------------------------------------
/src/Assets/audio/invincible.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/invincible.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/invincible.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/invincible.ogg
--------------------------------------------------------------------------------
/src/Assets/audio/lifeupgrade.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/lifeupgrade.mp3
--------------------------------------------------------------------------------
/src/Assets/audio/lifeupgrade.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/audio/lifeupgrade.ogg
--------------------------------------------------------------------------------
/src/Assets/fonts/SuperMarioBros.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlorianRappl/Mario5TS/HEAD/src/Assets/fonts/SuperMarioBros.ttf
--------------------------------------------------------------------------------
/src/engine/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Base';
2 | export * from './constants';
3 | export * from './Gauge';
4 | export * from './Level';
5 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Super Mario HTML5 TS (Demo)
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 120,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | bracketSpacing: true,
6 | parser: 'typescript',
7 | semi: true,
8 | jsxBracketSameLine: true,
9 | };
10 |
--------------------------------------------------------------------------------
/src/items/MultipleCoinBox.ts:
--------------------------------------------------------------------------------
1 | import { CoinBox } from './CoinBox';
2 | import { Level } from '../engine/Level';
3 |
4 | export class MultipleCoinBox extends CoinBox {
5 | constructor(level: Level) {
6 | super(level, 8);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 | max_line_length = null
11 |
--------------------------------------------------------------------------------
/src/modules.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css';
2 | declare module '*.jpeg';
3 | declare module '*.jpg';
4 | declare module '*.gif';
5 | declare module '*.png';
6 | declare module '*.svg';
7 | declare module '*.ttf';
8 | declare module '*.mp3';
9 | declare module '*.ogg';
10 | declare module '*.codegen';
11 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import { appendMarioTo } from './mario';
2 |
3 | function createHost() {
4 | return document.appendChild(document.createElement('div'));
5 | }
6 |
7 | const host = document.querySelector('#app') || createHost();
8 |
9 | appendMarioTo(host, {
10 | sound: true,
11 | }).then(game => game.start());
12 |
--------------------------------------------------------------------------------
/src/matter/Ground.ts:
--------------------------------------------------------------------------------
1 | import { Matter } from './Matter';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking } from '../engine/constants';
4 |
5 | export class Ground extends Matter {
6 | constructor(blocking: GroundBlocking, level: Level) {
7 | super(blocking, level);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/items/ItemFigure.ts:
--------------------------------------------------------------------------------
1 | import { Figure } from '../figures/Figure';
2 | import { Level } from '../engine/Level';
3 |
4 | export class ItemFigure extends Figure {
5 | constructor(level: Level) {
6 | super(level);
7 | }
8 |
9 | bounce(dx: number, dy: number) {
10 | this.setVelocity(dx, dy);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/matter/Soil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class Soil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 888, 438);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftBush.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftBush extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 178, 928);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 854, 540);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightBush.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightBush extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 382, 928);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 922, 540);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/items/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Coin';
2 | export * from './CoinBox';
3 | export * from './CoinBoxCoin';
4 | export * from './MultipleCoinBox';
5 | export * from './Item';
6 | export * from './ItemFigure';
7 | export * from './Mushroom';
8 | export * from './MushroomBox';
9 | export * from './Star';
10 | export * from './StarBox';
11 |
--------------------------------------------------------------------------------
/src/matter/LeftPipeSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftPipeSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 2, 458);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/MiddleBush.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class MiddleBush extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 348, 928);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftMiddleBush.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftMiddleBush extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 212, 928);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftPipeGrass.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftPipeGrass extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 2, 424);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightPipeGrass.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightPipeGrass extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 36, 424);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightPipeSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightPipeSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 36, 458);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftPlantedSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class LeftPlantedSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 714, 832);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightMiddleBush.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightMiddleBush extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 314, 928);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightPlantedSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class RightPlantedSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 782, 832);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopLeftGrassSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class TopLeftGrassSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 956, 506);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/MiddlePlantedSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class MiddlePlantedSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 748, 832);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/Stone.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class Stone extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.all, level);
8 | this.setImage(images.objects, 550, 160);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopLeftCornerGrass.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class TopLeftCornerGrass extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 648, 868);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopRightCornerGrass.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class TopRightCornerGrass extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 612, 868);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopRightGrassSoil.ts:
--------------------------------------------------------------------------------
1 | import { Decoration } from './Decoration';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 |
5 | export class TopRightGrassSoil extends Decoration {
6 | constructor(level: Level) {
7 | super(level);
8 | this.setImage(images.objects, 990, 506);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class LeftGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.left, level);
8 | this.setImage(images.objects, 854, 438);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class TopGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.top, level);
8 | this.setImage(images.objects, 888, 404);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/BrownBlock.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class BrownBlock extends Ground {
6 | constructor( level: Level) {
7 | super(GroundBlocking.all, level);
8 | this.setImage(images.objects, 514, 194);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/LeftTopPipe.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class LeftTopPipe extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.all, level);
8 | this.setImage(images.objects, 2, 358);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class RightGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.right, level);
8 | this.setImage(images.objects, 922, 438);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightTopPipe.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class RightTopPipe extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.all, level);
8 | this.setImage(images.objects, 36, 358);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/figures/Turtle.ts:
--------------------------------------------------------------------------------
1 | import { Enemy } from './Enemy';
2 | import { TurtleShell } from './TurtleShell';
3 | import { Level } from '../engine/Level';
4 |
5 | export class Turtle extends Enemy {
6 | house?: TurtleShell;
7 |
8 | constructor(level: Level) {
9 | super(level);
10 | }
11 |
12 | setShell(_shell: TurtleShell) {
13 | return false;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/matter/LeftPipe.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class LeftPipe extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.left + GroundBlocking.bottom, level);
8 | this.setImage(images.objects, 2, 390);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopLeftRoundedGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class TopLeftRoundedGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.top, level);
8 | this.setImage(images.objects, 854, 506);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/RightPipe.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class RightPipe extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.right + GroundBlocking.bottom, level);
8 | this.setImage(images.objects, 36, 390);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopRightRoundedGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class TopRightRoundedGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.top, level);
8 | this.setImage(images.objects, 922, 506);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopLeftGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class TopLeftGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.left + GroundBlocking.top, level);
8 | this.setImage(images.objects, 854, 404);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/matter/TopRightGrass.ts:
--------------------------------------------------------------------------------
1 | import { Ground } from './Ground';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 |
5 | export class TopRightGrass extends Ground {
6 | constructor(level: Level) {
7 | super(GroundBlocking.top + GroundBlocking.right, level);
8 | this.setImage(images.objects, 922, 404);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/figures/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Bullet';
2 | export * from './Enemy';
3 | export * from './Figure';
4 | export * from './GreenTurtle';
5 | export * from './Gumpa';
6 | export * from './Mario';
7 | export * from './PipePlant';
8 | export * from './Plant';
9 | export * from './SpikedTurtle';
10 | export * from './StaticPlant';
11 | export * from './Turtle';
12 | export * from './TurtleShell';
13 |
--------------------------------------------------------------------------------
/src/engine/Gauge.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './Base';
2 |
3 | export class Gauge extends Base {
4 | constructor(el: HTMLElement, startImgX: number, startImgY: number, fps: number, frames: number, rewind: boolean) {
5 | super();
6 | this.view = el;
7 |
8 | this.setSize(el.offsetWidth, el.offsetHeight);
9 | this.setImage(this.view.style.backgroundImage || '', startImgX, startImgY);
10 | this.setupFrames(fps, frames, rewind);
11 | }
12 |
13 | init() {
14 | super.init(0, 0);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/items/Coin.ts:
--------------------------------------------------------------------------------
1 | import { Item } from './Item';
2 | import { Level } from '../engine/Level';
3 | import { images } from '../engine/constants';
4 | import { Mario } from '../figures/Mario';
5 |
6 | export class Coin extends Item {
7 | constructor(level: Level) {
8 | super(false, level);
9 | this.setImage(images.objects, 0, 0);
10 | }
11 |
12 | init(x: number, y: number) {
13 | super.init(x, y);
14 | this.setupFrames(10, 4, true);
15 | }
16 |
17 | activate(from: Mario) {
18 | if (!this.activated) {
19 | this.level.playSound('coin');
20 | from.addCoin();
21 | this.remove();
22 | }
23 |
24 | super.activate(from);
25 | }
26 |
27 | remove() {
28 | this.view.remove();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 | .fusebox
10 | .cache
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
19 | .grunt
20 |
21 | # Compiled binary addons (http://nodejs.org/api/addons.html)
22 | build/Release
23 |
24 | # Dependency directory
25 | # Commenting this out is preferred by some people, see
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
27 | node_modules
28 |
29 | # Output directory
30 | release
31 | dist
32 |
33 | # Users Environment Variables
34 | .lock-wscript
35 |
--------------------------------------------------------------------------------
/src/effects.codegen:
--------------------------------------------------------------------------------
1 | const { readdirSync } = require('fs');
2 | const { resolve } = require('path');
3 |
4 | module.exports = function() {
5 | const root = resolve(__dirname, 'Assets', 'audio');
6 | const files = readdirSync(root);
7 |
8 | const oggFiles = files
9 | .filter(m => m.endsWith('.ogg'))
10 | .map(m => m.substr(0, m.length - 4))
11 | .map(m => `${m}: require('./Assets/audio/${m}.ogg')`);
12 |
13 | const mp3Files = files
14 | .filter(m => m.endsWith('.mp3'))
15 | .map(m => m.substr(0, m.length - 4))
16 | .map(m => `${m}: require('./Assets/audio/${m}.mp3')`);
17 |
18 | return `
19 | export const effects = {
20 | '.ogg': {
21 | ${oggFiles.join(',')}
22 | },
23 | '.mp3': {
24 | ${mp3Files.join(',')}
25 | },
26 | };
27 | `;
28 | };
29 |
--------------------------------------------------------------------------------
/src/matter/Decoration.ts:
--------------------------------------------------------------------------------
1 | import { Matter } from './Matter';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking } from '../engine/constants';
4 | import { toUrl, setStyle } from '../utils';
5 |
6 | export class Decoration extends Matter {
7 | constructor(level: Level) {
8 | super(GroundBlocking.none, level);
9 | level.decorations.push(this);
10 | }
11 |
12 | setImage(img: string, x: number = 0, y: number = 0) {
13 | setStyle(this.view, {
14 | backgroundImage: img ? toUrl(img) : 'none',
15 | backgroundPosition: `-${x}px -${y}px`,
16 | });
17 | super.setImage(img, x, y);
18 | }
19 |
20 | setPosition(x: number, y: number) {
21 | setStyle(this.view, {
22 | left: `${x}px`,
23 | bottom: `${y}px`,
24 | });
25 | super.setPosition(x, y);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/figures/Plant.ts:
--------------------------------------------------------------------------------
1 | import { Enemy } from './Enemy';
2 | import { Figure } from './Figure';
3 | import { Level } from '../engine/Level';
4 | import { images } from '../engine/constants';
5 |
6 | export class Plant extends Enemy {
7 | constructor(level: Level) {
8 | super(level);
9 | }
10 |
11 | init(x: number, y: number) {
12 | super.init(x, y);
13 | this.setSize(34, 42);
14 | this.setupFrames(5, 2, true);
15 | this.setImage(images.enemies, 0, 3);
16 | }
17 |
18 | setVelocity(_vx: number, _vy: number) {
19 | super.setVelocity(0, 0);
20 | }
21 |
22 | die() {
23 | this.level.playSound('shell');
24 | this.clearFrames();
25 | super.die();
26 | }
27 |
28 | hit(opponent: Figure) {
29 | if (!this.invisible && opponent.player) {
30 | opponent.hurt(this);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist",
4 | "sourceMap": true,
5 | "noImplicitAny": true,
6 | "allowUnreachableCode": false,
7 | "alwaysStrict": true,
8 | "declaration": true,
9 | "strictNullChecks": true,
10 | "noUnusedParameters": true,
11 | "noImplicitReturns": true,
12 | "module": "esnext",
13 | "target": "es5",
14 | "moduleResolution": "node",
15 | "emitDecoratorMetadata": true,
16 | "experimentalDecorators": true,
17 | "lib": [
18 | "es2015",
19 | "dom"
20 | ],
21 | "jsx": "react",
22 | "suppressImplicitAnyIndexErrors": true,
23 | "allowSyntheticDefaultImports": false
24 | },
25 | "include": [
26 | "./src/**/*"
27 | ],
28 | "exclude": [
29 | "node_modules",
30 | "src/app.ts",
31 | "src/**/*.test.ts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/items/StarBox.ts:
--------------------------------------------------------------------------------
1 | import { Item } from './Item';
2 | import { Star } from './Star';
3 | import { Level } from '../engine/Level';
4 | import { images } from '../engine/constants';
5 | import { Mario } from '../figures/Mario';
6 |
7 | export class StarBox extends Item {
8 | star: Star;
9 |
10 | constructor(level: Level) {
11 | super(true, level);
12 | this.setImage(images.objects, 96, 33);
13 | this.star = new Star(level);
14 | }
15 |
16 | init(x: number, y: number) {
17 | super.init(x, y);
18 | this.star.init(x, y);
19 | this.setupFrames(8, 4, false);
20 | }
21 |
22 | activate(from: Mario) {
23 | if (!this.activated) {
24 | this.star.release();
25 | this.clearFrames();
26 | this.bounce();
27 | this.setImage(images.objects, 514, 194);
28 | }
29 |
30 | super.activate(from);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/mario.ts:
--------------------------------------------------------------------------------
1 | import './Styles/mario.scss';
2 | import { Level } from './engine';
3 | import { assets } from './assets';
4 | import { keys } from './keys';
5 | import { LevelFormat } from './types';
6 |
7 | export interface MarioGameOptions {
8 | sound?: boolean;
9 | level?: number;
10 | levels?: Array;
11 | }
12 |
13 | export function appendMarioTo(host: Element, options: MarioGameOptions = {}) {
14 | const loadingLevels = options.levels || import('./levels').then(m => m.default);
15 |
16 | return Promise.resolve(loadingLevels).then(levels => {
17 | const level = new Level(host, keys, levels, assets);
18 | level.load(levels[options.level || 0]);
19 |
20 | if (options.sound !== false) {
21 | import('./audio').then(({ HtmlAudioManager }) => {
22 | const sounds = new HtmlAudioManager();
23 | level.setSounds(sounds);
24 | });
25 | }
26 |
27 | return level;
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/figures/StaticPlant.ts:
--------------------------------------------------------------------------------
1 | import { Plant } from './Plant';
2 | import { setup, images } from '../engine/constants';
3 | import { Level } from '../engine/Level';
4 | import { shiftBy } from '../utils';
5 |
6 | export class StaticPlant extends Plant {
7 | constructor(level: Level) {
8 | super(level);
9 | this.deathFrames = Math.floor(250 / setup.interval);
10 | this.deathStepUp = Math.ceil(100 / this.deathFrames);
11 | this.deathStepDown = Math.ceil(132 / this.deathFrames);
12 | this.deathDir = 1;
13 | this.deathCount = 0;
14 | }
15 |
16 | die() {
17 | super.die();
18 | this.setImage(images.enemies, 68, 3);
19 | }
20 |
21 | death() {
22 | shiftBy(this.view, 'bottom', this.deathDir, this.deathDir > 0 ? this.deathStepUp : this.deathStepDown);
23 | this.deathCount += this.deathDir;
24 |
25 | if (this.deathCount === this.deathFrames) {
26 | this.deathDir = -1;
27 | } else if (this.deathCount === 0) {
28 | return false;
29 | }
30 |
31 | return true;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mario5",
3 | "version": "3.0.0",
4 | "description": "TypeScript version of the Mario5 demo application.",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "scripts": {
8 | "start": "npm run build:debug",
9 | "lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}'",
10 | "test": "npm run lint",
11 | "prettier": "prettier --config prettier.config.js --write src/**/*.{ts,tsx}",
12 | "build:production": "parcel build src/index.html",
13 | "build:debug": "parcel src/index.html",
14 | "build:types": "tsc --emitDeclarationOnly",
15 | "build": "rimraf dist && npm run build:production && npm run build:types"
16 | },
17 | "author": "Florian Rappl",
18 | "license": "MIT",
19 | "devDependencies": {
20 | "parcel-bundler": "1.12.4",
21 | "parcel-plugin-codegen": "0.5.0",
22 | "prettier": "1.19.1",
23 | "rimraf": "^2.6.3",
24 | "sass": "^1.23.6",
25 | "tslint": "^5.17.0",
26 | "tslint-config-prettier": "^1.18.0",
27 | "tslint-plugin-prettier": "^1.3.0",
28 | "typescript": "^3.5.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/items/CoinBoxCoin.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from './Coin';
2 | import { Level } from '../engine/Level';
3 | import { images, setup } from '../engine/constants';
4 | import { Mario } from '../figures/Mario';
5 | import { setStyle, shiftBy } from '../utils';
6 |
7 | export class CoinBoxCoin extends Coin {
8 | count: number;
9 | step: number;
10 |
11 | constructor(level: Level) {
12 | super(level);
13 | this.setImage(images.objects, 96, 0);
14 | this.clearFrames();
15 | setStyle(this.view, {
16 | display: 'none',
17 | });
18 | this.count = 0;
19 | this.frames = Math.floor(150 / setup.interval);
20 | this.step = Math.ceil(30 / this.frames);
21 | }
22 |
23 | remove() {}
24 |
25 | addToGrid() {}
26 |
27 | addToAny() {}
28 |
29 | activate(from: Mario) {
30 | super.activate(from);
31 | setStyle(this.view, {
32 | display: 'block',
33 | });
34 | shiftBy(this.view, 'bottom', 1, 8);
35 | }
36 |
37 | act() {
38 | shiftBy(this.view, 'bottom', 1, this.step);
39 | this.count++;
40 | return this.count === this.frames;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint-config-prettier"
4 | ],
5 | "rulesDirectory": [
6 | "tslint-plugin-prettier"
7 | ],
8 | "rules": {
9 | "prettier": true,
10 | "prefer-for-of": true,
11 | "curly": [
12 | true,
13 | "ignore-same-line"
14 | ],
15 | "no-duplicate-variable": true,
16 | "no-null-keyword": true,
17 | "no-string-literal": true,
18 | "no-var-keyword": true,
19 | "radix": true,
20 | "triple-equals": [
21 | true,
22 | "allow-undefined-check"
23 | ],
24 | "array-type": [
25 | true,
26 | "generic"
27 | ],
28 | "arrow-return-shorthand": true,
29 | "no-consecutive-blank-lines": true,
30 | "one-variable-per-declaration": [
31 | true,
32 | "ignore-for-loop"
33 | ],
34 | "indent": [
35 | true,
36 | "spaces",
37 | 2
38 | ],
39 | "variable-name": [
40 | true,
41 | "ban-keywords",
42 | "check-format",
43 | "allow-pascal-case",
44 | "allow-leading-underscore"
45 | ],
46 | "no-eval": true,
47 | "prefer-const": true
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2014 - 2019 Florian Rappl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/items/MushroomBox.ts:
--------------------------------------------------------------------------------
1 | import { Item } from './Item';
2 | import { Mushroom } from './Mushroom';
3 | import { MushroomMode, images, SizeState } from '../engine/constants';
4 | import { Level } from '../engine/Level';
5 | import { Mario } from '../figures/Mario';
6 |
7 | export class MushroomBox extends Item {
8 | max_mode: MushroomMode;
9 | mushroom: Mushroom;
10 |
11 | constructor(level: Level) {
12 | super(true, level);
13 | this.setImage(images.objects, 96, 33);
14 | this.max_mode = MushroomMode.plant;
15 | this.mushroom = new Mushroom(level);
16 | }
17 |
18 | init(x: number, y: number) {
19 | super.init(x, y);
20 | this.mushroom.init(x, y);
21 | this.setupFrames(8, 4, false);
22 | }
23 |
24 | activate(from: Mario) {
25 | if (!this.activated) {
26 | if (from.state === SizeState.small || this.max_mode === MushroomMode.mushroom) {
27 | this.mushroom.release(MushroomMode.mushroom);
28 | } else {
29 | this.mushroom.release(MushroomMode.plant);
30 | }
31 |
32 | this.clearFrames();
33 | this.bounce();
34 | this.setImage(images.objects, 514, 194);
35 | }
36 |
37 | super.activate(from);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/matter/Matter.ts:
--------------------------------------------------------------------------------
1 | import { Base } from '../engine/Base';
2 | import { Level } from '../engine/Level';
3 | import { GroundBlocking, images } from '../engine/constants';
4 | import { toUrl, setStyle, createBox } from '../utils';
5 |
6 | export class Matter extends Base {
7 | blocking: GroundBlocking;
8 | level: Level;
9 |
10 | constructor(blocking: GroundBlocking, level: Level) {
11 | super();
12 | this.level = level;
13 | this.blocking = blocking;
14 | this.view = createBox(level.world, 'matter');
15 | }
16 |
17 | init(x: number, y: number) {
18 | super.init(x, y);
19 | this.setSize(32, 32);
20 | this.addToGrid(this.level);
21 | }
22 |
23 | addToGrid(level: Level) {
24 | level.obstacles[this.x / 32][this.level.getGridHeight() - 1 - this.y / 32] = this;
25 | }
26 |
27 | setImage(img: string, x = 0, y = 0) {
28 | setStyle(this.view, {
29 | backgroundImage: img ? toUrl(img) : 'none',
30 | backgroundPosition: `-${x}px -${y}px`,
31 | });
32 | super.setImage(img, x, y);
33 | }
34 |
35 | setPosition(x: number, y: number) {
36 | setStyle(this.view, {
37 | left: `${x}px`,
38 | bottom: `${y}px`,
39 | });
40 | super.setPosition(x, y);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export function toUrl(str: string) {
2 | return `url(${str})`;
3 | }
4 |
5 | export function setStyle(element: HTMLElement | null, style: Partial) {
6 | if (element) {
7 | for (const prop of Object.keys(style)) {
8 | element.style[prop] = style[prop];
9 | }
10 | }
11 | }
12 |
13 | export function createBox(parent: Element, cls: string) {
14 | const child = parent.appendChild(document.createElement('div'));
15 | child.classList.add(cls);
16 | return child;
17 | }
18 |
19 | export function setGauge(world: HTMLElement, cls: string, text: string) {
20 | const parent = world.parentElement;
21 |
22 | if (parent) {
23 | const elements = parent.getElementsByClassName(cls);
24 |
25 | for (let i = 0; i < elements.length; i++) {
26 | elements[i].textContent = text;
27 | }
28 | }
29 | }
30 |
31 | export function shiftBy(element: HTMLElement | null, prop: 'top' | 'bottom' | 'left' | 'right', dir: number, step: number) {
32 | if (element) {
33 | const current = element.style[prop] || '0px';
34 | const sign = Math.sign(dir);
35 | const value = +(current.replace('px', ''));
36 | setStyle(element, {
37 | [prop]: `${value + sign * step}px`,
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/matter/index.ts:
--------------------------------------------------------------------------------
1 | export * from './BrownBlock';
2 | export * from './Decoration';
3 | export * from './Ground';
4 | export * from './LeftBush';
5 | export * from './LeftGrass';
6 | export * from './LeftMiddleBush';
7 | export * from './LeftPipe';
8 | export * from './LeftPipeGrass';
9 | export * from './LeftPipeSoil';
10 | export * from './LeftPlantedSoil';
11 | export * from './LeftSoil';
12 | export * from './LeftTopPipe';
13 | export * from './Matter';
14 | export * from './MiddleBush';
15 | export * from './MiddlePlantedSoil';
16 | export * from './RightBush';
17 | export * from './RightGrass';
18 | export * from './RightMiddleBush';
19 | export * from './RightPipe';
20 | export * from './RightPipeGrass';
21 | export * from './RightPipeSoil';
22 | export * from './RightPlantedSoil';
23 | export * from './RightSoil';
24 | export * from './RightTopPipe';
25 | export * from './Soil';
26 | export * from './Stone';
27 | export * from './TopGrass';
28 | export * from './TopLeftGrass';
29 | export * from './TopLeftCornerGrass';
30 | export * from './TopLeftGrassSoil';
31 | export * from './TopLeftRoundedGrass';
32 | export * from './TopRightCornerGrass';
33 | export * from './TopRightGrass';
34 | export * from './TopRightGrassSoil';
35 | export * from './TopRightRoundedGrass';
36 |
--------------------------------------------------------------------------------
/src/figures/Bullet.ts:
--------------------------------------------------------------------------------
1 | import { Figure } from './Figure';
2 | import { images, setup, Direction } from '../engine/constants';
3 |
4 | export class Bullet extends Figure {
5 | parent: Figure;
6 | life: number;
7 | speed: number;
8 |
9 | constructor(parent: Figure) {
10 | super(parent.level);
11 | this.parent = parent;
12 | this.life = Math.ceil(2000 / setup.interval);
13 | this.speed = setup.bullet_v;
14 | }
15 |
16 | init(x: number, y: number) {
17 | super.init(x + 31, y + 14);
18 | this.setImage(images.sprites, 191, 366);
19 | this.setSize(16, 16);
20 | this.direction = this.parent.direction;
21 | this.vy = 0;
22 | this.vx = this.direction === Direction.right ? this.speed : -this.speed;
23 | }
24 |
25 | setVelocity(vx: number, vy: number) {
26 | super.setVelocity(vx, vy);
27 |
28 | if (this.vx === 0) {
29 | const s = this.speed * Math.sign(this.speed);
30 | this.vx = this.direction === Direction.right ? -s : s;
31 | }
32 |
33 | if (this.onground) {
34 | this.vy = setup.bounce;
35 | }
36 | }
37 |
38 | move() {
39 | if (--this.life) {
40 | super.move();
41 | } else {
42 | this.die();
43 | }
44 | }
45 |
46 | hit(opponent: Figure) {
47 | if (opponent !== this.parent) {
48 | opponent.die();
49 | this.die();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/keys.ts:
--------------------------------------------------------------------------------
1 | import { Keys } from './types';
2 |
3 | function keydownHandler(ev: KeyboardEvent) {
4 | return keys.handler(ev, true);
5 | }
6 |
7 | function keyupHandler(ev: KeyboardEvent) {
8 | return keys.handler(ev, false);
9 | }
10 |
11 | export const keys: Keys = {
12 | bind() {
13 | document.addEventListener('keydown', keydownHandler);
14 | document.addEventListener('keyup', keyupHandler);
15 | },
16 | reset() {
17 | keys.left = false;
18 | keys.right = false;
19 | keys.accelerate = false;
20 | keys.up = false;
21 | keys.down = false;
22 | },
23 | unbind() {
24 | document.removeEventListener('keydown', keydownHandler);
25 | document.removeEventListener('keyup', keyupHandler);
26 | },
27 | handler(ev: KeyboardEvent, status: boolean) {
28 | switch (ev.keyCode) {
29 | case 57392: //CTRL on MAC
30 | case 17: //CTRL
31 | case 65: //A
32 | keys.accelerate = status;
33 | break;
34 | case 40: //DOWN ARROW
35 | keys.down = status;
36 | break;
37 | case 39: //RIGHT ARROW
38 | keys.right = status;
39 | break;
40 | case 37: //LEFT ARROW
41 | keys.left = status;
42 | break;
43 | case 38: //UP ARROW
44 | keys.up = status;
45 | break;
46 | default:
47 | return true;
48 | }
49 |
50 | ev.preventDefault();
51 | return false;
52 | },
53 | accelerate: false,
54 | left: false,
55 | up: false,
56 | right: false,
57 | down: false,
58 | };
59 |
--------------------------------------------------------------------------------
/src/items/CoinBox.ts:
--------------------------------------------------------------------------------
1 | import { Item } from './Item';
2 | import { CoinBoxCoin } from './CoinBoxCoin';
3 | import { Mario } from '../figures/Mario';
4 | import { images } from '../engine/constants';
5 | import { Level } from '../engine/Level';
6 |
7 | export class CoinBox extends Item {
8 | items: Array;
9 | actors: Array;
10 |
11 | constructor(level: Level, private amount: number = 1) {
12 | super(true, level);
13 | this.setImage(images.objects, 346, 328);
14 | this.items = [];
15 | this.actors = [];
16 | }
17 |
18 | init(x: number, y: number) {
19 | super.init(x, y);
20 |
21 | for (let i = 0; i < this.amount; i++) {
22 | const coin = new CoinBoxCoin(this.level);
23 | coin.init(this.x, this.y);
24 | this.items.push(coin);
25 | }
26 | }
27 |
28 | activate(from: Mario) {
29 | if (!this.isBouncing) {
30 | if (this.items.length) {
31 | this.bounce();
32 | const coin = this.items.pop();
33 |
34 | if (coin) {
35 | coin.activate(from);
36 | this.actors.push(coin);
37 | }
38 |
39 | if (!this.items.length) {
40 | this.setImage(images.objects, 514, 194);
41 | }
42 | }
43 | }
44 |
45 | super.activate(from);
46 | }
47 |
48 | playFrame() {
49 | for (let i = this.actors.length; i--; ) {
50 | if (this.actors[i].act()) {
51 | this.actors[i].view.remove();
52 | this.actors.splice(i, 1);
53 | }
54 | }
55 |
56 | super.playFrame();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/items/Star.ts:
--------------------------------------------------------------------------------
1 | import { ItemFigure } from './ItemFigure';
2 | import { Figure } from '../figures/Figure';
3 | import { Level } from '../engine/Level';
4 | import { images, setup, GroundBlocking } from '../engine/constants';
5 | import { Mario } from '../figures/Mario';
6 | import { setStyle } from '../utils';
7 |
8 | export class Star extends ItemFigure {
9 | active: boolean;
10 | taken: number;
11 |
12 | constructor(level: Level) {
13 | super(level);
14 | this.active = false;
15 | this.setImage(images.objects, 32, 69);
16 | setStyle(this.view, {
17 | display: 'none',
18 | });
19 | }
20 |
21 | init(x: number, y: number) {
22 | super.init(x, y + 32);
23 | this.setSize(32, 32);
24 | }
25 |
26 | release() {
27 | this.taken = 4;
28 | this.active = true;
29 | this.level.playSound('mushroom');
30 | setStyle(this.view, {
31 | display: 'block',
32 | });
33 | this.setVelocity(setup.star_vx, setup.star_vy);
34 | this.setupFrames(10, 2, false);
35 | }
36 |
37 | collides(_is: number, _ie: number, _js: number, _je: number, _blocking: GroundBlocking) {
38 | return false;
39 | }
40 |
41 | move() {
42 | if (this.active) {
43 | this.vy += this.vy <= -setup.star_vy ? setup.gravity : setup.gravity / 2;
44 | super.move();
45 | }
46 |
47 | if (this.taken) {
48 | this.taken--;
49 | }
50 | }
51 |
52 | hit(opponent: Figure) {
53 | if (!this.taken && this.active && opponent.player) {
54 | (opponent).invincible();
55 | this.die();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mario5 TS
2 |
3 | This repository contains the code for the TypeScript version of the Mario5 demo application.
4 |
5 | **[Online demo at mario5ts.florian-rappl.de](https://mario5ts.florian-rappl.de)**
6 |
7 | ## Requirements
8 |
9 | For compiling the code you will need the following applications:
10 |
11 | * Node.js (tested with v10) for running gulp
12 | * NPM (tested with v6) for installation
13 |
14 | The rest will be installed upon local installation.
15 |
16 | # Installation
17 |
18 | First you should clone the repository. Then in the directory of the repository run
19 |
20 | ```sh
21 | npm install
22 | ```
23 |
24 | If you want to run the game just type in
25 |
26 | ```sh
27 | npm start
28 | ```
29 |
30 | Otherwise if you want to build it for deployment, run
31 |
32 | ```sh
33 | npm run build
34 | ```
35 |
36 | ## Releases
37 |
38 | ### Current
39 |
40 | (branch: `master`)
41 |
42 | * Refined use of modern module system
43 | * Use Parcel for bundling
44 | * Generate repetitive code
45 | * Use SASS for the style
46 |
47 | ### Fusebox
48 |
49 | (tag: `fuse`)
50 |
51 | * Use modern module system
52 | * Apply FuseBox for bundling
53 | * Standard CSS for the style
54 | * Removed jQuery (completely DOM standard rendering)
55 |
56 | ### Legacy
57 |
58 | (tag: `legacy`)
59 |
60 | * The original JavaScript code is available in `src/Original`.
61 | * The description below hints, where features of TypeScript have been placed.
62 | * An article describing the original code is available on [CodeProject](http://www.codeproject.com/Articles/396959/Mario).
63 | * The system is built by using `gulp`.
64 |
65 | The legacy branch README also contains some more background information.
66 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export interface Settings {
2 | state: number;
3 | marioState: number;
4 | lifes: number;
5 | coins: number;
6 | musicOn: boolean;
7 | }
8 |
9 | export interface BaseItem {
10 | x: number;
11 | y: number;
12 | }
13 |
14 | export interface StateItem extends BaseItem {
15 | vx: number;
16 | vy: number;
17 | dx: number;
18 | dy: number;
19 | cw: number;
20 | ch: number;
21 | player: boolean;
22 | shellHost: boolean;
23 | dead: boolean;
24 | death(): boolean;
25 | die(): void;
26 | playFrame(): void;
27 | store(settings: Partial): void;
28 | restore(settings: Settings): void;
29 | view: any;
30 | hit(other: StateItem): void;
31 | q2q(other: StateItem): boolean;
32 | move(): void;
33 | bounce(dx: number, dy: number): void;
34 | }
35 |
36 | export interface LevelFormat {
37 | width: number;
38 | height: number;
39 | id: number;
40 | background: number;
41 | data: Array>;
42 | }
43 |
44 | export interface Point {
45 | x: number;
46 | y: number;
47 | }
48 |
49 | export interface GridPoint {
50 | i: number;
51 | j: number;
52 | }
53 |
54 | export interface Size {
55 | width: number;
56 | height: number;
57 | }
58 |
59 | export interface Picture extends Point {
60 | path: string;
61 | }
62 |
63 | export interface SoundManager {
64 | play(label: string): void;
65 | sideMusic(label: string): void;
66 | }
67 |
68 | export interface DeathAnimation {
69 | deathDir: number;
70 | deathFrames: number;
71 | deathStepUp: number;
72 | deathStepDown: number;
73 | deathCount: number;
74 | }
75 |
76 | export interface Keys {
77 | bind(): void;
78 | reset(): void;
79 | unbind(): void;
80 | handler(event: KeyboardEvent, status: boolean): void;
81 | accelerate: boolean;
82 | left: boolean;
83 | up: boolean;
84 | right: boolean;
85 | down: boolean;
86 | }
87 |
--------------------------------------------------------------------------------
/src/items/Item.ts:
--------------------------------------------------------------------------------
1 | import { Matter } from '../matter';
2 | import { GroundBlocking, setup } from '../engine/constants';
3 | import { Level } from '../engine/Level';
4 | import { Mario } from '../figures/Mario';
5 | import { shiftBy } from '../utils';
6 |
7 | export class Item extends Matter {
8 | isBouncing: boolean;
9 | bounceFrames: number;
10 | bounceStep: number;
11 | bounceDir: number;
12 | bounceCount: number;
13 | activated: boolean;
14 | isBlocking: boolean;
15 |
16 | constructor(isBlocking: boolean, level: Level) {
17 | super(isBlocking ? GroundBlocking.all : GroundBlocking.none, level);
18 | this.isBouncing = false;
19 | this.bounceCount = 0;
20 | this.bounceFrames = Math.floor(50 / setup.interval);
21 | this.bounceStep = Math.ceil(10 / this.bounceFrames);
22 | this.bounceDir = 1;
23 | this.isBlocking = isBlocking;
24 | this.activated = false;
25 | this.addToAny(level);
26 | }
27 |
28 | addToAny(level: Level) {
29 | level.items.push(this);
30 | }
31 |
32 | activate(_from: Mario) {
33 | this.activated = true;
34 | }
35 |
36 | bounce() {
37 | this.isBouncing = true;
38 |
39 | for (let i = this.level.figures.length; i--; ) {
40 | const fig = this.level.figures[i];
41 |
42 | if (fig.y === this.y + 32 && fig.x >= this.x - 16 && fig.x <= this.x + 16) {
43 | fig.bounce(fig.vx, setup.bounce);
44 | }
45 | }
46 | }
47 |
48 | playFrame() {
49 | if (this.isBouncing) {
50 | shiftBy(this.view, 'bottom', this.bounceDir, this.bounceStep);
51 | this.bounceCount += this.bounceDir;
52 |
53 | if (this.bounceCount === this.bounceFrames) {
54 | this.bounceDir = -1;
55 | } else if (this.bounceCount === 0) {
56 | this.bounceDir = 1;
57 | this.isBouncing = false;
58 | }
59 | }
60 |
61 | super.playFrame();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Styles/mario.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'SMB';
3 | src: local('Super Mario Bros.'), url('../Assets/fonts/SuperMarioBros.ttf') format('truetype');
4 | font-style: normal;
5 | }
6 |
7 | .game {
8 | height: 480px;
9 | width: 640px;
10 | position: absolute;
11 | left: 50%;
12 | top: 50%;
13 | margin-left: -321px;
14 | margin-top: -241px;
15 | border: 1px solid #ccc;
16 | overflow: hidden;
17 |
18 | .figure {
19 | margin: 0;
20 | padding: 0;
21 | z-index: 99;
22 | position: absolute;
23 | }
24 |
25 | .matter {
26 | margin: 0;
27 | padding: 0;
28 | z-index: 95;
29 | position: absolute;
30 | width: 32px;
31 | height: 32px;
32 | }
33 |
34 | > .world {
35 | margin: 0;
36 | padding: 0;
37 | height: 100%;
38 | width: 100%;
39 | position: absolute;
40 | bottom: 0;
41 | left: 0;
42 | z-index: 0;
43 | }
44 |
45 | > .gauge {
46 | font-family: 'SMB';
47 | margin: 0;
48 | padding: 0;
49 | height: 50px;
50 | width: 70px;
51 | text-align: right;
52 | font-size: 2em;
53 | font-weight: bold;
54 | position: absolute;
55 | top: 17px;
56 | right: 52px;
57 | z-index: 1000;
58 | position: absolute;
59 | }
60 |
61 | > .gaugeSprite {
62 | margin: 0;
63 | padding: 0;
64 | z-index: 1000;
65 | position: absolute;
66 | }
67 |
68 | > .coinNumber {
69 | left: 0;
70 | }
71 |
72 | > .liveNumber {
73 | right: 52px;
74 | }
75 |
76 | > .coin {
77 | height: 32px;
78 | width: 32px;
79 | background-image: url('../Assets/mario-objects.png');
80 | background-position: 0 0;
81 | top: 15px;
82 | left: 70px;
83 | }
84 |
85 | > .live {
86 | height: 40px;
87 | width: 40px;
88 | background-image: url('../Assets/mario-sprites.png');
89 | background-position: 0 -430px;
90 | top: 12px;
91 | right: 8px;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/figures/SpikedTurtle.ts:
--------------------------------------------------------------------------------
1 | import { Turtle } from './Turtle';
2 | import { Level } from '../engine/Level';
3 | import { setup, Direction, images } from '../engine/constants';
4 | import { Figure } from './Figure';
5 | import { Mario } from './Mario';
6 | import { shiftBy } from '../utils';
7 |
8 | export class SpikedTurtle extends Turtle {
9 | constructor(level: Level) {
10 | super(level);
11 | this.deathFrames = Math.floor(250 / setup.interval);
12 | this.deathStepUp = Math.ceil(150 / this.deathFrames);
13 | this.deathStepDown = Math.ceil(182 / this.deathFrames);
14 | this.deathDir = 1;
15 | this.deathCount = 0;
16 | }
17 |
18 | init(x: number, y: number) {
19 | super.init(x, y);
20 | this.setSize(34, 32);
21 | this.setSpeed(setup.spiked_turtle_v);
22 | }
23 |
24 | setVelocity(vx: number, vy: number) {
25 | super.setVelocity(vx, vy);
26 |
27 | if (this.direction === Direction.left) {
28 | if (!this.setupFrames(4, 2, true, 'LeftWalk')) {
29 | this.setImage(images.enemies, 0, 106);
30 | }
31 | } else {
32 | if (!this.setupFrames(6, 2, false, 'RightWalk')) {
33 | this.setImage(images.enemies, 34, 147);
34 | }
35 | }
36 | }
37 |
38 | death() {
39 | shiftBy(this.view, 'bottom', this.deathDir, this.deathDir > 0 ? this.deathStepUp : this.deathStepDown);
40 | this.deathCount += this.deathDir;
41 |
42 | if (this.deathCount === this.deathFrames) {
43 | this.deathDir = -1;
44 | } else if (this.deathCount === 0) {
45 | return false;
46 | }
47 |
48 | return true;
49 | }
50 |
51 | die() {
52 | this.level.playSound('shell');
53 | this.clearFrames();
54 | super.die();
55 | this.setImage(images.enemies, 68, this.direction === Direction.left ? 106 : 147);
56 | }
57 |
58 | hit(opponent: Figure) {
59 | if (!this.invisible && opponent.player) {
60 | (opponent).hurt(this);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/figures/Gumpa.ts:
--------------------------------------------------------------------------------
1 | import { Enemy } from './Enemy';
2 | import { Level } from '../engine/Level';
3 | import { setup, Direction, images, DeathMode } from '../engine/constants';
4 | import { shiftBy } from '../utils';
5 |
6 | export class Gumpa extends Enemy {
7 | constructor(level: Level) {
8 | super(level);
9 | }
10 |
11 | init(x: number, y: number) {
12 | super.init(x, y);
13 | this.setSize(34, 32);
14 | this.setSpeed(setup.ballmonster_v);
15 | }
16 |
17 | setVelocity(vx: number, vy: number) {
18 | super.setVelocity(vx, vy);
19 |
20 | if (this.direction === Direction.left) {
21 | if (!this.setupFrames(6, 2, false, 'LeftWalk')) {
22 | this.setImage(images.enemies, 34, 188);
23 | }
24 | } else {
25 | if (!this.setupFrames(6, 2, true, 'RightWalk')) {
26 | this.setImage(images.enemies, 0, 228);
27 | }
28 | }
29 | }
30 |
31 | death() {
32 | if (this.deathMode === DeathMode.normal) {
33 | return !!--this.deathCount;
34 | }
35 |
36 | shiftBy(this.view, 'bottom', this.deathDir, this.deathStep);
37 | this.deathCount += this.deathDir;
38 |
39 | if (this.deathCount === this.deathFrames) {
40 | this.deathDir = -1;
41 | } else if (this.deathCount === 0) {
42 | return false;
43 | }
44 |
45 | return true;
46 | }
47 |
48 | die() {
49 | this.clearFrames();
50 |
51 | if (this.deathMode === DeathMode.normal) {
52 | this.level.playSound('enemy_die');
53 | this.setImage(images.enemies, 102, 228);
54 | this.deathCount = Math.ceil(600 / setup.interval);
55 | } else if (this.deathMode === DeathMode.shell) {
56 | this.level.playSound('shell');
57 | this.setImage(images.enemies, 68, this.direction === Direction.right ? 228 : 188);
58 | this.deathFrames = Math.floor(250 / setup.interval);
59 | this.deathDir = 1;
60 | this.deathStep = Math.ceil(150 / this.deathFrames);
61 | }
62 |
63 | super.die();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/figures/TurtleShell.ts:
--------------------------------------------------------------------------------
1 | import { Enemy } from './Enemy';
2 | import { Level } from '../engine/Level';
3 | import { images, Direction, setup, SizeState, GroundBlocking } from '../engine/constants';
4 | import { Turtle } from './Turtle';
5 | import { Figure } from './Figure';
6 |
7 | export class TurtleShell extends Enemy {
8 | idle: number;
9 |
10 | constructor(level: Level) {
11 | super(level);
12 | this.shell = true;
13 | this.speed = 0;
14 | }
15 |
16 | init(x: number, y: number) {
17 | super.init(x, y);
18 | this.setSize(34, 32);
19 | this.setImage(images.enemies, 0, 494);
20 | }
21 |
22 | activate(x: number, y: number) {
23 | this.setupFrames(6, 4, false);
24 | this.setPosition(x, y);
25 | this.show();
26 | }
27 |
28 | takeBack(where: Turtle) {
29 | if (where.setShell(this)) {
30 | this.clearFrames();
31 | }
32 | }
33 |
34 | hit(opponent: Figure) {
35 | if (this.invisible) {
36 | return;
37 | } else if (this.vx) {
38 | if (this.idle) {
39 | this.idle--;
40 | } else {
41 | opponent.hurt(this);
42 | }
43 | } else if (opponent.player) {
44 | this.setSpeed(opponent.direction === Direction.right ? -setup.shell_v : setup.shell_v);
45 | opponent.setVelocity(opponent.vx, setup.bounce);
46 | this.idle = 2;
47 | } else if (opponent.shellHost && opponent.state === SizeState.small) {
48 | this.takeBack(opponent);
49 | }
50 | }
51 |
52 | collides(is: number, ie: number, js: number, je: number, blocking: GroundBlocking) {
53 | if (is < 0 || ie >= this.level.obstacles.length) {
54 | return true;
55 | } else if (js < 0 || je >= this.level.getGridHeight()) {
56 | return false;
57 | } else {
58 | for (let i = is; i <= ie; i++) {
59 | for (let j = je; j >= js; j--) {
60 | const obj = this.level.obstacles[i][j];
61 |
62 | if (obj && (obj.blocking & blocking) === blocking) {
63 | return true;
64 | }
65 | }
66 | }
67 | }
68 |
69 |
70 | return false;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/items/Mushroom.ts:
--------------------------------------------------------------------------------
1 | import { ItemFigure } from './ItemFigure';
2 | import { Figure } from '../figures/Figure';
3 | import { MushroomMode, images, Direction, setup } from '../engine/constants';
4 | import { Level } from '../engine/Level';
5 | import { Mario } from '../figures/Mario';
6 | import { setStyle } from '../utils';
7 |
8 | export class Mushroom extends ItemFigure {
9 | mode: MushroomMode;
10 | active: boolean;
11 | released: number;
12 |
13 | constructor(level: Level) {
14 | super(level);
15 | this.active = false;
16 | this.setImage(images.objects, 582, 60);
17 | this.released = 0;
18 | setStyle(this.view, {
19 | zIndex: '94',
20 | display: 'none',
21 | });
22 | }
23 |
24 | init(x: number, y: number) {
25 | super.init(x, y);
26 | this.setSize(32, 32);
27 | }
28 |
29 | release(mode: MushroomMode) {
30 | this.released = 4;
31 | this.level.playSound('mushroom');
32 |
33 | if (mode === MushroomMode.plant) {
34 | this.setImage(images.objects, 548, 60);
35 | }
36 |
37 | this.mode = mode;
38 | setStyle(this.view, {
39 | display: 'block',
40 | });
41 | }
42 |
43 | move() {
44 | if (this.active) {
45 | super.move();
46 |
47 | if (this.mode === MushroomMode.mushroom && this.vx === 0) {
48 | this.setVelocity(this.direction === Direction.right ? -setup.mushroom_v : setup.mushroom_v, this.vy);
49 | }
50 | } else if (this.released) {
51 | this.released--;
52 | this.setPosition(this.x, this.y + 8);
53 |
54 | if (!this.released) {
55 | this.active = true;
56 | setStyle(this.view, {
57 | zIndex: '99',
58 | });
59 |
60 | if (this.mode === MushroomMode.mushroom) {
61 | this.setVelocity(setup.mushroom_v, setup.gravity);
62 | }
63 | }
64 | }
65 | }
66 |
67 | hit(opponent: Figure) {
68 | if (this.active && opponent.player) {
69 | if (this.mode === MushroomMode.mushroom) {
70 | (opponent).grow();
71 | } else if (this.mode === MushroomMode.plant) {
72 | (opponent).shooter();
73 | }
74 |
75 | this.die();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/engine/constants.ts:
--------------------------------------------------------------------------------
1 | import enemies from '../Assets/mario-enemies.png';
2 | import sprites from '../Assets/mario-sprites.png';
3 | import objects from '../Assets/mario-objects.png';
4 | import peach from '../Assets/mario-peach.png';
5 | import background1 from '../Assets/backgrounds/01.png';
6 | import background2 from '../Assets/backgrounds/02.png';
7 | import background3 from '../Assets/backgrounds/03.png';
8 | import background4 from '../Assets/backgrounds/04.png';
9 | import background5 from '../Assets/backgrounds/05.png';
10 | import background6 from '../Assets/backgrounds/06.png';
11 | import background7 from '../Assets/backgrounds/07.png';
12 | import background8 from '../Assets/backgrounds/08.png';
13 |
14 | export enum Direction {
15 | none = 0,
16 | left = 1,
17 | up = 2,
18 | right = 3,
19 | down = 4,
20 | }
21 |
22 | export enum MarioState {
23 | normal = 0,
24 | fire = 1,
25 | }
26 |
27 | export enum SizeState {
28 | small = 1,
29 | big = 2,
30 | }
31 |
32 | export enum GroundBlocking {
33 | none = 0,
34 | left = 1,
35 | top = 2,
36 | right = 4,
37 | bottom = 8,
38 | all = 15,
39 | }
40 |
41 | export enum CollisionType {
42 | none = 0,
43 | horizontal = 1,
44 | vertical = 2,
45 | }
46 |
47 | export enum DeathMode {
48 | normal = 0,
49 | shell = 1,
50 | }
51 |
52 | export enum MushroomMode {
53 | mushroom = 0,
54 | plant = 1,
55 | }
56 |
57 | export const backgrounds = [
58 | background1,
59 | background2,
60 | background3,
61 | background4,
62 | background5,
63 | background6,
64 | background7,
65 | background8,
66 | ];
67 |
68 | export const images = {
69 | enemies,
70 | sprites,
71 | objects,
72 | peach,
73 | };
74 |
75 | export const setup = {
76 | interval: 20,
77 | bounce: 15,
78 | cooldown: 20,
79 | gravity: 2,
80 | start_lives: 3,
81 | max_width: 400,
82 | max_height: 15,
83 | jumping_v: 27,
84 | walking_v: 5,
85 | mushroom_v: 3,
86 | ballmonster_v: 2,
87 | spiked_turtle_v: 1.5,
88 | small_turtle_v: 3,
89 | big_turtle_v: 2,
90 | shell_v: 10,
91 | shell_wait: 25,
92 | star_vx: 4,
93 | star_vy: 16,
94 | bullet_v: 12,
95 | max_coins: 100,
96 | pipeplant_count: 150,
97 | pipeplant_v: 1,
98 | invincible: 11000,
99 | invulnerable: 1000,
100 | blinkfactor: 5,
101 | };
102 |
--------------------------------------------------------------------------------
/src/assets.ts:
--------------------------------------------------------------------------------
1 | import { PipePlant, StaticPlant, GreenTurtle, SpikedTurtle, TurtleShell, Gumpa, Mario } from './figures';
2 | import { Coin, CoinBox, MultipleCoinBox, StarBox, MushroomBox } from './items';
3 | import {
4 | RightPipeGrass,
5 | LeftPipeGrass,
6 | RightPipeSoil,
7 | LeftPipeSoil,
8 | LeftPlantedSoil,
9 | MiddlePlantedSoil,
10 | RightPlantedSoil,
11 | TopRightGrassSoil,
12 | TopLeftGrassSoil,
13 | RightBush,
14 | RightMiddleBush,
15 | MiddleBush,
16 | LeftMiddleBush,
17 | LeftBush,
18 | Soil,
19 | RightSoil,
20 | LeftSoil,
21 | TopGrass,
22 | TopRightGrass,
23 | TopLeftGrass,
24 | RightGrass,
25 | LeftGrass,
26 | TopRightRoundedGrass,
27 | TopLeftRoundedGrass,
28 | Stone,
29 | BrownBlock,
30 | RightTopPipe,
31 | LeftTopPipe,
32 | RightPipe,
33 | LeftPipe,
34 | TopRightCornerGrass,
35 | TopLeftCornerGrass,
36 | } from './matter';
37 |
38 | export const assets = {
39 | pipeplant: PipePlant,
40 | staticplant: StaticPlant,
41 | greenturtle: GreenTurtle,
42 | spikedturtle: SpikedTurtle,
43 | shell: TurtleShell,
44 | ballmonster: Gumpa,
45 | mario: Mario,
46 | pipe_right_grass: RightPipeGrass,
47 | pipe_left_grass: LeftPipeGrass,
48 | pipe_right_soil: RightPipeSoil,
49 | pipe_left_soil: LeftPipeSoil,
50 | planted_soil_left: LeftPlantedSoil,
51 | planted_soil_middle: MiddlePlantedSoil,
52 | planted_soil_right: RightPlantedSoil,
53 | grass_top_right_rounded_soil: TopRightGrassSoil,
54 | grass_top_left_rounded_soil: TopLeftGrassSoil,
55 | bush_right: RightBush,
56 | bush_middle_right: RightMiddleBush,
57 | bush_middle: MiddleBush,
58 | bush_middle_left: LeftMiddleBush,
59 | bush_left: LeftBush,
60 | soil: Soil,
61 | soil_right: RightSoil,
62 | soil_left: LeftSoil,
63 | grass_top: TopGrass,
64 | grass_top_right: TopRightGrass,
65 | grass_top_left: TopLeftGrass,
66 | grass_right: RightGrass,
67 | grass_left: LeftGrass,
68 | grass_top_right_rounded: TopRightRoundedGrass,
69 | grass_top_left_rounded: TopLeftRoundedGrass,
70 | stone: Stone,
71 | brown_block: BrownBlock,
72 | pipe_top_right: RightTopPipe,
73 | pipe_top_left: LeftTopPipe,
74 | pipe_right: RightPipe,
75 | pipe_left: LeftPipe,
76 | grass_top_right_corner: TopRightCornerGrass,
77 | grass_top_left_corner: TopLeftCornerGrass,
78 | coin: Coin,
79 | coinbox: CoinBox,
80 | multiple_coinbox: MultipleCoinBox,
81 | starbox: StarBox,
82 | mushroombox: MushroomBox,
83 | };
84 |
--------------------------------------------------------------------------------
/src/figures/Enemy.ts:
--------------------------------------------------------------------------------
1 | import { Figure } from './Figure';
2 | import { DeathAnimation } from '../types';
3 | import { DeathMode, Direction, GroundBlocking, setup } from '../engine/constants';
4 | import { Level } from '../engine/Level';
5 | import { setStyle } from '../utils';
6 |
7 | export class Enemy extends Figure implements DeathAnimation {
8 | speed: number;
9 | invisible: boolean;
10 | shell: boolean;
11 | deathMode: DeathMode;
12 | deathStep: number;
13 | deathCount: number;
14 | deathDir: number;
15 | deathFrames: number;
16 | deathStepUp: number;
17 | deathStepDown: number;
18 |
19 | constructor(level: Level) {
20 | super(level);
21 | this.speed = 0;
22 | this.shell = false;
23 | this.deathMode = DeathMode.normal;
24 | this.deathCount = 0;
25 | }
26 |
27 | hide() {
28 | this.invisible = true;
29 | setStyle(this.view, {
30 | display: 'none',
31 | });
32 | }
33 |
34 | show() {
35 | this.invisible = false;
36 | setStyle(this.view, {
37 | display: 'block',
38 | });
39 | }
40 |
41 | move() {
42 | if (!this.invisible) {
43 | super.move();
44 |
45 | if (this.vx === 0) {
46 | const s = this.speed * Math.sign(this.speed);
47 | this.setVelocity(this.direction === Direction.right ? -s : s, this.vy);
48 | }
49 | }
50 | }
51 |
52 | collides(is: number, ie: number, js: number, je: number, blocking: GroundBlocking) {
53 | if (this.j + 1 < this.level.getGridHeight()) {
54 | for (let i = is; i <= ie; i++) {
55 | if (i < 0 || i >= this.level.getGridWidth()) {
56 | return true;
57 | }
58 |
59 | const obj = this.level.obstacles[i][this.j + 1];
60 |
61 | if (!obj || (obj.blocking & GroundBlocking.top) !== GroundBlocking.top) {
62 | return true;
63 | }
64 | }
65 | }
66 |
67 | return super.collides(is, ie, js, je, blocking);
68 | }
69 |
70 | setSpeed(v: number) {
71 | this.speed = v;
72 | this.setVelocity(-v, 0);
73 | }
74 |
75 | hurt(from: Figure) {
76 | if (from instanceof Enemy && from.shell) {
77 | this.deathMode = DeathMode.shell;
78 | }
79 |
80 | this.die();
81 | }
82 |
83 | hit(opponent: Figure) {
84 | if (!this.invisible && opponent.player) {
85 | if (opponent.vy < 0 && opponent.y - opponent.vy >= this.y + this.state * 32) {
86 | opponent.setVelocity(opponent.vx, setup.bounce);
87 | this.hurt(opponent);
88 | } else {
89 | opponent.hurt(this);
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/engine/Base.ts:
--------------------------------------------------------------------------------
1 | import { Point, Size, Picture } from '../types';
2 | import { setup } from './constants';
3 | import { setStyle } from '../utils';
4 |
5 | export class Base implements Point, Size {
6 | frameCount: number;
7 | x: number;
8 | y: number;
9 | image: Picture;
10 | width: number;
11 | height: number;
12 | currentFrame: number;
13 | frameID?: string;
14 | rewindFrames: boolean;
15 | frameTick: number;
16 | frames: number;
17 | view: HTMLElement;
18 |
19 | constructor() {
20 | this.frameCount = 0;
21 | }
22 |
23 | init(x: number = 0, y: number = 0) {
24 | this.clearFrames();
25 | this.setPosition(x, y);
26 | }
27 |
28 | setPosition(x: number, y: number) {
29 | this.x = x;
30 | this.y = y;
31 | }
32 |
33 | getPosition(): Point {
34 | return {
35 | x: this.x,
36 | y: this.y,
37 | };
38 | }
39 |
40 | setImage(img: string, x: number, y: number) {
41 | this.image = {
42 | path: img,
43 | x: x,
44 | y: y,
45 | };
46 | }
47 |
48 | setSize(width: number, height: number) {
49 | this.width = width;
50 | this.height = height;
51 | }
52 |
53 | getSize(): Size {
54 | return {
55 | width: this.width,
56 | height: this.height,
57 | };
58 | }
59 |
60 | setupFrames(fps: number, frames: number, rewind: boolean, id?: string) {
61 | if (id) {
62 | if (this.frameID === id) {
63 | return true;
64 | }
65 |
66 | this.frameID = id;
67 | }
68 |
69 | this.currentFrame = 0;
70 | this.frameTick = frames ? 1000 / fps / setup.interval : 0;
71 | this.frames = frames;
72 | this.rewindFrames = rewind;
73 | return false;
74 | }
75 |
76 | clearFrames() {
77 | this.frameID = undefined;
78 | this.frames = 0;
79 | this.currentFrame = 0;
80 | this.frameTick = 0;
81 | }
82 |
83 | playFrame() {
84 | if (this.frameTick && this.view) {
85 | this.frameCount++;
86 |
87 | if (this.frameCount >= this.frameTick) {
88 | this.frameCount = 0;
89 |
90 | if (this.currentFrame === this.frames) {
91 | this.currentFrame = 0;
92 | }
93 |
94 | const x = this.image.x + this.width * ((this.rewindFrames ? this.frames - 1 : 0) - this.currentFrame);
95 | const y = this.image.y;
96 | setStyle(this.view, {
97 | backgroundPosition: `-${x}px -${y}px`,
98 | })
99 | this.currentFrame++;
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/figures/PipePlant.ts:
--------------------------------------------------------------------------------
1 | import { Plant } from './Plant';
2 | import { images, setup, Direction } from '../engine/constants';
3 | import { Level } from '../engine/Level';
4 | import { setStyle, shiftBy } from '../utils';
5 |
6 | export class PipePlant extends Plant {
7 | deathFramesExtended: number;
8 | deathFramesExtendedActive: boolean;
9 | minimum: number;
10 | bottom: number;
11 | top: number;
12 |
13 | constructor(level: Level) {
14 | super(level);
15 | this.deathFrames = Math.floor(250 / setup.interval);
16 | this.deathFramesExtended = 6;
17 | this.deathFramesExtendedActive = false;
18 | this.deathStep = Math.ceil(100 / this.deathFrames);
19 | this.deathDir = 1;
20 | this.deathCount = 0;
21 | }
22 |
23 | init(x: number, y: number) {
24 | super.init(x + 16, y - 6);
25 | this.bottom = y - 48;
26 | this.top = y - 6;
27 | this.setDirection(Direction.down);
28 | this.setImage(images.enemies, 0, 56);
29 | setStyle(this.view, {
30 | zIndex: '95',
31 | });
32 | }
33 |
34 | setDirection(dir: Direction) {
35 | this.direction = dir;
36 | }
37 |
38 | setPosition(x: number, y: number) {
39 | if (y === this.bottom || y === this.top) {
40 | this.minimum = setup.pipeplant_count;
41 | this.setDirection(this.direction === Direction.up ? Direction.down : Direction.up);
42 | }
43 |
44 | super.setPosition(x, y);
45 | }
46 |
47 | blocked() {
48 | if (this.y === this.bottom) {
49 | let state = false;
50 | this.y += 48;
51 |
52 | for (let i = this.level.figures.length; i--; ) {
53 | if (this.level.figures[i] != this && this.q2q(this.level.figures[i])) {
54 | state = true;
55 | break;
56 | }
57 | }
58 |
59 | this.y -= 48;
60 | return state;
61 | }
62 |
63 | return false;
64 | }
65 |
66 | move() {
67 | if (this.minimum === 0) {
68 | if (!this.blocked()) {
69 | this.setPosition(this.x, this.y - (this.direction - 3) * setup.pipeplant_v);
70 | }
71 | } else {
72 | this.minimum--;
73 | }
74 | }
75 |
76 | die() {
77 | super.die();
78 | this.setImage(images.enemies, 68, 56);
79 | }
80 |
81 | death() {
82 | if (this.deathFramesExtendedActive) {
83 | this.setPosition(this.x, this.y - 8);
84 | return !!--this.deathFramesExtended;
85 | }
86 |
87 | shiftBy(this.view, 'bottom', this.deathDir, this.deathStep);
88 | this.deathCount += this.deathDir;
89 |
90 | if (this.deathCount === this.deathFrames) {
91 | this.deathDir = -1;
92 | } else if (this.deathCount === 0) {
93 | this.deathFramesExtendedActive = true;
94 | }
95 |
96 | return true;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/figures/GreenTurtle.ts:
--------------------------------------------------------------------------------
1 | import { Turtle } from './Turtle';
2 | import { Figure } from './Figure';
3 | import { Level } from '../engine/Level';
4 | import { Point } from '../types';
5 | import { DeathMode, SizeState, setup, Direction, images } from '../engine/constants';
6 | import { TurtleShell } from './TurtleShell';
7 | import { shiftBy } from '../utils';
8 |
9 | export class GreenTurtle extends Turtle {
10 | wait: number;
11 | walkSprites: Array>;
12 |
13 | constructor(level: Level) {
14 | super(level);
15 | this.shellHost = true;
16 | this.walkSprites = [[{ x: 34, y: 382 }, { x: 0, y: 437 }], [{ x: 34, y: 266 }, { x: 0, y: 325 }]];
17 | this.wait = 0;
18 | this.deathMode = DeathMode.normal;
19 | this.deathFrames = Math.floor(250 / setup.interval);
20 | this.deathStepUp = Math.ceil(150 / this.deathFrames);
21 | this.deathStepDown = Math.ceil(182 / this.deathFrames);
22 | this.deathDir = 1;
23 | this.deathCount = 0;
24 | }
25 |
26 | init(x: number, y: number) {
27 | super.init(x, y);
28 | const shell = new TurtleShell(this.level);
29 | this.setSize(34, 54);
30 | this.setShell(shell);
31 | shell.init(x, y);
32 | }
33 |
34 | setShell(shell: TurtleShell) {
35 | if (!this.house && !this.wait) {
36 | this.house = shell;
37 | shell.hide();
38 | this.setState(SizeState.big);
39 | return true;
40 | }
41 |
42 | return false;
43 | }
44 |
45 | setState(state: SizeState) {
46 | super.setState(state);
47 |
48 | if (state === SizeState.big) {
49 | this.setSpeed(setup.big_turtle_v);
50 | } else {
51 | this.setSpeed(setup.small_turtle_v);
52 | }
53 | }
54 |
55 | setVelocity(vx: number, vy: number) {
56 | super.setVelocity(vx, vy);
57 | const rewind = this.direction === Direction.right;
58 | const coords = this.walkSprites[this.state - 1][rewind ? 1 : 0];
59 | const label = Math.sign(vx) + '-' + this.state;
60 |
61 | if (!this.setupFrames(6, 2, rewind, label)) {
62 | this.setImage(images.enemies, coords.x, coords.y);
63 | }
64 | }
65 |
66 | die() {
67 | super.die();
68 | this.clearFrames();
69 |
70 | if (this.deathMode === DeathMode.normal) {
71 | this.deathFrames = Math.floor(600 / setup.interval);
72 | this.setImage(images.enemies, 102, 437);
73 | } else if (this.deathMode === DeathMode.shell) {
74 | this.level.playSound('shell');
75 | this.setImage(
76 | images.enemies,
77 | 68,
78 | this.state === SizeState.small ? (this.direction === Direction.right ? 437 : 382) : 325,
79 | );
80 | }
81 | }
82 |
83 | death() {
84 | if (this.deathMode === DeathMode.normal) {
85 | return !!--this.deathFrames;
86 | }
87 |
88 | shiftBy(this.view, 'bottom', this.deathDir, this.deathDir > 0 ? this.deathStepUp : this.deathStepDown);
89 | this.deathCount += this.deathDir;
90 |
91 | if (this.deathCount === this.deathFrames) {
92 | this.deathDir = -1;
93 | } else if (this.deathCount === 0) {
94 | return false;
95 | }
96 |
97 | return true;
98 | }
99 |
100 | move() {
101 | if (this.wait) {
102 | this.wait--;
103 | }
104 |
105 | super.move();
106 | }
107 |
108 | hurt(_opponent: Figure) {
109 | this.level.playSound('enemy_die');
110 |
111 | if (this.state !== SizeState.small) {
112 | this.wait = setup.shell_wait;
113 | this.setState(SizeState.small);
114 | this.house && this.house.activate(this.x, this.y);
115 | this.house = undefined;
116 | }
117 |
118 | return this.die();
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/audio.ts:
--------------------------------------------------------------------------------
1 | import { effects } from './effects.codegen';
2 | import { SoundManager } from './types';
3 |
4 | function getPath(ext: string, name: string): string | undefined {
5 | const relevant = effects[ext];
6 | const effect = relevant && relevant[name]
7 |
8 | if (!effect) {
9 | console.error(`Music effect not found, ${name} (in ${ext})!`);
10 | }
11 |
12 | return effect;
13 | }
14 |
15 | export interface MusicSettings {
16 | musicOn: boolean;
17 | }
18 |
19 | export class HtmlAudioManager implements SoundManager {
20 | soundNames: Array;
21 | musicNames: Array;
22 | musicLoops: Array;
23 | support: boolean;
24 | sounds: Array>;
25 | tracks: Array;
26 | settings: MusicSettings;
27 | previous: HTMLAudioElement | null;
28 | currentMusic: HTMLAudioElement | null;
29 | sides: number;
30 | onload?(): void;
31 |
32 | constructor(settings = { musicOn: true }, callback?: () => void) {
33 | let n = 0;
34 | const test = document.createElement('audio');
35 | this.support =
36 | typeof test.canPlayType === 'function' &&
37 | (test.canPlayType('audio/mpeg') !== '' || test.canPlayType('audio/ogg') !== '');
38 | this.onload = callback;
39 | this.soundNames = ['jump', 'coin', 'enemy_die', 'grow', 'hurt', 'mushroom', 'shell', 'shoot', 'lifeupgrade'];
40 | this.musicNames = ['game', 'invincibility', 'die', 'success', 'gameover', 'peach', 'ending', 'menu', 'editor'];
41 | this.musicLoops = [true, false, false, false, false, true, false, true, true];
42 | this.sounds = [];
43 | this.tracks = [];
44 | this.settings = settings;
45 | this.currentMusic = null;
46 | this.sides = 0;
47 |
48 | if (this.support) {
49 | let toLoad = 0;
50 | const ext = test.canPlayType('audio/ogg').match(/maybe|probably/i) ? '.ogg' : '.mp3';
51 | const start = () => {
52 | if (n++ < 25 && toLoad > 0) {
53 | setTimeout(start, 100);
54 | } else {
55 | this.loaded();
56 | }
57 | };
58 |
59 | this.soundNames.forEach(soundName => {
60 | ++toLoad;
61 | const t = document.createElement('audio');
62 | t.addEventListener('error', () => --toLoad, false);
63 | t.addEventListener('loadeddata', () => --toLoad, false);
64 | t.src = getPath(ext, soundName) || '';
65 | t.preload = 'auto';
66 | this.sounds.push([t]);
67 | });
68 |
69 | this.musicNames.forEach((musicName, index) => {
70 | ++toLoad;
71 | const t = document.createElement('audio');
72 | t.addEventListener('error', () => --toLoad, false);
73 | t.addEventListener('loadeddata', () => --toLoad, false);
74 | t.src = getPath(ext, musicName) || '';
75 |
76 | if (this.musicLoops[index]) {
77 | if (typeof t.loop !== 'boolean') {
78 | t.addEventListener(
79 | 'ended',
80 | function() {
81 | this.currentTime = 0;
82 | this.play();
83 | },
84 | false,
85 | );
86 | } else {
87 | t.loop = true;
88 | }
89 | } else {
90 | t.addEventListener('ended', () => this.sideMusicEnded(), false);
91 | }
92 |
93 | t.preload = 'auto';
94 | this.tracks.push(t);
95 | });
96 |
97 | if (callback !== undefined) {
98 | start();
99 | }
100 | } else {
101 | this.loaded();
102 | }
103 | }
104 |
105 | loaded() {
106 | if (this.onload) {
107 | setTimeout(this.onload, 10);
108 | }
109 | }
110 |
111 | play(name: string) {
112 | if (this.settings && this.settings.musicOn && this.support) {
113 | for (let i = this.soundNames.length; i--; ) {
114 | if (this.soundNames[i] === name) {
115 | const t = this.sounds[i];
116 |
117 | for (let j = t.length; j--; ) {
118 | if (t[j].duration === 0) {
119 | return;
120 | }
121 |
122 | if (t[j].ended) {
123 | t[j].currentTime = 0;
124 | } else if (t[j].currentTime <= 0) {
125 | t[j].play();
126 | return;
127 | }
128 | }
129 |
130 | const s = document.createElement('audio');
131 | s.src = t[0].src;
132 | t.push(s);
133 | s.play();
134 | return;
135 | }
136 | }
137 | }
138 | }
139 |
140 | pauseMusic() {
141 | if (this.support && this.currentMusic) {
142 | this.currentMusic.pause();
143 | }
144 | }
145 |
146 | playMusic() {
147 | if (this.support && this.currentMusic && this.settings.musicOn) {
148 | this.currentMusic.play();
149 | }
150 | }
151 |
152 | sideMusicEnded() {
153 | this.sides--;
154 |
155 | if (this.sides === 0) {
156 | this.currentMusic = this.previous;
157 | this.playMusic();
158 | }
159 | }
160 |
161 | sideMusic(id: string) {
162 | if (this.support) {
163 | if (this.sides === 0) {
164 | this.previous = this.currentMusic;
165 | this.pauseMusic();
166 | }
167 |
168 | for (let i = this.musicNames.length; i--; ) {
169 | if (this.musicNames[i] === id) {
170 | if (this.currentMusic !== this.tracks[i]) {
171 | this.sides++;
172 | this.currentMusic = this.tracks[i];
173 | }
174 |
175 | try {
176 | this.currentMusic.currentTime = 0;
177 | this.playMusic();
178 | } catch (e) {
179 | this.sideMusicEnded();
180 | }
181 | }
182 | }
183 | }
184 | }
185 |
186 | music(id: string, noRewind: boolean) {
187 | if (this.support) {
188 | for (let i = this.musicNames.length; i--; ) {
189 | if (this.musicNames[i] === id) {
190 | const music = this.tracks[i];
191 |
192 | if (music === this.currentMusic) {
193 | return;
194 | }
195 |
196 | this.pauseMusic();
197 | this.currentMusic = music;
198 |
199 | if (this.support) {
200 | try {
201 | if (!noRewind) {
202 | this.currentMusic.currentTime = 0;
203 | }
204 |
205 | this.playMusic();
206 | } catch (e) {}
207 | }
208 | }
209 | }
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/figures/Figure.ts:
--------------------------------------------------------------------------------
1 | import { Base } from '../engine/Base';
2 | import { GridPoint, Settings, StateItem } from '../types';
3 | import { Item } from '../items/Item';
4 | import { SizeState, Direction, GroundBlocking, setup } from '../engine/constants';
5 | import { toUrl, setStyle, createBox } from '../utils';
6 | import { Level } from '../engine/Level';
7 |
8 | export class Figure extends Base implements GridPoint, StateItem {
9 | player: boolean;
10 | shellHost: boolean;
11 | dx: number;
12 | dy: number;
13 | onground: boolean;
14 | dead: boolean;
15 | vx: number;
16 | vy: number;
17 | level: Level;
18 | state: SizeState;
19 | direction: Direction;
20 | i: number;
21 | j: number;
22 | cw: number;
23 | ch: number;
24 |
25 | constructor(level: Level) {
26 | super();
27 | this.view = createBox(level.world, 'figure');
28 | this.player = false;
29 | this.shellHost = false;
30 | this.dx = 0;
31 | this.dy = 0;
32 | this.dead = false;
33 | this.onground = true;
34 | this.level = level;
35 | }
36 |
37 | init(x: number, y: number) {
38 | super.init(x, y);
39 | this.setState(SizeState.small);
40 | this.setVelocity(0, 0);
41 | this.direction = Direction.none;
42 | this.level.figures.push(this);
43 | }
44 |
45 | q2q(opponent: StateItem) {
46 | if (this.x > opponent.x + opponent.cw) {
47 | return false;
48 | } else if (this.x < opponent.x - this.cw) {
49 | return false;
50 | } else if (this.y - 4 < opponent.y - this.ch) {
51 | return false;
52 | } else if (this.y + 4 > opponent.y + opponent.ch) {
53 | return false;
54 | }
55 |
56 | return true;
57 | }
58 |
59 | setState(state: SizeState) {
60 | this.state = state;
61 | this.cw = 32;
62 | this.ch = this.state * 32;
63 | }
64 |
65 | hurt(_from: Figure) {}
66 |
67 | store(_settings: Partial) {}
68 |
69 | restore(_settings: Settings) {}
70 |
71 | setImage(img: string, x = 0, y = 0) {
72 | setStyle(this.view, {
73 | backgroundImage: img ? toUrl(img) : 'none',
74 | backgroundPosition: `-${x}px -${y}px`,
75 | });
76 | super.setImage(img, x, y);
77 | }
78 |
79 | setOffset(dx: number, dy: number) {
80 | this.dx = dx;
81 | this.dy = dy;
82 | this.setPosition(this.x, this.y);
83 | }
84 |
85 | setPosition(x: number, y: number) {
86 | setStyle(this.view, {
87 | left: `${x}px`,
88 | bottom: `${y}px`,
89 | marginLeft: `${this.dx}px`,
90 | marginBottom: `${this.dy}px`,
91 | });
92 | super.setPosition(x, y);
93 | this.setGridPosition(x, y);
94 | }
95 |
96 | setSize(width: number, height: number) {
97 | setStyle(this.view, {
98 | width: `${width}px`,
99 | height: `${height}px`,
100 | });
101 | super.setSize(width, height);
102 | }
103 |
104 | setGridPosition(x: number, y: number) {
105 | this.i = Math.floor((x + 16) / 32);
106 | this.j = Math.ceil(this.level.getGridHeight() - 1 - y / 32);
107 |
108 | if (this.j > this.level.getGridHeight()) {
109 | this.die();
110 | }
111 | }
112 |
113 | getGridPosition(): GridPoint {
114 | return {
115 | i: this.i,
116 | j: this.j,
117 | };
118 | }
119 |
120 | setVelocity(vx: number, vy: number) {
121 | this.vx = vx;
122 | this.vy = vy;
123 |
124 | if (vx > 0) {
125 | this.direction = Direction.right;
126 | } else if (vx < 0) {
127 | this.direction = Direction.left;
128 | }
129 | }
130 |
131 | getVelocity() {
132 | return {
133 | vx: this.vx,
134 | vy: this.vy,
135 | };
136 | }
137 |
138 | hit(_opponent: Figure) {}
139 |
140 | trigger(_obj: Item) {}
141 |
142 | collides(is: number, ie: number, js: number, je: number, blocking: GroundBlocking) {
143 | if (is < 0 || ie >= this.level.obstacles.length) {
144 | return true;
145 | } else if (js < 0 || je >= this.level.getGridHeight()) {
146 | return false;
147 | }
148 |
149 | for (let i = is; i <= ie; i++) {
150 | for (let j = je; j >= js; j--) {
151 | const obj = this.level.obstacles[i][j];
152 |
153 | if (obj) {
154 | if (obj instanceof Item && (blocking === GroundBlocking.bottom || obj.blocking === GroundBlocking.none)) {
155 | this.trigger(- obj);
156 | }
157 |
158 | if ((obj.blocking & blocking) === blocking) {
159 | return true;
160 | }
161 | }
162 | }
163 | }
164 |
165 | return false;
166 | }
167 |
168 | move() {
169 | let vx = this.vx;
170 | let vy = this.vy - setup.gravity;
171 |
172 | const s = this.state;
173 |
174 | let x = this.x;
175 | let y = this.y;
176 |
177 | const dx = Math.sign(vx);
178 | const dy = Math.sign(vy);
179 |
180 | let is = this.i;
181 | let ie = is;
182 |
183 | const js = Math.ceil(this.level.getGridHeight() - s - (y + 31) / 32);
184 | const je = this.j;
185 |
186 | let d = 0;
187 | let b = GroundBlocking.none;
188 | let onground = false;
189 | let t = Math.floor((x + 16 + vx) / 32);
190 |
191 | if (dx > 0) {
192 | d = t - ie;
193 | t = ie;
194 | b = GroundBlocking.left;
195 | } else if (dx < 0) {
196 | d = is - t;
197 | t = is;
198 | b = GroundBlocking.right;
199 | }
200 |
201 | x += vx;
202 |
203 | for (let i = 0; i < d; i++) {
204 | if (this.collides(t + dx, t + dx, js, je, b)) {
205 | vx = 0;
206 | x = t * 32 + 15 * dx;
207 | break;
208 | }
209 |
210 | t += dx;
211 | is += dx;
212 | ie += dx;
213 | }
214 |
215 | if (dy > 0) {
216 | t = Math.ceil(this.level.getGridHeight() - s - (y + 31 + vy) / 32);
217 | d = js - t;
218 | t = js;
219 | b = GroundBlocking.bottom;
220 | } else if (dy < 0) {
221 | t = Math.ceil(this.level.getGridHeight() - 1 - (y + vy) / 32);
222 | d = t - je;
223 | t = je;
224 | b = GroundBlocking.top;
225 | } else {
226 | d = 0;
227 | }
228 |
229 | y += vy;
230 |
231 | for (let i = 0; i < d; i++) {
232 | if (this.collides(is, ie, t - dy, t - dy, b)) {
233 | onground = dy < 0;
234 | vy = 0;
235 | y = this.level.height - (t + 1) * 32 - (dy > 0 ? (s - 1) * 32 : 0);
236 | break;
237 | }
238 |
239 | t -= dy;
240 | }
241 |
242 | this.onground = onground;
243 | this.setVelocity(vx, vy);
244 | this.setPosition(x, y);
245 | }
246 |
247 | death() {
248 | return false;
249 | }
250 |
251 | die() {
252 | this.dead = true;
253 | }
254 |
255 | bounce(_dx: number, _dy: number) {
256 | this.die();
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/src/engine/Level.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './Base';
2 | import { Keys, SoundManager, LevelFormat, Settings, StateItem } from '../types';
3 | import { Matter } from '../matter/Matter';
4 | import { Item } from '../items/Item';
5 | import { setup, MarioState, SizeState, backgrounds } from './constants';
6 | import { Gauge } from './Gauge';
7 | import { toUrl, setStyle } from '../utils';
8 |
9 | export interface Assets {
10 | [asset: string]: {
11 | new(level: Level): {
12 | init(x: number, y: number): void;
13 | };
14 | };
15 | }
16 |
17 | function createChild(host: Element, ...classes: Array) {
18 | host.classList.add('game');
19 | const child = host.appendChild(document.createElement('div'));
20 |
21 | for (const cls of classes) {
22 | child.classList.add(cls);
23 | }
24 |
25 | return child;
26 | }
27 |
28 | export class Level extends Base {
29 | private liveGauge: Gauge;
30 | private coinGauge: Gauge;
31 | private active: boolean;
32 | private nextCycles: number;
33 | private loop: number | void;
34 | private sounds: SoundManager;
35 | private currentLevel: LevelFormat;
36 | private allLevels: Array;
37 | private assets: Assets;
38 | world: HTMLDivElement;
39 | figures: Array;
40 | obstacles: Array>;
41 | decorations: Array;
42 | items: Array
- ;
43 | controls: Keys;
44 |
45 | constructor(host: Element, controls: Keys, levels: Array, assets: Assets) {
46 | super();
47 | const world = createChild(host, 'world');
48 | createChild(host, 'coinNumber', 'gauge')
49 | const coins = createChild(host, 'coin', 'gaugeSprite');
50 | createChild(host, 'liveNumber', 'gauge')
51 | const lives = createChild(host, 'live', 'gaugeSprite');
52 | this.world = world;
53 | this.assets = assets;
54 | this.controls = controls;
55 | this.allLevels = levels;
56 | this.nextCycles = 0;
57 | this.active = false;
58 | this.figures = [];
59 | this.obstacles = [];
60 | this.decorations = [];
61 | this.items = [];
62 | this.coinGauge = new Gauge(coins, 0, 0, 10, 4, true);
63 | this.liveGauge = new Gauge(lives, 0, 430, 6, 6, true);
64 | }
65 |
66 | init() {
67 | super.init(0, 0);
68 | }
69 |
70 | private defaultSettings(): Settings {
71 | return {
72 | lifes: 0,
73 | coins: 0,
74 | state: SizeState.small,
75 | marioState: MarioState.normal,
76 | musicOn: true,
77 | };
78 | }
79 |
80 | reload() {
81 | const settings = this.defaultSettings();
82 | this.pause();
83 |
84 | for (let i = this.figures.length; i--; ) {
85 | this.figures[i].store(settings);
86 | }
87 |
88 | settings.lifes--;
89 | this.reset();
90 |
91 | if (settings.lifes < 0) {
92 | this.load(this.firstLevel());
93 | } else {
94 | this.load(this.currentLevel);
95 |
96 | for (let i = this.figures.length; i--; ) {
97 | this.figures[i].restore(settings);
98 | }
99 | }
100 |
101 | this.start();
102 | }
103 |
104 | nextLevel() {
105 | if (this.allLevels) {
106 | const index = this.allLevels.indexOf(this.currentLevel);
107 | const next = index + 1;
108 | const level = this.allLevels[next] || this.allLevels[0];
109 |
110 | if (level) {
111 | return level;
112 | }
113 | }
114 |
115 | return this.currentLevel;
116 | }
117 |
118 | firstLevel() {
119 | return this.currentLevel;
120 | }
121 |
122 | load(level: LevelFormat) {
123 | this.init();
124 |
125 | if (this.active) {
126 | if (this.loop) {
127 | this.pause();
128 | }
129 |
130 | this.reset();
131 | }
132 |
133 | this.setSize(level.width * 32, level.height * 32);
134 | this.setBackground(level.background);
135 | this.currentLevel = level;
136 | this.active = true;
137 | const data = level.data;
138 |
139 | for (let i = 0; i < level.width; i++) {
140 | const t: Array = [];
141 |
142 | for (let j = 0; j < level.height; j++) {
143 | t.push(undefined);
144 | }
145 |
146 | this.obstacles.push(t);
147 | }
148 |
149 | for (let i = 0, width = data.length; i < width; i++) {
150 | const col = data[i];
151 |
152 | for (let j = 0, height = col.length; j < height; j++) {
153 | const Asset = this.assets[col[j]];
154 |
155 | if (Asset) {
156 | const item = new Asset(this);
157 | item.init(i * 32, (height - j - 1) * 32);
158 | }
159 | }
160 | }
161 | }
162 |
163 | next() {
164 | this.nextCycles = Math.floor(7000 / setup.interval);
165 | }
166 |
167 | nextLoad() {
168 | if (!this.nextCycles) {
169 | const settings = this.defaultSettings();
170 | this.pause();
171 |
172 | for (let i = this.figures.length; i--; ) {
173 | this.figures[i].store(settings);
174 | }
175 |
176 | this.reset();
177 | this.load(this.nextLevel());
178 |
179 | for (let i = this.figures.length; i--; ) {
180 | this.figures[i].restore(settings);
181 | }
182 |
183 | this.start();
184 | }
185 | }
186 |
187 | getGridWidth() {
188 | return this.currentLevel.width;
189 | }
190 |
191 | getGridHeight() {
192 | return this.currentLevel.height;
193 | }
194 |
195 | setSounds(manager: SoundManager) {
196 | this.sounds = manager;
197 | }
198 |
199 | playSound(label: string) {
200 | if (this.sounds) {
201 | this.sounds.play(label);
202 | }
203 | }
204 |
205 | playMusic(label: string) {
206 | if (this.sounds) {
207 | this.sounds.sideMusic(label);
208 | }
209 | }
210 |
211 | reset() {
212 | this.active = false;
213 | this.world.innerHTML = '';
214 | this.figures = [];
215 | this.obstacles = [];
216 | this.items = [];
217 | this.decorations = [];
218 | }
219 |
220 | tick() {
221 | if (this.nextCycles) {
222 | this.nextCycles--;
223 | this.nextLoad();
224 | return;
225 | }
226 |
227 | for (let i = this.figures.length; i--; ) {
228 | const figure = this.figures[i];
229 |
230 | if (figure.dead) {
231 | if (!figure.death()) {
232 | if (figure.player) {
233 | return this.reload();
234 | }
235 |
236 | figure.view.remove();
237 | this.figures.splice(i, 1);
238 | } else {
239 | figure.playFrame();
240 | }
241 | } else {
242 | if (i) {
243 | for (let j = i; j--; ) {
244 | if (figure.dead) {
245 | break;
246 | }
247 |
248 | const opponent = this.figures[j];
249 |
250 | if (!opponent.dead && figure.q2q(opponent)) {
251 | figure.hit(opponent);
252 | opponent.hit(figure);
253 | }
254 | }
255 | }
256 | }
257 |
258 | if (!figure.dead) {
259 | figure.move();
260 | figure.playFrame();
261 | }
262 | }
263 |
264 | for (let i = this.items.length; i--; ) {
265 | this.items[i].playFrame();
266 | }
267 |
268 | this.coinGauge.playFrame();
269 | this.liveGauge.playFrame();
270 | }
271 |
272 | start() {
273 | this.controls.bind();
274 | this.loop = setInterval(() => {
275 | this.tick();
276 | }, setup.interval);
277 | }
278 |
279 | pause() {
280 | this.controls.unbind();
281 | this.loop = clearInterval(this.loop || 0);
282 | }
283 |
284 | setPosition(x: number, y: number) {
285 | super.setPosition(x, y);
286 | setStyle(this.world, {
287 | left: `-${x}px`,
288 | });
289 | }
290 |
291 | setBackground(index: number) {
292 | const img = backgrounds[index];
293 | setStyle(this.world.parentElement, {
294 | backgroundImage: toUrl(img),
295 | backgroundPosition: '0 -380px',
296 | });
297 | this.setImage(img, 0, 0);
298 | }
299 |
300 | setSize(width: number, height: number) {
301 | super.setSize(width, height);
302 | }
303 |
304 | setParallax(x: number) {
305 | this.setPosition(x, this.y);
306 | const pos = Math.floor(x / 3);
307 | setStyle(this.world.parentElement, {
308 | backgroundPosition: `-${pos}px -380px`,
309 | });
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/figures/Mario.ts:
--------------------------------------------------------------------------------
1 | import { DeathAnimation, Keys, Point, Settings } from '../types';
2 | import { Figure } from './Figure';
3 | import { Bullet } from './Bullet';
4 | import { Item } from '../items/Item';
5 | import { MarioState, setup, Direction, images, SizeState } from '../engine/constants';
6 | import { Level } from '../engine/Level';
7 | import { setStyle, setGauge, shiftBy } from '../utils';
8 |
9 | export class Mario extends Figure implements DeathAnimation {
10 | deadly: number;
11 | cooldown: number;
12 | blinking: number;
13 | fast: boolean;
14 | crouching: boolean;
15 | deathBeginWait: number;
16 | deathEndWait: number;
17 | deathDir: number;
18 | deathCount: number;
19 | deathFrames: number;
20 | deathStepUp: number;
21 | deathStepDown: number;
22 | invulnerable: number;
23 | coins: number;
24 | lifes: number;
25 | marioState: MarioState;
26 | standSprites: Array>>;
27 | crouchSprites: Array>;
28 |
29 | constructor(level: Level) {
30 | super(level);
31 | this.standSprites = [
32 | [[{ x: 0, y: 81 }, { x: 481, y: 83 }], [{ x: 81, y: 0 }, { x: 561, y: 83 }]],
33 | [[{ x: 0, y: 162 }, { x: 481, y: 247 }], [{ x: 81, y: 243 }, { x: 561, y: 247 }]],
34 | ];
35 | this.crouchSprites = [[{ x: 241, y: 0 }, { x: 161, y: 0 }], [{ x: 241, y: 162 }, { x: 241, y: 243 }]];
36 | this.deadly = 0;
37 | this.invulnerable = 0;
38 | this.width = 80;
39 | this.blinking = 0;
40 | this.setOffset(-24, 0);
41 | this.cooldown = 0;
42 | this.player = true;
43 | this.deathCount = 0;
44 | this.deathBeginWait = Math.floor(700 / setup.interval);
45 | this.deathEndWait = 0;
46 | this.deathFrames = Math.floor(600 / setup.interval);
47 | this.deathStepUp = Math.ceil(200 / this.deathFrames);
48 | this.deathDir = 1;
49 | this.direction = Direction.right;
50 | this.crouching = false;
51 | this.fast = false;
52 | }
53 |
54 | init(x: number, y: number) {
55 | super.init(x, y);
56 | this.setSize(80, 80);
57 | this.setMarioState(MarioState.normal);
58 | this.setLifes(setup.start_lives);
59 | this.setCoins(0);
60 | this.setImage(images.sprites, 81, 0);
61 | }
62 |
63 | setMarioState(state: MarioState) {
64 | this.marioState = state;
65 | }
66 |
67 | store(settings: Partial) {
68 | settings.lifes = this.lifes;
69 | settings.coins = this.coins;
70 | settings.state = this.state;
71 | settings.marioState = this.marioState;
72 | }
73 |
74 | restore(settings: Settings) {
75 | this.setLifes(settings.lifes);
76 | this.setCoins(settings.coins);
77 | this.setState(settings.state);
78 | this.setMarioState(settings.marioState);
79 | }
80 |
81 | setState(state: SizeState) {
82 | if (state !== this.state) {
83 | this.setMarioState(MarioState.normal);
84 | super.setState(state);
85 | }
86 | }
87 |
88 | setPosition(x: number, y: number) {
89 | super.setPosition(x, y);
90 | const r = this.level.width - 640;
91 | const w = this.x <= 210 ? 0 : this.x >= this.level.width - 230 ? r : r / (this.level.width - 440) * (this.x - 210);
92 | this.level.setParallax(w);
93 |
94 | if (this.onground && this.x >= this.level.width - 128) {
95 | this.victory();
96 | }
97 | }
98 |
99 | trigger(obj: Item) {
100 | obj.activate(this);
101 | }
102 |
103 | input(keys: Keys) {
104 | this.fast = keys.accelerate;
105 | this.crouching = keys.down;
106 |
107 | if (!this.crouching) {
108 | if (this.onground && keys.up) {
109 | this.jump();
110 | }
111 |
112 | if (keys.accelerate && this.marioState === MarioState.fire) {
113 | this.shoot();
114 | }
115 |
116 | if (keys.right || keys.left) {
117 | this.walk(keys.left, keys.accelerate);
118 | } else {
119 | this.vx = 0;
120 | }
121 | }
122 | }
123 |
124 | victory() {
125 | this.level.playMusic('success');
126 | this.clearFrames();
127 | setStyle(this.view, {
128 | display: 'block',
129 | });
130 | this.setImage(images.sprites, this.state === SizeState.small ? 241 : 161, 81);
131 | this.level.next();
132 | }
133 |
134 | shoot() {
135 | if (!this.cooldown) {
136 | this.cooldown = setup.cooldown;
137 | this.level.playSound('shoot');
138 | const bullet = new Bullet(this);
139 | bullet.init(this.x, this.y);
140 | }
141 | }
142 |
143 | setVelocity(vx: number, vy: number) {
144 | if (this.crouching) {
145 | vx = 0;
146 | this.crouch();
147 | } else {
148 | if (this.onground && vx > 0) {
149 | this.walkRight();
150 | } else if (this.onground && vx < 0) {
151 | this.walkLeft();
152 | } else {
153 | this.stand();
154 | }
155 | }
156 |
157 | super.setVelocity(vx, vy);
158 | }
159 |
160 | blink(times: number) {
161 | this.blinking = Math.max(2 * times * setup.blinkfactor, this.blinking);
162 | }
163 |
164 | invincible() {
165 | this.level.playMusic('invincibility');
166 | this.deadly = Math.floor(setup.invincible / setup.interval);
167 | this.invulnerable = this.deadly;
168 | this.blink(Math.ceil(this.deadly / (2 * setup.blinkfactor)));
169 | }
170 |
171 | grow() {
172 | if (this.state === SizeState.small) {
173 | this.level.playSound('grow');
174 | this.setState(SizeState.big);
175 | this.blink(3);
176 | }
177 | }
178 |
179 | shooter() {
180 | if (this.state === SizeState.small) {
181 | this.grow();
182 | } else {
183 | this.level.playSound('grow');
184 | }
185 |
186 | this.setMarioState(MarioState.fire);
187 | }
188 |
189 | walk(reverse: boolean, fast: boolean) {
190 | this.vx = setup.walking_v * (fast ? 2 : 1) * (reverse ? -1 : 1);
191 | }
192 |
193 | walkRight() {
194 | if (this.state === SizeState.small) {
195 | if (!this.setupFrames(8, 2, true, 'WalkRightSmall')) {
196 | this.setImage(images.sprites, 0, 0);
197 | }
198 | } else {
199 | if (!this.setupFrames(9, 2, true, 'WalkRightBig')) {
200 | this.setImage(images.sprites, 0, 243);
201 | }
202 | }
203 | }
204 |
205 | walkLeft() {
206 | if (this.state === SizeState.small) {
207 | if (!this.setupFrames(8, 2, false, 'WalkLeftSmall')) {
208 | this.setImage(images.sprites, 80, 81);
209 | }
210 | } else {
211 | if (!this.setupFrames(9, 2, false, 'WalkLeftBig')) {
212 | this.setImage(images.sprites, 81, 162);
213 | }
214 | }
215 | }
216 |
217 | stand() {
218 | const coords = this.standSprites[this.state - 1][this.direction === Direction.left ? 0 : 1][this.onground ? 0 : 1];
219 | this.setImage(images.sprites, coords.x, coords.y);
220 | this.clearFrames();
221 | }
222 |
223 | crouch() {
224 | const coords = this.crouchSprites[this.state - 1][this.direction === Direction.left ? 0 : 1];
225 | this.setImage(images.sprites, coords.x, coords.y);
226 | this.clearFrames();
227 | }
228 |
229 | jump() {
230 | this.level.playSound('jump');
231 | this.vy = setup.jumping_v;
232 | }
233 |
234 | move() {
235 | this.input(this.level.controls);
236 | super.move();
237 | }
238 |
239 | addCoin() {
240 | this.setCoins(this.coins + 1);
241 | }
242 |
243 | playFrame() {
244 | if (this.blinking) {
245 | if (this.blinking % setup.blinkfactor === 0) {
246 | setStyle(this.view, {
247 | display: this.view.style.display === 'none' ? 'block' : 'none',
248 | });
249 | }
250 |
251 | this.blinking--;
252 | }
253 |
254 | if (this.cooldown) {
255 | this.cooldown--;
256 | }
257 |
258 | if (this.deadly) {
259 | this.deadly--;
260 | }
261 |
262 | if (this.invulnerable) {
263 | this.invulnerable--;
264 | }
265 |
266 | super.playFrame();
267 | }
268 |
269 | setCoins(coins: number) {
270 | this.coins = coins;
271 |
272 | if (this.coins >= setup.max_coins) {
273 | this.addLife();
274 | this.coins -= setup.max_coins;
275 | }
276 |
277 | setGauge(this.level.world, 'coinNumber', `${this.coins}`);
278 | }
279 |
280 | addLife() {
281 | this.level.playSound('liveupgrade');
282 | this.setLifes(this.lifes + 1);
283 | }
284 |
285 | setLifes(lifes: number) {
286 | this.lifes = lifes;
287 | setGauge(this.level.world, 'liveNumber', `${this.lifes}`);
288 | }
289 |
290 | death() {
291 | if (this.deathBeginWait) {
292 | this.deathBeginWait--;
293 | return true;
294 | }
295 |
296 | if (this.deathEndWait) {
297 | return !!--this.deathEndWait;
298 | }
299 |
300 | shiftBy(this.view, 'bottom', this.deathDir, this.deathDir > 0 ? this.deathStepUp : this.deathStepDown);
301 | this.deathCount += this.deathDir;
302 |
303 | if (this.deathCount === this.deathFrames) {
304 | this.deathDir = -1;
305 | } else if (this.deathCount === 0) {
306 | this.deathEndWait = Math.floor(1800 / setup.interval);
307 | }
308 |
309 | return true;
310 | }
311 |
312 | die() {
313 | this.setMarioState(MarioState.normal);
314 | this.deathStepDown = Math.ceil(240 / this.deathFrames);
315 | this.setupFrames(9, 2, false);
316 | this.setImage(images.sprites, 81, 324);
317 | this.level.playMusic('die');
318 | super.die();
319 | }
320 |
321 | hurt(from: Figure) {
322 | if (this.deadly) {
323 | from.die();
324 | } else if (this.invulnerable) {
325 | return;
326 | } else if (this.state === SizeState.small) {
327 | this.die();
328 | } else {
329 | this.invulnerable = Math.floor(setup.invulnerable / setup.interval);
330 | this.blink(Math.ceil(this.invulnerable / (2 * setup.blinkfactor)));
331 | this.setState(SizeState.small);
332 | this.level.playSound('hurt');
333 | }
334 | }
335 | }
336 |
--------------------------------------------------------------------------------
/src/levels.ts:
--------------------------------------------------------------------------------
1 | import { LevelFormat } from './types';
2 |
3 | const standardLevels: Array = [
4 | {
5 | width: 252,
6 | height: 15,
7 | id: 0,
8 | background: 1,
9 | data: [
10 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
11 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
12 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
13 | [
14 | '',
15 | '',
16 | '',
17 | '',
18 | '',
19 | '',
20 | '',
21 | '',
22 | '',
23 | '',
24 | '',
25 | 'pipe_top_right',
26 | 'pipe_right',
27 | 'pipe_right_grass',
28 | 'pipe_right_soil',
29 | ],
30 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
31 | ['', '', '', '', '', '', '', '', '', '', '', '', 'mario', 'grass_top', 'soil'],
32 | ['', '', '', '', '', '', '', '', '', 'multiple_coinbox', '', '', '', 'grass_top', 'soil'],
33 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_left'],
34 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_middle'],
35 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_right'],
36 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
37 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
38 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
39 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
40 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
41 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
42 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_left', 'grass_top', 'soil'],
43 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle', 'grass_top', 'soil'],
44 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle_right', 'grass_top', 'soil'],
45 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_right', 'grass_top', 'soil'],
46 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
47 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
48 | ['', '', '', '', '', '', '', '', '', 'mushroombox', '', '', '', 'grass_top', 'soil'],
49 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
50 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
51 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil'],
52 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
53 | [
54 | '',
55 | '',
56 | '',
57 | '',
58 | '',
59 | '',
60 | '',
61 | '',
62 | '',
63 | '',
64 | '',
65 | 'pipe_top_right',
66 | 'pipe_right',
67 | 'pipe_right_grass',
68 | 'pipe_right_soil',
69 | ],
70 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
71 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
72 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
73 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
74 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
75 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
76 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
77 | ['', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
78 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
79 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
80 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
81 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
82 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
83 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
84 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
85 | [
86 | '',
87 | '',
88 | '',
89 | '',
90 | '',
91 | '',
92 | '',
93 | '',
94 | '',
95 | '',
96 | '',
97 | 'pipe_top_right',
98 | 'pipe_right',
99 | 'pipe_right_grass',
100 | 'pipe_right_soil',
101 | ],
102 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
103 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
104 | ['', '', '', '', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
105 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
106 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
107 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
108 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
109 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
110 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
111 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil'],
112 | [
113 | '',
114 | '',
115 | '',
116 | '',
117 | '',
118 | '',
119 | '',
120 | '',
121 | '',
122 | '',
123 | 'grass_top_left',
124 | 'grass_left',
125 | 'grass_left',
126 | 'grass_top_left_corner',
127 | 'soil',
128 | ],
129 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
130 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
131 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
132 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil', 'planted_soil_left', 'soil', 'soil'],
133 | ['', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil', 'planted_soil_right', 'soil', 'soil'],
134 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
135 | ['', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
136 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
137 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
138 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
139 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
140 | ['', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
141 | [
142 | '',
143 | '',
144 | '',
145 | '',
146 | '',
147 | '',
148 | '',
149 | 'pipe_top_left',
150 | 'pipe_left',
151 | 'pipe_left',
152 | 'pipe_left_grass',
153 | 'pipe_left_soil',
154 | 'pipe_left_soil',
155 | 'pipe_left_soil',
156 | 'pipe_left_soil',
157 | ],
158 | [
159 | '',
160 | '',
161 | '',
162 | '',
163 | '',
164 | '',
165 | '',
166 | 'pipe_top_right',
167 | 'pipe_right',
168 | 'pipe_right',
169 | 'pipe_right',
170 | 'pipe_right',
171 | 'pipe_right',
172 | 'pipe_right_grass',
173 | 'pipe_right_soil',
174 | ],
175 | ['', '', '', '', '', 'brown_block', '', '', '', '', '', '', '', 'grass_top', 'soil'],
176 | ['', '', '', '', '', 'brown_block', '', '', '', '', '', '', '', 'grass_top', 'soil'],
177 | ['', '', '', '', '', 'brown_block', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
178 | ['', '', '', '', '', 'brown_block', '', '', '', '', '', '', '', 'grass_top', 'soil'],
179 | ['', '', '', '', '', 'brown_block', '', '', '', '', '', '', '', 'grass_top', 'soil'],
180 | ['', '', '', '', 'ballmonster', 'brown_block', '', '', '', '', '', '', '', 'grass_top', 'soil'],
181 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
182 | ['', '', '', 'coin', 'coin', 'coin', 'coin', 'coin', 'coin', 'coin', '', '', '', 'grass_top', 'soil'],
183 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
184 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top_right_rounded', 'soil_right'],
185 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
186 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
187 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top_left_rounded', 'soil_left'],
188 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
189 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
190 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
191 | ['', '', '', '', '', 'mushroombox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
192 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
193 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
194 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
195 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
196 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
197 | [
198 | '',
199 | '',
200 | '',
201 | '',
202 | '',
203 | '',
204 | '',
205 | '',
206 | '',
207 | '',
208 | '',
209 | 'pipe_top_right',
210 | 'pipe_right',
211 | 'pipe_right_grass',
212 | 'pipe_right_soil',
213 | ],
214 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
215 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
216 | ['', '', '', '', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
217 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
218 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
219 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
220 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_left', 'grass_top', 'soil'],
221 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle_left', 'grass_top', 'soil'],
222 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle', 'grass_top', 'soil'],
223 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', 'bush_middle_right', 'grass_top', 'soil'],
224 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', 'bush_right', 'grass_top', 'soil'],
225 | ['', '', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
226 | ['', '', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
227 | ['', 'coinbox', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
228 | ['', 'coinbox', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
229 | ['', '', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
230 | ['', '', '', '', '', 'coinbox', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
231 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
232 | ['', '', '', '', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil'],
233 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
234 | ['', '', '', '', '', '', '', '', '', '', '', '', 'greenturtle', 'grass_top', 'soil'],
235 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
236 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top_right_rounded', 'soil_right'],
237 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
238 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
239 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top_left_rounded', 'soil_left'],
240 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
241 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
242 | [
243 | '',
244 | '',
245 | '',
246 | '',
247 | '',
248 | '',
249 | '',
250 | '',
251 | '',
252 | '',
253 | '',
254 | 'pipe_top_right',
255 | 'pipe_right',
256 | 'pipe_right_grass',
257 | 'pipe_right_soil',
258 | ],
259 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
260 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
261 | ['', '', '', '', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
262 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
263 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
264 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
265 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
266 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_left'],
267 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'planted_soil_middle'],
268 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_right'],
269 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
270 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
271 | ['', '', '', '', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
272 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
273 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
274 | ['', '', '', '', '', '', '', '', '', '', '', '', 'greenturtle', 'grass_top', 'soil'],
275 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
276 | [
277 | '',
278 | '',
279 | '',
280 | '',
281 | '',
282 | '',
283 | '',
284 | '',
285 | '',
286 | '',
287 | '',
288 | 'pipe_top_right',
289 | 'pipe_right',
290 | 'pipe_right_grass',
291 | 'pipe_right_soil',
292 | ],
293 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
294 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
295 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
296 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
297 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
298 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil'],
299 | ['', '', '', '', '', '', 'starbox', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
300 | ['', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
301 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
302 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
303 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
304 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
305 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
306 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
307 | [
308 | '',
309 | '',
310 | '',
311 | '',
312 | '',
313 | '',
314 | '',
315 | '',
316 | '',
317 | '',
318 | 'grass_top_left',
319 | 'grass_left',
320 | 'grass_left',
321 | 'grass_top_left_corner',
322 | 'soil',
323 | ],
324 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
325 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
326 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
327 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
328 | ['', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
329 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
330 | ['', '', '', '', '', '', 'brown_block', '', '', '', 'grass_top', 'planted_soil_left', 'soil', 'soil', 'soil'],
331 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'planted_soil_middle', 'soil', 'soil', 'soil'],
332 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'planted_soil_right', 'soil', 'soil', 'soil'],
333 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
334 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
335 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
336 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
337 | ['', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
338 | ['', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil', 'soil', 'soil', 'soil'],
339 | [
340 | '',
341 | '',
342 | '',
343 | '',
344 | '',
345 | '',
346 | '',
347 | 'pipe_top_left',
348 | 'pipe_left',
349 | 'pipe_left',
350 | 'pipe_left_grass',
351 | 'pipe_left_soil',
352 | 'pipe_left_soil',
353 | 'pipe_left_soil',
354 | 'pipe_left_soil',
355 | ],
356 | [
357 | '',
358 | '',
359 | '',
360 | '',
361 | '',
362 | '',
363 | '',
364 | 'pipe_top_right',
365 | 'pipe_right',
366 | 'pipe_right',
367 | 'pipe_right',
368 | 'pipe_right',
369 | 'pipe_right',
370 | 'pipe_right_grass',
371 | 'pipe_right_soil',
372 | ],
373 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
374 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
375 | ['', '', '', '', '', '', '', '', '', 'coinbox', '', '', '', 'grass_top', 'soil'],
376 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
377 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
378 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil'],
379 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
380 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
381 | [
382 | '',
383 | '',
384 | '',
385 | '',
386 | '',
387 | '',
388 | '',
389 | '',
390 | '',
391 | '',
392 | '',
393 | 'pipe_top_right',
394 | 'pipe_right',
395 | 'pipe_right_grass',
396 | 'pipe_right_soil',
397 | ],
398 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
399 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
400 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
401 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
402 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
403 | ['', '', '', '', '', '', '', '', '', '', '', '', 'ballmonster', 'grass_top', 'soil'],
404 | ['', '', '', '', '', '', 'coinbox', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
405 | ['', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
406 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
407 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
408 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
409 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
410 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
411 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
412 | ['', '', '', '', '', '', '', '', '', '', '', 'pipe_top_left', 'pipe_left', 'pipe_left_grass', 'pipe_left_soil'],
413 | [
414 | '',
415 | '',
416 | '',
417 | '',
418 | '',
419 | '',
420 | '',
421 | '',
422 | '',
423 | '',
424 | '',
425 | 'pipe_top_right',
426 | 'pipe_right',
427 | 'pipe_right_grass',
428 | 'pipe_right_soil',
429 | ],
430 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
431 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
432 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
433 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
434 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
435 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
436 | ['', '', '', '', '', '', '', '', '', '', '', '', 'greenturtle', 'grass_top', 'soil'],
437 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_left', 'grass_top', 'soil'],
438 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle_left', 'grass_top', 'soil'],
439 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_middle_right', 'grass_top', 'soil'],
440 | ['', '', '', '', '', '', '', '', '', '', '', '', 'bush_right', 'grass_top', 'soil'],
441 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
442 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
443 | ['', '', '', '', '', '', '', '', '', '', '', '', 'greenturtle', 'grass_top', 'soil'],
444 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top_right_rounded', 'soil_right'],
445 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
446 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
447 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
448 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top_left_rounded', 'soil_left'],
449 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
450 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
451 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
452 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
453 | ['', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
454 | ['', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
455 | ['', '', '', '', '', '', 'stone', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
456 | ['', '', '', '', '', '', 'stone', '', '', '', 'brown_block', 'brown_block', 'brown_block', 'grass_top', 'soil'],
457 | [
458 | '',
459 | '',
460 | '',
461 | '',
462 | '',
463 | '',
464 | 'multiple_coinbox',
465 | '',
466 | '',
467 | '',
468 | 'brown_block',
469 | 'brown_block',
470 | 'brown_block',
471 | 'grass_top',
472 | 'soil',
473 | ],
474 | [
475 | '',
476 | '',
477 | '',
478 | '',
479 | '',
480 | '',
481 | '',
482 | '',
483 | '',
484 | '',
485 | 'brown_block',
486 | 'brown_block',
487 | 'brown_block',
488 | 'grass_top_right_rounded',
489 | 'soil_right',
490 | ],
491 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
492 | ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
493 | ['', '', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'grass_top_left_rounded', 'soil_left'],
494 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
495 | ['', '', '', '', '', '', '', '', '', '', '', 'brown_block', 'brown_block', 'grass_top', 'soil'],
496 | ['', '', '', '', '', '', '', '', '', 'brown_block', 'coin', 'brown_block', 'brown_block', 'grass_top', 'soil'],
497 | [
498 | '',
499 | '',
500 | '',
501 | '',
502 | '',
503 | '',
504 | '',
505 | '',
506 | 'brown_block',
507 | 'brown_block',
508 | '',
509 | 'brown_block',
510 | 'brown_block',
511 | 'grass_top',
512 | 'soil',
513 | ],
514 | [
515 | '',
516 | '',
517 | '',
518 | '',
519 | '',
520 | '',
521 | '',
522 | '',
523 | 'brown_block',
524 | 'brown_block',
525 | 'coin',
526 | 'brown_block',
527 | 'brown_block',
528 | 'grass_top',
529 | 'soil',
530 | ],
531 | [
532 | '',
533 | '',
534 | '',
535 | '',
536 | '',
537 | '',
538 | 'brown_block',
539 | '',
540 | 'brown_block',
541 | 'brown_block',
542 | '',
543 | 'brown_block',
544 | 'brown_block',
545 | 'grass_top',
546 | 'soil',
547 | ],
548 | [
549 | '',
550 | '',
551 | '',
552 | '',
553 | '',
554 | 'brown_block',
555 | 'brown_block',
556 | 'coin',
557 | 'brown_block',
558 | 'brown_block',
559 | 'coin',
560 | 'brown_block',
561 | 'brown_block',
562 | 'grass_top',
563 | 'soil',
564 | ],
565 | [
566 | '',
567 | '',
568 | '',
569 | '',
570 | 'brown_block',
571 | 'brown_block',
572 | 'brown_block',
573 | '',
574 | 'brown_block',
575 | 'brown_block',
576 | 'brown_block',
577 | 'brown_block',
578 | 'brown_block',
579 | 'grass_top',
580 | 'soil',
581 | ],
582 | [
583 | '',
584 | '',
585 | '',
586 | 'brown_block',
587 | 'brown_block',
588 | 'brown_block',
589 | 'brown_block',
590 | 'coin',
591 | 'brown_block',
592 | 'brown_block',
593 | 'brown_block',
594 | 'brown_block',
595 | 'brown_block',
596 | 'grass_top',
597 | 'soil',
598 | ],
599 | [
600 | '',
601 | '',
602 | 'brown_block',
603 | 'brown_block',
604 | 'brown_block',
605 | 'brown_block',
606 | 'brown_block',
607 | '',
608 | 'brown_block',
609 | 'brown_block',
610 | 'brown_block',
611 | 'brown_block',
612 | 'brown_block',
613 | 'grass_top',
614 | 'soil',
615 | ],
616 | [
617 | '',
618 | 'brown_block',
619 | 'brown_block',
620 | 'brown_block',
621 | 'brown_block',
622 | 'brown_block',
623 | 'brown_block',
624 | 'coin',
625 | 'brown_block',
626 | 'brown_block',
627 | 'brown_block',
628 | 'brown_block',
629 | 'brown_block',
630 | 'grass_top',
631 | 'soil',
632 | ],
633 | [
634 | '',
635 | 'brown_block',
636 | 'brown_block',
637 | 'brown_block',
638 | 'brown_block',
639 | 'brown_block',
640 | 'brown_block',
641 | '',
642 | 'brown_block',
643 | 'brown_block',
644 | 'brown_block',
645 | 'brown_block',
646 | 'brown_block',
647 | 'grass_top',
648 | 'soil',
649 | ],
650 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
651 | ['', '', 'coin', '', 'coin', '', 'coin', '', 'coin', '', 'coin', '', '', 'grass_top', 'soil'],
652 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
653 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
654 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
655 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
656 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
657 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
658 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
659 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
660 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
661 | ['', '', '', '', '', '', '', '', '', '', '', '', '', 'grass_top', 'soil'],
662 | ],
663 | },
664 | ];
665 |
666 | export default standardLevels;
667 |
--------------------------------------------------------------------------------