adeism commited on
Commit
0a94a0c
·
verified ·
1 Parent(s): 91fe8a1

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1201 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Rocket Game
3
- emoji: 📊
4
- colorFrom: blue
5
- colorTo: green
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: rocket-game
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: purple
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,1201 @@
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>Galaxy Defender</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ overflow: hidden;
12
+ background-color: #000;
13
+ cursor: none;
14
+ }
15
+ #gameCanvas {
16
+ display: block;
17
+ background: linear-gradient(to bottom, #000428, #004e92);
18
+ }
19
+ #gameUI {
20
+ position: absolute;
21
+ top: 10px;
22
+ left: 10px;
23
+ color: white;
24
+ font-family: 'Arial', sans-serif;
25
+ pointer-events: none;
26
+ }
27
+ #startScreen, #gameOverScreen, #levelCompleteScreen {
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ display: flex;
34
+ flex-direction: column;
35
+ justify-content: center;
36
+ align-items: center;
37
+ background-color: rgba(0, 0, 0, 0.7);
38
+ color: white;
39
+ font-family: 'Arial', sans-serif;
40
+ }
41
+ #gameOverScreen, #levelCompleteScreen {
42
+ display: none;
43
+ }
44
+ .custom-cursor {
45
+ position: absolute;
46
+ width: 20px;
47
+ height: 20px;
48
+ background-color: rgba(255, 255, 255, 0.7);
49
+ border-radius: 50%;
50
+ pointer-events: none;
51
+ z-index: 9999;
52
+ transform: translate(-50%, -50%);
53
+ mix-blend-mode: difference;
54
+ }
55
+ .powerup {
56
+ animation: pulse 1s infinite;
57
+ }
58
+ @keyframes pulse {
59
+ 0% { transform: scale(1); }
60
+ 50% { transform: scale(1.2); }
61
+ 100% { transform: scale(1); }
62
+ }
63
+ </style>
64
+ </head>
65
+ <body>
66
+ <canvas id="gameCanvas"></canvas>
67
+
68
+ <div id="gameUI">
69
+ <div class="flex space-x-4">
70
+ <div class="bg-gray-800 bg-opacity-70 px-4 py-2 rounded">
71
+ Level: <span id="levelDisplay">1</span>
72
+ </div>
73
+ <div class="bg-gray-800 bg-opacity-70 px-4 py-2 rounded">
74
+ Score: <span id="scoreDisplay">0</span>
75
+ </div>
76
+ <div class="bg-gray-800 bg-opacity-70 px-4 py-2 rounded">
77
+ Health: <span id="healthDisplay">100</span>%
78
+ </div>
79
+ <div class="bg-gray-800 bg-opacity-70 px-4 py-2 rounded">
80
+ Weapon: <span id="weaponDisplay">Basic</span>
81
+ </div>
82
+ <div class="bg-gray-800 bg-opacity-70 px-4 py-2 rounded">
83
+ Bombs: <span id="bombDisplay">3</span>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <div id="startScreen">
89
+ <h1 class="text-5xl font-bold mb-6 text-blue-400">GALAXY DEFENDER</h1>
90
+ <p class="text-xl mb-8 text-gray-300">Defend the galaxy against alien invaders!</p>
91
+ <button id="startButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-full text-xl transition-all duration-300 transform hover:scale-110">
92
+ START MISSION
93
+ </button>
94
+ <div class="mt-8 text-gray-400">
95
+ <p>Controls: Move with mouse | Hold left click to shoot | Right click for bomb</p>
96
+ <p class="mt-2">Power-ups will appear during gameplay</p>
97
+ </div>
98
+ </div>
99
+
100
+ <div id="gameOverScreen">
101
+ <h1 class="text-5xl font-bold mb-6 text-red-400">MISSION FAILED</h1>
102
+ <p class="text-xl mb-4">Your score: <span id="finalScore">0</span></p>
103
+ <p class="text-xl mb-8">Level reached: <span id="finalLevel">1</span></p>
104
+ <button id="restartButton" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-full text-xl transition-all duration-300 transform hover:scale-110">
105
+ TRY AGAIN
106
+ </button>
107
+ </div>
108
+
109
+ <div id="levelCompleteScreen">
110
+ <h1 class="text-5xl font-bold mb-6 text-green-400">LEVEL COMPLETE!</h1>
111
+ <p class="text-xl mb-8">Preparing for next mission...</p>
112
+ <div class="w-64 h-3 bg-gray-700 rounded-full overflow-hidden">
113
+ <div id="levelProgress" class="h-full bg-green-500 transition-all duration-1000"></div>
114
+ </div>
115
+ </div>
116
+
117
+ <div class="custom-cursor" id="customCursor"></div>
118
+
119
+ <script>
120
+ // Game variables
121
+ const canvas = document.getElementById('gameCanvas');
122
+ const ctx = canvas.getContext('2d');
123
+ const startScreen = document.getElementById('startScreen');
124
+ const gameOverScreen = document.getElementById('gameOverScreen');
125
+ const levelCompleteScreen = document.getElementById('levelCompleteScreen');
126
+ const startButton = document.getElementById('startButton');
127
+ const restartButton = document.getElementById('restartButton');
128
+ const scoreDisplay = document.getElementById('scoreDisplay');
129
+ const healthDisplay = document.getElementById('healthDisplay');
130
+ const levelDisplay = document.getElementById('levelDisplay');
131
+ const weaponDisplay = document.getElementById('weaponDisplay');
132
+ const bombDisplay = document.getElementById('bombDisplay');
133
+ const finalScore = document.getElementById('finalScore');
134
+ const finalLevel = document.getElementById('finalLevel');
135
+ const levelProgress = document.getElementById('levelProgress');
136
+ const customCursor = document.getElementById('customCursor');
137
+
138
+ // Set canvas size
139
+ canvas.width = window.innerWidth;
140
+ canvas.height = window.innerHeight;
141
+
142
+ // Game state
143
+ let gameRunning = false;
144
+ let score = 0;
145
+ let health = 100;
146
+ let level = 1;
147
+ let enemies = [];
148
+ let bullets = [];
149
+ let enemyBullets = [];
150
+ let powerups = [];
151
+ let explosions = [];
152
+ let bombs = [];
153
+ let lastEnemySpawn = 0;
154
+ let lastPowerupSpawn = 0;
155
+ let enemiesDefeated = 0;
156
+ let enemiesToDefeat = 10;
157
+ let currentWeapon = 'basic';
158
+ let weaponCooldown = 0;
159
+ let bombCount = 3;
160
+ let isMouseDown = false;
161
+ let lastShotTime = 0;
162
+ let mouseX = canvas.width / 2;
163
+ let mouseY = canvas.height / 2;
164
+ let player = {
165
+ x: canvas.width / 2,
166
+ y: canvas.height - 100,
167
+ width: 50,
168
+ height: 60,
169
+ speed: 8
170
+ };
171
+
172
+ // Weapon types
173
+ const weapons = {
174
+ basic: {
175
+ cooldown: 15,
176
+ damage: 10,
177
+ color: '#00FFFF',
178
+ size: 5,
179
+ speed: 10,
180
+ spread: 0
181
+ },
182
+ double: {
183
+ cooldown: 20,
184
+ damage: 10,
185
+ color: '#FF00FF',
186
+ size: 5,
187
+ speed: 10,
188
+ spread: 0.2
189
+ },
190
+ laser: {
191
+ cooldown: 5,
192
+ damage: 5,
193
+ color: '#FFFF00',
194
+ size: 3,
195
+ speed: 15,
196
+ spread: 0
197
+ },
198
+ plasma: {
199
+ cooldown: 40,
200
+ damage: 25,
201
+ color: '#FF4500',
202
+ size: 8,
203
+ speed: 7,
204
+ spread: 0
205
+ }
206
+ };
207
+
208
+ // Enemy types
209
+ const enemyTypes = [
210
+ {
211
+ name: 'Scout',
212
+ width: 40,
213
+ height: 40,
214
+ speed: 2,
215
+ health: 30,
216
+ color: '#FF5555',
217
+ score: 50,
218
+ fireRate: 120,
219
+ bulletSpeed: 5,
220
+ bulletColor: '#FF0000',
221
+ movementPattern: 'straight',
222
+ directionChangeRate: 1000
223
+ },
224
+ {
225
+ name: 'Fighter',
226
+ width: 50,
227
+ height: 50,
228
+ speed: 3,
229
+ health: 50,
230
+ color: '#FFAA00',
231
+ score: 100,
232
+ fireRate: 90,
233
+ bulletSpeed: 6,
234
+ bulletColor: '#FF6600',
235
+ movementPattern: 'zigzag',
236
+ directionChangeRate: 800
237
+ },
238
+ {
239
+ name: 'Bomber',
240
+ width: 70,
241
+ height: 60,
242
+ speed: 1.5,
243
+ health: 100,
244
+ color: '#AA55FF',
245
+ score: 200,
246
+ fireRate: 150,
247
+ bulletSpeed: 4,
248
+ bulletColor: '#AA00FF',
249
+ movementPattern: 'sinusoidal',
250
+ directionChangeRate: 500
251
+ },
252
+ {
253
+ name: 'Elite',
254
+ width: 45,
255
+ height: 45,
256
+ speed: 4,
257
+ health: 80,
258
+ color: '#00FFAA',
259
+ score: 250,
260
+ fireRate: 60,
261
+ bulletSpeed: 7,
262
+ bulletColor: '#00FF00',
263
+ movementPattern: 'random',
264
+ directionChangeRate: 300
265
+ }
266
+ ];
267
+
268
+ // Powerup types
269
+ const powerupTypes = [
270
+ {
271
+ name: 'Health',
272
+ color: '#00FF00',
273
+ effect: (game) => {
274
+ game.health = Math.min(100, game.health + 30);
275
+ createFloatingText('+30 Health', game.player.x, game.player.y, '#00FF00');
276
+ }
277
+ },
278
+ {
279
+ name: 'Double Shot',
280
+ color: '#FF00FF',
281
+ effect: (game) => {
282
+ game.currentWeapon = 'double';
283
+ setTimeout(() => {
284
+ if (game.currentWeapon === 'double') {
285
+ game.currentWeapon = 'basic';
286
+ createFloatingText('Double Shot Ended', game.player.x, game.player.y, '#FF00FF');
287
+ }
288
+ }, 10000);
289
+ createFloatingText('Double Shot!', game.player.x, game.player.y, '#FF00FF');
290
+ }
291
+ },
292
+ {
293
+ name: 'Laser',
294
+ color: '#FFFF00',
295
+ effect: (game) => {
296
+ game.currentWeapon = 'laser';
297
+ setTimeout(() => {
298
+ if (game.currentWeapon === 'laser') {
299
+ game.currentWeapon = 'basic';
300
+ createFloatingText('Laser Ended', game.player.x, game.player.y, '#FFFF00');
301
+ }
302
+ }, 10000);
303
+ createFloatingText('Rapid Laser!', game.player.x, game.player.y, '#FFFF00');
304
+ }
305
+ },
306
+ {
307
+ name: 'Plasma',
308
+ color: '#FF4500',
309
+ effect: (game) => {
310
+ game.currentWeapon = 'plasma';
311
+ setTimeout(() => {
312
+ if (game.currentWeapon === 'plasma') {
313
+ game.currentWeapon = 'basic';
314
+ createFloatingText('Plasma Ended', game.player.x, game.player.y, '#FF4500');
315
+ }
316
+ }, 10000);
317
+ createFloatingText('Plasma Cannon!', game.player.x, game.player.y, '#FF4500');
318
+ }
319
+ },
320
+ {
321
+ name: 'Bomb',
322
+ color: '#FFFFFF',
323
+ effect: (game) => {
324
+ game.bombCount = Math.min(5, game.bombCount + 1);
325
+ createFloatingText('+1 Bomb', game.player.x, game.player.y, '#FFFFFF');
326
+ updateUI();
327
+ }
328
+ }
329
+ ];
330
+
331
+ // Floating text for effects
332
+ let floatingTexts = [];
333
+
334
+ function createFloatingText(text, x, y, color) {
335
+ floatingTexts.push({
336
+ text,
337
+ x,
338
+ y,
339
+ color,
340
+ alpha: 1,
341
+ lifetime: 60
342
+ });
343
+ }
344
+
345
+ // Initialize game
346
+ function initGame() {
347
+ gameRunning = true;
348
+ score = 0;
349
+ health = 100;
350
+ level = 1;
351
+ enemies = [];
352
+ bullets = [];
353
+ enemyBullets = [];
354
+ powerups = [];
355
+ explosions = [];
356
+ bombs = [];
357
+ floatingTexts = [];
358
+ enemiesDefeated = 0;
359
+ enemiesToDefeat = 10;
360
+ currentWeapon = 'basic';
361
+ bombCount = 3;
362
+ player.x = canvas.width / 2;
363
+ player.y = canvas.height - 100;
364
+
365
+ updateUI();
366
+ startScreen.style.display = 'none';
367
+ gameOverScreen.style.display = 'none';
368
+ levelCompleteScreen.style.display = 'none';
369
+
370
+ // Start game loop
371
+ requestAnimationFrame(gameLoop);
372
+ }
373
+
374
+ // Game loop
375
+ function gameLoop(timestamp) {
376
+ if (!gameRunning) return;
377
+
378
+ // Clear canvas
379
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
380
+
381
+ // Draw starfield background
382
+ drawBackground();
383
+
384
+ // Update and draw player
385
+ updatePlayer();
386
+ drawPlayer();
387
+
388
+ // Spawn enemies
389
+ if (timestamp - lastEnemySpawn > 1000 - (level * 50) && enemies.length < 5 + Math.floor(level / 2)) {
390
+ spawnEnemy();
391
+ lastEnemySpawn = timestamp;
392
+ }
393
+
394
+ // Spawn powerups
395
+ if (timestamp - lastPowerupSpawn > 15000 && Math.random() < 0.02) {
396
+ spawnPowerup();
397
+ lastPowerupSpawn = timestamp;
398
+ }
399
+
400
+ // Continuous firing when mouse is held down
401
+ if (isMouseDown && timestamp - lastShotTime > weapons[currentWeapon].cooldown) {
402
+ shoot();
403
+ lastShotTime = timestamp;
404
+ }
405
+
406
+ // Update and draw enemies
407
+ updateEnemies(timestamp);
408
+ drawEnemies();
409
+
410
+ // Update and draw bullets
411
+ updateBullets();
412
+ drawBullets();
413
+
414
+ // Update and draw enemy bullets
415
+ updateEnemyBullets();
416
+ drawEnemyBullets();
417
+
418
+ // Update and draw powerups
419
+ updatePowerups();
420
+ drawPowerups();
421
+
422
+ // Update and draw explosions
423
+ updateExplosions();
424
+ drawExplosions();
425
+
426
+ // Update and draw bombs
427
+ updateBombs();
428
+ drawBombs();
429
+
430
+ // Update and draw floating text
431
+ updateFloatingTexts();
432
+ drawFloatingTexts();
433
+
434
+ // Check level completion
435
+ if (enemiesDefeated >= enemiesToDefeat) {
436
+ levelComplete();
437
+ return;
438
+ }
439
+
440
+ // Check game over
441
+ if (health <= 0) {
442
+ gameOver();
443
+ return;
444
+ }
445
+
446
+ // Update weapon cooldown
447
+ if (weaponCooldown > 0) {
448
+ weaponCooldown--;
449
+ }
450
+
451
+ // Continue game loop
452
+ requestAnimationFrame(gameLoop);
453
+ }
454
+
455
+ // Draw starfield background
456
+ function drawBackground() {
457
+ // Draw gradient background
458
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
459
+ gradient.addColorStop(0, '#000428');
460
+ gradient.addColorStop(1, '#004e92');
461
+ ctx.fillStyle = gradient;
462
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
463
+
464
+ // Draw stars
465
+ for (let i = 0; i < 100; i++) {
466
+ const x = (i * canvas.width / 100 + Date.now() / 1000 * 10) % canvas.width;
467
+ const y = (i * canvas.height / 100 + Date.now() / 500 * 5) % canvas.height;
468
+ const size = Math.random() * 2;
469
+ const opacity = Math.random() * 0.8 + 0.2;
470
+
471
+ ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
472
+ ctx.beginPath();
473
+ ctx.arc(x, y, size, 0, Math.PI * 2);
474
+ ctx.fill();
475
+ }
476
+ }
477
+
478
+ // Update player position based on mouse
479
+ function updatePlayer() {
480
+ // Smooth movement towards mouse
481
+ player.x += (mouseX - player.x) * 0.1;
482
+ player.y += (mouseY - player.y) * 0.1;
483
+
484
+ // Keep player within bounds
485
+ player.x = Math.max(player.width / 2, Math.min(canvas.width - player.width / 2, player.x));
486
+ player.y = Math.max(player.height / 2, Math.min(canvas.height - player.height / 2, player.y));
487
+ }
488
+
489
+ // Draw player ship
490
+ function drawPlayer() {
491
+ ctx.save();
492
+ ctx.translate(player.x, player.y);
493
+
494
+ // Draw ship body
495
+ ctx.fillStyle = '#3498db';
496
+ ctx.beginPath();
497
+ ctx.moveTo(0, -player.height / 2);
498
+ ctx.lineTo(-player.width / 2, player.height / 2);
499
+ ctx.lineTo(player.width / 2, player.height / 2);
500
+ ctx.closePath();
501
+ ctx.fill();
502
+
503
+ // Draw ship details
504
+ ctx.fillStyle = '#2980b9';
505
+ ctx.beginPath();
506
+ ctx.moveTo(0, -player.height / 4);
507
+ ctx.lineTo(-player.width / 4, player.height / 4);
508
+ ctx.lineTo(player.width / 4, player.height / 4);
509
+ ctx.closePath();
510
+ ctx.fill();
511
+
512
+ // Draw engine glow
513
+ if (Math.random() < 0.3) {
514
+ ctx.fillStyle = '#e74c3c';
515
+ ctx.beginPath();
516
+ ctx.moveTo(-player.width / 4, player.height / 2);
517
+ ctx.lineTo(0, player.height / 2 + 10 + Math.random() * 5);
518
+ ctx.lineTo(player.width / 4, player.height / 2);
519
+ ctx.closePath();
520
+ ctx.fill();
521
+ }
522
+
523
+ ctx.restore();
524
+ }
525
+
526
+ // Spawn a new enemy
527
+ function spawnEnemy() {
528
+ const typeIndex = Math.min(Math.floor(Math.random() * (level / 2)), enemyTypes.length - 1);
529
+ const type = enemyTypes[typeIndex];
530
+
531
+ const enemy = {
532
+ x: Math.random() * (canvas.width - type.width) + type.width / 2,
533
+ y: -type.height,
534
+ width: type.width,
535
+ height: type.height,
536
+ speed: type.speed,
537
+ health: type.health,
538
+ maxHealth: type.health,
539
+ color: type.color,
540
+ score: type.score,
541
+ type: typeIndex,
542
+ lastShot: 0,
543
+ fireRate: type.fireRate,
544
+ bulletSpeed: type.bulletSpeed,
545
+ bulletColor: type.bulletColor,
546
+ movementPattern: type.movementPattern,
547
+ directionChangeRate: type.directionChangeRate,
548
+ lastDirectionChange: 0,
549
+ directionX: Math.random() > 0.5 ? 1 : -1,
550
+ directionY: 1,
551
+ angle: 0,
552
+ amplitude: Math.random() * 50 + 50,
553
+ frequency: Math.random() * 0.01 + 0.01
554
+ };
555
+
556
+ enemies.push(enemy);
557
+ }
558
+
559
+ // Update all enemies
560
+ function updateEnemies(timestamp) {
561
+ for (let i = enemies.length - 1; i >= 0; i--) {
562
+ const enemy = enemies[i];
563
+ const type = enemyTypes[enemy.type];
564
+
565
+ // Move enemy based on movement pattern
566
+ switch(enemy.movementPattern) {
567
+ case 'straight':
568
+ enemy.y += enemy.speed;
569
+ break;
570
+ case 'zigzag':
571
+ if (timestamp - enemy.lastDirectionChange > enemy.directionChangeRate) {
572
+ enemy.directionX = Math.random() > 0.5 ? 1 : -1;
573
+ enemy.lastDirectionChange = timestamp;
574
+ }
575
+ enemy.x += enemy.speed * 0.5 * enemy.directionX;
576
+ enemy.y += enemy.speed * 0.8;
577
+ break;
578
+ case 'sinusoidal':
579
+ enemy.angle += enemy.frequency;
580
+ enemy.x = enemy.x + Math.sin(enemy.angle) * 0.5;
581
+ enemy.y += enemy.speed * 0.7;
582
+ break;
583
+ case 'random':
584
+ if (timestamp - enemy.lastDirectionChange > enemy.directionChangeRate) {
585
+ enemy.directionX = Math.random() * 2 - 1;
586
+ enemy.directionY = Math.random() * 0.5 + 0.5;
587
+ enemy.lastDirectionChange = timestamp;
588
+ }
589
+ enemy.x += enemy.speed * enemy.directionX;
590
+ enemy.y += enemy.speed * enemy.directionY;
591
+ break;
592
+ }
593
+
594
+ // Keep enemies within bounds
595
+ enemy.x = Math.max(enemy.width / 2, Math.min(canvas.width - enemy.width / 2, enemy.x));
596
+
597
+ // Enemy shooting with random spread
598
+ if (timestamp - enemy.lastShot > enemy.fireRate) {
599
+ const spread = Math.random() * 0.5 - 0.25;
600
+ enemyBullets.push({
601
+ x: enemy.x,
602
+ y: enemy.y + enemy.height / 2,
603
+ width: 5,
604
+ height: 10,
605
+ speed: enemy.bulletSpeed,
606
+ damage: 10,
607
+ color: enemy.bulletColor,
608
+ directionX: spread
609
+ });
610
+ enemy.lastShot = timestamp;
611
+ }
612
+
613
+ // Check if enemy is out of bounds
614
+ if (enemy.y > canvas.height + enemy.height) {
615
+ enemies.splice(i, 1);
616
+ }
617
+ }
618
+ }
619
+
620
+ // Draw all enemies
621
+ function drawEnemies() {
622
+ for (const enemy of enemies) {
623
+ ctx.save();
624
+ ctx.translate(enemy.x, enemy.y);
625
+
626
+ // Draw enemy body
627
+ ctx.fillStyle = enemy.color;
628
+ ctx.beginPath();
629
+ ctx.moveTo(0, -enemy.height / 2);
630
+ ctx.lineTo(-enemy.width / 2, enemy.height / 2);
631
+ ctx.lineTo(0, enemy.height / 4);
632
+ ctx.lineTo(enemy.width / 2, enemy.height / 2);
633
+ ctx.closePath();
634
+ ctx.fill();
635
+
636
+ // Draw enemy details
637
+ ctx.fillStyle = '#000';
638
+ ctx.beginPath();
639
+ ctx.arc(0, -enemy.height / 4, enemy.width / 8, 0, Math.PI * 2);
640
+ ctx.fill();
641
+
642
+ // Draw health bar
643
+ const healthPercent = enemy.health / enemy.maxHealth;
644
+ ctx.fillStyle = '#FF0000';
645
+ ctx.fillRect(-enemy.width / 2, -enemy.height / 2 - 10, enemy.width, 5);
646
+ ctx.fillStyle = '#00FF00';
647
+ ctx.fillRect(-enemy.width / 2, -enemy.height / 2 - 10, enemy.width * healthPercent, 5);
648
+
649
+ ctx.restore();
650
+ }
651
+ }
652
+
653
+ // Spawn a powerup
654
+ function spawnPowerup() {
655
+ const typeIndex = Math.floor(Math.random() * powerupTypes.length);
656
+ const type = powerupTypes[typeIndex];
657
+
658
+ const powerup = {
659
+ x: Math.random() * (canvas.width - 30) + 15,
660
+ y: -30,
661
+ width: 30,
662
+ height: 30,
663
+ speed: 2,
664
+ type: typeIndex,
665
+ color: type.color
666
+ };
667
+
668
+ powerups.push(powerup);
669
+ }
670
+
671
+ // Update all powerups
672
+ function updatePowerups() {
673
+ for (let i = powerups.length - 1; i >= 0; i--) {
674
+ const powerup = powerups[i];
675
+
676
+ // Move powerup
677
+ powerup.y += powerup.speed;
678
+
679
+ // Check collision with player
680
+ if (
681
+ powerup.x + powerup.width / 2 > player.x - player.width / 2 &&
682
+ powerup.x - powerup.width / 2 < player.x + player.width / 2 &&
683
+ powerup.y + powerup.height / 2 > player.y - player.height / 2 &&
684
+ powerup.y - powerup.height / 2 < player.y + player.height / 2
685
+ ) {
686
+ // Apply powerup effect
687
+ powerupTypes[powerup.type].effect({
688
+ player,
689
+ health,
690
+ currentWeapon,
691
+ score,
692
+ bombCount
693
+ });
694
+
695
+ // Remove powerup
696
+ powerups.splice(i, 1);
697
+ continue;
698
+ }
699
+
700
+ // Check if powerup is out of bounds
701
+ if (powerup.y > canvas.height + powerup.height) {
702
+ powerups.splice(i, 1);
703
+ }
704
+ }
705
+ }
706
+
707
+ // Draw all powerups
708
+ function drawPowerups() {
709
+ for (const powerup of powerups) {
710
+ ctx.save();
711
+ ctx.translate(powerup.x, powerup.y);
712
+
713
+ // Draw powerup
714
+ ctx.fillStyle = powerup.color;
715
+ ctx.beginPath();
716
+ ctx.arc(0, 0, powerup.width / 2, 0, Math.PI * 2);
717
+ ctx.fill();
718
+
719
+ // Draw plus sign for health
720
+ if (powerup.type === 0) {
721
+ ctx.fillStyle = '#FFF';
722
+ ctx.fillRect(-powerup.width / 4, -2, powerup.width / 2, 4);
723
+ ctx.fillRect(-2, -powerup.width / 4, 4, powerup.width / 2);
724
+ }
725
+ // Draw bomb icon
726
+ else if (powerup.type === 4) {
727
+ ctx.fillStyle = '#000';
728
+ ctx.beginPath();
729
+ ctx.moveTo(0, -powerup.width / 4);
730
+ ctx.lineTo(-powerup.width / 4, powerup.width / 8);
731
+ ctx.lineTo(powerup.width / 4, powerup.width / 8);
732
+ ctx.closePath();
733
+ ctx.fill();
734
+
735
+ ctx.fillStyle = '#FF0000';
736
+ ctx.beginPath();
737
+ ctx.arc(0, -powerup.width / 6, powerup.width / 8, 0, Math.PI * 2);
738
+ ctx.fill();
739
+ }
740
+
741
+ ctx.restore();
742
+ }
743
+ }
744
+
745
+ // Player shooting
746
+ function shoot() {
747
+ if (weaponCooldown > 0) return;
748
+
749
+ const weapon = weapons[currentWeapon];
750
+ weaponCooldown = weapon.cooldown;
751
+
752
+ if (currentWeapon === 'basic' || currentWeapon === 'plasma') {
753
+ // Single shot
754
+ bullets.push({
755
+ x: player.x,
756
+ y: player.y - player.height / 2,
757
+ width: weapon.size,
758
+ height: weapon.size * 2,
759
+ speed: -weapon.speed,
760
+ damage: weapon.damage,
761
+ color: weapon.color
762
+ });
763
+ } else if (currentWeapon === 'double') {
764
+ // Double shot
765
+ bullets.push({
766
+ x: player.x - 15,
767
+ y: player.y - player.height / 2,
768
+ width: weapon.size,
769
+ height: weapon.size * 2,
770
+ speed: -weapon.speed,
771
+ damage: weapon.damage,
772
+ color: weapon.color
773
+ });
774
+ bullets.push({
775
+ x: player.x + 15,
776
+ y: player.y - player.height / 2,
777
+ width: weapon.size,
778
+ height: weapon.size * 2,
779
+ speed: -weapon.speed,
780
+ damage: weapon.damage,
781
+ color: weapon.color
782
+ });
783
+ } else if (currentWeapon === 'laser') {
784
+ // Rapid laser
785
+ bullets.push({
786
+ x: player.x + (Math.random() - 0.5) * 10,
787
+ y: player.y - player.height / 2,
788
+ width: weapon.size,
789
+ height: weapon.size * 2,
790
+ speed: -weapon.speed,
791
+ damage: weapon.damage,
792
+ color: weapon.color
793
+ });
794
+ }
795
+ }
796
+
797
+ // Update all bullets
798
+ function updateBullets() {
799
+ for (let i = bullets.length - 1; i >= 0; i--) {
800
+ const bullet = bullets[i];
801
+
802
+ // Move bullet
803
+ bullet.y += bullet.speed;
804
+
805
+ // Check collision with enemies
806
+ for (let j = enemies.length - 1; j >= 0; j--) {
807
+ const enemy = enemies[j];
808
+
809
+ if (
810
+ bullet.x + bullet.width / 2 > enemy.x - enemy.width / 2 &&
811
+ bullet.x - bullet.width / 2 < enemy.x + enemy.width / 2 &&
812
+ bullet.y + bullet.height / 2 > enemy.y - enemy.height / 2 &&
813
+ bullet.y - bullet.height / 2 < enemy.y + enemy.height / 2
814
+ ) {
815
+ // Damage enemy
816
+ enemy.health -= bullet.damage;
817
+
818
+ // Create explosion
819
+ createExplosion(bullet.x, bullet.y, bullet.color);
820
+
821
+ // Remove bullet
822
+ bullets.splice(i, 1);
823
+
824
+ // Check if enemy is defeated
825
+ if (enemy.health <= 0) {
826
+ // Add score
827
+ score += enemy.score;
828
+ enemiesDefeated++;
829
+
830
+ // Create bigger explosion
831
+ createExplosion(enemy.x, enemy.y, enemy.color, 2);
832
+
833
+ // Remove enemy
834
+ enemies.splice(j, 1);
835
+
836
+ // Random chance to drop powerup
837
+ if (Math.random() < 0.1) {
838
+ spawnPowerup();
839
+ }
840
+ }
841
+
842
+ updateUI();
843
+ break;
844
+ }
845
+ }
846
+
847
+ // Check if bullet is out of bounds
848
+ if (bullet.y < -bullet.height) {
849
+ bullets.splice(i, 1);
850
+ }
851
+ }
852
+ }
853
+
854
+ // Draw all bullets
855
+ function drawBullets() {
856
+ for (const bullet of bullets) {
857
+ ctx.save();
858
+ ctx.translate(bullet.x, bullet.y);
859
+
860
+ // Draw bullet
861
+ ctx.fillStyle = bullet.color;
862
+ ctx.beginPath();
863
+ ctx.rect(-bullet.width / 2, -bullet.height / 2, bullet.width, bullet.height);
864
+ ctx.fill();
865
+
866
+ // Add glow effect
867
+ if (bullet.damage > 10) {
868
+ ctx.fillStyle = `rgba(255, 255, 255, 0.3)`;
869
+ ctx.beginPath();
870
+ ctx.arc(0, 0, bullet.width * 1.5, 0, Math.PI * 2);
871
+ ctx.fill();
872
+ }
873
+
874
+ ctx.restore();
875
+ }
876
+ }
877
+
878
+ // Update all enemy bullets
879
+ function updateEnemyBullets() {
880
+ for (let i = enemyBullets.length - 1; i >= 0; i--) {
881
+ const bullet = enemyBullets[i];
882
+
883
+ // Move bullet with possible spread
884
+ bullet.y += bullet.speed;
885
+ bullet.x += bullet.directionX * 2;
886
+
887
+ // Check collision with player
888
+ if (
889
+ bullet.x + bullet.width / 2 > player.x - player.width / 2 &&
890
+ bullet.x - bullet.width / 2 < player.x + player.width / 2 &&
891
+ bullet.y + bullet.height / 2 > player.y - player.height / 2 &&
892
+ bullet.y - bullet.height / 2 < player.y + player.height / 2
893
+ ) {
894
+ // Damage player
895
+ health -= 10;
896
+
897
+ // Create explosion
898
+ createExplosion(bullet.x, bullet.y, bullet.color);
899
+
900
+ // Remove bullet
901
+ enemyBullets.splice(i, 1);
902
+
903
+ updateUI();
904
+ continue;
905
+ }
906
+
907
+ // Check if bullet is out of bounds
908
+ if (bullet.y > canvas.height + bullet.height) {
909
+ enemyBullets.splice(i, 1);
910
+ }
911
+ }
912
+ }
913
+
914
+ // Draw all enemy bullets
915
+ function drawEnemyBullets() {
916
+ for (const bullet of enemyBullets) {
917
+ ctx.save();
918
+ ctx.translate(bullet.x, bullet.y);
919
+
920
+ // Draw bullet
921
+ ctx.fillStyle = bullet.color;
922
+ ctx.beginPath();
923
+ ctx.rect(-bullet.width / 2, -bullet.height / 2, bullet.width, bullet.height);
924
+ ctx.fill();
925
+
926
+ ctx.restore();
927
+ }
928
+ }
929
+
930
+ // Create explosion effect
931
+ function createExplosion(x, y, color, sizeMultiplier = 1) {
932
+ const particleCount = 10 + Math.floor(Math.random() * 10);
933
+
934
+ for (let i = 0; i < particleCount; i++) {
935
+ explosions.push({
936
+ x,
937
+ y,
938
+ radius: Math.random() * 3 * sizeMultiplier + 1,
939
+ color,
940
+ speedX: (Math.random() - 0.5) * 5,
941
+ speedY: (Math.random() - 0.5) * 5,
942
+ alpha: 1,
943
+ lifetime: 30 + Math.random() * 30
944
+ });
945
+ }
946
+ }
947
+
948
+ // Update all explosions
949
+ function updateExplosions() {
950
+ for (let i = explosions.length - 1; i >= 0; i--) {
951
+ const explosion = explosions[i];
952
+
953
+ // Move particle
954
+ explosion.x += explosion.speedX;
955
+ explosion.y += explosion.speedY;
956
+
957
+ // Fade out
958
+ explosion.alpha -= 1 / explosion.lifetime;
959
+
960
+ // Remove when faded out
961
+ if (explosion.alpha <= 0) {
962
+ explosions.splice(i, 1);
963
+ }
964
+ }
965
+ }
966
+
967
+ // Draw all explosions
968
+ function drawExplosions() {
969
+ for (const explosion of explosions) {
970
+ ctx.save();
971
+ ctx.globalAlpha = explosion.alpha;
972
+
973
+ // Draw particle
974
+ ctx.fillStyle = explosion.color;
975
+ ctx.beginPath();
976
+ ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);
977
+ ctx.fill();
978
+
979
+ ctx.restore();
980
+ }
981
+ }
982
+
983
+ // Activate bomb
984
+ function activateBomb() {
985
+ if (bombCount <= 0) return;
986
+
987
+ bombCount--;
988
+ updateUI();
989
+
990
+ // Create a big explosion that covers most of the screen
991
+ createExplosion(player.x, player.y, '#FFFFFF', 5);
992
+
993
+ // Damage all enemies on screen
994
+ for (let i = enemies.length - 1; i >= 0; i--) {
995
+ const enemy = enemies[i];
996
+
997
+ // Damage enemy
998
+ enemy.health -= 50;
999
+
1000
+ // Check if enemy is defeated
1001
+ if (enemy.health <= 0) {
1002
+ // Add score
1003
+ score += enemy.score;
1004
+ enemiesDefeated++;
1005
+
1006
+ // Create bigger explosion
1007
+ createExplosion(enemy.x, enemy.y, enemy.color, 2);
1008
+
1009
+ // Remove enemy
1010
+ enemies.splice(i, 1);
1011
+ }
1012
+ }
1013
+
1014
+ // Clear all enemy bullets
1015
+ enemyBullets = [];
1016
+
1017
+ // Show bomb effect
1018
+ bombs.push({
1019
+ x: player.x,
1020
+ y: player.y,
1021
+ radius: 0,
1022
+ maxRadius: Math.max(canvas.width, canvas.height) * 0.8,
1023
+ color: 'rgba(255, 255, 255, 0.3)',
1024
+ lifetime: 30
1025
+ });
1026
+ }
1027
+
1028
+ // Update all bombs
1029
+ function updateBombs() {
1030
+ for (let i = bombs.length - 1; i >= 0; i--) {
1031
+ const bomb = bombs[i];
1032
+
1033
+ // Expand bomb radius
1034
+ bomb.radius += (bomb.maxRadius - bomb.radius) * 0.1;
1035
+
1036
+ // Fade out
1037
+ bomb.lifetime--;
1038
+
1039
+ // Remove when expired
1040
+ if (bomb.lifetime <= 0) {
1041
+ bombs.splice(i, 1);
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ // Draw all bombs
1047
+ function drawBombs() {
1048
+ for (const bomb of bombs) {
1049
+ ctx.save();
1050
+
1051
+ // Draw bomb shockwave
1052
+ ctx.fillStyle = bomb.color;
1053
+ ctx.beginPath();
1054
+ ctx.arc(bomb.x, bomb.y, bomb.radius, 0, Math.PI * 2);
1055
+ ctx.fill();
1056
+
1057
+ ctx.restore();
1058
+ }
1059
+ }
1060
+
1061
+ // Update floating texts
1062
+ function updateFloatingTexts() {
1063
+ for (let i = floatingTexts.length - 1; i >= 0; i--) {
1064
+ const text = floatingTexts[i];
1065
+
1066
+ // Move up
1067
+ text.y -= 1;
1068
+
1069
+ // Fade out
1070
+ text.alpha -= 1 / text.lifetime;
1071
+
1072
+ // Remove when faded out
1073
+ if (text.alpha <= 0) {
1074
+ floatingTexts.splice(i, 1);
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ // Draw floating texts
1080
+ function drawFloatingTexts() {
1081
+ for (const text of floatingTexts) {
1082
+ ctx.save();
1083
+ ctx.globalAlpha = text.alpha;
1084
+ ctx.font = '16px Arial';
1085
+ ctx.fillStyle = text.color;
1086
+ ctx.textAlign = 'center';
1087
+ ctx.fillText(text.text, text.x, text.y);
1088
+ ctx.restore();
1089
+ }
1090
+ }
1091
+
1092
+ // Update UI displays
1093
+ function updateUI() {
1094
+ scoreDisplay.textContent = score;
1095
+ healthDisplay.textContent = health;
1096
+ levelDisplay.textContent = level;
1097
+ weaponDisplay.textContent = currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1);
1098
+ bombDisplay.textContent = bombCount;
1099
+ }
1100
+
1101
+ // Level complete
1102
+ function levelComplete() {
1103
+ gameRunning = false;
1104
+ levelCompleteScreen.style.display = 'flex';
1105
+
1106
+ // Animate progress bar
1107
+ let progress = 0;
1108
+ const interval = setInterval(() => {
1109
+ progress += 0.01;
1110
+ levelProgress.style.width = `${progress * 100}%`;
1111
+
1112
+ if (progress >= 1) {
1113
+ clearInterval(interval);
1114
+ nextLevel();
1115
+ }
1116
+ }, 10);
1117
+ }
1118
+
1119
+ // Start next level
1120
+ function nextLevel() {
1121
+ level++;
1122
+ enemiesDefeated = 0;
1123
+ enemiesToDefeat = 10 + level * 2;
1124
+ bombCount = Math.min(bombCount + 1, 5); // Add one bomb per level, max 5
1125
+
1126
+ // Clear bullets and powerups
1127
+ bullets = [];
1128
+ enemyBullets = [];
1129
+ powerups = [];
1130
+
1131
+ updateUI();
1132
+ levelCompleteScreen.style.display = 'none';
1133
+ gameRunning = true;
1134
+ requestAnimationFrame(gameLoop);
1135
+ }
1136
+
1137
+ // Game over
1138
+ function gameOver() {
1139
+ gameRunning = false;
1140
+ finalScore.textContent = score;
1141
+ finalLevel.textContent = level;
1142
+ gameOverScreen.style.display = 'flex';
1143
+ }
1144
+
1145
+ // Event listeners
1146
+ startButton.addEventListener('click', initGame);
1147
+ restartButton.addEventListener('click', initGame);
1148
+
1149
+ // Mouse movement
1150
+ document.addEventListener('mousemove', (e) => {
1151
+ mouseX = e.clientX;
1152
+ mouseY = e.clientY;
1153
+
1154
+ // Update custom cursor position
1155
+ customCursor.style.left = `${e.clientX}px`;
1156
+ customCursor.style.top = `${e.clientY}px`;
1157
+ });
1158
+
1159
+ // Mouse click to shoot
1160
+ document.addEventListener('mousedown', (e) => {
1161
+ if (gameRunning) {
1162
+ if (e.button === 0) { // Left click
1163
+ isMouseDown = true;
1164
+ shoot();
1165
+ lastShotTime = Date.now();
1166
+ } else if (e.button === 2) { // Right click
1167
+ activateBomb();
1168
+ }
1169
+ }
1170
+ });
1171
+
1172
+ // Mouse release to stop shooting
1173
+ document.addEventListener('mouseup', (e) => {
1174
+ if (e.button === 0) { // Left click
1175
+ isMouseDown = false;
1176
+ }
1177
+ });
1178
+
1179
+ // Prevent context menu on right click
1180
+ document.addEventListener('contextmenu', (e) => {
1181
+ e.preventDefault();
1182
+ });
1183
+
1184
+ // Window resize
1185
+ window.addEventListener('resize', () => {
1186
+ canvas.width = window.innerWidth;
1187
+ canvas.height = window.innerHeight;
1188
+
1189
+ // Keep player on screen
1190
+ player.x = Math.max(player.width / 2, Math.min(canvas.width - player.width / 2, player.x));
1191
+ player.y = Math.max(player.height / 2, Math.min(canvas.height - player.height / 2, player.y));
1192
+ });
1193
+
1194
+ // Set canvas size on load
1195
+ window.addEventListener('load', () => {
1196
+ canvas.width = window.innerWidth;
1197
+ canvas.height = window.innerHeight;
1198
+ });
1199
+ </script>
1200
+ <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=adeism/rocket-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1201
+ </html>