stroumphs commited on
Commit
b63346e
·
verified ·
1 Parent(s): 00414f2

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +798 -27
index.html CHANGED
@@ -109,32 +109,153 @@
109
  transition: width 0.3s;
110
  }
111
 
112
- #bulletModeBtn {
 
113
  position: absolute;
114
  bottom: 20px;
115
- left: 50%;
116
- transform: translateX(-50%);
117
- padding: 10px 20px;
118
- font-size: 16px;
119
- background: linear-gradient(to bottom, #aa00aa, #880088);
120
  z-index: 5;
121
  }
122
 
123
- #bulletModeBtn:hover {
124
- background: linear-gradient(to bottom, #cc00cc, #aa00aa);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
126
 
127
- .bullet-mode-indicator {
 
 
 
 
 
 
 
 
 
128
  position: absolute;
129
- bottom: 60px;
130
  left: 50%;
131
- transform: translateX(-50%);
 
 
132
  color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  font-size: 14px;
134
- text-shadow: 0 0 5px #ff00ff;
135
  z-index: 5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
  </style>
 
138
  </head>
139
  <body>
140
  <canvas id="gameCanvas"></canvas>
@@ -154,7 +275,10 @@
154
  <p class="instructions">
155
  Defend your sector from the alien invasion!<br>
156
  Use <span class="highlight">mouse</span> to move your ship and <span class="highlight">click</span> to shoot.<br>
157
- Press <span class="highlight">SPACE</span> to switch between spread and parallel bullet modes.<br>
 
 
 
158
  Destroy aliens to increase your bullet power!<br>
159
  <span class="highlight">Every 5 kills</span> gives you an additional bullet (max 5)!
160
  </p>
@@ -168,8 +292,39 @@
168
  <button id="restartButton">TRY AGAIN</button>
169
  </div>
170
 
171
- <button id="bulletModeBtn">SWITCH TO PARALLEL MODE (SPACE)</button>
172
- <div class="bullet-mode-indicator">Current Mode: SPREAD</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  <script>
175
  // Game canvas setup
@@ -185,6 +340,9 @@
185
  let level = 1;
186
  let aliens = [];
187
  let bullets = [];
 
 
 
188
  let particles = [];
189
  let lastAlienSpawn = 0;
190
  let alienSpawnInterval = 2000;
@@ -196,6 +354,40 @@
196
  let bulletMode = 'spread'; // 'spread' or 'parallel'
197
  const MAX_BULLETS = 5;
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  // Player spaceship
200
  const player = {
201
  x: canvas.width / 2,
@@ -220,19 +412,35 @@
220
  const gameOverScreen = document.getElementById('gameOverScreen');
221
  const startButton = document.getElementById('startButton');
222
  const restartButton = document.getElementById('restartButton');
223
- const bulletModeBtn = document.getElementById('bulletModeBtn');
224
- const bulletModeIndicator = document.querySelector('.bullet-mode-indicator');
225
 
226
  // Event listeners
227
  startButton.addEventListener('click', startGame);
228
  restartButton.addEventListener('click', startGame);
229
  canvas.addEventListener('mousemove', movePlayer);
230
  canvas.addEventListener('click', shoot);
231
- bulletModeBtn.addEventListener('click', toggleBulletMode);
 
 
 
 
 
 
 
232
  document.addEventListener('keydown', (e) => {
233
  if (e.code === 'Space') {
234
  e.preventDefault();
235
  toggleBulletMode();
 
 
 
 
 
 
 
 
 
236
  }
237
  });
238
 
@@ -251,7 +459,11 @@
251
  level = 1;
252
  aliens = [];
253
  bullets = [];
 
 
 
254
  particles = [];
 
255
  alienSpawnInterval = 2000;
256
  alienSpeed = 1;
257
  alienHealth = 1;
@@ -260,13 +472,24 @@
260
  maxBulletPower = 1;
261
  bulletMode = 'spread';
262
 
 
 
 
 
 
 
 
 
 
 
 
263
  scoreElement.textContent = score;
264
  livesElement.textContent = lives;
265
  levelElement.textContent = level;
266
  bulletPowerElement.textContent = `${bulletPower}/${MAX_BULLETS}`;
267
  powerFillElement.style.width = '0%';
268
- bulletModeBtn.textContent = 'SWITCH TO PARALLEL MODE (SPACE)';
269
- bulletModeIndicator.textContent = 'Current Mode: SPREAD';
270
 
271
  startScreen.style.display = 'none';
272
  gameOverScreen.style.display = 'none';
@@ -281,17 +504,19 @@
281
 
282
  bulletMode = bulletMode === 'spread' ? 'parallel' : 'spread';
283
 
 
 
 
 
 
 
284
  if (bulletMode === 'spread') {
285
- bulletModeBtn.textContent = 'SWITCH TO PARALLEL MODE (SPACE)';
286
- bulletModeBtn.style.background = 'linear-gradient(to bottom, #aa00aa, #880088)';
287
- bulletModeIndicator.textContent = 'Current Mode: SPREAD';
288
  } else {
289
- bulletModeBtn.textContent = 'SWITCH TO SPREAD MODE (SPACE)';
290
- bulletModeBtn.style.background = 'linear-gradient(to bottom, #00aa00, #008800)';
291
- bulletModeIndicator.textContent = 'Current Mode: PARALLEL';
292
  }
293
 
294
- // Visual feedback
295
  for (let i = 0; i < 20; i++) {
296
  particles.push({
297
  x: player.x,
@@ -306,10 +531,429 @@
306
  }
307
  }
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  // Game loop
 
310
  function gameLoop(timestamp) {
311
  if (!gameRunning) return;
312
 
 
 
 
313
  // Clear canvas
314
  ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
315
  ctx.fillRect(0, 0, canvas.width, canvas.height);
@@ -326,15 +970,27 @@
326
  // Update and draw player
327
  drawPlayer();
328
 
 
 
 
 
 
 
329
  // Update and draw bullets
330
  updateBullets();
331
 
 
 
 
332
  // Update and draw aliens
333
  updateAliens();
334
 
335
  // Update and draw particles
336
  updateParticles();
337
 
 
 
 
338
  // Check for level up
339
  if (score >= level * 1000) {
340
  levelUp();
@@ -484,6 +1140,105 @@
484
  }
485
  }
486
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  // Spawn alien
488
  function spawnAlien() {
489
  const size = Math.random() * 30 + 20;
@@ -626,6 +1381,22 @@
626
  decay: Math.random() * 0.1 + 0.9
627
  });
628
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  }
630
 
631
  // Update particles
 
109
  transition: width 0.3s;
110
  }
111
 
112
+ /* Ability Icons */
113
+ .abilities-container {
114
  position: absolute;
115
  bottom: 20px;
116
+ right: 20px;
117
+ display: flex;
118
+ gap: 15px;
 
 
119
  z-index: 5;
120
  }
121
 
122
+ .ability {
123
+ width: 60px;
124
+ height: 60px;
125
+ border-radius: 10px;
126
+ background: rgba(0, 0, 0, 0.5);
127
+ border: 2px solid rgba(255, 255, 255, 0.2);
128
+ display: flex;
129
+ flex-direction: column;
130
+ align-items: center;
131
+ justify-content: center;
132
+ position: relative;
133
+ cursor: pointer;
134
+ transition: all 0.2s;
135
+ }
136
+
137
+ .ability:hover {
138
+ transform: scale(1.05);
139
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
140
+ }
141
+
142
+ .ability-icon {
143
+ font-size: 24px;
144
+ margin-bottom: 5px;
145
+ }
146
+
147
+ .ability-key {
148
+ position: absolute;
149
+ top: 5px;
150
+ right: 5px;
151
+ font-size: 12px;
152
+ background: rgba(0, 0, 0, 0.5);
153
+ border-radius: 3px;
154
+ padding: 2px 4px;
155
+ }
156
+
157
+ .ability-cooldown {
158
+ position: absolute;
159
+ bottom: 0;
160
+ left: 0;
161
+ width: 100%;
162
+ height: 0%;
163
+ background: rgba(0, 0, 0, 0.7);
164
+ border-radius: 0 0 8px 8px;
165
+ transition: height 0.1s;
166
  }
167
 
168
+ .ability.active {
169
+ border-color: #00ffff;
170
+ box-shadow: 0 0 15px #00ffff;
171
+ }
172
+
173
+ .ability.on-cooldown {
174
+ opacity: 0.6;
175
+ }
176
+
177
+ .ability-timer {
178
  position: absolute;
179
+ top: 50%;
180
  left: 50%;
181
+ transform: translate(-50%, -50%);
182
+ font-size: 14px;
183
+ font-weight: bold;
184
  color: white;
185
+ text-shadow: 0 0 3px black;
186
+ display: none;
187
+ }
188
+
189
+ .ability.active .ability-timer {
190
+ display: block;
191
+ }
192
+
193
+ /* Specific ability styles */
194
+ #bulletModeAbility {
195
+ border-color: #aa00aa;
196
+ }
197
+
198
+ #bulletModeAbility.active {
199
+ border-color: #ff00ff;
200
+ box-shadow: 0 0 15px #ff00ff;
201
+ }
202
+
203
+ #rocketAbility {
204
+ border-color: #ff5500;
205
+ }
206
+
207
+ #rocketAbility.active {
208
+ border-color: #ffaa00;
209
+ box-shadow: 0 0 15px #ffaa00;
210
+ }
211
+
212
+ #wingmanAbility {
213
+ border-color: #55aa00;
214
+ }
215
+
216
+ #wingmanAbility.active {
217
+ border-color: #55ff00;
218
+ box-shadow: 0 0 15px #55ff00;
219
+ }
220
+
221
+ #miniShipAbility {
222
+ border-color: #0055ff;
223
+ }
224
+
225
+ #miniShipAbility.active {
226
+ border-color: #00aaff;
227
+ box-shadow: 0 0 15px #00aaff;
228
+ }
229
+
230
+ .wingman-timer {
231
+ position: absolute;
232
+ bottom: 100px;
233
+ right: 20px;
234
+ color: #55ff00;
235
  font-size: 14px;
236
+ text-shadow: 0 0 5px #55ff00;
237
  z-index: 5;
238
+ display: none;
239
+ background: rgba(0, 0, 0, 0.5);
240
+ padding: 5px 10px;
241
+ border-radius: 5px;
242
+ }
243
+
244
+ .mini-ship-timer {
245
+ position: absolute;
246
+ bottom: 130px;
247
+ right: 20px;
248
+ color: #00aaff;
249
+ font-size: 14px;
250
+ text-shadow: 0 0 5px #00aaff;
251
+ z-index: 5;
252
+ display: none;
253
+ background: rgba(0, 0, 0, 0.5);
254
+ padding: 5px 10px;
255
+ border-radius: 5px;
256
  }
