32fd1dde91
./script.js:9517054/301
231 lines
5.5 KiB
JavaScript
231 lines
5.5 KiB
JavaScript
import game from "/data.js"; // import the game data
|
|
|
|
// import elements from html
|
|
const canvas = document.getElementById("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
// resize the canvas to full screen
|
|
function resize() {
|
|
const w = window.innerWidth;
|
|
const h = window.innerHeight;
|
|
canvas.width = w;
|
|
canvas.height = h;
|
|
ctx.width = w;
|
|
ctx.height = h;
|
|
game.size = [w, h];
|
|
}
|
|
|
|
// reset the player to the last checkpoint (or start)
|
|
function die() {
|
|
game.player.x = game.player.savex;
|
|
game.player.y = game.player.savey;
|
|
game.player.velx = 0;
|
|
game.player.vely = 0;
|
|
}
|
|
|
|
function draw() {
|
|
if (!game.cache.dirty) return;
|
|
|
|
requestAnimationFrame(draw); // to continuously animate
|
|
|
|
const offx = game.camera[0] - game.size[0] / 2;
|
|
const offy = game.camera[1] - game.size[1] / 2;
|
|
const c = game.color;
|
|
|
|
const point = (x, y) => [x - offx, y - offy];
|
|
const color = (which) => (ctx.fillStyle = game.color[which]);
|
|
const rect = (x, y, w, h) => ctx.fillRect(...point(x, y), w, h);
|
|
|
|
// draw background
|
|
color("background");
|
|
ctx.fillRect(0, 0, game.size[0], game.size[1]);
|
|
color("dots");
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
for (let j = 0; j < 40; j++) {
|
|
const spacing = 60;
|
|
const depth = 0.5;
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
i * spacing - (offx % (spacing / depth)) * depth,
|
|
j * spacing - (offy % (spacing / depth)) * depth,
|
|
5,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
// draw checkpoint animation (if there is one)
|
|
if (game.anim[2] < 1) {
|
|
color("shadow");
|
|
ctx.globalAlpha = 1 - game.anim[2];
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
...point(game.anim[0], game.anim[1]),
|
|
game.anim[2] * 100 + 25,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
ctx.globalAlpha = 1;
|
|
}
|
|
|
|
// draw platforms
|
|
for (let part of game.parts) {
|
|
color("shadow");
|
|
for (let i = 6; i > 0; i--) {
|
|
rect(part[0] + i, part[1] + i, part[2], part[3]);
|
|
}
|
|
color(part[4] || "platforms");
|
|
rect(part[0], part[1], part[2], part[3]);
|
|
}
|
|
|
|
// draw player
|
|
color("player");
|
|
rect(game.player.x, game.player.y, game.player.size, game.player.size);
|
|
|
|
game.cache.dirty = true;
|
|
}
|
|
|
|
function update() {
|
|
const p = game.player;
|
|
const c = game.camera;
|
|
|
|
// handle input
|
|
const i = game.input;
|
|
const speed = 2;
|
|
if (i.up && p.canJump) p.vely = -14;
|
|
if (i.down) p.vely += 0.8;
|
|
if (i.left) p.velx -= speed;
|
|
if (i.right) p.velx += speed;
|
|
|
|
// update x position
|
|
for (let i = 0; i < Math.abs(p.velx) - 1; i++) {
|
|
p.x += p.velx < 0 ? -1 : 1;
|
|
const c = collision();
|
|
if (c) {
|
|
p.x -= p.velx < 0 ? -1 : 1;
|
|
p.velx = 0;
|
|
}
|
|
}
|
|
|
|
// reset jump if they're falling or already jumping (there's a small grace period though)
|
|
if (Math.abs(p.vely) > 3) p.canJump = false;
|
|
|
|
// update y position
|
|
const collided = move("y", p.vely, p.vely < 0 ? -1 : 1);
|
|
if (collided) {
|
|
if (p.vely >= 0) {
|
|
// if they fell onto a platform then let them jump
|
|
p.canJump = true;
|
|
|
|
// if they hit a checkpoint
|
|
if (collided[4] === "checkpoint" || collided[4] === "final") {
|
|
// unset the other checkpoints
|
|
for (let part of game.parts.filter((i) => i[4] === "active")) {
|
|
part[4] = "checkpoint";
|
|
}
|
|
|
|
const x = collided[0] + collided[2] / 2;
|
|
const y = collided[1] + collided[3] / 2;
|
|
|
|
// play the checkpoint animation
|
|
game.anim = [x, y, 0];
|
|
game.player.savex = x - game.player.size / 2;
|
|
game.player.savey = y - collided[3] / 2 - game.player.size;
|
|
|
|
// they won!
|
|
if (collided[4] === "final") {
|
|
alert("ayo you win");
|
|
|
|
// reset the inputs, `alert()` does weird things
|
|
input.up = false;
|
|
input.down = false;
|
|
input.left = false;
|
|
input.right = false;
|
|
}
|
|
|
|
// don't spam animation; change platform color
|
|
collided[4] = collided[4] === "final" ? "win" : "active";
|
|
}
|
|
}
|
|
|
|
p.vely = 0;
|
|
}
|
|
|
|
// they fell out of the world
|
|
if (p.y > 1000) die();
|
|
|
|
// update velocity
|
|
p.velx *= 0.8;
|
|
p.vely = Math.min(30, p.vely + 0.8);
|
|
|
|
// update camera
|
|
const camx = (c[0] * 9 + p.x) / 10;
|
|
const camy = (c[1] * 9 + p.y) / 10;
|
|
if (c[0] !== camx || c[1] !== camy) game.cache.dirty = true;
|
|
c[0] = camx;
|
|
c[1] = camy;
|
|
|
|
if (game.anim[2] < 1) game.anim[2] += 0.04;
|
|
|
|
// move, and return the platform the player collided with if any
|
|
function move(key, amt, speed) {
|
|
for (let i = 0; i < Math.abs(amt) - 1; i++) {
|
|
p[key] += speed;
|
|
const c = collision();
|
|
if (c) {
|
|
p[key] -= speed;
|
|
return c;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// check if there was a collision
|
|
function collision() {
|
|
const s = game.player.size;
|
|
for (let part of game.parts) {
|
|
const inside =
|
|
p.x + s > part[0] &&
|
|
p.y + s > part[1] &&
|
|
p.x < part[0] + part[2] &&
|
|
p.y < part[1] + part[3];
|
|
if (inside) return part;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// handle input
|
|
function input(e) {
|
|
const i = game.input;
|
|
const down = e.type === "keydown";
|
|
switch (e.code) {
|
|
case "ArrowUp":
|
|
case "KeyW":
|
|
return (i.up = down);
|
|
case "ArrowDown":
|
|
case "KeyS":
|
|
return (i.down = down);
|
|
case "ArrowLeft":
|
|
case "KeyA":
|
|
return (i.left = down);
|
|
case "ArrowRight":
|
|
case "KeyD":
|
|
return (i.right = down);
|
|
case "KeyR":
|
|
return die();
|
|
}
|
|
}
|
|
|
|
// run the game!
|
|
resize();
|
|
draw();
|
|
setInterval(update, 1000 / 60);
|
|
|
|
window.addEventListener("resize", resize);
|
|
window.addEventListener("keydown", input);
|
|
window.addEventListener("keyup", input);
|