PAUTREL Johan commited on
Commit
6d418c5
·
verified ·
1 Parent(s): 6bd4e2f

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +619 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Ajnet31up
3
- emoji: 💻
4
- colorFrom: blue
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: ajnet31up
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,619 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AJNET 31 - Jeu de Nettoyage Expert</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @keyframes fadeIn {
11
+ from { opacity: 0; }
12
+ to { opacity: 1; }
13
+ }
14
+
15
+ @keyframes pulse {
16
+ 0% { transform: scale(1); }
17
+ 50% { transform: scale(1.05); }
18
+ 100% { transform: scale(1); }
19
+ }
20
+
21
+ @keyframes shake {
22
+ 0%, 100% { transform: translateX(0); }
23
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
24
+ 20%, 40%, 60%, 80% { transform: translateX(5px); }
25
+ }
26
+
27
+ @keyframes float {
28
+ 0%, 100% { transform: translateY(0); }
29
+ 50% { transform: translateY(-10px); }
30
+ }
31
+
32
+ .stain {
33
+ animation: fadeIn 0.3s ease-in;
34
+ transition: transform 0.2s;
35
+ cursor: pointer;
36
+ position: absolute;
37
+ }
38
+
39
+ .stain:hover {
40
+ transform: scale(0.95);
41
+ }
42
+
43
+ .moving-stain {
44
+ animation: moveRandom 4s linear infinite;
45
+ }
46
+
47
+ @keyframes moveRandom {
48
+ 0% {
49
+ transform: translate(0, 0);
50
+ left: var(--start-x);
51
+ top: var(--start-y);
52
+ }
53
+ 25% {
54
+ transform: translate(calc(var(--move-x1) * 1px), calc(var(--move-y1) * 1px));
55
+ }
56
+ 50% {
57
+ transform: translate(calc(var(--move-x2) * 1px), calc(var(--move-y2) * 1px));
58
+ }
59
+ 75% {
60
+ transform: translate(calc(var(--move-x3) * 1px), calc(var(--move-y3) * 1px));
61
+ }
62
+ 100% {
63
+ transform: translate(0, 0);
64
+ }
65
+ }
66
+
67
+ .game-container {
68
+ background-image: linear-gradient(to bottom, #f0f9ff, #e0f2fe);
69
+ position: relative;
70
+ overflow: hidden;
71
+ height: calc(100vh - 120px); /* Ajustement pour mieux utiliser l'espace */
72
+ }
73
+
74
+ .progress-bar {
75
+ transition: width 0.3s ease-out;
76
+ }
77
+
78
+ .modal {
79
+ animation: fadeIn 0.3s ease-out;
80
+ }
81
+
82
+ .btn-pulse {
83
+ animation: pulse 1.5s infinite;
84
+ }
85
+
86
+ .shake {
87
+ animation: shake 0.5s;
88
+ }
89
+
90
+ .combo-effect {
91
+ position: absolute;
92
+ font-size: 1.5rem;
93
+ font-weight: bold;
94
+ color: #3b82f6;
95
+ opacity: 0;
96
+ animation: fadeIn 0.5s forwards, float 1s forwards;
97
+ }
98
+
99
+ .penalty-effect {
100
+ position: absolute;
101
+ font-size: 1.5rem;
102
+ font-weight: bold;
103
+ color: #ef4444;
104
+ opacity: 0;
105
+ animation: fadeIn 0.5s forwards, shake 0.5s forwards;
106
+ }
107
+
108
+ .rules-modal {
109
+ max-height: 80vh;
110
+ overflow-y: auto;
111
+ }
112
+ </style>
113
+ </head>
114
+ <body class="bg-gray-100 font-sans">
115
+ <div class="min-h-screen flex flex-col">
116
+ <!-- Header -->
117
+ <header class="bg-blue-600 text-white py-4 shadow-lg">
118
+ <div class="container mx-auto px-4 flex justify-between items-center">
119
+ <div class="flex items-center space-x-2">
120
+ <i class="fas fa-broom text-2xl"></i>
121
+ <h1 class="text-2xl font-bold">AJNET 31</h1>
122
+ </div>
123
+ <div class="flex items-center space-x-4">
124
+ <button id="rules-btn" class="bg-blue-800 px-3 py-1 rounded-full font-bold text-sm hover:bg-blue-700 transition">
125
+ <i class="fas fa-info-circle mr-1"></i>Règles
126
+ </button>
127
+ <div id="combo-counter" class="hidden bg-blue-800 px-3 py-1 rounded-full font-bold text-sm">
128
+ Combo x<span id="combo">0</span>
129
+ </div>
130
+ <div id="score-display" class="bg-blue-800 px-4 py-2 rounded-full font-bold">
131
+ Score: <span id="score">0</span>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </header>
136
+
137
+ <!-- Main Game Area -->
138
+ <main class="flex-grow game-container relative overflow-hidden">
139
+ <div class="absolute top-4 left-4 bg-blue-100 bg-opacity-80 p-2 rounded-lg shadow">
140
+ <div class="flex items-center space-x-2">
141
+ <i class="fas fa-clock text-blue-600"></i>
142
+ <span id="time">60</span>s
143
+ </div>
144
+ <div class="mt-2 w-full bg-gray-200 rounded-full h-2.5">
145
+ <div id="time-bar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 100%"></div>
146
+ </div>
147
+ </div>
148
+
149
+ <div class="absolute top-4 right-4 bg-blue-100 bg-opacity-80 p-2 rounded-lg shadow">
150
+ <div class="flex items-center space-x-2">
151
+ <i class="fas fa-tachometer-alt text-blue-600"></i>
152
+ Niveau: <span id="level">1</span>
153
+ </div>
154
+ </div>
155
+
156
+ <div id="game-area" class="w-full h-full relative"></div>
157
+
158
+ <!-- Start Screen -->
159
+ <div id="start-screen" class="absolute inset-0 bg-blue-600 bg-opacity-90 flex flex-col items-center justify-center text-white">
160
+ <div class="text-center max-w-md px-6">
161
+ <i class="fas fa-broom text-6xl mb-6"></i>
162
+ <h2 class="text-4xl font-bold mb-4">AJNET 31</h2>
163
+ <p class="text-xl mb-6">Mode Expert: Taches mobiles, combo et pénalités!</p>
164
+ <div class="bg-blue-800 p-4 rounded-lg mb-6 text-left">
165
+ <p class="mb-2"><i class="fas fa-running mr-2"></i> Taches mobiles apparaissent</p>
166
+ <p class="mb-2"><i class="fas fa-bolt mr-2"></i> Combo pour bonus de points</p>
167
+ <p class="mb-2"><i class="fas fa-skull mr-2"></i> Taches spéciales pénalisantes</p>
168
+ </div>
169
+ <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">
170
+ DÉFI EXPERT
171
+ </button>
172
+ </div>
173
+ </div>
174
+
175
+ <!-- Game Over Screen -->
176
+ <div id="game-over" class="modal absolute inset-0 bg-blue-600 bg-opacity-90 hidden flex-col items-center justify-center text-white">
177
+ <div class="text-center max-w-md px-6">
178
+ <i class="fas fa-award text-6xl mb-6"></i>
179
+ <h2 class="text-4xl font-bold mb-4">Performance</h2>
180
+ <div class="bg-blue-800 p-6 rounded-xl mb-6">
181
+ <p class="text-5xl font-bold mb-2" id="final-score">0</p>
182
+ <p class="text-lg">points</p>
183
+ <p class="mt-4"><i class="fas fa-trophy mr-2"></i>Niveau atteint: <span id="final-level">1</span></p>
184
+ <p><i class="fas fa-bolt mr-2"></i>Combo max: x<span id="final-combo">0</span></p>
185
+ </div>
186
+ <div class="flex space-x-4">
187
+ <button id="restart-btn" class="bg-white text-blue-600 font-bold py-3 px-6 rounded-full hover:bg-blue-100 transition">
188
+ <i class="fas fa-redo mr-2"></i>Rejouer
189
+ </button>
190
+ <button id="share-btn" class="bg-blue-800 text-white font-bold py-3 px-6 rounded-full hover:bg-blue-700 transition">
191
+ <i class="fas fa-share-alt mr-2"></i>Partager
192
+ </button>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ <!-- Rules Modal -->
198
+ <div id="rules-modal" class="modal absolute inset-0 bg-blue-600 bg-opacity-90 hidden flex-col items-center justify-center text-white">
199
+ <div class="rules-modal bg-blue-800 p-6 rounded-xl max-w-md mx-4">
200
+ <div class="flex justify-between items-center mb-4">
201
+ <h2 class="text-2xl font-bold"><i class="fas fa-book mr-2"></i>Règles du Jeu</h2>
202
+ <button id="close-rules-btn" class="text-white hover:text-blue-200 text-2xl">
203
+ <i class="fas fa-times"></i>
204
+ </button>
205
+ </div>
206
+ <div class="space-y-4">
207
+ <div class="bg-blue-700 p-4 rounded-lg">
208
+ <h3 class="font-bold text-lg mb-2"><i class="fas fa-broom mr-2"></i>Objectif</h3>
209
+ <p>Nettoyer le maximum de taches en 60 secondes pour marquer des points.</p>
210
+ </div>
211
+ <div class="bg-blue-700 p-4 rounded-lg">
212
+ <h3 class="font-bold text-lg mb-2"><i class="fas fa-star mr-2"></i>Points</h3>
213
+ <ul class="list-disc pl-5 space-y-1">
214
+ <li>Taches normales: 1-3 points selon la taille</li>
215
+ <li>Taches mobiles: 2-4 points (plus difficiles à attraper)</li>
216
+ <li>Bonus de combo: +1 point par niveau de combo</li>
217
+ </ul>
218
+ </div>
219
+ <div class="bg-blue-700 p-4 rounded-lg">
220
+ <h3 class="font-bold text-lg mb-2"><i class="fas fa-skull mr-2"></i>Pénalités</h3>
221
+ <ul class="list-disc pl-5 space-y-1">
222
+ <li>Taches noires: -3 points</li>
223
+ <li>Taches rouges: -5 points</li>
224
+ </ul>
225
+ </div>
226
+ <div class="bg-blue-700 p-4 rounded-lg">
227
+ <h3 class="font-bold text-lg mb-2"><i class="fas fa-bolt mr-2"></i>Combo</h3>
228
+ <p>Nettoyer plusieurs taches rapidement augmente votre combo (max x10). Le combo se réinitialise après 1 seconde sans nettoyer de tache.</p>
229
+ </div>
230
+ <div class="bg-blue-700 p-4 rounded-lg">
231
+ <h3 class="font-bold text-lg mb-2"><i class="fas fa-level-up-alt mr-2"></i>Niveaux</h3>
232
+ <p>Nettoyer 10 taches vous fait passer au niveau suivant. La difficulté augmente avec plus de taches mobiles et pénalisantes.</p>
233
+ </div>
234
+ </div>
235
+ <div class="mt-6 text-center">
236
+ <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">
237
+ COMMENCER
238
+ </button>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </main>
243
+
244
+ <!-- Footer -->
245
+ <footer class="bg-blue-800 text-white py-3 text-center text-sm">
246
+ <p>© 2008 AJNET 31 - Défi de nettoyage expert</p>
247
+ </footer>
248
+ </div>
249
+
250
+ <script>
251
+ // Game variables
252
+ let score = 0;
253
+ let timeLeft = 60;
254
+ let level = 1;
255
+ let gameInterval;
256
+ let stainInterval;
257
+ let timeInterval;
258
+ let gameActive = false;
259
+ let stainsCleaned = 0;
260
+ let stainSpeed = 800;
261
+ let lastCleanTime = 0;
262
+ let combo = 0;
263
+ let maxCombo = 0;
264
+ let comboTimeout;
265
+ let movingStainChance = 0.5;
266
+ let penaltyStainChance = 0.25;
267
+ let stainCount = 0;
268
+ let penaltyStainCount = 0;
269
+
270
+ // DOM elements
271
+ const gameArea = document.getElementById('game-area');
272
+ const scoreDisplay = document.getElementById('score');
273
+ const timeDisplay = document.getElementById('time');
274
+ const timeBar = document.getElementById('time-bar');
275
+ const levelDisplay = document.getElementById('level');
276
+ const startScreen = document.getElementById('start-screen');
277
+ const gameOverScreen = document.getElementById('game-over');
278
+ const finalScoreDisplay = document.getElementById('final-score');
279
+ const finalLevelDisplay = document.getElementById('final-level');
280
+ const finalComboDisplay = document.getElementById('final-combo');
281
+ const startBtn = document.getElementById('start-btn');
282
+ const restartBtn = document.getElementById('restart-btn');
283
+ const shareBtn = document.getElementById('share-btn');
284
+ const comboCounter = document.getElementById('combo-counter');
285
+ const comboDisplay = document.getElementById('combo');
286
+ const rulesBtn = document.getElementById('rules-btn');
287
+ const rulesModal = document.getElementById('rules-modal');
288
+ const closeRulesBtn = document.getElementById('close-rules-btn');
289
+ const startFromRulesBtn = document.getElementById('start-from-rules-btn');
290
+
291
+ // Stain types with different colors, sizes and behaviors
292
+ const stainTypes = [
293
+ { class: 'bg-red-500', size: 'w-16 h-16', points: 1, moving: false, penalty: false },
294
+ { class: 'bg-yellow-500', size: 'w-20 h-20', points: 2, moving: false, penalty: false },
295
+ { class: 'bg-green-500', size: 'w-24 h-24', points: 3, moving: false, penalty: false },
296
+ { class: 'bg-purple-500', size: 'w-14 h-14', points: 1, moving: false, penalty: false },
297
+ { class: 'bg-pink-500', size: 'w-18 h-18', points: 2, moving: false, penalty: false },
298
+ { class: 'bg-blue-500', size: 'w-22 h-22', points: 3, moving: false, penalty: false },
299
+ { class: 'bg-red-600', size: 'w-20 h-20', points: -5, moving: true, penalty: true },
300
+ { class: 'bg-gray-800', size: 'w-24 h-24', points: -3, moving: false, penalty: true }
301
+ ];
302
+
303
+ // Initialize game
304
+ function initGame() {
305
+ score = 0;
306
+ timeLeft = 60;
307
+ level = 1;
308
+ stainsCleaned = 0;
309
+ stainSpeed = 800;
310
+ combo = 0;
311
+ maxCombo = 0;
312
+ stainCount = 0;
313
+ penaltyStainCount = 0;
314
+
315
+ updateScore();
316
+ updateTime();
317
+ updateLevel();
318
+ hideCombo();
319
+
320
+ gameArea.innerHTML = '';
321
+ }
322
+
323
+ // Start game
324
+ function startGame() {
325
+ initGame();
326
+ gameActive = true;
327
+ startScreen.classList.add('hidden');
328
+ gameOverScreen.classList.add('hidden');
329
+ rulesModal.classList.add('hidden');
330
+
331
+ // Start timers
332
+ timeInterval = setInterval(updateTimer, 1000);
333
+ stainInterval = setInterval(createStain, stainSpeed);
334
+
335
+ // Check game state every 500ms
336
+ gameInterval = setInterval(checkGameState, 500);
337
+ }
338
+
339
+ // Create a new stain with full screen movement
340
+ function createStain() {
341
+ if (!gameActive) return;
342
+
343
+ const gameAreaRect = gameArea.getBoundingClientRect();
344
+ const maxX = gameAreaRect.width - 100;
345
+ const maxY = gameAreaRect.height - 100;
346
+
347
+ // Position aléatoire mieux répartie sur toute la hauteur
348
+ const startX = Math.random() * maxX;
349
+ const startY = Math.random() * (maxY - 100) + 50; // +50 pour éviter le haut de l'écran
350
+
351
+ // Determine stain type
352
+ let stainTypeIndex;
353
+
354
+ if (penaltyStainCount >= Math.ceil(stainCount / 3) && Math.random() < penaltyStainChance) {
355
+ stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
356
+ } else {
357
+ if (Math.random() < movingStainChance) {
358
+ stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
359
+ }
360
+
361
+ if (Math.random() < penaltyStainChance) {
362
+ stainTypeIndex = Math.floor(Math.random() * 2) + (stainTypes.length - 2);
363
+ } else {
364
+ stainTypeIndex = Math.floor(Math.random() * (stainTypes.length - 2));
365
+ }
366
+ }
367
+
368
+ const stainType = stainTypes[stainTypeIndex];
369
+
370
+ const stain = document.createElement('div');
371
+ stain.className = `stain rounded-full ${stainType.class} ${stainType.size}`;
372
+ stain.style.setProperty('--start-x', `${startX}px`);
373
+ stain.style.setProperty('--start-y', `${startY}px`);
374
+
375
+ // Définir des mouvements aléatoires plus grands
376
+ const moveX1 = (Math.random() * maxX - startX) * 0.5;
377
+ const moveY1 = (Math.random() * (maxY - 100) - startY) * 0.5;
378
+ const moveX2 = (Math.random() * maxX - startX) * 0.5;
379
+ const moveY2 = (Math.random() * (maxY - 100) - startY) * 0.5;
380
+ const moveX3 = (Math.random() * maxX - startX) * 0.5;
381
+ const moveY3 = (Math.random() * (maxY - 100) - startY) * 0.5;
382
+
383
+ stain.style.setProperty('--move-x1', moveX1);
384
+ stain.style.setProperty('--move-y1', moveY1);
385
+ stain.style.setProperty('--move-x2', moveX2);
386
+ stain.style.setProperty('--move-y2', moveY2);
387
+ stain.style.setProperty('--move-x3', moveX3);
388
+ stain.style.setProperty('--move-y3', moveY3);
389
+
390
+ // Add moving class if needed (now most stains will move)
391
+ if (stainType.moving || Math.random() < movingStainChance) {
392
+ stain.classList.add('moving-stain');
393
+ // Make movement faster for moving stains
394
+ stain.style.animationDuration = `${3 + Math.random() * 2}s`;
395
+ }
396
+
397
+ // Add data attributes
398
+ stain.dataset.points = stainType.points;
399
+ stain.dataset.penalty = stainType.penalty;
400
+
401
+ // Position initiale
402
+ stain.style.left = `${startX}px`;
403
+ stain.style.top = `${startY}px`;
404
+
405
+ // Add click event
406
+ stain.addEventListener('click', cleanStain);
407
+
408
+ gameArea.appendChild(stain);
409
+ stainCount++;
410
+
411
+ if (stainType.penalty) {
412
+ penaltyStainCount++;
413
+ }
414
+ }
415
+
416
+ // Clean stain
417
+ function cleanStain(e) {
418
+ if (!gameActive) return;
419
+
420
+ const stain = e.target;
421
+ const points = parseInt(stain.dataset.points);
422
+ const isPenalty = stain.dataset.penalty === 'true';
423
+
424
+ stain.remove();
425
+ stainCount--;
426
+
427
+ if (isPenalty) {
428
+ penaltyStainCount = Math.max(0, penaltyStainCount - 1);
429
+ }
430
+
431
+ const now = Date.now();
432
+ if (now - lastCleanTime < 1000) {
433
+ combo++;
434
+ if (combo > maxCombo) maxCombo = combo;
435
+
436
+ showCombo();
437
+
438
+ clearTimeout(comboTimeout);
439
+ comboTimeout = setTimeout(resetCombo, 1000);
440
+ } else {
441
+ resetCombo();
442
+ }
443
+ lastCleanTime = now;
444
+
445
+ if (isPenalty) {
446
+ score += points;
447
+ showPenaltyEffect(stain, points);
448
+ } else {
449
+ const bonus = combo > 0 ? Math.floor(combo / 2) : 0;
450
+ score += points + bonus;
451
+ stainsCleaned++;
452
+ showComboEffect(stain, points + bonus);
453
+ }
454
+
455
+ updateScore();
456
+
457
+ if (stainsCleaned >= level * 10) {
458
+ levelUp();
459
+ }
460
+ }
461
+
462
+ // Show combo effect
463
+ function showComboEffect(element, points) {
464
+ const rect = element.getBoundingClientRect();
465
+ const gameAreaRect = gameArea.getBoundingClientRect();
466
+
467
+ const effect = document.createElement('div');
468
+ effect.className = 'combo-effect';
469
+ effect.textContent = `+${points}`;
470
+ effect.style.left = `${rect.left - gameAreaRect.left}px`;
471
+ effect.style.top = `${rect.top - gameAreaRect.top}px`;
472
+
473
+ gameArea.appendChild(effect);
474
+
475
+ setTimeout(() => {
476
+ effect.remove();
477
+ }, 1500);
478
+ }
479
+
480
+ // Show penalty effect
481
+ function showPenaltyEffect(element, points) {
482
+ const rect = element.getBoundingClientRect();
483
+ const gameAreaRect = gameArea.getBoundingClientRect();
484
+
485
+ const effect = document.createElement('div');
486
+ effect.className = 'penalty-effect';
487
+ effect.textContent = `${points}`;
488
+ effect.style.left = `${rect.left - gameAreaRect.left}px`;
489
+ effect.style.top = `${rect.top - gameAreaRect.top}px`;
490
+
491
+ gameArea.appendChild(effect);
492
+
493
+ setTimeout(() => {
494
+ effect.remove();
495
+ }, 1500);
496
+ }
497
+
498
+ // Show combo counter
499
+ function showCombo() {
500
+ comboCounter.classList.remove('hidden');
501
+ comboDisplay.textContent = combo;
502
+ }
503
+
504
+ // Hide combo counter
505
+ function hideCombo() {
506
+ comboCounter.classList.add('hidden');
507
+ }
508
+
509
+ // Reset combo
510
+ function resetCombo() {
511
+ combo = 0;
512
+ hideCombo();
513
+ }
514
+
515
+ // Level up
516
+ function levelUp() {
517
+ level++;
518
+ stainsCleaned = 0;
519
+
520
+ stainSpeed = Math.max(stainSpeed - 100, 300);
521
+ movingStainChance = Math.min(movingStainChance + 0.05, 0.8);
522
+ penaltyStainChance = Math.min(penaltyStainChance + 0.02, 0.4);
523
+
524
+ updateLevel();
525
+
526
+ const levelUpEffect = document.createElement('div');
527
+ levelUpEffect.className = 'absolute inset-0 flex items-center justify-center';
528
+ levelUpEffect.innerHTML = `
529
+ <div class="bg-blue-600 bg-opacity-80 text-white text-4xl font-bold p-8 rounded-xl animate-pulse">
530
+ Niveau ${level}!
531
+ </div>
532
+ `;
533
+ gameArea.appendChild(levelUpEffect);
534
+
535
+ setTimeout(() => {
536
+ levelUpEffect.remove();
537
+ }, 1500);
538
+ }
539
+
540
+ // Update timer
541
+ function updateTimer() {
542
+ timeLeft--;
543
+ updateTime();
544
+
545
+ if (timeLeft <= 0) {
546
+ endGame();
547
+ }
548
+ }
549
+
550
+ // Update time display
551
+ function updateTime() {
552
+ timeDisplay.textContent = timeLeft;
553
+ timeBar.style.width = `${(timeLeft / 60) * 100}%`;
554
+
555
+ if (timeLeft <= 10) {
556
+ timeBar.classList.remove('bg-blue-600');
557
+ timeBar.classList.add('bg-red-500');
558
+ timeDisplay.classList.add('text-red-500');
559
+ } else {
560
+ timeBar.classList.remove('bg-red-500');
561
+ timeBar.classList.add('bg-blue-600');
562
+ timeDisplay.classList.remove('text-red-500');
563
+ }
564
+ }
565
+
566
+ // Update score display
567
+ function updateScore() {
568
+ scoreDisplay.textContent = score;
569
+ }
570
+
571
+ // Update level display
572
+ function updateLevel() {
573
+ levelDisplay.textContent = level;
574
+ }
575
+
576
+ // Check game state
577
+ function checkGameState() {
578
+ if (timeLeft <= 0) {
579
+ endGame();
580
+ }
581
+ }
582
+
583
+ // End game
584
+ function endGame() {
585
+ gameActive = false;
586
+
587
+ clearInterval(timeInterval);
588
+ clearInterval(stainInterval);
589
+ clearInterval(gameInterval);
590
+ clearTimeout(comboTimeout);
591
+
592
+ finalScoreDisplay.textContent = score;
593
+ finalLevelDisplay.textContent = level;
594
+ finalComboDisplay.textContent = maxCombo;
595
+ gameOverScreen.classList.remove('hidden');
596
+ }
597
+
598
+ // Event listeners
599
+ startBtn.addEventListener('click', startGame);
600
+ restartBtn.addEventListener('click', startGame);
601
+ shareBtn.addEventListener('click', () => {
602
+ alert(`Partagez votre score de ${score} points (Niveau ${level}) avec vos amis!`);
603
+ });
604
+
605
+ rulesBtn.addEventListener('click', () => {
606
+ rulesModal.classList.remove('hidden');
607
+ });
608
+
609
+ closeRulesBtn.addEventListener('click', () => {
610
+ rulesModal.classList.add('hidden');
611
+ });
612
+
613
+ startFromRulesBtn.addEventListener('click', startGame);
614
+
615
+ // Initialize game on load
616
+ initGame();
617
+ </script>
618
+ <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>
619
+ </html>