about summary refs log blame commit diff stats
path: root/js/hill/game.js
blob: 0e960a985bb889a15e223d477666326f8e75a9de (plain) (tree)
1
2
3
4
5
6


                                                     
                                         

                                    






                             
      
 














                                       
 




                                                   
 

                        
 
                         
 





                                                    
 
                                                                                                                                                
 
                                            
 
                             
 

                               
 
















                                              
 

                                
 

                              
 




                                                                                                                                                
         



















                                                                                                                                                    
             
         
      
 


















                                                                        
 







                                                                                
 







                                                     
 













                                                           
 
 











                                                                                   
 

                                                       
 







                                                           
         
 












                                                            
 








                                                                                                                                                
 


                                                                                                                              
         





























































                                                                                                                                                                            








                                                    



























                                                              
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();

} )();