File size: 49,974 Bytes
6e2e189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chaos Cannon | MAYHEM EDITION</title> <!-- Changed Title -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        /* CSS remains the same as before */
        @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Orbitron:wght@400;700&display=swap');

        body {
            font-family: 'Orbitron', sans-serif;
            background: linear-gradient(135deg, #111 0%, #222 100%);
            overflow: hidden;
            user-select: none;
            margin: 0; padding: 0; display: flex;
            min-height: 100vh; align-items: center; justify-content: center;
            flex-direction: column;
        }
        #game-container { position: relative; width: 800px; height: 600px; margin-top: 1rem; }
        #game {
            background: radial-gradient(ellipse at center, #1a1a2e 0%, #16213e 100%);
            box-shadow: 0 0 40px rgba(0, 180, 255, 0.4); /* Enhanced glow */
            border-radius: 8px;
            border: 3px solid rgba(0, 180, 255, 0.6); /* Brighter border */
            display: block; cursor: crosshair;
        }
        .title-text { font-family: 'Press Start 2P', cursive; text-shadow: 0 0 15px #ff6600, 0 0 25px #ff6600, 0 0 5px #fff; /* Added white core */ letter-spacing: 3px; /* More spacing */ }
        .btn-glow { box-shadow: 0 0 15px rgba(255, 102, 0, 0.7); transition: all 0.3s ease; }
        .btn-glow:hover { box-shadow: 0 0 30px rgba(255, 102, 0, 1.0); /* Stronger hover */ transform: translateY(-3px) scale(1.05); /* Add scale */}
        .btn-glow:active { transform: translateY(1px) scale(1.0); }
        .score-display { background: rgba(0, 0, 0, 0.75); border: 2px solid rgba(0, 180, 255, 0.7); border-radius: 8px; text-shadow: 0 0 6px #00ff00, 0 0 10px #00ff00; /* Brighter score */ }
        .game-over { background: rgba(0, 0, 0, 0.9); border: 3px solid rgba(255, 0, 0, 0.7); box-shadow: 0 0 40px rgba(255, 0, 0, 0.6); }
        .star { position: absolute; background: white; border-radius: 50%; pointer-events: none; }
        #titleScreen, #gameOverScreen { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgba(0, 0, 0, 0.85); border-radius: 8px; z-index: 20; }
        #titleScreen { opacity: 1; transition: opacity 0.5s ease-out; pointer-events: auto; }
        #gameOverScreen { opacity: 0; transition: opacity 0.5s ease-in, background-color 0.5s ease-in; pointer-events: none; }
        #gameOverScreen.visible { opacity: 1; pointer-events: auto; background-color: rgba(0, 0, 0, 0.9); }
        /* Twinkle animation (no changes needed) */
        @keyframes twinkle { from { opacity: 0.1; } to { opacity: ${Math.random() * 0.5 + 0.5}; } }
    </style>
</head>
<body class="p-4 text-white">
    <div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0" id="stars"></div>

    <div class="text-center mb-4 z-10">
        <!-- Title updated -->
        <h1 class="title-text text-4xl md:text-5xl mb-2">CHAOS CANNON</h1>
        <div class="flex justify-center gap-4">
            <div class="score-display px-4 py-2 text-xl">
                <i class="fas fa-trophy mr-2 text-yellow-400"></i>
                <span id="highScore">0</span>
            </div>
            <div class="score-display px-4 py-2 text-xl">
                <i class="fas fa-bolt mr-2 text-orange-500"></i>
                <span id="streak">0x</span>
            </div>
        </div>
    </div>

    <div id="game-container" class="z-10">
        <canvas id="game" width="800" height="600"></canvas>
        <div id="titleScreen">
            <h2 class="title-text text-5xl mb-8">CHAOS CANNON</h2>
            <p class="text-xl mb-8 text-orange-400">MAYHEM EDITION</p> <!-- Subtitle -->
            <button id="startBtn" class="btn-glow bg-orange-600 hover:bg-orange-700 text-white font-bold py-3 px-8 rounded-full text-xl mb-4">
                <i class="fas fa-play mr-2"></i>START MAYHEM
            </button>
            <div class="text-gray-400 mt-4">
                <p class="mb-2"><i class="fas fa-mouse-pointer mr-2"></i>Aim with mouse</p>
                <p><i class="fas fa-mouse mr-2"></i>Click to SHOOT A LOT</p>
            </div>
        </div>
        <div id="gameOverScreen">
            <div class="game-over p-8 rounded-lg text-center max-w-md">
                <h2 class="title-text text-4xl text-red-500 mb-6">GAME OVER</h2>
                <p class="text-2xl mb-2">Your Score:</p>
                <p id="finalScore" class="text-4xl font-bold text-orange-500 mb-6">0</p>
                 <p class="text-lg mb-6 text-gray-300">The mayhem subsided... for now.</p>
                <button id="restartBtn" class="btn-glow bg-orange-600 hover:bg-orange-700 text-white font-bold py-3 px-8 rounded-full text-xl">
                    <i class="fas fa-redo mr-2"></i>RESTART MAYHEM
                </button>
            </div>
        </div>
    </div>

    <div class="mt-6 text-gray-400 text-sm z-10">
        <p>Made with <i class="fas fa-burn text-orange-500"></i> <!-- Changed icon --> for particle lovers</p>
    </div>

    <script>
        // --- Star Background (Same) ---
        const starsContainer = document.getElementById('stars');
        for (let i = 0; i < 250; i++) { // More stars
            const star = document.createElement('div');
            star.className = 'star';
            star.style.width = `${Math.random() * 3.5}px`; // Slightly bigger max
            star.style.height = star.style.width;
            star.style.left = `${Math.random() * 100}%`;
            star.style.top = `${Math.random() * 100}%`;
            const initialOpacity = Math.random() * 0.7 + 0.1;
            star.style.opacity = initialOpacity;
            star.style.animation = `twinkle ${1.5 + Math.random() * 5}s infinite alternate ease-in-out`; // Faster min twinkle
            starsContainer.appendChild(star);
        }
        const style = document.createElement('style');
        style.textContent = ` @keyframes twinkle { from { opacity: 0.1; } to { opacity: ${Math.random() * 0.6 + 0.4}; } } `;
        document.head.appendChild(style);

        // --- Game Setup (Same) ---
        const canvas = document.getElementById('game');
        const ctx = canvas.getContext('2d');
        let audioContext;
        const titleScreen = document.getElementById('titleScreen');
        const gameOverScreen = document.getElementById('gameOverScreen');
        const startBtn = document.getElementById('startBtn');
        const restartBtn = document.getElementById('restartBtn');
        const finalScore = document.getElementById('finalScore');
        const highScoreDisplay = document.getElementById('highScore');
        const streakDisplay = document.getElementById('streak');
        let canvasRect = canvas.getBoundingClientRect();
        let highScore = localStorage.getItem('chaosCannonHighScore') || 0;
        highScoreDisplay.textContent = highScore;
        let game;
        let lastTime = 0;
        let animationFrameId = null;
        let mousePos = { x: canvas.width / 2, y: 0 };

        // --- Audio Context Initialization (Same) ---
        function initAudioContext() { if (!audioContext && (window.AudioContext || window.webkitAudioContext)) { try { audioContext = new (window.AudioContext || window.webkitAudioContext)(); if (audioContext.state === 'suspended') { audioContext.resume(); } } catch (e) { console.error("Audio Error", e); audioContext = null; } } }
        document.addEventListener('DOMContentLoaded', initAudioContext);

        // --- Particle Class (MAYHEM Edition) ---
        class Particle {
            constructor(x, y, vx, vy, color, lifespan, size, gravity = 0.05, friction = 0.99, blendMode = 'source-over') { // Added blendMode
                this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.color = color;
                this.initialLifespan = lifespan; this.lifespan = lifespan;
                this.size = size; this.dead = false; this.opacity = 1;
                this.gravity = gravity; this.friction = friction;
                this.blendMode = blendMode; // Store blend mode
            }

            update(deltaTime) {
                const dtFactor = Math.min(deltaTime / 16.67, 4);
                this.x += this.vx * dtFactor; this.y += this.vy * dtFactor;
                this.vy += this.gravity * dtFactor;
                // Apply friction less aggressively for more hang time
                const frictionFactor = Math.pow(this.friction, dtFactor);
                this.vx *= frictionFactor; this.vy *= frictionFactor;
                this.lifespan -= deltaTime;
                // Fade out more slowly initially, then faster
                this.opacity = Math.max(0, Math.pow(this.lifespan / this.initialLifespan, 0.5));
                if (this.lifespan <= 0) this.dead = true;
            }

            draw(ctx) {
                ctx.globalCompositeOperation = this.blendMode; // Set blend mode
                ctx.fillStyle = this.color;
                ctx.globalAlpha = this.opacity;
                ctx.beginPath();
                const currentSize = Math.max(0, this.size * (this.opacity * 1.1)); // Size shrinks slightly slower than opacity
                ctx.arc(this.x, this.y, currentSize, 0, Math.PI * 2);
                ctx.fill();
                ctx.globalAlpha = 1.0;
                ctx.globalCompositeOperation = 'source-over'; // Reset blend mode
            }
        }

        // --- Explosion Class (MAYHEM Edition) ---
        class Explosion {
            constructor(x, y, type, game) {
                this.x = x; this.y = y; this.dead = false; this.type = type;
                this.game = game; this.duration = 600; // Slightly longer duration

                const isBoss = type === 'boss';
                // MORE POWER
                const basePower = isBoss ? 15 : 9;
                const particleMultiplier = isBoss ? 3.0 : 1.8; // MOOORE PARTICLES

                 // 0. Central Flash
                 const flashSize = isBoss ? 60 : 35;
                 game.particles.push(new Particle(this.x, this.y, 0, 0, 'rgba(255, 255, 255, 0.9)', 80, flashSize, 0, 1, 'lighter')); // Short life, big size, lighter blend
                 game.particles.push(new Particle(this.x, this.y, 0, 0, `hsl(${Math.random()*60}, 100%, 70%)`, 120, flashSize * 0.7, 0, 1, 'lighter')); // Colored flash under white

                // 1. Fireball Core Particles
                const coreCount = Math.floor(35 * particleMultiplier); // MORE
                for (let i = 0; i < coreCount; i++) {
                    const angle = Math.random() * Math.PI * 2;
                    const speed = Math.random() * basePower * 1.8 + basePower * 0.6; // Faster max speed
                    const vx = Math.cos(angle) * speed; const vy = Math.sin(angle) * speed;
                    const color = `hsl(${Math.random() * 40 + 10}, 100%, ${65 + Math.random() * 35}%)`; // Brighter range
                    const lifespan = Math.random() * 300 + 150; // Longer short lifespan
                    const size = Math.random() * (isBoss ? 12 : 8) + 5;
                    game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, 0.03, 0.96, 'lighter')); // Lighter blend for core
                }

                // 2. Sparks / Debris Particles
                const sparkCount = Math.floor(60 * particleMultiplier); // MORE
                for (let i = 0; i < sparkCount; i++) {
                    const angle = Math.random() * Math.PI * 2;
                    const speed = Math.random() * basePower * 1.2; // Slightly faster debris
                    const vx = Math.cos(angle) * speed; const vy = Math.sin(angle) * speed - Math.random() * 1.5;
                    const color = `hsl(${Math.random() * 45}, 100%, ${55 + Math.random() * 25}%)`; // More vibrant reds/oranges
                    const lifespan = Math.random() * 600 + 400; // Longer medium lifespan
                    const size = Math.random() * (isBoss ? 5 : 4) + 1.5;
                    game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, 0.06, 0.97)); // More friction
                }

                // 3. Smoke Particles
                const smokeCount = Math.floor(45 * particleMultiplier); // MORE
                for (let i = 0; i < smokeCount; i++) {
                    const angle = Math.random() * Math.PI * 2;
                    const speed = Math.random() * basePower * 0.5 + 0.5; // Slightly faster smoke base
                    const vx = Math.cos(angle) * speed + (Math.random() - 0.5) * 1.5; // More drift
                    const vy = Math.sin(angle) * speed - Math.random() * 0.8 - 0.3; // Stronger upward drift
                    const gray = 10 + Math.random() * 50; // Darker range
                    const color = `rgba(${gray}, ${gray}, ${gray+5}, ${0.5 + Math.random() * 0.4})`; // More opaque max
                    const lifespan = Math.random() * 1500 + 1000; // Even longer lifespan
                    const size = Math.random() * (isBoss ? 18 : 12) + 8; // Bigger smoke
                    game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, -0.015, 0.95)); // More anti-grav, more friction
                }
            }

            update(game, deltaTime) { this.duration -= deltaTime; if (this.duration <= 0) this.dead = true; }
            draw(ctx) { /* Only particles draw */ }
        }


        // --- Game Class (MAYHEM Edition) ---
        class Game {
             // Constructor remains largely the same
            constructor() {
                this.state = 'title'; this.score = 0; this.shake = 0; this.turret = new Turret();
                this.projectiles = []; this.enemies = []; this.particles = []; this.explosions = []; this.powerUps = [];
                this.streak = 0; this.lastHitTime = 0; this.stars = []; this.enemySpawnTimer = 0; this.powerUpSpawnTimer = 0;
                this.enemySpawnInterval = 1600; // Slightly faster initial spawn
                this.powerUpSpawnInterval = 9000; // Faster powerups
                this.difficultyTimer = 0;

                this.boundClickHandler = this.clickHandler.bind(this);
                this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this);
                canvas.addEventListener('click', this.boundClickHandler);
                canvas.addEventListener('mousemove', this.boundMouseMoveHandler);
                startBtn.addEventListener('click', () => this.start());
                restartBtn.addEventListener('click', () => this.restart());

                for(let i = 0; i < 70; i++) { // More background stars
                    this.stars.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, size: Math.random() * 2.5 + 0.5, opacity: Math.random() * 0.5 + 0.1, speed: Math.random() * 0.15 + 0.05 }); // Slightly faster stars
                }
            }

            // clickHandler, mouseMoveHandler, start, restart (remain the same)
            clickHandler(e) { if (!audioContext) initAudioContext(); if (audioContext && audioContext.state === 'suspended') { audioContext.resume(); } if (this.state === 'playing') { this.turret.shoot(this); } }
            mouseMoveHandler(e) { canvasRect = canvas.getBoundingClientRect(); mousePos.x = e.clientX - canvasRect.left; mousePos.y = e.clientY - canvasRect.top; const MIN_ANGLE = -Math.PI * 0.95; const MAX_ANGLE = -Math.PI * 0.05; let targetAngle = Math.atan2(mousePos.y - this.turret.y, mousePos.x - this.turret.x); this.turret.angle = Math.max(MIN_ANGLE, Math.min(MAX_ANGLE, targetAngle)); }
            start() { if (animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; } if (!audioContext) initAudioContext(); if (audioContext && audioContext.state === 'suspended') { audioContext.resume(); } this.state = 'playing'; this.score = 0; this.streak = 0; this.lastHitTime = 0; this.enemySpawnTimer = 0; this.powerUpSpawnTimer = 0; this.enemySpawnInterval = 1600; this.powerUpSpawnInterval = 9000; this.difficultyTimer = 0; this.shake = 0; this.turret = new Turret(); this.projectiles = []; this.enemies = []; this.particles = []; this.explosions = []; this.powerUps = []; this.updateScoreDisplay(); streakDisplay.textContent = `0x`; titleScreen.style.opacity = '0'; titleScreen.style.pointerEvents = 'none'; gameOverScreen.classList.remove('visible'); lastTime = performance.now(); this.animate(); }
            restart() { this.start(); }


            spawnEnemies(deltaTime) {
                this.enemySpawnTimer += deltaTime;
                if (this.enemySpawnTimer >= this.enemySpawnInterval) {
                    this.enemySpawnTimer -= this.enemySpawnInterval;

                    const isBoss = Math.random() < (0.12 + this.difficultyTimer / 250000); // Faster boss scaling
                    const type = isBoss ? 'boss' : 'normal';
                    const edgeBuffer = type === 'boss' ? 50 : 30;
                    // Faster base speed, faster scaling
                    const speed = (isBoss ? 0.8 : 1.2) * (Math.random() * 1.8 + 1.0 + (this.difficultyTimer / 60000));

                    const newEnemy = new Enemy( Math.random() * (canvas.width - edgeBuffer * 2) + edgeBuffer, -60, speed, type );
                    this.enemies.push(newEnemy);
                }
            }

            spawnPowerUps(deltaTime) {
                this.powerUpSpawnTimer += deltaTime;
                if (this.powerUpSpawnTimer >= this.powerUpSpawnInterval) {
                     this.powerUpSpawnTimer = 0;
                     if(Math.random() > 0.4) { // Even more powerups!
                        const edgeBuffer = 40;
                        this.powerUps.push(new PowerUp( Math.random() * (canvas.width - edgeBuffer * 2) + edgeBuffer, -30, ['rapid', 'explosive', 'multi'][Math.floor(Math.random() * 3)] ));
                    }
                }
            }

            increaseDifficulty(deltaTime) {
                this.difficultyTimer += deltaTime;
                const decreaseFactor = 1 - (deltaTime / 200000); // Faster difficulty scaling (interval decrease)
                this.enemySpawnInterval = Math.max(250, this.enemySpawnInterval * decreaseFactor); // Lower min interval (0.25s)
            }

            endGame() { if (this.state === 'gameover') return; this.state = 'gameover'; this.playGameOverSound(); if(this.score > highScore) { highScore = this.score; localStorage.setItem('chaosCannonHighScore', highScore); highScoreDisplay.textContent = highScore; } finalScore.textContent = this.score; gameOverScreen.classList.add('visible'); }

            update(deltaTime) {
                if(this.state !== 'playing') return;

                 this.increaseDifficulty(deltaTime);
                 this.spawnEnemies(deltaTime);
                 this.spawnPowerUps(deltaTime);

                // Update entities (pass deltaTime) - Turret needs it for idle particles now
                this.turret.update(deltaTime, this); // Pass game reference for particles
                [...this.projectiles, ...this.enemies, ...this.particles, ...this.explosions, ...this.powerUps].forEach(e => e.update(this, deltaTime));

                streakDisplay.textContent = `${this.streak}x`;
                if(this.streak > 0 && Date.now() - this.lastHitTime > 3000) this.streak = 0;

                // --- Collision Detection ---
                for (let i = this.projectiles.length - 1; i >= 0; i--) {
                    const projectile = this.projectiles[i];
                    if (projectile.dead) continue;

                    for (let j = this.enemies.length - 1; j >= 0; j--) {
                        const enemy = this.enemies[j];
                        const enemyRadius = enemy.type === 'boss' ? 40 : 20;
                        if (Math.hypot(projectile.x - enemy.x, projectile.y - enemy.y) < (enemyRadius + projectile.size)) {

                            enemy.health--; // Health reduced earlier, easier enemies!
                            if (enemy.health <= 0) {
                                // Enemy destroyed
                                if (Date.now() - this.lastHitTime < 3000) this.streak++; else this.streak = 1;
                                this.lastHitTime = Date.now();

                                this.explosions.push(new Explosion(enemy.x, enemy.y, enemy.type, this)); // MAYHEM explosion

                                const basePoints = enemy.type === 'boss' ? 250 : 50; // Less points needed as they die faster
                                this.score += Math.round(basePoints * (1 + this.streak * 0.20)); // Higher streak bonus
                                this.updateScoreDisplay();

                                this.enemies.splice(j, 1);
                                this.shake = enemy.type === 'boss' ? 25 : 12; // MORE SHAKE
                                this.playExplosionSound(enemy.type);

                                if(enemy.type === 'boss' && Math.random() > 0.3) { // Very high powerup drop chance
                                    this.powerUps.push(new PowerUp( enemy.x, enemy.y, ['rapid', 'explosive', 'multi'][Math.floor(Math.random() * 3)] ));
                                }
                            } else {
                                // --- Enemy Hit Spark Particles ---
                                this.playHitSound();
                                this.shake = 6; // Keep small hit shake
                                const impactAngle = Math.atan2(projectile.y - enemy.y, projectile.x - enemy.x);
                                for (let k = 0; k < 5 + Math.random() * 5; k++) { // 5-10 sparks
                                     const angle = impactAngle + (Math.random() - 0.5) * 1.5; // Spread sparks
                                     const speed = Math.random() * 3 + 1;
                                     const vx = Math.cos(angle) * speed;
                                     const vy = Math.sin(angle) * speed;
                                     const color = `hsl(${Math.random()*20 + 20}, 80%, ${50 + Math.random()*20}%)`; // Orange/Yellow sparks
                                     const lifespan = Math.random() * 150 + 50; // Short life
                                     const size = Math.random() * 2 + 0.5;
                                     this.particles.push(new Particle(projectile.x, projectile.y, vx, vy, color, lifespan, size, 0.1, 0.96)); // More gravity
                                }
                            }

                            if (projectile.explosive) {
                                this.explosions.push(new Explosion(projectile.x, projectile.y, 'normal', this)); // Explosive projectiles also get MAYHEM explosions
                                this.playExplosionSound('normal');
                            }
                            projectile.dead = true;
                            break;
                        }
                    }
                }

                // Powerup vs Turret (Same logic)
                for (let i = this.powerUps.length - 1; i >= 0; i--) { const powerUp = this.powerUps[i]; if (Math.hypot(powerUp.x - this.turret.x, powerUp.y - (this.turret.y + 10)) < (30 + powerUp.size / 2)) { this.turret.activatePowerUp(powerUp.type); this.powerUps.splice(i, 1); this.playPowerUpSound(); this.score += 100; this.updateScoreDisplay(); } }

                // --- Cleanup --- Filter dead entities
                this.projectiles = this.projectiles.filter(p => !p.dead && p.y > -150 && p.y < canvas.height + 150 && p.x > -150 && p.x < canvas.width + 150); // Even wider bounds
                this.particles = this.particles.filter(p => !p.dead);
                // Limit total particles if needed (optional performance safeguard)
                // const MAX_PARTICLES = 3000;
                // if (this.particles.length > MAX_PARTICLES) {
                //     this.particles.splice(0, this.particles.length - MAX_PARTICLES);
                // }
                this.explosions = this.explosions.filter(e => !e.dead);
                this.powerUps = this.powerUps.filter(p => !p.dead);

                 // Update in-game stars (Same)
                 this.stars.forEach(star => { star.y += star.speed * (deltaTime / 16.67); if (star.y > canvas.height) { star.y = -star.size; star.x = Math.random() * canvas.width; } });
            }

            // Draw method (minor adjustments for mayhem feel)
            draw() {
                ctx.save();
                if(this.shake > 0) { const shakeX = Math.random() * this.shake - this.shake / 2; const shakeY = Math.random() * this.shake - this.shake / 2; ctx.translate(shakeX, shakeY); this.shake *= 0.92; if (this.shake < 0.5) this.shake = 0; }

                // Background Color
                ctx.fillStyle = '#080814'; // Even darker
                ctx.fillRect(0, 0, canvas.width, canvas.height);

                // Background Moving Stars
                 this.stars.forEach(star => { ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity * 1.2})`; ctx.fillRect(star.x, star.y, star.size, star.size); }); // Brighter stars

                // Grid Lines (Fainter)
                ctx.strokeStyle = 'rgba(0, 150, 255, 0.06)'; ctx.lineWidth = 0.8;
                for(let x = 0; x < canvas.width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); }
                for(let y = 0; y < canvas.height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); }

                // Game Entities (Order matters for layering)
                this.powerUps.forEach(p => p.draw(ctx));
                this.turret.draw(ctx);
                this.enemies.forEach(e => e.draw(ctx));
                this.projectiles.forEach(p => p.draw(ctx));
                this.particles.forEach(p => p.draw(ctx)); // Draw ALL particles last

                ctx.restore(); // Restore before UI

                // --- UI (Drawn after restoring context) ---
                // Score Box
                ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.strokeStyle = 'rgba(0, 180, 255, 0.7)'; ctx.lineWidth = 2;
                ctx.strokeRect(10, 10, 200, 40); ctx.fillRect(10, 10, 200, 40);
                ctx.font = '18px Orbitron'; ctx.fillStyle = '#00ff00'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
                ctx.shadowColor = '#0f0'; ctx.shadowBlur = 5; // Add glow to score text
                ctx.fillText(`SCORE: ${this.score}`, 20, 30);
                ctx.shadowBlur = 0; // Reset shadow

                // Powerup Timer (minor visual tweaks)
                if(this.turret.powerUpActive) { const remaining = (this.turret.powerUpEnd - Date.now()) / 1000; if(remaining > 0) { const powerColor = this.turret.getPowerUpColor(); ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.strokeStyle = powerColor; ctx.lineWidth = 2.5; /* Thicker border */ const timerWidth = 220; const timerHeight = 30; const timerX = canvas.width - timerWidth - 10; const timerY = 10; ctx.strokeRect(timerX, timerY, timerWidth, timerHeight); ctx.fillRect(timerX, timerY, timerWidth, timerHeight); ctx.fillStyle = powerColor; ctx.font = '14px Orbitron'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.shadowColor = powerColor; ctx.shadowBlur = 8; /* Glow for timer text */ if (remaining < 3 && Math.floor(Date.now() / 150) % 2 === 0) { ctx.fillStyle = '#FFFFFF'; ctx.shadowColor = '#FFF'; } /* Faster flash */ ctx.fillText(`${this.turret.powerUpType.toUpperCase()} ACTIVE: ${remaining.toFixed(1)}s`, timerX + 10, timerY + timerHeight / 2); ctx.shadowBlur = 0; } }
            }

            // updateScoreDisplay, Sound Effects, animate, drawStaticBackground, drawUI (remain same structure)
             updateScoreDisplay() { /* Optional direct DOM update */ }
            _playSound(oscType, freqStart, freqEnd, gainVal, duration, filterType = null, filterFreqStart = 20000, filterFreqEnd = 20000) { if (!audioContext || audioContext.state !== 'running') return; const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); let lastNode = oscillator; if (filterType) { const filter = audioContext.createBiquadFilter(); filter.type = filterType; filter.frequency.setValueAtTime(filterFreqStart, audioContext.currentTime); filter.frequency.exponentialRampToValueAtTime(filterFreqEnd, audioContext.currentTime + duration); oscillator.connect(filter); lastNode = filter; } lastNode.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = oscType; oscillator.frequency.setValueAtTime(freqStart, audioContext.currentTime); if (freqStart !== freqEnd) { oscillator.frequency.exponentialRampToValueAtTime(freqEnd, audioContext.currentTime + duration * 0.8); } gainNode.gain.setValueAtTime(gainVal, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.0001, audioContext.currentTime + duration); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + duration); }
            playShootSound() { this._playSound('triangle', 700, 120, 0.10, 0.12); } // Slightly adjusted pitch/vol
            playHitSound() { this._playSound('square', 500, 400, 0.06, 0.08); } // Lower pitch hit
            playPowerUpSound() { this._playSound('sine', 440, 1300, 0.18, 0.4); }
            playGameOverSound() { this._playSound('sawtooth', 100, 40, 0.3, 1.2); this._playSound('square', 90, 35, 0.3, 1.2); }
            playExplosionSound(type) { const isBoss = type === 'boss'; this._playSound(isBoss ? 'noise' : 'square', isBoss ? 50 : 80 + Math.random() * 50, isBoss ? 20 : 40, isBoss ? 0.35 : 0.18, isBoss ? 0.6 : 0.45, 'lowpass', isBoss ? 1800 : 3500, 80); } // Use 'noise' for boss, louder/longer, lower filter end

            animate() { animationFrameId = requestAnimationFrame((ts) => this.animate(ts)); const now = performance.now(); const deltaTime = Math.min(now - lastTime, 100); lastTime = now; ctx.clearRect(0, 0, canvas.width, canvas.height); if (this.state === 'playing') { this.update(deltaTime); this.draw(); } else if (this.state === 'gameover') { this.drawStaticBackground(); this.particles.forEach(p => p.update(this, deltaTime)); this.particles = this.particles.filter(p => !p.dead); this.particles.forEach(p => p.draw(ctx)); this.turret.draw(ctx); this.drawUI(); } else if (this.state === 'title') { this.drawStaticBackground(); } }
            drawStaticBackground() { ctx.save(); ctx.fillStyle = '#080814'; ctx.fillRect(0, 0, canvas.width, canvas.height); this.stars.forEach(star => { ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity * 1.2})`; ctx.fillRect(star.x, star.y, star.size, star.size); }); ctx.strokeStyle = 'rgba(0, 150, 255, 0.06)'; ctx.lineWidth = 0.8; for (let x = 0; x < canvas.width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } for (let y = 0; y < canvas.height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } ctx.restore(); }
            drawUI() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.strokeStyle = 'rgba(0, 180, 255, 0.7)'; ctx.lineWidth = 2; ctx.strokeRect(10, 10, 200, 40); ctx.fillRect(10, 10, 200, 40); ctx.font = '18px Orbitron'; ctx.fillStyle = '#00ff00'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.shadowColor = '#0f0'; ctx.shadowBlur = 5; ctx.fillText(`SCORE: ${this.score}`, 20, 30); ctx.shadowBlur = 0; }
        }

        // --- Turret Class (MAYHEM Edition) ---
        class Turret {
            // Constructor and angle setter/getter same
            constructor() { this.x = canvas.width / 2; this.y = canvas.height - 30; this.angle = -Math.PI / 2; this.cooldown = 0; this.powerUpActive = false; this.powerUpType = null; this.powerUpEnd = 0; this.barrelLength = 45; this.recoil = 0; this.targetAngle = -Math.PI / 2; this.aimSpeed = 0.2; this._currentAngle = -Math.PI/2; this.idleParticleTimer = 0; } // Add idle timer
             set angle(target) { this.targetAngle = target; }
             get angle() { return this._currentAngle || -Math.PI / 2; }

            update(deltaTime, game) { // Added game reference
                const current = this.angle; const target = this.targetAngle; let diff = target - current; while (diff < -Math.PI) diff += Math.PI * 2; while (diff > Math.PI) diff -= Math.PI * 2;
                this._currentAngle = current + diff * this.aimSpeed * (deltaTime / 16.67);

                if (this.cooldown > 0) this.cooldown -= deltaTime;
                if (this.recoil > 0) this.recoil -= deltaTime * 0.4; // Even faster recoil recovery
                if (this.recoil < 0) this.recoil = 0;
                if(this.powerUpActive && Date.now() > this.powerUpEnd) { this.powerUpActive = false; this.powerUpType = null; }

                // --- Idle Turret Particles ---
                 this.idleParticleTimer += deltaTime;
                 const idleInterval = this.powerUpActive ? 30 : 150; // Faster sparks when powered up
                 if (this.idleParticleTimer > idleInterval) {
                     this.idleParticleTimer -= idleInterval;
                     const angle = Math.random() * Math.PI * 2; // Emit from base
                     const speed = Math.random() * 0.5 + 0.1;
                     const vx = Math.cos(angle) * speed;
                     const vy = Math.sin(angle) * speed;
                     const color = this.powerUpActive ? this.getPowerUpColor() + '80' : 'rgba(100, 150, 255, 0.5)'; // Use powerup color or default blue
                     const lifespan = Math.random() * 300 + 200;
                     const size = Math.random() * 1.5 + 0.5;
                     game.particles.push(new Particle(this.x + Math.cos(angle) * 20, this.y + Math.sin(angle) * 10, vx, vy, color, lifespan, size, 0.01, 0.98));
                 }
            }

            shoot(game) {
                if(this.cooldown <= 0) {
                     game.playShootSound();
                     this.recoil = 10; // More recoil visual

                    const shootAngle = this.angle;
                    const muzzleOffsetX = Math.cos(shootAngle) * (this.barrelLength - this.recoil);
                    const muzzleOffsetY = Math.sin(shootAngle) * (this.barrelLength - this.recoil);
                    const startX = this.x + muzzleOffsetX;
                    const startY = this.y + muzzleOffsetY;

                    // --- Muzzle Flash Particles ---
                    const flashCount = this.powerUpActive && this.powerUpType === 'explosive' ? 25 : 15; // More flash for explosive
                    const flashPower = this.powerUpActive && this.powerUpType === 'explosive' ? 6 : 4;
                    const flashBaseColor = this.powerUpActive && this.powerUpType === 'explosive' ? 15 : 40; // Orange/Red for explosive, Yellow otherwise
                    for (let i = 0; i < flashCount; i++) {
                        const angle = shootAngle + (Math.random() - 0.5) * 0.8; // Cone shape
                        const speed = Math.random() * flashPower + 1.0;
                        const vx = Math.cos(angle) * speed;
                        const vy = Math.sin(angle) * speed;
                        const color = `hsl(${flashBaseColor + Math.random()*20 - 10}, 100%, ${70 + Math.random()*30}%)`; // Bright yellow/orange/white
                        const lifespan = Math.random() * 80 + 40; // Very short life
                        const size = Math.random() * 4 + 1;
                        game.particles.push(new Particle(startX, startY, vx, vy, color, lifespan, size, 0.02, 0.92, 'lighter')); // Lighter blend
                    }

                    // --- Projectile Spawning ---
                    const projectileSpeed = 14; // Faster projectiles
                    const vx = Math.cos(shootAngle) * projectileSpeed;
                    const vy = Math.sin(shootAngle) * projectileSpeed;
                    const isExplosive = this.powerUpActive && this.powerUpType === 'explosive';

                    if(this.powerUpActive && this.powerUpType === 'multi') {
                        const spreadAngle = 0.2; // Even wider multi
                        for(let i = -1; i <= 1; i++) {
                            const angle = shootAngle + (i * spreadAngle);
                            game.projectiles.push(new Projectile( startX, startY, Math.cos(angle) * projectileSpeed, Math.sin(angle) * projectileSpeed, isExplosive ));
                        }
                        this.cooldown = 250; // Keep multi cooldown reasonable
                    } else {
                        game.projectiles.push(new Projectile(startX, startY, vx, vy, isExplosive));
                        // Much faster firing rate!
                        this.cooldown = this.powerUpActive && this.powerUpType === 'rapid' ? 40 : 100; // RAPID FIRE!
                    }
                }
            }

            // activatePowerUp, getPowerUpColor, draw (remain same structure, visuals already good)
            activatePowerUp(type) { this.powerUpActive = true; this.powerUpType = type; this.powerUpEnd = Date.now() + 9000; } // Longer powerup duration
            getPowerUpColor() { switch(this.powerUpType) { case 'rapid': return '#00ffff'; case 'explosive': return '#ff3300'; case 'multi': return '#ffff00'; default: return '#ffffff'; } }
            draw(ctx) { ctx.save(); ctx.translate(this.x, this.y); ctx.fillStyle = 'rgba(60, 60, 80, 0.9)'; ctx.strokeStyle = 'rgba(100, 100, 120, 1)'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(-50, 20); ctx.lineTo(50, 20); ctx.arc(0, 20, 50, 0, Math.PI, false); ctx.closePath(); ctx.fill(); ctx.stroke(); ctx.rotate(this.angle); const barrelX = -this.recoil; const gradient = ctx.createLinearGradient(barrelX, -6, barrelX + this.barrelLength, 6); gradient.addColorStop(0, '#AAA'); gradient.addColorStop(0.5, '#888'); gradient.addColorStop(1, '#777'); ctx.fillStyle = gradient; ctx.strokeStyle = '#555'; ctx.lineWidth = 1; ctx.beginPath(); ctx.rect(barrelX, -6, this.barrelLength, 12); ctx.fill(); ctx.stroke(); const baseGradient = ctx.createRadialGradient(0, 0, 5, 0, 0, 25); baseGradient.addColorStop(0, '#888'); baseGradient.addColorStop(1, '#555'); ctx.fillStyle = baseGradient; ctx.strokeStyle = '#444'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(0, 0, 25, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); const coreRadius = 12; let powerColor = '#555'; let glow = false; if(this.powerUpActive) { powerColor = this.getPowerUpColor(); glow = true; } ctx.fillStyle = powerColor; ctx.beginPath(); ctx.arc(0, 0, coreRadius, 0, Math.PI * 2); ctx.fill(); if (glow) { ctx.shadowColor = powerColor; ctx.shadowBlur = 30; /* More intense glow */ ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'; ctx.beginPath(); ctx.arc(0, 0, coreRadius * 0.6, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; } ctx.restore(); }
        }

        // --- Projectile Class (MAYHEM Edition) ---
        class Projectile {
             constructor(x, y, vx, vy, explosive) {
                 this.x = x; this.y = y; this.vx = vx; this.vy = vy;
                 this.dead = false; this.explosive = explosive;
                 this.size = explosive ? 8 : 5; // Slightly larger base size
                 this.trailTimer = 0;
                 this.trailInterval = 8; // FASTER trail emission
             }

             update(game, deltaTime) {
                 const dtFactor = Math.min(deltaTime / 16.67, 4);
                 this.x += this.vx * dtFactor; this.y += this.vy * dtFactor;
                 if(this.y < -150 || this.y > canvas.height + 150 || this.x < -150 || this.x > canvas.width + 150) this.dead = true;

                 this.trailTimer += deltaTime;
                 if (this.trailTimer >= this.trailInterval) {
                     this.trailTimer -= this.trailInterval;
                     const trailColor = this.explosive ?
                         `hsl(${Math.random() * 25 + 2}, 100%, ${65 + Math.random() * 25}%)` : // More intense red/orange
                         `hsl(${Math.random() * 25 + 30}, 100%, ${75 + Math.random() * 25}%)`; // Brighter yellow/orange

                     game.particles.push(new Particle(
                         this.x + (Math.random()-0.5)*this.size, // Emit from within projectile area
                         this.y + (Math.random()-0.5)*this.size,
                         this.vx * -0.05 + Math.random() * 1 - 0.5, // Less opposing velocity
                         this.vy * -0.05 + Math.random() * 1 - 0.5,
                         trailColor,
                         200 + Math.random() * 150, // Longer trail lifespan
                         this.explosive ? 5 : 3, // Slightly larger trail particles
                         0.01, 0.97, 'lighter' // Use lighter blend for trails too
                     ));
                 }
             }

            // Draw method (same structure, lighter blend will make it brighter)
            draw(ctx) {
                 const coreColor = this.explosive ? '#ffeecc' : '#ffffdd';
                 const outerColor1 = this.explosive ? '#ff6600' : '#ffaa00';
                 const outerColor2 = this.explosive ? '#cc2200' : '#dd6600';
                 const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
                 gradient.addColorStop(0, coreColor); gradient.addColorStop(0.4, outerColor1); gradient.addColorStop(1, outerColor2);

                 ctx.globalCompositeOperation = 'lighter'; // Draw projectile with lighter blend
                 ctx.fillStyle = gradient;
                 ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill();
                 ctx.shadowColor = this.explosive ? '#ff3300' : '#ff9900';
                 ctx.shadowBlur = this.explosive ? 22 : 15; // More glow
                 ctx.fill(); // Draw again with shadow enabled
                 ctx.shadowBlur = 0;
                 ctx.globalCompositeOperation = 'source-over'; // Reset blend mode
            }
        }

        // --- Enemy Class (MAYHEM Edition - Health Change!) ---
        class Enemy {
            constructor(x, y, speed, type) {
                this.x = x; this.y = y; this.speed = speed; this.type = type || 'normal';
                // --- HEALTH CHANGE ---
                this.initialHealth = type === 'boss' ? 3 : 1; // BOSS: 3 HP, NORMAL: 1 HP
                this._health = this.initialHealth; // Initialize private health
                // --------------------
                this.wobble = Math.random() * Math.PI * 2; this.wobbleSpeed = type === 'boss' ? 0.05 : 0.08; // Faster wobble
                this.wobbleAmount = type === 'boss' ? 8 : 5; // More wobble
                this.angle = 0; this.hitTimer = 0; this.hitDuration = 80; // Shorter hit flash
            }

            set health(value) { if (value < this._health) { this.hitTimer = this.hitDuration; } this._health = value; }
            get health() { return this._health; } // No need for default here anymore

            // Update method (same logic, checks remain)
            update(game, deltaTime) { const dtFactor = Math.min(deltaTime / 16.67, 4); this.y += this.speed * dtFactor; this.wobble += this.wobbleSpeed * dtFactor; const wobbleOffset = Math.sin(this.wobble) * this.wobbleAmount; this.x += wobbleOffset * 0.1 * dtFactor; if (this.hitTimer > 0) { this.hitTimer -= deltaTime; } const radius = this.type === 'boss' ? 40 : 20; this.x = Math.max(radius, Math.min(canvas.width - radius, this.x)); if(this.y > canvas.height + radius * 2) { if (this.type !== 'boss') { game.endGame(); } else { const index = game.enemies.indexOf(this); if (index > -1) game.enemies.splice(index, 1); } } }

            // Draw method (same structure, flash effect remains)
            draw(ctx) { ctx.save(); const wobbleOffsetY = Math.sin(this.wobble) * this.wobbleAmount * 0.5; ctx.translate(this.x, this.y + wobbleOffsetY); ctx.rotate(this.angle); const isHit = this.hitTimer > 0; if(this.type === 'boss') { const width = 80; const height = 55; const gradient = ctx.createRadialGradient(0, 0, 10, 0, 0, width / 2); gradient.addColorStop(0, isHit ? '#ffffff' : '#ff4444'); gradient.addColorStop(0.6, '#cc0000'); gradient.addColorStop(1, '#880000'); ctx.fillStyle = gradient; ctx.strokeStyle = '#660000'; ctx.lineWidth = 2; ctx.beginPath(); ctx.ellipse(0, 0, width / 2, height / 2, 0, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.fillStyle = isHit ? '#ffcccc' : '#ffff00'; ctx.beginPath(); ctx.ellipse(-18, -8, 10, 6, -0.2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(18, -8, 10, 6, 0.2, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000000'; ctx.beginPath(); ctx.arc(-18, -8, 3, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(18, -8, 3, 0, Math.PI * 2); ctx.fill(); const healthBarWidth = 60; const healthBarHeight = 8; const healthBarY = -(height / 2) - 15; ctx.fillStyle = 'rgba(80, 80, 80, 0.7)'; ctx.fillRect(-healthBarWidth / 2, healthBarY, healthBarWidth, healthBarHeight); const healthPercent = Math.max(0, this.health / this.initialHealth); ctx.fillStyle = healthPercent > 0.66 ? '#00ff00' : healthPercent > 0.33 ? '#ffff00' : '#ff0000'; /* Adjusted thresholds for lower HP */ ctx.fillRect(-healthBarWidth / 2, healthBarY, healthBarWidth * healthPercent, healthBarHeight); ctx.strokeStyle = 'rgba(200, 200, 200, 0.8)'; ctx.lineWidth = 1; ctx.strokeRect(-healthBarWidth / 2, healthBarY, healthBarWidth, healthBarHeight); } else { const size = 20; const gradient = ctx.createLinearGradient(0, -size * 0.7, 0, size * 0.3); gradient.addColorStop(0, isHit ? '#ffffff' : '#33dd33'); gradient.addColorStop(1, '#008800'); ctx.fillStyle = gradient; ctx.strokeStyle = '#005500'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(0, -size * 0.7); ctx.lineTo(-size, size * 0.3); ctx.lineTo(size, size * 0.3); ctx.closePath(); ctx.fill(); ctx.stroke(); ctx.fillStyle = isHit ? 'rgba(255, 100, 100, 0.9)' : 'rgba(0, 200, 255, 0.6)'; ctx.shadowColor = isHit ? 'rgba(255, 0, 0, 1)' : 'rgba(0, 200, 255, 1)'; ctx.shadowBlur = 8; ctx.beginPath(); ctx.arc(0, -size * 0.1, size * 0.3, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; } ctx.restore(); }
        }

        // --- PowerUp Class (Same structure, visuals already good) ---
        class PowerUp { constructor(x, y, type) { this.x = x; this.y = y; this.type = type; this.vy = 1.8; /* Slightly faster fall */ this.dead = false; this.size = 18; this.angle = Math.random() * Math.PI * 2; this.rotationSpeed = (Math.random() - 0.5) * 0.05; this.pulseTimer = Math.random() * 1000; } update(game, deltaTime) { const dtFactor = Math.min(deltaTime / 16.67, 4); this.y += this.vy * dtFactor; this.angle += this.rotationSpeed * dtFactor; this.pulseTimer += deltaTime; if(this.y > canvas.height + this.size * 2) this.dead = true; } draw(ctx) { ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); let color1, color2, symbolFunc; const iconSize = this.size * 0.8; switch(this.type) { case 'rapid': color1 = '#00ffff'; color2 = '#00aaff'; symbolFunc = () => { ctx.beginPath(); ctx.moveTo(-iconSize*0.2, -iconSize*0.5); ctx.lineTo(iconSize*0.3, 0); ctx.lineTo(-iconSize*0.3, 0); ctx.lineTo(iconSize*0.2, iconSize*0.5); ctx.strokeStyle=color2; ctx.lineWidth=3; ctx.stroke(); }; break; case 'explosive': color1 = '#ff6600'; color2 = '#ffaa00'; symbolFunc = () => { ctx.fillStyle=color2; ctx.beginPath(); ctx.arc(0, 0, iconSize*0.4, 0, Math.PI*2); ctx.fill(); ctx.strokeStyle='#555'; ctx.lineWidth=2; ctx.beginPath(); ctx.moveTo(0, -iconSize*0.4); ctx.lineTo(iconSize*0.3, -iconSize*0.6); ctx.stroke(); ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(iconSize*0.3, -iconSize*0.6, 2, 0, Math.PI*2); ctx.fill(); }; break; case 'multi': color1 = '#ffff00'; color2 = '#ffcc00'; symbolFunc = () => { ctx.fillStyle=color2; const r = iconSize*0.2; ctx.beginPath(); ctx.arc(-iconSize*0.3, iconSize*0.15, r, 0, Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.arc( iconSize*0.3, iconSize*0.15, r, 0, Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.arc( 0, -iconSize*0.3, r, 0, Math.PI*2); ctx.fill(); }; break; } const pulseFactor = 1.0 + Math.sin(this.pulseTimer / 250) * 0.12; ctx.strokeStyle = color1; ctx.lineWidth = 2; ctx.globalAlpha = 0.6 + Math.sin(this.pulseTimer / 250) * 0.3; ctx.beginPath(); ctx.arc(0, 0, this.size * 0.9 * pulseFactor, 0, Math.PI * 2); ctx.stroke(); ctx.globalAlpha = 1.0; ctx.fillStyle = 'rgba(50, 50, 70, 0.8)'; ctx.strokeStyle = color1; ctx.lineWidth = 1.5; const backSize = this.size * 0.9; ctx.beginPath(); ctx.roundRect(-backSize, -backSize, backSize*2, backSize*2, 5); ctx.fill(); ctx.stroke(); symbolFunc(); ctx.restore(); } }

        // --- Initialize and Start Game (Same) ---
        function initGame() { game = new Game(); game.drawStaticBackground(); }
        function resizeCanvas() { canvasRect = canvas.getBoundingClientRect(); }
        window.addEventListener('resize', resizeCanvas);
        resizeCanvas();
        initGame();

    </script>
<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=LukasBe/chaos-cannon-turbo" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>