257
  </style>
258
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
259
  </head>
260
  <body>
261
  <canvas id="gameCanvas"></canvas>
 
275
  <p class="instructions">
276
  Defend your sector from the alien invasion!<br>
277
  Use <span class="highlight">mouse</span> to move your ship and <span class="highlight">click</span> to shoot.<br>
278
+ Press <span class="highlight">SPACE</span> or click the icon to switch bullet modes.<br>
279
+ Press <span class="highlight">R</span> or click the icon to fire a powerful rocket (cooldown: 5s).<br>
280
+ Press <span class="highlight">W</span> or click the icon to deploy wingman drones (cooldown: 10s).<br>
281
+ Press <span class="highlight">M</span> or click the icon to summon mini ships (cooldown: 15s).<br>
282
  Destroy aliens to increase your bullet power!<br>
283
  <span class="highlight">Every 5 kills</span> gives you an additional bullet (max 5)!
284
  </p>
 
292
  <button id="restartButton">TRY AGAIN</button>
293
  </div>
294
 
295
+ <!-- Ability Icons -->
296
+ <div class="abilities-container">
297
+ <div class="ability" id="bulletModeAbility" title="Switch Bullet Mode (SPACE)">
298
+ <div class="ability-icon"><i class="fas fa-arrows-alt-h"></i></div>
299
+ <div class="ability-key">SPACE</div>
300
+ <div class="ability-cooldown"></div>
301
+ <div class="ability-timer"></div>
302
+ </div>
303
+
304
+ <div class="ability" id="rocketAbility" title="Fire Rocket (R)">
305
+ <div class="ability-icon"><i class="fas fa-rocket"></i></div>
306
+ <div class="ability-key">R</div>
307
+ <div class="ability-cooldown"></div>
308
+ <div class="ability-timer"></div>
309
+ </div>
310
+
311
+ <div class="ability" id="wingmanAbility" title="Deploy Wingmen (W)">
312
+ <div class="ability-icon"><i class="fas fa-fighter-jet"></i></div>
313
+ <div class="ability-key">W</div>
314
+ <div class="ability-cooldown"></div>
315
+ <div class="ability-timer"></div>
316
+ </div>
317
+
318
+ <div class="ability" id="miniShipAbility" title="Summon Mini Ships (M)">
319
+ <div class="ability-icon"><i class="fas fa-space-shuttle"></i></div>
320
+ <div class="ability-key">M</div>
321
+ <div class="ability-cooldown"></div>
322
+ <div class="ability-timer"></div>
323
+ </div>
324
+ </div>
325
+
326
+ <div class="wingman-timer" id="wingmanTimer">Wingmen: 10s</div>
327
+ <div class="mini-ship-timer" id="miniShipTimer">Mini Ships: 10s</div>
328
 
