Bounce 100
by NovaWizard651235 lines43.6 KB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Bounce 100</title>
<!-- GamePix SDK -->
<script src="https://integration.gamepix.com/sdk/v3/gamepix.sdk.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #0f172a;
font-family: 'Segoe UI', Arial, sans-serif;
}
#gameWrapper {
position: relative;
width: 100%;
height: 100%;
max-width: 900px;
max-height: 550px;
margin: 0 auto;
background: #000;
border: 3px solid #ffd700;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0,0,0,0.8);
aspect-ratio: 900 / 550;
}
#gameWrapper canvas {
display: block;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0,0,0,0.88);
backdrop-filter: blur(4px);
z-index: 20;
padding: 20px;
text-align: center;
border-radius: 16px;
}
.overlay.active {
display: flex;
}
.overlay h1 {
font-size: clamp(2rem, 6vw, 3.2rem);
font-weight: 900;
color: #ffd700;
text-shadow: 0 0 30px #ffaa00, 0 0 60px #ff6600;
letter-spacing: 4px;
margin-bottom: 8px;
}
.overlay p {
color: #ccc;
max-width: 400px;
font-size: clamp(0.85rem, 2vw, 1rem);
margin: 6px 0;
}
.overlay .keys {
color: #aaa;
font-size: 0.8rem;
background: rgba(255,255,255,0.06);
padding: 6px 16px;
border-radius: 40px;
border: 1px solid rgba(255,255,255,0.08);
margin: 6px 0;
}
.overlay .keys kbd {
background: #1a1a2e;
color: #ffd700;
padding: 2px 10px;
border-radius: 4px;
border: 1px solid #444;
font-family: monospace;
font-size: 0.8rem;
}
.btn {
background: linear-gradient(135deg, #ff416c, #ff4b2b);
border: none;
padding: 12px 32px;
border-radius: 40px;
font-weight: bold;
font-size: clamp(0.9rem, 2vw, 1.1rem);
color: #fff;
cursor: pointer;
margin: 6px;
transition: transform 0.15s;
box-shadow: 0 4px 20px rgba(255,65,108,0.4);
touch-action: manipulation;
min-height: 48px;
min-width: 120px;
pointer-events: auto;
}
.btn:hover {
transform: scale(1.04);
}
.btn:active {
transform: scale(0.96);
}
.btn.secondary {
background: rgba(255,255,255,0.12);
border: 1px solid rgba(255,255,255,0.2);
box-shadow: none;
}
.btn.gold {
background: linear-gradient(135deg, #f7971e, #ffd200);
color: #1a1a2e;
box-shadow: 0 4px 20px rgba(255,215,0,0.4);
}
.grid {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 4px;
max-width: 380px;
max-height: 140px;
overflow-y: auto;
margin: 6px 0;
width: 100%;
padding: 4px;
}
.grid button {
background: rgba(255,215,0,0.12);
border: 1px solid rgba(255,215,0,0.2);
color: #ffd700;
padding: 4px 0;
border-radius: 6px;
font-size: 0.65rem;
cursor: pointer;
transition: 0.1s;
touch-action: manipulation;
pointer-events: auto;
}
.grid button:hover:not(.locked) {
background: rgba(255,215,0,0.3);
}
.grid button.locked {
opacity: 0.35;
cursor: not-allowed;
border-color: #444;
color: #666;
}
.grid button.completed {
border-color: #4caf50;
color: #4caf50;
}
.grid button.current {
border-color: #ff9800;
color: #ff9800;
background: rgba(255,152,0,0.2);
}
.header {
position: absolute;
top: 8px;
left: 8px;
right: 8px;
display: flex;
justify-content: space-between;
z-index: 10;
pointer-events: none;
gap: 6px;
flex-wrap: wrap;
}
.header .stats {
display: flex;
gap: 6px;
flex-wrap: wrap;
pointer-events: none;
}
.header .stats span {
background: rgba(0,0,0,0.75);
padding: 3px 10px;
border-radius: 20px;
font-size: clamp(0.6rem, 1.2vw, 0.75rem);
font-weight: bold;
color: #ffd700;
pointer-events: none;
white-space: nowrap;
}
.header .menu-btn {
background: rgba(0,0,0,0.75);
border: 1px solid #ffd700;
color: #ffd700;
padding: 3px 12px;
border-radius: 20px;
font-size: clamp(0.6rem, 1.2vw, 0.75rem);
cursor: pointer;
pointer-events: auto;
transition: 0.15s;
touch-action: manipulation;
min-height: 28px;
}
.header .menu-btn:hover {
background: #ffd700;
color: #000;
}
.header .menu-btn:active {
transform: scale(0.95);
}
.title-in-game {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
color: rgba(255,215,0,0.10);
font-size: clamp(1rem, 3vw, 1.8rem);
font-weight: 900;
letter-spacing: 6px;
pointer-events: none;
z-index: 5;
text-transform: uppercase;
white-space: nowrap;
}
.grid::-webkit-scrollbar {
width: 4px;
}
.grid::-webkit-scrollbar-track {
background: rgba(255,255,255,0.05);
border-radius: 10px;
}
.grid::-webkit-scrollbar-thumb {
background: #ffd700;
border-radius: 10px;
}
.hidden {
display: none !important;
}
@media (max-width: 600px) {
#gameWrapper {
border-radius: 0;
border-width: 2px;
max-width: 100%;
max-height: 100%;
aspect-ratio: 900/550;
}
.header .stats span {
font-size: 0.55rem;
padding: 2px 6px;
}
.header .menu-btn {
font-size: 0.55rem;
padding: 2px 8px;
min-height: 24px;
}
.btn {
padding: 10px 20px;
font-size: 0.85rem;
min-height: 40px;
min-width: 80px;
}
.grid {
max-width: 100%;
gap: 3px;
}
.grid button {
font-size: 0.55rem;
padding: 3px 0;
}
.overlay h1 {
font-size: 1.8rem;
}
}
@media (max-width: 400px) {
.header .stats span {
font-size: 0.45rem;
padding: 1px 4px;
}
.header .menu-btn {
font-size: 0.45rem;
padding: 1px 6px;
min-height: 20px;
}
.btn {
padding: 8px 14px;
font-size: 0.75rem;
min-height: 34px;
min-width: 60px;
}
.overlay h1 {
font-size: 1.4rem;
}
.grid button {
font-size: 0.45rem;
padding: 2px 0;
}
}
</style>
</head>
<body>
<div id="gameWrapper">
<canvas id="gameCanvas" width="900" height="550"></canvas>
<!-- HEADER -->
<div class="header">
<div class="stats">
<span>❤️ <span id="livesVal">3</span></span>
<span>⭐ <span id="levelVal">1</span>/100</span>
<span>🏆 <span id="scoreVal">0</span></span>
<span>💎 <span id="starsVal">0</span></span>
</div>
<div style="display:flex; gap:6px; pointer-events:auto;">
<button class="menu-btn" id="rewardBtn">📺 +1 vie</button>
<button class="menu-btn" id="inGameMenuBtn">🏠 Menu</button>
</div>
</div>
<div class="title-in-game">BOUNCE 100</div>
<!-- MENU PRINCIPAL -->
<div id="mainMenu" class="overlay active">
<h1>⚡ BOUNCE 100 ⚡</h1>
<p>100 niveaux de plateformes addictifs.</p>
<p style="font-size:0.9rem;color:#aaa;">Collecte les étoiles, évite les ennemis, atteins le drapeau.</p>
<div class="keys">
<kbd>←</kbd> <kbd>→</kbd> ou <kbd>Q</kbd> <kbd>D</kbd> · <kbd>↑</kbd> <kbd>Espace</kbd> <kbd>W</kbd> sauter
</div>
<button class="btn" id="playBtn">🎮 Jouer</button>
<button class="btn secondary" id="levelsBtn">📋 Niveaux</button>
<div id="levelGridContainer" class="hidden" style="width:100%;"></div>
</div>
<!-- GAME OVER -->
<div id="gameOverOverlay" class="overlay">
<h1 style="color:#ff6b6b;">💀 GAME OVER</h1>
<p>Score : <span id="finalScoreSpan">0</span></p>
<button class="btn" id="retryBtn">🔄 Réessayer</button>
<button class="btn secondary" id="menuBtnRetry">🏠 Menu</button>
</div>
<!-- NIVEAU TERMINÉ -->
<div id="levelCompleteOverlay" class="overlay">
<h1>🎉 Niveau terminé !</h1>
<p id="completeMsg"></p>
<button class="btn" id="nextLevelBtn">⏩ Suivant</button>
<button class="btn secondary" id="menuBtnComplete">🏠 Menu</button>
</div>
<!-- VICTOIRE FINALE -->
<div id="finalWinOverlay" class="overlay">
<h1>🏆 LÉGENDE !</h1>
<p>Vous avez terminé les 100 niveaux !</p>
<p>Score final : <span id="finalWinScore">0</span></p>
<button class="btn" id="winMenuBtn">🏠 Menu</button>
</div>
</div>
<script>
(function() {
"use strict";
// ========== GAMEPIX SDK FONCTIONS ==========
function gamepixSetItem(key, value) {
try {
if (typeof GamePix !== 'undefined' && GamePix.localStorage && GamePix.localStorage.setItem) {
GamePix.localStorage.setItem(key, String(value));
} else {
localStorage.setItem(key, String(value));
}
} catch(e) {}
}
function gamepixGetItem(key) {
try {
if (typeof GamePix !== 'undefined' && GamePix.localStorage && GamePix.localStorage.getItem) {
return GamePix.localStorage.getItem(key);
} else {
return localStorage.getItem(key);
}
} catch(e) { return null; }
}
function gamepixInterstitialAd(callback) {
try {
if (typeof GamePix !== 'undefined' && GamePix.interstitialAd) {
isPaused = true;
GamePix.interstitialAd().then(function(res) {
isPaused = false;
if (callback) callback(res);
}).catch(function() {
isPaused = false;
if (callback) callback(null);
});
} else {
if (callback) callback(null);
}
} catch(e) {
isPaused = false;
if (callback) callback(null);
}
}
function gamepixRewardAd(callback) {
try {
if (typeof GamePix !== 'undefined' && GamePix.rewardAd) {
isPaused = true;
GamePix.rewardAd().then(function(res) {
isPaused = false;
if (callback) callback(res && res.success);
}).catch(function() {
isPaused = false;
if (callback) callback(false);
});
} else {
if (callback) callback(false);
}
} catch(e) {
isPaused = false;
if (callback) callback(false);
}
}
function gamepixUpdateLevel(levelNum) {
try {
if (typeof GamePix !== 'undefined' && GamePix.updateLevel) {
GamePix.updateLevel(levelNum);
}
} catch(e) {}
}
function gamepixHappyMoment() {
try {
if (typeof GamePix !== 'undefined' && GamePix.happyMoment) {
GamePix.happyMoment();
}
} catch(e) {}
}
// ========== ÉLÉMENTS DOM ==========
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const livesSpan = document.getElementById('livesVal');
const levelSpan = document.getElementById('levelVal');
const scoreSpan = document.getElementById('scoreVal');
const starsSpan = document.getElementById('starsVal');
const finalScoreSpan = document.getElementById('finalScoreSpan');
const finalWinScoreSpan = document.getElementById('finalWinScore');
const completeMsgSpan = document.getElementById('completeMsg');
const mainMenu = document.getElementById('mainMenu');
const gameOverOverlay = document.getElementById('gameOverOverlay');
const levelCompleteOverlay = document.getElementById('levelCompleteOverlay');
const finalWinOverlay = document.getElementById('finalWinOverlay');
const levelGridContainer = document.getElementById('levelGridContainer');
const playBtn = document.getElementById('playBtn');
const levelsBtn = document.getElementById('levelsBtn');
const retryBtn = document.getElementById('retryBtn');
const menuBtnRetry = document.getElementById('menuBtnRetry');
const nextLevelBtn = document.getElementById('nextLevelBtn');
const menuBtnComplete = document.getElementById('menuBtnComplete');
const winMenuBtn = document.getElementById('winMenuBtn');
const inGameMenuBtn = document.getElementById('inGameMenuBtn');
const rewardBtn = document.getElementById('rewardBtn');
// ========== CONSTANTES ==========
const W = 900,
H = 550;
const GRAVITY = 0.45;
const JUMP = -7.6;
const GROUND = H - 55;
// ========== ÉTAT JEU ==========
let level = 1;
let maxUnlocked = 1;
let lives = 3;
let score = 0;
let starsGot = 0;
let running = false;
let levelDone = false;
let lock = false;
let highScore = 0;
let completed = [];
let isPaused = false;
let p = { x: 80, y: GROUND - 22, vx: 0, vy: 0, w: 20, h: 20, onGround: false, facingRight: true };
let left = false,
right = false,
jump = false;
let platforms = [],
stars = [],
enemies = [],
moving = [],
tele = [],
wind = [],
particles = [];
// ========== OVERLAYS ==========
function hideAll() {
document.querySelectorAll('.overlay').forEach(function(el) {
el.classList.remove('active');
});
levelGridContainer.classList.add('hidden');
}
function show(id) {
hideAll();
var el = document.getElementById(id);
if (el) el.classList.add('active');
}
// ========== SAUVEGARDE ==========
function save() {
try {
var data = JSON.stringify({ maxUnlocked: maxUnlocked, highScore: Math.max(highScore, score),
completed: completed, score: score });
gamepixSetItem('bounce100', data);
} catch(e) {}
}
function load() {
try {
var raw = gamepixGetItem('bounce100');
if (raw) {
var data = JSON.parse(raw);
maxUnlocked = data.maxUnlocked || 1;
highScore = data.highScore || 0;
completed = data.completed || [];
score = data.score || 0;
return;
}
} catch(e) {}
maxUnlocked = 1;
highScore = 0;
completed = [];
score = 0;
}
// ========== GÉNÉRATION NIVEAU ==========
function genLevel(n) {
var plat = [],
starList = [],
enemyList = [],
movingList = [],
teleList = [],
windList = [];
plat.push({ x: 0, y: GROUND, w: W, h: 25, type: 'normal' });
var diff = (n - 1) / 99;
var pCount = 5 + Math.floor(n / 8);
var eCount = Math.min(6, 1 + Math.floor(n / 12));
var sCount = Math.min(8, 3 + Math.floor(n / 10));
var theme = Math.floor((n % 5));
for (var i = 0; i < pCount; i++) {
var x = 80 + (i * (W - 160) / pCount) + (Math.sin(n * i) * 35);
x = Math.min(Math.max(x, 60), W - 110);
var y = GROUND - 55 - (i * 10) - (Math.sin(n + i) * 18);
y = Math.min(Math.max(y, GROUND - 190), GROUND - 45);
var w = 55 + (Math.sin(n + i) * 15);
w = Math.min(Math.max(w, 45), 90);
var type = 'normal';
if (n >= 3 && i === 2 && Math.random() > 0.6) type = 'spring';
if (n >= 5 && i === 3 && Math.random() > 0.7) type = 'spike';
if (i === pCount - 1) type = 'goal';
if (n >= 10 && i === 1 && Math.random() > 0.7) {
movingList.push({ x: x, y: y, w: w, h: 18, type: 'normal', range: 60, speed: 1, dir: 1,
startX: x });
continue;
}
plat.push({ x: x, y: y, w: w, h: 18, type: type });
}
for (var j = 0; j < sCount; j++) {
var idx = 1 + (j % (plat.length - 1));
var pl = plat[idx];
if (pl && pl.type !== 'spike') {
starList.push({ x: pl.x + pl.w / 2 - 7, y: pl.y - 18, collected: false });
}
}
for (var k = 0; k < eCount; k++) {
var idx2 = 1 + (k % (plat.length - 2));
var pl2 = plat[idx2];
if (pl2 && pl2.type !== 'spike' && pl2.type !== 'goal') {
enemyList.push({
x: pl2.x + 15,
y: pl2.y - 25,
w: 22,
h: 22,
dir: k % 2 === 0 ? 1 : -1,
speed: 1.2 + diff * 2.5,
type: theme === 1 ? 'fire' : 'slime'
});
}
}
if (n >= 15 && Math.random() > 0.8) {
teleList.push({ x: 200, y: GROUND - 100, w: 30, h: 30, tx: W - 200, ty: GROUND - 100 });
teleList.push({ x: W - 200, y: GROUND - 100, w: 30, h: 30, tx: 200, ty: GROUND - 100 });
}
if (n >= 20 && Math.random() > 0.7) {
windList.push({ x: 400, y: 0, w: 150, h: H, force: 2 + diff * 3, dir: 1 });
}
return { platforms: plat, stars: starList, enemies: enemyList, moving: movingList, tele: teleList,
wind: windList };
}
var cache = {};
function getLevel(n) {
if (!cache[n]) cache[n] = genLevel(n);
return JSON.parse(JSON.stringify(cache[n]));
}
function loadLevel(n) {
if (n > 100) { showFinalWin(); return; }
var lvl = getLevel(n);
platforms = lvl.platforms;
stars = lvl.stars;
enemies = lvl.enemies;
moving = lvl.moving || [];
tele = lvl.tele || [];
wind = lvl.wind || [];
p.x = 80;
p.y = GROUND - p.h;
p.vx = 0;
p.vy = 0;
p.onGround = false;
starsGot = 0;
running = true;
levelDone = false;
isPaused = false;
updateUI();
gamepixUpdateLevel(n);
}
// ========== UI ==========
function updateUI() {
livesSpan.textContent = lives;
levelSpan.textContent = level;
scoreSpan.textContent = score;
var total = stars.length || 0;
starsSpan.textContent = starsGot + '/' + total;
}
function buildGrid() {
levelGridContainer.innerHTML = '';
var div = document.createElement('div');
div.className = 'grid';
for (var i = 1; i <= 100; i++) {
var btn = document.createElement('button');
btn.textContent = i;
if (i <= maxUnlocked) {
if (completed.indexOf(i) !== -1) btn.classList.add('completed');
if (i === level) btn.classList.add('current');
btn.onclick = (function(lvl) {
return function() {
if (confirm('Changer de niveau ? La progression actuelle sera perdue.')) {
level = lvl;
lives = 3;
score = 0;
startGame();
}
};
})(i);
} else {
btn.classList.add('locked');
btn.disabled = true;
}
div.appendChild(btn);
}
levelGridContainer.appendChild(div);
levelGridContainer.classList.remove('hidden');
}
// ========== TRANSITIONS ==========
function startGame() {
load();
if (maxUnlocked < 1) maxUnlocked = 1;
if (level > maxUnlocked) level = maxUnlocked;
lives = 3;
score = 0;
starsGot = 0;
hideAll();
loadLevel(level);
running = true;
updateUI();
}
function goMenu() {
hideAll();
mainMenu.classList.add('active');
running = false;
isPaused = true;
buildGrid();
}
function showGameOver() {
hideAll();
finalScoreSpan.textContent = score;
gameOverOverlay.classList.add('active');
running = false;
isPaused = true;
}
function showLevelComplete(msg) {
hideAll();
completeMsgSpan.innerHTML = msg;
levelCompleteOverlay.classList.add('active');
running = false;
isPaused = true;
gamepixInterstitialAd(function() {});
}
function showFinalWin() {
hideAll();
finalWinScoreSpan.textContent = score;
finalWinOverlay.classList.add('active');
running = false;
isPaused = true;
save();
}
function nextLevel() {
if (lock) return;
lock = true;
levelCompleteOverlay.classList.remove('active');
if (level < 100) {
level++;
lives = Math.min(lives + 1, 5);
loadLevel(level);
running = true;
levelDone = false;
isPaused = false;
updateUI();
} else {
showFinalWin();
}
setTimeout(function() { lock = false; }, 200);
}
function retry() {
gameOverOverlay.classList.remove('active');
startGame();
}
// ========== BOUTONS ==========
// Bouton Jouer
playBtn.addEventListener('click', startGame);
// Bouton Niveaux
levelsBtn.addEventListener('click', function() {
if (levelGridContainer.classList.contains('hidden')) {
buildGrid();
} else {
levelGridContainer.classList.add('hidden');
}
});
// Bouton Réessayer
retryBtn.addEventListener('click', retry);
// Bouton Menu (Game Over)
menuBtnRetry.addEventListener('click', goMenu);
// Bouton Niveau suivant
nextLevelBtn.addEventListener('click', nextLevel);
// Bouton Menu (Niveau terminé)
menuBtnComplete.addEventListener('click', goMenu);
// Bouton Menu (Victoire finale)
winMenuBtn.addEventListener('click', goMenu);
// Bouton Menu en jeu
inGameMenuBtn.addEventListener('click', function() {
if (confirm('Revenir au menu principal ? La progression actuelle sera perdue.')) {
goMenu();
}
});
// Bouton +1 vie (récompensé)
rewardBtn.addEventListener('click', function() {
if (lives < 5 && running) {
gamepixRewardAd(function(success) {
if (success) {
lives++;
updateUI();
addParticles(p.x + p.w / 2, p.y + p.h / 2, '#ffd700', 20);
playSound('collect');
gamepixHappyMoment();
} else {
alert("Publicité non disponible pour le moment.");
}
});
} else {
alert("Vies pleines (max 5) ou jeu terminé.");
}
});
// ========== PHYSIQUE ==========
function applyPhysics() {
for (var wi = 0; wi < wind.length; wi++) {
var w = wind[wi];
if (p.x + p.w > w.x && p.x < w.x + w.w) {
p.vx += w.force * w.dir * 0.1;
}
}
p.vy += GRAVITY;
if (left) {
p.vx = Math.max(p.vx - 0.55, -5.8);
p.facingRight = false;
} else if (right) {
p.vx = Math.min(p.vx + 0.55, 5.8);
p.facingRight = true;
} else {
p.vx *= 0.94;
}
p.x += p.vx;
p.y += p.vy;
for (var mi = 0; mi < moving.length; mi++) {
var m = moving[mi];
m.x += m.speed * m.dir;
if (m.x > m.startX + m.range || m.x < m.startX - m.range) m.dir *= -1;
if (p.x + p.w > m.x && p.x < m.x + m.w && p.y + p.h > m.y && p.y < m.y + m.h) {
if (p.vy >= 0 && p.y + p.h - p.vy <= m.y + 12) {
p.y = m.y - p.h;
p.vy = 0;
p.onGround = true;
p.x += m.speed * m.dir;
}
}
}
p.onGround = false;
for (var pi = 0; pi < platforms.length; pi++) {
var plat = platforms[pi];
if (p.x + p.w > plat.x && p.x < plat.x + plat.w && p.y + p.h > plat.y && p.y < plat.y + plat.h) {
if (p.vy >= 0 && p.y + p.h - p.vy <= plat.y + 12) {
p.y = plat.y - p.h;
p.vy = 0;
p.onGround = true;
if (plat.type === 'spring') {
p.vy = JUMP * 1.3;
addParticles(p.x + p.w / 2, p.y + p.h, '#ffaa44', 12);
playSound('jump');
}
if (plat.type === 'spike') {
hitPlayer();
}
if (plat.type === 'goal' && !levelDone && running) {
completeLevel();
}
} else if (p.x + p.w - p.vx <= plat.x + 8) {
p.x = plat.x - p.w;
} else if (p.x - p.vx >= plat.x + plat.w - 8) {
p.x = plat.x + plat.w;
} else if (p.vy < 0) {
p.y = plat.y + plat.h;
p.vy = 0;
}
}
}
for (var ti = 0; ti < tele.length; ti++) {
var t = tele[ti];
if (p.x + p.w > t.x && p.x < t.x + t.w && p.y + p.h > t.y && p.y < t.y + t.h) {
p.x = t.tx;
p.y = t.ty;
playSound('collect');
addParticles(p.x, p.y, '#00ffff', 15);
}
}
if (jump && p.onGround && running) {
p.vy = JUMP;
p.onGround = false;
jump = false;
addParticles(p.x + p.w / 2, p.y + p.h, '#aaaaff', 8);
playSound('jump');
}
if (p.y > H && running) hitPlayer();
if (p.x < 0) p.x = 0;
if (p.x + p.w > W) p.x = W - p.w;
}
function collectStars() {
for (var si = 0; si < stars.length; si++) {
var s = stars[si];
if (!s.collected && p.x + p.w > s.x && p.x < s.x + 14 && p.y + p.h > s.y && p.y < s.y + 14) {
s.collected = true;
score += 50;
starsGot++;
updateUI();
addParticles(s.x + 7, s.y + 7, '#ffd700', 10);
playSound('collect');
gamepixHappyMoment();
}
}
}
function updateEnemies() {
for (var ei = 0; ei < enemies.length; ei++) {
var e = enemies[ei];
e.x += e.speed * e.dir;
if (e.x < 40 || e.x + e.w > W - 40) e.dir *= -1;
if (p.x + p.w > e.x && p.x < e.x + e.w && p.y + p.h > e.y && p.y < e.y + e.h && running) {
if (p.vy > 0 && p.y + p.h - p.vy <= e.y + 10) {
enemies.splice(ei, 1);
ei--;
p.vy = JUMP * 0.6;
score += 100;
updateUI();
addParticles(e.x + e.w / 2, e.y + e.h / 2, '#ff6666', 15);
playSound('collect');
gamepixHappyMoment();
} else {
hitPlayer();
}
}
}
}
function hitPlayer() {
lives--;
updateUI();
addParticles(p.x + p.w / 2, p.y + p.h / 2, '#ff0000', 20);
playSound('hit');
if (lives <= 0) {
running = false;
isPaused = true;
save();
showGameOver();
} else {
p.x = 80;
p.y = GROUND - p.h;
p.vx = 0;
p.vy = 0;
}
}
function completeLevel() {
if (levelDone) return;
levelDone = true;
running = false;
isPaused = true;
var bonus = starsGot * 50 + 200;
score += bonus;
if (completed.indexOf(level) === -1) completed.push(level);
if (level === maxUnlocked && level < 100) maxUnlocked = level + 1;
if (score > highScore) highScore = score;
save();
playSound('levelComplete');
gamepixHappyMoment();
var msg = '+' + bonus + ' pts ⭐ ' + starsGot + '/' + stars.length;
showLevelComplete(msg);
}
// ========== SON ==========
var actx = null;
function playSound(type) {
try {
if (!actx) actx = new(window.AudioContext || window.webkitAudioContext)();
var osc = actx.createOscillator();
var gain = actx.createGain();
osc.connect(gain);
gain.connect(actx.destination);
var f = 440,
d = 0.2;
switch (type) {
case 'jump':
f = 523.25;
d = 0.12;
break;
case 'collect':
f = 659.25;
d = 0.1;
break;
case 'hit':
f = 220;
d = 0.3;
break;
case 'levelComplete':
f = 783.99;
d = 0.4;
break;
case 'levelStart':
f = 392;
d = 0.2;
break;
}
osc.frequency.value = f;
gain.gain.value = 0.2;
osc.start();
gain.gain.exponentialRampToValueAtTime(0.00001, actx.currentTime + d);
osc.stop(actx.currentTime + d);
} catch(e) {}
}
// ========== PARTICULES ==========
function addParticles(x, y, color, count) {
if (!count) count = 8;
for (var i = 0; i < count; i++) {
particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 3 - 1,
life: 0.7,
color: color,
size: Math.random() * 3 + 1.5
});
}
}
function updateParticles() {
for (var i = 0; i < particles.length; i++) {
var pt = particles[i];
pt.x += pt.vx;
pt.y += pt.vy;
pt.life -= 0.02;
if (pt.life <= 0) {
particles.splice(i, 1);
i--;
}
}
}
// ========== DESSIN ==========
function drawBg() {
var g = ctx.createLinearGradient(0, 0, 0, H);
g.addColorStop(0, '#0a0f2a');
g.addColorStop(1, '#1a1f3a');
ctx.fillStyle = g;
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = 'rgba(255,255,200,0.2)';
for (var i = 0; i < 80; i++) {
ctx.beginPath();
ctx.arc((i * 131) % W, (i * 57) % H, 1.2, 0, Math.PI * 2);
ctx.fill();
}
}
function drawPlatforms() {
for (var pi = 0; pi < platforms.length; pi++) {
var plat = platforms[pi];
ctx.shadowBlur = 3;
if (plat.type === 'spike') {
ctx.fillStyle = '#aa4444';
ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
for (var 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 (var j = 0; j < 4; j++) ctx.fillRect(plat.x + 8 + j * 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 {
var g = ctx.createLinearGradient(plat.x, plat.y, plat.x, plat.y + plat.h);
g.addColorStop(0, '#5a7a5a');
g.addColorStop(1, '#3a5a3a');
ctx.fillStyle = g;
ctx.fillRect(plat.x, plat.y, plat.w, plat.h);
ctx.fillStyle = '#7a9a7a';
ctx.fillRect(plat.x, plat.y, plat.w, 4);
}
}
for (var mi = 0; mi < moving.length; mi++) {
var m = moving[mi];
ctx.fillStyle = '#88aaff';
ctx.fillRect(m.x, m.y, m.w, m.h);
ctx.fillStyle = '#aaccff';
ctx.fillRect(m.x, m.y, m.w, 4);
}
for (var ti = 0; ti < tele.length; ti++) {
var t = tele[ti];
ctx.fillStyle = '#44aaff';
ctx.globalAlpha = 0.7;
ctx.fillRect(t.x, t.y, t.w, t.h);
ctx.fillStyle = '#fff';
ctx.font = '18px monospace';
ctx.fillText('🌀', t.x + 8, t.y + 22);
ctx.globalAlpha = 1;
}
ctx.shadowBlur = 0;
}
function drawStars() {
for (var si = 0; si < stars.length; si++) {
var s = stars[si];
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 (var ei = 0; ei < enemies.length; ei++) {
var e = enemies[ei];
ctx.fillStyle = e.type === 'fire' ? '#ff6633' : '#8844cc';
ctx.shadowBlur = 4;
ctx.fillRect(e.x, e.y, e.w, e.h);
ctx.fillStyle = '#fff';
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 = 15;
ctx.shadowColor = '#ff6666';
var g = ctx.createRadialGradient(p.x + 7, p.y + 7, 3, p.x + 10, p.y + 10, 14);
g.addColorStop(0, '#ff7e7e');
g.addColorStop(1, '#cc2222');
ctx.fillStyle = g;
ctx.beginPath();
ctx.arc(p.x + p.w / 2, p.y + p.h / 2, p.w / 2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.beginPath();
ctx.ellipse(p.x + 6, p.y + 6, 4, 2, -0.3, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(p.x + (p.facingRight ? 13 : 7), p.y + 7, 3.5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#222';
ctx.beginPath();
ctx.arc(p.x + (p.facingRight ? 13 : 7), p.y + 6, 2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
function drawParticles() {
for (var pi = 0; pi < particles.length; pi++) {
var pt = particles[pi];
ctx.globalAlpha = pt.life;
ctx.fillStyle = pt.color;
ctx.fillRect(pt.x, pt.y, pt.size, pt.size);
}
ctx.globalAlpha = 1;
}
function draw() {
drawBg();
drawPlatforms();
drawStars();
drawEnemies();
drawPlayer();
drawParticles();
ctx.font = 'bold 12px monospace';
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillText('Niveau ' + level + '/100', 12, 28);
}
// ========== BOUCLE ==========
function update() {
if (!running || isPaused) return;
applyPhysics();
collectStars();
updateEnemies();
updateParticles();
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
// ========== CONTROLES ==========
function keyDown(e) {
var k = e.key;
if (k === 'ArrowLeft' || k === 'q' || k === 'Q' || k === 'a' || k === 'A') { left = true;
e.preventDefault(); }
if (k === 'ArrowRight' || k === 'd' || k === 'D') { right = true;
e.preventDefault(); }
if (k === 'ArrowUp' || k === ' ' || k === 'w' || k === 'W' || k === 'z' || k === 'Z') { jump = true;
e.preventDefault(); }
if (k === 'r' || k === 'R') { startGame();
e.preventDefault(); }
}
function keyUp(e) {
var k = e.key;
if (k === 'ArrowLeft' || k === 'q' || k === 'Q' || k === 'a' || k === 'A') { left = false;
e.preventDefault(); }
if (k === 'ArrowRight' || k === 'd' || k === 'D') { right = false;
e.preventDefault(); }
}
// ========== INIT ==========
function init() {
load();
goMenu();
window.addEventListener('keydown', keyDown);
window.addEventListener('keyup', keyUp);
loop();
}
init();
})();
</script>
</body>
</html>Game Source: Bounce 100
Creator: NovaWizard65
Libraries: none
Complexity: complex (1235 lines, 43.6 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-novawizard65-mqjsti6v" to link back to the original. Then publish at arcadelab.ai/publish.