Bounce 100 - Le défi ultime
by NovaWizard65784 lines37.2 KB
<!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.