// TODO: if there are no more levels show a "game win" message
// Game constants
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
const GRAVITY = 4;
const JUMP_STRENGTH = -33;
const MOVE_SPEED = 7;
let keys = { left: false, right: false, up: false, down: false, space: false };
let levels = [
[
// Next level example
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
],
[
[4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
// Add more levels as needed
];
let currentLevel = 0;
class Player {
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 30;
this.height = 30;
this.velocityX = 0;
this.velocityY = 0;
this.jumping = false;
this.doubleJumping = false;
}
update() {
if (keys.left) this.velocityX = -MOVE_SPEED;
if (keys.right) this.velocityX = MOVE_SPEED;
if (!keys.left && !keys.right) this.velocityX = 0;
this.velocityY += GRAVITY;
this.x += this.velocityX;
this.y += this.velocityY;
// Collision detection
this.checkCollision();
// Limit falling speed
if (this.velocityY > 10) this.velocityY = 10;
}
jump() {
if (!this.jumping) {
this.velocityY = JUMP_STRENGTH;
this.jumping = true;
} else if (!this.doubleJumping) {
this.velocityY = JUMP_STRENGTH;
this.doubleJumping = true;
}
}
checkCollision() {
let row, col, belowRow, aboveRow, leftCol, rightCol;
// Check collision with ground from below
row = Math.floor(this.y / 30);
col = Math.floor(this.x / 30);
belowRow = Math.floor((this.y + this.height) / 30);
if (levels[currentLevel][belowRow] && levels[currentLevel][belowRow][col] === 1) {
if (this.velocityY > 0) {
this.y = belowRow * 30 - this.height;
this.velocityY = 0;
this.jumping = false;
this.doubleJumping = false;
}
}
// Check collision with ground from above
aboveRow = Math.floor((this.y - 1) / 30);
if (levels[currentLevel][aboveRow] && levels[currentLevel][aboveRow][col] === 1) {
if (this.velocityY < 0) {
this.y = (aboveRow + 1) * 30;
this.velocityY = 0;
}
}
// Check collision with ground from the left
leftCol = Math.floor(this.x / 30);
if (levels[currentLevel][row] && levels[currentLevel][row][leftCol] === 1) {
if (this.velocityX < 0) {
this.x = (leftCol + 1) * 30;
this.velocityX = 0;
}
}
// Check collision with ground from the right
rightCol = Math.floor((this.x + this.width) / 30);
if (levels[currentLevel][row] && levels[currentLevel][row][rightCol] === 1) {
if (this.velocityX > 0) {
this.x = rightCol * 30 - this.width;
this.velocityX = 0;
}
}
// Collision with lava
if (levels[currentLevel][row] && levels[currentLevel][row][col] === 2) {
this.reset();
}
// Collision with save point
if (levels[currentLevel][row] && levels[currentLevel][row][col] === 3) {
localStorage.setItem('savePoint', JSON.stringify({ x: this.x, y: this.y, level: currentLevel }));
}
// Collision with goal
if (levels[currentLevel][row] && levels[currentLevel][row][col] === 5) {
alert('Level Complete!');
loadNextLevel();
}
}
reset() {
let savePoint = JSON.parse(localStorage.getItem('savePoint')) || { x: 30, y: 30, level: 0 };
this.x = savePoint.x;
this.y = savePoint.y;
currentLevel = savePoint.level;
this.velocityY = 0;
}
render() {
ctx.fillStyle = 'blue';
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
function findPlayerStartPosition(level) {
for (let row = 0; row < levels[level].length; row++) {
for (let col = 0; col < levels[level][row].length; col++) {
if (levels[level][row][col] === 4) {
return { x: col * 30, y: row * 30 };
}
}
}
// Default position if no start point found
return { x: 30, y: 570 };
}
function renderLevel() {
for (let row = 0; row < levels[currentLevel].length; row++) {
for (let col = 0; col < levels[currentLevel][row].length; col++) {
if (levels[currentLevel][row][col] === 1) {
ctx.fillStyle = 'brown';
ctx.fillRect(col * 30, row * 30, 30, 30);
} else if (levels[currentLevel][row][col] === 2) {
ctx.fillStyle = 'red';
ctx.fillRect(col * 30, row * 30, 30, 30);
} else if (levels[currentLevel][row][col] === 3) {
ctx.fillStyle = 'green';
ctx.fillRect(col * 30, row * 30, 30, 30);
} else if (levels[currentLevel][row][col] === 5) {
ctx.fillStyle = 'gold';
ctx.fillRect(col * 30, row * 30, 30, 30);
}
}
}
}
function loadNextLevel() {
currentLevel++;
if (currentLevel >= levels.length) {
alert("You've completed all levels!");
currentLevel = 0;
}
let startPosition = findPlayerStartPosition(currentLevel);
player.x = startPosition.x;
player.y = startPosition.y;
player.velocityY = 0;
localStorage.setItem('savePoint', JSON.stringify({ x: player.x, y: player.y, level: currentLevel }));
}
let startPosition = findPlayerStartPosition(currentLevel);
let player = new Player(startPosition.x, startPosition.y);
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
renderLevel();
player.update();
player.render();
requestAnimationFrame(gameLoop);
}
document.addEventListener('keydown', (e) => {
if (e.code === 'ArrowLeft') keys.left = true;
if (e.code === 'ArrowRight') keys.right = true;
if (e.code === 'ArrowUp') keys.up = true;
if (e.code === 'ArrowDown') keys.down = true;
if (e.code === 'Space') player.jump();
});
document.addEventListener('keyup', (e) => {
if (e.code === 'ArrowLeft') keys.left = false;
if (e.code === 'ArrowRight') keys.right = false;
if (e.code === 'ArrowUp') keys.up = false;
if (e.code === 'ArrowDown') keys.down = false;
});
gameLoop();