🎮ArcadeLab

Bounce 100 - Le défi ultime

by NovaWizard65
784 lines37.2 KB
▶ Play
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Bounce 100 - Le défi ultime</title>
    <!-- Y8 SDK (chargement asynchrone) -->
    <script>
        // Charger le SDK Y8 seulement si on est sur Y8 (ou pour test, on simule)
        (function() {
            var script = document.createElement('script');
            script.src = "https://sdk.y8.com/sdk/v1/afp-ads-sdk.js";
            script.async = true;
            script.onload = function() {
                console.log("Y8 SDK chargé");
                if (window.AFPAdsSDK) {
                    window.AFPAdsSDK.init({});
                }
            };
            script.onerror = function() {
                console.log("Y8 SDK non disponible (test local)");
                window.Y8_SDK_READY = false;
            };
            document.head.appendChild(script);
        })();
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            user-select: none;
        }
        body {
            background: linear-gradient(135deg, #0a0f1e, #0f172a);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', 'Courier New', monospace;
            padding: 20px;
        }
        .game-layout {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            justify-content: center;
            align-items: stretch;
        }
        /* Menu des niveaux */
        .level-menu {
            background: rgba(0,0,0,0.85);
            backdrop-filter: blur(10px);
            border-radius: 25px;
            padding: 20px;
            width: 420px;
            border: 1px solid #ffd700;
            max-height: 90vh;
            overflow-y: auto;
        }
        .level-menu h1 {
            text-align: center;
            color: #ffd700;
            margin-bottom: 15px;
            font-size: 1.8rem;
            text-shadow: 0 0 10px #ffaa00;
        }
        .level-grid {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 8px;
            margin: 15px 0;
        }
        .level-grid button {
            background: linear-gradient(135deg, #2a2a4a, #1a1a3a);
            border: 1px solid #ffd700;
            color: #ffd700;
            padding: 8px 5px;
            font-size: 0.8rem;
            font-weight: bold;
            border-radius: 12px;
            cursor: pointer;
            transition: all 0.2s;
        }
        .level-grid button:hover:not(.locked) {
            transform: scale(1.05);
            background: #ffd700;
            color: #1a1a3a;
        }
        .level-grid button.locked {
            border-color: #555;
            color: #555;
            cursor: not-allowed;
            opacity: 0.5;
        }
        .level-grid button.completed {
            border-color: #44ff44;
            color: #44ff44;
            background: #1a3a1a;
        }
        .level-grid button.current {
            border-color: #ff6600;
            color: #ff6600;
            background: #2a1a0a;
            box-shadow: 0 0 10px #ff6600;
        }
        .game-wrapper {
            position: relative;
            display: none;
        }
        .game-container {
            background: #000;
            border-radius: 20px;
            box-shadow: 0 25px 50px rgba(0,0,0,0.5);
            overflow: hidden;
            border: 2px solid #ffd700;
        }
        canvas {
            display: block;
            margin: 0 auto;
            cursor: none;
        }
        .game-header {
            position: absolute;
            top: 10px;
            left: 10px;
            right: 10px;
            display: flex;
            justify-content: space-between;
            pointer-events: none;
            z-index: 10;
        }
        .game-header div {
            background: rgba(0,0,0,0.7);
            padding: 4px 12px;
            border-radius: 20px;
            font-size: 0.8rem;
            font-weight: bold;
            color: #ffd700;
        }
        button {
            background: linear-gradient(45deg, #ff416c, #ff4b2b);
            border: none;
            padding: 8px 15px;
            border-radius: 25px;
            color: white;
            font-weight: bold;
            cursor: pointer;
            margin: 5px;
            transition: transform 0.1s;
        }
        button:hover {
            transform: scale(0.96);
        }
        .overlay {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.95);
            backdrop-filter: blur(10px);
            padding: 25px;
            border-radius: 25px;
            text-align: center;
            z-index: 30;
            display: none;
            border: 2px solid #ffd700;
            min-width: 280px;
        }
        .info-panel {
            background: rgba(0,0,0,0.85);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 20px;
            width: 260px;
            border: 2px solid #ffd700;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        .stat {
            background: rgba(0,0,0,0.6);
            border-radius: 15px;
            padding: 12px;
            text-align: center;
            border-left: 4px solid #ffd700;
        }
        .stat span:first-child {
            display: block;
            font-size: 0.8rem;
            color: #aaa;
        }
        .stat span:last-child {
            font-size: 1.8rem;
            font-weight: bold;
            color: #ffd700;
            display: block;
            margin-top: 5px;
        }
        .ad-space {
            background: linear-gradient(135deg, #1a1a2e, #16213e);
            border-radius: 15px;
            padding: 15px;
            text-align: center;
            border: 1px dashed #ffd700;
        }
        .sound-toggle {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: rgba(0,0,0,0.7);
            border-radius: 50%;
            width: 45px;
            height: 45px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 1.3rem;
            z-index: 100;
        }
        @media (max-width: 1000px) {
            .game-layout {
                flex-direction: column;
                align-items: center;
            }
            .level-menu, .info-panel {
                width: 90%;
                max-width: 420px;
            }
        }
    </style>
</head>
<body>
<div class="game-layout">
    <!-- MENU DES NIVEAUX -->
    <div class="level-menu" id="levelMenu">
        <h1>⚡ BOUNCE 100 ⚡</h1>
        <div style="text-align:center; margin-bottom:10px; color:#ccc;">100 niveaux – Pub Y8 intégrée</div>
        <div class="level-grid" id="levelGrid"></div>
        <div style="display: flex; justify-content: center; gap: 10px; margin-top: 10px;">
            <button id="resetAllBtn">🔄 Tout reset</button>
            <button id="startGameFromMenuBtn">🎮 Jouer</button>
        </div>
        <div class="ad-space" style="margin-top: 15px;">
            <div>📢 PARTENAIRE Y8</div>
            <small style="color:#aaa;">Publicités intégrées</small>
            <div style="background:#2a2a4a; padding:10px; margin-top:8px; border-radius:10px; color:#ffd700;">
                🎮 Gagne des vies en regardant des pubs !
            </div>
        </div>
    </div>

    <!-- ZONE DE JEU -->
    <div id="gameWrapper" class="game-wrapper">
        <div class="game-container">
            <canvas id="gameCanvas" width="900" height="550"></canvas>
            <div class="game-header">
                <div>❤️ <span id="livesVal">3</span></div>
                <div>⭐ Niv.<span id="levelVal">1</span>/100</div>
                <div>🏆 <span id="scoreVal">0</span></div>
                <div>💎 <span id="starsVal">0</span></div>
            </div>
            <button id="rewardedAdBtn" style="position:absolute; bottom:15px; left:15px; background:#ffaa00; color:#000;">📺 +1 vie (pub)</button>
            <button id="menuBackBtn" style="position:absolute; bottom:15px; right:15px; background:rgba(0,0,0,0.7);">🏠 Menu</button>
        </div>
        <div id="gameOverlay" class="overlay">
            <h2 style="color:#ffd700;">💀 GAME OVER 💀</h2>
            <p>Score: <span id="finalScoreSpan">0</span></p>
            <button id="retryBtn">🔄 Réessayer</button>
            <button id="menuBtn">🏠 Menu</button>
        </div>
        <div id="levelCompleteOverlay" class="overlay">
            <h2 style="color:#ffd700;">🎉 Niveau terminé ! 🎉</h2>
            <p id="completeMsg"></p>
            <button id="nextLevelBtn">⏩ Niveau suivant</button>
            <button id="completeMenuBtn">🏠 Menu</button>
        </div>
        <div id="finalWinOverlay" class="overlay">
            <h2 style="color:#ffd700;">🏆 LÉGENDE ! 🏆</h2>
            <p>Vous avez terminé les 100 niveaux !</p>
            <p>Score final: <span id="finalWinScore">0</span></p>
            <button id="winMenuBtn">🏠 Menu principal</button>
        </div>
    </div>

    <!-- PANNEAU STATS & PUBLICITÉ -->
    <div class="info-panel">
        <div class="stat"><span>🏆 MEILLEUR SCORE</span><span id="highScoreSpan">0</span></div>
        <div class="stat"><span>⭐ NIVEAU MAX DÉBLOQUÉ</span><span id="maxLevelSpan">1</span>/100</div>
        <div class="stat"><span>🎯 DERNIER NIVEAU</span><span id="lastPlayedSpan">-</span></div>
        <button id="shareGameBtn" style="background:#3b5998; width:100%;">📤 Partager le jeu</button>
    </div>
</div>
<div class="sound-toggle" id="soundToggle">🔊</div>

<script>
    (function(){
        // ---------- CONFIGURATION ----------
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const WIDTH = 900, HEIGHT = 550;
        const GRAVITY = 0.45;
        const JUMP_POWER = -7.6;
        const GROUND_Y = HEIGHT - 55;

        // Éléments DOM
        const levelMenuDiv = document.getElementById('levelMenu');
        const gameWrapper = document.getElementById('gameWrapper');
        const livesSpan = document.getElementById('livesVal');
        const levelSpan = document.getElementById('levelVal');
        const scoreSpan = document.getElementById('scoreVal');
        const starsSpan = document.getElementById('starsVal');
        const highScoreSpan = document.getElementById('highScoreSpan');
        const maxLevelSpanSpan = document.getElementById('maxLevelSpan');
        const lastPlayedSpan = document.getElementById('lastPlayedSpan');
        const resetAllBtn = document.getElementById('resetAllBtn');
        const menuBackBtn = document.getElementById('menuBackBtn');
        const gameOverlay = document.getElementById('gameOverlay');
        const levelCompleteOverlay = document.getElementById('levelCompleteOverlay');
        const finalWinOverlay = document.getElementById('finalWinOverlay');
        const retryBtn = document.getElementById('retryBtn');
        const menuBtn = document.getElementById('menuBtn');
        const nextLevelBtn = document.getElementById('nextLevelBtn');
        const completeMenuBtn = document.getElementById('completeMenuBtn');
        const winMenuBtn = document.getElementById('winMenuBtn');
        const shareGameBtn = document.getElementById('shareGameBtn');
        const soundToggle = document.getElementById('soundToggle');
        const finalScoreSpan = document.getElementById('finalScoreSpan');
        const finalWinScoreSpan = document.getElementById('finalWinScore');
        const completeMsgSpan = document.getElementById('completeMsg');
        const startGameFromMenuBtn = document.getElementById('startGameFromMenuBtn');
        const rewardedAdBtn = document.getElementById('rewardedAdBtn');

        // Variables jeu
        let currentLevel = 1;
        let maxUnlocked = 1;
        let lives = 3;
        let score = 0;
        let starsCollected = 0;
        let gameRunning = true;
        let levelCompleteFlag = false;
        let soundEnabled = true;
        let highScore = 0;
        let completedLevels = [];

        // Joueur
        let player = {
            x: 80, y: GROUND_Y - 22,
            vx: 0, vy: 0,
            width: 20, height: 20,
            onGround: true, facingRight: true
        };

        // Contrôles
        let leftPressed = false, rightPressed = false, jumpRequested = false;

        // Éléments niveau
        let platforms = [], stars = [], enemies = [], movingPlatforms = [], teleporters = [], windZones = [], particles = [];

        // ---------- FONCTIONS Y8 SDK ----------
        function showInterstitialAd(callback) {
            if (window.AFPAdsSDK && window.AFPAdsSDK.showInterstitialAd) {
                window.AFPAdsSDK.showInterstitialAd(callback);
            } else {
                console.log("SDK Y8 non disponible, pas d'interstitiel");
                if (callback) callback();
            }
        }

        function showRewardedAd(callback) {
            if (window.AFPAdsSDK && window.AFPAdsSDK.showRewardedAd) {
                window.AFPAdsSDK.showRewardedAd(callback);
            } else {
                console.log("SDK Y8 non disponible, pas de récompense");
                if (callback) callback(false);
            }
        }

        // Bouton pub récompensée : +1 vie
        rewardedAdBtn.onclick = () => {
            if (lives < 5 && gameRunning) {
                showRewardedAd((success) => {
                    if (success) {
                        lives++;
                        updateUI();
                        addParticles(player.x+player.width/2, player.y+player.height/2, '#ffd700', 20);
                        playSound('collect');
                    } else {
                        alert("Impossible de charger la publicité pour le moment.");
                    }
                });
            } else {
                alert("Vies pleines (max 5) ou jeu terminé.");
            }
        };

        // ---------- GÉNÉRATION DES NIVEAUX (inchangée) ----------
        function generateLevel(levelNum) {
            const plat = [], starList = [], enemyList = [], movingPlatList = [], teleList = [], windList = [];
            plat.push({ x: 0, y: GROUND_Y, w: WIDTH, h: 25, type: 'normal' });
            const difficulty = (levelNum - 1) / 99;
            const platformCount = 5 + Math.floor(levelNum / 8);
            const enemyCount = Math.min(6, 1 + Math.floor(levelNum / 12));
            const starCount = Math.min(8, 3 + Math.floor(levelNum / 10));
            const theme = Math.floor((levelNum % 5));
            for (let i = 0; i < platformCount; i++) {
                let x = 80 + (i * (WIDTH - 160) / platformCount) + (Math.sin(levelNum * i) * 35);
                x = Math.min(Math.max(x, 60), WIDTH - 110);
                let y = GROUND_Y - 55 - (i * 10) - (Math.sin(levelNum + i) * 18);
                y = Math.min(Math.max(y, GROUND_Y - 190), GROUND_Y - 45);
                let w = 55 + (Math.sin(levelNum + i) * 15);
                w = Math.min(Math.max(w, 45), 90);
                let type = 'normal';
                if (levelNum >= 3 && i === 2 && Math.random() > 0.6) type = 'spring';
                if (levelNum >= 5 && i === 3 && Math.random() > 0.7) type = 'spike';
                if (i === platformCount - 1) type = 'goal';
                if (levelNum >= 10 && i === 1 && Math.random() > 0.7) {
                    movingPlatList.push({ x: x, y: y, w: w, h: 18, type: 'normal', range: 60, speed: 1, direction: 1, startX: x });
                    continue;
                }
                plat.push({ x: x, y: y, w: w, h: 18, type: type });
            }
            for (let i = 0; i < starCount; i++) {
                let idx = 1 + (i % (plat.length - 1));
                let p = plat[idx];
                if (p && p.type !== 'spike') starList.push({ x: p.x + p.w/2 - 7, y: p.y - 18, collected: false });
            }
            for (let i = 0; i < enemyCount; i++) {
                let idx = 1 + (i % (plat.length - 2));
                let p = plat[idx];
                if (p && p.type !== 'spike' && p.type !== 'goal') {
                    enemyList.push({
                        x: p.x + 15, y: p.y - 25, w: 22, h: 22,
                        direction: i%2===0 ? 1 : -1,
                        speed: 1.2 + difficulty * 2.5,
                        type: theme === 1 ? 'fire' : 'slime'
                    });
                }
            }
            if (levelNum >= 15 && Math.random() > 0.8) {
                teleList.push({ x: 200, y: GROUND_Y - 100, w: 30, h: 30, targetX: WIDTH-200, targetY: GROUND_Y-100 });
                teleList.push({ x: WIDTH-200, y: GROUND_Y - 100, w: 30, h: 30, targetX: 200, targetY: GROUND_Y-100 });
            }
            if (levelNum >= 20 && Math.random() > 0.7) {
                windList.push({ x: 400, y: 0, w: 150, h: HEIGHT, force: 2 + difficulty * 3, direction: 1 });
            }
            return { platforms: plat, stars: starList, enemies: enemyList, movingPlatforms: movingPlatList, teleporters: teleList, windZones: windList };
        }
        const levelCache = {};
        function getLevel(levelNum) {
            if (!levelCache[levelNum]) levelCache[levelNum] = generateLevel(levelNum);
            return JSON.parse(JSON.stringify(levelCache[levelNum]));
        }

        function loadLevel(levelNum) {
            if (levelNum > 100) { showFinalWin(); return; }
            const lvl = getLevel(levelNum);
            platforms = lvl.platforms;
            stars = lvl.stars;
            enemies = lvl.enemies;
            movingPlatforms = lvl.movingPlatforms || [];
            teleporters = lvl.teleporters || [];
            windZones = lvl.windZones || [];
            player.x = 80; player.y = GROUND_Y - player.height;
            player.vx = 0; player.vy = 0; player.onGround = true;
            starsCollected = 0;
            gameRunning = true;
            levelCompleteFlag = false;
            updateUI();
            playSound('levelStart');
        }

        // ---------- UI et sauvegarde ----------
        function updateUI() {
            livesSpan.innerText = lives;
            levelSpan.innerText = currentLevel;
            scoreSpan.innerText = score;
            const totalStars = stars.length;
            starsSpan.innerText = `${starsCollected}/${totalStars}`;
            lastPlayedSpan.innerText = currentLevel;
        }
        function saveProgress() {
            const data = { maxUnlocked: maxUnlocked, highScore: Math.max(highScore, score), completedLevels: completedLevels, score: score };
            localStorage.setItem('bounce100', JSON.stringify(data));
            highScoreSpan.innerText = data.highScore;
            maxLevelSpanSpan.innerText = maxUnlocked;
            highScore = data.highScore;
        }
        function loadProgress() {
            const saved = localStorage.getItem('bounce100');
            if (saved) {
                const data = JSON.parse(saved);
                maxUnlocked = data.maxUnlocked || 1;
                highScore = data.highScore || 0;
                completedLevels = data.completedLevels || [];
                score = data.score || 0;
            } else {
                maxUnlocked = 1; highScore = 0; completedLevels = []; score = 0;
            }
            highScoreSpan.innerText = highScore;
            maxLevelSpanSpan.innerText = maxUnlocked;
            lastPlayedSpan.innerText = currentLevel;
            updateLevelButtons();
        }
        function updateLevelButtons() {
            const grid = document.getElementById('levelGrid');
            grid.innerHTML = '';
            for (let i = 1; i <= 100; i++) {
                const btn = document.createElement('button');
                btn.innerText = `${i}`;
                if (i <= maxUnlocked) {
                    if (completedLevels.includes(i)) btn.classList.add('completed');
                    if (i === currentLevel) btn.classList.add('current');
                    btn.onclick = (function(lvl) { return function() {
                        if (gameWrapper.style.display === 'block') {
                            if (confirm('Changer de niveau ? La progression actuelle sera perdue.')) {
                                currentLevel = lvl; lives = 3; score = 0; fullResetGame(); startGame();
                            }
                        } else { currentLevel = lvl; lives = 3; score = 0; fullResetGame(); startGame(); }
                    }; })(i);
                } else {
                    btn.classList.add('locked');
                    btn.innerText = `🔒${i}`;
                    btn.disabled = true;
                }
                grid.appendChild(btn);
            }
        }

        // ---------- Physique ----------
        function applyPhysics() {
            for (let w of windZones) if (player.x + player.width > w.x && player.x < w.x + w.w) player.vx += w.force * w.direction * 0.1;
            player.vy += GRAVITY;
            if (leftPressed) { player.vx = Math.max(player.vx - 0.55, -5.8); player.facingRight = false; }
            else if (rightPressed) { player.vx = Math.min(player.vx + 0.55, 5.8); player.facingRight = true; }
            else player.vx *= 0.94;
            player.x += player.vx; player.y += player.vy;
            for (let mp of movingPlatforms) {
                mp.x += mp.speed * mp.direction;
                if (mp.x > mp.startX + mp.range || mp.x < mp.startX - mp.range) mp.direction *= -1;
                if (player.x + player.width > mp.x && player.x < mp.x + mp.w && player.y + player.height > mp.y && player.y < mp.y + mp.h) {
                    if (player.vy >= 0 && player.y + player.height - player.vy <= mp.y + 12) {
                        player.y = mp.y - player.height; player.vy = 0; player.onGround = true; player.x += mp.speed * mp.direction;
                    }
                }
            }
            player.onGround = false;
            for (let plat of platforms) {
                if (player.x + player.width > plat.x && player.x < plat.x + plat.w && player.y + player.height > plat.y && player.y < plat.y + plat.h) {
                    if (player.vy >= 0 && player.y + player.height - player.vy <= plat.y + 12) {
                        player.y = plat.y - player.height; player.vy = 0; player.onGround = true;
                        if (plat.type === 'spring') { player.vy = JUMP_POWER * 1.3; addParticles(player.x+player.width/2, player.y+player.height, '#ffaa44', 12); playSound('jump'); }
                        if (plat.type === 'spike') hitPlayer();
                        if (plat.type === 'goal' && !levelCompleteFlag && gameRunning) completeLevel();
                    } else if (player.x + player.width - player.vx <= plat.x + 8) player.x = plat.x - player.width;
                    else if (player.x - player.vx >= plat.x + plat.w - 8) player.x = plat.x + plat.w;
                    else if (player.vy < 0) { player.y = plat.y + plat.h; player.vy = 0; }
                }
            }
            for (let tp of teleporters) {
                if (player.x + player.width > tp.x && player.x < tp.x + tp.w && player.y + player.height > tp.y && player.y < tp.y + tp.h) {
                    player.x = tp.targetX; player.y = tp.targetY; playSound('collect'); addParticles(player.x, player.y, '#00ffff', 15);
                }
            }
            if (jumpRequested && player.onGround && gameRunning) {
                player.vy = JUMP_POWER; player.onGround = false; jumpRequested = false;
                addParticles(player.x+player.width/2, player.y+player.height, '#aaaaff', 8); playSound('jump');
            }
            if (player.y > HEIGHT && gameRunning) hitPlayer();
            if (player.x < 0) player.x = 0;
            if (player.x + player.width > WIDTH) player.x = WIDTH - player.width;
        }
        function collectStars() {
            for (let s of stars) {
                if (!s.collected && player.x + player.width > s.x && player.x < s.x+14 && player.y + player.height > s.y && player.y < s.y+14) {
                    s.collected = true; score += 50; starsCollected++; updateUI();
                    addParticles(s.x+7, s.y+7, '#ffd700', 10); playSound('collect');
                }
            }
        }
        function updateEnemies() {
            for (let e of enemies) {
                e.x += e.speed * e.direction;
                if (e.x < 40 || e.x + e.w > WIDTH-40) e.direction *= -1;
                if (player.x + player.width > e.x && player.x < e.x + e.w && player.y + player.height > e.y && player.y < e.y + e.h && gameRunning) {
                    if (player.vy > 0 && player.y + player.height - player.vy <= e.y + 10) {
                        enemies.splice(enemies.indexOf(e),1); player.vy = JUMP_POWER * 0.6; score += 100; updateUI();
                        addParticles(e.x+e.w/2, e.y+e.h/2, '#ff6666', 15); playSound('collect');
                    } else hitPlayer();
                }
            }
        }
        function hitPlayer() {
            lives--; updateUI(); addParticles(player.x+player.width/2, player.y+player.height/2, '#ff0000', 20); playSound('hit');
            if (lives <= 0) gameOver();
            else { player.x = 80; player.y = GROUND_Y - player.height; player.vx = 0; player.vy = 0; }
        }
        function completeLevel() {
            levelCompleteFlag = true; gameRunning = false;
            const starBonus = starsCollected * 50, levelBonus = 200, totalBonus = levelBonus + starBonus;
            score += totalBonus;
            if (!completedLevels.includes(currentLevel)) completedLevels.push(currentLevel);
            if (currentLevel === maxUnlocked && currentLevel < 100) maxUnlocked = currentLevel + 1;
            if (score > highScore) highScore = score;
            saveProgress(); updateLevelButtons(); playSound('levelComplete');
            completeMsgSpan.innerHTML = `+${totalBonus} points !<br>⭐ ${starsCollected}/${stars.length} étoiles`;
            levelCompleteOverlay.style.display = 'block';
            // Afficher une publicité interstitielle après validation (pas avant que l'overlay soit fermé)
            showInterstitialAd(() => {});
        }
        function nextLevel() {
            levelCompleteOverlay.style.display = 'none';
            if (currentLevel < 100) {
                currentLevel++; lives = Math.min(lives + 1, 5); loadLevel(currentLevel); gameRunning = true; levelCompleteFlag = false; updateUI();
            } else showFinalWin();
        }
        function showFinalWin() { finalWinScoreSpan.innerText = score; finalWinOverlay.style.display = 'block'; gameRunning = false; }
        function gameOver() { gameRunning = false; finalScoreSpan.innerText = score; gameOverlay.style.display = 'block'; saveProgress(); }
        function fullResetGame() { lives = 3; score = 0; starsCollected = 0; gameRunning = true; levelCompleteFlag = false; loadLevel(currentLevel); updateUI(); }
        function resetAllProgress() {
            if (confirm("⚠️ Effacer toute votre progression sur les 100 niveaux ?")) {
                localStorage.removeItem('bounce100');
                maxUnlocked = 1; highScore = 0; completedLevels = []; score = 0; currentLevel = 1; lives = 3;
                saveProgress(); updateLevelButtons();
                if (gameWrapper.style.display === 'block') fullResetGame();
                lastPlayedSpan.innerText = currentLevel; highScoreSpan.innerText = highScore; maxLevelSpanSpan.innerText = maxUnlocked;
            }
        }
        function startGame() { levelMenuDiv.style.display = 'none'; gameWrapper.style.display = 'block'; fullResetGame(); }
        function returnToMenu() { levelMenuDiv.style.display = 'flex'; gameWrapper.style.display = 'none'; gameRunning = false; updateLevelButtons(); }

        // ---------- Son ----------
        let audioCtx = null;
        function playSound(type) {
            if (!soundEnabled) return;
            try {
                if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                const osc = audioCtx.createOscillator(), gain = audioCtx.createGain();
                osc.connect(gain); gain.connect(audioCtx.destination);
                let freq = 440, dur = 0.2;
                switch(type) {
                    case 'jump': freq = 523.25; dur=0.12; break;
                    case 'collect': freq = 659.25; dur=0.1; break;
                    case 'hit': freq = 220; dur=0.3; break;
                    case 'levelComplete': freq = 783.99; dur=0.4; break;
                    case 'levelStart': freq = 392; dur=0.2; break;
                }
                osc.frequency.value = freq; gain.gain.value = 0.2;
                osc.start(); gain.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + dur);
                osc.stop(audioCtx.currentTime + dur);
            } catch(e) {}
        }

        // ---------- Particules ----------
        function addParticles(x,y,color,count=8) {
            for(let i=0;i<count;i++) particles.push({ x,y, vx:(Math.random()-0.5)*4, vy:(Math.random()-0.5)*3-1, life:0.7, color, size:Math.random()*3+1.5 });
        }
        function updateParticles() {
            for(let i=0;i<particles.length;i++) {
                particles[i].x += particles[i].vx; particles[i].y += particles[i].vy; particles[i].life -= 0.02;
                if(particles[i].life<=0) particles.splice(i,1), i--;
            }
        }

        // ---------- Dessin ----------
        function drawBackground() {
            const grad = ctx.createLinearGradient(0,0,0,HEIGHT);
            grad.addColorStop(0, '#0a0f2a'); grad.addColorStop(1, '#1a1f3a');
            ctx.fillStyle = grad; ctx.fillRect(0,0,WIDTH,HEIGHT);
            ctx.fillStyle = 'rgba(255,255,200,0.3)';
            for(let i=0;i<80;i++) { ctx.beginPath(); ctx.arc((i*131)%WIDTH, (i*57)%HEIGHT, 1.2, 0, Math.PI*2); ctx.fill(); }
        }
        function drawPlatforms() {
            for(let plat of platforms) {
                ctx.shadowBlur = 3;
                if(plat.type === 'spike') {
                    ctx.fillStyle = '#aa4444'; ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
                    for(let i=0;i<plat.w/12;i++) { ctx.beginPath(); ctx.moveTo(plat.x+i*12, plat.y); ctx.lineTo(plat.x+i*12+6, plat.y-10); ctx.lineTo(plat.x+i*12-6, plat.y-10); ctx.fillStyle = '#ff6666'; ctx.fill(); }
                } else if(plat.type === 'spring') {
                    ctx.fillStyle = '#f0a030'; ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
                    ctx.fillStyle = '#d08020';
                    for(let i=0;i<4;i++) ctx.fillRect(plat.x+8+i*14, plat.y-6, 8,6);
                } else if(plat.type === 'goal') {
                    ctx.fillStyle = '#44aa77'; ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
                    ctx.fillStyle = '#ffdd88'; ctx.font = "22px monospace"; ctx.fillText("🏁", plat.x+plat.w/2-8, plat.y+14);
                } else {
                    const grad = ctx.createLinearGradient(plat.x, plat.y, plat.x, plat.y+plat.h);
                    grad.addColorStop(0, '#5a7a5a'); grad.addColorStop(1, '#3a5a3a');
                    ctx.fillStyle = grad; ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
                    ctx.fillStyle = '#7a9a7a'; ctx.fillRect(plat.x, plat.y, plat.w, 4);
                }
            }
            for(let mp of movingPlatforms) { ctx.fillStyle = '#88aaff'; ctx.fillRect(mp.x, mp.y, mp.w, mp.h); ctx.fillStyle = '#aaccff'; ctx.fillRect(mp.x, mp.y, mp.w, 4); }
            for(let tp of teleporters) { ctx.fillStyle = '#44aaff'; ctx.globalAlpha = 0.7; ctx.fillRect(tp.x, tp.y, tp.w, tp.h); ctx.fillStyle = '#ffffff'; ctx.font = "18px monospace"; ctx.fillText("🌀", tp.x+8, tp.y+22); ctx.globalAlpha = 1; }
            ctx.shadowBlur = 0;
        }
        function drawStars() {
            for(let s of stars) {
                if(!s.collected) {
                    ctx.shadowBlur = 8; ctx.shadowColor = '#ffaa00';
                    ctx.fillStyle = '#ffdd77'; ctx.beginPath(); ctx.arc(s.x+7, s.y+7, 7, 0, Math.PI*2); ctx.fill();
                    ctx.fillStyle = '#ffaa33'; ctx.font = "16px monospace"; ctx.fillText("★", s.x+2, s.y+11);
                }
            }
            ctx.shadowBlur = 0;
        }
        function drawEnemies() {
            for(let e of enemies) {
                ctx.fillStyle = e.type === 'fire' ? '#ff6633' : '#8844cc';
                ctx.shadowBlur = 4; ctx.fillRect(e.x, e.y, e.w, e.h);
                ctx.fillStyle = '#ffffff'; ctx.fillRect(e.x+5, e.y+5, 4,4); ctx.fillRect(e.x+13, e.y+5, 4,4); ctx.fillRect(e.x+4, e.y+12, 14,3);
            }
        }
        function drawPlayer() {
            ctx.shadowBlur = 10; ctx.shadowColor = '#ff6666';
            const grad = ctx.createRadialGradient(player.x+7, player.y+7, 3, player.x+10, player.y+10, 12);
            grad.addColorStop(0, '#ff6666'); grad.addColorStop(1, '#cc3333');
            ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(player.x+player.width/2, player.y+player.height/2, player.width/2, 0, Math.PI*2); ctx.fill();
            ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(player.x+(player.facingRight?13:7), player.y+7, 3, 0, Math.PI*2); ctx.fill();
            ctx.fillStyle = '#222'; ctx.beginPath(); ctx.arc(player.x+(player.facingRight?13:7), player.y+6, 1.8, 0, Math.PI*2); ctx.fill();
            ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.beginPath(); ctx.ellipse(player.x+5, player.y+5, 4, 2, 0, 0, Math.PI*2); ctx.fill();
            ctx.shadowBlur = 0;
        }
        function drawParticles() { for(let p of particles) { ctx.globalAlpha = p.life; ctx.fillStyle = p.color; ctx.fillRect(p.x, p.y, p.size, p.size); } ctx.globalAlpha = 1; }
        function draw() { drawBackground(); drawPlatforms(); drawStars(); drawEnemies(); drawPlayer(); drawParticles(); ctx.font = "bold 12px monospace"; ctx.fillStyle = "#ffd700"; ctx.fillText(`Niveau ${currentLevel}/100`, 15, 30); }

        // ---------- Boucle jeu ----------
        function update() { if(!gameRunning) return; applyPhysics(); collectStars(); updateEnemies(); updateParticles(); }
        function gameLoop() { update(); draw(); requestAnimationFrame(gameLoop); }

        // ---------- Contrôles clavier ----------
        function handleKeyDown(e) {
            const key = e.key;
            if(key === 'ArrowLeft' || key === 'q' || key === 'Q') { leftPressed = true; e.preventDefault(); }
            if(key === 'ArrowRight' || key === 'd' || key === 'D') { rightPressed = true; e.preventDefault(); }
            if(key === 'ArrowUp' || key === ' ' || key === 'w' || key === 'W') { jumpRequested = true; e.preventDefault(); }
            if(key === 'r' || key === 'R') { fullResetGame(); e.preventDefault(); }
        }
        function handleKeyUp(e) {
            const key = e.key;
            if(key === 'ArrowLeft' || key === 'q' || key === 'Q') leftPressed = false;
            if(key === 'ArrowRight' || key === 'd' || key === 'D') rightPressed = false;
        }

        // ---------- Partage ----------
        shareGameBtn.onclick = () => {
            if (navigator.share) {
                navigator.share({ title: 'Bounce 100', text: `J'ai marqué ${score} points sur Bounce 100 !`, url: window.location.href });
            } else {
                const dummy = document.createElement('textarea');
                dummy.value = window.location.href;
                document.body.appendChild(dummy);
                dummy.select();
                document.execCommand('copy');
                document.body.removeChild(dummy);
                alert('✅ Lien copié ! Partage-le avec tes amis :\n' + window.location.href);
            }
        };

        // ---------- Sons toggle ----------
        soundToggle.onclick = () => { soundEnabled = !soundEnabled; soundToggle.innerText = soundEnabled ? "🔊" : "🔇"; };

        // ---------- Initialisation ----------
        function init() {
            loadProgress();
            updateLevelButtons();
            resetAllBtn.onclick = resetAllProgress;
            menuBackBtn.onclick = returnToMenu;
            retryBtn.onclick = () => { gameOverlay.style.display = 'none'; fullResetGame(); };
            menuBtn.onclick = returnToMenu;
            nextLevelBtn.onclick = nextLevel;
            completeMenuBtn.onclick = returnToMenu;
            winMenuBtn.onclick = returnToMenu;
            startGameFromMenuBtn.onclick = startGame;
            window.addEventListener('keydown', handleKeyDown);
            window.addEventListener('keyup', handleKeyUp);
            gameLoop();
        }
        init();
    })();
</script>
</body>
</html>

Game Source: Bounce 100 - Le défi ultime

Creator: NovaWizard65

Libraries: none

Complexity: complex (784 lines, 37.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: bounce-100-le-d-fi-ultime-novawizard65" to link back to the original. Then publish at arcadelab.ai/publish.