ajnet31up / index.html
PAUTREL Johan
Add 2 files
6d418c5 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJNET 31 - Jeu de Nettoyage Expert</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.stain {
animation: fadeIn 0.3s ease-in;
transition: transform 0.2s;
cursor: pointer;
position: absolute;
}
.stain:hover {
transform: scale(0.95);
}
.moving-stain {
animation: moveRandom 4s linear infinite;
}
@keyframes moveRandom {
0% {
transform: translate(0, 0);
left: var(--start-x);
top: var(--start-y);
}
25% {
transform: translate(calc(var(--move-x1) * 1px), calc(var(--move-y1) * 1px));
}
50% {
transform: translate(calc(var(--move-x2) * 1px), calc(var(--move-y2) * 1px));
}
75% {
transform: translate(calc(var(--move-x3) * 1px), calc(var(--move-y3) * 1px));
}
100% {
transform: translate(0, 0);
}
}
.game-container {
background-image: linear-gradient(to bottom, #f0f9ff, #e0f2fe);
position: relative;
overflow: hidden;
height: calc(100vh - 120px); /* Ajustement pour mieux utiliser l'espace */
}
.progress-bar {
transition: width 0.3s ease-out;
}
.modal {
animation: fadeIn 0.3s ease-out;
}
.btn-pulse {
animation: pulse 1.5s infinite;
}
.shake {
animation: shake 0.5s;
}
.combo-effect {
position: absolute;
font-size: 1.5rem;
font-weight: bold;
color: #3b82f6;
opacity: 0;
animation: fadeIn 0.5s forwards, float 1s forwards;
}
.penalty-effect {
position: absolute;
font-size: 1.5rem;
font-weight: bold;
color: #ef4444;
opacity: 0;
animation: fadeIn 0.5s forwards, shake 0.5s forwards;
}
.rules-modal {
max-height: 80vh;
overflow-y: auto;
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-blue-600 text-white py-4 shadow-lg">
<div class="container mx-auto px-4 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-broom text-2xl"></i>
<h1 class="text-2xl font-bold">AJNET 31</h1>
</div>
<div class="flex items-center space-x-4">
<button id="rules-btn" class="bg-blue-800 px-3 py-1 rounded-full font-bold text-sm hover:bg-blue-700 transition">
<i class="fas fa-info-circle mr-1"></i>Règles
</button>
<div id="combo-counter" class="hidden bg-blue-800 px-3 py-1 rounded-full font-bold text-sm">
Combo x<span id="combo">0</span>
</div>
<div id="score-display" class="bg-blue-800 px-4 py-2 rounded-full font-bold">
Score: <span id="score">0</span>
</div>
</div>
</div>
</header>
<!-- Main Game Area -->
<main class="flex-grow game-container relative overflow-hidden">
<div class="absolute top-4 left-4 bg-blue-100 bg-opacity-80 p-2 rounded-lg shadow">
<div class="flex items-center space-x-2">
<i class="fas fa-clock text-blue-600"></i>
<span id="time">60</span>s
</div>
<div class="mt-2 w-full bg-gray-200 rounded-full h-2.5">
<div id="time-bar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 100%"></div>
</div>
</div>
<div class="absolute top-4 right-4 bg-blue-100 bg-opacity-80 p-2 rounded-lg shadow">
<div class="flex items-center space-x-2">
<i class="fas fa-tachometer-alt text-blue-600"></i>
Niveau: <span id="level">1</span>
</div>
</div>
<div id="game-area" class="w-full h-full relative"></div>
<!-- Start Screen -->
<div id="start-screen" class="absolute inset-0 bg-blue-600 bg-opacity-90 flex flex-col items-center justify-center text-white">
<div class="text-center max-w-md px-6">
<i class="fas fa-broom text-6xl mb-6"></i>
<h2 class="text-4xl font-bold mb-4">AJNET 31</h2>
<p class="text-xl mb-6">Mode Expert: Taches mobiles, combo et pénalités!</p>
<div class="bg-blue-800 p-4 rounded-lg mb-6 text-left">
<p class="mb-2"><i class="fas fa-running mr-2"></i> Taches mobiles apparaissent</p>
<p class="mb-2"><i class="fas fa-bolt mr-2"></i> Combo pour bonus de points</p>
<p class="mb-2"><i class="fas fa-skull mr-2"></i> Taches spéciales pénalisantes</p>
</div>
<button id="start-btn" class="btn-pulse bg-white text-blue-600 font-bold py-3 px-8 rounded-full text-lg hover:bg-blue-100 transition">
DÉFI EXPERT
</button>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over" class="modal absolute inset-0 bg-blue-600 bg-opacity-90 hidden flex-col items-center justify-center text-white">
<div class="text-center max-w-md px-6">
<i class="fas fa-award text-6xl mb-6"></i>
<h2 class="text-4xl font-bold mb-4">Performance</h2>
<div class="bg-blue-800 p-6 rounded-xl mb-6">
<p class="text-5xl font-bold mb-2" id="final-score">0</p>
<p class="text-lg">points</p>
<p class="mt-4"><i class="fas fa-trophy mr-2"></i>Niveau atteint: <span id="final-level">1</span></p>
<p><i class="fas fa-bolt mr-2"></i>Combo max: x<span id="final-combo">0</span></p>
</div>
<div class="flex space-x-4">
<button id="restart-btn" class="bg-white text-blue-600 font-bold py-3 px-6 rounded-full hover:bg-blue-100 transition">
<i class="fas fa-redo mr-2"></i>Rejouer
</button>
<button id="share-btn" class="bg-blue-800 text-white font-bold py-3 px-6 rounded-full hover:bg-blue-700 transition">
<i class="fas fa-share-alt mr-2"></i>Partager
</button>
</div>
</div>
</div>
<!-- Rules Modal -->
<div id="rules-modal" class="modal absolute inset-0 bg-blue-600 bg-opacity-90 hidden flex-col items-center justify-center text-white">
<div class="rules-modal bg-blue-800 p-6 rounded-xl max-w-md mx-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-2xl font-bold"><i class="fas fa-book mr-2"></i>Règles du Jeu</h2>
<button id="close-rules-btn" class="text-white hover:text-blue-200 text-2xl">
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-4">
<div class="bg-blue-700 p-4 rounded-lg">
<h3 class="font-bold text-lg mb-2"><i class="fas fa-broom mr-2"></i>Objectif</h3>
<p>Nettoyer le maximum de taches en 60 secondes pour marquer des points.</p>
</div>
<div class="bg-blue-700 p-4 rounded-lg">
<h3 class="font-bold text-lg mb-2"><i class="fas fa-star mr-2"></i>Points</h3>
<ul class="list-disc pl-5 space-y-1">
<li>Taches normales: 1-3 points selon la taille</li>
<li>Taches mobiles: 2-4 points (plus difficiles à attraper)</li>
<li>Bonus de combo: +1 point par niveau de combo</li>
</ul>
</div>
<div class="bg-blue-700 p-4 rounded-lg">
<h3 class="font-bold text-lg mb-2"><i class="fas fa-skull mr-2"></i>Pénalités</h3>
<ul class="list-disc pl-5 space-y-1">
<li>Taches noires: -3 points</li>
<li>Taches rouges: -5 points</li>
</ul>
</div>
<div class="bg-blue-700 p-4 rounded-lg">
<h3 class="font-bold text-lg mb-2"><i class="fas fa-bolt mr-2"></i>Combo</h3>
<p>Nettoyer plusieurs taches rapidement augmente votre combo (max x10). Le combo se réinitialise après 1 seconde sans nettoyer de tache.</p>
</div>
<div class="bg-blue-700 p-4 rounded-lg">
<h3 class="font-bold text-lg mb-2"><i class="fas fa-level-up-alt mr-2"></i>Niveaux</h3>
<p>Nettoyer 10 taches vous fait passer au niveau suivant. La difficulté augmente avec plus de taches mobiles et pénalisantes.</p>
</div>
</div>
<div class="mt-6 text-center">
<button id="start-from-rules-btn" class="btn-pulse bg-white text-blue-600 font-bold py-3 px-8 rounded-full text-lg hover:bg-blue-100 transition">
COMMENCER
</button>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-blue-800 text-white py-3 text-center text-sm">
<p>© 2008 AJNET 31 - Défi de nettoyage expert</p>
</footer>
</div>
<script>
// Game variables
let score = 0;
let timeLeft = 60;
let level = 1;
let gameInterval;
let stainInterval;
let timeInterval;
let gameActive = false;
let stainsCleaned = 0;
let stainSpeed = 800;
let lastCleanTime = 0;
let combo = 0;
let maxCombo = 0;
let comboTimeout;
let movingStainChance = 0.5;
let penaltyStainChance = 0.25;
let stainCount = 0;
let penaltyStainCount = 0;
// DOM elements
const gameArea = document.getElementById('game-area');
const scoreDisplay = document.getElementById('score');
const timeDisplay = document.getElementById('time');
const timeBar = document.getElementById('time-bar');
const levelDisplay = document.getElementById('level');
const startScreen = document.getElementById('start-screen');
const gameOverScreen = document.getElementById('game-over');
const finalScoreDisplay = document.getElementById('final-score');
const finalLevelDisplay = document.getElementById('final-level');
const finalComboDisplay = document.getElementById('final-combo');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
const shareBtn = document.getElementById('share-btn');
const comboCounter = document.getElementById('combo-counter');
const comboDisplay = document.getElementById('combo');
const rulesBtn = document.getElementById('rules-btn');
const rulesModal = document.getElementById('rules-modal');
const closeRulesBtn = document.getElementById('close-rules-btn');
const startFromRulesBtn = document.getElementById('start-from-rules-btn');
// Stain types with different colors, sizes and behaviors
const stainTypes = [
{ class: 'bg-red-500', size: 'w-16 h-16', points: 1, moving: false, penalty: false },
{ class: 'bg-yellow-500', size: 'w-20 h-20', points: 2, moving: false, penalty: false },
{ class: 'bg-green-500', size: 'w-24 h-24', points: 3, moving: false, penalty: false },
{ class: 'bg-purple-500', size: 'w-14 h-14', points: 1, moving: false, penalty: false },
{ class: 'bg-pink-500', size: 'w-18 h-18', points: 2, moving: false, penalty: false },
{ class: 'bg-blue-500', size: 'w-22 h-22', points: 3, moving: false, penalty: false },
{ class: 'bg-red-600', size: 'w-20 h-20', points: -5, moving: true, penalty: true },
{ class: 'bg-gray-800', size: 'w-24 h-24', points: -3, moving: false, penalty: true }
];
// Initialize game
function initGame() {
score = 0;
timeLeft = 60;
level = 1;
stainsCleaned = 0;
stainSpeed = 800;
combo = 0;
maxCombo = 0;
stainCount = 0;
penaltyStainCount = 0;
updateScore();
updateTime();
updateLevel();
hideCombo();
gameArea.innerHTML = '';
}
// Start game
function startGame() {
initGame();
gameActive = true;
startScreen.classList.add('hidden');
gameOverScreen.classList.add('hidden');
rulesModal.classList.add('hidden');
// Start timers
timeInterval = setInterval(updateTimer, 1000);
stainInterval = setInterval(createStain, stainSpeed);
// Check game state every 500ms
gameInterval = setInterval(checkGameState, 500);
}
// Create a new stain with full screen movement
function createStain() {
if (!gameActive) return;
const gameAreaRect = gameArea.getBoundingClientRect();
const maxX = gameAreaRect.width - 100;
const maxY = gameAreaRect.height - 100;
// Position aléatoire mieux répartie sur toute la hauteur
const startX = Math.random() * maxX;
const startY = Math.random() * (maxY - 100) + 50; // +50 pour éviter le haut de l'écran
// Determine stain type
let stainTypeIndex;
if (penaltyStainCount >= Math.ceil(stainCount / 3) && Math.random() < penaltyStainChance) {
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
} else {
if (Math.random() < movingStainChance) {
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
}
if (Math.random() < penaltyStainChance) {
stainTypeIndex = Math.floor(Math.random() * 2) + (stainTypes.length - 2);
} else {
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
}
}
const stainType = stainTypes[stainTypeIndex];
const stain = document.createElement('div');
stain.className = `stain rounded-full ${stainType.class} ${stainType.size}`;
stain.style.setProperty('--start-x', `${startX}px`);
stain.style.setProperty('--start-y', `${startY}px`);
// Définir des mouvements aléatoires plus grands
const moveX1 = (Math.random() * maxX - startX) * 0.5;
const moveY1 = (Math.random() * (maxY - 100) - startY) * 0.5;
const moveX2 = (Math.random() * maxX - startX) * 0.5;
const moveY2 = (Math.random() * (maxY - 100) - startY) * 0.5;
const moveX3 = (Math.random() * maxX - startX) * 0.5;
const moveY3 = (Math.random() * (maxY - 100) - startY) * 0.5;
stain.style.setProperty('--move-x1', moveX1);
stain.style.setProperty('--move-y1', moveY1);
stain.style.setProperty('--move-x2', moveX2);
stain.style.setProperty('--move-y2', moveY2);
stain.style.setProperty('--move-x3', moveX3);
stain.style.setProperty('--move-y3', moveY3);
// Add moving class if needed (now most stains will move)
if (stainType.moving || Math.random() < movingStainChance) {
stain.classList.add('moving-stain');
// Make movement faster for moving stains
stain.style.animationDuration = `${3 + Math.random() * 2}s`;
}
// Add data attributes
stain.dataset.points = stainType.points;
stain.dataset.penalty = stainType.penalty;
// Position initiale
stain.style.left = `${startX}px`;
stain.style.top = `${startY}px`;
// Add click event
stain.addEventListener('click', cleanStain);
gameArea.appendChild(stain);
stainCount++;
if (stainType.penalty) {
penaltyStainCount++;
}
}
// Clean stain
function cleanStain(e) {
if (!gameActive) return;
const stain = e.target;
const points = parseInt(stain.dataset.points);
const isPenalty = stain.dataset.penalty === 'true';
stain.remove();
stainCount--;
if (isPenalty) {
penaltyStainCount = Math.max(0, penaltyStainCount - 1);
}
const now = Date.now();
if (now - lastCleanTime < 1000) {
combo++;
if (combo > maxCombo) maxCombo = combo;
showCombo();
clearTimeout(comboTimeout);
comboTimeout = setTimeout(resetCombo, 1000);
} else {
resetCombo();
}
lastCleanTime = now;
if (isPenalty) {
score += points;
showPenaltyEffect(stain, points);
} else {
const bonus = combo > 0 ? Math.floor(combo / 2) : 0;
score += points + bonus;
stainsCleaned++;
showComboEffect(stain, points + bonus);
}
updateScore();
if (stainsCleaned >= level * 10) {
levelUp();
}
}
// Show combo effect
function showComboEffect(element, points) {
const rect = element.getBoundingClientRect();
const gameAreaRect = gameArea.getBoundingClientRect();
const effect = document.createElement('div');
effect.className = 'combo-effect';
effect.textContent = `+${points}`;
effect.style.left = `${rect.left - gameAreaRect.left}px`;
effect.style.top = `${rect.top - gameAreaRect.top}px`;
gameArea.appendChild(effect);
setTimeout(() => {
effect.remove();
}, 1500);
}
// Show penalty effect
function showPenaltyEffect(element, points) {
const rect = element.getBoundingClientRect();
const gameAreaRect = gameArea.getBoundingClientRect();
const effect = document.createElement('div');
effect.className = 'penalty-effect';
effect.textContent = `${points}`;
effect.style.left = `${rect.left - gameAreaRect.left}px`;
effect.style.top = `${rect.top - gameAreaRect.top}px`;
gameArea.appendChild(effect);
setTimeout(() => {
effect.remove();
}, 1500);
}
// Show combo counter
function showCombo() {
comboCounter.classList.remove('hidden');
comboDisplay.textContent = combo;
}
// Hide combo counter
function hideCombo() {
comboCounter.classList.add('hidden');
}
// Reset combo
function resetCombo() {
combo = 0;
hideCombo();
}
// Level up
function levelUp() {
level++;
stainsCleaned = 0;
stainSpeed = Math.max(stainSpeed - 100, 300);
movingStainChance = Math.min(movingStainChance + 0.05, 0.8);
penaltyStainChance = Math.min(penaltyStainChance + 0.02, 0.4);
updateLevel();
const levelUpEffect = document.createElement('div');
levelUpEffect.className = 'absolute inset-0 flex items-center justify-center';
levelUpEffect.innerHTML = `
<div class="bg-blue-600 bg-opacity-80 text-white text-4xl font-bold p-8 rounded-xl animate-pulse">
Niveau ${level}!
</div>
`;
gameArea.appendChild(levelUpEffect);
setTimeout(() => {
levelUpEffect.remove();
}, 1500);
}
// Update timer
function updateTimer() {
timeLeft--;
updateTime();
if (timeLeft <= 0) {
endGame();
}
}
// Update time display
function updateTime() {
timeDisplay.textContent = timeLeft;
timeBar.style.width = `${(timeLeft / 60) * 100}%`;
if (timeLeft <= 10) {
timeBar.classList.remove('bg-blue-600');
timeBar.classList.add('bg-red-500');
timeDisplay.classList.add('text-red-500');
} else {
timeBar.classList.remove('bg-red-500');
timeBar.classList.add('bg-blue-600');
timeDisplay.classList.remove('text-red-500');
}
}
// Update score display
function updateScore() {
scoreDisplay.textContent = score;
}
// Update level display
function updateLevel() {
levelDisplay.textContent = level;
}
// Check game state
function checkGameState() {
if (timeLeft <= 0) {
endGame();
}
}
// End game
function endGame() {
gameActive = false;
clearInterval(timeInterval);
clearInterval(stainInterval);
clearInterval(gameInterval);
clearTimeout(comboTimeout);
finalScoreDisplay.textContent = score;
finalLevelDisplay.textContent = level;
finalComboDisplay.textContent = maxCombo;
gameOverScreen.classList.remove('hidden');
}
// Event listeners
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
shareBtn.addEventListener('click', () => {
alert(`Partagez votre score de ${score} points (Niveau ${level}) avec vos amis!`);
});
rulesBtn.addEventListener('click', () => {
rulesModal.classList.remove('hidden');
});
closeRulesBtn.addEventListener('click', () => {
rulesModal.classList.add('hidden');
});
startFromRulesBtn.addEventListener('click', startGame);
// Initialize game on load
initGame();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=johanpautrel/ajnet31up" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>