drdata commited on
Commit
44c4929
·
verified ·
1 Parent(s): 4b352d4

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +648 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Space Invaders
3
- emoji: 🏆
4
  colorFrom: green
5
- colorTo: red
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: space-invaders
3
+ emoji: 🐳
4
  colorFrom: green
5
+ colorTo: pink
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,648 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Space Invaders</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ @keyframes enemyMove {
10
+ 0%, 100% { transform: translateX(0); }
11
+ 25% { transform: translateX(10px); }
12
+ 50% { transform: translateX(0); }
13
+ 75% { transform: translateX(-10px); }
14
+ }
15
+
16
+ @keyframes explosion {
17
+ 0% { transform: scale(0.5); opacity: 1; }
18
+ 100% { transform: scale(1.5); opacity: 0; }
19
+ }
20
+
21
+ .enemy-animation {
22
+ animation: enemyMove 1s infinite;
23
+ }
24
+
25
+ .explosion {
26
+ animation: explosion 0.5s forwards;
27
+ }
28
+
29
+ #gameCanvas {
30
+ background-color: #111827;
31
+ border-radius: 8px;
32
+ box-shadow: 0 0 30px rgba(59, 130, 246, 0.5);
33
+ }
34
+
35
+ .pixel-art {
36
+ image-rendering: pixelated;
37
+ image-rendering: -moz-crisp-edges;
38
+ image-rendering: crisp-edges;
39
+ }
40
+ </style>
41
+ </head>
42
+ <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
43
+ <div class="text-center mb-6">
44
+ <h1 class="text-4xl font-bold mb-2 text-blue-400">SPACE INVADERS</h1>
45
+ <div class="flex justify-center items-center gap-8 mb-4">
46
+ <div class="text-xl">
47
+ <span class="text-blue-300">Score:</span>
48
+ <span id="score" class="ml-2 text-yellow-300">0</span>
49
+ </div>
50
+ <div class="text-xl">
51
+ <span class="text-blue-300">Lives:</span>
52
+ <span id="lives" class="ml-2 text-red-400">3</span>
53
+ </div>
54
+ <div class="text-xl">
55
+ <span class="text-blue-300">Level:</span>
56
+ <span id="level" class="ml-2 text-green-400">1</span>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="relative">
62
+ <canvas id="gameCanvas" width="600" height="500" class="border-2 border-blue-500"></canvas>
63
+
64
+ <!-- Start Screen -->
65
+ <div id="startScreen" class="absolute inset-0 bg-black bg-opacity-80 flex flex-col items-center justify-center">
66
+ <h2 class="text-4xl font-bold mb-6 text-blue-400">SPACE INVADERS</h2>
67
+ <p class="text-xl mb-8 text-center max-w-md">
68
+ Defend Earth from alien invasion!<br>
69
+ Use arrow keys to move and space to shoot.
70
+ </p>
71
+ <button id="startButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-full text-xl transition-all transform hover:scale-105">
72
+ START GAME
73
+ </button>
74
+ <div class="mt-8 text-gray-400">
75
+ <p>Controls:</p>
76
+ <div class="flex gap-4 mt-2">
77
+ <div class="bg-gray-800 px-3 py-1 rounded">← →</div>
78
+ <div class="bg-gray-800 px-3 py-1 rounded">SPACE</div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Game Over Screen -->
84
+ <div id="gameOverScreen" class="absolute inset-0 bg-black bg-opacity-80 hidden flex-col items-center justify-center">
85
+ <h2 class="text-4xl font-bold mb-2 text-red-500">GAME OVER</h2>
86
+ <p class="text-2xl mb-6">Your score: <span id="finalScore" class="text-yellow-300">0</span></p>
87
+ <button id="restartButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-full transition-all transform hover:scale-105">
88
+ PLAY AGAIN
89
+ </button>
90
+ </div>
91
+
92
+ <!-- Level Complete Screen -->
93
+ <div id="levelCompleteScreen" class="absolute inset-0 bg-black bg-opacity-80 hidden flex-col items-center justify-center">
94
+ <h2 class="text-4xl font-bold mb-2 text-green-500">LEVEL COMPLETE!</h2>
95
+ <p class="text-2xl mb-6">Get ready for level <span id="nextLevel" class="text-yellow-300">2</span></p>
96
+ <button id="nextLevelButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-full transition-all transform hover:scale-105">
97
+ CONTINUE
98
+ </button>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="mt-8 text-gray-400 text-center max-w-md">
103
+ <p class="mb-2">Tip: The aliens move faster as their numbers decrease!</p>
104
+ <p>Destroy the mothership for bonus points!</p>
105
+ </div>
106
+
107
+ <script>
108
+ // Game variables
109
+ const canvas = document.getElementById('gameCanvas');
110
+ const ctx = canvas.getContext('2d');
111
+ const startScreen = document.getElementById('startScreen');
112
+ const gameOverScreen = document.getElementById('gameOverScreen');
113
+ const levelCompleteScreen = document.getElementById('levelCompleteScreen');
114
+ const startButton = document.getElementById('startButton');
115
+ const restartButton = document.getElementById('restartButton');
116
+ const nextLevelButton = document.getElementById('nextLevelButton');
117
+ const scoreDisplay = document.getElementById('score');
118
+ const livesDisplay = document.getElementById('lives');
119
+ const levelDisplay = document.getElementById('level');
120
+ const finalScoreDisplay = document.getElementById('finalScore');
121
+ const nextLevelDisplay = document.getElementById('nextLevel');
122
+
123
+ // Game state
124
+ let gameRunning = false;
125
+ let score = 0;
126
+ let lives = 3;
127
+ let level = 1;
128
+ let enemies = [];
129
+ let enemyBullets = [];
130
+ let playerBullets = [];
131
+ let explosions = [];
132
+ let player = {
133
+ x: canvas.width / 2 - 25,
134
+ y: canvas.height - 60,
135
+ width: 50,
136
+ height: 30,
137
+ speed: 8,
138
+ color: '#3B82F6'
139
+ };
140
+
141
+ let keys = {
142
+ ArrowLeft: false,
143
+ ArrowRight: false,
144
+ ' ': false
145
+ };
146
+
147
+ // Enemy types
148
+ const enemyTypes = [
149
+ { color: '#F59E0B', points: 10, width: 40, height: 30 }, // Yellow - basic
150
+ { color: '#8B5CF6', points: 20, width: 40, height: 30 }, // Purple - faster
151
+ { color: '#EC4899', points: 30, width: 40, height: 30 }, // Pink - shoots more
152
+ { color: '#10B981', points: 50, width: 60, height: 40 } // Green - mothership
153
+ ];
154
+
155
+ // Initialize game
156
+ function initGame() {
157
+ score = 0;
158
+ lives = 3;
159
+ level = 1;
160
+ updateDisplays();
161
+ createEnemies();
162
+ gameRunning = true;
163
+ gameLoop();
164
+ }
165
+
166
+ // Create enemies based on level
167
+ function createEnemies() {
168
+ enemies = [];
169
+ const rows = Math.min(3 + Math.floor(level / 2), 6);
170
+ const cols = Math.min(5 + level, 10);
171
+ const spacing = 60;
172
+ const startX = (canvas.width - (cols - 1) * spacing) / 2;
173
+ const startY = 50;
174
+
175
+ // Regular enemies
176
+ for (let row = 0; row < rows; row++) {
177
+ for (let col = 0; col < cols; col++) {
178
+ const typeIndex = row % 3; // Cycle through first 3 types
179
+ enemies.push({
180
+ x: startX + col * spacing,
181
+ y: startY + row * spacing,
182
+ width: enemyTypes[typeIndex].width,
183
+ height: enemyTypes[typeIndex].height,
184
+ color: enemyTypes[typeIndex].color,
185
+ points: enemyTypes[typeIndex].points,
186
+ speed: 1 + level * 0.2,
187
+ shootChance: 0.005 + (level * 0.001)
188
+ });
189
+ }
190
+ }
191
+
192
+ // Add mothership (random position top row)
193
+ if (level % 3 === 0) {
194
+ enemies.push({
195
+ x: Math.random() * (canvas.width - 60),
196
+ y: 30,
197
+ width: enemyTypes[3].width,
198
+ height: enemyTypes[3].height,
199
+ color: enemyTypes[3].color,
200
+ points: enemyTypes[3].points,
201
+ speed: 1.5 + level * 0.1,
202
+ shootChance: 0.01 + (level * 0.002)
203
+ });
204
+ }
205
+ }
206
+
207
+ // Update displays
208
+ function updateDisplays() {
209
+ scoreDisplay.textContent = score;
210
+ livesDisplay.textContent = lives;
211
+ levelDisplay.textContent = level;
212
+ }
213
+
214
+ // Game loop
215
+ function gameLoop() {
216
+ if (!gameRunning) return;
217
+
218
+ // Clear canvas
219
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
220
+
221
+ // Update and draw player
222
+ updatePlayer();
223
+ drawPlayer();
224
+
225
+ // Update and draw enemies
226
+ updateEnemies();
227
+ drawEnemies();
228
+
229
+ // Update and draw bullets
230
+ updateBullets();
231
+ drawBullets();
232
+
233
+ // Draw explosions
234
+ drawExplosions();
235
+
236
+ // Check for level completion
237
+ if (enemies.length === 0) {
238
+ levelComplete();
239
+ return;
240
+ }
241
+
242
+ // Check for game over
243
+ if (lives <= 0) {
244
+ gameOver();
245
+ return;
246
+ }
247
+
248
+ // Continue game loop
249
+ requestAnimationFrame(gameLoop);
250
+ }
251
+
252
+ // Update player position
253
+ function updatePlayer() {
254
+ if (keys.ArrowLeft && player.x > 0) {
255
+ player.x -= player.speed;
256
+ }
257
+ if (keys.ArrowRight && player.x < canvas.width - player.width) {
258
+ player.x += player.speed;
259
+ }
260
+
261
+ // Player shooting
262
+ if (keys[' ']) {
263
+ keys[' '] = false; // Prevent continuous shooting
264
+ playerBullets.push({
265
+ x: player.x + player.width / 2 - 2,
266
+ y: player.y,
267
+ width: 4,
268
+ height: 15,
269
+ speed: 10,
270
+ color: '#FFFFFF'
271
+ });
272
+
273
+ // Play shoot sound
274
+ playSound('shoot');
275
+ }
276
+ }
277
+
278
+ // Draw player
279
+ function drawPlayer() {
280
+ ctx.fillStyle = player.color;
281
+ // Main body
282
+ ctx.fillRect(player.x, player.y, player.width, player.height);
283
+ // Cannon
284
+ ctx.fillRect(player.x + player.width / 2 - 5, player.y - 10, 10, 10);
285
+ }
286
+
287
+ // Update enemies
288
+ function updateEnemies() {
289
+ let directionChange = false;
290
+
291
+ // Move enemies and check for direction change
292
+ enemies.forEach(enemy => {
293
+ enemy.x += enemy.speed;
294
+
295
+ // Check if any enemy hits the edge
296
+ if ((enemy.x + enemy.width > canvas.width || enemy.x < 0) && !directionChange) {
297
+ directionChange = true;
298
+ }
299
+ });
300
+
301
+ // Change direction and move down if edge hit
302
+ if (directionChange) {
303
+ enemies.forEach(enemy => {
304
+ enemy.speed = -enemy.speed;
305
+ enemy.y += 20;
306
+ });
307
+ }
308
+
309
+ // Enemy shooting
310
+ enemies.forEach(enemy => {
311
+ if (Math.random() < enemy.shootChance) {
312
+ enemyBullets.push({
313
+ x: enemy.x + enemy.width / 2 - 2,
314
+ y: enemy.y + enemy.height,
315
+ width: 4,
316
+ height: 15,
317
+ speed: 5 + level * 0.5,
318
+ color: '#F87171' // Red bullets
319
+ });
320
+ }
321
+ });
322
+
323
+ // Check if any enemy reached the bottom
324
+ enemies.forEach(enemy => {
325
+ if (enemy.y + enemy.height > canvas.height - 50) {
326
+ lives = 0; // Instant game over
327
+ }
328
+ });
329
+ }
330
+
331
+ // Draw enemies
332
+ function drawEnemies() {
333
+ enemies.forEach(enemy => {
334
+ ctx.fillStyle = enemy.color;
335
+ // Main body
336
+ ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
337
+ // Eyes
338
+ ctx.fillStyle = '#000000';
339
+ ctx.fillRect(enemy.x + 10, enemy.y + 10, 8, 8);
340
+ ctx.fillRect(enemy.x + enemy.width - 18, enemy.y + 10, 8, 8);
341
+
342
+ // Special decoration for mothership
343
+ if (enemy.width === 60) {
344
+ ctx.fillStyle = '#FFFFFF';
345
+ ctx.fillRect(enemy.x + 15, enemy.y - 5, 30, 5);
346
+ }
347
+ });
348
+ }
349
+
350
+ // Update bullets
351
+ function updateBullets() {
352
+ // Player bullets
353
+ for (let i = playerBullets.length - 1; i >= 0; i--) {
354
+ playerBullets[i].y -= playerBullets[i].speed;
355
+
356
+ // Remove if off screen
357
+ if (playerBullets[i].y < 0) {
358
+ playerBullets.splice(i, 1);
359
+ continue;
360
+ }
361
+
362
+ // Check for enemy hits
363
+ for (let j = enemies.length - 1; j >= 0; j--) {
364
+ if (checkCollision(playerBullets[i], enemies[j])) {
365
+ // Add explosion
366
+ explosions.push({
367
+ x: enemies[j].x + enemies[j].width / 2,
368
+ y: enemies[j].y + enemies[j].height / 2,
369
+ size: Math.max(enemies[j].width, enemies[j].height),
370
+ color: enemies[j].color,
371
+ time: 0
372
+ });
373
+
374
+ // Increase score
375
+ score += enemies[j].points;
376
+ updateDisplays();
377
+
378
+ // Play explosion sound
379
+ playSound('explosion');
380
+
381
+ // Remove enemy and bullet
382
+ enemies.splice(j, 1);
383
+ playerBullets.splice(i, 1);
384
+ break;
385
+ }
386
+ }
387
+ }
388
+
389
+ // Enemy bullets
390
+ for (let i = enemyBullets.length - 1; i >= 0; i--) {
391
+ enemyBullets[i].y += enemyBullets[i].speed;
392
+
393
+ // Remove if off screen
394
+ if (enemyBullets[i].y > canvas.height) {
395
+ enemyBullets.splice(i, 1);
396
+ continue;
397
+ }
398
+
399
+ // Check for player hit
400
+ if (checkCollision(enemyBullets[i], player)) {
401
+ // Add explosion
402
+ explosions.push({
403
+ x: player.x + player.width / 2,
404
+ y: player.y + player.height / 2,
405
+ size: 30,
406
+ color: player.color,
407
+ time: 0
408
+ });
409
+
410
+ // Play explosion sound
411
+ playSound('explosion');
412
+
413
+ // Decrease lives
414
+ lives--;
415
+ updateDisplays();
416
+
417
+ // Remove bullet
418
+ enemyBullets.splice(i, 1);
419
+
420
+ // Short invincibility
421
+ if (lives > 0) {
422
+ player.color = '#93C5FD'; // Light blue for invincibility
423
+ setTimeout(() => {
424
+ player.color = '#3B82F6'; // Back to normal
425
+ }, 1000);
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ // Draw bullets
432
+ function drawBullets() {
433
+ // Player bullets
434
+ playerBullets.forEach(bullet => {
435
+ ctx.fillStyle = bullet.color;
436
+ ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
437
+ });
438
+
439
+ // Enemy bullets
440
+ enemyBullets.forEach(bullet => {
441
+ ctx.fillStyle = bullet.color;
442
+ ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
443
+ });
444
+ }
445
+
446
+ // Draw explosions
447
+ function drawExplosions() {
448
+ for (let i = explosions.length - 1; i >= 0; i--) {
449
+ explosions[i].time++;
450
+
451
+ const progress = explosions[i].time / 30;
452
+ const size = explosions[i].size * (0.5 + progress * 1.5);
453
+ const alpha = 1 - progress;
454
+
455
+ ctx.save();
456
+ ctx.globalAlpha = alpha;
457
+ ctx.fillStyle = explosions[i].color;
458
+
459
+ // Draw explosion particles
460
+ for (let j = 0; j < 8; j++) {
461
+ const angle = (j / 8) * Math.PI * 2;
462
+ const distance = size * 0.3 * progress;
463
+ const px = explosions[i].x + Math.cos(angle) * distance;
464
+ const py = explosions[i].y + Math.sin(angle) * distance;
465
+ ctx.fillRect(px - 2, py - 2, 4, 4);
466
+ }
467
+
468
+ ctx.restore();
469
+
470
+ // Remove if animation complete
471
+ if (explosions[i].time >= 30) {
472
+ explosions.splice(i, 1);
473
+ }
474
+ }
475
+ }
476
+
477
+ // Check collision between two objects
478
+ function checkCollision(obj1, obj2) {
479
+ return obj1.x < obj2.x + obj2.width &&
480
+ obj1.x + obj1.width > obj2.x &&
481
+ obj1.y < obj2.y + obj2.height &&
482
+ obj1.y + obj1.height > obj2.y;
483
+ }
484
+
485
+ // Game over
486
+ function gameOver() {
487
+ gameRunning = false;
488
+ finalScoreDisplay.textContent = score;
489
+ gameOverScreen.classList.remove('hidden');
490
+ gameOverScreen.classList.add('flex');
491
+
492
+ // Play game over sound
493
+ playSound('gameOver');
494
+ }
495
+
496
+ // Level complete
497
+ function levelComplete() {
498
+ gameRunning = false;
499
+ nextLevelDisplay.textContent = level + 1;
500
+ levelCompleteScreen.classList.remove('hidden');
501
+ levelCompleteScreen.classList.add('flex');
502
+
503
+ // Play level complete sound
504
+ playSound('levelComplete');
505
+ }
506
+
507
+ // Start next level
508
+ function nextLevel() {
509
+ level++;
510
+ updateDisplays();
511
+ createEnemies();
512
+ gameRunning = true;
513
+ levelCompleteScreen.classList.add('hidden');
514
+ levelCompleteScreen.classList.remove('flex');
515
+ gameLoop();
516
+ }
517
+
518
+ // Simple sound effects
519
+ function playSound(type) {
520
+ const sounds = {
521
+ shoot: {
522
+ frequency: 220,
523
+ type: 'square',
524
+ duration: 0.1
525
+ },
526
+ explosion: {
527
+ frequency: 100,
528
+ type: 'sawtooth',
529
+ duration: 0.3
530
+ },
531
+ gameOver: {
532
+ frequency: 110,
533
+ type: 'sine',
534
+ duration: 1.5
535
+ },
536
+ levelComplete: {
537
+ frequency: 523.25,
538
+ type: 'sine',
539
+ duration: 0.2
540
+ }
541
+ };
542
+
543
+ if (sounds[type]) {
544
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
545
+ const oscillator = audioCtx.createOscillator();
546
+ const gainNode = audioCtx.createGain();
547
+
548
+ oscillator.type = sounds[type].type;
549
+ oscillator.frequency.value = sounds[type].frequency;
550
+ oscillator.connect(gainNode);
551
+ gainNode.connect(audioCtx.destination);
552
+
553
+ // For explosion, add some randomness
554
+ if (type === 'explosion') {
555
+ oscillator.frequency.exponentialRampToValueAtTime(
556
+ 50,
557
+ audioCtx.currentTime + sounds[type].duration
558
+ );
559
+ }
560
+
561
+ // For game over, make it descending
562
+ if (type === 'gameOver') {
563
+ oscillator.frequency.exponentialRampToValueAtTime(
564
+ 55,
565
+ audioCtx.currentTime + sounds[type].duration
566
+ );
567
+ }
568
+
569
+ // For level complete, make it a little melody
570
+ if (type === 'levelComplete') {
571
+ oscillator.frequency.setValueAtTime(523.25, audioCtx.currentTime);
572
+ oscillator.frequency.setValueAtTime(659.25, audioCtx.currentTime + 0.1);
573
+ oscillator.frequency.setValueAtTime(783.99, audioCtx.currentTime + 0.2);
574
+ }
575
+
576
+ gainNode.gain.exponentialRampToValueAtTime(
577
+ 0.001,
578
+ audioCtx.currentTime + sounds[type].duration
579
+ );
580
+
581
+ oscillator.start();
582
+ oscillator.stop(audioCtx.currentTime + sounds[type].duration);
583
+ }
584
+ }
585
+
586
+ // Event listeners
587
+ startButton.addEventListener('click', () => {
588
+ startScreen.classList.add('hidden');
589
+ initGame();
590
+ });
591
+
592
+ restartButton.addEventListener('click', () => {
593
+ gameOverScreen.classList.add('hidden');
594
+ gameOverScreen.classList.remove('flex');
595
+ initGame();
596
+ });
597
+
598
+ nextLevelButton.addEventListener('click', () => {
599
+ nextLevel();
600
+ });
601
+
602
+ // Keyboard controls - FIXED SPACE KEY ISSUE
603
+ window.addEventListener('keydown', (e) => {
604
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === ' ') {
605
+ e.preventDefault();
606
+ }
607
+
608
+ if (e.key === 'ArrowLeft') keys.ArrowLeft = true;
609
+ if (e.key === 'ArrowRight') keys.ArrowRight = true;
610
+ if (e.key === ' ') keys[' '] = true;
611
+ });
612
+
613
+ window.addEventListener('keyup', (e) => {
614
+ if (e.key === 'ArrowLeft') keys.ArrowLeft = false;
615
+ if (e.key === 'ArrowRight') keys.ArrowRight = false;
616
+ if (e.key === ' ') keys[' '] = false;
617
+ });
618
+
619
+ // Touch controls for mobile
620
+ let touchStartX = 0;
621
+
622
+ canvas.addEventListener('touchstart', (e) => {
623
+ e.preventDefault();
624
+ touchStartX = e.touches[0].clientX;
625
+ keys[' '] = true; // Shoot on touch
626
+ });
627
+
628
+ canvas.addEventListener('touchmove', (e) => {
629
+ e.preventDefault();
630
+ const touchX = e.touches[0].clientX;
631
+ if (touchX < touchStartX - 10) {
632
+ keys.ArrowLeft = true;
633
+ keys.ArrowRight = false;
634
+ } else if (touchX > touchStartX + 10) {
635
+ keys.ArrowRight = true;
636
+ keys.ArrowLeft = false;
637
+ }
638
+ });
639
+
640
+ canvas.addEventListener('touchend', (e) => {
641
+ e.preventDefault();
642
+ keys.ArrowLeft = false;
643
+ keys.ArrowRight = false;
644
+ keys[' '] = false;
645
+ });
646
+ </script>
647
+ <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=drdata/space-invaders" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
648
+ </html>