329
  <script>
330
  // Game canvas setup
 
340
  let level = 1;
341
  let aliens = [];
342
  let bullets = [];
343
+ let rockets = [];
344
+ let wingmen = [];
345
+ let miniShips = [];
346
  let particles = [];
347
  let lastAlienSpawn = 0;
348
  let alienSpawnInterval = 2000;
 
354
  let bulletMode = 'spread'; // 'spread' or 'parallel'
355
  const MAX_BULLETS = 5;
356
 
357
+ // Ability cooldowns
358
+ const abilities = {
359
+ bulletMode: {
360
+ cooldown: 0,
361
+ maxCooldown: 0,
362
+ ready: true,
363
+ element: document.getElementById('bulletModeAbility')
364
+ },
365
+ rocket: {
366
+ cooldown: 0,
367
+ maxCooldown: 5000, // 5 seconds
368
+ ready: true,
369
+ element: document.getElementById('rocketAbility')
370
+ },
371
+ wingman: {
372
+ cooldown: 0,
373
+ maxCooldown: 10000, // 10 seconds
374
+ ready: true,
375
+ element: document.getElementById('wingmanAbility'),
376
+ active: false,
377
+ duration: 0,
378
+ maxDuration: 10000 // 10 seconds
379
+ },
380
+ miniShip: {
381
+ cooldown: 0,
382
+ maxCooldown: 15000, // 15 seconds
383
+ ready: true,
384
+ element: document.getElementById('miniShipAbility'),
385
+ active: false,
386
+ duration: 0,
387
+ maxDuration: 10000 // 10 seconds
388
+ }
389
+ };
390
+
391
  // Player spaceship
