Alpine Racer - Ski Slalom

✨ Remixed from FunInventor8509's game

A fun game created with AI

🤖 System_default ⚡ 133.6s 🎯 20,068 tokens $0.2013

Created by FunDesigner0263 • November 23, 2025

View Game Code
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
    <title>Alpine Racer - Ski Slalom</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(to bottom, #1a2a4a 0%, #2d4a6f 100%);
            font-family: 'Arial', sans-serif;
            overflow: hidden;
            touch-action: manipulation;
            padding-top: env(safe-area-inset-top);
            padding-bottom: env(safe-area-inset-bottom);
            padding-left: env(safe-area-inset-left);
            padding-right: env(safe-area-inset-right);
            -webkit-user-select: none;
            user-select: none;
            -webkit-touch-callout: none;
        }
        #gameContainer {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 100%;
            height: 100vh;
            max-width: 800px;
        }
        canvas {
            border: 3px solid #fff;
            background: #e8f4f8;
            box-shadow: 0 0 20px rgba(0,0,0,0.5);
            touch-action: none;
        }
        #ui {
            position: absolute;
            top: 10px;
            left: 10px;
            right: 10px;
            color: white;
            font-size: clamp(14px, 3vw, 18px);
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            pointer-events: none;
            z-index: 10;
        }
        .stat {
            background: rgba(0,0,0,0.6);
            padding: 8px 12px;
            border-radius: 8px;
            margin-bottom: 5px;
            display: inline-block;
            margin-right: 10px;
        }
        .stat.highlight {
            background: rgba(255,215,0,0.8);
            animation: pulse 0.5s ease-in-out;
        }
        @keyframes pulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.1); }
        }
        #controls {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            color: white;
            font-size: clamp(12px, 2.5vw, 14px);
            text-align: center;
            background: rgba(0,0,0,0.6);
            padding: 10px 20px;
            border-radius: 10px;
            pointer-events: none;
        }
        #gameOver, #levelSelect {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.9);
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            color: white;
            display: none;
            z-index: 100;
            max-width: 90%;
        }
        #levelSelect {
            display: block;
        }
        #gameOver h2, #levelSelect h2 {
            font-size: clamp(24px, 5vw, 36px);
            margin-bottom: 20px;
            color: #4af;
        }
        #gameOver p, #levelSelect p {
            font-size: clamp(16px, 3vw, 20px);
            margin: 10px 0;
        }
        .btn {
            margin: 10px 5px;
            padding: 15px 30px;
            font-size: clamp(16px, 3vw, 20px);
            background: #4af;
            border: none;
            border-radius: 10px;
            color: white;
            cursor: pointer;
            font-weight: bold;
            touch-action: manipulation;
            display: inline-block;
        }
        .btn:active {
            background: #39e;
            transform: scale(0.95);
        }
        .btn.easy { background: #4af; }
        .btn.medium { background: #fa4; }
        .btn.hard { background: #f44; }
        .level-info {
            font-size: clamp(12px, 2.5vw, 14px);
            color: #aaa;
            margin-top: 5px;
        }
        #jumpIndicator {
            position: absolute;
            bottom: 100px;
            left: 50%;
            transform: translateX(-50%);
            color: white;
            font-size: clamp(16px, 3vw, 24px);
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            background: rgba(0,0,0,0.6);
            padding: 10px 20px;
            border-radius: 10px;
            pointer-events: none;
            display: none;
            z-index: 10;
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <div id="ui">
            <div class="stat">⏱️ Time: <span id="time">0.0</span>s</div>
            <div class="stat">🎯 Gates: <span id="gates">0</span>/<span id="totalGates">20</span></div>
            <div class="stat">💨 Speed: <span id="speed">0</span></div>
            <div class="stat">❤️ Lives: <span id="lives">3</span></div>
            <div class="stat" id="coinStat">🪙 Coins: <span id="coins">0</span></div>
            <div class="stat" id="gemStat">💎 Gems: <span id="gems">0</span></div>
            <div class="stat">📊 Level: <span id="currentLevel">1</span></div>
        </div>
        <canvas id="game"></canvas>
        <div id="controls">🎮 Arrow Keys/WASD: Steer & Brake | SPACE: Jump (2x) | 📱 Swipe to turn, Tap to jump</div>
        <div id="jumpIndicator">🚀 Double Jump Available!</div>
        <div id="levelSelect">
            <h2>🎿 Select Your Level</h2>
            <p>Choose your difficulty and start racing!</p>
            <button class="btn easy" onclick="startLevel(1)">⛷️ Easy Slope</button>
            <div class="level-info">Slower speed • More gates spacing • 3 lives</div>
            <button class="btn medium" onclick="startLevel(2)">🏔️ Medium Mountain</button>
            <div class="level-info">Normal speed • Standard spacing • 3 lives</div>
            <button class="btn hard" onclick="startLevel(3)">🔥 Expert Peak</button>
            <div class="level-info">Fast speed • Tight spacing • 2 lives</div>
        </div>
        <div id="gameOver">
            <h2 id="resultTitle">Race Complete!</h2>
            <p id="finalTime"></p>
            <p id="finalGates"></p>
            <p id="finalCoins"></p>
            <p id="finalGems"></p>
            <p id="finalScore"></p>
            <button class="btn" id="restartBtn">Race Again</button>
            <button class="btn" id="levelSelectBtn">Change Level</button>
        </div>
    </div>
    <script>
        window.addEventListener('load', function() {
            try {
                const canvas = document.getElementById('game');
                if (!canvas) { alert('Canvas not found'); return; }
                const ctx = canvas.getContext('2d');
                if (!ctx) { alert('Canvas context failed'); return; }

                // Responsive canvas setup
                function resizeCanvas() {
                    const container = document.getElementById('gameContainer');
                    if (!container) return;
                    const maxWidth = Math.min(800, window.innerWidth - 20);
                    const maxHeight = window.innerHeight - 100;
                    const size = Math.min(maxWidth, maxHeight * 0.7);
                    canvas.style.width = size + 'px';
                    canvas.style.height = (size * 1.2) + 'px';
                    canvas.width = 800;
                    canvas.height = 960;
                }
                resizeCanvas();
                window.addEventListener('resize', resizeCanvas);
                window.addEventListener('orientationchange', () => setTimeout(resizeCanvas, 100));

                // Game constants
                const WIDTH = 800;
                const HEIGHT = 960;

                // Level configurations
                const LEVELS = {
                    1: { // Easy
                        name: 'Easy Slope',
                        baseSpeed: 2,
                        maxSpeed: 8,
                        gateSpacing: 400,
                        totalGates: 15,
                        lives: 3,
                        obstacleCount: 10,
                        coinFrequency: 0.8,
                        gemFrequency: 0.3
                    },
                    2: { // Medium
                        name: 'Medium Mountain',
                        baseSpeed: 3.5,
                        maxSpeed: 12,
                        gateSpacing: 300,
                        totalGates: 20,
                        lives: 3,
                        obstacleCount: 15,
                        coinFrequency: 0.6,
                        gemFrequency: 0.2
                    },
                    3: { // Hard
                        name: 'Expert Peak',
                        baseSpeed: 5,
                        maxSpeed: 15,
                        gateSpacing: 250,
                        totalGates: 25,
                        lives: 2,
                        obstacleCount: 20,
                        coinFrequency: 0.4,
                        gemFrequency: 0.15
                    }
                };

                // Game state
                let gameRunning = false;
                let gameTime = 0;
                let lastFrameTime = Date.now();
                let currentLevelNum = 1;
                let currentLevelConfig = LEVELS[1];

                // Player
                const player = {
                    x: WIDTH / 2,
                    y: HEIGHT - 150,
                    width: 30,
                    height: 40,
                    speed: 0,
                    maxSpeed: 12,
                    acceleration: 0.2,
                    friction: 0.98,
                    turnSpeed: 0,
                    maxTurnSpeed: 8,
                    angle: 0,
                    lives: 3,
                    invincible: 0,
                    carving: false,
                    jumping: false,
                    jumpHeight: 0,
                    jumpVelocity: 0,
                    jumpsRemaining: 2,
                    maxJumps: 2,
                    coins: 0,
                    gems: 0
                };

                // Course
                let scrollSpeed = 3.5;
                let gatesCleared = 0;
                let totalGates = 20;
                let courseOffset = 0;

                // Game objects
                const gates = [];
                const obstacles = [];
                const trees = [];
                const collectibles = [];
                const snowParticles = [];
                const sprayParticles = [];
                const collectParticles = [];

                // Keyboard
                const keys = {};
                window.addEventListener('keydown', (e) => {
                    if (!keys[e.key]) {
                        keys[e.key] = true;
                        if (e.key === ' ' || e.key === 'Spacebar') {
                            handleJump();
                        }
                    }
                    if(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight',' '].includes(e.key)) {
                        e.preventDefault();
                    }
                });
                window.addEventListener('keyup', (e) => {
                    keys[e.key] = false;
                });

                // Touch controls
                let touchStartX = 0;
                let touchStartY = 0;
                let touchActive = false;
                canvas.addEventListener('touchstart', (e) => {
                    e.preventDefault();
                    touchStartX = e.touches[0].clientX;
                    touchStartY = e.touches[0].clientY;
                    touchActive = true;
                    handleJump();
                }, { passive: false });

                canvas.addEventListener('touchmove', (e) => {
                    e.preventDefault();
                    if (touchActive) {
                        const deltaX = e.touches[0].clientX - touchStartX;
                        if (Math.abs(deltaX) > 10) {
                            if (deltaX > 0) {
                                keys['ArrowRight'] = true;
                                keys['ArrowLeft'] = false;
                            } else {
                                keys['ArrowLeft'] = true;
                                keys['ArrowRight'] = false;
                            }
                        }
                    }
                }, { passive: false });

                canvas.addEventListener('touchend', (e) => {
                    e.preventDefault();
                    touchActive = false;
                    keys['ArrowLeft'] = false;
                    keys['ArrowRight'] = false;
                }, { passive: false });

                // Jump handler
                function handleJump() {
                    if (!gameRunning) return;
                    if (player.jumpsRemaining > 0) {
                        player.jumping = true;
                        player.jumpVelocity = -12;
                        player.jumpsRemaining--;
                        
                        // Show double jump indicator
                        if (player.jumpsRemaining === 1) {
                            const indicator = document.getElementById('jumpIndicator');
                            if (indicator) {
                                indicator.style.display = 'block';
                                setTimeout(() => {
                                    indicator.style.display = 'none';
                                }, 1000);
                            }
                        }
                    }
                }

                // Initialize course
                function initCourse() {
                    gates.length = 0;
                    obstacles.length = 0;
                    trees.length = 0;
                    collectibles.length = 0;
                    snowParticles.length = 0;
                    sprayParticles.length = 0;
                    collectParticles.length = 0;

                    const config = currentLevelConfig;
                    totalGates = config.totalGates;

                    // Create gates
                    for (let i = 0; i < totalGates; i++) {
                        const y = -200 - (i * config.gateSpacing);
                        const side = i % 2 === 0 ? -1 : 1;
                        const offset = side * (Math.random() * 100 + 50);
                        gates.push({
                            x: WIDTH / 2 + offset,
                            y: y,
                            width: 150,
                            cleared: false,
                            side: side,
                            type: Math.random() > 0.8 ? 'jump' : 'normal'
                        });

                        // Add collectibles near gates
                        if (Math.random() < config.coinFrequency) {
                            const coinX = WIDTH / 2 + offset + (Math.random() - 0.5) * 100;
                            collectibles.push({
                                x: coinX,
                                y: y - 100,
                                type: 'coin',
                                collected: false,
                                rotation: 0
                            });
                        }

                        if (Math.random() < config.gemFrequency) {
                            const gemX = WIDTH / 2 + offset + (Math.random() - 0.5) * 120;
                            collectibles.push({
                                x: gemX,
                                y: y - 150,
                                type: 'gem',
                                collected: false,
                                rotation: 0,
                                pulse: 0
                            });
                        }
                    }

                    // Add bonus collectibles between gates
                    for (let i = 0; i < totalGates * 2; i++) {
                        if (Math.random() < 0.3) {
                            collectibles.push({
                                x: 150 + Math.random() * (WIDTH - 300),
                                y: -Math.random() * totalGates * config.gateSpacing,
                                type: 'coin',
                                collected: false,
                                rotation: 0
                            });
                        }
                    }

                    // Create trees
                    for (let i = 0; i < 50; i++) {
                        const side = Math.random() > 0.5 ? 1 : -1;
                        trees.push({
                            x: side > 0 ? WIDTH - 80 + Math.random() * 60 : 20 + Math.random() * 60,
                            y: -Math.random() * totalGates * config.gateSpacing,
                            size: 30 + Math.random() * 20
                        });
                    }

                    // Create obstacles
                    for (let i = 0; i < config.obstacleCount; i++) {
                        obstacles.push({
                            x: 150 + Math.random() * (WIDTH - 300),
                            y: -Math.random() * totalGates * config.gateSpacing * 0.8,
                            width: 40,
                            height: 40,
                            type: 'rock'
                        });
                    }

                    // Initialize snow particles
                    for (let i = 0; i < 100; i++) {
                        snowParticles.push({
                            x: Math.random() * WIDTH,
                            y: Math.random() * HEIGHT,
                            size: Math.random() * 3 + 1,
                            speed: Math.random() * 2 + 1
                        });
                    }
                }

                // Draw functions
                function drawSnow() {
                    ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
                    snowParticles.forEach(p => {
                        ctx.beginPath();
                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
                        ctx.fill();
                    });
                }

                function drawTrees() {
                    trees.forEach(tree => {
                        const screenY = tree.y + courseOffset;
                        if (screenY > -100 && screenY < HEIGHT + 100) {
                            ctx.fillStyle = '#4a3520';
                            ctx.fillRect(tree.x - 8, screenY, 16, tree.size);
                            ctx.fillStyle = '#1a5c3a';
                            ctx.beginPath();
                            ctx.moveTo(tree.x, screenY - tree.size * 0.5);
                            ctx.lineTo(tree.x - tree.size * 0.6, screenY + tree.size * 0.3);
                            ctx.lineTo(tree.x + tree.size * 0.6, screenY + tree.size * 0.3);
                            ctx.closePath();
                            ctx.fill();
                        }
                    });
                }

                function drawGate(gate) {
                    const screenY = gate.y + courseOffset;
                    if (screenY > -100 && screenY < HEIGHT + 100) {
                        const leftX = gate.x - gate.width / 2;
                        const rightX = gate.x + gate.width / 2;
                        const poleColor = gate.cleared ? '#4a4a4a' : (gate.side > 0 ? '#ff3333' : '#3333ff');
                        
                        ctx.fillStyle = poleColor;
                        ctx.fillRect(leftX - 10, screenY - 60, 20, 80);
                        ctx.fillStyle = gate.cleared ? '#666' : '#ffff00';
                        ctx.fillRect(leftX - 15, screenY - 65, 30, 15);
                        
                        ctx.fillStyle = poleColor;
                        ctx.fillRect(rightX - 10, screenY - 60, 20, 80);
                        ctx.fillStyle = gate.cleared ? '#666' : '#ffff00';
                        ctx.fillRect(rightX - 15, screenY - 65, 30, 15);
                        
                        if (gate.type === 'jump' && !gate.cleared) {
                            ctx.fillStyle = '#ffa500';
                            ctx.font = 'bold 24px Arial';
                            ctx.textAlign = 'center';
                            ctx.fillText('🚀', gate.x, screenY - 80);
                        }
                    }
                }

                function drawObstacle(obs) {
                    const screenY = obs.y + courseOffset;
                    if (screenY > -100 && screenY < HEIGHT + 100) {
                        ctx.fillStyle = '#666';
                        ctx.beginPath();
                        ctx.arc(obs.x, screenY, obs.width / 2, 0, Math.PI * 2);
                        ctx.fill();
                        ctx.fillStyle = '#888';
                        ctx.beginPath();
                        ctx.arc(obs.x - 5, screenY - 5, obs.width / 3, 0, Math.PI * 2);
                        ctx.fill();
                    }
                }

                function drawCollectibles() {
                    collectibles.forEach(item => {
                        if (item.collected) return;
                        const screenY = item.y + courseOffset;
                        if (screenY > -100 && screenY < HEIGHT + 100) {
                            ctx.save();
                            ctx.translate(item.x, screenY);
                            ctx.rotate(item.rotation);
                            
                            if (item.type === 'coin') {
                                // Gold coin
                                ctx.fillStyle = '#ffd700';
                                ctx.beginPath();
                                ctx.arc(0, 0, 15, 0, Math.PI * 2);
                                ctx.fill();
                                ctx.fillStyle = '#ffed4e';
                                ctx.beginPath();
                                ctx.arc(-3, -3, 8, 0, Math.PI * 2);
                                ctx.fill();
                                ctx.strokeStyle = '#b8860b';
                                ctx.lineWidth = 2;
                                ctx.beginPath();
                                ctx.arc(0, 0, 15, 0, Math.PI * 2);
                                ctx.stroke();
                            } else if (item.type === 'gem') {
                                // Diamond gem
                                const size = 12 + Math.sin(item.pulse) * 2;
                                ctx.fillStyle = '#00ffff';
                                ctx.beginPath();
                                ctx.moveTo(0, -size);
                                ctx.lineTo(size * 0.7, 0);
                                ctx.lineTo(0, size);
                                ctx.lineTo(-size * 0.7, 0);
                                ctx.closePath();
                                ctx.fill();
                                ctx.fillStyle = '#ffffff';
                                ctx.beginPath();
                                ctx.moveTo(0, -size * 0.5);
                                ctx.lineTo(size * 0.3, 0);
                                ctx.lineTo(0, size * 0.5);
                                ctx.lineTo(-size * 0.3, 0);
                                ctx.closePath();
                                ctx.fill();
                            }
                            
                            ctx.restore();
                        }
                    });
                }

                function drawPlayer() {
                    ctx.save();
                    const drawY = player.y - player.jumpHeight;
                    ctx.translate(player.x, drawY);
                    ctx.rotate(player.angle);
                    
                    if (player.invincible > 0 && Math.floor(Date.now() / 100) % 2 === 0) {
                        ctx.globalAlpha = 0.5;
                    }
                    
                    // Shadow when jumping
                    if (player.jumping) {
                        ctx.fillStyle = 'rgba(0,0,0,0.3)';
                        ctx.beginPath();
                        ctx.ellipse(0, player.jumpHeight + 30, 20, 8, 0, 0, Math.PI * 2);
                        ctx.fill();
                    }
                    
                    ctx.fillStyle = '#ff6600';
                    ctx.fillRect(-18, 10, 12, 30);
                    ctx.fillRect(6, 10, 12, 30);
                    
                    ctx.fillStyle = '#0066cc';
                    ctx.fillRect(-12, -10, 24, 25);
                    
                    ctx.fillStyle = '#ffcc99';
                    ctx.beginPath();
                    ctx.arc(0, -20, 10, 0, Math.PI * 2);
                    ctx.fill();
                    
                    ctx.fillStyle = '#cc0000';
                    ctx.beginPath();
                    ctx.arc(0, -23, 11, Math.PI, Math.PI * 2);
                    ctx.fill();
                    
                    ctx.strokeStyle = '#333';
                    ctx.lineWidth = 3;
                    ctx.beginPath();
                    ctx.moveTo(-15, 0);
                    ctx.lineTo(-20, 20);
                    ctx.moveTo(15, 0);
                    ctx.lineTo(20, 20);
                    ctx.stroke();
                    
                    ctx.restore();
                    
                    if (player.carving && Math.abs(player.turnSpeed) > 3 && !player.jumping) {
                        for (let i = 0; i < 3; i++) {
                            sprayParticles.push({
                                x: player.x + (Math.random() - 0.5) * 40,
                                y: player.y + 20,
                                vx: (Math.random() - 0.5) * 4 - player.turnSpeed * 0.5,
                                vy: Math.random() * 2 + 2,
                                life: 30,
                                size: Math.random() * 4 + 2
                            });
                        }
                    }
                }

                function drawSpray() {
                    sprayParticles.forEach(p => {
                        ctx.fillStyle = `rgba(255, 255, 255, ${p.life / 30})`;
                        ctx.beginPath();
                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
                        ctx.fill();
                    });
                }

                function drawCollectParticles() {
                    collectParticles.forEach(p => {
                        ctx.fillStyle = p.color;
                        ctx.globalAlpha = p.life / 30;
                        ctx.font = 'bold 20px Arial';
                        ctx.textAlign = 'center';
                        ctx.fillText(p.text, p.x, p.y);
                        ctx.globalAlpha = 1;
                    });
                }

                // Update functions
                function updatePlayer() {
                    if (keys['ArrowLeft'] || keys['a'] || keys['A']) {
                        player.turnSpeed = Math.max(player.turnSpeed - 0.5, -player.maxTurnSpeed);
                        player.carving = true;
                    } else if (keys['ArrowRight'] || keys['d'] || keys['D']) {
                        player.turnSpeed = Math.min(player.turnSpeed + 0.5, player.maxTurnSpeed);
                        player.carving = true;
                    } else {
                        player.turnSpeed *= 0.9;
                        player.carving = false;
                    }
                    
                    if (keys['ArrowDown'] || keys['s'] || keys['S']) {
                        player.speed *= 0.95;
                    } else {
                        player.speed = Math.min(player.speed + player.acceleration, player.maxSpeed);
                    }
                    
                    player.x += player.turnSpeed;
                    player.angle = player.turnSpeed * 0.05;
                    player.x = Math.max(50, Math.min(WIDTH - 50, player.x));
                    
                    // Jump physics
                    if (player.jumping) {
                        player.jumpVelocity += 0.6;
                        player.jumpHeight += player.jumpVelocity;
                        
                        if (player.jumpHeight >= 0) {
                            player.jumpHeight = 0;
                            player.jumpVelocity = 0;
                            player.jumping = false;
                            player.jumpsRemaining = player.maxJumps;
                        }
                    }
                    
                    if (player.invincible > 0) player.invincible--;
                }

                function updateCourse() {
                    courseOffset += scrollSpeed + player.speed * 0.3;
                    
                    snowParticles.forEach(p => {
                        p.y += p.speed + scrollSpeed * 0.5;
                        if (p.y > HEIGHT) {
                            p.y = -10;
                            p.x = Math.random() * WIDTH;
                        }
                    });
                    
                    for (let i = sprayParticles.length - 1; i >= 0; i--) {
                        const p = sprayParticles[i];
                        p.x += p.vx;
                        p.y += p.vy;
                        p.life--;
                        if (p.life <= 0) sprayParticles.splice(i, 1);
                    }

                    for (let i = collectParticles.length - 1; i >= 0; i--) {
                        const p = collectParticles[i];
                        p.y -= 2;
                        p.life--;
                        if (p.life <= 0) collectParticles.splice(i, 1);
                    }

                    collectibles.forEach(item => {
                        item.rotation += 0.05;
                        if (item.type === 'gem') {
                            item.pulse += 0.1;
                        }
                    });
                }

                function checkCollisions() {
                    // Check gates
                    gates.forEach(gate => {
                        if (!gate.cleared) {
                            const screenY = gate.y + courseOffset;
                            if (screenY > player.y - 50 && screenY < player.y + 50) {
                                const leftX = gate.x - gate.width / 2;
                                const rightX = gate.x + gate.width / 2;
                                if (player.x > leftX && player.x < rightX) {
                                    gate.cleared = true;
                                    gatesCleared++;
                                    if (gate.type === 'jump') {
                                        player.speed = Math.min(player.speed + 3, player.maxSpeed + 5);
                                    }
                                } else if (screenY > player.y) {
                                    if (player.invincible === 0 && !player.jumping) {
                                        player.lives--;
                                        player.invincible = 60;
                                        if (player.lives <= 0) {
                                            endGame(false);
                                        }
                                    }
                                }
                            }
                        }
                    });
                    
                    // Check obstacles (can jump over them)
                    if (player.invincible === 0 && !player.jumping) {
                        obstacles.forEach(obs => {
                            const screenY = obs.y + courseOffset;
                            if (screenY > player.y - 30 && screenY < player.y + 30) {
                                const dx = player.x - obs.x;
                                const dy = player.y - screenY;
                                const dist = Math.sqrt(dx * dx + dy * dy);
                                if (dist < 35) {
                                    player.lives--;
                                    player.invincible = 60;
                                    player.speed *= 0.5;
                                    if (player.lives <= 0) {
                                        endGame(false);
                                    }
                                }
                            }
                        });
                    }

                    // Check collectibles
                    collectibles.forEach(item => {
                        if (!item.collected) {
                            const screenY = item.y + courseOffset;
                            const dx = player.x - item.x;
                            const dy = (player.y - player.jumpHeight) - screenY;
                            const dist = Math.sqrt(dx * dx + dy * dy);
                            
                            if (dist < 30) {
                                item.collected = true;
                                if (item.type === 'coin') {
                                    player.coins++;
                                    highlightStat('coinStat');
                                    collectParticles.push({
                                        x: item.x,
                                        y: screenY,
                                        text: '+10',
                                        color: '#ffd700',
                                        life: 30
                                    });
                                } else if (item.type === 'gem') {
                                    player.gems++;
                                    highlightStat('gemStat');
                                    collectParticles.push({
                                        x: item.x,
                                        y: screenY,
                                        text: '+50',
                                        color: '#00ffff',
                                        life: 30
                                    });
                                }
                            }
                        }
                    });
                    
                    if (gatesCleared >= totalGates) {
                        endGame(true);
                    }
                }

                function highlightStat(statId) {
                    const stat = document.getElementById(statId);
                    if (stat) {
                        stat.classList.add('highlight');
                        setTimeout(() => stat.classList.remove('highlight'), 500);
                    }
                }

                function endGame(completed) {
                    gameRunning = false;
                    const gameOverDiv = document.getElementById('gameOver');
                    const resultTitle = document.getElementById('resultTitle');
                    const finalTime = document.getElementById('finalTime');
                    const finalGates = document.getElementById('finalGates');
                    const finalCoins = document.getElementById('finalCoins');
                    const finalGems = document.getElementById('finalGems');
                    const finalScore = document.getElementById('finalScore');
                    
                    if (!gameOverDiv || !resultTitle || !finalTime || !finalGates || !finalCoins || !finalGems || !finalScore) return;
                    
                    if (completed) {
                        resultTitle.textContent = '🏆 Race Complete!';
                        resultTitle.style.color = '#4af';
                    } else {
                        resultTitle.textContent = '💥 Race Over';
                        resultTitle.style.color = '#f44';
                    }
                    
                    finalTime.textContent = `⏱️ Time: ${gameTime.toFixed(1)}s`;
                    finalGates.textContent = `🎯 Gates Cleared: ${gatesCleared}/${totalGates}`;
                    finalCoins.textContent = `🪙 Coins Collected: ${player.coins}`;
                    finalGems.textContent = `💎 Gems Collected: ${player.gems}`;
                    
                    const score = Math.floor(
                        (gatesCleared * 1000) - 
                        (gameTime * 10) + 
                        (player.lives * 500) +
                        (player.coins * 10) +
                        (player.gems * 50)
                    );
                    finalScore.textContent = `⭐ Total Score: ${Math.max(0, score)}`;
                    
                    gameOverDiv.style.display = 'block';
                }

                function updateUI() {
                    const timeEl = document.getElementById('time');
                    const gatesEl = document.getElementById('gates');
                    const totalGatesEl = document.getElementById('totalGates');
                    const speedEl = document.getElementById('speed');
                    const livesEl = document.getElementById('lives');
                    const coinsEl = document.getElementById('coins');
                    const gemsEl = document.getElementById('gems');
                    const levelEl = document.getElementById('currentLevel');
                    
                    if (timeEl) timeEl.textContent = gameTime.toFixed(1);
                    if (gatesEl) gatesEl.textContent = gatesCleared;
                    if (totalGatesEl) totalGatesEl.textContent = totalGates;
                    if (speedEl) speedEl.textContent = Math.floor(player.speed * 10);
                    if (livesEl) livesEl.textContent = player.lives;
                    if (coinsEl) coinsEl.textContent = player.coins;
                    if (gemsEl) gemsEl.textContent = player.gems;
                    if (levelEl) levelEl.textContent = currentLevelNum;
                }

                function gameLoop() {
                    if (!gameRunning) return;
                    
                    const now = Date.now();
                    const deltaTime = (now - lastFrameTime) / 1000;
                    lastFrameTime = now;
                    gameTime += deltaTime;
                    
                    ctx.fillStyle = '#e8f4f8';
                    ctx.fillRect(0, 0, WIDTH, HEIGHT);
                    
                    ctx.fillStyle = '#c8e0e8';
                    ctx.fillRect(0, 0, 100, HEIGHT);
                    ctx.fillRect(WIDTH - 100, 0, 100, HEIGHT);
                    
                    updatePlayer();
                    updateCourse();
                    checkCollisions();
                    
                    drawTrees();
                    obstacles.forEach(drawObstacle);
                    gates.forEach(drawGate);
                    drawCollectibles();
                    drawSpray();
                    drawPlayer();
                    drawCollectParticles();
                    drawSnow();
                    
                    updateUI();
                    
                    requestAnimationFrame(gameLoop);
                }

                // Level selection
                window.startLevel = function(level) {
                    currentLevelNum = level;
                    currentLevelConfig = LEVELS[level];
                    
                    const levelSelectDiv = document.getElementById('levelSelect');
                    if (levelSelectDiv) levelSelectDiv.style.display = 'none';
                    
                    scrollSpeed = currentLevelConfig.baseSpeed;
                    player.maxSpeed = currentLevelConfig.maxSpeed;
                    player.lives = currentLevelConfig.lives;
                    player.acceleration = currentLevelConfig.baseSpeed * 0.06;
                    
                    resetGame();
                    gameLoop();
                };

                function resetGame() {
                    const gameOverDiv = document.getElementById('gameOver');
                    if (gameOverDiv) gameOverDiv.style.display = 'none';
                    
                    gameRunning = true;
                    gameTime = 0;
                    gatesCleared = 0;
                    courseOffset = 0;
                    player.x = WIDTH / 2;
                    player.speed = 0;
                    player.turnSpeed = 0;
                    player.lives = currentLevelConfig.lives;
                    player.invincible = 0;
                    player.jumping = false;
                    player.jumpHeight = 0;
                    player.jumpVelocity = 0;
                    player.jumpsRemaining = 2;
                    player.coins = 0;
                    player.gems = 0;
                    sprayParticles.length = 0;
                    collectParticles.length = 0;
                    lastFrameTime = Date.now();
                    initCourse();
                }

                // Restart button
                const restartBtn = document.getElementById('restartBtn');
                if (restartBtn) {
                    restartBtn.addEventListener('click', function() {
                        resetGame();
                        gameLoop();
                    });
                }

                // Level select button
                const levelSelectBtn = document.getElementById('levelSelectBtn');
                if (levelSelectBtn) {
                    levelSelectBtn.addEventListener('click', function() {
                        const gameOverDiv = document.getElementById('gameOver');
                        const levelSelectDiv = document.getElementById('levelSelect');
                        if (gameOverDiv) gameOverDiv.style.display = 'none';
                        if (levelSelectDiv) levelSelectDiv.style.display = 'block';
                    });
                }

                // Show level select on load
                const levelSelectDiv = document.getElementById('levelSelect');
                if (levelSelectDiv) levelSelectDiv.style.display = 'block';
                
            } catch(e) {
                document.body.innerHTML = '<div style="color:white;padding:20px;text-align:center;">Game error: ' + e.message + '<br><br>Please refresh the page.</div>';
                console.error(e);
            }
        });
    </script>
</body>
</html>

Improving Your Game...

Analyzing your request...

This usually takes 1-3 minutes

📱

Install GB3A

Install GB3A on your device for a better experience!