diff --git a/.github/workflows/gcs-deploy.yml b/.github/workflows/gcs-deploy.yml
new file mode 100644
index 00000000..13f549ef
--- /dev/null
+++ b/.github/workflows/gcs-deploy.yml
@@ -0,0 +1,31 @@
+name: Deploy to GCS
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build_and_deploy:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: "read"
+ id-token: "write"
+
+ steps:
+ - id: "checkout"
+ uses: "actions/checkout@v4"
+
+ - id: "auth"
+ uses: "google-github-actions/auth@v3"
+ with:
+ credentials_json: "${{ secrets.GCS_DEPLOY }}"
+
+ - id: "upload-folder"
+ uses: "google-github-actions/upload-cloud-storage@v3"
+ with:
+ path: "public"
+ destination: "super-mario.pomle.com"
+ parent: false
+ headers: |
+ cache-control: no-cache, max-age=0
diff --git a/public/audio/coin.ogg b/public/audio/coin.ogg
deleted file mode 100644
index d21eba4c..00000000
Binary files a/public/audio/coin.ogg and /dev/null differ
diff --git a/public/audio/fx/1up.ogg b/public/audio/fx/1up.ogg
new file mode 100644
index 00000000..963f2773
Binary files /dev/null and b/public/audio/fx/1up.ogg differ
diff --git a/public/audio/fx/bowser-die.ogg b/public/audio/fx/bowser-die.ogg
new file mode 100644
index 00000000..8a8e98c3
Binary files /dev/null and b/public/audio/fx/bowser-die.ogg differ
diff --git a/public/audio/fx/bowser-fire.ogg b/public/audio/fx/bowser-fire.ogg
new file mode 100644
index 00000000..1785e8ca
Binary files /dev/null and b/public/audio/fx/bowser-fire.ogg differ
diff --git a/public/audio/fx/brick-bump.ogg b/public/audio/fx/brick-bump.ogg
new file mode 100644
index 00000000..588a4ca2
Binary files /dev/null and b/public/audio/fx/brick-bump.ogg differ
diff --git a/public/audio/fx/brick-destroy.ogg b/public/audio/fx/brick-destroy.ogg
new file mode 100644
index 00000000..d3ce3c68
Binary files /dev/null and b/public/audio/fx/brick-destroy.ogg differ
diff --git a/public/audio/fx/coin.ogg b/public/audio/fx/coin.ogg
new file mode 100644
index 00000000..00070261
Binary files /dev/null and b/public/audio/fx/coin.ogg differ
diff --git a/public/audio/fx/fireball.ogg b/public/audio/fx/fireball.ogg
new file mode 100644
index 00000000..bc9dd358
Binary files /dev/null and b/public/audio/fx/fireball.ogg differ
diff --git a/public/audio/fx/fireworks.ogg b/public/audio/fx/fireworks.ogg
new file mode 100644
index 00000000..7a402c5f
Binary files /dev/null and b/public/audio/fx/fireworks.ogg differ
diff --git a/public/audio/fx/flagpole.ogg b/public/audio/fx/flagpole.ogg
new file mode 100644
index 00000000..17a3228b
Binary files /dev/null and b/public/audio/fx/flagpole.ogg differ
diff --git a/public/audio/fx/jump-large.ogg b/public/audio/fx/jump-large.ogg
new file mode 100644
index 00000000..86b5d254
Binary files /dev/null and b/public/audio/fx/jump-large.ogg differ
diff --git a/public/audio/fx/jump.ogg b/public/audio/fx/jump.ogg
new file mode 100644
index 00000000..d90f0576
Binary files /dev/null and b/public/audio/fx/jump.ogg differ
diff --git a/public/audio/fx/kick.ogg b/public/audio/fx/kick.ogg
new file mode 100644
index 00000000..bfc26f75
Binary files /dev/null and b/public/audio/fx/kick.ogg differ
diff --git a/public/audio/fx/pause.ogg b/public/audio/fx/pause.ogg
new file mode 100644
index 00000000..2c55474e
Binary files /dev/null and b/public/audio/fx/pause.ogg differ
diff --git a/public/audio/fx/pipe.ogg b/public/audio/fx/pipe.ogg
new file mode 100644
index 00000000..e3396e78
Binary files /dev/null and b/public/audio/fx/pipe.ogg differ
diff --git a/public/audio/fx/power-up-appears.ogg b/public/audio/fx/power-up-appears.ogg
new file mode 100644
index 00000000..e21ce2ff
Binary files /dev/null and b/public/audio/fx/power-up-appears.ogg differ
diff --git a/public/audio/fx/power-up-consume.ogg b/public/audio/fx/power-up-consume.ogg
new file mode 100644
index 00000000..eccd525c
Binary files /dev/null and b/public/audio/fx/power-up-consume.ogg differ
diff --git a/public/audio/fx/stomp.ogg b/public/audio/fx/stomp.ogg
new file mode 100644
index 00000000..9ac79d3e
Binary files /dev/null and b/public/audio/fx/stomp.ogg differ
diff --git a/public/audio/fx/thwomp.ogg b/public/audio/fx/thwomp.ogg
new file mode 100644
index 00000000..b4831ad0
Binary files /dev/null and b/public/audio/fx/thwomp.ogg differ
diff --git a/public/audio/fx/vine.ogg b/public/audio/fx/vine.ogg
new file mode 100644
index 00000000..620b657c
Binary files /dev/null and b/public/audio/fx/vine.ogg differ
diff --git a/public/audio/jump.ogg b/public/audio/jump.ogg
deleted file mode 100644
index 94cd13a6..00000000
Binary files a/public/audio/jump.ogg and /dev/null differ
diff --git a/public/audio/music/castle-clear.ogg b/public/audio/music/castle-clear.ogg
new file mode 100644
index 00000000..c7989e34
Binary files /dev/null and b/public/audio/music/castle-clear.ogg differ
diff --git a/public/audio/music/castle.ogg b/public/audio/music/castle.ogg
new file mode 100644
index 00000000..cbc242b3
Binary files /dev/null and b/public/audio/music/castle.ogg differ
diff --git a/public/audio/music/die.ogg b/public/audio/music/die.ogg
new file mode 100644
index 00000000..aaff5779
Binary files /dev/null and b/public/audio/music/die.ogg differ
diff --git a/public/audio/music/game-over.ogg b/public/audio/music/game-over.ogg
new file mode 100644
index 00000000..ff480808
Binary files /dev/null and b/public/audio/music/game-over.ogg differ
diff --git a/public/audio/music/hurry.ogg b/public/audio/music/hurry.ogg
index 6fa58686..ecb9e712 100644
Binary files a/public/audio/music/hurry.ogg and b/public/audio/music/hurry.ogg differ
diff --git a/public/audio/music/level-clear.ogg b/public/audio/music/level-clear.ogg
new file mode 100644
index 00000000..e5844930
Binary files /dev/null and b/public/audio/music/level-clear.ogg differ
diff --git a/public/audio/music/overworld.ogg b/public/audio/music/overworld.ogg
index e0c557b9..e7932d04 100644
Binary files a/public/audio/music/overworld.ogg and b/public/audio/music/overworld.ogg differ
diff --git a/public/audio/music/princess.ogg b/public/audio/music/princess.ogg
new file mode 100644
index 00000000..2792b60f
Binary files /dev/null and b/public/audio/music/princess.ogg differ
diff --git a/public/audio/music/starman.ogg b/public/audio/music/starman.ogg
new file mode 100644
index 00000000..4223d4d1
Binary files /dev/null and b/public/audio/music/starman.ogg differ
diff --git a/public/audio/music/underwater.ogg b/public/audio/music/underwater.ogg
new file mode 100644
index 00000000..2133a59d
Binary files /dev/null and b/public/audio/music/underwater.ogg differ
diff --git a/public/audio/music/underworld.ogg b/public/audio/music/underworld.ogg
index 25d423a3..3610e51a 100644
Binary files a/public/audio/music/underworld.ogg and b/public/audio/music/underworld.ogg differ
diff --git a/public/audio/music/uw-entrance.ogg b/public/audio/music/uw-entrance.ogg
new file mode 100644
index 00000000..ad4bbafb
Binary files /dev/null and b/public/audio/music/uw-entrance.ogg differ
diff --git a/public/audio/stomp.ogg b/public/audio/stomp.ogg
deleted file mode 100644
index a2e0e425..00000000
Binary files a/public/audio/stomp.ogg and /dev/null differ
diff --git a/public/audio/thwomp.ogg b/public/audio/thwomp.ogg
deleted file mode 100644
index 51e5973e..00000000
Binary files a/public/audio/thwomp.ogg and /dev/null differ
diff --git a/public/css/main.css b/public/css/main.css
new file mode 100644
index 00000000..878d588e
--- /dev/null
+++ b/public/css/main.css
@@ -0,0 +1,22 @@
+body {
+ background: #000;
+ margin: 0;
+}
+
+#screen {
+ display: block;
+ image-rendering: pixelated;
+ margin: auto;
+ width: 100vw;
+ max-height: 100vh;
+}
+
+.aspect-4-3 {
+ height: 75vw;
+ max-width: 133.3vh;
+}
+
+.aspect-16-9 {
+ height: 56.25vw;
+ max-width: 177.7vh;
+}
\ No newline at end of file
diff --git a/public/img/characters.gif b/public/img/characters.gif
deleted file mode 100644
index 02fdba0d..00000000
Binary files a/public/img/characters.gif and /dev/null differ
diff --git a/public/img/font.png b/public/img/font.png
index 4fb4c064..790290eb 100644
Binary files a/public/img/font.png and b/public/img/font.png differ
diff --git a/public/img/points.png b/public/img/points.png
new file mode 100644
index 00000000..d73612ef
Binary files /dev/null and b/public/img/points.png differ
diff --git a/public/img/sprites.png b/public/img/sprites.png
new file mode 100644
index 00000000..7eed6ea3
Binary files /dev/null and b/public/img/sprites.png differ
diff --git a/public/img/tiles.png b/public/img/tiles.png
index 31d7a8f9..cb9c4c11 100644
Binary files a/public/img/tiles.png and b/public/img/tiles.png differ
diff --git a/public/index.html b/public/index.html
index d5b402b5..6050b5f3 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,22 +2,10 @@
Super Mario
-
+
-
+
diff --git a/public/js/BoundingBox.js b/public/js/BoundingBox.js
index 9534d5ed..ebde30e8 100644
--- a/public/js/BoundingBox.js
+++ b/public/js/BoundingBox.js
@@ -1,3 +1,5 @@
+import { Vec2 } from "./math.js";
+
export default class BoundingBox {
constructor(pos, size, offset) {
this.pos = pos;
@@ -12,6 +14,31 @@ export default class BoundingBox {
&& this.right > box.left;
}
+ getCenter() {
+ return new Vec2(this.meridian, this.equator);
+ }
+
+ setCenter(vec2) {
+ this.meridian = vec2.x;
+ this.equator = vec2.y;
+ }
+
+ get meridian() {
+ return this.pos.x + this.offset.x + this.size.x / 2;
+ }
+
+ set meridian(c) {
+ this.pos.x = c - (this.size.x / 2 + this.offset.x);
+ }
+
+ get equator() {
+ return this.pos.y + this.offset.y + this.size.y / 2;
+ }
+
+ set equator(c) {
+ this.pos.y = c - (this.size.y / 2 + this.offset.y);
+ }
+
get bottom() {
return this.pos.y + this.size.y + this.offset.y;
}
diff --git a/public/js/Camera.js b/public/js/Camera.js
index 1dfe0be3..d1238811 100644
--- a/public/js/Camera.js
+++ b/public/js/Camera.js
@@ -4,5 +4,8 @@ export default class Camera {
constructor() {
this.pos = new Vec2(0, 0);
this.size = new Vec2(256, 224);
+
+ this.min = new Vec2(0, 0);
+ this.max = new Vec2(Infinity, Infinity);
}
}
diff --git a/public/js/Entity.js b/public/js/Entity.js
index e07b6dd2..42491410 100644
--- a/public/js/Entity.js
+++ b/public/js/Entity.js
@@ -1,8 +1,30 @@
import {Vec2} from './math.js';
-import Trait from './Trait.js';
import AudioBoard from './AudioBoard.js';
import BoundingBox from './BoundingBox.js';
import EventBuffer from './EventBuffer.js';
+import Trait from './Trait.js';
+
+export const Align = {
+ center(target, subject) {
+ subject.bounds.setCenter(target.bounds.getCenter());
+ },
+
+ bottom(target, subject) {
+ subject.bounds.bottom = target.bounds.bottom;
+ },
+
+ top(target, subject) {
+ subject.bounds.top = target.bounds.top;
+ },
+
+ left(target, subject) {
+ subject.bounds.left = target.bounds.left;
+ },
+
+ right(target, subject) {
+ subject.bounds.right = target.bounds.right;
+ },
+};
export const Sides = {
TOP: Symbol('top'),
@@ -13,12 +35,11 @@ export const Sides = {
export default class Entity {
constructor() {
+ this.id = null;
this.audio = new AudioBoard();
this.events = new EventBuffer();
this.sounds = new Set();
- this.events = new EventBuffer();
-
this.pos = new Vec2(0, 0);
this.vel = new Vec2(0, 0);
this.size = new Vec2(0, 0);
@@ -45,10 +66,6 @@ export default class Entity {
});
}
- draw() {
-
- }
-
finalize() {
this.events.emit(Trait.EVENT_TASK, this);
diff --git a/public/js/Level.js b/public/js/Level.js
index 64fe0d4f..53506f86 100644
--- a/public/js/Level.js
+++ b/public/js/Level.js
@@ -1,26 +1,41 @@
import Camera from './Camera.js';
-import Compositor from './Compositor.js';
-import EventEmitter from './EventEmitter.js';
import MusicController from './MusicController.js';
import EntityCollider from './EntityCollider.js';
import Scene from './Scene.js';
import TileCollider from './TileCollider.js';
+import { clamp } from './math.js';
import { findPlayers } from './player.js';
function focusPlayer(level) {
for (const player of findPlayers(level.entities)) {
- level.camera.pos.x = Math.max(0, player.pos.x - 100);
+ level.camera.pos.x = clamp(
+ player.pos.x - 100,
+ level.camera.min.x,
+ level.camera.max.x - level.camera.size.x);
+ }
+}
+
+class EntityCollection extends Set {
+ get(id) {
+ for (const entity of this) {
+ if (entity.id === id) {
+ return entity;
+ }
+ }
}
}
export default class Level extends Scene {
static EVENT_TRIGGER = Symbol('trigger');
+ static EVENT_COMPLETE = Symbol('complete');
constructor() {
super();
this.name = "";
+ this.checkpoints = [];
+
this.gravity = 1500;
this.totalTime = 0;
@@ -28,7 +43,7 @@ export default class Level extends Scene {
this.music = new MusicController();
- this.entities = new Set();
+ this.entities = new EntityCollection();
this.entityCollider = new EntityCollider(this.entities);
this.tileCollider = new TileCollider();
diff --git a/public/js/TileCollider.js b/public/js/TileCollider.js
index 7a923167..8c0cfa00 100644
--- a/public/js/TileCollider.js
+++ b/public/js/TileCollider.js
@@ -69,7 +69,7 @@ export default class TileCollider {
level,
};
- const handler = handlers[match.tile.type];
+ const handler = handlers[match.tile.behavior];
if (handler) {
handler[index](tileCollisionContext);
}
diff --git a/public/js/entities.js b/public/js/entities.js
index 833fd22b..5e5403f6 100644
--- a/public/js/entities.js
+++ b/public/js/entities.js
@@ -1,23 +1,77 @@
import {loadMario} from './entities/Mario.js';
-import {loadGoomba} from './entities/Goomba.js';
-import {loadKoopa} from './entities/Koopa.js';
+import {loadGoombaBrown, loadGoombaBlue} from './entities/Goomba.js';
+import {loadKoopaGreen, loadKoopaBlue} from './entities/Koopa.js';
+import {loadCheepSlow, loadCheepFast, loadCheepSlowWavy, loadCheepFastWavy} from './entities/CheepCheep.js';
+import {loadPiranhaPlant} from './entities/PiranhaPlant.js';
import {loadBullet} from './entities/Bullet.js';
import {loadCannon} from './entities/Cannon.js';
+import {loadBrickShrapnel} from './entities/BrickShrapnel.js';
+import {loadPipePortal} from './entities/PipePortal.js';
+import {loadFlagPole} from './entities/FlagPole.js';
-export function loadEntities(audioContext) {
+function createPool(size) {
+ const pool = [];
+
+ return function createPooledFactory(factory) {
+ for (let i = 0; i < size; i++) {
+ pool.push(factory());
+ }
+
+ let count = 0;
+ return function pooledFactory() {
+ const entity = pool[count++ % pool.length];
+ entity.lifetime = 0;
+ return entity;
+ }
+ }
+}
+
+export async function loadEntities(audioContext) {
const entityFactories = {};
+ function setup(loader) {
+ return loader(audioContext);
+ }
+
function addAs(name) {
- return factory => entityFactories[name] = factory;
+ return function addFactory(factory) {
+ entityFactories[name] = factory;
+ }
}
- return Promise.all([
- loadMario(audioContext).then(addAs('mario')),
- loadGoomba(audioContext).then(addAs('goomba')),
- loadKoopa(audioContext).then(addAs('koopa')),
- loadBullet(audioContext).then(addAs('bullet')),
- loadCannon(audioContext).then(addAs('cannon')),
+ await Promise.all([
+ setup(loadMario)
+ .then(addAs('mario')),
+ setup(loadPiranhaPlant)
+ .then(addAs('piranha-plant')),
+ setup(loadGoombaBrown)
+ .then(addAs('goomba-brown')),
+ setup(loadGoombaBlue)
+ .then(addAs('goomba-blue')),
+ setup(loadKoopaGreen)
+ .then(addAs('koopa-green')),
+ setup(loadKoopaBlue)
+ .then(addAs('koopa-blue')),
+ setup(loadCheepSlow)
+ .then(addAs('cheep-slow')),
+ setup(loadCheepFast)
+ .then(addAs('cheep-fast')),
+ setup(loadCheepSlowWavy)
+ .then(addAs('cheep-slow-wavy')),
+ setup(loadCheepFastWavy)
+ .then(addAs('cheep-fast-wavy')),
+ setup(loadBullet)
+ .then(addAs('bullet')),
+ setup(loadCannon)
+ .then(addAs('cannon')),
+ setup(loadPipePortal)
+ .then(addAs('pipe-portal')),
+ setup(loadFlagPole)
+ .then(addAs('flag-pole')),
+ setup(loadBrickShrapnel)
+ .then(createPool(8))
+ .then(addAs('brickShrapnel')),
+ ]);
- ])
- .then(() => entityFactories);
+ return entityFactories;
}
diff --git a/public/js/entities/BrickShrapnel.js b/public/js/entities/BrickShrapnel.js
new file mode 100644
index 00000000..aac69b9e
--- /dev/null
+++ b/public/js/entities/BrickShrapnel.js
@@ -0,0 +1,35 @@
+import Entity from '../Entity.js';
+import LifeLimit from '../traits/LifeLimit.js';
+import Gravity from '../traits/Gravity.js';
+import Velocity from '../traits/Velocity.js';
+import {loadAudioBoard} from '../loaders/audio.js';
+import {loadSpriteSheet} from '../loaders/sprite.js';
+
+export function loadBrickShrapnel(audioContext) {
+ return Promise.all([
+ loadSpriteSheet('brick-shrapnel'),
+ loadAudioBoard('brick-shrapnel', audioContext),
+ ])
+ .then(([sprite, audio]) => {
+ return createFactory(sprite, audio);
+ });
+}
+
+function createFactory(sprite, audio) {
+ const spinBrick = sprite.animations.get('spinning-brick');
+
+ function draw(context) {
+ sprite.draw(spinBrick(this.lifetime), context, 0, 0);
+ }
+
+ return function createBrickShrapnel() {
+ const entity = new Entity();
+ entity.audio = audio;
+ entity.size.set(8, 8);
+ entity.addTrait(new LifeLimit());
+ entity.addTrait(new Gravity());
+ entity.addTrait(new Velocity());
+ entity.draw = draw;
+ return entity;
+ };
+}
diff --git a/public/js/entities/Bullet.js b/public/js/entities/Bullet.js
index d41fea37..2be8691c 100644
--- a/public/js/entities/Bullet.js
+++ b/public/js/entities/Bullet.js
@@ -1,8 +1,8 @@
-import Entity, {Sides} from '../Entity.js';
+import Entity from '../Entity.js';
import Trait from '../Trait.js';
import Killable from '../traits/Killable.js';
-import Stomper from '../traits/Stomper.js';
import Gravity from '../traits/Gravity.js';
+import Stomper from '../traits/Stomper.js';
import Velocity from '../traits/Velocity.js';
import {loadSpriteSheet} from '../loaders/sprite.js';
@@ -44,7 +44,7 @@ class Behavior extends Trait {
function createBulletFactory(sprite) {
function drawBullet(context) {
- sprite.draw('bullet', context, 0, 0, this.vel.x < 0);
+ sprite.draw('bullet', context, 0, 0, this.vel.x > 0);
}
return function createBullet() {
diff --git a/public/js/entities/CheepCheep.js b/public/js/entities/CheepCheep.js
new file mode 100644
index 00000000..5b22d1c0
--- /dev/null
+++ b/public/js/entities/CheepCheep.js
@@ -0,0 +1,155 @@
+import Entity from '../Entity.js';
+import Trait from '../Trait.js';
+import Killable from '../traits/Killable.js';
+import {loadSpriteSheet} from '../loaders/sprite.js';
+
+export function loadCheepSlow() {
+ return loadSpriteSheet('cheep-gray')
+ .then(createCheepSlowFactory);
+}
+
+export function loadCheepSlowWavy() {
+ return loadSpriteSheet('cheep-gray')
+ .then(createCheepSlowWavyFactory);
+}
+
+export function loadCheepFast() {
+ return loadSpriteSheet('cheep-red')
+ .then(createCheepFastFactory);
+}
+
+export function loadCheepFastWavy() {
+ return loadSpriteSheet('cheep-red')
+ .then(createCheepFastWavyFactory);
+}
+
+class Behavior extends Trait {
+
+ collides(us, them) {
+ if(them.traits.has(Killable)) {
+ them.traits.get(Killable).kill();
+ }
+ }
+
+ update(entity, gameContext, level) {
+ const {deltaTime} = gameContext;
+ entity.pos.x += entity.vel.x * deltaTime;
+ }
+
+}
+
+class Wavy extends Trait {
+ constructor() {
+ super();
+ this.amplitude = 16;
+ this.direction = 1;
+ this.offset = 0;
+ this.speed = 0.5;
+ }
+
+ update(entity, gameContext, level) {
+ const {deltaTime} = gameContext;
+ const movementY = (entity.vel.x * deltaTime * this.direction) * this.speed;
+ entity.pos.y += movementY;
+
+ this.offset += movementY;
+ if (Math.abs(this.offset) > this.amplitude) {
+ this.direction = -this.direction;
+ }
+ }
+}
+
+function createCheepSlowFactory(sprite) {
+ const swimAnim = sprite.animations.get('swim');
+
+ function routeAnim(entity) {
+ return swimAnim(entity.lifetime);
+ }
+
+ function drawCheepSlow(context) {
+ sprite.draw(routeAnim(this), context, 0, 0, true);
+ }
+
+ return function createCheepSlow() {
+ const entity = new Entity();
+ entity.size.set(16, 16);
+ entity.vel.x = -16;
+ entity.addTrait(new Behavior());
+ entity.draw = drawCheepSlow;
+
+ return entity;
+ };
+}
+
+function createCheepSlowWavyFactory(sprite) {
+ const swimAnim = sprite.animations.get('swim');
+
+ function routeAnim(entity) {
+ return swimAnim(entity.lifetime);
+ }
+
+ function drawCheepSlowWavy(context) {
+ sprite.draw(routeAnim(this), context, 0, 0, true);
+ }
+
+ return function createCheepSlowWavy() {
+ const entity = new Entity();
+ entity.size.set(16, 16);
+ entity.vel.x = -16;
+
+ entity.addTrait(new Behavior());
+ entity.addTrait(new Wavy());
+
+ entity.draw = drawCheepSlowWavy;
+
+ return entity;
+ };
+}
+
+function createCheepFastFactory(sprite) {
+ const swimAnim = sprite.animations.get('swim');
+
+ function routeAnim(entity) {
+ return swimAnim(entity.lifetime);
+ }
+
+ function drawCheepFast(context) {
+ sprite.draw(routeAnim(this), context, 0, 0, true);
+ }
+
+ return function createCheepFast() {
+ const entity = new Entity();
+ entity.size.set(16, 16);
+ entity.vel.x = -32;
+ entity.addTrait(new Behavior());
+ entity.draw = drawCheepFast;
+
+ return entity;
+ };
+}
+
+function createCheepFastWavyFactory(sprite) {
+ const swimAnim = sprite.animations.get('swim');
+
+ function routeAnim(entity) {
+ return swimAnim(entity.lifetime);
+ }
+
+ function drawCheepFastWavy(context) {
+ sprite.draw(routeAnim(this), context, 0, 0, true);
+ }
+
+ return function createCheepFastWavy() {
+ const entity = new Entity();
+ entity.size.set(16, 16);
+ entity.vel.x = -32;
+
+ entity.addTrait(new Behavior());
+ entity.addTrait(new Wavy());
+ entity.traits.get(Wavy).speed = 0.25;
+
+ entity.draw = drawCheepFastWavy;
+
+ return entity;
+ };
+}
diff --git a/public/js/entities/FlagPole.js b/public/js/entities/FlagPole.js
new file mode 100644
index 00000000..94488a01
--- /dev/null
+++ b/public/js/entities/FlagPole.js
@@ -0,0 +1,24 @@
+import Entity from '../Entity.js';
+import Pole from '../traits/Pole.js';
+import {loadAudioBoard} from '../loaders/audio.js';
+
+export function loadFlagPole(audioContext) {
+ return Promise.all([
+ loadAudioBoard('flag-pole', audioContext),
+ ])
+ .then(([audio]) => {
+ return createFactory(audio);
+ });
+}
+
+function createFactory(audio) {
+ return function createFlagPole() {
+ const entity = new Entity();
+ const pole = new Pole();
+ entity.audio = audio;
+ entity.size.set(8, 144);
+ entity.offset.set(4, 0);
+ entity.addTrait(pole);
+ return entity;
+ };
+}
diff --git a/public/js/entities/Goomba.js b/public/js/entities/Goomba.js
index 2ce1df84..1c06be40 100644
--- a/public/js/entities/Goomba.js
+++ b/public/js/entities/Goomba.js
@@ -1,16 +1,22 @@
-import Entity, {Sides} from '../Entity.js';
+import Entity from '../Entity.js';
import Trait from '../Trait.js';
import Killable from '../traits/Killable.js';
import PendulumMove from '../traits/PendulumMove.js';
import Physics from '../traits/Physics.js';
import Solid from '../traits/Solid.js';
+import Stomper from '../traits/Stomper.js';
import {loadSpriteSheet} from '../loaders/sprite.js';
-export function loadGoomba() {
- return loadSpriteSheet('goomba')
- .then(createGoombaFactory);
+export function loadGoombaBrown() {
+ return loadSpriteSheet('goomba-brown')
+ .then(createGoombaFactory);
}
+export function loadGoombaBlue() {
+ return loadSpriteSheet('goomba-blue')
+ .then(createGoombaFactory);
+ }
+
class Behavior extends Trait {
collides(us, them) {
@@ -18,7 +24,7 @@ class Behavior extends Trait {
return;
}
- if (them.stomper) {
+ if (them.traits.has(Stomper)) {
if (them.vel.y > us.vel.y) {
us.traits.get(Killable).kill();
us.traits.get(PendulumMove).speed = 0;
diff --git a/public/js/entities/Koopa.js b/public/js/entities/Koopa.js
index 979f7a90..2162b858 100644
--- a/public/js/entities/Koopa.js
+++ b/public/js/entities/Koopa.js
@@ -1,17 +1,23 @@
-import Entity, {Sides} from '../Entity.js';
+import Entity from '../Entity.js';
import Trait from '../Trait.js';
import Killable from '../traits/Killable.js';
-import Stomper from '../traits/Stomper.js';
import PendulumMove from '../traits/PendulumMove.js';
import Physics from '../traits/Physics.js';
import Solid from '../traits/Solid.js';
+import Stomper from '../traits/Stomper.js';
import {loadSpriteSheet} from '../loaders/sprite.js';
-export function loadKoopa() {
- return loadSpriteSheet('koopa')
+export function loadKoopaGreen() {
+ return loadSpriteSheet('koopa-green')
.then(createKoopaFactory);
}
+export function loadKoopaBlue() {
+ return loadSpriteSheet('koopa-blue')
+ .then(createKoopaFactory);
+ }
+
+
const STATE_WALKING = Symbol('walking');
const STATE_HIDING = Symbol('hiding');
const STATE_PANIC = Symbol('panic');
diff --git a/public/js/entities/Mario.js b/public/js/entities/Mario.js
index d0f1a780..60e328df 100644
--- a/public/js/entities/Mario.js
+++ b/public/js/entities/Mario.js
@@ -3,6 +3,8 @@ import Go from '../traits/Go.js';
import Jump from '../traits/Jump.js';
import Killable from '../traits/Killable.js';
import Physics from '../traits/Physics.js';
+import PipeTraveller from '../traits/PipeTraveller.js';
+import PoleTraveller from '../traits/PoleTraveller.js';
import Solid from '../traits/Solid.js';
import Stomper from '../traits/Stomper.js';
import {loadAudioBoard} from '../loaders/audio.js';
@@ -23,8 +25,30 @@ export function loadMario(audioContext) {
function createMarioFactory(sprite, audio) {
const runAnim = sprite.animations.get('run');
+ const climbAnim = sprite.animations.get('climb');
+
+ function getHeading(mario) {
+ const poleTraveller = mario.traits.get(PoleTraveller);
+ if (poleTraveller.distance) {
+ return false;
+ }
+ return mario.traits.get(Go).heading < 0;
+ }
function routeFrame(mario) {
+ const pipeTraveller = mario.traits.get(PipeTraveller);
+ if (pipeTraveller.movement.x != 0) {
+ return runAnim(pipeTraveller.distance.x * 2);
+ }
+ if (pipeTraveller.movement.y != 0) {
+ return 'idle';
+ }
+
+ const poleTraveller = mario.traits.get(PoleTraveller);
+ if (poleTraveller.distance) {
+ return climbAnim(poleTraveller.distance);
+ }
+
if (mario.traits.get(Jump).falling) {
return 'jump';
}
@@ -35,7 +59,7 @@ function createMarioFactory(sprite, audio) {
return 'break';
}
- return runAnim(go.distance);
+ return runAnim(mario.traits.get(Go).distance);
}
return 'idle';
@@ -46,8 +70,7 @@ function createMarioFactory(sprite, audio) {
}
function drawMario(context) {
- const go = this.traits.get(Go);
- sprite.draw(routeFrame(this), context, 0, 0, go.heading < 0);
+ sprite.draw(routeFrame(this), context, 0, 0, getHeading(this));
}
return function createMario() {
@@ -61,8 +84,11 @@ function createMarioFactory(sprite, audio) {
mario.addTrait(new Jump());
mario.addTrait(new Killable());
mario.addTrait(new Stomper());
+ mario.addTrait(new PipeTraveller());
+ mario.addTrait(new PoleTraveller());
- mario.traits.get(Killable).removeAfter = 0;
+ mario.traits.get(Killable).removeAfter = Infinity;
+ mario.traits.get(Jump).velocity = 175;
mario.turbo = setTurboState;
mario.draw = drawMario;
diff --git a/public/js/entities/PipePortal.js b/public/js/entities/PipePortal.js
new file mode 100644
index 00000000..37bf7d4c
--- /dev/null
+++ b/public/js/entities/PipePortal.js
@@ -0,0 +1,27 @@
+import { Direction } from '../math.js';
+import Entity from '../Entity.js';
+import Pipe from '../traits/Pipe.js';
+import {loadAudioBoard} from '../loaders/audio.js';
+
+export function loadPipePortal(audioContext) {
+ return Promise.all([
+ loadAudioBoard('pipe-portal', audioContext),
+ ])
+ .then(([audio]) => {
+ return createFactory(audio);
+ });
+}
+
+function createFactory(audio) {
+
+ return function createPipePortal(props) {
+ const pipe = new Pipe();
+ pipe.direction.copy(Direction[props.dir]);
+ const entity = new Entity();
+ entity.props = props;
+ entity.audio = audio;
+ entity.size.set(24, 30);
+ entity.addTrait(pipe);
+ return entity;
+ };
+}
diff --git a/public/js/entities/PiranhaPlant.js b/public/js/entities/PiranhaPlant.js
new file mode 100644
index 00000000..b5cb5000
--- /dev/null
+++ b/public/js/entities/PiranhaPlant.js
@@ -0,0 +1,102 @@
+import Entity from '../Entity.js';
+import Trait from '../Trait.js';
+import {loadSpriteSheet} from '../loaders/sprite.js';
+import {findPlayers} from '../player.js';
+
+export function loadPiranhaPlant() {
+ return loadSpriteSheet('piranha-plant')
+ .then(createPiranhaPlantFactory);
+}
+
+class Behavior extends Trait {
+ constructor() {
+ super();
+
+ this.graceDistance = 32;
+
+ this.idleTime = 4;
+ this.idleCounter = 0;
+ this.attackTime = 2;
+ this.attackCounter = null;
+ this.holdTime = 2;
+ this.holdCounter = null;
+ this.retreatTime = 2;
+ this.retreatCounter = null;
+
+ this.velocity = 30;
+ this.deltaMove = 0;
+ }
+
+ update(entity, gameContext, level) {
+ const {deltaTime} = gameContext;
+
+ if (this.idleCounter !== null) {
+ for (const player of findPlayers(level.entities)) {
+ const distance = player.bounds.getCenter().distance(entity.bounds.getCenter());
+ if (distance < this.graceDistance) {
+ this.idleCounter = 0;
+ return;
+ }
+ }
+
+ this.idleCounter += deltaTime;
+ if (this.idleCounter >= this.idleTime) {
+ this.attackCounter = 0;
+ this.idleCounter = null;
+ }
+ }
+ else if (this.attackCounter !== null) {
+ this.attackCounter += deltaTime;
+ const movement = this.velocity * deltaTime;
+ this.deltaMove += movement;
+ entity.pos.y -= movement;
+ if (this.deltaMove >= entity.size.y) {
+ entity.pos.y += entity.size.y - this.deltaMove;
+ this.attackCounter = null;
+ this.holdCounter = 0;
+ }
+ }
+ else if (this.holdCounter !== null) {
+ this.holdCounter += deltaTime;
+ if (this.holdCounter >= this.holdTime) {
+ this.retreatCounter = 0;
+ this.holdCounter = null;
+ }
+ }
+ else if (this.retreatCounter !== null) {
+ this.retreatCounter += deltaTime;
+ const movement = this.velocity * deltaTime;
+ this.deltaMove -= movement;
+ entity.pos.y += movement;
+ if (this.deltaMove <= 0) {
+ entity.pos.y -= this.deltaMove;
+ this.retreatCounter = null;
+ this.idleCounter = 0;
+ }
+ }
+ }
+}
+
+
+function createPiranhaPlantFactory(sprite) {
+ const chewAnim = sprite.animations.get('chew');
+
+ function routeAnim(entity) {
+ return chewAnim(entity.lifetime);
+ }
+
+ function drawPiranhaPlant(context) {
+ sprite.draw(routeAnim(this), context, 0, 0);
+ }
+
+ return function createPiranhaPlant() {
+ const entity = new Entity();
+ entity.size.set(16, 24);
+
+ entity.addTrait(new Behavior());
+
+ entity.draw = drawPiranhaPlant;
+
+ return entity;
+ };
+}
diff --git a/public/js/input.js b/public/js/input.js
index 05385683..34d8ab11 100644
--- a/public/js/input.js
+++ b/public/js/input.js
@@ -1,7 +1,17 @@
import Keyboard from './KeyboardState.js';
import InputRouter from './InputRouter.js';
-import Go from './traits/Go.js';
import Jump from './traits/Jump.js';
+import PipeTraveller from './traits/PipeTraveller.js';
+import Go from './traits/Go.js';
+
+const KEYMAP = {
+ UP: 'KeyW',
+ DOWN: 'KeyS',
+ LEFT: 'KeyA',
+ RIGHT: 'KeyD',
+ A: "KeyP",
+ B: "KeyO",
+};
export function setupKeyboard(window) {
const input = new Keyboard();
@@ -9,7 +19,7 @@ export function setupKeyboard(window) {
input.listenTo(window);
- input.addMapping('KeyP', keyState => {
+ input.addMapping(KEYMAP.A, keyState => {
if (keyState) {
router.route(entity => entity.traits.get(Jump).start());
} else {
@@ -17,16 +27,34 @@ export function setupKeyboard(window) {
}
});
- input.addMapping('KeyO', keyState => {
+ input.addMapping(KEYMAP.B, keyState => {
router.route(entity => entity.turbo(keyState));
});
- input.addMapping('KeyD', keyState => {
- router.route(entity => entity.traits.get(Go).dir += keyState ? 1 : -1);
+ input.addMapping(KEYMAP.UP, keyState => {
+ router.route(entity => {
+ entity.traits.get(PipeTraveller).direction.y += keyState ? -1 : 1;
+ });
+ });
+
+ input.addMapping(KEYMAP.DOWN, keyState => {
+ router.route(entity => {
+ entity.traits.get(PipeTraveller).direction.y += keyState ? 1 : -1;
+ });
+ });
+
+ input.addMapping(KEYMAP.RIGHT, keyState => {
+ router.route(entity => {
+ entity.traits.get(Go).dir += keyState ? 1 : -1;
+ entity.traits.get(PipeTraveller).direction.x += keyState ? 1 : -1;
+ });
});
- input.addMapping('KeyA', keyState => {
- router.route(entity => entity.traits.get(Go).dir += keyState ? -1 : 1);
+ input.addMapping(KEYMAP.LEFT, keyState => {
+ router.route(entity => {
+ entity.traits.get(Go).dir += keyState ? -1 : 1;
+ entity.traits.get(PipeTraveller).direction.x += keyState ? -1 : 1;
+ });
});
return router;
diff --git a/public/js/layers/background.js b/public/js/layers/background.js
index 3808ddb0..9ac310dd 100644
--- a/public/js/layers/background.js
+++ b/public/js/layers/background.js
@@ -16,10 +16,10 @@ export function createBackgroundLayer(level, tiles, sprites) {
const col = tiles.grid[x];
if (col) {
col.forEach((tile, y) => {
- if (sprites.animations.has(tile.name)) {
- sprites.drawAnim(tile.name, context, x - startIndex, y, level.totalTime);
+ if (sprites.animations.has(tile.style)) {
+ sprites.drawAnim(tile.style, context, x - startIndex, y, level.totalTime);
} else {
- sprites.drawTile(tile.name, context, x - startIndex, y);
+ sprites.drawTile(tile.style, context, x - startIndex, y);
}
});
}
diff --git a/public/js/layers/collision.js b/public/js/layers/collision.js
index 02a6e987..3757f0de 100644
--- a/public/js/layers/collision.js
+++ b/public/js/layers/collision.js
@@ -4,10 +4,10 @@ function createEntityLayer(entities) {
entities.forEach(entity => {
context.beginPath();
context.rect(
- entity.bounds.left - camera.pos.x,
- entity.bounds.top - camera.pos.y,
- entity.size.x,
- entity.size.y);
+ Math.floor(entity.bounds.left - camera.pos.x) + .5,
+ Math.floor(entity.bounds.top - camera.pos.y) + .5,
+ entity.size.x - 1,
+ entity.size.y -1);
context.stroke();
});
};
@@ -29,9 +29,10 @@ function createTileCandidateLayer(tileResolver) {
resolvedTiles.forEach(({x, y}) => {
context.beginPath();
context.rect(
- x * tileSize - camera.pos.x,
- y * tileSize - camera.pos.y,
- tileSize, tileSize);
+ Math.floor(x * tileSize - camera.pos.x) + .5,
+ Math.floor(y * tileSize - camera.pos.y) + .5,
+ tileSize - 1,
+ tileSize - 1);
context.stroke();
});
diff --git a/public/js/layers/dashboard.js b/public/js/layers/dashboard.js
index d49ec70d..f717d50e 100644
--- a/public/js/layers/dashboard.js
+++ b/public/js/layers/dashboard.js
@@ -2,38 +2,23 @@ import Player from "../traits/Player.js";
import LevelTimer from "../traits/LevelTimer.js";
import {findPlayers} from "../player.js";
-function getPlayerTrait(entities) {
- for (const entity of findPlayers(entities)) {
- return entity.traits.get(Player);
- }
-}
-
-function getTimerTrait(entities) {
- for (const entity of entities) {
- if (entity.traits.has(LevelTimer)) {
- return entity.traits.get(LevelTimer);
- }
- }
-}
-
-export function createDashboardLayer(font, level) {
- const LINE1 = font.size;
- const LINE2 = font.size * 2;
-
- const timerTrait = getTimerTrait(level.entities);
+export function createDashboardLayer(font, entity) {
+ const LINE1 = font.size * 2;
+ const LINE2 = font.size * 3;
return function drawDashboard(context) {
- const playerTrait = getPlayerTrait(level.entities);
+ const playerTrait = entity.traits.get(Player);
+ const timerTrait = entity.traits.get(LevelTimer);
- font.print(playerTrait.name, context, 16, LINE1);
- font.print(playerTrait.score.toString().padStart(6, '0'), context, 16, LINE2);
+ font.print(playerTrait.name, context, 24, LINE1);
+ font.print(playerTrait.score.toString().padStart(6, '0'), context, 24, LINE2);
- font.print('@x' + playerTrait.coins.toString().padStart(2, '0'), context, 96, LINE2);
+ font.print('×' + playerTrait.coins.toString().padStart(2, '0'), context, 96, LINE2);
- font.print('WORLD', context, 152, LINE1);
- font.print(level.name, context, 160, LINE2);
+ font.print('WORLD', context, 144, LINE1);
+ font.print(playerTrait.world, context, 152, LINE2);
- font.print('TIME', context, 208, LINE1);
- font.print(timerTrait.currentTime.toFixed().toString().padStart(3, '0'), context, 216, LINE2);
+ font.print('TIME', context, 200, LINE1);
+ font.print(timerTrait.currentTime.toFixed().toString().padStart(3, '0'), context, 208, LINE2);
};
}
diff --git a/public/js/layers/player-progress.js b/public/js/layers/player-progress.js
index 25d67b3a..45a326bc 100644
--- a/public/js/layers/player-progress.js
+++ b/public/js/layers/player-progress.js
@@ -1,5 +1,5 @@
-import Player from "../traits/Player.js";
import {findPlayers} from "../player.js";
+import Player from "../traits/Player.js";
function getPlayer(entities) {
for (const entity of findPlayers(entities)) {
@@ -20,12 +20,12 @@ export function createPlayerProgressLayer(font, level) {
const player = entity.traits.get(Player);
font.print('WORLD ' + level.name, context, size * 12, size * 12);
- font.print('x ' + player.lives.toString().padStart(3, ' '),
+ font.print('×' + player.lives.toString().padStart(3, ' '),
context, size * 16, size * 16);
spriteBufferContext.clearRect(0, 0,
spriteBuffer.width, spriteBuffer.height);
entity.draw(spriteBufferContext);
- context.drawImage(spriteBuffer, size * 12, size * 15);
+ context.drawImage(spriteBuffer, size * 13, size * 15);
};
}
diff --git a/public/js/layers/sprites.js b/public/js/layers/sprites.js
index 88360fd1..6608b56d 100644
--- a/public/js/layers/sprites.js
+++ b/public/js/layers/sprites.js
@@ -6,6 +6,10 @@ export function createSpriteLayer(entities, width = 64, height = 64) {
return function drawSpriteLayer(context, camera) {
entities.forEach(entity => {
+ if (!entity.draw) {
+ return;
+ }
+
spriteBufferContext.clearRect(0, 0, width, height);
entity.draw(spriteBufferContext);
diff --git a/public/js/layers/text.js b/public/js/layers/text.js
index 64c17d5f..80f904c4 100644
--- a/public/js/layers/text.js
+++ b/public/js/layers/text.js
@@ -1,11 +1,11 @@
export function createTextLayer(font, text) {
const size = font.size;
-
return function drawText(context) {
const textW = text.length;
const screenW = Math.floor(context.canvas.width / size);
+ const screenH = Math.floor(context.canvas.height / size);
const x = screenW / 2 - textW / 2;
- const y = 12;
+ const y = screenH / 2;
font.print(text, context, x * size, y * size);
};
}
diff --git a/public/js/loaders/font.js b/public/js/loaders/font.js
index b1266850..28070c5e 100644
--- a/public/js/loaders/font.js
+++ b/public/js/loaders/font.js
@@ -1,7 +1,7 @@
import {loadImage} from '../loaders.js';
import SpriteSheet from '../SpriteSheet.js';
-const CHARS = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+const CHARS = ' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ©!-×.';
class Font {
constructor(sprites, size) {
@@ -10,7 +10,7 @@ class Font {
}
print(text, context, x, y) {
- [...text].forEach((char, pos) => {
+ [...text.toUpperCase()].forEach((char, pos) => {
this.sprites.draw(char, context, x + pos * this.size, y);
});
}
diff --git a/public/js/loaders/level.js b/public/js/loaders/level.js
index 1c25eb0f..f2e3b477 100644
--- a/public/js/loaders/level.js
+++ b/public/js/loaders/level.js
@@ -1,5 +1,6 @@
-import {Matrix} from '../math.js';
+import {Matrix, Vec2} from '../math.js';
import Entity from '../Entity.js';
+import Trait from '../Trait.js';
import LevelTimer from '../traits/LevelTimer.js';
import Trigger from '../traits/Trigger.js';
import Level from '../Level.js';
@@ -9,16 +10,33 @@ import {loadMusicSheet} from './music.js';
import {loadSpriteSheet} from './sprite.js';
import {loadJSON} from '../loaders.js';
-function createTimer() {
- const timer = new Entity();
- timer.addTrait(new LevelTimer());
- return timer;
-}
+function createSpawner() {
+ class Spawner extends Trait {
+ constructor() {
+ super();
+ this.entities = [];
+ this.offsetX = 64;
+ }
+
+ addEntity(entity) {
+ this.entities.push(entity);
+ this.entities.sort((a, b) => a.pos.x < b.pos.x ? -1 : 1);
+ }
+
+ update(entity, gameContext, level) {
+ const cameraMaxX = level.camera.pos.x + level.camera.size.x + this.offsetX;
+ while (this.entities[0]) {
+ if (cameraMaxX > this.entities[0].pos.x) {
+ level.entities.add(this.entities.shift());
+ } else {
+ break;
+ }
+ }
+
+ }
+ }
-function createTrigger() {
- const entity = new Entity();
- entity.addTrait(new Trigger());
- return entity;
+ return new Spawner();
}
function loadPattern(name) {
@@ -26,9 +44,6 @@ function loadPattern(name) {
}
function setupBehavior(level) {
- const timer = createTimer();
- level.entities.add(timer);
-
level.events.listen(LevelTimer.EVENT_TIMER_OK, () => {
level.music.playTheme();
});
@@ -37,25 +52,62 @@ function setupBehavior(level) {
});
}
-function setupBackgrounds(levelSpec, level, backgroundSprites, patterns) {
+function setupBackgrounds(levelSpec, level, patterns) {
levelSpec.layers.forEach(layer => {
const grid = createGrid(layer.tiles, patterns);
- const backgroundLayer = createBackgroundLayer(level, grid, backgroundSprites);
- level.comp.layers.push(backgroundLayer);
level.tileCollider.addGrid(grid);
});
}
+function setupCamera(level) {
+ let maxX = 0;
+ let maxTileSize = 0;
+ for (const resolver of level.tileCollider.resolvers) {
+ if (resolver.tileSize > maxTileSize) {
+ maxTileSize = resolver.tileSize;
+ }
+ resolver.matrix.forEach((tile, x, y) => {
+ if (x > maxX) {
+ maxX = x;
+ }
+ });
+ }
+ level.camera.max.x = (maxX + 1) * maxTileSize;
+}
+
+function setupCheckpoints(levelSpec, level) {
+ if (!levelSpec.checkpoints) {
+ level.checkpoints.push(new Vec2(0, 0));
+ return;
+ }
+
+ levelSpec.checkpoints.forEach(([x, y]) => {
+ level.checkpoints.push(new Vec2(x, y));
+ });
+}
+
function setupEntities(levelSpec, level, entityFactory) {
- levelSpec.entities.forEach(({name, pos: [x, y]}) => {
+ const spawner = createSpawner();
+ levelSpec.entities.forEach(({id, name, pos: [x, y], props}) => {
const createEntity = entityFactory[name];
- const entity = createEntity();
+ if (!createEntity) {
+ throw new Error(`No entity ${name}`);
+ }
+
+ const entity = createEntity(props);
entity.pos.set(x, y);
- level.entities.add(entity);
+
+ if (id) {
+ entity.id = id;
+ level.entities.add(entity);
+ } else {
+ spawner.addEntity(entity);
+ }
});
- const spriteLayer = createSpriteLayer(level.entities);
- level.comp.layers.push(spriteLayer);
+ const entityProxy = new Entity();
+ entityProxy.addTrait(spawner);
+ level.entities.add(entityProxy);
}
function setupTriggers(levelSpec, level) {
@@ -65,9 +117,11 @@ function setupTriggers(levelSpec, level) {
for (const triggerSpec of levelSpec.triggers) {
const trigger = new Trigger();
+
trigger.conditions.push((entity, touches, gc, level) => {
level.events.emit(Level.EVENT_TRIGGER, triggerSpec, entity, touches);
});
+
const entity = new Entity();
entity.addTrait(trigger);
entity.size.set(64, 64);
@@ -90,10 +144,21 @@ export function createLevelLoader(entityFactory) {
level.name = name;
level.music.setPlayer(musicPlayer);
- setupBackgrounds(levelSpec, level, backgroundSprites, patterns);
+ setupBackgrounds(levelSpec, level, patterns);
setupEntities(levelSpec, level, entityFactory);
setupTriggers(levelSpec, level);
+ setupCheckpoints(levelSpec, level);
+
setupBehavior(level);
+ setupCamera(level);
+
+ for (const resolver of level.tileCollider.resolvers) {
+ const backgroundLayer = createBackgroundLayer(level, resolver.matrix, backgroundSprites);
+ level.comp.layers.push(backgroundLayer);
+ }
+
+ const spriteLayer = createSpriteLayer(level.entities);
+ level.comp.layers.splice(level.comp.layers.length - 1, 0, spriteLayer);
return level;
});
diff --git a/public/js/main.js b/public/js/main.js
index efa9bfd8..9a0cfb77 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -1,18 +1,20 @@
import Level from './Level.js';
import Timer from './Timer.js';
+import Pipe from './traits/Pipe.js';
import {createLevelLoader} from './loaders/level.js';
import {loadFont} from './loaders/font.js';
import {loadEntities} from './entities.js';
-import {makePlayer, createPlayerEnv, findPlayers} from './player.js';
+import {makePlayer, bootstrapPlayer, resetPlayer, findPlayers} from './player.js';
import {setupKeyboard} from './input.js';
import {createColorLayer} from './layers/color.js';
import {createTextLayer} from './layers/text.js';
import {createCollisionLayer} from './layers/collision.js';
import {createDashboardLayer} from './layers/dashboard.js';
+import { createPlayerProgressLayer } from './layers/player-progress.js';
import SceneRunner from './SceneRunner.js';
import Scene from './Scene.js';
-import { createPlayerProgressLayer } from './layers/player-progress.js';
import TimedScene from './TimedScene.js';
+import { connectEntity } from './traits/Pipe.js';
async function main(canvas) {
const videoContext = canvas.getContext('2d');
@@ -29,49 +31,80 @@ async function main(canvas) {
const sceneRunner = new SceneRunner();
const mario = entityFactory.mario();
- makePlayer(mario, 'MARIO');
+ makePlayer(mario, "MARIO");
+
+ window.mario = mario;
const inputRouter = setupKeyboard(window);
inputRouter.addReceiver(mario);
- async function runLevel(name) {
- const loadScreen = new Scene();
- loadScreen.comp.layers.push(createColorLayer('#000'));
- loadScreen.comp.layers.push(createTextLayer(font, `Loading ${name}...`));
- sceneRunner.addScene(loadScreen);
+ function createLoadingScreen(name) {
+ const scene = new Scene();
+ scene.comp.layers.push(createColorLayer('#000'));
+ scene.comp.layers.push(createTextLayer(font, `Loading ${name}...`));
+ return scene;
+ }
+
+ async function setupLevel(name) {
+ const loadingScreen = createLoadingScreen(name);
+ sceneRunner.addScene(loadingScreen);
sceneRunner.runNext();
const level = await loadLevel(name);
+ bootstrapPlayer(mario, level);
level.events.listen(Level.EVENT_TRIGGER, (spec, trigger, touches) => {
if (spec.type === "goto") {
for (const _ of findPlayers(touches)) {
- runLevel(spec.name);
+ startWorld(spec.name);
return;
}
}
});
- const playerProgressLayer = createPlayerProgressLayer(font, level);
- const dashboardLayer = createDashboardLayer(font, level);
+ level.events.listen(Pipe.EVENT_PIPE_COMPLETE, async pipe => {
+ if (pipe.props.goesTo) {
+ const nextLevel = await setupLevel(pipe.props.goesTo.name);
+ sceneRunner.addScene(nextLevel);
+ sceneRunner.runNext();
+ if (pipe.props.backTo) {
+ console.log(pipe.props);
+ nextLevel.events.listen(Level.EVENT_COMPLETE, async () => {
+ const level = await setupLevel(name);
+ const exitPipe = level.entities.get(pipe.props.backTo);
+ connectEntity(exitPipe, mario);
+ sceneRunner.addScene(level);
+ sceneRunner.runNext();
+ });
+ }
+ } else {
+ level.events.emit(Level.EVENT_COMPLETE);
+ }
+ });
- mario.pos.set(0, 0);
- level.entities.add(mario);
+ level.comp.layers.push(createCollisionLayer(level));
- const playerEnv = createPlayerEnv(mario);
- level.entities.add(playerEnv);
+ const dashboardLayer = createDashboardLayer(font, mario);
+ level.comp.layers.push(dashboardLayer);
+
+ return level;
+ }
+
+ async function startWorld(name) {
+ const level = await setupLevel(name);
+ resetPlayer(mario, name);
+
+ const playerProgressLayer = createPlayerProgressLayer(font, level);
+ const dashboardLayer = createDashboardLayer(font, mario);
const waitScreen = new TimedScene();
- waitScreen.countDown = 2;
+ waitScreen.countDown = 4;
waitScreen.comp.layers.push(createColorLayer('#000'));
waitScreen.comp.layers.push(dashboardLayer);
waitScreen.comp.layers.push(playerProgressLayer);
- sceneRunner.addScene(waitScreen);
- level.comp.layers.push(createCollisionLayer(level));
- level.comp.layers.push(dashboardLayer);
+ sceneRunner.addScene(waitScreen);
sceneRunner.addScene(level);
-
sceneRunner.runNext();
}
@@ -80,17 +113,19 @@ async function main(canvas) {
videoContext,
entityFactory,
deltaTime: null,
+ tick: 0,
};
const timer = new Timer(1/60);
timer.update = function update(deltaTime) {
+ gameContext.tick++;
gameContext.deltaTime = deltaTime;
sceneRunner.update(gameContext);
}
timer.start();
- runLevel('debug-progression');
+ startWorld('1-1');
}
const canvas = document.getElementById('screen');
diff --git a/public/js/math.js b/public/js/math.js
index 9e0b9506..93c301fa 100644
--- a/public/js/math.js
+++ b/public/js/math.js
@@ -45,8 +45,34 @@ export class Vec2 {
this.y = vec2.y;
}
+ equals(vec2) {
+ return this.x === vec2.x && this.y === vec2.y;
+ }
+
+ distance(v) {
+ const dx = this.x - v.x, dy = this.y - v.y;
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
set(x, y) {
this.x = x;
this.y = y;
}
}
+
+export function clamp(value, min, max) {
+ if (value > max) {
+ return max;
+ }
+ if (value < min) {
+ return min;
+ }
+ return value
+}
+
+export const Direction = {
+ UP: new Vec2(0, -1),
+ DOWN: new Vec2(0, 1),
+ RIGHT: new Vec2(1, 0),
+ LEFT: new Vec2(-1, 0),
+};
diff --git a/public/js/player.js b/public/js/player.js
index 511a7f4b..33c69666 100644
--- a/public/js/player.js
+++ b/public/js/player.js
@@ -1,20 +1,24 @@
-import Entity from './Entity.js';
import Player from './traits/Player.js';
-import PlayerController from './traits/PlayerController.js';
-
-export function createPlayerEnv(playerEntity) {
- const playerEnv = new Entity();
- const playerControl = new PlayerController();
- playerControl.checkpoint.set(64, 64);
- playerControl.setPlayer(playerEntity);
- playerEnv.addTrait(playerControl);
- return playerEnv;
-}
+import LevelTimer from './traits/LevelTimer.js';
export function makePlayer(entity, name) {
const player = new Player();
- player.name = name;
+ player.name = "MARIO";
entity.addTrait(player);
+
+ const timer = new LevelTimer();
+ entity.addTrait(timer);
+}
+
+export function resetPlayer(entity, worldName) {
+ entity.traits.get(LevelTimer).reset();
+ entity.traits.get(Player).world = worldName;
+}
+
+export function bootstrapPlayer(entity, level) {
+ entity.traits.get(LevelTimer).hurryEmitted = null;
+ entity.pos.copy(level.checkpoints[0]);
+ level.entities.add(entity);
}
export function* findPlayers(entities) {
diff --git a/public/js/tiles/brick.js b/public/js/tiles/brick.js
index 64fff1a8..fcc59104 100644
--- a/public/js/tiles/brick.js
+++ b/public/js/tiles/brick.js
@@ -1,8 +1,40 @@
-import {Sides} from '../Entity.js';
+import { Vec2 } from '../math.js';
+import { Sides } from '../Entity.js';
import Player from "../traits/Player.js";
-function handleX({entity, match}) {
+function centerEntity(entity, pos) {
+ entity.pos.x = pos.x - entity.size.x / 2;
+ entity.pos.y = pos.y - entity.size.y / 2;
+}
+
+function getMatchCenter(match) {
+ return new Vec2(
+ match.x1 + ((match.x2 - match.x1) / 2),
+ match.y1 + ((match.y2 - match.y1) / 2),
+ );
+}
+
+function addShrapnel(level, gameContext, match) {
+ const center = getMatchCenter(match);
+ const bricks = [];
+ for (let i = 0; i < 4; i++) {
+ const brick = gameContext.entityFactory.brickShrapnel();
+ centerEntity(brick, center);
+ level.entities.add(brick);
+ bricks.push(brick);
+ }
+
+ const spreadH = 60;
+ const spreadV = 400;
+ bricks[0].sounds.add('break');
+ bricks[0].vel.set(-spreadH, -spreadV * 1.2);
+ bricks[1].vel.set(-spreadH, -spreadV);
+ bricks[2].vel.set(spreadH, -spreadV * 1.2);
+ bricks[3].vel.set(spreadH, -spreadV);
+}
+
+function handleX({entity, match}) {
if (entity.vel.x > 0) {
if (entity.bounds.right > match.x1) {
entity.obstruct(Sides.RIGHT, match);
@@ -23,11 +55,7 @@ function handleY({entity, match, resolver, gameContext, level}) {
if (entity.traits.has(Player)) {
const grid = resolver.matrix;
grid.delete(match.indexX, match.indexY);
-
- const goomba = gameContext.entityFactory.goomba();
- goomba.vel.set(50, -400);
- goomba.pos.set(entity.pos.x, match.y1);
- level.entities.add(goomba);
+ addShrapnel(level, gameContext, match);
}
if (entity.bounds.top < match.y2) {
diff --git a/public/js/tiles/coin.js b/public/js/tiles/coin.js
index 2a1335aa..196c5e12 100644
--- a/public/js/tiles/coin.js
+++ b/public/js/tiles/coin.js
@@ -1,6 +1,9 @@
+import Player from "../traits/Player.js";
+
function handle({entity, match, resolver}) {
- if (entity.player) {
- entity.player.addCoins(1);
+ const player = entity.traits.get(Player);
+ if (player) {
+ player.addCoins(1);
const grid = resolver.matrix;
grid.delete(match.indexX, match.indexY);
}
diff --git a/public/js/traits/Killable.js b/public/js/traits/Killable.js
index 41268e69..f0b60725 100644
--- a/public/js/traits/Killable.js
+++ b/public/js/traits/Killable.js
@@ -1,4 +1,3 @@
-import {Sides} from '../Entity.js';
import Trait from '../Trait.js';
export default class Killable extends Trait {
diff --git a/public/js/traits/LevelTimer.js b/public/js/traits/LevelTimer.js
index 98c10eef..34d46ba9 100644
--- a/public/js/traits/LevelTimer.js
+++ b/public/js/traits/LevelTimer.js
@@ -1,19 +1,30 @@
import Trait from '../Trait.js';
+const MARK = Symbol('level timer earmark');
+
export default class LevelTimer extends Trait {
static EVENT_TIMER_HURRY = Symbol('timer hurry');
static EVENT_TIMER_OK = Symbol('timer ok');
constructor() {
super();
- this.totalTime = 300;
+ this.totalTime = 400;
this.currentTime = this.totalTime;
this.hurryTime = 100;
this.hurryEmitted = null;
}
+ reset() {
+ this.currentTime = this.totalTime;
+ }
+
update(entity, {deltaTime}, level) {
- this.currentTime -= deltaTime * 2;
+ this.currentTime -= deltaTime * 2.5;
+
+ if (!level[MARK]) {
+ this.hurryEmitted = null;
+ }
+
if (this.hurryEmitted !== true && this.currentTime < this.hurryTime) {
level.events.emit(LevelTimer.EVENT_TIMER_HURRY);
this.hurryEmitted = true;
@@ -22,5 +33,7 @@ export default class LevelTimer extends Trait {
level.events.emit(LevelTimer.EVENT_TIMER_OK);
this.hurryEmitted = false;
}
+
+ level[MARK] = true;
}
}
diff --git a/public/js/traits/LifeLimit.js b/public/js/traits/LifeLimit.js
new file mode 100644
index 00000000..703b9e36
--- /dev/null
+++ b/public/js/traits/LifeLimit.js
@@ -0,0 +1,16 @@
+import Trait from '../Trait.js';
+
+export default class LifeLimit extends Trait {
+ constructor() {
+ super();
+ this.time = 2;
+ }
+
+ update(entity, _, level) {
+ if (entity.lifetime > this.time) {
+ this.queue(() => {
+ level.entities.delete(entity);
+ });
+ }
+ }
+}
diff --git a/public/js/traits/Pipe.js b/public/js/traits/Pipe.js
new file mode 100644
index 00000000..5a1e1501
--- /dev/null
+++ b/public/js/traits/Pipe.js
@@ -0,0 +1,103 @@
+import { Vec2, Direction } from '../math.js';
+import { Sides, Align } from '../Entity.js';
+import Trait from '../Trait.js';
+import PipeTraveller from './PipeTraveller.js';
+
+function createTravellerState() {
+ return {
+ time: 0,
+ start: new Vec2(),
+ end: new Vec2(),
+ };
+}
+
+export function connectEntity(pipeEntity, travellerEntity) {
+ const pipeTrait = pipeEntity.traits.get(Pipe);
+ Align.center(pipeEntity, travellerEntity);
+ if (pipeTrait.direction.equals(Direction.UP)) {
+ Align.bottom(pipeEntity, travellerEntity);
+ }
+ else if (pipeTrait.direction.equals(Direction.DOWN)) {
+ Align.top(pipeEntity, travellerEntity);
+ }
+ else if (pipeTrait.direction.equals(Direction.LEFT)) {
+ Align.right(pipeEntity, travellerEntity);
+ }
+ else if (pipeTrait.direction.equals(Direction.RIGHT)) {
+ Align.left(pipeEntity, travellerEntity);
+ }
+ pipeTrait.addTraveller(pipeEntity, travellerEntity);
+}
+
+export default class Pipe extends Trait {
+ static EVENT_PIPE_COMPLETE = Symbol('pipe complete');
+
+ constructor() {
+ super();
+ this.duration = 1;
+ this.direction = new Vec2(0, 0);
+ this.travellers = new Map();
+ }
+
+ addTraveller(pipe, traveller) {
+
+ const pipeTraveller = traveller.traits.get(PipeTraveller);
+ pipeTraveller.distance.set(0, 0);
+
+ const state = createTravellerState();
+ state.start.copy(traveller.pos);
+ state.end.copy(traveller.pos);
+ state.end.x += this.direction.x * pipe.size.x;
+ state.end.y += this.direction.y * pipe.size.y;
+ this.travellers.set(traveller, state);
+ }
+
+ collides(pipe, traveller) {
+ if (!traveller.traits.has(PipeTraveller)) {
+ return;
+ }
+
+ if (this.travellers.has(traveller)) {
+ return;
+ }
+
+ if (traveller.traits.get(PipeTraveller).direction.equals(this.direction)) {
+ const tBounds = traveller.bounds;
+ const pBounds = pipe.bounds;
+ if (this.direction.x &&
+ (tBounds.top < pBounds.top || tBounds.bottom > pBounds.bottom)) {
+ return;
+ }
+ if (this.direction.y &&
+ (tBounds.left < pBounds.left || tBounds.right > pBounds.right)) {
+ return;
+ }
+ pipe.sounds.add('pipe');
+ this.addTraveller(pipe, traveller);
+ }
+ }
+
+ update(pipe, gameContext, level) {
+ const {deltaTime} = gameContext;
+ for (const [traveller, state] of this.travellers.entries()) {
+ state.time += deltaTime;
+ const progress = state.time / this.duration;
+ traveller.pos.x = state.start.x + (state.end.x - state.start.x) * progress;
+ traveller.pos.y = state.start.y + (state.end.y - state.start.y) * progress;
+ traveller.vel.set(0, 0);
+
+ const pipeTraveller = traveller.traits.get(PipeTraveller);
+ pipeTraveller.movement.copy(this.direction);
+ pipeTraveller.distance.x = traveller.pos.x - state.start.x;
+ pipeTraveller.distance.y = traveller.pos.y - state.start.y;
+
+ if (state.time > this.duration) {
+ this.travellers.delete(traveller);
+ pipeTraveller.movement.set(0, 0);
+ pipeTraveller.distance.set(0, 0);
+
+ level.events.emit(Pipe.EVENT_PIPE_COMPLETE, pipe, traveller);
+ }
+ }
+ }
+}
diff --git a/public/js/traits/PipeTraveller.js b/public/js/traits/PipeTraveller.js
new file mode 100644
index 00000000..8144c815
--- /dev/null
+++ b/public/js/traits/PipeTraveller.js
@@ -0,0 +1,11 @@
+import { Vec2 } from '../math.js';
+import Trait from '../Trait.js';
+
+export default class PipeTraveller extends Trait {
+ constructor() {
+ super();
+ this.direction = new Vec2(0, 0);
+ this.movement = new Vec2(0, 0);
+ this.distance = new Vec2(0, 0);
+ }
+}
diff --git a/public/js/traits/Player.js b/public/js/traits/Player.js
index 3055b4a2..ead8222e 100644
--- a/public/js/traits/Player.js
+++ b/public/js/traits/Player.js
@@ -7,6 +7,7 @@ export default class Player extends Trait {
constructor() {
super();
this.name = "UNNAMED";
+ this.world = "UNKNOWN";
this.coins = 0;
this.lives = 3;
this.score = 0;
diff --git a/public/js/traits/PlayerController.js b/public/js/traits/PlayerController.js
deleted file mode 100644
index 6ec88832..00000000
--- a/public/js/traits/PlayerController.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import Trait from '../Trait.js';
-import {Vec2} from '../math.js';
-
-export default class PlayerController extends Trait {
- constructor() {
- super();
- this.checkpoint = new Vec2(0, 0);
- this.player = null;
- }
-
- setPlayer(entity) {
- this.player = entity;
- }
-
- update(entity, {deltaTime}, level) {
- if (!level.entities.has(this.player)) {
- this.player.killable.revive();
- this.player.pos.set(this.checkpoint.x, this.checkpoint.y);
- level.entities.add(this.player);
- }
- }
-}
diff --git a/public/js/traits/Pole.js b/public/js/traits/Pole.js
new file mode 100644
index 00000000..fdf228eb
--- /dev/null
+++ b/public/js/traits/Pole.js
@@ -0,0 +1,69 @@
+import { Vec2, Direction } from '../math.js';
+import { Sides, Align } from '../Entity.js';
+import Trait from '../Trait.js';
+import PoleTraveller from './PoleTraveller.js';
+
+function createTravellerState() {
+ return {
+ current: new Vec2(),
+ goal: new Vec2(),
+ done: false,
+ };
+}
+
+export default class Pole extends Trait {
+ constructor() {
+ super();
+ this.velocity = 100;
+ this.travellers = new Map();
+ }
+
+ addTraveller(pole, traveller) {
+ pole.sounds.add('ride');
+
+ const poleTraveller = traveller.traits.get(PoleTraveller);
+ poleTraveller.distance = 0;
+
+ const state = createTravellerState();
+ state.current.x = pole.bounds.meridian;
+ state.current.y = traveller.bounds.bottom;
+ state.goal.x = state.current.x;
+ state.goal.y = pole.bounds.bottom;
+ this.travellers.set(traveller, state);
+ }
+
+ collides(pole, traveller) {
+ if (!traveller.traits.has(PoleTraveller)) {
+ return;
+ }
+
+ if (this.travellers.has(traveller)) {
+ return;
+ }
+
+ this.addTraveller(pole, traveller);
+ }
+
+ update(pole, gameContext, level) {
+ const {deltaTime} = gameContext;
+ const distance = this.velocity * deltaTime;
+ for (const [traveller, state] of this.travellers.entries()) {
+ if (!state.done) {
+ state.current.y += distance;
+ traveller.bounds.right = state.current.x;
+ traveller.bounds.bottom = state.current.y;
+
+ const poleTraveller = traveller.traits.get(PoleTraveller);
+ poleTraveller.distance += distance;
+
+ if (traveller.bounds.bottom > state.goal.y) {
+ state.done = true;
+ traveller.bounds.bottom = state.goal.y;
+ poleTraveller.distance = 0;
+ }
+ } else if (!pole.bounds.overlaps(traveller.bounds)) {
+ this.travellers.delete(traveller);
+ }
+ }
+ }
+}
diff --git a/public/js/traits/PoleTraveller.js b/public/js/traits/PoleTraveller.js
new file mode 100644
index 00000000..0ea40c9d
--- /dev/null
+++ b/public/js/traits/PoleTraveller.js
@@ -0,0 +1,9 @@
+import { Vec2 } from '../math.js';
+import Trait from '../Trait.js';
+
+export default class PoleTraveller extends Trait {
+ constructor() {
+ super();
+ this.distance = 0;
+ }
+}
diff --git a/public/js/traits/Stomper.js b/public/js/traits/Stomper.js
index 8e2475fc..89b13207 100644
--- a/public/js/traits/Stomper.js
+++ b/public/js/traits/Stomper.js
@@ -1,5 +1,5 @@
-import {Sides} from '../Entity.js';
import Trait from '../Trait.js';
+import Killable from './Killable.js';
export default class Stomper extends Trait {
static EVENT_STOMP = Symbol('stomp');
@@ -15,7 +15,7 @@ export default class Stomper extends Trait {
}
collides(us, them) {
- if (!them.killable || them.killable.dead) {
+ if (!them.traits.has(Killable) || them.traits.get(Killable).dead) {
return;
}
diff --git a/public/js/traits/Velocity.js b/public/js/traits/Velocity.js
index 3b744a2b..7f519b5d 100644
--- a/public/js/traits/Velocity.js
+++ b/public/js/traits/Velocity.js
@@ -1,10 +1,6 @@
import Trait from '../Trait.js';
export default class Velocity extends Trait {
- constructor() {
- super();
- }
-
update(entity, {deltaTime}, level) {
entity.pos.x += entity.vel.x * deltaTime;
entity.pos.y += entity.vel.y * deltaTime;
diff --git a/public/levels/1-1.json b/public/levels/1-1.json
index b9f2130b..ba9c3159 100644
--- a/public/levels/1-1.json
+++ b/public/levels/1-1.json
@@ -2,267 +2,292 @@
"spriteSheet": "overworld",
"musicSheet": "overworld",
"patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
"layers": [
{
"tiles": [
{
- "name": "sky",
+ "style": "sky",
"ranges": [
- [
- 0, 212,
- 0, 13
- ]
+ [0, 212, 0, 15]
]
},
{
- "name": "ground",
- "type": "ground",
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 69, 13, 2],
+ [71, 15, 13, 2],
+ [89, 64, 13, 2],
+ [155, 57, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-single",
"ranges": [
- [
- 0, 212,
- 13, 2
- ]
+ [8, 3],
+ [19, 2],
+ [56, 3],
+ [67, 2],
+ [104, 3],
+ [115, 2],
+ [152, 3],
+ [163, 2],
+ [200, 3]
]
},
{
- "name": "sky",
+ "pattern": "cloud-double",
"ranges": [
- [
- 75, 2,
- 13, 2
- ],
- [
- 92, 2,
- 13, 2
- ],
- [
- 157, 2,
- 13, 2
- ]
+ [36, 2],
+ [84, 2],
+ [132, 2],
+ [180, 2]
]
},
{
- "name": "ground",
- "type": "ground",
+ "pattern": "cloud-triple",
"ranges": [
- [
- 5, 3,
- 9, 1
- ],
- [
- 29, 5
- ],
- [
- 5, 7,
- 9
- ],
- [
- 12, 6,
- 11, 1
- ],
- [
- 2, 1,
- 11, 1
- ],
- [
- 10, 2,
- 10, 1
- ],
- [
- 10, 2,
- 10
- ],
- [
- 9, 1,
- 0, 7
- ]
+ [27, 3],
+ [75, 3],
+ [123, 3],
+ [171, 3]
]
- }
- ]
- },
- {
- "tiles": [
+ },
{
- "name": "bricks",
- "type": "brick",
+ "pattern": "bush-single",
"ranges": [
- [
- 27, 5,
- 9
- ],
- [
- 83, 3,
- 9
- ],
- [
- 86, 6,
- 5
- ],
- [
- 96, 3,
- 5
- ],
- [
- 99, 9
- ],
- [
- 105, 2,
- 9
- ],
- [
- 123, 5
- ],
- [
- 126, 3,
- 5
- ],
- [
- 132, 4,
- 5
- ],
- [
- 133, 2,
- 9
- ],
- [
- 171, 4,
- 9
- ]
+ [23, 12],
+ [71, 12],
+ [119, 12],
+ [167, 12]
]
},
{
- "name": "chance",
- "type": "ground",
+ "pattern": "bush-double",
"ranges": [
- [2, 2],
-
+ [41, 12],
+ [89, 12],
+ [137, 12]
+ ]
+ },
+ {
+ "pattern": "bush-triple",
+ "ranges": [
+ [11, 12],
+ [59, 12],
+ [107, 12]
+ ]
+ },
+ {
+ "style": "bush-3",
+ "ranges": [
+ [159, 12],
+ [207, 12]
+ ]
+ },
+ {
+ "pattern": "hill-small",
+ "ranges": [
+ [16, 11],
+ [64, 11],
+ [112, 11],
+ [160, 11],
+ [208, 11]
+ ]
+ },
+ {
+ "pattern": "hill-large",
+ "ranges": [
+ [0, 10],
+ [48, 10],
+ [96, 10],
+ [144, 10],
+ [192, 10]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [64, 8]
+ ]
+ },
+ {
+ "style": "bricks-top",
+ "behavior": "brick",
+ "ranges": [
+ [20, 5, 9],
+ [77, 3, 9],
+ [80, 8, 5],
+ [91, 4, 5],
+ [94, 9],
+ [100, 2, 9],
+ [118, 9],
+ [121, 3, 5],
+ [128, 4, 5],
+ [129, 2, 9],
+ [168, 4, 9]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [16, 9],
+ [22, 5],
+ [21, 9],
[23, 9],
- [28, 9],
- [30, 9],
- [29, 5],
- [84, 9],
- [99, 5],
-
- [114, 5],
- [111, 9],
- [114, 9],
- [117, 9],
-
- [133, 2, 5],
-
- [173, 9]
-
+ [78, 9],
+ [94, 5],
+ [106, 9],
+ [109, 5],
+ [109, 9],
+ [112, 9],
+ [129, 2, 5],
+ [170, 9]
]
},
{
- "name": "chocolate",
- "type": "ground",
+ "pattern": "chocolate-stairs-4h",
"ranges": [
- [141, 1, 9],
- [140, 2, 10],
- [139, 3, 11],
- [138, 4, 12],
-
- [144, 1, 9],
- [144, 2, 10],
- [144, 3, 11],
- [144, 4, 12],
-
- [155, 2, 9],
- [154, 3, 10],
- [153, 4, 11],
- [152, 5, 12],
-
- [159, 1, 9],
- [159, 2, 10],
- [159, 3, 11],
- [159, 4, 12],
-
- [191, 2, 5],
- [190, 3, 6],
- [189, 4, 7],
- [188, 5, 8],
- [187, 6, 9],
- [186, 7, 10],
- [185, 8, 11],
- [184, 9, 12]
+ [134, 9],
+ [148, 9]
]
},
{
- "pattern": "pipe-2h",
+ "pattern": "chocolate-stairs-4h-reverse",
"ranges": [
- [35, 11],
- [167, 11],
- [182, 11]
+ [140, 9],
+ [155, 9]
]
},
{
- "pattern": "pipe-3h",
+ "pattern": "chocolate-stairs-final",
"ranges": [
- [45, 10]
+ [181, 5]
]
},
{
- "pattern": "pipe-4h",
+ "pattern": "chocolate-stack-4h",
"ranges": [
- [53, 9],
- [64, 9]
+ [152, 1, 9]
]
},
{
- "pattern": "cloud-single",
+ "pattern": "flag-pole-green",
"ranges": [
- [2, 2],
- [25, 2],
- [35, 3],
- [44, 2],
- [64, 3],
- [74, 2],
- [80, 3],
- [90, 2],
- [108, 3],
- [118, 2],
- [128, 3],
- [138, 2]
+ [198, 2]
]
- }
- ]
- },
- {
- "tiles": [
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [202, 8]
+ ]
+ },
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [28, 11],
+ [163, 11],
+ [179, 11]
+ ]
+ },
+ {
+ "pattern": "pipe-3h",
+ "ranges": [
+ [38, 10]
+ ]
+ },
{
- "pattern": "cannon-2h",
+ "pattern": "pipe-4h",
"ranges": [
- [
- 6, 7
- ]
+ [46, 9],
+ [57, 9]
]
}
+
]
}
],
"entities": [
{
- "name": "koopa",
- "pos": [260, 0]
+ "name": "goomba-brown",
+ "pos": [352, 192]
},
{
- "name": "goomba",
- "pos": [220, 0]
+ "name": "goomba-brown",
+ "pos": [640, 192]
},
{
- "name": "cannon",
- "pos": [96, 112]
- }
- ],
-
- "triggers": [
+ "name": "goomba-brown",
+ "pos": [816, 192]
+ },
{
- "type": "goto",
- "name": "1-2",
- "pos": [64, 64]
+ "name": "goomba-brown",
+ "pos": [840, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1280, 64]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1312, 64]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1552, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1576, 192]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [1712, 185]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1824, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1848, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1984, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2008, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2048, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2072, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2784, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2808, 192]
}
- ]
+ ],
+ "triggers": []
}
diff --git a/public/levels/1-2.json b/public/levels/1-2.json
index 3a834882..97f1fc39 100644
--- a/public/levels/1-2.json
+++ b/public/levels/1-2.json
@@ -1,35 +1,239 @@
{
"spriteSheet": "underworld",
"musicSheet": "underworld",
- "patternSheet": "overworld-pattern",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [40, 48]
+ ],
"layers": [
{
"tiles": [
{
- "name": "sky",
+ "style": "sky",
"ranges": [
- [
- 0, 212,
- 0, 13
- ]
+ [0, 192, 0, 15]
]
},
{
- "name": "ground",
- "type": "ground",
+ "style": "ground",
+ "behavior": "ground",
"ranges": [
- [
- 0, 212,
- 13, 2
- ]
+ [0, 80, 13, 2],
+ [83, 37, 13, 2],
+ [122, 2, 13, 2],
+ [126, 12, 13, 2],
+ [145, 8, 13, 2],
+ [160, 32, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1 ,2, 11],
+ [6, 132 ,2],
+ [39, 1, 7, 3],
+ [41, 1, 7, 3],
+ [44, 1, 7, 3],
+ [46, 1, 8, 2],
+ [40, 9],
+ [42, 2, 7],
+ [45, 9],
+ [52, 2, 5, 5],
+ [54, 2, 3, 2],
+ [54, 2, 9, 3],
+ [58, 6, 3, 2],
+ [62, 2, 5, 5],
+ [58, 4, 9],
+ [66, 4, 3, 2],
+ [67, 1, 5, 5],
+ [68, 2, 9],
+ [72, 2, 5, 5],
+ [76, 4, 3, 2],
+ [76, 4, 9],
+ [84, 6, 7, 2],
+ [122, 2, 11, 2],
+ [145, 6, 8],
+ [161, 7, 2],
+ [160, 17, 10, 3],
+ [170, 7, 2, 8],
+ [177, 10, 2],
+ [190, 2, 2, 11]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [10, 5, 9]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [41, 4, 5],
+ [40, 8],
+ [45, 8],
+ [58, 4, 8],
+ [68, 8],
+ [84, 6, 5]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [29, 8],
+ [46, 7],
+ [69, 8],
+ [73, 8],
+ [89, 2],
+ [150, 8]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [17, 12]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [19, 11],
+ [33, 11]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [21, 10],
+ [27, 10],
+ [31, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-4h",
+ "ranges": [
+ [23, 9],
+ [25, 9],
+ [137, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-3h",
+ "ranges": [
+ [103, 10],
+ [178, 10],
+ [182, 10],
+ [186, 10]
+ ]
+ },
+ {
+ "pattern": "pipe-4h",
+ "ranges": [
+ [109, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [115, 11]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-4h",
+ "ranges": [
+ [133, 9]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-8h",
+ "ranges": [
+ [166, 2]
]
}
]
}
],
- "entities": [],
+ "entities": [
+ {
+ "name": "goomba-blue",
+ "pos": [256, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [272, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [464, 192]
+ },
+ {
+ "name": "koopa-blue",
+ "pos": [704, 185]
+ },
+ {
+ "name": "koopa-blue",
+ "pos": [728, 185]
+ },
+ {
+ "name": "koopa-blue",
+ "pos": [944, 185]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [992, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1024, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1168, 64]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1216, 128]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1240, 128]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1548, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1608, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1632, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [1808, 192]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [2160, 144]
+ },
+ {
+ "name": "goomba-blue",
+ "pos": [2184, 128]
+ }
+ ],
"triggers": [
{
diff --git a/public/levels/1-3.json b/public/levels/1-3.json
new file mode 100644
index 00000000..8e3699fb
--- /dev/null
+++ b/public/levels/1-3.json
@@ -0,0 +1,208 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 166, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2],
+ [129, 37, 13, 2]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [59, 10]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [27, 3, 4],
+ [33, 11],
+ [50, 2, 6],
+ [60, 4, 4],
+ [85, 2, 5],
+ [93, 2, 4],
+ [97, 2, 4],
+ [113, 3, 12],
+ [120, 2, 5]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [3, 3],
+ [19, 2],
+ [51, 3],
+ [99, 3],
+ [114, 2],
+ [147, 3],
+ [162, 2]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [9, 7],
+ [35, 7],
+ [38, 6],
+ [46, 11],
+ [57, 7],
+ [76, 11],
+ [83, 7],
+ [86, 6],
+ [94, 11],
+ [124, 11],
+ [131, 7],
+ [134, 6],
+ [142, 11],
+ [153, 7]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [138, 2, 9, 4],
+ [140, 2, 7, 6],
+ [142, 2, 5, 8]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [0, 8]
+ ]
+ },
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [155, 2]
+ ]
+ },
+ {
+ "pattern": "castle-wall-6h",
+ "ranges": [
+ [164, 2, 7]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [152, 2]
+ ]
+ },
+ {
+ "style": "dirt",
+ "ranges": [
+ [19, 2, 13, 2],
+ [25, 6, 10, 5],
+ [27, 3, 6, 3],
+ [33, 1, 13, 2],
+ [36, 3, 9, 6],
+ [41, 5, 5, 10],
+ [51, 2, 14],
+ [60, 3, 14],
+ [61, 2, 6, 7],
+ [66, 3, 14],
+ [71, 1, 10, 5],
+ [77, 4, 7, 8],
+ [99, 2, 12, 3],
+ [105, 6, 8, 7],
+ [114, 1, 14],
+ [117, 2, 10, 5],
+ [123, 2, 10, 5]
+ ]
+ },
+ {
+ "pattern": "grass-top-3w",
+ "ranges": [
+ [32, 12],
+ [70, 9],
+ [113, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-4w",
+ "ranges": [
+ [18, 12],
+ [50, 13],
+ [60, 5],
+ [98, 11],
+ [116, 9],
+ [122, 9]
+ ]
+ },
+ {
+ "pattern": "grass-top-5w",
+ "ranges": [
+ [26, 5],
+ [35, 8],
+ [59, 13],
+ [65, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-6w",
+ "ranges": [
+ [76, 6]
+ ]
+ },
+ {
+ "pattern": "grass-top-7w",
+ "ranges": [
+ [40, 4]
+ ]
+ },
+ {
+ "pattern": "grass-top-8w",
+ "ranges": [
+ [24, 9],
+ [104, 7]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "goomba-brown",
+ "pos": [704, 48]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [736, 48]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1280, 64]
+ }
+ ],
+ "triggers": []
+}
diff --git a/public/levels/1-4.json b/public/levels/1-4.json
new file mode 100644
index 00000000..63504370
--- /dev/null
+++ b/public/levels/1-4.json
@@ -0,0 +1,127 @@
+{
+ "spriteSheet": "castle",
+ "musicSheet": "castle",
+ "patternSheet": "castle-pattern",
+ "checkpoints": [
+ [40, 96]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 161, 0, 15]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 25, 2, 3],
+ [0, 13, 10, 5],
+ [15, 12, 10, 5],
+ [30, 3, 10, 5],
+ [25, 13, 2],
+ [24, 5],
+ [36, 37, 9, 6],
+ [38, 35, 2, 4],
+ [73, 32, 10, 5],
+ [73, 88, 2],
+ [81, 3],
+ [89, 3],
+ [98, 7, 3, 2],
+ [105, 24, 13, 2],
+ [117, 4, 10, 3],
+ [124, 5, 3, 2],
+ [124, 5, 10, 3],
+ [142, 3, 9, 6],
+ [143, 2, 3, 2],
+ [145, 16, 13, 2]
+ ]
+ },
+ {
+ "pattern": "entrance-stairs",
+ "ranges": [
+ [0, 6]
+ ]
+ },
+ {
+ "style": "waves",
+ "ranges": [
+ [13, 2, 12],
+ [27, 3, 13],
+ [33, 3, 13],
+ [129, 13, 13]
+ ]
+ },
+ {
+ "style": "tile-red",
+ "ranges": [
+ [13, 2, 13, 2],
+ [27, 3, 14],
+ [33, 3, 14],
+ [129, 13, 14]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [31, 6]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [107, 9],
+ [108, 5],
+ [110, 9],
+ [111, 5],
+ [113, 9],
+ [114, 5]
+ ]
+ },
+ {
+ "style": "metal-alt",
+ "behavior": "ground",
+ "ranges": [
+ [24, 6],
+ [31, 10],
+ [38, 6],
+ [50, 6],
+ [61, 6],
+ [68, 6],
+ [77, 9],
+ [81, 4],
+ [85, 9],
+ [89, 4],
+ [93, 9]
+ ]
+ },
+ {
+ "pattern": "bridge",
+ "behavior": "ground",
+ "ranges": [
+ [129, 9]
+ ]
+ },
+ {
+ "pattern": "toad",
+ "ranges": [
+ [154, 11]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/2-1.json b/public/levels/2-1.json
new file mode 100644
index 00000000..30fbfe99
--- /dev/null
+++ b/public/levels/2-1.json
@@ -0,0 +1,339 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 214, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 92, 13, 2],
+ [96, 10, 13, 2],
+ [109, 30, 13, 2],
+ [142, 10, 13, 2],
+ [154, 60, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks-top",
+ "behavior": "ground",
+ "ranges": [
+ [15, 3, 9],
+ [28, 4, 5],
+ [68, 9],
+ [69, 4, 5],
+ [81, 5, 5],
+ [92, 4, 5],
+ [125, 4, 5],
+ [161, 9],
+ [164, 5, 5],
+ [172, 5],
+ [185, 2, 9]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [53, 5, 5],
+ [53, 5, 9],
+ [79, 4, 9],
+ [85, 3, 9],
+ [170, 9]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [17, 9],
+ [28, 5],
+ [28, 9],
+ [69, 5],
+ [125, 5],
+ [186, 5]
+ ]
+ },
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [-2, 2]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [18, 3],
+ [27, 2],
+ [45, 2],
+ [66, 3],
+ [75, 2],
+ [93, 2],
+ [114, 3],
+ [123, 2],
+ [141, 2],
+ [162, 3],
+ [171, 2],
+ [189, 2],
+ [210, 3]
+ ]
+ },
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [30, 3],
+ [48, 3],
+ [78, 3],
+ [96, 3],
+ [126, 3],
+ [144, 3],
+ [174, 3],
+ [192, 3]
+ ]
+ },
+
+ {
+ "pattern": "tree-small",
+ "ranges": [
+ [11, 11],
+ [40, 11],
+ [59, 11],
+ [71, 11],
+ [72, 11],
+ [88, 11],
+ [119, 11],
+ [120, 11],
+ [136, 11],
+ [155, 11],
+ [167, 11],
+ [168, 11],
+ [184, 11],
+ [203, 11]
+ ]
+ },
+ {
+ "pattern": "tree-large",
+ "ranges": [
+ [13, 10],
+ [43, 10],
+ [61, 10],
+ [69, 10],
+ [91, 10],
+ [109, 10],
+ [117, 10],
+ [157, 10],
+ [165, 10],
+ [187, 10],
+ [213, 10]
+ ]
+ },
+ {
+ "style": "tree-large-top",
+ "ranges": [
+ [21, 10]
+ ]
+ },
+ {
+ "style": "fence",
+ "ranges": [
+ [14, 4, 12],
+ [38, 2, 12],
+ [41, 12],
+ [62, 4, 12],
+ [86, 2, 12],
+ [89, 12],
+ [110, 4, 12],
+ [134, 2, 12],
+ [137, 12],
+ [158, 4, 12],
+ [182, 2, 12],
+ [185, 12],
+ [209, 12]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-4h",
+ "ranges": [
+ [20, 9]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [35, 11]
+ ]
+ }, {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [154, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-4h",
+ "ranges": [
+ [34, 9]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-5h",
+ "ranges": [
+ [24, 8],
+ [190, 2, 3],
+ [190, 2, 8]
+ ]
+ },
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [115, 11]
+ ]
+ }, {
+ "pattern": "pipe-3h",
+ "ranges": [
+ [126, 10],
+ [176, 10]
+ ]
+ },
+ {
+ "pattern": "pipe-4h",
+ "ranges": [
+ [46, 9],
+ [74, 9],
+ [103, 9],
+ [122, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-5h",
+ "ranges": [
+ [130, 8]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [200, 2]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [204, 8]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "goomba-brown",
+ "pos": [384, 112]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [496, 185]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [528, 185]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [672, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [696, 192]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [880, 121]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [944, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [968, 192]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [1056, 185]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1088, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1088, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1112, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1136, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1392, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1416, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1440, 192]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1640, 128]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1832, 160]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [1920, 192]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [2192, 185]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [2592, 192]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [2616, 185]
+ },
+ {
+ "name": "koopa-green",
+ "pos": [2960, 185]
+ }
+ ],
+ "triggers": []
+}
diff --git a/public/levels/2-2.json b/public/levels/2-2.json
new file mode 100644
index 00000000..5706b7da
--- /dev/null
+++ b/public/levels/2-2.json
@@ -0,0 +1,197 @@
+{
+ "spriteSheet": "underwater",
+ "musicSheet": "underwater",
+ "patternSheet": "underwater-pattern",
+ "checkpoints": [
+ [40, 16]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 192, 0, 3]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "water",
+ "ranges": [
+ [0, 190, 2]
+ ]
+ }
+
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 66, 13, 2],
+ [18, 3, 9],
+ [42, 2, 9],
+ [64, 1, 10, 3],
+ [65, 1, 8, 5],
+ [71, 60, 13, 2],
+ [71, 1, 8, 5],
+ [72, 1, 10, 3],
+ [78, 2, 10, 3],
+ [78, 2, 2, 3],
+ [82, 3, 5],
+ [102, 2, 9],
+ [115, 2, 8],
+ [129, 1, 9, 4],
+ [130, 1, 11, 2],
+ [131, 1, 2, 3],
+ [132, 8, 4],
+ [140, 17, 13, 2],
+ [140, 1, 11, 2],
+ [141, 1, 9, 4],
+ [156, 1, 5, 8],
+ [157, 2, 5],
+ [162, 2, 5],
+ [164, 1, 5, 8],
+ [164, 28, 13, 2],
+ [172, 5, 5],
+ [172, 5, 9],
+ [180, 4, 5],
+ [180, 4, 9],
+ [185, 12],
+ [186, 1, 11, 2],
+ [187, 1, 10, 3],
+ [188, 4, 2, 4],
+ [188, 4, 9, 4],
+ [190, 2, 6, 3]
+
+ ]
+ },
+ {
+ "style": "coral",
+ "behavior": "ground",
+ "ranges": [
+ [11, 1, 10, 3],
+ [33, 1, 8, 5],
+ [42, 1, 7, 2],
+ [50, 1, 9, 4],
+ [83, 1, 3, 2],
+ [89, 1, 10, 3],
+ [102, 1, 5, 4],
+ [120, 1, 9, 4],
+ [147, 1, 11, 2],
+ [149, 1, 10, 3],
+ [173, 1, 3, 2]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [14, 2, 12],
+ [27, 3, 5],
+ [36, 3, 12],
+ [67, 3, 10],
+ [101, 3, 11],
+ [113, 3, 6],
+ [134, 3, 12],
+ [133, 11],
+ [137, 11],
+ [159, 3, 9],
+ [159, 3, 12]
+ ]
+ },
+ {
+ "pattern": "pipe-cap-hor",
+ "ranges": [
+ [189, 1, 7]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": []
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "cheep-slow",
+ "pos": [1184, 112]
+ },
+ {
+ "name": "cheep-slow",
+ "pos": [1376, 160]
+ },
+ {
+ "name": "cheep-slow",
+ "pos": [1664, 160]
+ },
+ {
+ "name": "cheep-fast",
+ "pos": [1184, 144]
+ },
+ {
+ "name": "cheep-fast",
+ "pos": [1616, 64]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [1248, 112]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [1552, 96]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [1872, 80]
+ },
+ {
+ "name": "cheep-fast-wavy",
+ "pos": [2048, 160]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [2098, 176]
+ },
+ {
+ "name": "cheep-slow",
+ "pos": [2192, 128]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [2320, 144]
+ },
+ {
+ "name": "cheep-fast",
+ "pos": [2400, 80]
+ },
+ {
+ "name": "cheep-slow",
+ "pos": [2624, 48]
+ },
+ {
+ "name": "cheep-fast",
+ "pos": [2672, 160]
+ },
+ {
+ "name": "cheep-slow-wavy",
+ "pos": [2800, 128]
+ },
+ {
+ "name": "cheep-fast",
+ "pos": [2928, 64]
+ },
+ {
+ "name": "cheep-fast-wavy",
+ "pos": [2976, 128]
+ }
+ ],
+ "triggers": []
+}
diff --git a/public/levels/2-3.json b/public/levels/2-3.json
new file mode 100644
index 00000000..844216f4
--- /dev/null
+++ b/public/levels/2-3.json
@@ -0,0 +1,216 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 237, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 7, 13, 2],
+ [207, 30, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [3, 3],
+ [18, 2],
+ [51, 3],
+ [66, 2],
+ [99, 3],
+ [114, 2],
+ [147, 3],
+ [162, 2],
+ [195, 3],
+ [210, 2]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [9, 7],
+ [28, 11],
+ [35, 7],
+ [38, 6],
+ [46, 11],
+ [57, 7],
+ [76, 11],
+ [83, 7],
+ [86, 6],
+ [94, 11],
+ [105, 7],
+ [124, 11],
+ [131, 7],
+ [134, 6],
+ [142, 11],
+ [153, 7],
+ [172, 11],
+ [179, 7],
+ [182, 6],
+ [190, 11],
+ [201, 7],
+ [220, 11],
+ [227, 7]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-3h",
+ "ranges": [
+ [10, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-3h-reverse",
+ "ranges": [
+ [194, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [13, 2, 10],
+ [146, 1, 12],
+ [155, 1, 12],
+ [193, 1, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-5h",
+ "ranges": [
+ [31, 1, 10],
+ [47, 1, 10],
+ [63, 1, 10],
+ [68, 1, 10],
+ [79, 1, 10],
+ [84, 1, 10],
+ [95, 1, 10],
+ [127, 1, 10],
+ [143, 1, 10],
+ [159, 1, 10],
+ [168, 1, 10],
+ [183, 1, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-6h",
+ "ranges": [
+ [99, 1, 9],
+ [105, 1, 9]
+ ]
+ },
+ {
+ "pattern": "bridge-green",
+ "ranges": [
+ [15, 16, 9],
+ [32, 15, 9],
+ [48, 15, 9],
+ [69, 10, 9],
+ [85, 10, 9],
+ [100, 5, 8],
+ [122, 3, 9],
+ [128, 15, 9],
+ [147, 8, 11],
+ [160, 8, 9],
+ [171, 2, 9],
+ [175, 2, 9],
+ [179, 2, 9],
+ [184, 9, 9]
+
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [36, 4, 5],
+ [55, 5],
+ [56, 6],
+ [57, 5],
+ [58, 6],
+ [59, 5],
+ [72, 5],
+ [73, 2, 4],
+ [75, 5],
+ [97, 3, 5],
+ [108, 3, 5],
+ [133, 6, 5],
+ [149, 4, 8],
+ [173, 6, 6]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [102, 5]
+ ]
+ },
+ {
+ "style": "dirt",
+ "ranges": [
+ [9, 6, 13, 2],
+ [113, 6, 13, 2],
+ [193, 11, 13, 2]
+ ]
+ },
+ {
+ "pattern": "grass-top-8w",
+ "ranges": [
+ [8, 13],
+ [112, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-13w",
+ "ranges": [
+ [192, 13]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [0, 8]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-final",
+ "ranges": [
+ [208, 5]
+ ]
+ },
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [228, 2]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [225, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/2-4.json b/public/levels/2-4.json
new file mode 100644
index 00000000..ac61ec7f
--- /dev/null
+++ b/public/levels/2-4.json
@@ -0,0 +1,141 @@
+{
+ "spriteSheet": "castle",
+ "musicSheet": "castle",
+ "patternSheet": "castle-pattern",
+ "checkpoints": [
+ [40, 96]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 160, 0, 15]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 2, 3],
+ [0, 16, 10, 5],
+ [18, 2, 9],
+ [22, 3, 7],
+ [27, 2, 9],
+ [32, 2, 10, 5],
+ [34, 49, 2, 4],
+ [34, 46, 13, 2],
+ [37, 36, 9],
+ [80, 4, 10, 5],
+ [92, 7, 10, 5],
+ [93, 6, 2, 3],
+ [99, 10, 13, 2],
+ [108, 1, 10, 3],
+ [111, 2, 10, 5],
+ [115, 5, 10, 5],
+ [115, 13, 2, 3],
+ [120, 8, 13, 2],
+ [122, 2, 10, 3],
+ [126, 2, 10, 3],
+ [128, 32, 2],
+ [141, 3, 9, 6],
+ [142, 2, 3, 3],
+ [144, 16, 13, 2]
+ ]
+ },
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [128, 6, 5]
+ ]
+ },
+ {
+ "style": "metal-alt",
+ "behavior": "ground",
+ "ranges": [
+ [23, 7],
+ [43, 13],
+ [49, 9],
+ [55, 5],
+ [55, 13],
+ [61, 9],
+ [67, 13],
+ [73, 9],
+ [82, 6],
+ [92, 10],
+ [103, 11]
+ ]
+ },
+ {
+ "pattern": "entrance-stairs",
+ "ranges": [
+ [0, 6]
+ ]
+ },
+ {
+ "style": "waves",
+ "ranges": [
+ [16, 16, 13],
+ [109, 2, 13],
+ [113, 2, 13],
+ [128, 13, 13]
+ ]
+ },
+ {
+ "style": "tile-red",
+ "ranges": [
+ [16, 16, 14],
+ [109, 2, 14],
+ [113, 2, 14],
+ [128, 13, 14]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [23, 3]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [102, 3, 8, 1],
+ [102, 3, 12, 1]
+ ]
+ },
+ {
+ "style": "beam-track",
+ "ranges": [
+ [86, 1, 2, 13],
+ [89, 1, 2, 13]
+ ]
+ },
+ {
+ "pattern": "bridge",
+ "behavior": "ground",
+ "ranges": [
+ [128, 9]
+ ]
+ },
+ {
+ "pattern": "toad",
+ "ranges": [
+ [153, 11]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/3-1.json b/public/levels/3-1.json
new file mode 100644
index 00000000..9bc1ee41
--- /dev/null
+++ b/public/levels/3-1.json
@@ -0,0 +1,270 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "tile-black",
+ "ranges": [
+ [0, 214, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 45, 13, 2],
+ [48, 29, 13, 2],
+ [85, 1, 13, 2],
+ [88, 40, 13, 2],
+ [132, 10, 13, 2],
+ [144, 33, 13, 2],
+ [180, 34, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [-2, 2]
+ ]
+ },
+ {
+ "style": "tree-white-small",
+ "ranges": [
+ [136, 11]
+ ]
+ },
+ {
+ "pattern": "tree-white-small",
+ "ranges": [
+ [11, 11],
+ [23, 11],
+ [24, 11],
+ [40, 11],
+ [59, 11],
+ [71, 11],
+ [72, 11],
+ [107, 11],
+ [119, 11],
+ [120, 11],
+ [155, 11],
+ [167, 11],
+ [168, 11],
+ [203, 11]
+ ]
+ },
+ {
+ "pattern": "tree-white-large",
+ "ranges": [
+ [13, 10],
+ [21, 10],
+ [43, 10],
+ [61, 10],
+ [69, 10],
+ [91, 10],
+ [109, 10],
+ [117, 10],
+ [157, 10],
+ [165, 10],
+ [213, 10]
+ ]
+ },
+ {
+ "style": "fence",
+ "ranges": [
+ [14, 4, 12],
+ [41, 12],
+ [62, 4, 12],
+ [110, 4, 12],
+ [134, 2, 12],
+ [158, 4, 12],
+ [182, 12],
+ [209, 12]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [18, 3],
+ [27, 2],
+ [45, 2],
+ [66, 3],
+ [75, 2],
+ [93, 2],
+ [114, 3],
+ [123, 2],
+ [141, 2],
+ [162, 3],
+ [171, 2],
+ [189, 2],
+ [210, 3]
+ ]
+ },
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [30, 3],
+ [48, 3],
+ [78, 3],
+ [96, 3],
+ [126, 3],
+ [144, 3],
+ [174, 3],
+ [192, 3]
+ ]
+ },
+ {
+ "style": "bricks-top",
+ "behavior": "ground",
+ "ranges":[
+ [26, 3, 9],
+ [61, 9],
+ [90, 3, 5],
+ [111, 10, 5],
+ [111, 11, 9],
+ [129, 3, 5],
+ [129, 3, 8],
+ [150, 3, 5],
+ [150, 3, 9],
+ [155, 3, 5],
+ [155, 3, 9],
+ [166, 5, 9]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [16, 9],
+ [19, 8],
+ [22, 8],
+ [113, 5],
+ [117, 5],
+ [151, 5],
+ [151, 9],
+ [156, 5],
+ [156, 9]
+ ]
+ },
+ {
+ "style": "metal",
+ "ranges":[
+ [90, 5],
+ [131, 5],
+ [167, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-chrome-2h",
+ "ranges":[
+ [67, 11]
+ ]
+ },
+ {
+ "pattern": "pipe-chrome-3h",
+ "ranges":[
+ [32, 10],
+ [57, 10]
+ ]
+ },
+ {
+ "pattern": "pipe-chrome-4h",
+ "ranges":[
+ [38, 9],
+ [103, 9]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-2h",
+ "ranges": [
+ [140, 7]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-4h",
+ "ranges": [
+ [73, 9],
+ [136, 9]
+ ]
+ },
+ {
+ "style": "waves",
+ "ranges": [
+ [77, 8, 12],
+ [86, 2, 12]
+ ]
+ },
+ {
+ "style": "tile-light-blue",
+ "ranges": [
+ [77, 8, 13, 2],
+ [86, 2, 13, 2]
+ ]
+ },
+ {
+ "pattern": "bridge-white",
+ "ranges": [
+ [77, 8, 8]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [89, 11]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [174, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-4h",
+ "ranges": [
+ [85, 9],
+ [88, 9],
+ [140, 2, 9]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-6h",
+ "ranges": [
+ [175, 7]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-final",
+ "ranges": [
+ [183, 5]
+ ]
+ },
+ {
+ "pattern": "flag-pole-dark-grey",
+ "ranges": [
+ [200, 2]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [204, 8]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/5-3.json b/public/levels/5-3.json
new file mode 100644
index 00000000..970287c7
--- /dev/null
+++ b/public/levels/5-3.json
@@ -0,0 +1,192 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 166, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2],
+ [129, 37, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [27, 3, 4, 1],
+ [33, 11],
+ [50, 2, 6, 1],
+ [60, 4, 4, 1],
+ [85, 2, 5, 1],
+ [93, 2, 4, 1],
+ [97, 2, 4, 1],
+ [113, 3, 12, 1],
+ [120, 2, 5, 1]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [59, 10]
+ ]
+ },
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [3, 3],
+ [19, 2],
+ [51, 3],
+ [99, 3],
+ [114, 2],
+ [147, 3],
+ [162, 2]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [9, 7],
+ [35, 7],
+ [38, 6],
+ [46, 11],
+ [57, 7],
+ [76, 11],
+ [83, 7],
+ [86, 6],
+ [94, 11],
+ [124, 11],
+ [131, 7],
+ [134, 6],
+ [142, 11],
+ [153, 7]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [138, 2, 9, 4],
+ [140, 2, 7, 6],
+ [142, 2, 5, 8]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [0, 8]
+ ]
+ },
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [155, 2]
+ ]
+ },
+ {
+ "pattern": "castle-wall-6h",
+ "ranges": [
+ [164, 2, 7, 1]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [152, 2]
+ ]
+ },
+ {
+ "style": "dirt",
+ "ranges": [
+ [19, 2, 13, 2],
+ [25, 6, 10, 5],
+ [27, 3, 6, 3],
+ [33, 1, 13, 2],
+ [36, 3, 9, 6],
+ [41, 5, 5, 10],
+ [51, 2, 14],
+ [60, 3, 14],
+ [61, 2, 6, 7],
+ [66, 3, 14],
+ [71, 1, 10, 5],
+ [77, 4, 7, 8],
+ [99, 2, 12, 3],
+ [105, 6, 8, 7],
+ [114, 1, 14],
+ [117, 2, 10, 5],
+ [123, 2, 10, 5]
+ ]
+ },
+ {
+ "pattern": "grass-top-3w",
+ "ranges": [
+ [32, 12],
+ [70, 9],
+ [113, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-4w",
+ "ranges": [
+ [18, 12],
+ [50, 13],
+ [60, 5],
+ [98, 11],
+ [116, 9],
+ [122, 9]
+ ]
+ },
+ {
+ "pattern": "grass-top-5w",
+ "ranges": [
+ [26, 5],
+ [35, 8],
+ [59, 13],
+ [65, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-6w",
+ "ranges": [
+ [76, 6]
+ ]
+ },
+ {
+ "pattern": "grass-top-7w",
+ "ranges": [
+ [40, 4]
+ ]
+ },
+ {
+ "pattern": "grass-top-8w",
+ "ranges": [
+ [24, 9],
+ [104, 7]
+ ]
+ }
+
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/7-2.json b/public/levels/7-2.json
new file mode 100644
index 00000000..591ebc96
--- /dev/null
+++ b/public/levels/7-2.json
@@ -0,0 +1,121 @@
+{
+ "spriteSheet": "underwater",
+ "musicSheet": "underwater",
+ "patternSheet": "underwater-pattern",
+ "checkpoints": [
+ [40, 16]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 192, 0, 3]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "water",
+ "ranges": [
+ [0, 190, 2]
+ ]
+ }
+
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 66, 13, 2],
+ [18, 3, 9],
+ [42, 2, 9],
+ [64, 1, 10, 3],
+ [65, 1, 8, 5],
+ [71, 60, 13, 2],
+ [71, 1, 8, 5],
+ [72, 1, 10, 3],
+ [78, 2, 10, 3],
+ [78, 2, 2, 3],
+ [82, 3, 5],
+ [102, 2, 9],
+ [115, 2, 8],
+ [129, 1, 9, 4],
+ [130, 1, 11, 2],
+ [131, 1, 2, 3],
+ [132, 8, 4],
+ [140, 17, 13, 2],
+ [140, 1, 11, 2],
+ [141, 1, 9, 4],
+ [156, 1, 5, 8],
+ [157, 2, 5],
+ [162, 2, 5],
+ [164, 1, 5, 8],
+ [164, 28, 13, 2],
+ [172, 5, 5],
+ [172, 5, 9],
+ [180, 4, 5],
+ [180, 4, 9],
+ [185, 12],
+ [186, 1, 11, 2],
+ [187, 1, 10, 3],
+ [188, 4, 2, 4],
+ [188, 4, 9, 4],
+ [190, 2, 6, 3]
+
+ ]
+ },
+ {
+ "style": "coral",
+ "behavior": "ground",
+ "ranges": [
+ [11, 1, 10, 3],
+ [33, 1, 8, 5],
+ [42, 1, 7, 2],
+ [50, 1, 9, 4],
+ [83, 1, 3, 2],
+ [89, 1, 10, 3],
+ [102, 1, 5, 4],
+ [120, 1, 9, 4],
+ [147, 1, 11, 2],
+ [149, 1, 10, 3],
+ [173, 1, 3, 2]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [14, 2, 12],
+ [27, 3, 5],
+ [36, 3, 12],
+ [67, 3, 10],
+ [101, 3, 11],
+ [113, 3, 4],
+ [134, 3, 12],
+ [133, 11],
+ [137, 11],
+ [159, 3, 9],
+ [159, 3, 12]
+ ]
+ },
+ {
+ "pattern": "pipe-cap-hor",
+ "ranges": [
+ [189, 1, 7]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/7-3.json b/public/levels/7-3.json
new file mode 100644
index 00000000..844216f4
--- /dev/null
+++ b/public/levels/7-3.json
@@ -0,0 +1,216 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 237, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 7, 13, 2],
+ [207, 30, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [3, 3],
+ [18, 2],
+ [51, 3],
+ [66, 2],
+ [99, 3],
+ [114, 2],
+ [147, 3],
+ [162, 2],
+ [195, 3],
+ [210, 2]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [9, 7],
+ [28, 11],
+ [35, 7],
+ [38, 6],
+ [46, 11],
+ [57, 7],
+ [76, 11],
+ [83, 7],
+ [86, 6],
+ [94, 11],
+ [105, 7],
+ [124, 11],
+ [131, 7],
+ [134, 6],
+ [142, 11],
+ [153, 7],
+ [172, 11],
+ [179, 7],
+ [182, 6],
+ [190, 11],
+ [201, 7],
+ [220, 11],
+ [227, 7]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-3h",
+ "ranges": [
+ [10, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-3h-reverse",
+ "ranges": [
+ [194, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [13, 2, 10],
+ [146, 1, 12],
+ [155, 1, 12],
+ [193, 1, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-5h",
+ "ranges": [
+ [31, 1, 10],
+ [47, 1, 10],
+ [63, 1, 10],
+ [68, 1, 10],
+ [79, 1, 10],
+ [84, 1, 10],
+ [95, 1, 10],
+ [127, 1, 10],
+ [143, 1, 10],
+ [159, 1, 10],
+ [168, 1, 10],
+ [183, 1, 10]
+ ]
+ },
+ {
+ "pattern": "chocolate-stack-6h",
+ "ranges": [
+ [99, 1, 9],
+ [105, 1, 9]
+ ]
+ },
+ {
+ "pattern": "bridge-green",
+ "ranges": [
+ [15, 16, 9],
+ [32, 15, 9],
+ [48, 15, 9],
+ [69, 10, 9],
+ [85, 10, 9],
+ [100, 5, 8],
+ [122, 3, 9],
+ [128, 15, 9],
+ [147, 8, 11],
+ [160, 8, 9],
+ [171, 2, 9],
+ [175, 2, 9],
+ [179, 2, 9],
+ [184, 9, 9]
+
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [36, 4, 5],
+ [55, 5],
+ [56, 6],
+ [57, 5],
+ [58, 6],
+ [59, 5],
+ [72, 5],
+ [73, 2, 4],
+ [75, 5],
+ [97, 3, 5],
+ [108, 3, 5],
+ [133, 6, 5],
+ [149, 4, 8],
+ [173, 6, 6]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [102, 5]
+ ]
+ },
+ {
+ "style": "dirt",
+ "ranges": [
+ [9, 6, 13, 2],
+ [113, 6, 13, 2],
+ [193, 11, 13, 2]
+ ]
+ },
+ {
+ "pattern": "grass-top-8w",
+ "ranges": [
+ [8, 13],
+ [112, 13]
+ ]
+ },
+ {
+ "pattern": "grass-top-13w",
+ "ranges": [
+ [192, 13]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [0, 8]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-final",
+ "ranges": [
+ [208, 5]
+ ]
+ },
+ {
+ "pattern": "castle-large",
+ "ranges": [
+ [228, 2]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [225, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/coin-clouds-1.json b/public/levels/coin-clouds-1.json
new file mode 100644
index 00000000..c3c087d9
--- /dev/null
+++ b/public/levels/coin-clouds-1.json
@@ -0,0 +1,48 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "coin-clouds",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [80, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 79, 0, 15]
+ ]
+ }
+
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "cloud-tile",
+ "behavior": "ground",
+ "ranges": [
+ [0, 4, 13],
+ [5, 57, 13]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [15, 16, 6],
+ [32, 3, 4],
+ [36, 16, 5],
+ [53, 3, 4],
+ [69, 3, 12]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/coin-room-1.json b/public/levels/coin-room-1.json
new file mode 100644
index 00000000..ddbd5927
--- /dev/null
+++ b/public/levels/coin-room-1.json
@@ -0,0 +1,68 @@
+{
+ "spriteSheet": "underworld",
+ "musicSheet": "underworld",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [24, 48]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 13]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 2, 11],
+ [4, 7, 2],
+ [4, 7, 10, 3]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [5, 5, 5],
+ [4, 7, 7],
+ [4, 7, 9]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-12h",
+ "ranges": [
+ [13, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "pipe-portal",
+ "pos": [206, 184],
+ "props": {
+ "dir": "RIGHT"
+ }
+ }
+ ],
+
+ "triggers": []
+}
diff --git a/public/levels/coin-room-2.json b/public/levels/coin-room-2.json
new file mode 100644
index 00000000..821a1332
--- /dev/null
+++ b/public/levels/coin-room-2.json
@@ -0,0 +1,66 @@
+{
+ "spriteSheet": "underworld",
+ "musicSheet": "underworld",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [24, 48]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 13]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 2, 11],
+ [3, 10, 2, 4],
+ [13, 2, 2, 9],
+ [3, 9, 9]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [12, 9]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [4, 8, 8],
+ [3, 9, 12]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-12h",
+ "ranges": [
+ [13, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/coin-room-3.json b/public/levels/coin-room-3.json
new file mode 100644
index 00000000..b3314cc4
--- /dev/null
+++ b/public/levels/coin-room-3.json
@@ -0,0 +1,82 @@
+{
+ "spriteSheet": "underworld",
+ "musicSheet": "underworld",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [24, 48]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 13]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 2, 11],
+ [3, 2, 5, 2],
+ [7, 2, 5, 2],
+ [11, 2, 5, 2],
+ [3, 1, 7, 2],
+ [12, 1, 7, 2],
+ [10, 5],
+ [9, 7],
+ [6, 7],
+ [5, 8],
+ [4, 9],
+ [10, 8],
+ [11, 9],
+ [10, 8]
+ ]
+ },
+ {
+ "style" : "metal",
+ "behavior": "ground",
+ "ranges":[
+ [5, 5]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [7, 2, 3],
+ [6, 4, 4],
+ [6, 6],
+ [9, 6],
+ [5, 7],
+ [10, 7],
+ [4, 8],
+ [11, 8]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-12h",
+ "ranges": [
+ [13, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/coin-room-4.json b/public/levels/coin-room-4.json
new file mode 100644
index 00000000..d3d8658e
--- /dev/null
+++ b/public/levels/coin-room-4.json
@@ -0,0 +1,67 @@
+{
+ "spriteSheet": "underworld",
+ "musicSheet": "underworld",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [24, 48]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 13]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 2, 11],
+ [3, 12, 2],
+ [3, 8, 9],
+ [3, 1, 10, 2],
+ [10, 1, 10, 2]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [13, 9]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [3, 8, 8],
+ [3, 10, 12]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-12h",
+ "ranges": [
+ [13, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/coin-room-5.json b/public/levels/coin-room-5.json
new file mode 100644
index 00000000..cf753d21
--- /dev/null
+++ b/public/levels/coin-room-5.json
@@ -0,0 +1,68 @@
+{
+ "spriteSheet": "underworld",
+ "musicSheet": "underworld",
+ "patternSheet": "underworld-pattern",
+ "checkpoints": [
+ [24, 48]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 13]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 2, 11],
+ [4, 7, 2],
+ [10, 1, 3, 5],
+ [4, 6, 7],
+ [4, 6],
+ [11, 2, 6]
+ ]
+ },
+ {
+ "style": "metal",
+ "behavior": "ground",
+ "ranges": [
+ [14, 7]
+ ]
+ },
+ {
+ "style": "coin",
+ "behavior": "coin",
+ "ranges": [
+ [5, 5, 5],
+ [5, 5, 6]
+ ]
+ },
+ {
+ "pattern": "exit-pipe-12h",
+ "ranges": [
+ [13, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/debug-coin.json b/public/levels/debug-coin.json
index 729a8c48..dac85bf0 100644
--- a/public/levels/debug-coin.json
+++ b/public/levels/debug-coin.json
@@ -2,12 +2,15 @@
"spriteSheet": "overworld",
"musicSheet": "silent",
"patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
"layers": [
{
"tiles": [
{
- "name": "sky",
+ "style": "sky",
"ranges": [
[
0, 212,
@@ -16,8 +19,8 @@
]
},
{
- "name": "ground",
- "type": "ground",
+ "style": "ground",
+ "behavior": "ground",
"ranges": [
[
0, 212,
@@ -30,8 +33,8 @@
{
"tiles": [
{
- "name": "coin",
- "type": "coin",
+ "style": "coin",
+ "behavior": "coin",
"ranges": [
[6, 100, 8, 5]
]
diff --git a/public/levels/debug-flag.json b/public/levels/debug-flag.json
new file mode 100644
index 00000000..9641af33
--- /dev/null
+++ b/public/levels/debug-flag.json
@@ -0,0 +1,54 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [160, 32]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "name": "sky",
+ "ranges": [
+ [0, 212, 0, 15]
+ ]
+ },
+ {
+ "name": "ground",
+ "type": "ground",
+ "ranges": [
+ [0, 69, 13, 2],
+ [71, 15, 13, 2],
+ [89, 64, 13, 2],
+ [155, 57, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-final",
+ "ranges": [
+ [2, 5]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [12, 2]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "flag-pole",
+ "pos": [192, 48]
+ }
+ ]
+}
diff --git a/public/levels/debug-level.json b/public/levels/debug-level.json
new file mode 100644
index 00000000..3d76fc30
--- /dev/null
+++ b/public/levels/debug-level.json
@@ -0,0 +1,295 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [
+ 0, 212,
+ 0, 13
+ ]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [
+ 0, 212,
+ 13, 2
+ ]
+ ]
+ },
+ {
+ "style": "sky",
+ "ranges": [
+ [
+ 75, 2,
+ 13, 2
+ ],
+ [
+ 92, 2,
+ 13, 2
+ ],
+ [
+ 157, 2,
+ 13, 2
+ ]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [
+ 5, 3,
+ 9, 1
+ ],
+ [
+ 29, 5
+ ],
+ [
+ 5, 7,
+ 9
+ ],
+ [
+ 12, 6,
+ 11, 1
+ ],
+ [
+ 2, 1,
+ 11, 1
+ ],
+ [
+ 10, 2,
+ 10, 1
+ ],
+ [
+ 10, 2,
+ 10
+ ],
+ [
+ 9, 1,
+ 0, 7
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "style": "bricks",
+ "behavior": "brick",
+ "ranges": [
+ [
+ 27, 5,
+ 9
+ ],
+ [
+ 83, 3,
+ 9
+ ],
+ [
+ 86, 6,
+ 5
+ ],
+ [
+ 96, 3,
+ 5
+ ],
+ [
+ 99, 9
+ ],
+ [
+ 105, 2,
+ 9
+ ],
+ [
+ 123, 5
+ ],
+ [
+ 126, 3,
+ 5
+ ],
+ [
+ 132, 4,
+ 5
+ ],
+ [
+ 133, 2,
+ 9
+ ],
+ [
+ 171, 4,
+ 9
+ ]
+ ]
+ },
+ {
+ "style": "chance",
+ "behavior": "ground",
+ "ranges": [
+ [2, 2],
+
+ [23, 9],
+ [28, 9],
+ [30, 9],
+ [29, 5],
+ [84, 9],
+ [99, 5],
+
+ [114, 5],
+ [111, 9],
+ [114, 9],
+ [117, 9],
+
+ [133, 2, 5],
+
+ [173, 9]
+
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [141, 1, 9],
+ [140, 2, 10],
+ [139, 3, 11],
+ [138, 4, 12],
+
+ [144, 1, 9],
+ [144, 2, 10],
+ [144, 3, 11],
+ [144, 4, 12],
+
+ [155, 2, 9],
+ [154, 3, 10],
+ [153, 4, 11],
+ [152, 5, 12],
+
+ [159, 1, 9],
+ [159, 2, 10],
+ [159, 3, 11],
+ [159, 4, 12],
+
+ [191, 2, 5],
+ [190, 3, 6],
+ [189, 4, 7],
+ [188, 5, 8],
+ [187, 6, 9],
+ [186, 7, 10],
+ [185, 8, 11],
+ [184, 9, 12]
+ ]
+ },
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [35, 11],
+ [167, 11],
+ [182, 11]
+ ]
+ },
+ {
+ "pattern": "pipe-3h",
+ "ranges": [
+ [45, 10]
+ ]
+ },
+ {
+ "pattern": "pipe-4h",
+ "ranges": [
+ [53, 9],
+ [64, 9]
+ ]
+ },
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [2, 2],
+ [25, 2],
+ [35, 3],
+ [44, 2],
+ [64, 3],
+ [74, 2],
+ [80, 3],
+ [90, 2],
+ [108, 3],
+ [118, 2],
+ [128, 3],
+ [138, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cannon-2h",
+ "ranges": [
+ [
+ 6, 7
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "bush-single",
+ "ranges": [
+ [
+ 8, 12
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "bush-double",
+ "ranges": [
+ [
+ 20, 12
+ ]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "koopa-green",
+ "pos": [260, 0]
+ },
+ {
+ "name": "goomba-brown",
+ "pos": [220, 0]
+ },
+ {
+ "name": "cannon",
+ "pos": [96, 112]
+ }
+ ],
+
+ "triggers": [
+ {
+ "type": "goto",
+ "name": "1-2",
+ "pos": [64, 64]
+ }
+ ]
+}
diff --git a/public/levels/debug-pipe.json b/public/levels/debug-pipe.json
new file mode 100644
index 00000000..08248a71
--- /dev/null
+++ b/public/levels/debug-pipe.json
@@ -0,0 +1,66 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 212, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 69, 13, 2],
+ [71, 15, 13, 2],
+ [89, 64, 13, 2],
+ [155, 57, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [2, 11],
+ [6, 11],
+ [12, 11]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [
+ {
+ "name": "piranha-plant",
+ "pos": [104, 176]
+ },
+ {
+ "name": "pipe-portal",
+ "pos": [100, 174],
+ "props": {
+ "dir": "DOWN",
+ "goesTo": {
+ "name": "coin-room-1"
+ },
+ "backTo": "outlet1"
+ }
+ },
+ {
+ "id": "outlet1",
+ "name": "pipe-portal",
+ "pos": [196, 176],
+ "props": {
+ "dir": "UP"
+ }
+ }
+ ]
+}
diff --git a/public/levels/debug-progression.json b/public/levels/debug-progression.json
index 77ba65f2..2a83950b 100644
--- a/public/levels/debug-progression.json
+++ b/public/levels/debug-progression.json
@@ -2,12 +2,15 @@
"spriteSheet": "overworld",
"musicSheet": "silent",
"patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
"layers": [
{
"tiles": [
{
- "name": "sky",
+ "style": "sky",
"ranges": [
[
0, 212,
@@ -16,8 +19,8 @@
]
},
{
- "name": "ground",
- "type": "ground",
+ "style": "ground",
+ "behavior": "ground",
"ranges": [
[
0, 212,
diff --git a/public/levels/uw-entrance.json b/public/levels/uw-entrance.json
new file mode 100644
index 00000000..1c24db3e
--- /dev/null
+++ b/public/levels/uw-entrance.json
@@ -0,0 +1,59 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "uw-entrance",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [40, 192]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 16, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 16, 13, 2]
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [9, 7]
+ ]
+ },
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [3, 3]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [0, 8]
+ ]
+ },
+ {
+ "pattern": "pipe-uw-entrance",
+ "ranges": [
+ [10, 9]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/levels/uw-exit.json b/public/levels/uw-exit.json
new file mode 100644
index 00000000..1b281de2
--- /dev/null
+++ b/public/levels/uw-exit.json
@@ -0,0 +1,90 @@
+{
+ "spriteSheet": "overworld",
+ "musicSheet": "overworld",
+ "patternSheet": "overworld-pattern",
+ "checkpoints": [
+ [56, 160]
+ ],
+
+ "layers": [
+ {
+ "tiles": [
+ {
+ "style": "sky",
+ "ranges": [
+ [0, 36, 0, 15]
+ ]
+ },
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 36, 13, 2]
+
+ ]
+ }
+ ]
+ },
+ {
+ "tiles": [
+ {
+ "pattern": "cloud-single",
+ "ranges": [
+ [24, 3]
+ ]
+ },
+ {
+ "pattern": "cloud-double",
+ "ranges": [
+ [4, 2]
+ ]
+ },
+ {
+ "pattern": "pipe-2h",
+ "ranges": [
+ [3, 11]
+ ]
+ },
+ {
+ "pattern": "chocolate-stairs-final",
+ "ranges": [
+ [5, 5]
+ ]
+ },
+ {
+ "pattern": "hill-large",
+ "ranges": [
+ [16, 10]
+ ]
+ },
+ {
+ "pattern": "hill-small",
+ "ranges": [
+ [32, 11]
+ ]
+ },
+ {
+ "pattern": "flag-pole-green",
+ "ranges": [
+ [22, 2]
+ ]
+ },
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [26, 8]
+ ]
+ },
+ {
+ "style": "bush-3",
+ "ranges": [
+ [31, 12]
+ ]
+ }
+ ]
+ }
+ ],
+
+ "entities": [],
+ "triggers": []
+}
diff --git a/public/music/castle.json b/public/music/castle.json
new file mode 100644
index 00000000..59aa1a45
--- /dev/null
+++ b/public/music/castle.json
@@ -0,0 +1,8 @@
+{
+ "main": {
+ "url": "/audio/music/castle.ogg"
+ },
+ "hurry": {
+ "url": "/audio/music/hurry.ogg"
+ }
+}
diff --git a/public/music/coin-clouds.json b/public/music/coin-clouds.json
new file mode 100644
index 00000000..6f61e03a
--- /dev/null
+++ b/public/music/coin-clouds.json
@@ -0,0 +1,5 @@
+{
+ "main": {
+ "url": "/audio/music/starman.ogg"
+ }
+}
diff --git a/public/music/underwater.json b/public/music/underwater.json
new file mode 100644
index 00000000..d96957ec
--- /dev/null
+++ b/public/music/underwater.json
@@ -0,0 +1,8 @@
+{
+ "main": {
+ "url": "/audio/music/underwater.ogg"
+ },
+ "hurry": {
+ "url": "/audio/music/hurry.ogg"
+ }
+}
diff --git a/public/music/uw-entrance.json b/public/music/uw-entrance.json
new file mode 100644
index 00000000..3fd46848
--- /dev/null
+++ b/public/music/uw-entrance.json
@@ -0,0 +1,5 @@
+{
+ "main": {
+ "url": "/audio/music/uw-entrance.ogg"
+ }
+}
diff --git a/public/sounds/brick-shrapnel.json b/public/sounds/brick-shrapnel.json
new file mode 100644
index 00000000..95aaf8f4
--- /dev/null
+++ b/public/sounds/brick-shrapnel.json
@@ -0,0 +1,7 @@
+{
+ "fx": {
+ "break": {
+ "url": "/audio/fx/brick-destroy.ogg"
+ }
+ }
+}
diff --git a/public/sounds/cannon.json b/public/sounds/cannon.json
index 058f433d..75c5437a 100644
--- a/public/sounds/cannon.json
+++ b/public/sounds/cannon.json
@@ -1,7 +1,7 @@
{
"fx": {
"shoot": {
- "url": "/audio/thwomp.ogg"
+ "url": "/audio/fx/thwomp.ogg"
}
}
}
diff --git a/public/sounds/flag-pole.json b/public/sounds/flag-pole.json
new file mode 100644
index 00000000..7f027163
--- /dev/null
+++ b/public/sounds/flag-pole.json
@@ -0,0 +1,7 @@
+{
+ "fx": {
+ "ride": {
+ "url": "/audio/fx/flagpole.ogg"
+ }
+ }
+}
diff --git a/public/sounds/mario.json b/public/sounds/mario.json
index 249ae90c..b2070da0 100644
--- a/public/sounds/mario.json
+++ b/public/sounds/mario.json
@@ -1,13 +1,13 @@
{
"fx": {
"coin": {
- "url": "/audio/coin.ogg"
+ "url": "/audio/fx/coin.ogg"
},
"jump": {
- "url": "/audio/jump.ogg"
+ "url": "/audio/fx/jump.ogg"
},
"stomp": {
- "url": "/audio/stomp.ogg"
+ "url": "/audio/fx/stomp.ogg"
}
}
}
diff --git a/public/sounds/pipe-portal.json b/public/sounds/pipe-portal.json
new file mode 100644
index 00000000..9c083fcc
--- /dev/null
+++ b/public/sounds/pipe-portal.json
@@ -0,0 +1,7 @@
+{
+ "fx": {
+ "pipe": {
+ "url": "/audio/fx/pipe.ogg"
+ }
+ }
+}
diff --git a/public/sprites/brick-shrapnel.json b/public/sprites/brick-shrapnel.json
new file mode 100644
index 00000000..8db63680
--- /dev/null
+++ b/public/sprites/brick-shrapnel.json
@@ -0,0 +1,35 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "brick-1",
+ "rect": [192, 168, 8, 8]
+ },
+ {
+ "name": "brick-2",
+ "rect": [200, 168, 8, 8]
+ },
+ {
+ "name": "brick-3",
+ "rect": [192, 176, 8, 8]
+ },
+ {
+ "name": "brick-4",
+ "rect": [200, 176, 8, 8]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "spinning-brick",
+ "frameLen": 0.15,
+ "frames": [
+ "brick-1",
+ "brick-2",
+ "brick-3",
+ "brick-4"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/bullet.json b/public/sprites/bullet.json
index fde315ae..5a51ac92 100644
--- a/public/sprites/bullet.json
+++ b/public/sprites/bullet.json
@@ -1,10 +1,10 @@
{
- "imageURL": "/img/characters.gif",
+ "imageURL": "/img/sprites.png",
"frames": [
{
"name": "bullet",
- "rect": [267, 334, 16, 14]
+ "rect": [128, 0, 16, 16]
}
]
}
diff --git a/public/sprites/castle.json b/public/sprites/castle.json
new file mode 100644
index 00000000..390ecda0
--- /dev/null
+++ b/public/sprites/castle.json
@@ -0,0 +1,121 @@
+{
+ "imageURL": "/img/tiles.png",
+ "tileW": 16,
+ "tileH": 16,
+
+ "tiles": [
+ {
+ "name": "ground",
+ "index": [14, 1]
+ },
+ {
+ "name": "sky",
+ "index": [13, 7]
+ },
+ {
+ "name": "bricks",
+ "index": [1, 1]
+ },
+ {
+ "name": "tile-red",
+ "index": [8, 7]
+ },
+ {
+ "name": "waves",
+ "index": [2, 7]
+ },
+ {
+ "name": "metal",
+ "index": [2, 1]
+ },
+ {
+ "name": "metal-alt",
+ "index": [7, 1]
+ },
+ {
+ "name": "chance-1",
+ "index": [4, 1]
+ },
+ {
+ "name": "chance-2",
+ "index": [5, 1]
+ },
+ {
+ "name": "chance-3",
+ "index": [6, 1]
+ },
+ {
+ "name": "coin-1",
+ "index": [15, 9]
+ },
+ {
+ "name": "coin-2",
+ "index": [15, 10]
+ },
+ {
+ "name": "coin-3",
+ "index": [15, 11]
+ },
+ {
+ "name": "pipe-insert-vert-left",
+ "index": [0, 3]
+ },
+ {
+ "name": "pipe-insert-vert-right",
+ "index": [1, 3]
+ },
+ {
+ "name": "pipe-vert-left",
+ "index": [0, 4]
+ },
+ {
+ "name": "pipe-vert-right",
+ "index": [1, 4]
+ },
+ {
+ "name": "beam-track",
+ "index": [11, 13]
+ },
+ {
+ "name": "bridge",
+ "index": [13, 4]
+ },
+ {
+ "name": "bridge-chain",
+ "index": [13, 3]
+ },
+ {
+ "name": "toad-1",
+ "index": [15, 12]
+ },
+ {
+ "name": "toad-2",
+ "index": [15, 13]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "chance",
+ "frameLen": 0.16,
+ "frames": [
+ "chance-1",
+ "chance-1",
+ "chance-2",
+ "chance-3",
+ "chance-2"
+ ]
+ },
+ {
+ "name": "coin",
+ "frameLen": 0.16,
+ "frames": [
+ "coin-1",
+ "coin-1",
+ "coin-2",
+ "coin-3",
+ "coin-2"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/cheep-gray.json b/public/sprites/cheep-gray.json
new file mode 100644
index 00000000..f0c3e10c
--- /dev/null
+++ b/public/sprites/cheep-gray.json
@@ -0,0 +1,25 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "swim-1",
+ "rect": [48, 32, 16, 16]
+ },
+ {
+ "name": "swim-2",
+ "rect": [64, 32, 16, 16]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "swim",
+ "frameLen": 0.13,
+ "frames": [
+ "swim-1",
+ "swim-2"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/cheep-red.json b/public/sprites/cheep-red.json
new file mode 100644
index 00000000..65087860
--- /dev/null
+++ b/public/sprites/cheep-red.json
@@ -0,0 +1,25 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "swim-1",
+ "rect": [48, 0, 16, 16]
+ },
+ {
+ "name": "swim-2",
+ "rect": [64, 0, 16, 16]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "swim",
+ "frameLen": 0.13,
+ "frames": [
+ "swim-1",
+ "swim-2"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/goomba.json b/public/sprites/goomba-blue.json
similarity index 70%
rename from public/sprites/goomba.json
rename to public/sprites/goomba-blue.json
index 5795a71e..3186486e 100644
--- a/public/sprites/goomba.json
+++ b/public/sprites/goomba-blue.json
@@ -1,18 +1,18 @@
{
- "imageURL": "/img/characters.gif",
+ "imageURL": "/img/sprites.png",
"frames": [
{
"name": "walk-1",
- "rect": [296, 187, 16, 16]
+ "rect": [80, 16, 16, 16]
},
{
"name": "walk-2",
- "rect": [315, 187, 16, 16]
+ "rect": [96, 16, 16, 16]
},
{
"name": "flat",
- "rect": [277, 187, 16, 16]
+ "rect": [112, 16, 16, 16]
}
],
diff --git a/public/sprites/goomba-brown.json b/public/sprites/goomba-brown.json
new file mode 100644
index 00000000..00a5633a
--- /dev/null
+++ b/public/sprites/goomba-brown.json
@@ -0,0 +1,29 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "walk-1",
+ "rect": [80, 0, 16, 16]
+ },
+ {
+ "name": "walk-2",
+ "rect": [96, 0, 16, 16]
+ },
+ {
+ "name": "flat",
+ "rect": [112, 0, 16, 16]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "walk",
+ "frameLen": 0.15,
+ "frames": [
+ "walk-1",
+ "walk-2"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/koopa.json b/public/sprites/koopa-blue.json
similarity index 75%
rename from public/sprites/koopa.json
rename to public/sprites/koopa-blue.json
index 6ee79e4a..9c3432ce 100644
--- a/public/sprites/koopa.json
+++ b/public/sprites/koopa-blue.json
@@ -1,22 +1,22 @@
{
- "imageURL": "/img/characters.gif",
+ "imageURL": "/img/sprites.png",
"frames": [
{
"name": "walk-1",
- "rect": [296, 206, 16, 24]
+ "rect": [224, 48, 16, 24]
},
{
"name": "walk-2",
- "rect": [315, 206, 16, 24]
+ "rect": [240, 48, 16, 24]
},
{
"name": "hiding",
- "rect": [144, 206, 16, 24]
+ "rect": [240, 168, 16, 24]
},
{
"name": "hiding-with-legs",
- "rect": [163, 206, 16, 24]
+ "rect": [240, 192, 16, 24]
}
],
diff --git a/public/sprites/koopa-green.json b/public/sprites/koopa-green.json
new file mode 100644
index 00000000..5e579c0d
--- /dev/null
+++ b/public/sprites/koopa-green.json
@@ -0,0 +1,41 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "walk-1",
+ "rect": [224, 0, 16, 24]
+ },
+ {
+ "name": "walk-2",
+ "rect": [240, 0, 16, 24]
+ },
+ {
+ "name": "hiding",
+ "rect": [208, 168, 16, 24]
+ },
+ {
+ "name": "hiding-with-legs",
+ "rect": [208, 192, 16, 24]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "walk",
+ "frameLen": 0.15,
+ "frames": [
+ "walk-1",
+ "walk-2"
+ ]
+ },
+ {
+ "name": "wake",
+ "frameLen": 0.15,
+ "frames": [
+ "hiding-with-legs",
+ "hiding"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/mario.json b/public/sprites/mario.json
index 4f530528..ffef6394 100644
--- a/public/sprites/mario.json
+++ b/public/sprites/mario.json
@@ -1,30 +1,90 @@
{
- "imageURL": "/img/characters.gif",
+ "imageURL": "/img/sprites.png",
"frames": [
{
"name": "idle",
- "rect": [276, 44, 16, 16]
+ "rect": [0, 88, 16, 16]
},
{
"name": "run-1",
- "rect": [290, 44, 16, 16]
+ "rect": [16, 88, 16, 16]
},
{
"name": "run-2",
- "rect": [304, 43, 16, 16]
+ "rect": [32, 88, 16, 16]
},
{
"name": "run-3",
- "rect": [321, 44, 16, 16]
+ "rect": [48, 88, 16, 16]
},
{
"name": "break",
- "rect": [337, 44, 16, 16]
+ "rect": [64, 88, 16, 16]
},
{
"name": "jump",
- "rect": [355, 44, 16, 16]
+ "rect": [80, 88, 16, 16]
+ },
+ {
+ "name": "die",
+ "rect": [96, 88, 16, 16]
+ },
+ {
+ "name": "climb-1",
+ "rect": [0, 104, 16, 16]
+ },
+ {
+ "name": "climb-2",
+ "rect": [16, 104, 16, 16]
+ },
+ {
+ "name": "swim-1",
+ "rect": [32, 104, 16, 16]
+ },
+ {
+ "name": "swim-2",
+ "rect": [48, 104, 16, 16]
+ },
+ {
+ "name": "swim-3",
+ "rect": [64, 104, 16, 16]
+ },
+ {
+ "name": "swim-4",
+ "rect": [80, 104, 16, 16]
+ },
+ {
+ "name": "swim-5",
+ "rect": [96, 104, 16, 16]
+ },
+ {
+ "name": "idle-large",
+ "rect": [112, 88, 16, 32]
+ },
+ {
+ "name": "run-1-large",
+ "rect": [128, 88, 16, 32]
+ },
+ {
+ "name": "run-2-large",
+ "rect": [144, 88, 16, 32]
+ },
+ {
+ "name": "run-3-large",
+ "rect": [160, 88, 16, 32]
+ },
+ {
+ "name": "break-large",
+ "rect": [176, 88, 16, 32]
+ },
+ {
+ "name": "jump-large",
+ "rect": [192, 88, 16, 32]
+ },
+ {
+ "name": "crouch-large",
+ "rect": [0, 120, 16, 32]
}
],
@@ -37,6 +97,40 @@
"run-2",
"run-3"
]
+ },
+ {
+ "name": "climb",
+ "frameLen": 4,
+ "frames": [
+ "climb-1",
+ "climb-2"
+ ]
+ },
+ {
+ "name": "swim-idle",
+ "frameLen": 4,
+ "frames": [
+ "swim-1",
+ "swim-2"
+ ]
+ },
+ {
+ "name": "swim",
+ "frameLen": 6,
+ "frames": [
+ "swim-3",
+ "swim-4",
+ "swim-5"
+ ]
+ },
+ {
+ "name": "run-large",
+ "frameLen": 6,
+ "frames": [
+ "run-1-large",
+ "run-2-large",
+ "run-3-large"
+ ]
}
]
}
diff --git a/public/sprites/overworld.json b/public/sprites/overworld.json
index 65770e83..0d6075ce 100644
--- a/public/sprites/overworld.json
+++ b/public/sprites/overworld.json
@@ -10,105 +10,294 @@
},
{
"name": "sky",
- "index": [3, 23]
+ "index": [14, 7]
},
{
"name": "chocolate",
- "index": [0, 1]
+ "index": [3, 0]
},
{
"name": "bricks",
"index": [1, 0]
},
{
- "name": "chance",
- "index": [24, 0]
+ "name": "bricks-top",
+ "index": [14, 0]
+ },
+ {
+ "name": "metal",
+ "index": [2, 0]
},
-
{
"name": "chance-1",
- "index": [24, 0]
+ "index": [4, 0]
},
{
"name": "chance-2",
- "index": [25, 0]
+ "index": [5, 0]
},
{
"name": "chance-3",
- "index": [26, 0]
+ "index": [6, 0]
},
-
{
"name": "coin-1",
- "index": [24, 1]
+ "index": [15, 0]
},
{
"name": "coin-2",
- "index": [25, 1]
+ "index": [15, 1]
},
{
"name": "coin-3",
- "index": [26, 1]
+ "index": [15, 2]
},
-
{
"name": "pipe-insert-vert-left",
- "index": [0, 8]
+ "index": [0, 5]
},
{
"name": "pipe-insert-vert-right",
- "index": [1, 8]
+ "index": [1, 5]
},
{
"name": "pipe-vert-left",
- "index": [0, 9]
+ "index": [0, 6]
},
{
"name": "pipe-vert-right",
- "index": [1, 9]
+ "index": [1, 6]
+ },
+ {
+ "name": "pipe-chrome-insert-vert-left",
+ "index": [2, 3]
+ },
+ {
+ "name": "pipe-chrome-insert-vert-right",
+ "index": [3, 3]
+ },
+ {
+ "name": "pipe-chrome-vert-left",
+ "index": [2, 4]
+ },
+ {
+ "name": "pipe-chrome-vert-right",
+ "index": [3, 4]
+ },
+ {
+ "name": "pipe-insert-hor-top",
+ "index": [6, 3]
+ },
+ {
+ "name": "pipe-insert-hor-bottom",
+ "index": [6, 4]
+ },
+ {
+ "name": "pipe-hor-top",
+ "index": [7, 3]
+ },
+ {
+ "name": "pipe-hor-bottom",
+ "index": [7, 4]
+ },
+ {
+ "name": "pipe-conn-hor-top",
+ "index": [8, 3]
+ },
+ {
+ "name": "pipe-conn-hor-bottom",
+ "index": [8, 4]
+ },
+ {
+ "name": "cloud-tile",
+ "index": [14, 8]
},
{
"name": "cloud-1-1",
- "index": [0, 20]
+ "index": [11, 10]
},
{
"name": "cloud-1-2",
- "index": [1, 20]
+ "index": [12, 10]
},
{
"name": "cloud-1-3",
- "index": [2, 20]
+ "index": [13, 10]
},
{
"name": "cloud-2-1",
- "index": [0, 21]
+ "index": [11, 11]
},
{
"name": "cloud-2-2",
- "index": [1, 21]
+ "index": [12, 11]
},
{
"name": "cloud-2-3",
- "index": [2, 21]
+ "index": [13, 11]
},
{
"name": "cannon-1",
- "index": [9, 0]
+ "index": [14, 3]
},
{
"name": "cannon-2",
- "index": [8, 1]
+ "index": [14, 4]
},
{
"name": "cannon-3",
- "index": [9, 1]
+ "index": [14, 5]
+ },
+ {
+ "name": "bush-1",
+ "index": [11, 12]
+ },
+ {
+ "name": "bush-2",
+ "index": [12, 12]
+ },
+ {
+ "name": "bush-3",
+ "index": [13, 12]
+ },
+ {
+ "name": "grass-left",
+ "index": [9, 2]
+ },
+ {
+ "name": "grass",
+ "index": [10, 2]
+ },
+ {
+ "name": "grass-right",
+ "index": [11, 2]
+ },
+ {
+ "name": "dirt",
+ "index": [13, 0]
+ },
+ {
+ "name": "tile-black",
+ "index": [13, 7]
+ },
+ {
+ "name": "tile-light-blue",
+ "index": [10, 7]
+ },
+ {
+ "name": "castle-top-closed",
+ "index": [8, 0]
+ },
+ {
+ "name": "castle-top-open",
+ "index": [9, 0]
+ },
+ {
+ "name": "castle-window-right",
+ "index": [10, 0]
+ },
+ {
+ "name": "castle-arch",
+ "index": [11, 0]
+ },
+ {
+ "name": "castle-window-left",
+ "index": [12, 0]
+ },
+ {
+ "name": "pole-green",
+ "index": [12, 13]
+ },
+ {
+ "name": "pole-white",
+ "index": [11, 13]
+ },
+ {
+ "name": "pole-finial-dark-grey",
+ "index": [7, 8]
+ },
+ {
+ "name": "pole-finial-green",
+ "index": [8, 8]
+ },
+ {
+ "name": "hill-left",
+ "index": [4, 7]
+ },
+ {
+ "name": "hill-right",
+ "index": [6, 7]
+ },
+ {
+ "name": "hill-top",
+ "index": [7, 7]
+ },
+ {
+ "name": "hill-stains-right",
+ "index": [5, 7]
+ },
+ {
+ "name": "hill-stains-left",
+ "index": [3, 7]
+ },
+ {
+ "name": "tile-green",
+ "index": [12, 7]
+ },
+ {
+ "name": "tree-large-top",
+ "index": [10, 5]
+ },
+ {
+ "name": "tree-large-bottom",
+ "index": [10, 6]
+ },
+ {
+ "name": "tree-small",
+ "index": [12, 5]
+ },
+ {
+ "name": "tree-white-large-top",
+ "index": [11, 5]
+ },
+ {
+ "name": "tree-white-large-bottom",
+ "index": [11, 6]
+ },
+ {
+ "name": "tree-white-small",
+ "index": [13, 5]
+ },
+ {
+ "name": "tree-trunk",
+ "index": [12, 6]
+ },
+ {
+ "name": "fence",
+ "index": [14, 6]
+ },
+ {
+ "name": "bridge",
+ "index": [13, 6]
+ },
+ {
+ "name": "bridge-rail-green",
+ "index": [14, 11]
+ },
+ {
+ "name": "bridge-rail-white",
+ "index": [14, 10]
+ },
+ {
+ "name": "waves",
+ "index": [0, 7]
}
],
"animations": [
{
"name": "chance",
- "frameLen": 0.12,
+ "frameLen": 0.16,
"frames": [
"chance-1",
"chance-1",
@@ -119,7 +308,7 @@
},
{
"name": "coin",
- "frameLen": 0.12,
+ "frameLen": 0.16,
"frames": [
"coin-1",
"coin-1",
diff --git a/public/sprites/patterns/castle-pattern.json b/public/sprites/patterns/castle-pattern.json
new file mode 100644
index 00000000..dc2f135e
--- /dev/null
+++ b/public/sprites/patterns/castle-pattern.json
@@ -0,0 +1,149 @@
+{
+ "entrance-stairs": {
+ "tiles": [
+ {
+ "style": "ground",
+ "behavior": "ground",
+ "ranges": [
+ [0, 3, 1, 1],
+ [0, 4, 2, 1],
+ [0, 5, 3, 1]
+ ]
+ }
+ ]
+ },
+
+ "pipe-section-vert": {
+ "tiles": [
+ {
+ "style": "pipe-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-cap-vert": {
+ "tiles": [
+ {
+ "style": "pipe-insert-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-insert-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-2h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 1
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-3h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 2
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-4h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 3
+ ]
+ ]
+ }
+ ]
+ },
+
+ "bridge": {
+ "tiles": [
+ {
+ "style": "bridge",
+ "behavior": "ground",
+ "ranges": [
+ [0, 13, 1]
+ ]
+ },
+ {
+ "style": "bridge-chain",
+ "ranges": [
+ [12, 0]
+ ]
+ }
+ ]
+ },
+
+ "toad": {
+ "tiles": [
+ {
+ "style": "toad-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "toad-2",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ }
+
+}
diff --git a/public/sprites/patterns/overworld-pattern.json b/public/sprites/patterns/overworld-pattern.json
index cc669748..4439da13 100644
--- a/public/sprites/patterns/overworld-pattern.json
+++ b/public/sprites/patterns/overworld-pattern.json
@@ -2,28 +2,222 @@
"cloud-single": {
"tiles": [
{
- "name": "cloud-1-1",
- "ranges": [[0, 0]]
+ "style": "cloud-1-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-2",
+ "ranges": [
+ [1, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-3",
+ "ranges": [
+ [2, 0]
+ ]
+ },
+ {
+ "style": "cloud-2-1",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-2",
+ "ranges": [
+ [1, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-3",
+ "ranges": [
+ [2, 1]
+ ]
+ }
+ ]
+ },
+
+ "cloud-double": {
+ "tiles": [
+ {
+ "style": "cloud-1-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-2",
+ "ranges": [
+ [1, 2, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-3",
+ "ranges": [
+ [3, 0]
+ ]
+ },
+
+ {
+ "style": "cloud-2-1",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-2",
+ "ranges": [
+ [1, 2, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-3",
+ "ranges": [
+ [3, 1]
+ ]
+ }
+ ]
+ },
+
+ "cloud-triple": {
+ "tiles": [
+ {
+ "style": "cloud-1-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-2",
+ "ranges": [
+ [1, 3, 0]
+ ]
+ },
+ {
+ "style": "cloud-1-3",
+ "ranges": [
+ [4, 0]
+ ]
+ },
+
+ {
+ "style": "cloud-2-1",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-2",
+ "ranges": [
+ [1, 3, 1]
+ ]
+ },
+ {
+ "style": "cloud-2-3",
+ "ranges": [
+ [4, 1]
+ ]
+ }
+ ]
+ },
+
+ "pipe-chrome-section-vert": {
+ "tiles": [
+ {
+ "style": "pipe-chrome-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
},
{
- "name": "cloud-1-2",
- "ranges": [[1, 0]]
+ "style": "pipe-chrome-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-chrome-cap-vert": {
+ "tiles": [
+ {
+ "style": "pipe-chrome-insert-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
},
{
- "name": "cloud-1-3",
- "ranges": [[2, 0]]
+ "style": "pipe-chrome-insert-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-chrome-2h": {
+ "tiles": [
+ {
+ "pattern": "pipe-chrome-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
},
{
- "name": "cloud-2-1",
- "ranges": [[0, 1]]
+ "pattern": "pipe-chrome-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 1
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-chrome-3h": {
+ "tiles": [
+ {
+ "pattern": "pipe-chrome-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
},
{
- "name": "cloud-2-2",
- "ranges": [[1, 1]]
+ "pattern": "pipe-chrome-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 2
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-chrome-4h": {
+ "tiles": [
+ {
+ "pattern": "pipe-chrome-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
},
{
- "name": "cloud-2-3",
- "ranges": [[2, 1]]
+ "pattern": "pipe-chrome-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 3
+ ]
+ ]
}
]
},
@@ -31,15 +225,15 @@
"pipe-section-vert": {
"tiles": [
{
- "name": "pipe-vert-left",
- "type": "ground",
+ "style": "pipe-vert-left",
+ "behavior": "ground",
"ranges": [
[0, 0]
]
},
{
- "name": "pipe-vert-right",
- "type": "ground",
+ "style": "pipe-vert-right",
+ "behavior": "ground",
"ranges": [
[1, 0]
]
@@ -50,15 +244,15 @@
"pipe-cap-vert": {
"tiles": [
{
- "name": "pipe-insert-vert-left",
- "type": "ground",
+ "style": "pipe-insert-vert-left",
+ "behavior": "ground",
"ranges": [
[0, 0]
]
},
{
- "name": "pipe-insert-vert-right",
- "type": "ground",
+ "style": "pipe-insert-vert-right",
+ "behavior": "ground",
"ranges": [
[1, 0]
]
@@ -126,22 +320,913 @@
]
},
+ "pipe-5h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 4
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-uw-entrance": {
+ "tiles": [
+ {
+ "pattern": "pipe-4h",
+ "ranges": [
+ [2, 0]
+ ]
+ },
+ {
+ "style": "pipe-insert-hor-top",
+ "ranges": [
+ [0, 2]
+ ]
+ },
+ {
+ "style": "pipe-insert-hor-bottom",
+ "ranges": [
+ [0, 3]
+ ]
+ },
+ {
+ "style": "pipe-hor-top",
+ "ranges": [
+ [1, 2]
+ ]
+ },
+ {
+ "style": "pipe-hor-bottom",
+ "ranges": [
+ [1, 3]
+ ]
+ },
+ {
+ "style": "pipe-conn-hor-top",
+ "ranges": [
+ [2, 2]
+ ]
+ },
+ {
+ "style": "pipe-conn-hor-bottom",
+ "ranges": [
+ [2, 3]
+ ]
+ }
+ ]
+ },
+
"cannon-2h": {
"tiles": [
{
- "name": "cannon-1",
- "type": "ground",
+ "style": "cannon-1",
+ "behavior": "ground",
"ranges": [
[0, 0]
]
},
{
- "name": "cannon-3",
- "type": "ground",
+ "style": "cannon-2",
+ "behavior": "ground",
"ranges": [
[0, 1]
]
}
]
- }
+ },
+
+ "bush-single": {
+ "tiles": [
+ {
+ "style": "bush-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "bush-2",
+ "ranges": [
+ [1, 0]
+ ]
+ },
+ {
+ "style": "bush-3",
+ "ranges": [
+ [2, 0]
+ ]
+ }
+
+ ]
+ },
+
+ "bush-double": {
+ "tiles": [
+ {
+ "style": "bush-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "bush-2",
+ "ranges": [
+ [1, 0],
+ [2, 0]
+ ]
+ },
+ {
+ "style": "bush-3",
+ "ranges": [
+ [3, 0]
+ ]
+ }
+ ]
+ },
+
+ "bush-triple": {
+ "tiles": [
+ {
+ "style": "bush-1",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "bush-2",
+ "ranges": [
+ [1, 0],
+ [2, 0],
+ [3, 0]
+ ]
+ },
+ {
+ "style": "bush-3",
+ "ranges": [
+ [4, 0]
+ ]
+ }
+ ]
+ },
+
+ "hill-small": {
+ "tiles": [
+ {
+ "style": "hill-top",
+ "ranges": [
+ [1, 0]
+ ]
+ },
+ {
+ "style": "hill-left",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "hill-stains-right",
+ "ranges": [
+ [1, 1]
+ ]
+ },
+ {
+ "style": "hill-right",
+ "ranges": [
+ [2, 1]
+ ]
+ }
+ ]
+ },
+
+ "hill-large": {
+ "tiles": [
+ {
+ "pattern": "hill-small",
+ "ranges": [
+ [1, 0]
+ ]
+ },
+ {
+ "style": "hill-left",
+ "ranges": [
+ [0, 2]
+ ]
+ },
+ {
+ "style": "hill-stains-right",
+ "ranges": [
+ [1, 2]
+ ]
+ },
+ {
+ "style": "tile-green",
+ "ranges": [
+ [2, 2]
+ ]
+ },
+ {
+ "style": "hill-stains-left",
+ "ranges": [
+ [3, 2]
+ ]
+ },
+ {
+ "style": "hill-right",
+ "ranges": [
+ [4, 2]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-3w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [2, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-4w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 2, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [3, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-5w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 3, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [4, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-6w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 4, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [5, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-7w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 5, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [6, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-8w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 6, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [7, 0]
+ ]
+ }
+ ]
+ },
+
+ "grass-top-13w": {
+ "tiles": [
+ {
+ "style": "grass-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "grass",
+ "behavior": "ground",
+ "ranges": [
+ [1, 11, 0, 1]
+ ]
+ },
+ {
+ "style": "grass-right",
+ "behavior": "ground",
+ "ranges": [
+ [12, 0]
+ ]
+ }
+ ]
+ },
+
+ "castle-wall-3h": {
+ "tiles": [
+ {
+ "style": "castle-top-open",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "bricks",
+ "ranges": [
+ [0, 1, 1, 2]
+ ]
+ }
+ ]
+ },
+
+ "castle-wall-6h": {
+ "tiles": [
+ {
+ "style": "castle-top-open",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "bricks",
+ "ranges": [
+ [0, 1, 1, 5]
+ ]
+ }
+ ]
+ },
+
+ "castle-opening": {
+ "tiles": [
+ {
+ "style": "castle-arch",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "tile-black",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "castle-small": {
+ "tiles": [
+ {
+ "pattern": "castle-wall-3h",
+ "ranges": [
+ [0, 2],
+ [4, 2]
+ ]
+ },
+ {
+ "style": "castle-top-open",
+ "ranges": [
+ [1, 3, 0, 1]
+ ]
+ },
+ {
+ "style": "castle-window-left",
+ "ranges": [
+ [1, 1]
+ ]
+ },
+ {
+ "style": "castle-window-right",
+ "ranges": [
+ [3, 1]
+ ]
+ },
+ {
+ "style": "bricks",
+ "ranges": [
+ [2, 1],
+ [1, 3, 3, 2]
+
+ ]
+ },
+ {
+ "style": "castle-top-closed",
+ "ranges": [
+ [1, 3, 2, 1]
+ ]
+ },
+ {
+ "pattern": "castle-opening",
+ "ranges": [
+ [2, 3]
+ ]
+ }
+ ]
+ },
+
+ "castle-large": {
+ "tiles": [
+ {
+ "pattern": "castle-small",
+ "ranges": [
+ [2, 0]
+ ]
+ },
+ {
+ "pattern": "castle-wall-6h",
+ "ranges": [
+ [0, 2, 5, 1],
+ [7, 2, 5, 1]
+ ]
+ },
+ {
+ "style": "castle-top-closed",
+ "ranges": [
+ [2, 5, 5, 1]
+ ]
+ },
+ {
+ "style": "bricks",
+ "ranges": [
+ [2, 5, 6, 5]
+ ]
+ },
+ {
+ "pattern": "castle-opening",
+ "ranges": [
+ [3, 6],
+ [5, 6],
+ [2, 9],
+ [4, 9],
+ [6, 9]
+ ]
+ }
+ ]
+ },
+
+ "flag-pole-green": {
+ "tiles": [
+ {
+ "style": "pole-finial-green",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 10]
+ ]
+ },
+ {
+ "style": "pole-green",
+ "ranges": [
+ [0, 1, 1, 9]
+ ]
+ }
+
+ ]
+ },
+
+ "flag-pole-dark-grey": {
+ "tiles": [
+ {
+ "style": "pole-finial-dark-grey",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 10]
+ ]
+ },
+ {
+ "style": "pole-white",
+ "ranges": [
+ [0, 1, 1, 9]
+ ]
+ }
+
+ ]
+ },
+
+ "chocolate-stack-2h": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 0, 2]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-3h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 2]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-4h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [0, 0],
+ [0, 2]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-5h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stack-4h",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 4]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-6h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stack-3h",
+ "ranges": [
+ [0, 0],
+ [0, 3]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-2h": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [1, 1, 0, 2],
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-2h-reverse": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0],
+ [0, 2, 1, 1]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-3h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-2h",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [2, 1, 0, 3]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-3h-reverse": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-2h-reverse",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 3, 2, 1]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-4h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-3h",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [3, 1, 0, 4]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-4h-reverse": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-3h-reverse",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 4, 3, 1]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-final": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-4h",
+ "ranges": [
+ [0, 4],
+ [4, 0]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [4, 4, 4, 4],
+ [8, 1, 0, 8]
+ ]
+ }
+
+ ]
+ },
+
+ "tree-small": {
+ "tiles": [
+ {
+ "style": "tree-small",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "tree-trunk",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "tree-large": {
+ "tiles": [
+ {
+ "style": "tree-large-top",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "tree-large-bottom",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "tree-trunk",
+ "ranges": [
+ [0, 2]
+ ]
+ }
+ ]
+ },
+
+ "tree-white-small": {
+ "tiles": [
+ {
+ "style": "tree-white-small",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "tree-trunk",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "tree-white-large": {
+ "tiles": [
+ {
+ "style": "tree-white-large-top",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "tree-white-large-bottom",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "tree-trunk",
+ "ranges": [
+ [0, 2]
+ ]
+ }
+ ]
+ },
+
+ "bridge-green": {
+ "tiles": [
+ {
+ "style": "bridge",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "bridge-rail-green",
+ "ranges": [
+ [0, 0]
+ ]
+ }
+ ]
+ },
+
+ "bridge-white": {
+ "tiles": [
+ {
+ "style": "bridge",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "bridge-rail-white",
+ "ranges": [
+ [0, 0]
+ ]
+ }
+ ]
+ }
+
}
diff --git a/public/sprites/patterns/underwater-pattern.json b/public/sprites/patterns/underwater-pattern.json
new file mode 100644
index 00000000..6f77fbcb
--- /dev/null
+++ b/public/sprites/patterns/underwater-pattern.json
@@ -0,0 +1,37 @@
+{
+ "water": {
+ "tiles": [
+ {
+ "style": "waves",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "water",
+ "ranges": [
+ [0, 1, 1, 12]
+ ]
+ }
+ ]
+ },
+ "pipe-cap-hor": {
+ "tiles": [
+ {
+ "style": "pipe-insert-hor-top",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-insert-hor-bottom",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ }
+
+}
diff --git a/public/sprites/patterns/underworld-pattern.json b/public/sprites/patterns/underworld-pattern.json
new file mode 100644
index 00000000..e26ed4d6
--- /dev/null
+++ b/public/sprites/patterns/underworld-pattern.json
@@ -0,0 +1,299 @@
+{
+ "pipe-section-vert": {
+ "tiles": [
+ {
+ "style": "pipe-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-cap-vert": {
+ "tiles": [
+ {
+ "style": "pipe-insert-vert-left",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-insert-vert-right",
+ "behavior": "ground",
+ "ranges": [
+ [1, 0]
+ ]
+ }
+ ]
+ },
+
+ "pipe-section-hor": {
+ "tiles": [
+ {
+ "style": "pipe-hor-top",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-hor-bottom",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "pipe-cap-hor": {
+ "tiles": [
+ {
+ "style": "pipe-insert-hor-top",
+ "behavior": "ground",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-insert-hor-bottom",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "pipe-conn-hor": {
+ "tiles": [
+ {
+ "style": "pipe-conn-hor-top",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "style": "pipe-conn-hor-bottom",
+ "ranges": [
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "exit-pipe-12h" : {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-hor",
+ "ranges": [
+ [0, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-section-hor",
+ "ranges": [
+ [1, 9]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [2, 1, 0, 11]
+ ]
+ },
+ {
+ "pattern": "pipe-conn-hor",
+ "ranges": [
+ [2, 9]
+ ]
+ }
+ ]
+ },
+
+ "exit-pipe-8h" : {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-hor",
+ "ranges": [
+ [0, 6]
+ ]
+ },
+ {
+ "pattern": "pipe-section-hor",
+ "ranges": [
+ [1, 6]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [2, 1, 0, 8]
+ ]
+ },
+ {
+ "pattern": "pipe-conn-hor",
+ "ranges": [
+ [2, 6]
+ ]
+ }
+ ]
+ },
+
+
+ "pipe-2h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 1
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-3h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 2
+ ]
+ ]
+ }
+ ]
+ },
+
+ "pipe-4h": {
+ "tiles": [
+ {
+ "pattern": "pipe-cap-vert",
+ "ranges": [
+ [0, 0]
+ ]
+ },
+ {
+ "pattern": "pipe-section-vert",
+ "ranges": [
+ [
+ 0, 1,
+ 1, 3
+ ]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-2h": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 0, 2]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-3h": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [0, 1, 0, 3]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stack-4h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stack-2h",
+ "ranges": [
+ [0, 0],
+ [0, 2]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-2h": {
+ "tiles": [
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [1, 1, 0, 2],
+ [0, 1]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-3h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-2h",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [2, 1, 0, 3]
+ ]
+ }
+ ]
+ },
+
+ "chocolate-stairs-4h": {
+ "tiles": [
+ {
+ "pattern": "chocolate-stairs-3h",
+ "ranges": [
+ [0, 1]
+ ]
+ },
+ {
+ "style": "chocolate",
+ "behavior": "ground",
+ "ranges": [
+ [3, 1, 0, 4]
+ ]
+ }
+ ]
+ }
+
+}
diff --git a/public/sprites/piranha-plant.json b/public/sprites/piranha-plant.json
new file mode 100644
index 00000000..9814bd17
--- /dev/null
+++ b/public/sprites/piranha-plant.json
@@ -0,0 +1,25 @@
+{
+ "imageURL": "/img/sprites.png",
+
+ "frames": [
+ {
+ "name": "close",
+ "rect": [240, 72, 16, 24]
+ },
+ {
+ "name": "open",
+ "rect": [240, 96, 16, 24]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "chew",
+ "frameLen": 0.15,
+ "frames": [
+ "close",
+ "open"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/underwater.json b/public/sprites/underwater.json
new file mode 100644
index 00000000..1f749b47
--- /dev/null
+++ b/public/sprites/underwater.json
@@ -0,0 +1,62 @@
+{
+ "imageURL": "/img/tiles.png",
+ "tileW": 16,
+ "tileH": 16,
+
+ "tiles": [
+ {
+ "name": "ground",
+ "index": [12, 2]
+ },
+ {
+ "name": "sky",
+ "index": [10, 7]
+ },
+ {
+ "name": "water",
+ "index": [9, 7]
+ },
+ {
+ "name": "waves",
+ "index": [1, 7]
+ },
+ {
+ "name": "coral",
+ "index": [13, 2]
+ },
+ {
+ "name": "coin-1",
+ "index": [15, 6]
+ },
+ {
+ "name": "coin-2",
+ "index": [15, 7]
+ },
+ {
+ "name": "coin-3",
+ "index": [15, 8]
+ },
+ {
+ "name": "pipe-insert-hor-top",
+ "index": [9, 5]
+ },
+ {
+ "name": "pipe-insert-hor-bottom",
+ "index": [9, 6]
+ }
+ ],
+
+ "animations": [
+ {
+ "name": "coin",
+ "frameLen": 0.16,
+ "frames": [
+ "coin-1",
+ "coin-1",
+ "coin-2",
+ "coin-3",
+ "coin-2"
+ ]
+ }
+ ]
+}
diff --git a/public/sprites/underworld.json b/public/sprites/underworld.json
index 26fac104..ae77d3c4 100644
--- a/public/sprites/underworld.json
+++ b/public/sprites/underworld.json
@@ -10,7 +10,109 @@
},
{
"name": "sky",
- "index": [13, 3]
+ "index": [13, 7]
+ },
+ {
+ "name": "bricks",
+ "index": [1, 2]
+ },
+ {
+ "name": "chocolate",
+ "index": [3, 2]
+ },
+ {
+ "name": "metal",
+ "index": [2, 2]
+ },
+ {
+ "name": "chance-1",
+ "index": [4, 2]
+ },
+ {
+ "name": "chance-2",
+ "index": [5, 2]
+ },
+ {
+ "name": "chance-3",
+ "index": [6, 2]
+ },
+ {
+ "name": "coin-1",
+ "index": [15, 3]
+ },
+ {
+ "name": "coin-2",
+ "index": [15, 4]
+ },
+ {
+ "name": "coin-3",
+ "index": [15, 5]
+ },
+ {
+ "name": "pipe-insert-vert-left",
+ "index": [2, 5]
+ },
+ {
+ "name": "pipe-insert-vert-right",
+ "index": [3, 5]
+ },
+ {
+ "name": "pipe-vert-left",
+ "index": [2, 6]
+ },
+ {
+ "name": "pipe-vert-right",
+ "index": [3, 6]
+ },
+ {
+ "name": "pipe-insert-hor-top",
+ "index": [6, 5]
+ },
+ {
+ "name": "pipe-insert-hor-bottom",
+ "index": [6, 6]
+ },
+ {
+ "name": "pipe-hor-top",
+ "index": [7, 5]
+ },
+ {
+ "name": "pipe-hor-bottom",
+ "index": [7, 6]
+ },
+ {
+ "name": "pipe-conn-hor-top",
+ "index": [8, 5]
+ },
+ {
+ "name": "pipe-conn-hor-bottom",
+ "index": [8, 6]
+ }
+
+ ],
+
+ "animations": [
+ {
+ "name": "chance",
+ "frameLen": 0.16,
+ "frames": [
+ "chance-1",
+ "chance-1",
+ "chance-2",
+ "chance-3",
+ "chance-2"
+ ]
+ },
+ {
+ "name": "coin",
+ "frameLen": 0.16,
+ "frames": [
+ "coin-1",
+ "coin-1",
+ "coin-2",
+ "coin-3",
+ "coin-2"
+ ]
}
]
}