LukasBe commited on
Commit
6e2e189
·
verified ·
1 Parent(s): 5dfa52f

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +611 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Chaos Cannon Turbo
3
- emoji: 🐢
4
- colorFrom: red
5
- colorTo: yellow
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: chaos-cannon-turbo
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: blue
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,611 @@
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>Chaos Cannon | MAYHEM EDITION</title> <!-- Changed Title -->
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* CSS remains the same as before */
11
+ @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Orbitron:wght@400;700&display=swap');
12
+
13
+ body {
14
+ font-family: 'Orbitron', sans-serif;
15
+ background: linear-gradient(135deg, #111 0%, #222 100%);
16
+ overflow: hidden;
17
+ user-select: none;
18
+ margin: 0; padding: 0; display: flex;
19
+ min-height: 100vh; align-items: center; justify-content: center;
20
+ flex-direction: column;
21
+ }
22
+ #game-container { position: relative; width: 800px; height: 600px; margin-top: 1rem; }
23
+ #game {
24
+ background: radial-gradient(ellipse at center, #1a1a2e 0%, #16213e 100%);
25
+ box-shadow: 0 0 40px rgba(0, 180, 255, 0.4); /* Enhanced glow */
26
+ border-radius: 8px;
27
+ border: 3px solid rgba(0, 180, 255, 0.6); /* Brighter border */
28
+ display: block; cursor: crosshair;
29
+ }
30
+ .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 */ }
31
+ .btn-glow { box-shadow: 0 0 15px rgba(255, 102, 0, 0.7); transition: all 0.3s ease; }
32
+ .btn-glow:hover { box-shadow: 0 0 30px rgba(255, 102, 0, 1.0); /* Stronger hover */ transform: translateY(-3px) scale(1.05); /* Add scale */}
33
+ .btn-glow:active { transform: translateY(1px) scale(1.0); }
34
+ .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 */ }
35
+ .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); }
36
+ .star { position: absolute; background: white; border-radius: 50%; pointer-events: none; }
37
+ #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; }
38
+ #titleScreen { opacity: 1; transition: opacity 0.5s ease-out; pointer-events: auto; }
39
+ #gameOverScreen { opacity: 0; transition: opacity 0.5s ease-in, background-color 0.5s ease-in; pointer-events: none; }
40
+ #gameOverScreen.visible { opacity: 1; pointer-events: auto; background-color: rgba(0, 0, 0, 0.9); }
41
+ /* Twinkle animation (no changes needed) */
42
+ @keyframes twinkle { from { opacity: 0.1; } to { opacity: ${Math.random() * 0.5 + 0.5}; } }
43
+ </style>
44
+ </head>
45
+ <body class="p-4 text-white">
46
+ <div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0" id="stars"></div>
47
+
48
+ <div class="text-center mb-4 z-10">
49
+ <!-- Title updated -->
50
+ <h1 class="title-text text-4xl md:text-5xl mb-2">CHAOS CANNON</h1>
51
+ <div class="flex justify-center gap-4">
52
+ <div class="score-display px-4 py-2 text-xl">
53
+ <i class="fas fa-trophy mr-2 text-yellow-400"></i>
54
+ <span id="highScore">0</span>
55
+ </div>
56
+ <div class="score-display px-4 py-2 text-xl">
57
+ <i class="fas fa-bolt mr-2 text-orange-500"></i>
58
+ <span id="streak">0x</span>
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ <div id="game-container" class="z-10">
64
+ <canvas id="game" width="800" height="600"></canvas>
65
+ <div id="titleScreen">
66
+ <h2 class="title-text text-5xl mb-8">CHAOS CANNON</h2>
67
+ <p class="text-xl mb-8 text-orange-400">MAYHEM EDITION</p> <!-- Subtitle -->
68
+ <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">
69
+ <i class="fas fa-play mr-2"></i>START MAYHEM
70
+ </button>
71
+ <div class="text-gray-400 mt-4">
72
+ <p class="mb-2"><i class="fas fa-mouse-pointer mr-2"></i>Aim with mouse</p>
73
+ <p><i class="fas fa-mouse mr-2"></i>Click to SHOOT A LOT</p>
74
+ </div>
75
+ </div>
76
+ <div id="gameOverScreen">
77
+ <div class="game-over p-8 rounded-lg text-center max-w-md">
78
+ <h2 class="title-text text-4xl text-red-500 mb-6">GAME OVER</h2>
79
+ <p class="text-2xl mb-2">Your Score:</p>
80
+ <p id="finalScore" class="text-4xl font-bold text-orange-500 mb-6">0</p>
81
+ <p class="text-lg mb-6 text-gray-300">The mayhem subsided... for now.</p>
82
+ <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">
83
+ <i class="fas fa-redo mr-2"></i>RESTART MAYHEM
84
+ </button>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <div class="mt-6 text-gray-400 text-sm z-10">
90
+ <p>Made with <i class="fas fa-burn text-orange-500"></i> <!-- Changed icon --> for particle lovers</p>
91
+ </div>
92
+
93
+ <script>
94
+ // --- Star Background (Same) ---
95
+ const starsContainer = document.getElementById('stars');
96
+ for (let i = 0; i < 250; i++) { // More stars
97
+ const star = document.createElement('div');
98
+ star.className = 'star';
99
+ star.style.width = `${Math.random() * 3.5}px`; // Slightly bigger max
100
+ star.style.height = star.style.width;
101
+ star.style.left = `${Math.random() * 100}%`;
102
+ star.style.top = `${Math.random() * 100}%`;
103
+ const initialOpacity = Math.random() * 0.7 + 0.1;
104
+ star.style.opacity = initialOpacity;
105
+ star.style.animation = `twinkle ${1.5 + Math.random() * 5}s infinite alternate ease-in-out`; // Faster min twinkle
106
+ starsContainer.appendChild(star);
107
+ }
108
+ const style = document.createElement('style');
109
+ style.textContent = ` @keyframes twinkle { from { opacity: 0.1; } to { opacity: ${Math.random() * 0.6 + 0.4}; } } `;
110
+ document.head.appendChild(style);
111
+
112
+ // --- Game Setup (Same) ---
113
+ const canvas = document.getElementById('game');
114
+ const ctx = canvas.getContext('2d');
115
+ let audioContext;
116
+ const titleScreen = document.getElementById('titleScreen');
117
+ const gameOverScreen = document.getElementById('gameOverScreen');
118
+ const startBtn = document.getElementById('startBtn');
119
+ const restartBtn = document.getElementById('restartBtn');
120
+ const finalScore = document.getElementById('finalScore');
121
+ const highScoreDisplay = document.getElementById('highScore');
122
+ const streakDisplay = document.getElementById('streak');
123
+ let canvasRect = canvas.getBoundingClientRect();
124
+ let highScore = localStorage.getItem('chaosCannonHighScore') || 0;
125
+ highScoreDisplay.textContent = highScore;
126
+ let game;
127
+ let lastTime = 0;
128
+ let animationFrameId = null;
129
+ let mousePos = { x: canvas.width / 2, y: 0 };
130
+
131
+ // --- Audio Context Initialization (Same) ---
132
+ 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; } } }
133
+ document.addEventListener('DOMContentLoaded', initAudioContext);
134
+
135
+ // --- Particle Class (MAYHEM Edition) ---
136
+ class Particle {
137
+ constructor(x, y, vx, vy, color, lifespan, size, gravity = 0.05, friction = 0.99, blendMode = 'source-over') { // Added blendMode
138
+ this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.color = color;
139
+ this.initialLifespan = lifespan; this.lifespan = lifespan;
140
+ this.size = size; this.dead = false; this.opacity = 1;
141
+ this.gravity = gravity; this.friction = friction;
142
+ this.blendMode = blendMode; // Store blend mode
143
+ }
144
+
145
+ update(deltaTime) {
146
+ const dtFactor = Math.min(deltaTime / 16.67, 4);
147
+ this.x += this.vx * dtFactor; this.y += this.vy * dtFactor;
148
+ this.vy += this.gravity * dtFactor;
149
+ // Apply friction less aggressively for more hang time
150
+ const frictionFactor = Math.pow(this.friction, dtFactor);
151
+ this.vx *= frictionFactor; this.vy *= frictionFactor;
152
+ this.lifespan -= deltaTime;
153
+ // Fade out more slowly initially, then faster
154
+ this.opacity = Math.max(0, Math.pow(this.lifespan / this.initialLifespan, 0.5));
155
+ if (this.lifespan <= 0) this.dead = true;
156
+ }
157
+
158
+ draw(ctx) {
159
+ ctx.globalCompositeOperation = this.blendMode; // Set blend mode
160
+ ctx.fillStyle = this.color;
161
+ ctx.globalAlpha = this.opacity;
162
+ ctx.beginPath();
163
+ const currentSize = Math.max(0, this.size * (this.opacity * 1.1)); // Size shrinks slightly slower than opacity
164
+ ctx.arc(this.x, this.y, currentSize, 0, Math.PI * 2);
165
+ ctx.fill();
166
+ ctx.globalAlpha = 1.0;
167
+ ctx.globalCompositeOperation = 'source-over'; // Reset blend mode
168
+ }
169
+ }
170
+
171
+ // --- Explosion Class (MAYHEM Edition) ---
172
+ class Explosion {
173
+ constructor(x, y, type, game) {
174
+ this.x = x; this.y = y; this.dead = false; this.type = type;
175
+ this.game = game; this.duration = 600; // Slightly longer duration
176
+
177
+ const isBoss = type === 'boss';
178
+ // MORE POWER
179
+ const basePower = isBoss ? 15 : 9;
180
+ const particleMultiplier = isBoss ? 3.0 : 1.8; // MOOORE PARTICLES
181
+
182
+ // 0. Central Flash
183
+ const flashSize = isBoss ? 60 : 35;
184
+ 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
185
+ 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
186
+
187
+ // 1. Fireball Core Particles
188
+ const coreCount = Math.floor(35 * particleMultiplier); // MORE
189
+ for (let i = 0; i < coreCount; i++) {
190
+ const angle = Math.random() * Math.PI * 2;
191
+ const speed = Math.random() * basePower * 1.8 + basePower * 0.6; // Faster max speed
192
+ const vx = Math.cos(angle) * speed; const vy = Math.sin(angle) * speed;
193
+ const color = `hsl(${Math.random() * 40 + 10}, 100%, ${65 + Math.random() * 35}%)`; // Brighter range
194
+ const lifespan = Math.random() * 300 + 150; // Longer short lifespan
195
+ const size = Math.random() * (isBoss ? 12 : 8) + 5;
196
+ game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, 0.03, 0.96, 'lighter')); // Lighter blend for core
197
+ }
198
+
199
+ // 2. Sparks / Debris Particles
200
+ const sparkCount = Math.floor(60 * particleMultiplier); // MORE
201
+ for (let i = 0; i < sparkCount; i++) {
202
+ const angle = Math.random() * Math.PI * 2;
203
+ const speed = Math.random() * basePower * 1.2; // Slightly faster debris
204
+ const vx = Math.cos(angle) * speed; const vy = Math.sin(angle) * speed - Math.random() * 1.5;
205
+ const color = `hsl(${Math.random() * 45}, 100%, ${55 + Math.random() * 25}%)`; // More vibrant reds/oranges
206
+ const lifespan = Math.random() * 600 + 400; // Longer medium lifespan
207
+ const size = Math.random() * (isBoss ? 5 : 4) + 1.5;
208
+ game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, 0.06, 0.97)); // More friction
209
+ }
210
+
211
+ // 3. Smoke Particles
212
+ const smokeCount = Math.floor(45 * particleMultiplier); // MORE
213
+ for (let i = 0; i < smokeCount; i++) {
214
+ const angle = Math.random() * Math.PI * 2;
215
+ const speed = Math.random() * basePower * 0.5 + 0.5; // Slightly faster smoke base
216
+ const vx = Math.cos(angle) * speed + (Math.random() - 0.5) * 1.5; // More drift
217
+ const vy = Math.sin(angle) * speed - Math.random() * 0.8 - 0.3; // Stronger upward drift
218
+ const gray = 10 + Math.random() * 50; // Darker range
219
+ const color = `rgba(${gray}, ${gray}, ${gray+5}, ${0.5 + Math.random() * 0.4})`; // More opaque max
220
+ const lifespan = Math.random() * 1500 + 1000; // Even longer lifespan
221
+ const size = Math.random() * (isBoss ? 18 : 12) + 8; // Bigger smoke
222
+ game.particles.push(new Particle(this.x, this.y, vx, vy, color, lifespan, size, -0.015, 0.95)); // More anti-grav, more friction
223
+ }
224
+ }
225
+
226
+ update(game, deltaTime) { this.duration -= deltaTime; if (this.duration <= 0) this.dead = true; }
227
+ draw(ctx) { /* Only particles draw */ }
228
+ }
229
+
230
+
231
+ // --- Game Class (MAYHEM Edition) ---
232
+ class Game {
233
+ // Constructor remains largely the same
234
+ constructor() {
235
+ this.state = 'title'; this.score = 0; this.shake = 0; this.turret = new Turret();
236
+ this.projectiles = []; this.enemies = []; this.particles = []; this.explosions = []; this.powerUps = [];
237
+ this.streak = 0; this.lastHitTime = 0; this.stars = []; this.enemySpawnTimer = 0; this.powerUpSpawnTimer = 0;
238
+ this.enemySpawnInterval = 1600; // Slightly faster initial spawn
239
+ this.powerUpSpawnInterval = 9000; // Faster powerups
240
+ this.difficultyTimer = 0;
241
+
242
+ this.boundClickHandler = this.clickHandler.bind(this);
243
+ this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this);
244
+ canvas.addEventListener('click', this.boundClickHandler);
245
+ canvas.addEventListener('mousemove', this.boundMouseMoveHandler);
246
+ startBtn.addEventListener('click', () => this.start());
247
+ restartBtn.addEventListener('click', () => this.restart());
248
+
249
+ for(let i = 0; i < 70; i++) { // More background stars
250
+ 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
251
+ }
252
+ }
253
+
254
+ // clickHandler, mouseMoveHandler, start, restart (remain the same)
255
+ clickHandler(e) { if (!audioContext) initAudioContext(); if (audioContext && audioContext.state === 'suspended') { audioContext.resume(); } if (this.state === 'playing') { this.turret.shoot(this); } }
256
+ 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)); }
257
+ 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(); }
258
+ restart() { this.start(); }
259
+
260
+
261
+ spawnEnemies(deltaTime) {
262
+ this.enemySpawnTimer += deltaTime;
263
+ if (this.enemySpawnTimer >= this.enemySpawnInterval) {
264
+ this.enemySpawnTimer -= this.enemySpawnInterval;
265
+
266
+ const isBoss = Math.random() < (0.12 + this.difficultyTimer / 250000); // Faster boss scaling
267
+ const type = isBoss ? 'boss' : 'normal';
268
+ const edgeBuffer = type === 'boss' ? 50 : 30;
269
+ // Faster base speed, faster scaling
270
+ const speed = (isBoss ? 0.8 : 1.2) * (Math.random() * 1.8 + 1.0 + (this.difficultyTimer / 60000));
271
+
272
+ const newEnemy = new Enemy( Math.random() * (canvas.width - edgeBuffer * 2) + edgeBuffer, -60, speed, type );
273
+ this.enemies.push(newEnemy);
274
+ }
275
+ }
276
+
277
+ spawnPowerUps(deltaTime) {
278
+ this.powerUpSpawnTimer += deltaTime;
279
+ if (this.powerUpSpawnTimer >= this.powerUpSpawnInterval) {
280
+ this.powerUpSpawnTimer = 0;
281
+ if(Math.random() > 0.4) { // Even more powerups!
282
+ const edgeBuffer = 40;
283
+ this.powerUps.push(new PowerUp( Math.random() * (canvas.width - edgeBuffer * 2) + edgeBuffer, -30, ['rapid', 'explosive', 'multi'][Math.floor(Math.random() * 3)] ));
284
+ }
285
+ }
286
+ }
287
+
288
+ increaseDifficulty(deltaTime) {
289
+ this.difficultyTimer += deltaTime;
290
+ const decreaseFactor = 1 - (deltaTime / 200000); // Faster difficulty scaling (interval decrease)
291
+ this.enemySpawnInterval = Math.max(250, this.enemySpawnInterval * decreaseFactor); // Lower min interval (0.25s)
292
+ }
293
+
294
+ 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'); }
295
+
296
+ update(deltaTime) {
297
+ if(this.state !== 'playing') return;
298
+
299
+ this.increaseDifficulty(deltaTime);
300
+ this.spawnEnemies(deltaTime);
301
+ this.spawnPowerUps(deltaTime);
302
+
303
+ // Update entities (pass deltaTime) - Turret needs it for idle particles now
304
+ this.turret.update(deltaTime, this); // Pass game reference for particles
305
+ [...this.projectiles, ...this.enemies, ...this.particles, ...this.explosions, ...this.powerUps].forEach(e => e.update(this, deltaTime));
306
+
307
+ streakDisplay.textContent = `${this.streak}x`;
308
+ if(this.streak > 0 && Date.now() - this.lastHitTime > 3000) this.streak = 0;
309
+
310
+ // --- Collision Detection ---
311
+ for (let i = this.projectiles.length - 1; i >= 0; i--) {
312
+ const projectile = this.projectiles[i];
313
+ if (projectile.dead) continue;
314
+
315
+ for (let j = this.enemies.length - 1; j >= 0; j--) {
316
+ const enemy = this.enemies[j];
317
+ const enemyRadius = enemy.type === 'boss' ? 40 : 20;
318
+ if (Math.hypot(projectile.x - enemy.x, projectile.y - enemy.y) < (enemyRadius + projectile.size)) {
319
+
320
+ enemy.health--; // Health reduced earlier, easier enemies!
321
+ if (enemy.health <= 0) {
322
+ // Enemy destroyed
323
+ if (Date.now() - this.lastHitTime < 3000) this.streak++; else this.streak = 1;
324
+ this.lastHitTime = Date.now();
325
+
326
+ this.explosions.push(new Explosion(enemy.x, enemy.y, enemy.type, this)); // MAYHEM explosion
327
+
328
+ const basePoints = enemy.type === 'boss' ? 250 : 50; // Less points needed as they die faster
329
+ this.score += Math.round(basePoints * (1 + this.streak * 0.20)); // Higher streak bonus
330
+ this.updateScoreDisplay();
331
+
332
+ this.enemies.splice(j, 1);
333
+ this.shake = enemy.type === 'boss' ? 25 : 12; // MORE SHAKE
334
+ this.playExplosionSound(enemy.type);
335
+
336
+ if(enemy.type === 'boss' && Math.random() > 0.3) { // Very high powerup drop chance
337
+ this.powerUps.push(new PowerUp( enemy.x, enemy.y, ['rapid', 'explosive', 'multi'][Math.floor(Math.random() * 3)] ));
338
+ }
339
+ } else {
340
+ // --- Enemy Hit Spark Particles ---
341
+ this.playHitSound();
342
+ this.shake = 6; // Keep small hit shake
343
+ const impactAngle = Math.atan2(projectile.y - enemy.y, projectile.x - enemy.x);
344
+ for (let k = 0; k < 5 + Math.random() * 5; k++) { // 5-10 sparks
345
+ const angle = impactAngle + (Math.random() - 0.5) * 1.5; // Spread sparks
346
+ const speed = Math.random() * 3 + 1;
347
+ const vx = Math.cos(angle) * speed;
348
+ const vy = Math.sin(angle) * speed;
349
+ const color = `hsl(${Math.random()*20 + 20}, 80%, ${50 + Math.random()*20}%)`; // Orange/Yellow sparks
350
+ const lifespan = Math.random() * 150 + 50; // Short life
351
+ const size = Math.random() * 2 + 0.5;
352
+ this.particles.push(new Particle(projectile.x, projectile.y, vx, vy, color, lifespan, size, 0.1, 0.96)); // More gravity
353
+ }
354
+ }
355
+
356
+ if (projectile.explosive) {
357
+ this.explosions.push(new Explosion(projectile.x, projectile.y, 'normal', this)); // Explosive projectiles also get MAYHEM explosions
358
+ this.playExplosionSound('normal');
359
+ }
360
+ projectile.dead = true;
361
+ break;
362
+ }
363
+ }
364
+ }
365
+
366
+ // Powerup vs Turret (Same logic)
367
+ 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(); } }
368
+
369
+ // --- Cleanup --- Filter dead entities
370
+ 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
371
+ this.particles = this.particles.filter(p => !p.dead);
372
+ // Limit total particles if needed (optional performance safeguard)
373
+ // const MAX_PARTICLES = 3000;
374
+ // if (this.particles.length > MAX_PARTICLES) {
375
+ // this.particles.splice(0, this.particles.length - MAX_PARTICLES);
376
+ // }
377
+ this.explosions = this.explosions.filter(e => !e.dead);
378
+ this.powerUps = this.powerUps.filter(p => !p.dead);
379
+
380
+ // Update in-game stars (Same)
381
+ 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; } });
382
+ }
383
+
384
+ // Draw method (minor adjustments for mayhem feel)
385
+ draw() {
386
+ ctx.save();
387
+ 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; }
388
+
389
+ // Background Color
390
+ ctx.fillStyle = '#080814'; // Even darker
391
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
392
+
393
+ // Background Moving Stars
394
+ 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
395
+
396
+ // Grid Lines (Fainter)
397
+ ctx.strokeStyle = 'rgba(0, 150, 255, 0.06)'; ctx.lineWidth = 0.8;
398
+ for(let x = 0; x < canvas.width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); }
399
+ for(let y = 0; y < canvas.height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); }
400
+
401
+ // Game Entities (Order matters for layering)
402
+ this.powerUps.forEach(p => p.draw(ctx));
403
+ this.turret.draw(ctx);
404
+ this.enemies.forEach(e => e.draw(ctx));
405
+ this.projectiles.forEach(p => p.draw(ctx));
406
+ this.particles.forEach(p => p.draw(ctx)); // Draw ALL particles last
407
+
408
+ ctx.restore(); // Restore before UI
409
+
410
+ // --- UI (Drawn after restoring context) ---
411
+ // Score Box
412
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.strokeStyle = 'rgba(0, 180, 255, 0.7)'; ctx.lineWidth = 2;
413
+ ctx.strokeRect(10, 10, 200, 40); ctx.fillRect(10, 10, 200, 40);
414
+ ctx.font = '18px Orbitron'; ctx.fillStyle = '#00ff00'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
415
+ ctx.shadowColor = '#0f0'; ctx.shadowBlur = 5; // Add glow to score text
416
+ ctx.fillText(`SCORE: ${this.score}`, 20, 30);
417
+ ctx.shadowBlur = 0; // Reset shadow
418
+
419
+ // Powerup Timer (minor visual tweaks)
420
+ 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; } }
421
+ }
422
+
423
+ // updateScoreDisplay, Sound Effects, animate, drawStaticBackground, drawUI (remain same structure)
424
+ updateScoreDisplay() { /* Optional direct DOM update */ }
425
+ _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); }
426
+ playShootSound() { this._playSound('triangle', 700, 120, 0.10, 0.12); } // Slightly adjusted pitch/vol
427
+ playHitSound() { this._playSound('square', 500, 400, 0.06, 0.08); } // Lower pitch hit
428
+ playPowerUpSound() { this._playSound('sine', 440, 1300, 0.18, 0.4); }
429
+ playGameOverSound() { this._playSound('sawtooth', 100, 40, 0.3, 1.2); this._playSound('square', 90, 35, 0.3, 1.2); }
430
+ 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
431
+
432
+ 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(); } }
433
+ 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(); }
434
+ 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; }
435
+ }
436
+
437
+ // --- Turret Class (MAYHEM Edition) ---
438
+ class Turret {
439
+ // Constructor and angle setter/getter same
440
+ 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
441
+ set angle(target) { this.targetAngle = target; }
442
+ get angle() { return this._currentAngle || -Math.PI / 2; }
443
+
444
+ update(deltaTime, game) { // Added game reference
445
+ 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;
446
+ this._currentAngle = current + diff * this.aimSpeed * (deltaTime / 16.67);
447
+
448
+ if (this.cooldown > 0) this.cooldown -= deltaTime;
449
+ if (this.recoil > 0) this.recoil -= deltaTime * 0.4; // Even faster recoil recovery
450
+ if (this.recoil < 0) this.recoil = 0;
451
+ if(this.powerUpActive && Date.now() > this.powerUpEnd) { this.powerUpActive = false; this.powerUpType = null; }
452
+
453
+ // --- Idle Turret Particles ---
454
+ this.idleParticleTimer += deltaTime;
455
+ const idleInterval = this.powerUpActive ? 30 : 150; // Faster sparks when powered up
456
+ if (this.idleParticleTimer > idleInterval) {
457
+ this.idleParticleTimer -= idleInterval;
458
+ const angle = Math.random() * Math.PI * 2; // Emit from base
459
+ const speed = Math.random() * 0.5 + 0.1;
460
+ const vx = Math.cos(angle) * speed;
461
+ const vy = Math.sin(angle) * speed;
462
+ const color = this.powerUpActive ? this.getPowerUpColor() + '80' : 'rgba(100, 150, 255, 0.5)'; // Use powerup color or default blue
463
+ const lifespan = Math.random() * 300 + 200;
464
+ const size = Math.random() * 1.5 + 0.5;
465
+ 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));
466
+ }
467
+ }
468
+
469
+ shoot(game) {
470
+ if(this.cooldown <= 0) {
471
+ game.playShootSound();
472
+ this.recoil = 10; // More recoil visual
473
+
474
+ const shootAngle = this.angle;
475
+ const muzzleOffsetX = Math.cos(shootAngle) * (this.barrelLength - this.recoil);
476
+ const muzzleOffsetY = Math.sin(shootAngle) * (this.barrelLength - this.recoil);
477
+ const startX = this.x + muzzleOffsetX;
478
+ const startY = this.y + muzzleOffsetY;
479
+
480
+ // --- Muzzle Flash Particles ---
481
+ const flashCount = this.powerUpActive && this.powerUpType === 'explosive' ? 25 : 15; // More flash for explosive
482
+ const flashPower = this.powerUpActive && this.powerUpType === 'explosive' ? 6 : 4;
483
+ const flashBaseColor = this.powerUpActive && this.powerUpType === 'explosive' ? 15 : 40; // Orange/Red for explosive, Yellow otherwise
484
+ for (let i = 0; i < flashCount; i++) {
485
+ const angle = shootAngle + (Math.random() - 0.5) * 0.8; // Cone shape
486
+ const speed = Math.random() * flashPower + 1.0;
487
+ const vx = Math.cos(angle) * speed;
488
+ const vy = Math.sin(angle) * speed;
489
+ const color = `hsl(${flashBaseColor + Math.random()*20 - 10}, 100%, ${70 + Math.random()*30}%)`; // Bright yellow/orange/white
490
+ const lifespan = Math.random() * 80 + 40; // Very short life
491
+ const size = Math.random() * 4 + 1;
492
+ game.particles.push(new Particle(startX, startY, vx, vy, color, lifespan, size, 0.02, 0.92, 'lighter')); // Lighter blend
493
+ }
494
+
495
+ // --- Projectile Spawning ---
496
+ const projectileSpeed = 14; // Faster projectiles
497
+ const vx = Math.cos(shootAngle) * projectileSpeed;
498
+ const vy = Math.sin(shootAngle) * projectileSpeed;
499
+ const isExplosive = this.powerUpActive && this.powerUpType === 'explosive';
500
+
501
+ if(this.powerUpActive && this.powerUpType === 'multi') {
502
+ const spreadAngle = 0.2; // Even wider multi
503
+ for(let i = -1; i <= 1; i++) {
504
+ const angle = shootAngle + (i * spreadAngle);
505
+ game.projectiles.push(new Projectile( startX, startY, Math.cos(angle) * projectileSpeed, Math.sin(angle) * projectileSpeed, isExplosive ));
506
+ }
507
+ this.cooldown = 250; // Keep multi cooldown reasonable
508
+ } else {
509
+ game.projectiles.push(new Projectile(startX, startY, vx, vy, isExplosive));
510
+ // Much faster firing rate!
511
+ this.cooldown = this.powerUpActive && this.powerUpType === 'rapid' ? 40 : 100; // RAPID FIRE!
512
+ }
513
+ }
514
+ }
515
+
516
+ // activatePowerUp, getPowerUpColor, draw (remain same structure, visuals already good)
517
+ activatePowerUp(type) { this.powerUpActive = true; this.powerUpType = type; this.powerUpEnd = Date.now() + 9000; } // Longer powerup duration
518
+ getPowerUpColor() { switch(this.powerUpType) { case 'rapid': return '#00ffff'; case 'explosive': return '#ff3300'; case 'multi': return '#ffff00'; default: return '#ffffff'; } }
519
+ 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(); }
520
+ }
521
+
522
+ // --- Projectile Class (MAYHEM Edition) ---
523
+ class Projectile {
524
+ constructor(x, y, vx, vy, explosive) {
525
+ this.x = x; this.y = y; this.vx = vx; this.vy = vy;
526
+ this.dead = false; this.explosive = explosive;
527
+ this.size = explosive ? 8 : 5; // Slightly larger base size
528
+ this.trailTimer = 0;
529
+ this.trailInterval = 8; // FASTER trail emission
530
+ }
531
+
532
+ update(game, deltaTime) {
533
+ const dtFactor = Math.min(deltaTime / 16.67, 4);
534
+ this.x += this.vx * dtFactor; this.y += this.vy * dtFactor;
535
+ if(this.y < -150 || this.y > canvas.height + 150 || this.x < -150 || this.x > canvas.width + 150) this.dead = true;
536
+
537
+ this.trailTimer += deltaTime;
538
+ if (this.trailTimer >= this.trailInterval) {
539
+ this.trailTimer -= this.trailInterval;
540
+ const trailColor = this.explosive ?
541
+ `hsl(${Math.random() * 25 + 2}, 100%, ${65 + Math.random() * 25}%)` : // More intense red/orange
542
+ `hsl(${Math.random() * 25 + 30}, 100%, ${75 + Math.random() * 25}%)`; // Brighter yellow/orange
543
+
544
+ game.particles.push(new Particle(
545
+ this.x + (Math.random()-0.5)*this.size, // Emit from within projectile area
546
+ this.y + (Math.random()-0.5)*this.size,
547
+ this.vx * -0.05 + Math.random() * 1 - 0.5, // Less opposing velocity
548
+ this.vy * -0.05 + Math.random() * 1 - 0.5,
549
+ trailColor,
550
+ 200 + Math.random() * 150, // Longer trail lifespan
551
+ this.explosive ? 5 : 3, // Slightly larger trail particles
552
+ 0.01, 0.97, 'lighter' // Use lighter blend for trails too
553
+ ));
554
+ }
555
+ }
556
+
557
+ // Draw method (same structure, lighter blend will make it brighter)
558
+ draw(ctx) {
559
+ const coreColor = this.explosive ? '#ffeecc' : '#ffffdd';
560
+ const outerColor1 = this.explosive ? '#ff6600' : '#ffaa00';
561
+ const outerColor2 = this.explosive ? '#cc2200' : '#dd6600';
562
+ const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
563
+ gradient.addColorStop(0, coreColor); gradient.addColorStop(0.4, outerColor1); gradient.addColorStop(1, outerColor2);
564
+
565
+ ctx.globalCompositeOperation = 'lighter'; // Draw projectile with lighter blend
566
+ ctx.fillStyle = gradient;
567
+ ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill();
568
+ ctx.shadowColor = this.explosive ? '#ff3300' : '#ff9900';
569
+ ctx.shadowBlur = this.explosive ? 22 : 15; // More glow
570
+ ctx.fill(); // Draw again with shadow enabled
571
+ ctx.shadowBlur = 0;
572
+ ctx.globalCompositeOperation = 'source-over'; // Reset blend mode
573
+ }
574
+ }
575
+
576
+ // --- Enemy Class (MAYHEM Edition - Health Change!) ---
577
+ class Enemy {
578
+ constructor(x, y, speed, type) {
579
+ this.x = x; this.y = y; this.speed = speed; this.type = type || 'normal';
580
+ // --- HEALTH CHANGE ---
581
+ this.initialHealth = type === 'boss' ? 3 : 1; // BOSS: 3 HP, NORMAL: 1 HP
582
+ this._health = this.initialHealth; // Initialize private health
583
+ // --------------------
584
+ this.wobble = Math.random() * Math.PI * 2; this.wobbleSpeed = type === 'boss' ? 0.05 : 0.08; // Faster wobble
585
+ this.wobbleAmount = type === 'boss' ? 8 : 5; // More wobble
586
+ this.angle = 0; this.hitTimer = 0; this.hitDuration = 80; // Shorter hit flash
587
+ }
588
+
589
+ set health(value) { if (value < this._health) { this.hitTimer = this.hitDuration; } this._health = value; }
590
+ get health() { return this._health; } // No need for default here anymore
591
+
592
+ // Update method (same logic, checks remain)
593
+ 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); } } }
594
+
595
+ // Draw method (same structure, flash effect remains)
596
+ 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(); }
597
+ }
598
+
599
+ // --- PowerUp Class (Same structure, visuals already good) ---
600
+ 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(); } }
601
+
602
+ // --- Initialize and Start Game (Same) ---
603
+ function initGame() { game = new Game(); game.drawStaticBackground(); }
604
+ function resizeCanvas() { canvasRect = canvas.getBoundingClientRect(); }
605
+ window.addEventListener('resize', resizeCanvas);
606
+ resizeCanvas();
607
+ initGame();
608
+
609
+ </script>
610
+ <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>
611
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ pimp it up yo