Mousco commited on
Commit
e0474c2
·
verified ·
1 Parent(s): 9813a0a

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +732 -19
index.html CHANGED
@@ -1,19 +1,732 @@
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, maximum-scale=1.0, user-scalable=no">
6
+ <title>Catch Up: Neon Chase</title>
7
+
8
+ <!-- Importation de la police Google Fonts (Orbitron pour le look Sci-Fi) -->
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Roboto:wght@300;400&display=swap" rel="stylesheet">
12
+
13
+ <!-- Importation de FontAwesome pour les icônes -->
14
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
15
+
16
+ <style>
17
+ :root {
18
+ --bg-color: #0b0c15;
19
+ --primary-color: #00f3ff; /* Cyan Néon */
20
+ --secondary-color: #ff0055; /* Rouge Néon */
21
+ --accent-color: #00ff88; /* Vert Néon */
22
+ --text-color: #ffffff;
23
+ --glass-bg: rgba(255, 255, 255, 0.05);
24
+ --glass-border: rgba(255, 255, 255, 0.1);
25
+ --font-display: 'Orbitron', sans-serif;
26
+ --font-body: 'Roboto', sans-serif;
27
+ }
28
+
29
+ * {
30
+ box-sizing: border-box;
31
+ margin: 0;
32
+ padding: 0;
33
+ user-select: none; /* Empêche la sélection de texte pendant le jeu */
34
+ -webkit-user-select: none;
35
+ }
36
+
37
+ body {
38
+ background-color: var(--bg-color);
39
+ color: var(--text-color);
40
+ font-family: var(--font-body);
41
+ overflow: hidden; /* Empêche le défilement */
42
+ width: 100vw;
43
+ height: 100vh;
44
+ display: flex;
45
+ flex-direction: column;
46
+ }
47
+
48
+ /* --- Header --- */
49
+ header {
50
+ position: absolute;
51
+ top: 0;
52
+ left: 0;
53
+ width: 100%;
54
+ padding: 1rem 2rem;
55
+ display: flex;
56
+ justify-content: space-between;
57
+ align-items: center;
58
+ z-index: 100;
59
+ pointer-events: none; /* Laisse passer les clics vers le canvas si besoin */
60
+ }
61
+
62
+ .brand {
63
+ font-family: var(--font-display);
64
+ font-size: 1.5rem;
65
+ font-weight: 900;
66
+ text-transform: uppercase;
67
+ letter-spacing: 2px;
68
+ color: var(--primary-color);
69
+ text-shadow: 0 0 10px var(--primary-color);
70
+ pointer-events: auto;
71
+ }
72
+
73
+ .credits {
74
+ font-size: 0.9rem;
75
+ opacity: 0.8;
76
+ pointer-events: auto;
77
+ }
78
+
79
+ .credits a {
80
+ color: var(--accent-color);
81
+ text-decoration: none;
82
+ font-weight: bold;
83
+ transition: color 0.3s ease;
84
+ }
85
+
86
+ .credits a:hover {
87
+ color: var(--primary-color);
88
+ text-shadow: 0 0 8px var(--primary-color);
89
+ }
90
+
91
+ /* --- Canvas Container --- */
92
+ #game-container {
93
+ position: relative;
94
+ width: 100%;
95
+ height: 100%;
96
+ display: flex;
97
+ justify-content: center;
98
+ align-items: center;
99
+ }
100
+
101
+ canvas {
102
+ display: block;
103
+ box-shadow: 0 0 50px rgba(0, 0, 0, 0.5);
104
+ }
105
+
106
+ /* --- UI Overlays (Start / Game Over) --- */
107
+ .overlay {
108
+ position: absolute;
109
+ top: 0;
110
+ left: 0;
111
+ width: 100%;
112
+ height: 100%;
113
+ background: rgba(11, 12, 21, 0.85);
114
+ backdrop-filter: blur(8px);
115
+ display: flex;
116
+ flex-direction: column;
117
+ justify-content: center;
118
+ align-items: center;
119
+ z-index: 50;
120
+ transition: opacity 0.4s ease;
121
+ }
122
+
123
+ .overlay.hidden {
124
+ opacity: 0;
125
+ pointer-events: none;
126
+ }
127
+
128
+ .menu-card {
129
+ background: var(--glass-bg);
130
+ border: 1px solid var(--glass-border);
131
+ padding: 3rem;
132
+ border-radius: 20px;
133
+ text-align: center;
134
+ box-shadow: 0 0 30px rgba(0, 243, 255, 0.1);
135
+ max-width: 500px;
136
+ width: 90%;
137
+ animation: float 6s ease-in-out infinite;
138
+ }
139
+
140
+ h1 {
141
+ font-family: var(--font-display);
142
+ font-size: 3rem;
143
+ margin-bottom: 0.5rem;
144
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
145
+ -webkit-background-clip: text;
146
+ -webkit-text-fill-color: transparent;
147
+ }
148
+
149
+ p.subtitle {
150
+ font-size: 1.1rem;
151
+ margin-bottom: 2rem;
152
+ color: #aaa;
153
+ }
154
+
155
+ .score-display {
156
+ font-family: var(--font-display);
157
+ font-size: 4rem;
158
+ margin: 1rem 0;
159
+ color: var(--text-color);
160
+ }
161
+
162
+ .instructions {
163
+ margin-bottom: 2rem;
164
+ font-size: 0.95rem;
165
+ line-height: 1.6;
166
+ color: #ddd;
167
+ text-align: left;
168
+ background: rgba(0,0,0,0.3);
169
+ padding: 1rem;
170
+ border-radius: 8px;
171
+ }
172
+
173
+ .instructions ul {
174
+ list-style: none;
175
+ }
176
+
177
+ .instructions li {
178
+ margin-bottom: 0.5rem;
179
+ }
180
+
181
+ .instructions i {
182
+ margin-right: 10px;
183
+ width: 20px;
184
+ text-align: center;
185
+ }
186
+
187
+ .btn {
188
+ background: linear-gradient(45deg, var(--primary-color), #00a8ff);
189
+ color: #000;
190
+ font-family: var(--font-display);
191
+ font-weight: 700;
192
+ font-size: 1.2rem;
193
+ padding: 1rem 3rem;
194
+ border: none;
195
+ border-radius: 50px;
196
+ cursor: pointer;
197
+ transition: transform 0.2s, box-shadow 0.2s;
198
+ text-transform: uppercase;
199
+ letter-spacing: 1px;
200
+ }
201
+
202
+ .btn:hover {
203
+ transform: translateY(-3px);
204
+ box-shadow: 0 0 20px var(--primary-color);
205
+ }
206
+
207
+ .btn:active {
208
+ transform: translateY(1px);
209
+ }
210
+
211
+ /* --- HUD In-Game --- */
212
+ #hud {
213
+ position: absolute;
214
+ top: 20px;
215
+ width: 100%;
216
+ padding: 0 40px;
217
+ display: flex;
218
+ justify-content: space-between;
219
+ pointer-events: none;
220
+ z-index: 10;
221
+ }
222
+
223
+ .hud-item {
224
+ font-family: var(--font-display);
225
+ font-size: 1.5rem;
226
+ color: var(--text-color);
227
+ text-shadow: 0 0 5px rgba(0,0,0,0.5);
228
+ }
229
+
230
+ .hud-label {
231
+ font-size: 0.8rem;
232
+ color: #888;
233
+ display: block;
234
+ text-transform: uppercase;
235
+ }
236
+
237
+ /* --- Animations --- */
238
+ @keyframes float {
239
+ 0% { transform: translateY(0px); }
240
+ 50% { transform: translateY(-10px); }
241
+ 100% { transform: translateY(0px); }
242
+ }
243
+
244
+ /* --- Mobile Adjustments --- */
245
+ @media (max-width: 600px) {
246
+ h1 { font-size: 2rem; }
247
+ .menu-card { padding: 1.5rem; }
248
+ #hud { padding: 0 20px; }
249
+ .hud-item { font-size: 1.2rem; }
250
+ header { padding: 1rem; }
251
+ .brand { font-size: 1.1rem; }
252
+ }
253
+ </style>
254
+ </head>
255
+ <body>
256
+
257
+ <!-- Header avec le lien requis -->
258
+ <header>
259
+ <div class="brand">
260
+ <i class="fa-solid fa-bolt"></i> Catch Up
261
+ </div>
262
+ <div class="credits">
263
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
264
+ </div>
265
+ </header>
266
+
267
+ <!-- HUD (Score en jeu) -->
268
+ <div id="hud" class="hidden">
269
+ <div class="hud-item">
270
+ <span class="hud-label">Score</span>
271
+ <span id="current-score">0</span>
272
+ </div>
273
+ <div class="hud-item" style="text-align: right;">
274
+ <span class="hud-label">Record</span>
275
+ <span id="high-score">0</span>
276
+ </div>
277
+ </div>
278
+
279
+ <!-- Conteneur principal du jeu -->
280
+ <div id="game-container">
281
+ <canvas id="gameCanvas"></canvas>
282
+
283
+ <!-- Écran de Démarrage -->
284
+ <div id="start-screen" class="overlay">
285
+ <div class="menu-card">
286
+ <h1>Catch Up</h1>
287
+ <p class="subtitle">Neon Chase Edition</p>
288
+
289
+ <div class="instructions">
290
+ <ul>
291
+ <li><i class="fa-solid fa-computer-mouse" style="color: var(--primary-color)"></i> Bougez la souris ou glissez pour contrôler le point bleu.</li>
292
+ <li><i class="fa-solid fa-bullseye" style="color: var(--accent-color)"></i> Attrapez les orbes <strong>VERTS</strong> pour gagner.</li>
293
+ <li><i class="fa-solid fa-skull" style="color: var(--secondary-color)"></i> Évitez les triangles <strong>ROUGES</strong>.</li>
294
+ <li><i class="fa-solid fa-gauge-high"></i> La vitesse augmente à chaque point !</li>
295
+ </ul>
296
+ </div>
297
+
298
+ <button class="btn" id="start-btn">Jouer</button>
299
+ </div>
300
+ </div>
301
+
302
+ <!-- Écran Game Over -->
303
+ <div id="game-over-screen" class="overlay hidden">
304
+ <div class="menu-card">
305
+ <h1 style="color: var(--secondary-color);">Partie Terminée</h1>
306
+ <p class="subtitle">Vous avez été attrapé !</p>
307
+
308
+ <div class="score-display" id="final-score">0</div>
309
+ <p style="margin-bottom: 2rem; color: #888;">Meilleur Score: <span id="final-high-score">0</span></p>
310
+
311
+ <button class="btn" id="restart-btn">Réessayer</button>
312
+ </div>
313
+ </div>
314
+ </div>
315
+
316
+ <script>
317
+ /**
318
+ * Logique du Jeu "Catch Up"
319
+ * Utilise HTML5 Canvas pour le rendu
320
+ */
321
+
322
+ const canvas = document.getElementById('gameCanvas');
323
+ const ctx = canvas.getContext('2d');
324
+
325
+ // Éléments UI
326
+ const startScreen = document.getElementById('start-screen');
327
+ const gameOverScreen = document.getElementById('game-over-screen');
328
+ const hud = document.getElementById('hud');
329
+ const currentScoreEl = document.getElementById('current-score');
330
+ const highScoreEl = document.getElementById('high-score');
331
+ const finalScoreEl = document.getElementById('final-score');
332
+ const finalHighScoreEl = document.getElementById('final-high-score');
333
+ const startBtn = document.getElementById('start-btn');
334
+ const restartBtn = document.getElementById('restart-btn');
335
+
336
+ // État du jeu
337
+ let gameRunning = false;
338
+ let score = 0;
339
+ let highScore = localStorage.getItem('catchUpHighScore') || 0;
340
+ let animationId;
341
+ let frameCount = 0;
342
+
343
+ // Configuration
344
+ const config = {
345
+ playerSpeed: 0.15, // Lerp factor
346
+ enemyBaseSpeed: 2,
347
+ enemySpeedIncrement: 0.2,
348
+ colors: {
349
+ player: '#00f3ff',
350
+ target: '#00ff88',
351
+ enemy: '#ff0055',
352
+ particle: '#ffffff'
353
+ }
354
+ };
355
+
356
+ // Dimensions du canevas
357
+ let width, height;
358
+
359
+ function resize() {
360
+ width = window.innerWidth;
361
+ height = window.innerHeight;
362
+ canvas.width = width;
363
+ canvas.height = height;
364
+ }
365
+ window.addEventListener('resize', resize);
366
+ resize();
367
+
368
+ // --- Classes du Jeu ---
369
+
370
+ class Player {
371
+ constructor() {
372
+ this.x = width / 2;
373
+ this.y = height / 2;
374
+ this.radius = 15;
375
+ this.targetX = this.x;
376
+ this.targetY = this.y;
377
+ this.trail = []; // Queue visuelle
378
+ }
379
+
380
+ update(mouseX, mouseY) {
381
+ // Lissage du mouvement (Lerp)
382
+ if (mouseX !== undefined && mouseY !== undefined) {
383
+ this.targetX = mouseX;
384
+ this.targetY = mouseY;
385
+ }
386
+
387
+ this.x += (this.targetX - this.x) * config.playerSpeed;
388
+ this.y += (this.targetY - this.y) * config.playerSpeed;
389
+
390
+ // Ajout à la traînée
391
+ this.trail.push({ x: this.x, y: this.y, alpha: 1.0 });
392
+ if (this.trail.length > 20) {
393
+ this.trail.shift();
394
+ }
395
+
396
+ // Mise à jour de l'alpha de la traînée
397
+ this.trail.forEach(t => t.alpha -= 0.05);
398
+ }
399
+
400
+ draw() {
401
+ // Dessin de la traînée
402
+ ctx.save();
403
+ for (let i = 0; i < this.trail.length; i++) {
404
+ const point = this.trail[i];
405
+ ctx.beginPath();
406
+ ctx.arc(point.x, point.y, this.radius * (i / this.trail.length), 0, Math.PI * 2);
407
+ ctx.fillStyle = `rgba(0, 243, 255, ${point.alpha * 0.5})`;
408
+ ctx.fill();
409
+ }
410
+ ctx.restore();
411
+
412
+ // Dessin du joueur
413
+ ctx.save();
414
+ ctx.shadowBlur = 20;
415
+ ctx.shadowColor = config.colors.player;
416
+ ctx.fillStyle = config.colors.player;
417
+ ctx.beginPath();
418
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
419
+ ctx.fill();
420
+
421
+ // Noyau blanc
422
+ ctx.fillStyle = '#fff';
423
+ ctx.beginPath();
424
+ ctx.arc(this.x, this.y, this.radius * 0.4, 0, Math.PI * 2);
425
+ ctx.fill();
426
+ ctx.restore();
427
+ }
428
+ }
429
+
430
+ class Target {
431
+ constructor() {
432
+ this.radius = 12;
433
+ this.respawn();
434
+ this.pulse = 0;
435
+ }
436
+
437
+ respawn() {
438
+ // Marge de sécurité pour ne pas apparaître sur les bords
439
+ const margin = 50;
440
+ this.x = margin + Math.random() * (width - margin * 2);
441
+ this.y = margin + Math.random() * (height - margin * 2);
442
+ }
443
+
444
+ update() {
445
+ this.pulse += 0.1;
446
+ }
447
+
448
+ draw() {
449
+ const scale = 1 + Math.sin(this.pulse) * 0.1;
450
+ ctx.save();
451
+ ctx.translate(this.x, this.y);
452
+ ctx.scale(scale, scale);
453
+
454
+ ctx.shadowBlur = 15;
455
+ ctx.shadowColor = config.colors.target;
456
+ ctx.fillStyle = config.colors.target;
457
+
458
+ // Forme de losange
459
+ ctx.beginPath();
460
+ ctx.moveTo(0, -this.radius);
461
+ ctx.lineTo(this.radius, 0);
462
+ ctx.lineTo(0, this.radius);
463
+ ctx.lineTo(-this.radius, 0);
464
+ ctx.closePath();
465
+ ctx.fill();
466
+
467
+ ctx.restore();
468
+ }
469
+ }
470
+
471
+ class Enemy {
472
+ constructor() {
473
+ this.radius = 18;
474
+ this.resetPosition();
475
+ this.angle = Math.random() * Math.PI * 2;
476
+ this.speed = config.enemyBaseSpeed + (score * config.enemySpeedIncrement);
477
+ }
478
+
479
+ resetPosition() {
480
+ // Apparaît en dehors de l'écran
481
+ if (Math.random() < 0.5) {
482
+ this.x = Math.random() < 0.5 ? -50 : width + 50;
483
+ this.y = Math.random() * height;
484
+ } else {
485
+ this.x = Math.random() * width;
486
+ this.y = Math.random() < 0.5 ? -50 : height + 50;
487
+ }
488
+ }
489
+
490
+ update(playerX, playerY) {
491
+ // Suit le joueur
492
+ const dx = playerX - this.x;
493
+ const dy = playerY - this.y;
494
+ const distance = Math.sqrt(dx * dx + dy * dy);
495
+
496
+ this.x += (dx / distance) * this.speed;
497
+ this.y += (dy / distance) * this.speed;
498
+
499
+ // Rotation visuelle
500
+ this.angle += 0.05;
501
+ }
502
+
503
+ draw() {
504
+ ctx.save();
505
+ ctx.translate(this.x, this.y);
506
+ ctx.rotate(this.angle);
507
+
508
+ ctx.shadowBlur = 15;
509
+ ctx.shadowColor = config.colors.enemy;
510
+ ctx.fillStyle = config.colors.enemy;
511
+
512
+ // Forme de triangle
513
+ ctx.beginPath();
514
+ ctx.moveTo(0, -this.radius);
515
+ ctx.lineTo(this.radius, this.radius);
516
+ ctx.lineTo(-this.radius, this.radius);
517
+ ctx.closePath();
518
+ ctx.fill();
519
+
520
+ ctx.restore();
521
+ }
522
+ }
523
+
524
+ class Particle {
525
+ constructor(x, y, color) {
526
+ this.x = x;
527
+ this.y = y;
528
+ this.color = color;
529
+ const angle = Math.random() * Math.PI * 2;
530
+ const speed = Math.random() * 4 + 1;
531
+ this.vx = Math.cos(angle) * speed;
532
+ this.vy = Math.sin(angle) * speed;
533
+ this.life = 1.0;
534
+ this.decay = Math.random() * 0.03 + 0.02;
535
+ }
536
+
537
+ update() {
538
+ this.x += this.vx;
539
+ this.y += this.vy;
540
+ this.life -= this.decay;
541
+ }
542
+
543
+ draw() {
544
+ ctx.save();
545
+ ctx.globalAlpha = this.life;
546
+ ctx.fillStyle = this.color;
547
+ ctx.beginPath();
548
+ ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
549
+ ctx.fill();
550
+ ctx.restore();
551
+ }
552
+ }
553
+
554
+ // --- Gestion du Jeu ---
555
+
556
+ let player;
557
+ let target;
558
+ let enemies = [];
559
+ let particles = [];
560
+ let mouseX = width / 2;
561
+ let mouseY = height / 2;
562
+
563
+ function init() {
564
+ player = new Player();
565
+ target = new Target();
566
+ enemies = [];
567
+ particles = [];
568
+ score = 0;
569
+ updateScoreUI();
570
+
571
+ // Spawn initial d'ennemis
572
+ spawnEnemy();
573
+ }
574
+
575
+ function spawnEnemy() {
576
+ enemies.push(new Enemy());
577
+ }
578
+
579
+ function createExplosion(x, y, color) {
580
+ for (let i = 0; i < 15; i++) {
581
+ particles.push(new Particle(x, y, color));
582
+ }
583
+ }
584
+
585
+ function updateScoreUI() {
586
+ currentScoreEl.textContent = score;
587
+ highScoreEl.textContent = highScore;
588
+ }
589
+
590
+ function gameOver() {
591
+ gameRunning = false;
592
+ cancelAnimationFrame(animationId);
593
+
594
+ if (score > highScore) {
595
+ highScore = score;
596
+ localStorage.setItem('catchUpHighScore', highScore);
597
+ }
598
+
599
+ finalScoreEl.textContent = score;
600
+ finalHighScoreEl.textContent = highScore;
601
+
602
+ hud.classList.add('hidden');
603
+ gameOverScreen.classList.remove('hidden');
604
+ }
605
+
606
+ function gameLoop() {
607
+ if (!gameRunning) return;
608
+
609
+ // Nettoyage de l'écran avec une traînée légère
610
+ ctx.fillStyle = 'rgba(11, 12, 21, 0.3)';
611
+ ctx.fillRect(0, 0, width, height);
612
+
613
+ // Effet de grille subtil en arrière-plan
614
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.03)';
615
+ ctx.lineWidth = 1;
616
+ const gridSize = 50;
617
+ const offset = (frameCount * 0.5) % gridSize;
618
+
619
+ ctx.beginPath();
620
+ // Lignes verticales
621
+ for (let x = offset; x < width; x += gridSize) {
622
+ ctx.moveTo(x, 0);
623
+ ctx.lineTo(x, height);
624
+ }
625
+ // Lignes horizontales
626
+ for (let y = offset; y < height; y += gridSize) {
627
+ ctx.moveTo(0, y);
628
+ ctx.lineTo(width, y);
629
+ }
630
+ ctx.stroke();
631
+
632
+ // Logique Joueur
633
+ player.update(mouseX, mouseY);
634
+ player.draw();
635
+
636
+ // Logique Cible
637
+ target.update();
638
+ target.draw();
639
+
640
+ // Collision Joueur - Cible
641
+ const distToTarget = Math.hypot(player.x - target.x, player.y - target.y);
642
+ if (distToTarget < player.radius + target.radius) {
643
+ score++;
644
+ createExplosion(target.x, target.y, config.colors.target);
645
+ updateScoreUI();
646
+ target.respawn();
647
+
648
+ // Difficulté : Ajouter un ennemi tous les 3 points
649
+ if (score % 3 === 0) {
650
+ spawnEnemy();
651
+ }
652
+ }
653
+
654
+ // Logique Ennemis
655
+ enemies.forEach(enemy => {
656
+ enemy.update(player.x, player.y);
657
+ enemy.draw();
658
+
659
+ // Collision Joueur - Ennemi
660
+ const distToEnemy = Math.hypot(player.x - enemy.x, player.y - enemy.y);
661
+ if (distToEnemy < player.radius + enemy.radius - 5) { // -5 pour hitbox plus indulgente
662
+ createExplosion(player.x, player.y, config.colors.player);
663
+ gameOver();
664
+ }
665
+ });
666
+
667
+ // Logique Particules
668
+ particles.forEach((p, index) => {
669
+ p.update();
670
+ p.draw();
671
+ if (p.life <= 0) {
672
+ particles.splice(index, 1);
673
+ }
674
+ });
675
+
676
+ frameCount++;
677
+ animationId = requestAnimationFrame(gameLoop);
678
+ }
679
+
680
+ // --- Entrées Utilisateur ---
681
+
682
+ // Souris
683
+ window.addEventListener('mousemove', (e) => {
684
+ if (gameRunning) {
685
+ mouseX = e.clientX;
686
+ mouseY = e.clientY;
687
+ }
688
+ });
689
+
690
+ // Tactile
691
+ window.addEventListener('touchmove', (e) => {
692
+ if (gameRunning) {
693
+ e.preventDefault(); // Empêche le scroll
694
+ mouseX = e.touches[0].clientX;
695
+ mouseY = e.touches[0].clientY;
696
+ }
697
+ }, { passive: false });
698
+
699
+ window.addEventListener('touchstart', (e) => {
700
+ if (gameRunning) {
701
+ mouseX = e.touches[0].clientX;
702
+ mouseY = e.touches[0].clientY;
703
+ }
704
+ }, { passive: false });
705
+
706
+ // Boutons
707
+ startBtn.addEventListener('click', () => {
708
+ startScreen.classList.add('hidden');
709
+ hud.classList.remove('hidden');
710
+ init();
711
+ gameRunning = true;
712
+ mouseX = width / 2; // Réinitialiser la position de la souris virtuelle
713
+ mouseY = height / 2;
714
+ gameLoop();
715
+ });
716
+
717
+ restartBtn.addEventListener('click', () => {
718
+ gameOverScreen.classList.add('hidden');
719
+ hud.classList.remove('hidden');
720
+ init();
721
+ gameRunning = true;
722
+ mouseX = width / 2;
723
+ mouseY = height / 2;
724
+ gameLoop();
725
+ });
726
+
727
+ // Initialisation de l'affichage High Score
728
+ highScoreEl.textContent = highScore;
729
+
730
+ </script>
731
+ </body>
732
+ </html>