about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--js/mountain/game.js235
-rw-r--r--js/mountain/hill.js319
-rw-r--r--js/mountain/index.html19
3 files changed, 247 insertions, 326 deletions
diff --git a/js/mountain/game.js b/js/mountain/game.js
index 3c96005..5601d94 100644
--- a/js/mountain/game.js
+++ b/js/mountain/game.js
@@ -1,3 +1,234 @@
-// a 2d platformer game, where the character can jump, double jump, move left and right.
-// That character can avoid spikes and obstacles.
+// 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 = 0.5;
+const JUMP_STRENGTH = -12;
+const MOVE_SPEED = 5;
+
+let keys = { left: false, right: false, up: false, down: false, space: false };
+
+let levels = [
+    [
+        [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]
+    ],
+    [
+        // Next level example
+        [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]
+    ]
+    // 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();
diff --git a/js/mountain/hill.js b/js/mountain/hill.js
deleted file mode 100644
index 0e960a9..0000000
--- a/js/mountain/hill.js
+++ /dev/null
@@ -1,319 +0,0 @@
-const canvas = document.getElementById('gameCanvas');
-canvas.width = window.innerWidth;
-canvas.height = window.innerHeight;
-canvas.style.backgroundColor = '#f0f0f0';
-const ctx = canvas.getContext('2d');
-
-(function(){
-
-    const COLORS = {
-        player: '#2d2d2d',
-        particles: '#2d2d2d',
-        objects: '#2d2d2d',
-        terrain: '#2d2d2d'
-    };
-
-    const gravity = 1;
-    const jumpForce = 14.75;
-
-    const player = {
-        x: 200,
-        y: 0,
-        vx: 16,
-        vy: 16,
-        vm: 32,
-        jumpForce: gravity + jumpForce,
-        onGround: false,
-        width: 20,
-        height: 20,
-        distanceTraveled: 0
-    };
-
-    const terrain = {
-        start: { x: 0, y: 0 },
-        end: { x: canvas.width, y: canvas.height },
-        height: 8
-    };
-
-    const objects = [];
-    let lastObjectX = 0;
-
-    const particles = [];
-
-    const createObject = () => {
-        const object = {
-            x: lastObjectX + 10 * canvas.width / 10,
-            width: Math.random() * 100,
-            height: Math.random() * 22
-        };
-
-        let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y;
-
-        object.y = terrainY - object.height;
-
-        objects.push(object);
-
-        lastObjectX = object.x;
-    };
-
-    const isColliding = (obj1, obj2) => (
-        obj1.x < obj2.x + obj2.width &&
-        obj1.x + obj1.width > obj2.x &&
-        obj1.y < obj2.y + obj2.height &&
-        obj1.y + obj1.height > obj2.y
-    );
-
-    const checkCollision = () => {
-        objects.forEach(object => {
-            if (isColliding(player, object)) {
-                if (player.vx >= 1) {
-                    player.vx -= 0.5;
-                    player.vy -= 0.5;
-                }
-            }
-        });
-    };
-
-    const updatePlayer = () => {
-        player.vy += gravity;
-
-        player.x += player.vx;
-        player.y += player.vy;
-
-        const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y;
-
-        if (player.y + player.height > terrainY) {
-            player.y = terrainY - player.height;
-            player.vy = 0;
-        }
-
-        if (particlesEnabled) {
-            particles.push({
-                x: player.x,
-                y: player.y,
-                vx: Math.random() * 2 - 1,
-                vy: Math.random() * 2 - 1,
-                lifespan: player.vx * 4
-            });
-        }
-    };
-
-    const updateObjects = () => {
-        for (let object of objects) {
-            object.y += gravity;
-
-            let terrainY = ((terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x)) * (object.x - terrain.start.x) + terrain.start.y;
-
-            if (object.y + object.height > terrainY) {
-                object.y = terrainY - object.height;
-            }
-        }
-    };
-
-    const drawPlayer = () => {
-
-        if (player.vx < 4) {
-            ctx.fillStyle = COLORS.player;
-            ctx.font = '22px Arial';
-            const text = 'Jump to build up speed!';
-            ctx.fillText(text, player.x, player.y - 12);
-        } else {
-            if (distanceMeterEnabled) {
-                ctx.fillStyle = '#aaa';
-                ctx.font = '22px Arial';
-                const text = player.distanceTraveled.toFixed(0) + ' px';
-                ctx.fillText(text, player.x, player.y - 12);
-            }
-        }
-
-        ctx.fillStyle = COLORS.player;
-        ctx.fillRect(player.x, player.y, player.width, player.height);
-    };
-
-    const drawObjects = () => {
-        ctx.fillStyle = COLORS.objects;
-        objects.forEach(object => {
-            ctx.beginPath();
-            ctx.arc(object.x, object.y, object.width / 2, 0, Math.PI * 2, true);
-            ctx.fill();
-        });
-    };
-
-    const drawTerrain = () => {
-        ctx.beginPath();
-        ctx.moveTo(terrain.start.x, terrain.start.y);
-        ctx.lineTo(terrain.end.x, terrain.end.y);
-        ctx.strokeStyle = COLORS.terrain;
-        ctx.lineWidth = terrain.height;
-        ctx.stroke();
-    };
-
-    const drawParticles = () => {
-        if (particlesEnabled) {
-            ctx.fillStyle = COLORS.particles;
-            particles.forEach((particle, index) => {
-                ctx.fillRect(particle.x, particle.y, 5, 5);
-                particle.x += particle.vx;
-                particle.y += particle.vy;
-                particle.lifespan--;
-                if (particle.lifespan === 0) {
-                    particles.splice(index, 1);
-                }
-            });
-        }
-    };
-
-
-    const draw = () => {
-        ctx.clearRect(0, 0, canvas.width, canvas.height);
-        ctx.save();
-        ctx.translate(-player.x + canvas.width / 4, -player.y + canvas.height / 2);
-        drawPlayer();
-        terrain.start.x = player.x - canvas.width;
-        terrain.end.x = player.x + canvas.width;
-        drawObjects();
-        drawTerrain();
-        drawParticles();
-        ctx.restore();
-    };
-
-    const update = (currentTime = 0) => {
-        const deltaTime = currentTime - lastUpdateTime;
-
-        player.distanceTraveled = Math.abs(player.x - 200);
-
-        if (deltaTime >= frameDelay) {
-            createObject();
-            updatePlayer();
-            updateObjects();
-            checkCollision();
-            lastUpdateTime = currentTime;
-        }
-
-        requestAnimationFrame(update);
-    };
-
-    const initialize = () => {
-        terrain.start = { x: 0, y: 0 };
-        terrain.end = { x: canvas.width, y: canvas.height };
-    };
-
-    const gameLoop = () => {
-        initialize();
-        draw();
-        requestAnimationFrame(gameLoop);
-    };
-
-    const playerDoJump = () => {
-        const terrainY = (terrain.end.y - terrain.start.y) / (terrain.end.x - terrain.start.x) * (player.x - terrain.start.x) + terrain.start.y;
-        if (player.y + player.height >= terrainY) {
-            player.vy = player.jumpForce * -1;
-            if (player.vx < player.vm) {
-                player.vx += 0.5;
-            }
-        }
-    };
-
-    const handleKeyDown = event => {
-        if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') {
-            playerDoJump();
-        }
-    };
-
-    canvas.addEventListener('click', playerDoJump);
-    canvas.addEventListener('touchstart', playerDoJump);
-    window.addEventListener('keydown', handleKeyDown);
-
-    window.addEventListener('resize', () => {
-        canvas.width = window.innerWidth;
-        canvas.height = window.innerHeight;
-        draw();
-    });
-
-    const helpText = 'Controls:\n\nJump: space, up arrow, w or tap the screen on mobile\nFullscreen: f or the button in the upper-right\nParticles: p\nDistance: d\nHelp: h'
-
-    document.addEventListener('keydown', function(event) {
-        if (event.key === 'f') {
-            if (canvas.requestFullscreen) {
-                canvas.requestFullscreen();
-            } else if (canvas.mozRequestFullScreen) {
-                canvas.mozRequestFullScreen();
-            } else if (canvas.webkitRequestFullscreen) {
-                canvas.webkitRequestFullscreen();
-            } else if (canvas.msRequestFullscreen) {
-                canvas.msRequestFullscreen();
-            }
-        } else if (event.key === 'p' || event.key === 'P') {
-            particlesEnabled = !particlesEnabled;
-        } else if (event.key === 'h' || event.key === 'H') {
-            alert(helpText);
-        } else if (event.key === 'd' || event.key === 'D') {
-            distanceMeterEnabled = !distanceMeterEnabled;
-        }
-    });
-
-    let particlesEnabled = true;
-    let distanceMeterEnabled = false;
-
-    const fullscreenButton = document.createElement('button');
-    fullscreenButton.style.position = 'absolute';
-    fullscreenButton.style.top = '10px';
-    fullscreenButton.style.right = '10px';
-    fullscreenButton.textContent = 'Fullscreen';
-
-    const distanceMeterButton = document.createElement('button');
-    distanceMeterButton.style.position = 'absolute';
-    distanceMeterButton.style.top = '40px';
-    distanceMeterButton.style.right = '10px';
-    distanceMeterButton.textContent = 'Distance';
-
-    const particlesButton = document.createElement('button');
-    particlesButton.style.position = 'absolute';
-    particlesButton.style.top = '70px';
-    particlesButton.style.right = '10px';
-    particlesButton.textContent = 'Particles';
-
-    const helpButton = document.createElement('button');
-    helpButton.style.position = 'absolute';
-    helpButton.style.top = '100px';
-    helpButton.style.right = '10px';
-    helpButton.textContent = 'Help';
-
-    fullscreenButton.addEventListener('click', function() {
-        if (canvas.requestFullscreen) {
-            canvas.requestFullscreen();
-        } else if (canvas.mozRequestFullScreen) {
-            canvas.mozRequestFullScreen();
-        } else if (canvas.webkitRequestFullscreen) {
-            canvas.webkitRequestFullscreen();
-        } else if (canvas.msRequestFullscreen) {
-            canvas.msRequestFullscreen();
-        }
-    });
-
-    distanceMeterButton.addEventListener('click', function() {
-        distanceMeterEnabled = !distanceMeterEnabled;
-    });
-
-    particlesButton.addEventListener('click', function() {
-        particlesEnabled = !particlesEnabled;
-    });
-
-    helpButton.addEventListener('click', function() {
-        alert(helpText);
-    });
-
-    canvas.parentNode.appendChild(fullscreenButton);
-    canvas.parentNode.appendChild(distanceMeterButton);
-    canvas.parentNode.appendChild(particlesButton);
-    canvas.parentNode.appendChild(helpButton);
-
-
-    let lastUpdateTime = 0;
-    const fps = 60;
-    const frameDelay = 1000 / fps; 
-
-    update();
-    gameLoop();
-
-} )();
\ No newline at end of file
diff --git a/js/mountain/index.html b/js/mountain/index.html
index 016ad87..75d47aa 100644
--- a/js/mountain/index.html
+++ b/js/mountain/index.html
@@ -2,11 +2,20 @@
 <html lang="en">
 <head>
     <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>mountain</title>
-    <script src="game.js"></script>
+    <title>2D Side Scrolling Platformer</title>
+    <style>
+        body {
+            margin: 0;
+            padding: 0;
+            background-color: beige;
+        }
+        #gameCanvas {
+            display: block;
+        }
+    </style>
 </head>
 <body>
-    <canvas id="mountain"></canvas>
+    <canvas id="gameCanvas"></canvas>
+    <script src="game.js"></script>
 </body>
-</html>
\ No newline at end of file
+</html>