From 37967f50aa3fadb3e86f58c81846b6867527e13d Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Sun, 12 Dec 2021 06:44:57 -0500 Subject: [PATCH 01/15] Fix collision bug --- main.js | 2 +- modules/mover.mjs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/main.js b/main.js index dc0fcbd..f067253 100644 --- a/main.js +++ b/main.js @@ -46,7 +46,7 @@ document.addEventListener('keydown', e => { if (GAME.debug) speed = 0; break; case '9': - if (GAME.debug) speed = 300; + if (GAME.debug) speed = 1000; break; case '8': if (GAME.debug) speed = 100; diff --git a/modules/mover.mjs b/modules/mover.mjs index 9dc8545..6d817dc 100644 --- a/modules/mover.mjs +++ b/modules/mover.mjs @@ -38,25 +38,29 @@ class Mover extends Object { let y = false; - if (this.vel.y >= that.nvel.y && this.pos.y < that.box.top && that.box.top > this.box.top && that.box.top <= this.box.bottom) { + if (this.pos.y < that.box.top && that.box.top > this.box.top && that.box.top <= this.box.bottom) { + if (this.pos.y < that.pos.y && this.nvel.y < that.nvel.y) return; if (!this.touch.bottom || (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 > that.npos.y - that.dim.h / 2)) { this.touch.bottom = that; } y = true; } - if (this.vel.y <= that.nvel.y && this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom) { + if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom) { + if (this.pos.y > that.pos.y && this.nvel.y > that.nvel.y) return; if (!this.touch.top || (this.touch.top.npos.y + this.touch.top.dim.h / 2 > that.npos.y + that.dim.h / 2)) { this.touch.top = that; } y = true; } if (y) return; - if (this.vel.x >= that.nvel.x && this.pos.x < that.box.left && that.box.left > this.box.left && that.box.left <= this.box.right) { + if (this.pos.x < that.box.left && that.box.left > this.box.left && that.box.left <= this.box.right) { + if (this.pos.x < that.pos.x && this.nvel.x < that.nvel.x) return; if (!this.touch.right || (this.touch.right.npos.x - this.touch.right.dim.h / 2 > that.npos.x - that.dim.w / 2)) { this.touch.right = that; } } - if (this.vel.x <= that.nvel.x && this.pos.x > that.box.right && that.box.right >= this.box.left && that.box.right < this.box.right) { + if (this.pos.x > that.box.right && that.box.right >= this.box.left && that.box.right < this.box.right) { + if (this.pos.x > that.pos.x && this.nvel.x > that.nvel.x) return; if (!this.touch.left || (this.touch.left.npos.x + this.touch.left.dim.h / 2 > that.npos.x + that.dim.w / 2)) { this.touch.left = that; } From f21159995f3f88feb1470ecdb73413b5860ce346 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Sun, 12 Dec 2021 08:19:06 -0500 Subject: [PATCH 02/15] Further fix bugs --- main.js | 4 ++++ modules/mover.mjs | 21 ++++++++------------- modules/player.mjs | 5 +++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/main.js b/main.js index f067253..9496c93 100644 --- a/main.js +++ b/main.js @@ -86,6 +86,8 @@ function generateLevels() { stair.addSpawn(new Spawn({ x: 300, y: 475 }, players[0])); stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); + //stair.addObject(new Platform([{ x: 310, y: 625 }, { x: 550, y: 625 }], { w: 800, h: 60 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 60 }), { x: 0, y: 0 }, 3)); + stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 50 }))); stair.addObject(new Ground({ x: 310, y: 525 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 360, y: 475 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 410, y: 425 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); @@ -99,6 +101,8 @@ function generateLevels() { stair.addObject(new Door({ x: 360, y: 448.5 }, { w: 16, h: 3 }, [new Texture('../assets/interactive/button-up.png', { w: 16, h: 6 }), new Texture('../assets/interactive/button-down.png', { w: 16, h: 6 })], { x: 0, y: -1.5 }, stairP1)); stair.addObject(new Door({ x: 410, y: 398.5 }, { w: 16, h: 3 }, [new Texture('../assets/interactive/button-up.png', { w: 16, h: 6 }), new Texture('../assets/interactive/button-down.png', { w: 16, h: 6 })], { x: 0, y: -1.5 }, stairP2)); + stair.addObject(new Box({ x: 200, y: 500 }, { w: 16, h: 16 }, new Texture('../assets/interactive/box-small.png', { w: 16, h: 16 }))); + stair.addGoal(new Goal({ x: 470, y: 425 }, { w: 16, h: 6 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 16, h: 6 }))); stair.addGoal(new Goal({ x: 450, y: 425 }, { w: 16, h: 6 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 16, h: 6 }))); diff --git a/modules/mover.mjs b/modules/mover.mjs index 6d817dc..e0a8c0c 100644 --- a/modules/mover.mjs +++ b/modules/mover.mjs @@ -36,31 +36,26 @@ class Mover extends Object { (this.pos.x + this.dim.w / 2 < that.pos.x - that.dim.w / 2 && this.npos.x + this.dim.w / 2 < that.npos.x - that.dim.w / 2) || (this.pos.x - this.dim.w / 2 > that.pos.x + that.dim.w / 2 && this.npos.x - this.dim.w / 2 > that.npos.x + that.dim.w / 2)) return; - let y = false; - - if (this.pos.y < that.box.top && that.box.top > this.box.top && that.box.top <= this.box.bottom) { - if (this.pos.y < that.pos.y && this.nvel.y < that.nvel.y) return; + if (this.pos.y < that.box.top && that.box.top > this.box.top && that.box.top <= this.box.bottom && this.nvel.y >= that.nvel.y && + this.pos.x + this.dim.w / 2 > that.pos.x - that.dim.w / 2 && this.pos.x - this.dim.w / 2 < that.pos.x + that.dim.w / 2) { if (!this.touch.bottom || (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 > that.npos.y - that.dim.h / 2)) { this.touch.bottom = that; } - y = true; } - if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom) { - if (this.pos.y > that.pos.y && this.nvel.y > that.nvel.y) return; + if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom && this.nvel.y <= that.nvel.y && + this.pos.x + this.dim.w / 2 > that.pos.x - that.dim.w / 2 && this.pos.x - this.dim.w / 2 < that.pos.x + that.dim.w / 2) { if (!this.touch.top || (this.touch.top.npos.y + this.touch.top.dim.h / 2 > that.npos.y + that.dim.h / 2)) { this.touch.top = that; } - y = true; } - if (y) return; - if (this.pos.x < that.box.left && that.box.left > this.box.left && that.box.left <= this.box.right) { - if (this.pos.x < that.pos.x && this.nvel.x < that.nvel.x) return; + if (this.pos.x < that.box.left && that.box.left > this.box.left && that.box.left <= this.box.right && this.nvel.x >= that.nvel.x && + this.pos.y + this.dim.h / 2 > that.pos.y - that.dim.h / 2 && this.pos.y - this.dim.h / 2 < that.pos.y + that.dim.h / 2) { if (!this.touch.right || (this.touch.right.npos.x - this.touch.right.dim.h / 2 > that.npos.x - that.dim.w / 2)) { this.touch.right = that; } } - if (this.pos.x > that.box.right && that.box.right >= this.box.left && that.box.right < this.box.right) { - if (this.pos.x > that.pos.x && this.nvel.x > that.nvel.x) return; + if (this.pos.x > that.box.right && that.box.right >= this.box.left && that.box.right < this.box.right && this.nvel.x <= that.nvel.x && + this.pos.y + this.dim.h / 2 > that.pos.y - that.dim.h / 2 && this.pos.y - this.dim.h / 2 < that.pos.y + that.dim.h / 2) { if (!this.touch.left || (this.touch.left.npos.x + this.touch.left.dim.h / 2 > that.npos.x + that.dim.w / 2)) { this.touch.left = that; } diff --git a/modules/player.mjs b/modules/player.mjs index d516994..468e740 100644 --- a/modules/player.mjs +++ b/modules/player.mjs @@ -12,12 +12,13 @@ class Player extends Mover { move() { let nstate = 'idle'; + let real = this.nvel.x - (this.ground ? this.ground.nvel.x : 0); - this.dir = this.vel.x === 0 ? this.dir : (this.vel.x > 0 ? 1 : 0); + this.dir = real === 0 ? this.dir : (real > 0 ? 1 : 0); if (!this.touch.bottom) { this.texture = this.sprite.data.jump[this.dir]; nstate = 'jump'; - } else if (Math.abs(this.vel.x) > 0.5) { + } else if (Math.abs(real) > 0.25) { this.texture = this.sprite.data.run[this.dir]; nstate = 'run'; } else { From e7970201f1b6855ef89f0df36ed245dacd342ecc Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Sun, 12 Dec 2021 08:40:51 -0500 Subject: [PATCH 03/15] Make collisions prefer closer if equal --- modules/mover.mjs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/mover.mjs b/modules/mover.mjs index e0a8c0c..59c6317 100644 --- a/modules/mover.mjs +++ b/modules/mover.mjs @@ -38,25 +38,37 @@ class Mover extends Object { if (this.pos.y < that.box.top && that.box.top > this.box.top && that.box.top <= this.box.bottom && this.nvel.y >= that.nvel.y && this.pos.x + this.dim.w / 2 > that.pos.x - that.dim.w / 2 && this.pos.x - this.dim.w / 2 < that.pos.x + that.dim.w / 2) { - if (!this.touch.bottom || (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 > that.npos.y - that.dim.h / 2)) { + if (!this.touch.bottom || + (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 > that.npos.y - that.dim.h / 2) || + (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 === that.npos.y - that.dim.h / 2 && + Math.abs(this.touch.bottom.npos.x - this.npos.x) > Math.abs(that.npos.x - this.npos.x))) { this.touch.bottom = that; } } if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom && this.nvel.y <= that.nvel.y && this.pos.x + this.dim.w / 2 > that.pos.x - that.dim.w / 2 && this.pos.x - this.dim.w / 2 < that.pos.x + that.dim.w / 2) { - if (!this.touch.top || (this.touch.top.npos.y + this.touch.top.dim.h / 2 > that.npos.y + that.dim.h / 2)) { + if (!this.touch.top || + (this.touch.top.npos.y + this.touch.top.dim.h / 2 > that.npos.y + that.dim.h / 2) || + (this.touch.top.npos.y + this.touch.top.dim.h / 2 === that.npos.y + that.dim.h / 2 && + Math.abs(this.touch.top.npos.x - this.npos.x) > Math.abs(that.npos.x - this.npos.x))) { this.touch.top = that; } } if (this.pos.x < that.box.left && that.box.left > this.box.left && that.box.left <= this.box.right && this.nvel.x >= that.nvel.x && this.pos.y + this.dim.h / 2 > that.pos.y - that.dim.h / 2 && this.pos.y - this.dim.h / 2 < that.pos.y + that.dim.h / 2) { - if (!this.touch.right || (this.touch.right.npos.x - this.touch.right.dim.h / 2 > that.npos.x - that.dim.w / 2)) { + if (!this.touch.right || + (this.touch.right.npos.x - this.touch.right.dim.h / 2 > that.npos.x - that.dim.w / 2) || + (this.touch.right.npos.x - this.touch.right.dim.h / 2 === that.npos.x - that.dim.w / 2 && + Math.abs(this.touch.right.npos.y - this.npos.y) > Math.abs(that.npos.y - this.npos.y))) { this.touch.right = that; } } if (this.pos.x > that.box.right && that.box.right >= this.box.left && that.box.right < this.box.right && this.nvel.x <= that.nvel.x && this.pos.y + this.dim.h / 2 > that.pos.y - that.dim.h / 2 && this.pos.y - this.dim.h / 2 < that.pos.y + that.dim.h / 2) { - if (!this.touch.left || (this.touch.left.npos.x + this.touch.left.dim.h / 2 > that.npos.x + that.dim.w / 2)) { + if (!this.touch.left || + (this.touch.left.npos.x + this.touch.left.dim.h / 2 > that.npos.x + that.dim.w / 2) || + (this.touch.left.npos.x + this.touch.left.dim.h / 2 > that.npos.x + that.dim.w / 2 && + Math.abs(this.touch.left.npos.y - this.npos.y) > Math.abs(that.npos.y - this.npos.y))) { this.touch.left = that; } } From 268fe808f9e0000dda80997f34cceaf150957f23 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Sun, 12 Dec 2021 09:07:11 -0500 Subject: [PATCH 04/15] Add death --- main.js | 1 + modules/game.mjs | 16 ++++++++++++++-- modules/object.mjs | 8 ++++++++ modules/player.mjs | 18 +++++++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index 9496c93..1f28782 100644 --- a/main.js +++ b/main.js @@ -87,6 +87,7 @@ function generateLevels() { stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); //stair.addObject(new Platform([{ x: 310, y: 625 }, { x: 550, y: 625 }], { w: 800, h: 60 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 60 }), { x: 0, y: 0 }, 3)); + stair.addObject(new Spike({ x: 310, y: 625-64 }, { w: 800, h: 50 }, new Texture('../assets/interactive/spike-small.png', { w: 800, h: 50 }))); stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 50 }))); stair.addObject(new Ground({ x: 310, y: 525 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 360, y: 475 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); diff --git a/modules/game.mjs b/modules/game.mjs index eb6e957..72e32d7 100644 --- a/modules/game.mjs +++ b/modules/game.mjs @@ -69,6 +69,7 @@ class Game { gameTick() { let level = this.levels[this.level]; let objects = level.objects.concat(this.players).filter(o => !o.dead); + let ghosts = level.objects.concat(this.players).filter(o => o.dead); this.players[0].tag = "P1"; this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); @@ -99,9 +100,15 @@ class Game { this.draw(objects[i]); } + for (let i = 0; i < ghosts.length; i++) { + this.draw(ghosts[i], ghosts[i].dying()); + } + this.camera.update(this.players); - if (level.goals[0].player && level.goals[1].player) { + if (this.players[0].fade === 0 || this.players[1].fade === 0) { + this.resetLevel(); + } else if (level.goals[0].player && level.goals[1].player) { this.nextLevel(); } @@ -146,14 +153,19 @@ class Game { 1024 * real); } - draw(object) { + draw(object, opacity = 1) { let real = this.camera.zoom * this.canvas.width / 1000; + this.context.save(); + this.context.globalAlpha = opacity; + this.context.drawImage(object.texture.draw(), 0, 0, object.texture.dim.w, object.texture.dim.h, this.canvas.width / 2 + (object.pos.x + object.offset.x - object.texture.dim.w / 2 - this.camera.pos.x) * real, this.canvas.height / 2 + (object.pos.y + object.offset.y - object.texture.dim.h / 2 - this.camera.pos.y) * real, object.texture.dim.w * real, object.texture.dim.h * real); + + this.context.restore(); if (!this.debug) return; diff --git a/modules/object.mjs b/modules/object.mjs index 301cfac..3be1d14 100644 --- a/modules/object.mjs +++ b/modules/object.mjs @@ -17,12 +17,14 @@ class Object { this.dead = false; this.waiting = false; + this.fade = 100; } init() { this.pos = { ...this.base }; this.vel = { x: 0, y: 0 }; this.dead = this.waiting; + this.fade = this.waiting ? 0 : 50; } update() { @@ -56,6 +58,12 @@ class Object { wait() { this.waiting = true; this.dead = true; + this.fade = 0; + } + + dying() { + this.fade = this.fade === 0 ? 0 : this.fade - 1; + return this.fade / 100; } } diff --git a/modules/player.mjs b/modules/player.mjs index 468e740..22676ba 100644 --- a/modules/player.mjs +++ b/modules/player.mjs @@ -10,11 +10,18 @@ class Player extends Mover { this.state = 'idle'; } + init() { + this.dir = 1; + this.state = 'idle'; + + super.init(); + } + move() { let nstate = 'idle'; let real = this.nvel.x - (this.ground ? this.ground.nvel.x : 0); - this.dir = real === 0 ? this.dir : (real > 0 ? 1 : 0); + if (!this.touch.bottom) { this.texture = this.sprite.data.jump[this.dir]; nstate = 'jump'; @@ -32,6 +39,15 @@ class Player extends Mover { super.move(); } + + die() { + let real = this.nvel.x - (this.ground ? this.ground.nvel.x : 0); + this.dir = real === 0 ? this.dir : (real > 0 ? 1 : 0); + + this.texture = this.sprite.data.dead[this.dir]; + + super.die(); + } } export { Player }; \ No newline at end of file From 196917968120640b531803d1001c6122ae406f40 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Sun, 12 Dec 2021 09:18:38 -0500 Subject: [PATCH 05/15] Add more death cases --- main.js | 4 ++-- modules/mover.mjs | 7 ++++--- modules/object.mjs | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/main.js b/main.js index 1f28782..43c9f6c 100644 --- a/main.js +++ b/main.js @@ -87,8 +87,8 @@ function generateLevels() { stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); //stair.addObject(new Platform([{ x: 310, y: 625 }, { x: 550, y: 625 }], { w: 800, h: 60 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 60 }), { x: 0, y: 0 }, 3)); - stair.addObject(new Spike({ x: 310, y: 625-64 }, { w: 800, h: 50 }, new Texture('../assets/interactive/spike-small.png', { w: 800, h: 50 }))); - stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 50 }))); + //stair.addObject(new Spike({ x: 310, y: 625-64 }, { w: 800, h: 50 }, new Texture('../assets/interactive/spike-small.png', { w: 800, h: 50 }))); + //stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 50 }))); stair.addObject(new Ground({ x: 310, y: 525 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 360, y: 475 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 410, y: 425 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); diff --git a/modules/mover.mjs b/modules/mover.mjs index 59c6317..500a94e 100644 --- a/modules/mover.mjs +++ b/modules/mover.mjs @@ -13,7 +13,7 @@ class Mover extends Object { this.ground = this.touch.bottom; while (this.ground) { - if (!this.ground.touch) break; + if (!this.ground.touch || !this.ground.touch.bottom) break; this.ground = this.ground.touch.bottom; } @@ -43,6 +43,7 @@ class Mover extends Object { (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 === that.npos.y - that.dim.h / 2 && Math.abs(this.touch.bottom.npos.x - this.npos.x) > Math.abs(that.npos.x - this.npos.x))) { this.touch.bottom = that; + if (this.vel.y > 7.25) this.die(); } } if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom && this.nvel.y <= that.nvel.y && @@ -99,7 +100,7 @@ class Mover extends Object { this.ground = this.touch.bottom; while (this.ground) { - if (!this.ground.touch) break; + if (!this.ground.touch || !this.ground.touch.bottom) break; this.ground = this.ground.touch.bottom; } @@ -110,7 +111,7 @@ class Mover extends Object { } this.vel.x *= this.touch.bottom ? 0.5 : 0.75; - this.vel.y *= 0.875; + this.vel.y *= 0.9375; this.vel.y += 0.5; } diff --git a/modules/object.mjs b/modules/object.mjs index 3be1d14..b92971c 100644 --- a/modules/object.mjs +++ b/modules/object.mjs @@ -49,6 +49,10 @@ class Object { move() { this.pos = { ...this.cpos }; this.vel = { ...this.cvel }; + + if (this.pos.y > 1024) { + this.die(); + } } die(state = true) { From e07d448a3a3ff44df68fb9e0a9bb6c34853fdcd5 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 01:10:05 -0500 Subject: [PATCH 06/15] Add link level --- assets/background/outside.png | Bin 45980 -> 24610 bytes main.css | 2 +- main.js | 54 +++++++++++++++++++++++++++++----- modules/game.mjs | 12 ++++++++ modules/goal.mjs | 5 ++-- modules/link.mjs | 7 ++++- 6 files changed, 69 insertions(+), 11 deletions(-) diff --git a/assets/background/outside.png b/assets/background/outside.png index 4434a6f22106c02fe6f454a1389d6454c3233e97..42435df8dc2e43cdfe1aca4bb442ddfcef563e08 100644 GIT binary patch literal 24610 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 Date: Mon, 13 Dec 2021 01:34:30 -0500 Subject: [PATCH 07/15] Add sandwich --- main.js | 3 ++- modules/sandwich.mjs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 modules/sandwich.mjs diff --git a/main.js b/main.js index 9496c93..ec25fd0 100644 --- a/main.js +++ b/main.js @@ -17,6 +17,7 @@ import { Link } from './modules/link.mjs'; import { Portal } from './modules/portal.mjs'; import { Box } from './modules/box.mjs'; import { Goal } from './modules/goal.mjs'; +import { Sandwich } from './modules/sandwich.mjs'; const GAME = new Game(new Canvas(document.getElementById('canvas'))); @@ -87,7 +88,7 @@ function generateLevels() { stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); //stair.addObject(new Platform([{ x: 310, y: 625 }, { x: 550, y: 625 }], { w: 800, h: 60 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 60 }), { x: 0, y: 0 }, 3)); - stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 50 }))); + stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Sandwich(['../assets/ground/brick-small-side.png', '../assets/ground/brick-small-middle.png'], { w: 800, h: 50 }))); stair.addObject(new Ground({ x: 310, y: 525 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 360, y: 475 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); stair.addObject(new Ground({ x: 410, y: 425 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); diff --git a/modules/sandwich.mjs b/modules/sandwich.mjs new file mode 100644 index 0000000..0c148a8 --- /dev/null +++ b/modules/sandwich.mjs @@ -0,0 +1,42 @@ +import { Texture } from "./texture.mjs"; + +class Sandwich extends Texture { + constructor(urls, dim) { + super(urls[0], dim, false); + + this.urls = urls; + + this.load(this.urls, 0); + this.load(this.urls, 1); + } + + load(urls, part) { + let source = new Image(); + source.src = urls[part]; + source.addEventListener('load', () => { + switch (part) { + case 0: + for (let y = 0; y < this.dim.h; y += source.height) { + this.image.getContext('2d').drawImage(source, 0, y); + } + this.image.getContext('2d').save(); + this.image.getContext('2d').translate(source.width, 0); + this.image.getContext('2d').scale(-1, 1); + for (let y = 0; y < this.dim.h; y += source.height) { + this.image.getContext('2d').drawImage(source, source.width - this.dim.w, y); + } + this.image.getContext('2d').restore(); + break; + case 1: + for (let x = source.width; x < this.dim.w - source.width; x += source.width) { + for (let y = 0; y < this.dim.h; y += source.height) { + this.image.getContext('2d').drawImage(source, x, y); + } + } + break; + } + }); + } +} + +export { Sandwich }; \ No newline at end of file From 0cb65c839e5de5cbec304e4db1588d78909d69e2 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 01:42:40 -0500 Subject: [PATCH 08/15] Kind of tile background nicely --- modules/game.mjs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/game.mjs b/modules/game.mjs index eb6e957..0761e00 100644 --- a/modules/game.mjs +++ b/modules/game.mjs @@ -139,11 +139,14 @@ class Game { background(level) { let real = this.camera.zoom * this.canvas.width / 1000; - this.context.drawImage(level.background.draw(), - this.canvas.width / 2 - this.camera.pos.x * real, - this.canvas.height / 2 - this.camera.pos.y * real, - 1024 * real, - 1024 * real); + for (let x = 1024 * Math.floor(Math.min(this.players[0].pos.x, this.players[1].pos.x) / 1024 - 1); + x < 1024 * Math.ceil(Math.max(this.players[0].pos.x, this.players[1].pos.x) / 1024 + 1); x += 1024) { + this.context.drawImage(level.background.draw(), + this.canvas.width / 2 - this.camera.pos.x * real + x * real, + this.canvas.height / 2 - this.camera.pos.y * real, + 1024 * real, + 1024 * real); + } } draw(object) { From eb22f018ea26f68bbb33efe2f1d6d709ffae5d90 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 01:51:11 -0500 Subject: [PATCH 09/15] Implement and fix sandwich --- main.js | 27 +++++++++++++++++---------- modules/sandwich.mjs | 4 ++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/main.js b/main.js index 02a5c73..9fb1d94 100644 --- a/main.js +++ b/main.js @@ -90,31 +90,38 @@ function generateLevels() { down: new Texture('../assets/interactive/button-down.png', { w: 16, h: 6 }) }; + let ground = ['../assets/ground/ground-side.png', '../assets/ground/ground-middle.png']; + let cracked = ['../assets/ground/brick-cracked-side.png', '../assets/ground/brick-cracked-middle.png']; + let bricks = { + big: ['../assets/ground/brick-big-side.png', '../assets/ground/brick-big-middle.png'], + small: ['../assets/ground/brick-small-side.png', '../assets/ground/brick-small-middle.png'] + }; + let links = new Level(); links.addBackground(backgrounds.outside); links.addSpawn(new Spawn({ x: 400, y: 240 }, players[0])); links.addSpawn(new Spawn({ x: 400, y: 400 }, players[1])); - links.addObject(new Ground({ x: 440, y: 280 }, { w: 160, h: 32 }, new Texture('../assets/ground/ground-middle.png', { w: 160, h: 32 }))); - links.addObject(new Ground({ x: 440, y: 440 }, { w: 160, h: 32 }, new Texture('../assets/ground/ground-middle.png', { w: 160, h: 32 }))); + links.addObject(new Ground({ x: 440, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); + links.addObject(new Ground({ x: 440, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); - let cracked1 = new Cracked({ x: 600, y: 280 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 80, h: 16 })); + let cracked1 = new Cracked({ x: 600, y: 280 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked1); links.addObject(new Spike({ x: 600, y: 296 }, { w: 80, h: 16 }, new Texture('../assets/interactive/spike-small.png', { w: 80, h: 16 }))); - links.addObject(new Link({ x: 600, y: 440 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-small-middle.png', { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); + links.addObject(new Link({ x: 600, y: 440 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); - let cracked2 = new Cracked({ x: 760, y: 440 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 80, h: 16 })); + let cracked2 = new Cracked({ x: 760, y: 440 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked2); - links.addObject(new Link({ x: 760, y: 280 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-small-middle.png', { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); + links.addObject(new Link({ x: 760, y: 280 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); - let cracked3 = new Cracked({ x: 920, y: 280 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 80, h: 16 })); + let cracked3 = new Cracked({ x: 920, y: 280 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked3); links.addObject(new Spike({ x: 920, y: 296 }, { w: 80, h: 16 }, new Texture('../assets/interactive/spike-small.png', { w: 80, h: 16 }))); - links.addObject(new Link({ x: 920, y: 440 }, { w: 80, h: 16 }, new Texture('../assets/ground/brick-small-middle.png', { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); + links.addObject(new Link({ x: 920, y: 440 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); - links.addObject(new Ground({ x: 1080, y: 280 }, { w: 160, h: 32 }, new Texture('../assets/ground/ground-middle.png', { w: 160, h: 32 }))); - links.addObject(new Ground({ x: 1080, y: 440 }, { w: 160, h: 32 }, new Texture('../assets/ground/ground-middle.png', { w: 160, h: 32 }))); + links.addObject(new Ground({ x: 1080, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); + links.addObject(new Ground({ x: 1080, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addGoal(new Goal({ x: 1176, y: 280 })); links.addGoal(new Goal({ x: 1176, y: 440 })); diff --git a/modules/sandwich.mjs b/modules/sandwich.mjs index 0c148a8..af7b834 100644 --- a/modules/sandwich.mjs +++ b/modules/sandwich.mjs @@ -6,6 +6,10 @@ class Sandwich extends Texture { this.urls = urls; + this.image = document.createElement('canvas'); + this.image.width = this.dim.w; + this.image.height = this.dim.h; + this.load(this.urls, 0); this.load(this.urls, 1); } From 938f27aaec87225f260a84a619021cb61c093fd5 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 01:57:53 -0500 Subject: [PATCH 10/15] Refine link level --- main.css | 2 +- main.js | 22 ++++++++++++++-------- modules/link.mjs | 5 +++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/main.css b/main.css index 13528f3..a1d49d3 100644 --- a/main.css +++ b/main.css @@ -29,7 +29,7 @@ body { } canvas { - background-color: #89c9e4; + background-color: #477553; border-radius: 10px; } diff --git a/main.js b/main.js index 9fb1d94..1d5dca3 100644 --- a/main.js +++ b/main.js @@ -92,6 +92,10 @@ function generateLevels() { let ground = ['../assets/ground/ground-side.png', '../assets/ground/ground-middle.png']; let cracked = ['../assets/ground/brick-cracked-side.png', '../assets/ground/brick-cracked-middle.png']; + let spike = { + small: '../assets/interactive/spike-small.png', + big: '../assets/interactive/spike-small.png' + }; let bricks = { big: ['../assets/ground/brick-big-side.png', '../assets/ground/brick-big-middle.png'], small: ['../assets/ground/brick-small-side.png', '../assets/ground/brick-small-middle.png'] @@ -106,19 +110,21 @@ function generateLevels() { links.addObject(new Ground({ x: 440, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addObject(new Ground({ x: 440, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); - let cracked1 = new Cracked({ x: 600, y: 280 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + let cracked1 = new Cracked({ x: 600, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked1); - links.addObject(new Spike({ x: 600, y: 296 }, { w: 80, h: 16 }, new Texture('../assets/interactive/spike-small.png', { w: 80, h: 16 }))); - links.addObject(new Link({ x: 600, y: 440 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); + links.addObject(new Spike({ x: 600, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); + links.addObject(new Ground({ x: 600, y: 304 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }))); + links.addObject(new Link({ x: 600, y: 432 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); - let cracked2 = new Cracked({ x: 760, y: 440 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + let cracked2 = new Cracked({ x: 760, y: 432 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked2); - links.addObject(new Link({ x: 760, y: 280 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); + links.addObject(new Link({ x: 760, y: 272 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); - let cracked3 = new Cracked({ x: 920, y: 280 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + let cracked3 = new Cracked({ x: 920, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked3); - links.addObject(new Spike({ x: 920, y: 296 }, { w: 80, h: 16 }, new Texture('../assets/interactive/spike-small.png', { w: 80, h: 16 }))); - links.addObject(new Link({ x: 920, y: 440 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); + links.addObject(new Spike({ x: 920, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); + links.addObject(new Ground({ x: 920, y: 304 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }))); + links.addObject(new Link({ x: 920, y: 432 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); links.addObject(new Ground({ x: 1080, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addObject(new Ground({ x: 1080, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); diff --git a/modules/link.mjs b/modules/link.mjs index 20a39a9..15fdb7d 100644 --- a/modules/link.mjs +++ b/modules/link.mjs @@ -7,8 +7,13 @@ class Link extends Interactive { this.object = object; this.object.wait(); + this.played = false; + } + init() { this.played = false; + + super.init(); } trigger() { From 3520890f1d5fec88b4876b9475b33387e1834a13 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 02:03:58 -0500 Subject: [PATCH 11/15] Finish link level --- main.js | 30 ++++++++++++++++-------------- modules/mover.mjs | 1 - 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/main.js b/main.js index 1d5dca3..50f0bcc 100644 --- a/main.js +++ b/main.js @@ -80,12 +80,12 @@ function generateLevels() { GAME.addInput(new Input(players[0])); GAME.addInput(new Input(players[1], { left: 'arrowleft', right: 'arrowright', up: 'arrowup' })); - let backgrounds = { + let backgroundTextures = { outside: new Texture('../assets/background/outside.png', { w: 1024, h: 1024 }), inside: new Texture('../assets/background/inside.png', { w: 1024, h: 1024 }) }; - let buttons = { + let buttonTextures = { up: new Texture('../assets/interactive/button-up.png', { w: 16, h: 6 }), down: new Texture('../assets/interactive/button-down.png', { w: 16, h: 6 }) }; @@ -93,16 +93,16 @@ function generateLevels() { let ground = ['../assets/ground/ground-side.png', '../assets/ground/ground-middle.png']; let cracked = ['../assets/ground/brick-cracked-side.png', '../assets/ground/brick-cracked-middle.png']; let spike = { - small: '../assets/interactive/spike-small.png', - big: '../assets/interactive/spike-small.png' + big: '../assets/interactive/spike-small.png', + small: '../assets/interactive/spike-small.png' }; - let bricks = { + let brick = { big: ['../assets/ground/brick-big-side.png', '../assets/ground/brick-big-middle.png'], small: ['../assets/ground/brick-small-side.png', '../assets/ground/brick-small-middle.png'] }; let links = new Level(); - links.addBackground(backgrounds.outside); + links.addBackground(backgroundTextures.outside); links.addSpawn(new Spawn({ x: 400, y: 240 }, players[0])); links.addSpawn(new Spawn({ x: 400, y: 400 }, players[1])); @@ -113,29 +113,31 @@ function generateLevels() { let cracked1 = new Cracked({ x: 600, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked1); links.addObject(new Spike({ x: 600, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); - links.addObject(new Ground({ x: 600, y: 304 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }))); - links.addObject(new Link({ x: 600, y: 432 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); + links.addObject(new Ground({ x: 600, y: 304 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }))); + links.addObject(new Link({ x: 600, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); let cracked2 = new Cracked({ x: 760, y: 432 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked2); - links.addObject(new Link({ x: 760, y: 272 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); + links.addObject(new Link({ x: 760, y: 272 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); let cracked3 = new Cracked({ x: 920, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); links.addObject(cracked3); links.addObject(new Spike({ x: 920, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); - links.addObject(new Ground({ x: 920, y: 304 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }))); - links.addObject(new Link({ x: 920, y: 432 }, { w: 80, h: 16 }, new Sandwich(bricks.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); + links.addObject(new Ground({ x: 920, y: 304 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }))); + links.addObject(new Link({ x: 920, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); links.addObject(new Ground({ x: 1080, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addObject(new Ground({ x: 1080, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addGoal(new Goal({ x: 1176, y: 280 })); + links.addObject(new Ground({ x: 1176, y: 288 }, { w: 32, h: 16 }, new Sandwich(brick.small, { w: 32, h: 32 }), { x: 0, y: -8 })); links.addGoal(new Goal({ x: 1176, y: 440 })); + links.addObject(new Ground({ x: 1176, y: 448 }, { w: 32, h: 16 }, new Sandwich(brick.small, { w: 32, h: 32 }), { x: 0, y: -8 })); GAME.addLevel(links); let stair = new Level(); - stair.addBackground(backgrounds.outside); + stair.addBackground(backgroundTextures.outside); stair.addSpawn(new Spawn({ x: 300, y: 475 }, players[0])); stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); @@ -154,8 +156,8 @@ function generateLevels() { stair.addObject(stairP1); stair.addObject(stairP2); - stair.addObject(new Door({ x: 360, y: 448.5 }, { w: 16, h: 3 }, [buttons.up, buttons.down], { x: 0, y: -1.5 }, stairP1)); - stair.addObject(new Door({ x: 410, y: 398.5 }, { w: 16, h: 3 }, [buttons.up, buttons.down], { x: 0, y: -1.5 }, stairP2)); + stair.addObject(new Door({ x: 360, y: 448.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP1)); + stair.addObject(new Door({ x: 410, y: 398.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP2)); stair.addObject(new Box({ x: 200, y: 500 }, { w: 16, h: 16 }, new Texture('../assets/interactive/box-small.png', { w: 16, h: 16 }))); diff --git a/modules/mover.mjs b/modules/mover.mjs index 500a94e..0deb9e9 100644 --- a/modules/mover.mjs +++ b/modules/mover.mjs @@ -43,7 +43,6 @@ class Mover extends Object { (this.touch.bottom.npos.y - this.touch.bottom.dim.h / 2 === that.npos.y - that.dim.h / 2 && Math.abs(this.touch.bottom.npos.x - this.npos.x) > Math.abs(that.npos.x - this.npos.x))) { this.touch.bottom = that; - if (this.vel.y > 7.25) this.die(); } } if (this.pos.y > that.box.bottom && that.box.bottom >= this.box.top && that.box.bottom < this.box.bottom && this.nvel.y <= that.nvel.y && From 175d2222ece5a97b53248690ea5a8fa704009fd4 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 02:27:32 -0500 Subject: [PATCH 12/15] Make stair level scale properly --- main.js | 63 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/main.js b/main.js index 50f0bcc..3437132 100644 --- a/main.js +++ b/main.js @@ -110,21 +110,21 @@ function generateLevels() { links.addObject(new Ground({ x: 440, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addObject(new Ground({ x: 440, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); - let cracked1 = new Cracked({ x: 600, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); - links.addObject(cracked1); + let linksC1 = new Cracked({ x: 600, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + links.addObject(linksC1); links.addObject(new Spike({ x: 600, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); links.addObject(new Ground({ x: 600, y: 304 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }))); - links.addObject(new Link({ x: 600, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked1)); + links.addObject(new Link({ x: 600, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, linksC1)); - let cracked2 = new Cracked({ x: 760, y: 432 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); - links.addObject(cracked2); - links.addObject(new Link({ x: 760, y: 272 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked2)); + let linksC2 = new Cracked({ x: 760, y: 432 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + links.addObject(linksC2); + links.addObject(new Link({ x: 760, y: 272 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, linksC2)); - let cracked3 = new Cracked({ x: 920, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); - links.addObject(cracked3); + let linksC3 = new Cracked({ x: 920, y: 272 }, { w: 80, h: 16 }, new Sandwich(cracked, { w: 80, h: 16 })); + links.addObject(linksC3); links.addObject(new Spike({ x: 920, y: 288 }, { w: 80, h: 16 }, new Texture(spike.small, { w: 80, h: 16 }))); links.addObject(new Ground({ x: 920, y: 304 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }))); - links.addObject(new Link({ x: 920, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, cracked3)); + links.addObject(new Link({ x: 920, y: 432 }, { w: 80, h: 16 }, new Sandwich(brick.small, { w: 80, h: 16 }), { x: 0, y: 0 }, linksC3)); links.addObject(new Ground({ x: 1080, y: 280 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); links.addObject(new Ground({ x: 1080, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); @@ -134,35 +134,36 @@ function generateLevels() { links.addGoal(new Goal({ x: 1176, y: 440 })); links.addObject(new Ground({ x: 1176, y: 448 }, { w: 32, h: 16 }, new Sandwich(brick.small, { w: 32, h: 32 }), { x: 0, y: -8 })); - GAME.addLevel(links); + //GAME.addLevel(links); let stair = new Level(); stair.addBackground(backgroundTextures.outside); + + stair.addSpawn(new Spawn({ x: 400, y: 400 }, players[0])); + stair.addSpawn(new Spawn({ x: 440, y: 400 }, players[1])); - stair.addSpawn(new Spawn({ x: 300, y: 475 }, players[0])); - stair.addSpawn(new Spawn({ x: 325, y: 475 }, players[1])); - - //stair.addObject(new Platform([{ x: 310, y: 625 }, { x: 550, y: 625 }], { w: 800, h: 60 }, new Texture('../assets/ground/brick-small-middle.png', { w: 800, h: 60 }), { x: 0, y: 0 }, 3)); - - stair.addObject(new Ground({ x: 310, y: 625-32 }, { w: 800, h: 50 }, new Sandwich(['../assets/ground/brick-small-side.png', '../assets/ground/brick-small-middle.png'], { w: 800, h: 50 }))); - - stair.addObject(new Ground({ x: 310, y: 525 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); - stair.addObject(new Ground({ x: 360, y: 475 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); - stair.addObject(new Ground({ x: 410, y: 425 }, { w: 40, h: 50 }, new Texture('../assets/ground/brick-small-middle.png', { w: 40, h: 50 }))); - - let stairP1 = new Platform([{ x: 335, y: 525 }, { x: 335, y: 475 }], { w: 10, h: 50 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 10, h: 50 }), { x: 0, y: 0 }, 3, 'pause'); - let stairP2 = new Platform([{ x: 385, y: 475 }, { x: 385, y: 425 }], { w: 10, h: 50 }, new Texture('../assets/ground/brick-cracked-middle.png', { w: 10, h: 50 }), { x: 0, y: 0 }, 3, 'pause'); + stair.addObject(new Ground({ x: 440, y: 440 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); + let stairP1 = new Platform([{ x: 536, y: 472 }, { x: 536, y: 376 }], { w: 32, h: 96 }, new Sandwich(cracked, { w: 32, h: 96 }), { x: 0, y: 0 }, 2, 'pause'); stair.addObject(stairP1); + stair.addObject(new Ground({ x: 584, y: 376 }, { w: 64, h: 96 }, new Sandwich(brick.big, { w: 64, h: 96 }))); + stair.addObject(new Door({ x: 584, y: 326.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP1)); + + let stairP2 = new Platform([{ x: 632, y: 376 }, { x: 632, y: 280 }], { w: 32, h: 96 }, new Sandwich(cracked, { w: 32, h: 96 }), { x: 0, y: 0 }, 2, 'pause'); stair.addObject(stairP2); - - stair.addObject(new Door({ x: 360, y: 448.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP1)); - stair.addObject(new Door({ x: 410, y: 398.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP2)); - - stair.addObject(new Box({ x: 200, y: 500 }, { w: 16, h: 16 }, new Texture('../assets/interactive/box-small.png', { w: 16, h: 16 }))); - - stair.addGoal(new Goal({ x: 510, y: 425 })); - stair.addGoal(new Goal({ x: 470, y: 425 })); + stair.addObject(new Ground({ x: 680, y: 280 }, { w: 64, h: 96 }, new Sandwich(brick.big, { w: 64, h: 96 }))); + stair.addObject(new Door({ x: 680, y: 230.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP2)); + + let stairP3 = new Platform([{ x: 728, y: 280 }, { x: 728, y: 184 }], { w: 32, h: 96 }, new Sandwich(cracked, { w: 32, h: 96 }), { x: 0, y: 0 }, 2, 'pause'); + stair.addObject(stairP3); + stair.addObject(new Ground({ x: 776, y: 184 }, { w: 64, h: 96 }, new Sandwich(brick.big, { w: 64, h: 96 }))); + stair.addObject(new Door({ x: 776, y: 134.5 }, { w: 16, h: 3 }, [buttonTextures.up, buttonTextures.down], { x: 0, y: -1.5 }, stairP3)); + + stair.addObject(new Ground({ x: 888, y: 152 }, { w: 160, h: 32 }, new Sandwich(ground, { w: 160, h: 32 }))); + + stair.addGoal(new Goal({ x: 984, y: 152 })); + stair.addGoal(new Goal({ x: 1048, y: 152 })); + stair.addObject(new Ground({ x: 1016, y: 160 }, { w: 96, h: 16 }, new Sandwich(brick.small, { w: 96, h: 32 }), { x: 0, y: -8 })); GAME.addLevel(stair); } \ No newline at end of file From 06ea0bab2f07b42bb44cc588d64bef3fcd5744e4 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 02:38:29 -0500 Subject: [PATCH 13/15] Set background colors --- main.css | 1 - main.js | 15 +++++++-------- modules/background.mjs | 8 ++++++++ modules/game.mjs | 16 ++++++++++++++-- modules/level.mjs | 2 +- 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 modules/background.mjs diff --git a/main.css b/main.css index a1d49d3..34dd9ed 100644 --- a/main.css +++ b/main.css @@ -29,7 +29,6 @@ body { } canvas { - background-color: #477553; border-radius: 10px; } diff --git a/main.js b/main.js index 3437132..2296251 100644 --- a/main.js +++ b/main.js @@ -1,11 +1,13 @@ import { Game } from './modules/game.mjs'; import { Canvas } from './modules/canvas.mjs'; import { Texture } from './modules/texture.mjs'; +import { Sandwich } from './modules/sandwich.mjs'; import { Animated } from './modules/animated.mjs'; import { Player } from './modules/player.mjs'; import { Sprite } from './modules/sprite.mjs'; import { Input } from './modules/input.mjs'; import { Level } from './modules/level.mjs'; +import { Background } from './modules/background.mjs'; import { Spawn } from './modules/spawn.mjs'; import { Ground } from './modules/ground.mjs'; import { Cracked } from './modules/cracked.mjs'; @@ -17,7 +19,6 @@ import { Link } from './modules/link.mjs'; import { Portal } from './modules/portal.mjs'; import { Box } from './modules/box.mjs'; import { Goal } from './modules/goal.mjs'; -import { Sandwich } from './modules/sandwich.mjs'; const GAME = new Game(new Canvas(document.getElementById('canvas'))); @@ -80,10 +81,7 @@ function generateLevels() { GAME.addInput(new Input(players[0])); GAME.addInput(new Input(players[1], { left: 'arrowleft', right: 'arrowright', up: 'arrowup' })); - let backgroundTextures = { - outside: new Texture('../assets/background/outside.png', { w: 1024, h: 1024 }), - inside: new Texture('../assets/background/inside.png', { w: 1024, h: 1024 }) - }; + let outside = new Background(new Texture('../assets/background/outside.png', { w: 1024, h: 1024 }), ['#89c9e4', '#477553']) let buttonTextures = { up: new Texture('../assets/interactive/button-up.png', { w: 16, h: 6 }), @@ -102,7 +100,7 @@ function generateLevels() { }; let links = new Level(); - links.addBackground(backgroundTextures.outside); + links.setBackground(outside); links.addSpawn(new Spawn({ x: 400, y: 240 }, players[0])); links.addSpawn(new Spawn({ x: 400, y: 400 }, players[1])); @@ -137,7 +135,7 @@ function generateLevels() { //GAME.addLevel(links); let stair = new Level(); - stair.addBackground(backgroundTextures.outside); + stair.setBackground(outside); stair.addSpawn(new Spawn({ x: 400, y: 400 }, players[0])); stair.addSpawn(new Spawn({ x: 440, y: 400 }, players[1])); @@ -163,7 +161,8 @@ function generateLevels() { stair.addGoal(new Goal({ x: 984, y: 152 })); stair.addGoal(new Goal({ x: 1048, y: 152 })); - stair.addObject(new Ground({ x: 1016, y: 160 }, { w: 96, h: 16 }, new Sandwich(brick.small, { w: 96, h: 32 }), { x: 0, y: -8 })); + stair.addObject(new Ground({ x: 1016, y: 160 }, { w: 96, h: 16 }, new Sandwich(brick.big, { w: 96, h: 32 }), { x: 0, y: -8 })); + stair.addObject(new Ground({ x: 1016, y: 152 }, { w: 32, h: 32 }, new Texture(brick.big[1], { w: 32, h: 32 }), { x: 0, y: 0 })); GAME.addLevel(stair); } \ No newline at end of file diff --git a/modules/background.mjs b/modules/background.mjs new file mode 100644 index 0000000..a0af00d --- /dev/null +++ b/modules/background.mjs @@ -0,0 +1,8 @@ +class Background { + constructor (texture, colors) { + this.texture = texture; + this.colors = colors; + } +} + +export { Background }; \ No newline at end of file diff --git a/modules/game.mjs b/modules/game.mjs index a559b6f..1d134de 100644 --- a/modules/game.mjs +++ b/modules/game.mjs @@ -148,11 +148,23 @@ class Game { for (let x = 1024 * Math.floor(Math.min(this.players[0].pos.x, this.players[1].pos.x) / 1024 - 1); x < 1024 * Math.ceil(Math.max(this.players[0].pos.x, this.players[1].pos.x) / 1024 + 1); x += 1024) { - this.context.drawImage(level.background.draw(), + this.context.drawImage(level.background.texture.draw(), this.canvas.width / 2 - this.camera.pos.x * real + x * real, this.canvas.height / 2 - this.camera.pos.y * real, 1024 * real, 1024 * real); + this.context.fillStyle = level.background.colors[0]; + this.context.fillRect( + this.canvas.width / 2 - this.camera.pos.x * real + x * real - 1, + this.canvas.height / 2 - this.camera.pos.y * real - 1024 * real - 1, + 1024 * real + 3, + 1024 * real + 3); + this.context.fillStyle = level.background.colors[1]; + this.context.fillRect( + this.canvas.width / 2 - this.camera.pos.x * real + x * real - 1, + this.canvas.height / 2 - this.camera.pos.y * real + 1024 * real - 1, + 1024 * real + 3, + 1024 * real + 3); } } @@ -167,7 +179,7 @@ class Game { this.canvas.height / 2 + (object.pos.y + object.offset.y - object.texture.dim.h / 2 - this.camera.pos.y) * real, object.texture.dim.w * real, object.texture.dim.h * real); - + this.context.restore(); if (!this.debug) return; diff --git a/modules/level.mjs b/modules/level.mjs index 386b180..5251e2d 100644 --- a/modules/level.mjs +++ b/modules/level.mjs @@ -19,7 +19,7 @@ class Level { this.goals.push(goal); } - addBackground(background) { + setBackground(background) { this.background = background; } } From a07b0187dd3ec61cd2933154d59c1c9ec5a3b1b6 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 02:42:45 -0500 Subject: [PATCH 14/15] Make camera reset on level start --- main.js | 2 +- modules/camera.mjs | 22 +++++++++++++++++----- modules/game.mjs | 1 + 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/main.js b/main.js index 2296251..c7fa956 100644 --- a/main.js +++ b/main.js @@ -132,7 +132,7 @@ function generateLevels() { links.addGoal(new Goal({ x: 1176, y: 440 })); links.addObject(new Ground({ x: 1176, y: 448 }, { w: 32, h: 16 }, new Sandwich(brick.small, { w: 32, h: 32 }), { x: 0, y: -8 })); - //GAME.addLevel(links); + GAME.addLevel(links); let stair = new Level(); stair.setBackground(outside); diff --git a/modules/camera.mjs b/modules/camera.mjs index 3667b46..5cef1a2 100644 --- a/modules/camera.mjs +++ b/modules/camera.mjs @@ -8,7 +8,23 @@ class Camera { } update(players) { - let target = { + let target = this.position(players); + + this.zoom = this.smooth.zoom * this.zoom + (1 - this.smooth.zoom) * target.zoom; + this.pos.x = this.smooth.pos * this.pos.x + (1 - this.smooth.pos) * target.pos.x; + this.pos.y = this.smooth.pos * this.pos.y + (1 - this.smooth.pos) * target.pos.y; + } + + snap(players) { + let target = this.position(players); + + this.zoom = target.zoom; + this.pos.x = target.pos.x; + this.pos.y = target.pos.y; + } + + position(players) { + return { pos: { x: (players[0].pos.x + players[1].pos.x) / 2, y: (players[0].pos.y + players[1].pos.y) / 2 @@ -16,10 +32,6 @@ class Camera { zoom: Math.min(500 / Math.abs(players[0].pos.x - players[1].pos.x), 500 / Math.abs(players[0].pos.y - players[1].pos.y), this.limit) }; - - this.zoom = this.smooth.zoom * this.zoom + (1 - this.smooth.zoom) * target.zoom; - this.pos.x = this.smooth.pos * this.pos.x + (1 - this.smooth.pos) * target.pos.x; - this.pos.y = this.smooth.pos * this.pos.y + (1 - this.smooth.pos) * target.pos.y; } } diff --git a/modules/game.mjs b/modules/game.mjs index 1d134de..148bafc 100644 --- a/modules/game.mjs +++ b/modules/game.mjs @@ -48,6 +48,7 @@ class Game { } this.resetLevel(); + this.camera.snap(this.players); } addPlayer(player) { From dc67fabac094a03e00ecde0362fc5e9e21ed1e75 Mon Sep 17 00:00:00 2001 From: Brandon Chung Date: Mon, 13 Dec 2021 02:49:03 -0500 Subject: [PATCH 15/15] Update info --- index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 05c1c56..94c3d64 100644 --- a/index.html +++ b/index.html @@ -3,22 +3,22 @@ - Final v0.2 + SpLit
-

You Won

+

You Won!

Some info
goes here