ajnet-jeu / index.html
PAUTREL Johan
Add 1 files
802650d verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJNET - 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); }
}
@keyframes moveRandom {
0% { transform: translate(0, 0); }
25% { transform: translate(50px, 30px); }
50% { transform: translate(20px, -20px); }
75% { transform: translate(-30px, 10px); }
100% { transform: translate(0, 0); }
}
.stain {
animation: fadeIn 0.3s ease-in;
transition: transform 0.2s;
cursor: pointer;
}
.stain:hover {
transform: scale(0.95);
}
.moving-stain {
animation: moveRandom 4s linear infinite;
}
.game-container {
background-image: linear-gradient(to bottom, #f0f9ff, #e0f2fe);
}
.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 PRO</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 PRO</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>© 2023 AJNET PRO - 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 maxStains = 12;
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; // Track penalty stains
// 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 }, // Penalty stain
{ class: 'bg-gray-800', size: 'w-24 h-24', points: -3, moving: false, penalty: true } // Another penalty stain
];
// Initialize game
function initGame() {
score = 0;
timeLeft = 60;
level = 1;
stainsCleaned = 0;
maxStains = 12;
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 (now more centered and lower on screen)
function createStain() {
if (!gameActive || stainCount >= maxStains) return;
const gameAreaRect = gameArea.getBoundingClientRect();
const centerX = gameAreaRect.width / 2;
const centerY = gameAreaRect.height * 0.6; // Lower center point (60% from top)
// Create stains more towards the center (within 60% of screen centered)
const areaWidth = gameAreaRect.width * 0.6;
const areaHeight = gameAreaRect.height * 0.4; // Smaller height area to keep stains lower
const x = centerX - (areaWidth / 2) + Math.random() * areaWidth;
const y = centerY - (areaHeight / 2) + Math.random() * areaHeight;
// Determine if this should be a moving or penalty stain
let stainTypeIndex;
// Prevent too many penalty stains (max 1 penalty stain for every 3 normal stains)
if (penaltyStainCount >= Math.ceil(stainCount / 3) && Math.random() < penaltyStainChance) {
// If we already have enough penalty stains, force a normal stain
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
} else {
// Chance for moving stain
if (Math.random() < movingStainChance) {
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2)); // Random normal stain but will be moving
}
// Chance for penalty stain (more rare)
if (Math.random() < penaltyStainChance) {
stainTypeIndex = Math.floor(Math.random() * 2) + (stainTypes.length - 2); // Last two are penalty stains
} else {
stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
}
}
const stainType = stainTypes[stainTypeIndex];
const stain = document.createElement('div');
stain.className = `stain rounded-full absolute ${stainType.class} ${stainType.size}`;
stain.style.left = `${x}px`;
stain.style.top = `${y}px`;
// Add moving class if needed
if (stainType.moving || Math.random() < movingStainChance) {
stain.classList.add('moving-stain');
}
// Add data attributes
stain.dataset.points = stainType.points;
stain.dataset.penalty = stainType.penalty;
// Add click event
stain.addEventListener('click', cleanStain);
gameArea.appendChild(stain);
stainCount++;
// Track penalty stains
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';
// Remove stain
stain.remove();
stainCount--;
// Update penalty stain count
if (isPenalty) {
penaltyStainCount = Math.max(0, penaltyStainCount - 1);
}
// Update combo
const now = Date.now();
if (now - lastCleanTime < 1000) { // Combo if cleaned within 1 second
combo++;
if (combo > maxCombo) maxCombo = combo;
// Show combo effect
showCombo();
// Reset combo timeout
clearTimeout(comboTimeout);
comboTimeout = setTimeout(resetCombo, 1000);
} else {
resetCombo();
}
lastCleanTime = now;
// Update score
if (isPenalty) {
score += points; // points are negative for penalty
showPenaltyEffect(stain, points);
} else {
// Bonus points for combo
const bonus = combo > 0 ? Math.floor(combo / 2) : 0;
score += points + bonus;
stainsCleaned++;
showComboEffect(stain, points + bonus);
}
updateScore();
// Check level up
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);
// Remove after animation
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);
// Remove after animation
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;
// Increase difficulty
maxStains = Math.min(maxStains + 2, 20); // Cap at 20 stains
stainSpeed = Math.max(stainSpeed - 100, 400); // Even faster spawn
movingStainChance = Math.min(movingStainChance + 0.05, 0.7); // More moving stains
penaltyStainChance = Math.min(penaltyStainChance + 0.02, 0.35); // More penalty stains
// Update display
updateLevel();
// Show level up effect
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}%`;
// Change color when time is running out
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() {
// Check if time is up
if (timeLeft <= 0) {
endGame();
}
}
// End game
function endGame() {
gameActive = false;
// Clear intervals
clearInterval(timeInterval);
clearInterval(stainInterval);
clearInterval(gameInterval);
clearTimeout(comboTimeout);
// Show game over screen
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!`);
});
// Rules modal
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/ajnet-jeu" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>