about summary refs log blame commit diff stats
path: root/html/rogue/js/rogue.js
blob: b4379ea4fdc3b2cc9b63d04c5a1ee41cf76ed969 (plain) (tree)



















                                                            





                             




                                         

                                  
                                                 






                                                                        








                                                               































                                                                    
                                          
                                                                 












                                                                             
                                                  







                                                                   




                                                    
                            

                              
                        
                
                                           


                         
                                                
                                                 


                                                                    


                                                 


                                                                    
       

                                                  


                                                                      
       











                                                                              


                                                                    


                                                 


                                                                    

       
                                                  


                                                                      

       
                                            




                                
                                        

      

                                          
                      

                                    


                                                                                                  
                      
     



                                            
window.gameState = null;

const initGame = () => {
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');

    // Set canvas to full viewport size
    const resizeCanvas = () => {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    };

    window.addEventListener('resize', resizeCanvas);
    resizeCanvas();

    // Game state
    let gameState = {
        time: 0,
        player: createPlayer(100, 100), // Starting position
        camera: createCamera(0, 0),
        world: createWorld(),
        debug: {
            enabled: false,
            mouseX: 0,
            mouseY: 0
        }
    };
    
    // Make gameState globally accessible
    window.gameState = gameState;

    // Throttle mouse move updates
    let mouseMoveThrottle;
    canvas.addEventListener('mousemove', (e) => {
        if (!mouseMoveThrottle) {
            mouseMoveThrottle = setTimeout(() => {
                gameState.debug.mouseX = e.clientX + gameState.camera.x;
                gameState.debug.mouseY = e.clientY + gameState.camera.y;
                mouseMoveThrottle = null;
            }, 16); // ~60fps
        }
    });

    // Add debug toggle listener
    window.addEventListener('keydown', (e) => {
        if (e.key === 'd') {
            gameState.debug.enabled = !gameState.debug.enabled;
        }
    });

    // Game loop
    const gameLoop = (timestamp) => {
        // Calculate delta time
        const deltaTime = timestamp - gameState.time;
        gameState.time = timestamp;

        // Update
        gameState = updateGame(gameState, deltaTime);

        // Render
        render(ctx, gameState);

        // Next frame
        requestAnimationFrame(gameLoop);
    };

    // Start the game loop
    requestAnimationFrame(gameLoop);
};

const updateGame = (state, deltaTime) => {
    const updatedPlayer = updatePlayer(state.player, deltaTime);
    const updatedCamera = updateCamera(state.camera, updatedPlayer);

    return {
        ...state,
        player: updatedPlayer,
        camera: updatedCamera
    };
};

const render = (ctx, state) => {
    // Calculate groundY once at the start
    const groundY = ctx.canvas.height - state.world.groundHeight;

    // Create gradient for sky - cache this instead of recreating every frame
    if (!state.cachedGradient) {
        const gradient = ctx.createLinearGradient(0, 0, 0, groundY);
        gradient.addColorStop(0, '#1a1a2e');
        gradient.addColorStop(0.4, '#2d1b3d');
        gradient.addColorStop(0.7, '#462639');
        gradient.addColorStop(1, '#1f1f2f');
        state.cachedGradient = gradient;
    }

    // Fill sky using cached gradient
    ctx.fillStyle = state.cachedGradient;
    ctx.fillRect(0, 0, ctx.canvas.width, groundY);
    
    // Implement view frustum culling - only render visible objects
    const viewBounds = {
        left: state.camera.x - 100, // Add small buffer
        right: state.camera.x + state.camera.width + 100,
        top: state.camera.y - 100,
        bottom: state.camera.y + state.camera.height + 100
    };

    // Apply camera transform
    ctx.save();
    ctx.translate(-state.camera.x, -state.camera.y);

    // Fill black background
    ctx.fillStyle = '#000000';
    ctx.fillRect(
        viewBounds.left,
        groundY,
        viewBounds.right - viewBounds.left,
        ctx.canvas.height
    );

    // 1. Render only visible background objects
    state.world.backgroundTrees.forEach(tree => {
        if (tree.x > viewBounds.left && tree.x < viewBounds.right) {
            renderTree(ctx, tree, groundY);
        }
    });
    
    state.world.backgroundRocks.forEach(rock => {
        if (rock.x > viewBounds.left && rock.x < viewBounds.right) {
            renderRock(ctx, rock, groundY);
        }
    });

    state.world.backgroundGrass.forEach(grass => {
        if (grass.x > viewBounds.left && grass.x < viewBounds.right) {
            renderGrass(ctx, grass, groundY, state.time);
        }
    });
    
    // 2. Render platforms
    state.world.platforms.forEach(platform => {
        ctx.fillStyle = platform.color;
        ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
    });

    // 3. Render player
    renderPlayer(ctx, state.player);

    // 4. Render foreground objects
    state.world.foregroundTrees.forEach(tree => {
        if (tree.x > viewBounds.left && tree.x < viewBounds.right) {
            renderTree(ctx, tree, groundY);
        }
    });
    
    state.world.foregroundRocks.forEach(rock => {
        if (rock.x > viewBounds.left && rock.x < viewBounds.right) {
            renderRock(ctx, rock, groundY);
        }
    });

    state.world.foregroundGrass.forEach(grass => {
        if (grass.x > viewBounds.left && grass.x < viewBounds.right) {
            renderGrass(ctx, grass, groundY, state.time);
        }
    });

    // 5. Render ground (just the grass top)
    ctx.fillStyle = '#4a4';
    ctx.fillRect(
        state.camera.x - 1000,
        groundY,
        ctx.canvas.width + 2000,
        1  // Only render the grass line
    );

    // Render debug information if enabled
    if (state.debug.enabled) {
        ctx.restore();
        ctx.fillStyle = '#ffffff';
        ctx.font = '14px monospace';
        const text = `x: ${Math.round(state.debug.mouseX)}, y: ${Math.round(state.debug.mouseY)}`;
        ctx.fillText(text, 10, ctx.canvas.height - 20);
    } else {
        ctx.restore();
    }
};

// Initialize the game when the window loads
window.addEventListener('load', initGame);