392
  const player = {
393
  x: canvas.width / 2,
 
412
  const gameOverScreen = document.getElementById('gameOverScreen');
413
  const startButton = document.getElementById('startButton');
414
  const restartButton = document.getElementById('restartButton');
415
+ const wingmanTimerElement = document.getElementById('wingmanTimer');
416
+ const miniShipTimerElement = document.getElementById('miniShipTimer');
417
 
418
  // Event listeners
419
  startButton.addEventListener('click', startGame);
420
  restartButton.addEventListener('click', startGame);
421
  canvas.addEventListener('mousemove', movePlayer);
422
  canvas.addEventListener('click', shoot);
423
+
424
+ // Ability click handlers
425
+ document.getElementById('bulletModeAbility').addEventListener('click', toggleBulletMode);
426
+ document.getElementById('rocketAbility').addEventListener('click', fireRocket);
427
+ document.getElementById('wingmanAbility').addEventListener('click', deployWingmen);
428
+ document.getElementById('miniShipAbility').addEventListener('click', summonMiniShips);
429
+
430
+ // Keyboard controls
431
  document.addEventListener('keydown', (e) => {
432
  if (e.code === 'Space') {
433
  e.preventDefault();
434
  toggleBulletMode();
435
+ } else if (e.code === 'KeyR' && abilities.rocket.ready) {
436
+ e.preventDefault();
437
+ fireRocket();
438
+ } else if (e.code === 'KeyW' && abilities.wingman.ready) {
439
+ e.preventDefault();
440
+ deployWingmen();
441
+ } else if (e.code === 'KeyM' && abilities.miniShip.ready) {
442
+ e.preventDefault();
443
+ summonMiniShips();
444
  }
445
  });
446
 
 
459
  level = 1;
460
  aliens = [];
461
  bullets = [];
462
+ rockets = [];
463
+ wingmen = [];
464
+ miniShips = [];
465
  particles = [];
466
+ lastAlienSpawn = 0;
467
  alienSpawnInterval = 2000;
468
  alienSpeed = 1;
469
  alienHealth = 1;
 
472
  maxBulletPower = 1;
473
  bulletMode = 'spread';
474
 
475
+ // Reset abilities
476
+ for (const ability in abilities) {
477
+ abilities[ability].cooldown = 0;
478
+ abilities[ability].ready = true;
479
+ abilities[ability].active = false;
480
+ abilities[ability].duration = 0;
481
+ abilities[ability].element.classList.remove('active', 'on-cooldown');
482
+ abilities[ability].element.querySelector('.ability-cooldown').style.height = '0%';
483
+ abilities[ability].element.querySelector('.ability-timer').style.display = 'none';
484
+ }
485
+
486
  scoreElement.textContent = score;
487
  livesElement.textContent = lives;
488
  levelElement.textContent = level;
489
  bulletPowerElement.textContent = `${bulletPower}/${MAX_BULLETS}`;
490
  powerFillElement.style.width = '0%';
491
+ wingmanTimerElement.style.display = 'none';
492
+ miniShipTimerElement.style.display = 'none';
493
 
494
  startScreen.style.display = 'none';
495
  gameOverScreen.style.display = 'none';
 
504
 
505
  bulletMode = bulletMode === 'spread' ? 'parallel' : 'spread';
506
 
507
+ // Visual feedback
508
+ abilities.bulletMode.element.classList.add('active');
509
+ setTimeout(() => {
510
+ abilities.bulletMode.element.classList.remove('active');
511
+ }, 300);
512
+
513
  if (bulletMode === 'spread') {
514
+ abilities.bulletMode.element.style.borderColor = '#aa00aa';
 
 
515
  } else {
516
+ abilities.bulletMode.element.style.borderColor = '#00aa00';
 
 
517
  }
518
 
519
+ // Visual effect
520
  for (let i = 0; i < 20; i++) {
521
  particles.push({
522
  x: player.x,
 
531
  }
532
  }
533
 
534
+ // Fire rocket
535
+ function fireRocket() {
536
+ if (!gameRunning || !abilities.rocket.ready) return;
537
+
538
+ // Create rocket
539
+ rockets.push({
540
+ x: player.x,
541
+ y: player.y - player.height / 2,
542
+ width: 15,
543
+ height: 30,
544
+ speedY: -15,
545
+ color: '#ff5500',
546
+ explosionRadius: 100,
547
+ damage: 5
548
+ });
549
+
550
+ // Rocket exhaust effect
551
+ for (let i = 0; i < 20; i++) {
552
+ particles.push({
553
+ x: player.x,
554
+ y: player.y - player.height / 2 + 15,
555
+ radius: Math.random() * 6 + 3,
556
+ color: '#ffaa00',
557
+ speedX: (Math.random() - 0.5) * 3,
558
+ speedY: Math.random() * 5,
559
+ life: 30,
560
+ decay: 0.9
561
+ });
562
+ }
563
+
564
+ // Start cooldown
565
+ abilities.rocket.ready = false;
566
+ abilities.rocket.cooldown = abilities.rocket.maxCooldown;
567
+ abilities.rocket.element.classList.add('on-cooldown');
568
+
569
+ // Visual feedback
570
+ abilities.rocket.element.classList.add('active');
571
+ setTimeout(() => {
572
+ abilities.rocket.element.classList.remove('active');
573
+ }, 300);
574
+ }
575
+
576
+ // Deploy wingmen drones
577
+ function deployWingmen() {
578
+ if (!gameRunning || !abilities.wingman.ready) return;
579
+
580
+ // Create two wingmen drones
581
+ wingmen.push({
582
+ x: player.x - 60,
583
+ y: player.y,
584
+ width: 25,
585
+ height: 40,
586
+ offsetX: -60,
587
+ offsetY: 0,
588
+ targetX: player.x - 60,
589
+ targetY: player.y,
590
+ color: '#55ff00',
591
+ lastShot: 0,
592
+ shootDelay: 800
593
+ });
594
+
595
+ wingmen.push({
596
+ x: player.x + 60,
597
+ y: player.y,
598
+ width: 25,
599
+ height: 40,
600
+ offsetX: 60,
601
+ offsetY: 0,
602
+ targetX: player.x + 60,
603
+ targetY: player.y,
604
+ color: '#55ff00',
605
+ lastShot: 0,
606
+ shootDelay: 800
607
+ });
608
+
609
+ // Wingman deployment effect
610
+ for (let i = 0; i < 30; i++) {
611
+ particles.push({
612
+ x: player.x - 60,
613
+ y: player.y,
614
+ radius: Math.random() * 5 + 2,
615
+ color: '#55ff00',
616
+ speedX: (Math.random() - 0.5) * 4,
617
+ speedY: (Math.random() - 0.5) * 4,
618
+ life: Math.random() * 30 + 20,
619
+ decay: 0.9
620
+ });
621
+
622
+ particles.push({
623
+ x: player.x + 60,
624
+ y: player.y,
625
+ radius: Math.random() * 5 + 2,
626
+ color: '#55ff00',
627
+ speedX: (Math.random() - 0.5) * 4,
628
+ speedY: (Math.random() - 0.5) * 4,
629
+ life: Math.random() * 30 + 20,
630
+ decay: 0.9
631
+ });
632
+ }
633
+
634
+ // Start wingman duration
635
+ abilities.wingman.active = true;
636
+ abilities.wingman.duration = abilities.wingman.maxDuration;
637
+ wingmanTimerElement.style.display = 'block';
638
+ wingmanTimerElement.textContent = `Wingmen: ${Math.ceil(abilities.wingman.duration/1000)}s`;
639
+
640
+ // Start cooldown
641
+ abilities.wingman.ready = false;
642
+ abilities.wingman.cooldown = abilities.wingman.maxCooldown;
643
+ abilities.wingman.element.classList.add('on-cooldown');
644
+
645
+ // Visual feedback
646
+ abilities.wingman.element.classList.add('active');
647
+ setTimeout(() => {
648
+ abilities.wingman.element.classList.remove('active');
649
+ }, 300);
650
+ }
651
+
652
+ // Summon mini ships
653
+ function summonMiniShips() {
654
+ if (!gameRunning || !abilities.miniShip.ready) return;
655
+
656
+ // Create three mini ships
657
+ for (let i = 0; i < 3; i++) {
658
+ miniShips.push({
659
+ x: player.x + (i - 1) * 40,
660
+ y: player.y,
661
+ width: 20,
662
+ height: 30,
663
+ color: '#00aaff',
664
+ lastShot: 0,
665
+ shootDelay: 500,
666
+ targetAlien: null
667
+ });
668
+ }
669
+
670
+ // Mini ship summon effect
671
+ for (let i = 0; i < 40; i++) {
672
+ particles.push({
673
+ x: player.x,
674
+ y: player.y,
675
+ radius: Math.random() * 6 + 3,
676
+ color: '#00aaff',
677
+ speedX: (Math.random() - 0.5) * 6,
678
+ speedY: (Math.random() - 0.5) * 6,
679
+ life: Math.random() * 30 + 20,
680
+ decay: 0.9
681
+ });
682
+ }
683
+
684
+ // Start mini ship duration
685
+ abilities.miniShip.active = true;
686
+ abilities.miniShip.duration = abilities.miniShip.maxDuration;
687
+ miniShipTimerElement.style.display = 'block';
688
+ miniShipTimerElement.textContent = `Mini Ships: ${Math.ceil(abilities.miniShip.duration/1000)}s`;
689
+
690
+ // Start cooldown
691
+ abilities.miniShip.ready = false;
692
+ abilities.miniShip.cooldown = abilities.miniShip.maxCooldown;
693
+ abilities.miniShip.element.classList.add('on-cooldown');
694
+
695
+ // Visual feedback
696
+ abilities.miniShip.element.classList.add('active');
697
+ setTimeout(() => {
698
+ abilities.miniShip.element.classList.remove('active');
699
+ }, 300);
700
+ }
701
+
702
+ // Update wingmen
703
+ function updateWingmen(deltaTime) {
704
+ if (!abilities.wingman.active) return;
705
+
706
+ // Update wingman duration
707
+ abilities.wingman.duration -= deltaTime;
708
+ wingmanTimerElement.textContent = `Wingmen: ${Math.ceil(abilities.wingman.duration/1000)}s`;
709
+
710
+ if (abilities.wingman.duration <= 0) {
711
+ abilities.wingman.active = false;
712
+ wingmen = [];
713
+ wingmanTimerElement.style.display = 'none';
714
+ }
715
+
716
+ // Update wingmen positions to follow player with slight delay
717
+ for (let i = 0; i < wingmen.length; i++) {
718
+ const wingman = wingmen[i];
719
+
720
+ // Update target position relative to player
721
+ wingman.targetX = player.x + wingman.offsetX;
722
+ wingman.targetY = player.y + wingman.offsetY;
723
+
724
+ // Move towards target with easing
725
+ wingman.x += (wingman.targetX - wingman.x) * 0.1;
726
+ wingman.y += (wingman.targetY - wingman.y) * 0.1;
727
+
728
+ // Draw wingman
729
+ ctx.fillStyle = wingman.color;
730
+ ctx.beginPath();
731
+ ctx.moveTo(wingman.x, wingman.y - wingman.height / 2);
732
+ ctx.lineTo(wingman.x - wingman.width / 2, wingman.y + wingman.height / 2);
733
+ ctx.lineTo(wingman.x + wingman.width / 2, wingman.y + wingman.height / 2);
734
+ ctx.closePath();
735
+ ctx.fill();
736
+
737
+ // Cockpit glow
738
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
739
+ ctx.beginPath();
740
+ ctx.arc(wingman.x, wingman.y - wingman.height / 4, wingman.width / 4, 0, Math.PI * 2);
741
+ ctx.fill();
742
+
743
+ // Engine glow
744
+ ctx.fillStyle = 'rgba(150, 255, 100, 0.7)';
745
+ ctx.beginPath();
746
+ ctx.moveTo(wingman.x - wingman.width / 3, wingman.y + wingman.height / 2);
747
+ ctx.lineTo(wingman.x, wingman.y + wingman.height / 2 + 10);
748
+ ctx.lineTo(wingman.x + wingman.width / 3, wingman.y + wingman.height / 2);
749
+ ctx.closePath();
750
+ ctx.fill();
751
+
752
+ // Wingmen shoot at aliens
753
+ const now = Date.now();
754
+ if (now - wingman.lastShot >= wingman.shootDelay && aliens.length > 0) {
755
+ wingman.lastShot = now;
756
+
757
+ // Find closest alien
758
+ let closestAlien = null;
759
+ let minDistance = Infinity;
760
+
761
+ for (const alien of aliens) {
762
+ const distance = Math.sqrt(
763
+ Math.pow(wingman.x - (alien.x + alien.width / 2), 2) +
764
+ Math.pow(wingman.y - (alien.y + alien.height / 2), 2)
765
+ );
766
+
767
+ if (distance < minDistance) {
768
+ minDistance = distance;
769
+ closestAlien = alien;
770
+ }
771
+ }
772
+
773
+ if (closestAlien) {
774
+ // Calculate direction to alien
775
+ const angle = Math.atan2(
776
+ closestAlien.y + closestAlien.height / 2 - wingman.y,
777
+ closestAlien.x + closestAlien.width / 2 - wingman.x
778
+ );
779
+
780
+ // Create bullet
781
+ bullets.push({
782
+ x: wingman.x,
783
+ y: wingman.y - wingman.height / 2,
784
+ width: 3,
785
+ height: 15,
786
+ speedX: Math.cos(angle) * 7,
787
+ speedY: Math.sin(angle) * 7,
788
+ color: '#55ff00'
789
+ });
790
+
791
+ // Muzzle flash effect
792
+ for (let j = 0; j < 5; j++) {
793
+ particles.push({
794
+ x: wingman.x,
795
+ y: wingman.y - wingman.height / 2,
796
+ radius: Math.random() * 3 + 1,
797
+ color: '#55ff00',
798
+ speedX: Math.cos(angle) * (Math.random() * 3 + 2),
799
+ speedY: Math.sin(angle) * (Math.random() * 3 + 2),
800
+ life: 15,
801
+ decay: 0.9
802
+ });
803
+ }
804
+ }
805
+ }
806
+ }
807
+ }
808
+
809
+ // Update mini ships
810
+ function updateMiniShips(deltaTime) {
811
+ if (!abilities.miniShip.active) return;
812
+
813
+ // Update mini ship duration
814
+ abilities.miniShip.duration -= deltaTime;
815
+ miniShipTimerElement.textContent = `Mini Ships: ${Math.ceil(abilities.miniShip.duration/1000)}s`;
816
+
817
+ if (abilities.miniShip.duration <= 0) {
818
+ abilities.miniShip.active = false;
819
+ miniShips = [];
820
+ miniShipTimerElement.style.display = 'none';
821
+ }
822
+
823
+ // Update mini ships
824
+ for (let i = miniShips.length - 1; i >= 0; i--) {
825
+ const miniShip = miniShips[i];
826
+
827
+ // Find the furthest alien if not already targeting one
828
+ if (!miniShip.targetAlien || miniShip.targetAlien.health <= 0) {
829
+ let furthestAlien = null;
830
+ let maxDistance = 0;
831
+
832
+ for (const alien of aliens) {
833
+ const distance = Math.sqrt(
834
+ Math.pow(miniShip.x - (alien.x + alien.width / 2), 2) +
835
+ Math.pow(miniShip.y - (alien.y + alien.height / 2), 2)
836
+ );
837
+
838
+ if (distance > maxDistance) {
839
+ maxDistance = distance;
840
+ furthestAlien = alien;
841
+ }
842
+ }
843
+
844
+ miniShip.targetAlien = furthestAlien;
845
+ }
846
+
847
+ // Move towards target alien if exists
848
+ if (miniShip.targetAlien) {
849
+ const targetX = miniShip.targetAlien.x + miniShip.targetAlien.width / 2;
850
+ const targetY = miniShip.targetAlien.y + miniShip.targetAlien.height / 2;
851
+
852
+ // Move with easing
853
+ miniShip.x += (targetX - miniShip.x) * 0.05;
854
+ miniShip.y += (targetY - miniShip.y) * 0.05;
855
+
856
+ // Shoot at target
857
+ const now = Date.now();
858
+ if (now - miniShip.lastShot >= miniShip.shootDelay) {
859
+ miniShip.lastShot = now;
860
+
861
+ // Calculate direction to target
862
+ const angle = Math.atan2(
863
+ targetY - miniShip.y,
864
+ targetX - miniShip.x
865
+ );
866
+
867
+ // Create bullet
868
+ bullets.push({
869
+ x: miniShip.x,
870
+ y: miniShip.y,
871
+ width: 3,
872
+ height: 10,
873
+ speedX: Math.cos(angle) * 8,
874
+ speedY: Math.sin(angle) * 8,
875
+ color: '#00aaff'
876
+ });
877
+
878
+ // Muzzle flash effect
879
+ for (let j = 0; j < 3; j++) {
880
+ particles.push({
881
+ x: miniShip.x,
882
+ y: miniShip.y,
883
+ radius: Math.random() * 2 + 1,
884
+ color: '#00aaff',
885
+ speedX: Math.cos(angle) * (Math.random() * 2 + 1),
886
+ speedY: Math.sin(angle) * (Math.random() * 2 + 1),
887
+ life: 10,
888
+ decay: 0.9
889
+ });
890
+ }
891
+ }
892
+ } else {
893
+ // No target, return to player
894
+ miniShip.x += (player.x - miniShip.x) * 0.05;
895
+ miniShip.y += (player.y - 50 - miniShip.y) * 0.05;
896
+ }
897
+
898
+ // Draw mini ship
899
+ ctx.fillStyle = miniShip.color;
900
+ ctx.beginPath();
901
+ ctx.moveTo(miniShip.x, miniShip.y - miniShip.height / 2);
902
+ ctx.lineTo(miniShip.x - miniShip.width / 2, miniShip.y + miniShip.height / 2);
903
+ ctx.lineTo(miniShip.x + miniShip.width / 2, miniShip.y + miniShip.height / 2);
904
+ ctx.closePath();
905
+ ctx.fill();
906
+
907
+ // Cockpit glow
908
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
909
+ ctx.beginPath();
910
+ ctx.arc(miniShip.x, miniShip.y - miniShip.height / 4, miniShip.width / 4, 0, Math.PI * 2);
911
+ ctx.fill();
912
+
913
+ // Engine glow
914
+ ctx.fillStyle = 'rgba(100, 200, 255, 0.7)';
915
+ ctx.beginPath();
916
+ ctx.moveTo(miniShip.x - miniShip.width / 3, miniShip.y + miniShip.height / 2);
917
+ ctx.lineTo(miniShip.x, miniShip.y + miniShip.height / 2 + 8);
918
+ ctx.lineTo(miniShip.x + miniShip.width / 3, miniShip.y + miniShip.height / 2);
919
+ ctx.closePath();
920
+ ctx.fill();
921
+ }
922
+ }
923
+
924
+ // Update cooldowns
925
+ function updateCooldowns(deltaTime) {
926
+ for (const ability in abilities) {
927
+ if (!abilities[ability].ready) {
928
+ abilities[ability].cooldown -= deltaTime;
929
+
930
+ // Update UI
931
+ const cooldownPercent = (1 - abilities[ability].cooldown / abilities[ability].maxCooldown) * 100;
932
+ abilities[ability].element.querySelector('.ability-cooldown').style.height = `${100 - cooldownPercent}%`;
933
+
934
+ // Update timer for active abilities
935
+ if (abilities[ability].active) {
936
+ const timer = abilities[ability].element.querySelector('.ability-timer');
937
+ timer.textContent = Math.ceil(abilities[ability].duration / 1000);
938
+ }
939
+
940
+ if (abilities[ability].cooldown <= 0) {
941
+ abilities[ability].ready = true;
942
+ abilities[ability].cooldown = 0;
943
+ abilities[ability].element.classList.remove('on-cooldown');
944
+ }
945
+ }
946
+ }
947
+ }
948
+
949
  // Game loop
950
+ let lastTime = 0;
951
  function gameLoop(timestamp) {
952
  if (!gameRunning) return;
953
 
954
+ const deltaTime = timestamp - lastTime;
955
+ lastTime = timestamp;
956
+
957
  // Clear canvas
958
  ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
959
  ctx.fillRect(0, 0, canvas.width, canvas.height);
 
970
  // Update and draw player
971
  drawPlayer();
972
 
973
+ // Update and draw wingmen
974
+ updateWingmen(deltaTime);
975
+
976
+ // Update and draw mini ships
977
+ updateMiniShips(deltaTime);
978
+
979
  // Update and draw bullets
980
  updateBullets();
981
 
982
+ // Update and draw rockets
983
+ updateRockets();
984
+
985
  // Update and draw aliens
986
  updateAliens();
987
 
988
  // Update and draw particles
989
  updateParticles();
990
 
991
+ // Update cooldowns
992
+ updateCooldowns(deltaTime);
993
+
994
  // Check for level up
995
  if (score >= level * 1000) {
996
  levelUp();
 
1140
  }
1141
  }
1142
 
1143
+ // Update rockets
1144
+ function updateRockets() {
1145
+ for (let i = rockets.length - 1; i >= 0; i--) {
1146
+ const rocket = rockets[i];
1147
+
1148
+ // Update position
1149
+ rocket.y += rocket.speedY;
1150
+
1151
+ // Draw rocket
1152
+ ctx.fillStyle = rocket.color;
1153
+ ctx.beginPath();
1154
+ ctx.moveTo(rocket.x, rocket.y);
1155
+ ctx.lineTo(rocket.x - rocket.width / 2, rocket.y + rocket.height);
1156
+ ctx.lineTo(rocket.x + rocket.width / 2, rocket.y + rocket.height);
1157
+ ctx.closePath();
1158
+ ctx.fill();
1159
+
1160
+ // Rocket flame effect
1161
+ if (Math.random() > 0.3) {
1162
+ particles.push({
1163
+ x: rocket.x + (Math.random() - 0.5) * rocket.width / 2,
1164
+ y: rocket.y + rocket.height,
1165
+ radius: Math.random() * 5 + 2,
1166
+ color: '#ffaa00',
1167
+ speedX: (Math.random() - 0.5) * 2,
1168
+ speedY: Math.random() * 5,
1169
+ life: 15,
1170
+ decay: 0.9
1171
+ });
1172
+ }
1173
+
1174
+ // Check for collisions with aliens
1175
+ for (let j = aliens.length - 1; j >= 0; j--) {
1176
+ const alien = aliens[j];
1177
+ const distance = Math.sqrt(
1178
+ Math.pow(rocket.x - (alien.x + alien.width / 2), 2) +
1179
+ Math.pow(rocket.y - (alien.y + alien.height / 2), 2)
1180
+ );
1181
+
1182
+ if (distance < rocket.explosionRadius) {
1183
+ // Rocket hit alien
1184
+ alien.health -= rocket.damage;
1185
+
1186
+ if (alien.health <= 0) {
1187
+ // Alien destroyed
1188
+ createExplosion(
1189
+ alien.x + alien.width / 2,
1190
+ alien.y + alien.height / 2,
1191
+ alien.color,
1192
+ 25
1193
+ );
1194
+
1195
+ score += alien.points;
1196
+ kills++;
1197
+ scoreElement.textContent = score;
1198
+ aliens.splice(j, 1);
1199
+
1200
+ // Check for bullet power increase
1201
+ updateBulletPower();
1202
+ }
1203
+ }
1204
+ }
1205
+
1206
+ // Create explosion when rocket reaches top or hits alien
1207
+ if (rocket.y + rocket.height < 0) {
1208
+ // Rocket reached top of screen - explode
1209
+ createExplosion(
1210
+ rocket.x,
1211
+ rocket.y,
1212
+ rocket.color,
1213
+ 30
1214
+ );
1215
+
1216
+ // Damage all aliens in explosion radius
1217
+ for (let j = aliens.length - 1; j >= 0; j--) {
1218
+ const alien = aliens[j];
1219
+ const distance = Math.sqrt(
1220
+ Math.pow(rocket.x - (alien.x + alien.width / 2), 2) +
1221
+ Math.pow(rocket.y - (alien.y + alien.height / 2), 2)
1222
+ );
1223
+
1224
+ if (distance < rocket.explosionRadius) {
1225
+ alien.health -= rocket.damage;
1226
+
1227
+ if (alien.health <= 0) {
1228
+ score += alien.points;
1229
+ kills++;
1230
+ scoreElement.textContent = score;
1231
+ aliens.splice(j, 1);
1232
+ updateBulletPower();
1233
+ }
1234
+ }
1235
+ }
1236
+
1237
+ rockets.splice(i, 1);
1238
+ }
1239
+ }
1240
+ }
1241
+
1242
  // Spawn alien
1243
  function spawnAlien() {
1244
  const size = Math.random() * 30 + 20;
 
1381
  decay: Math.random() * 0.1 + 0.9
1382
  });
1383
  }
1384
+
1385
+ // Add shockwave effect for larger explosions
1386
+ if (particleCount > 15) {
1387
+ for (let i = 0; i < 10; i++) {
1388
+ particles.push({
1389
+ x: x,
1390
+ y: y,
1391
+ radius: Math.random() * 15 + 5,
1392
+ color: '#ffffff',
1393
+ speedX: (Math.random() - 0.5) * 10,
1394
+ speedY: (Math.random() - 0.5) * 10,
1395
+ life: 40,
1396
+ decay: 0.85
1397
+ });
1398
+ }
1399
+ }
1400
  }
1401
 
1402
  // Update particles