LukasBe commited on
Commit
4b64993
·
verified ·
1 Parent(s): 1d1b529

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1421 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Breakout Game
3
- emoji: 🌍
4
- colorFrom: pink
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: breakout-game
3
+ emoji: 🐳
4
+ colorFrom: blue
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,1421 @@
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>Cosmic Laser Breakout 3D</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
8
+ <script src="https://kit.fontawesome.com/5a4d8a29cf.js" crossorigin="anonymous"></script>
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ overflow: hidden;
18
+ background: radial-gradient(ellipse at center, #000000 0%, #16004b 100%);
19
+ color: #fff;
20
+ font-family: 'Orbitron', sans-serif;
21
+ display: flex;
22
+ flex-direction: column;
23
+ align-items: center;
24
+ justify-content: center;
25
+ height: 100vh;
26
+ perspective: 1200px;
27
+ }
28
+
29
+ #gameWrapper {
30
+ position: relative;
31
+ width: 1000px;
32
+ height: 700px;
33
+ transform-style: preserve-3d;
34
+ transform: rotateX(10deg) rotateY(0deg) rotateZ(0deg);
35
+ perspective: 1500px;
36
+ filter: drop-shadow(0 0 50px rgba(107, 0, 255, 0.7));
37
+ }
38
+
39
+ #gameContainer {
40
+ position: relative;
41
+ width: 100%;
42
+ height: 100%;
43
+ border: 4px solid transparent;
44
+ border-image: linear-gradient(45deg, #6b00ff, #00dcff, #ff00aa);
45
+ border-image-slice: 1;
46
+ box-shadow:
47
+ 0 0 60px rgba(107, 0, 255, 0.8),
48
+ inset 0 0 40px rgba(107, 0, 255, 0.6),
49
+ inset 0 0 80px rgba(0, 220, 255, 0.4),
50
+ inset 0 0 120px rgba(255, 0, 170, 0.2);
51
+ overflow: hidden;
52
+ background:
53
+ radial-gradient(circle at 50% 0%, rgba(107, 0, 255, 0.2) 0%, transparent 70%),
54
+ radial-gradient(circle at 50% 100%, rgba(0, 220, 255, 0.2) 0%, transparent 70%),
55
+ linear-gradient(0deg, rgba(0, 0, 30, 0.9) 0%, rgba(10, 0, 80, 0.7) 100%);
56
+ transform-style: preserve-3d;
57
+ backdrop-filter: blur(2px);
58
+ }
59
+
60
+ #paddle {
61
+ position: absolute;
62
+ width: 140px;
63
+ height: 20px;
64
+ background: linear-gradient(90deg, #00dcff, #6b00ff);
65
+ border-radius: 12px;
66
+ box-shadow:
67
+ 0 0 40px rgba(0, 220, 255, 0.8),
68
+ 0 0 80px rgba(107, 0, 255, 0.4),
69
+ inset 0 5px 10px rgba(255, 255, 255, 0.4);
70
+ bottom: 40px;
71
+ left: 430px;
72
+ z-index: 10;
73
+ transition: transform 0.1s cubic-bezier(0.17, 0.67, 0.83, 0.67);
74
+ transform-origin: center bottom;
75
+ transform: translateZ(20px);
76
+ }
77
+
78
+ #paddle::before {
79
+ content: '';
80
+ position: absolute;
81
+ top: -5px;
82
+ left: 0;
83
+ right: 0;
84
+ height: 5px;
85
+ background: linear-gradient(90deg, #00dcff, #6b00ff);
86
+ border-radius: 5px;
87
+ filter: blur(2px);
88
+ opacity: 0.8;
89
+ }
90
+
91
+ #paddle::after {
92
+ content: '';
93
+ position: absolute;
94
+ bottom: -10px;
95
+ left: 10%;
96
+ right: 10%;
97
+ height: 5px;
98
+ background: rgba(0, 220, 255, 0.3);
99
+ border-radius: 50%;
100
+ filter: blur(5px);
101
+ transform: translateZ(-5px);
102
+ }
103
+
104
+ #ball {
105
+ position: absolute;
106
+ width: 18px;
107
+ height: 18px;
108
+ background: radial-gradient(circle at 30% 30%, #fff, #00dcff);
109
+ border-radius: 50%;
110
+ box-shadow:
111
+ 0 0 15px #fff,
112
+ 0 0 30px rgba(255, 255, 255, 0.8),
113
+ 0 0 60px rgba(255, 255, 255, 0.4),
114
+ inset 0 0 5px rgba(255, 255, 255, 0.8);
115
+ z-index: 20;
116
+ transform-style: preserve-3d;
117
+ filter: drop-shadow(0 0 5px #fff);
118
+ }
119
+
120
+ .brick {
121
+ position: absolute;
122
+ width: 92px;
123
+ height: 32px;
124
+ border-radius: 4px;
125
+ transform: translateZ(20px);
126
+ opacity: 1;
127
+ transition: all 0.3s ease-out;
128
+ perspective: 500px;
129
+ border-bottom: 4px solid rgba(0,0,0,0.4);
130
+ overflow: hidden;
131
+ will-change: transform, opacity;
132
+ }
133
+
134
+ .brick::before {
135
+ content: '';
136
+ position: absolute;
137
+ top: 0;
138
+ left: 0;
139
+ right: 0;
140
+ height: 50%;
141
+ background: linear-gradient(to bottom, rgba(255,255,255,0.4) 0%, transparent 100%);
142
+ border-radius: 4px 4px 0 0;
143
+ transform-origin: top;
144
+ transform: rotateX(60deg);
145
+ }
146
+
147
+ .brick::after {
148
+ content: '';
149
+ position: absolute;
150
+ bottom: -2px;
151
+ left: 5%;
152
+ right: 5%;
153
+ height: 3px;
154
+ background: rgba(255,255,255,0.7);
155
+ border-radius: 50%;
156
+ filter: blur(2px);
157
+ transform: translateZ(-5px);
158
+ }
159
+
160
+ #scoreDisplay, #livesDisplay {
161
+ position: absolute;
162
+ top: 20px;
163
+ font-size: 24px;
164
+ font-weight: 700;
165
+ letter-spacing: 2px;
166
+ text-shadow:
167
+ 0 0 10px #6b00ff,
168
+ 0 0 20px rgba(107, 0, 255, 0.8);
169
+ color: #fff;
170
+ z-index: 10;
171
+ transform: translateZ(30px);
172
+ font-family: 'Orbitron', sans-serif;
173
+ }
174
+
175
+ #scoreDisplay { left: 20px; }
176
+ #livesDisplay { right: 20px; }
177
+
178
+ #comboCounter {
179
+ position: absolute;
180
+ top: 50%;
181
+ left: 50%;
182
+ transform: translate(-50%, -50%) translateZ(30px) scale(0);
183
+ font-size: 48px;
184
+ font-weight: 900;
185
+ letter-spacing: 4px;
186
+ color: #ff00aa;
187
+ text-shadow:
188
+ 0 0 20px #ff00aa,
189
+ 0 0 40px rgba(255, 0, 170, 0.8);
190
+ z-index: 10;
191
+ opacity: 0;
192
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
193
+ }
194
+
195
+ #gameOver {
196
+ position: absolute;
197
+ top: 50%;
198
+ left: 50%;
199
+ transform: translate(-50%, -50%) translateZ(50px);
200
+ font-size: 80px;
201
+ font-weight: 900;
202
+ color: #ff00aa;
203
+ text-shadow:
204
+ 0 0 20px #ff00aa,
205
+ 0 0 40px rgba(255, 0, 170, 0.9),
206
+ 0 0 80px rgba(255, 0, 170, 0.7);
207
+ opacity: 0;
208
+ pointer-events: none;
209
+ transition: opacity 0.5s ease-out;
210
+ letter-spacing: 5px;
211
+ z-index: 100;
212
+ transform-style: preserve-3d;
213
+ text-transform: uppercase;
214
+ text-align: center;
215
+ }
216
+
217
+ #startMessage {
218
+ position: absolute;
219
+ top: 70%;
220
+ left: 50%;
221
+ transform: translate(-50%, -50%) translateZ(30px);
222
+ font-size: 24px;
223
+ color: #00dcff;
224
+ text-shadow:
225
+ 0 0 10px #6b00ff,
226
+ 0 0 20px rgba(107, 0, 255, 0.8);
227
+ text-align: center;
228
+ opacity: 1;
229
+ transition: opacity 0.3s ease-out;
230
+ letter-spacing: 2px;
231
+ z-index: 50;
232
+ }
233
+
234
+ #startBtn {
235
+ position: absolute;
236
+ top: 60%;
237
+ left: 50%;
238
+ transform: translate(-50%, 0) translateZ(30px);
239
+ padding: 12px 30px;
240
+ background: linear-gradient(135deg, #ff00aa 0%, #6b00ff 100%);
241
+ border: none;
242
+ border-radius: 30px;
243
+ color: white;
244
+ font-weight: bold;
245
+ font-size: 18px;
246
+ letter-spacing: 2px;
247
+ cursor: pointer;
248
+ opacity: 1;
249
+ transition: all 0.3s ease-out;
250
+ box-shadow:
251
+ 0 0 10px #ff00aa,
252
+ 0 0 30px rgba(255, 0, 170, 0.8);
253
+ z-index: 50;
254
+ text-transform: uppercase;
255
+ font-family: 'Orbitron', sans-serif;
256
+ }
257
+
258
+ #startBtn:hover {
259
+ background: linear-gradient(135deg, #ff00aa 0%, #00dcff 100%);
260
+ box-shadow:
261
+ 0 0 15px #ff00aa,
262
+ 0 0 50px rgba(255, 0, 170, 0.9);
263
+ }
264
+
265
+ .particle {
266
+ position: absolute;
267
+ width: 10px;
268
+ height: 10px;
269
+ border-radius: 50%;
270
+ pointer-events: none;
271
+ z-index: 5;
272
+ will-change: transform, opacity;
273
+ }
274
+
275
+ .explosion {
276
+ position: absolute;
277
+ border-radius: 50%;
278
+ pointer-events: none;
279
+ z-index: 20;
280
+ transform-style: preserve-3d;
281
+ will-change: transform, opacity;
282
+ }
283
+
284
+ .bonus {
285
+ position: absolute;
286
+ width: 24px;
287
+ height: 24px;
288
+ border-radius: 50%;
289
+ z-index: 15;
290
+ font-family: 'Orbitron', sans-serif;
291
+ font-weight: bold;
292
+ text-align: center;
293
+ line-height: 24px;
294
+ font-size: 14px;
295
+ box-shadow:
296
+ 0 0 10px currentColor,
297
+ 0 0 20px rgba(255,255,255,0.4);
298
+ animation: float 1s ease-in-out infinite alternate;
299
+ }
300
+
301
+ @keyframes float {
302
+ 0% { transform: translateY(0px); }
303
+ 100% { transform: translateY(-3px); }
304
+ }
305
+
306
+ .bonus.multiball {
307
+ background: radial-gradient(circle at 30% 30%, #00dcff, #6b00ff);
308
+ color: white;
309
+ }
310
+
311
+ .bonus.extralife {
312
+ background: radial-gradient(circle at 30% 30%, #ff0000, #ff00aa);
313
+ color: white;
314
+ }
315
+
316
+ .bonus.widepaddle {
317
+ background: radial-gradient(circle at 30% 30%, #ffff00, #ffaa00);
318
+ color: black;
319
+ }
320
+
321
+ .bonus.slowmo {
322
+ background: radial-gradient(circle at 30% 30%, #00ff00, #007700);
323
+ color: white;
324
+ }
325
+
326
+ .bonus.points {
327
+ background: radial-gradient(circle at 30% 30%, #ffffff, #aaaaaa);
328
+ color: black;
329
+ }
330
+
331
+ .powerup-active {
332
+ animation: powerupGlow 0.5s ease-in-out infinite alternate;
333
+ }
334
+
335
+ @keyframes powerupGlow {
336
+ 0% { box-shadow: 0 0 10px #ff00aa; }
337
+ 100% { box-shadow: 0 0 40px #ff00aa; }
338
+ }
339
+
340
+ .trail {
341
+ position: absolute;
342
+ width: 10px;
343
+ height: 10px;
344
+ border-radius: 50%;
345
+ pointer-events: none;
346
+ z-index: 19;
347
+ filter: blur(1px);
348
+ transform: scale(0);
349
+ opacity: 0;
350
+ will-change: transform, opacity;
351
+ }
352
+
353
+ .ball-pulse {
354
+ position: absolute;
355
+ border-radius: 50%;
356
+ border: 2px solid rgba(0, 220, 255, 0.8);
357
+ pointer-events: none;
358
+ z-index: 18;
359
+ transform: scale(0);
360
+ opacity: 1;
361
+ will-change: transform, opacity;
362
+ }
363
+
364
+ .star {
365
+ position: absolute;
366
+ width: 6px;
367
+ height: 6px;
368
+ background: white;
369
+ clip-path: polygon(
370
+ 50% 0%,
371
+ 61% 35%,
372
+ 98% 35%,
373
+ 68% 57%,
374
+ 79% 91%,
375
+ 50% 70%,
376
+ 21% 91%,
377
+ 32% 57%,
378
+ 2% 35%,
379
+ 39% 35%
380
+ );
381
+ pointer-events: none;
382
+ z-index: 3;
383
+ will-change: transform, opacity;
384
+ filter: drop-shadow(0 0 5px currentColor);
385
+ }
386
+ </style>
387
+ </head>
388
+ <body>
389
+ <div id="gameWrapper">
390
+ <div id="gameContainer">
391
+ <div id="paddle"></div>
392
+ <div id="ball"></div>
393
+ <div id="scoreDisplay">SCORE: 0</div>
394
+ <div id="livesDisplay">LIVES: 3</div>
395
+ <div id="comboCounter">COMBO x3!</div>
396
+ <div id="gameOver">GAME OVER</div>
397
+ <div id="startMessage">COSMIC LASER BREAKOUT</div>
398
+ <button id="startBtn">START GAME</button>
399
+ </div>
400
+ </div>
401
+
402
+ <script>
403
+ document.addEventListener('DOMContentLoaded', () => {
404
+ // Game elements
405
+ const gameContainer = document.getElementById('gameContainer');
406
+ const paddle = document.getElementById('paddle');
407
+ const ball = document.getElementById('ball');
408
+ const scoreDisplay = document.getElementById('scoreDisplay');
409
+ const livesDisplay = document.getElementById('livesDisplay');
410
+ const comboCounter = document.getElementById('comboCounter');
411
+ const gameOver = document.getElementById('gameOver');
412
+ const startMessage = document.getElementById('startMessage');
413
+ const startBtn = document.getElementById('startBtn');
414
+
415
+ // Game dimensions
416
+ const gameWidth = gameContainer.offsetWidth;
417
+ const gameHeight = gameContainer.offsetHeight;
418
+ const paddleWidth = paddle.offsetWidth;
419
+ const paddleHeight = paddle.offsetHeight;
420
+ const ballSize = ball.offsetWidth;
421
+
422
+ // Game state
423
+ let paddleX = (gameWidth - paddleWidth) / 2;
424
+ let ballX = gameWidth / 2 - ballSize / 2;
425
+ let ballY = gameHeight - paddleHeight - 40 - ballSize;
426
+ let ballSpeedX = 0;
427
+ let ballSpeedY = 0;
428
+ let score = 0;
429
+ let lives = 3;
430
+ let combo = 0;
431
+ let gameStarted = false;
432
+ let gameActive = false;
433
+ let bricks = [];
434
+ let brickCount = 0;
435
+ let activePowerups = [];
436
+ let balls = [{ x: ballX, y: ballY, speedX: ballSpeedX, speedY: ballSpeedY, element: ball }];
437
+ let trailInterval;
438
+ let starParticles = [];
439
+ let originalPaddleWidth = paddleWidth;
440
+
441
+ // Initialize game elements
442
+ function initGame() {
443
+ // Position paddle and ball
444
+ paddleX = (gameWidth - originalPaddleWidth) / 2;
445
+ paddle.style.width = originalPaddleWidth + 'px';
446
+ paddle.style.left = paddleX + 'px';
447
+ ballX = paddleX + originalPaddleWidth / 2 - ballSize / 2;
448
+ ballY = gameHeight - paddleHeight - 40 - ballSize;
449
+ ball.style.left = ballX + 'px';
450
+ ball.style.top = ballY + 'px';
451
+ ballSpeedX = 0;
452
+ ballSpeedY = 0;
453
+
454
+ // Create bricks
455
+ createBricks();
456
+
457
+ // Clear active powerups
458
+ activePowerups.forEach(powerup => {
459
+ clearTimeout(powerup.timer);
460
+ });
461
+ activePowerups = [];
462
+
463
+ // Remove any extra balls
464
+ balls.slice(1).forEach(b => {
465
+ if (gameContainer.contains(b.element)) {
466
+ gameContainer.removeChild(b.element);
467
+ }
468
+ });
469
+ balls = [{ x: ballX, y: ballY, speedX: ballSpeedX, speedY: ballSpeedY, element: ball }];
470
+
471
+ // Update displays
472
+ scoreDisplay.textContent = `SCORE: ${score}`;
473
+ livesDisplay.textContent = `LIVES: ${lives}`;
474
+
475
+ // Hide game over if visible
476
+ gameOver.style.opacity = '0';
477
+
478
+ gameActive = true;
479
+ }
480
+
481
+ // Create brick layout
482
+ function createBricks() {
483
+ // Clear existing bricks
484
+ bricks.forEach(brick => {
485
+ if (gameContainer.contains(brick)) {
486
+ gameContainer.removeChild(brick);
487
+ }
488
+ });
489
+ bricks = [];
490
+ brickCount = 0;
491
+
492
+ const rows = 5;
493
+ const cols = 10;
494
+ const brickWidth = 92;
495
+ const brickHeight = 32;
496
+ const brickPadding = 4;
497
+ const offsetTop = 60;
498
+ const offsetLeft = (gameWidth - (cols * (brickWidth + brickPadding))) / 2;
499
+
500
+ // Colors for different brick rows
501
+ const colors = [
502
+ 'rgba(255, 50, 50, 0.8)',
503
+ 'rgba(255, 153, 51, 0.8)',
504
+ 'rgba(247, 219, 96, 0.8)',
505
+ 'rgba(80, 200, 120, 0.8)',
506
+ 'rgba(100, 175, 255, 0.8)'
507
+ ];
508
+
509
+ for (let r = 0; r < rows; r++) {
510
+ for (let c = 0; c < cols; c++) {
511
+ const brick = document.createElement('div');
512
+ brick.className = 'brick';
513
+ brick.style.left = offsetLeft + c * (brickWidth + brickPadding) + 'px';
514
+ brick.style.top = offsetTop + r * (brickHeight + brickPadding) + 'px';
515
+ brick.style.backgroundColor = colors[r];
516
+
517
+ brick.hits = 1; // Number of hits to break (1 for standard bricks)
518
+ brick.scoreValue = (rows - r) * 10; // More points for higher rows
519
+
520
+ // 15% chance to have a powerup brick
521
+ if (Math.random() < 0.15) {
522
+ brick.hasPowerup = true;
523
+ brick.powerupType = ['multiball', 'extralife', 'widepaddle', 'slowmo', 'points'][Math.floor(Math.random() * 5)];
524
+ }
525
+
526
+ gameContainer.appendChild(brick);
527
+ bricks.push(brick);
528
+ brickCount++;
529
+ }
530
+ }
531
+ }
532
+
533
+ // Launch the ball
534
+ function launchBall() {
535
+ if (!gameActive || gameStarted) return;
536
+
537
+ gameStarted = true;
538
+ startMessage.style.opacity = '0';
539
+ startBtn.style.opacity = '0';
540
+ startBtn.style.pointerEvents = 'none';
541
+
542
+ // Random initial angle
543
+ const angle = (Math.random() * Math.PI / 2) + Math.PI / 4; // Between 45 and 135 degrees
544
+ const speed = 4;
545
+ ballSpeedX = Math.cos(angle) * speed;
546
+ ballSpeedY = -Math.sin(angle) * speed;
547
+
548
+ balls[0].speedX = ballSpeedX;
549
+ balls[0].speedY = ballSpeedY;
550
+
551
+ // Start ball trail
552
+ if (!trailInterval) {
553
+ trailInterval = setInterval(createBallTrail, 50);
554
+ }
555
+
556
+ // Create stars background
557
+ createStars();
558
+ }
559
+
560
+ // Create stars background
561
+ function createStars() {
562
+ // Clear existing stars
563
+ starParticles.forEach(star => {
564
+ if (gameContainer.contains(star)) {
565
+ gameContainer.removeChild(star);
566
+ }
567
+ });
568
+ starParticles = [];
569
+
570
+ // Create 100 stars
571
+ for (let i = 0; i < 100; i++) {
572
+ const star = document.createElement('div');
573
+ star.className = 'star';
574
+ star.style.left = Math.random() * gameWidth + 'px';
575
+ star.style.top = Math.random() * gameHeight + 'px';
576
+
577
+ // Random size and color
578
+ const size = 2 + Math.random() * 5;
579
+ const hue = Math.random() * 360;
580
+ const opacity = 0.2 + Math.random() * 0.8;
581
+
582
+ star.style.width = size + 'px';
583
+ star.style.height = size + 'px';
584
+ star.style.color = `hsla(${hue}, 100%, 80%, ${opacity})`;
585
+
586
+ // Random animation for twinkling
587
+ star.style.animation = `float ${2 + Math.random() * 3}s ease-in-out infinite ${Math.random() * 3}s alternate`;
588
+
589
+ gameContainer.appendChild(star);
590
+ starParticles.push(star);
591
+ }
592
+ }
593
+
594
+ // Create ball trail effect
595
+ function createBallTrail() {
596
+ if (!gameStarted || !gameActive) return;
597
+
598
+ balls.forEach(ballData => {
599
+ const trail = document.createElement('div');
600
+ trail.className = 'trail';
601
+ trail.style.left = (ballData.x + ballSize/2) + 'px';
602
+ trail.style.top = (ballData.y + ballSize/2) + 'px';
603
+ trail.style.background = `radial-gradient(circle, rgba(0,220,255,0.8) 0%, rgba(107,0,255,0.6) 100%)`;
604
+ trail.style.transform = 'scale(0.5)';
605
+
606
+ gameContainer.appendChild(trail);
607
+
608
+ // Animate trail
609
+ setTimeout(() => {
610
+ trail.style.transform = 'scale(1.5)';
611
+ trail.style.opacity = '0.6';
612
+
613
+ setTimeout(() => {
614
+ if (gameContainer.contains(trail)) {
615
+ gameContainer.removeChild(trail);
616
+ }
617
+ }, 200);
618
+ }, 10);
619
+ });
620
+ }
621
+
622
+ // Create a pulse effect when ball hits something
623
+ function createBallPulse(x, y) {
624
+ const pulse = document.createElement('div');
625
+ pulse.className = 'ball-pulse';
626
+ pulse.style.left = x + 'px';
627
+ pulse.style.top = y + 'px';
628
+ pulse.style.width = ballSize + 'px';
629
+ pulse.style.height = ballSize + 'px';
630
+ pulse.style.borderColor = `hsl(${Math.random() * 360}, 100%, 70%)`;
631
+
632
+ gameContainer.appendChild(pulse);
633
+
634
+ // Animate pulse
635
+ let scale = 1;
636
+ const animate = () => {
637
+ scale += 0.2;
638
+ pulse.style.transform = `scale(${scale})`;
639
+ pulse.style.opacity = 1 - scale * 0.1;
640
+
641
+ if (scale < 5) {
642
+ requestAnimationFrame(animate);
643
+ } else {
644
+ if (gameContainer.contains(pulse)) {
645
+ gameContainer.removeChild(pulse);
646
+ }
647
+ }
648
+ };
649
+
650
+ requestAnimationFrame(animate);
651
+ }
652
+
653
+ // Reset ball after losing a life
654
+ function resetBall() {
655
+ gameStarted = false;
656
+ ballX = paddleX + paddleWidth / 2 - ballSize / 2;
657
+ ballY = gameHeight - paddleHeight - 40 - ballSize;
658
+ ball.style.left = ballX + 'px';
659
+ ball.style.top = ballY + 'px';
660
+ ballSpeedX = 0;
661
+ ballSpeedY = 0;
662
+
663
+ // Update main ball position in array
664
+ balls[0].x = ballX;
665
+ balls[0].y = ballY;
666
+ balls[0].speedX = 0;
667
+ balls[0].speedY = 0;
668
+
669
+ startMessage.style.opacity = '1';
670
+ startMessage.textContent = 'GET READY';
671
+ startBtn.style.opacity = '1';
672
+ startBtn.style.pointerEvents = 'auto';
673
+ startBtn.textContent = 'CONTINUE';
674
+
675
+ // Clear trail interval since ball is not moving
676
+ clearInterval(trailInterval);
677
+ trailInterval = null;
678
+ }
679
+
680
+ // Game over
681
+ function endGame() {
682
+ gameActive = false;
683
+ gameOver.style.opacity = '1';
684
+ startBtn.style.opacity = '1';
685
+ startBtn.style.pointerEvents = 'auto';
686
+ startBtn.textContent = 'PLAY AGAIN';
687
+
688
+ // Clear trail interval
689
+ clearInterval(trailInterval);
690
+ trailInterval = null;
691
+ }
692
+
693
+ // Create a bonus/powerup that falls from brick
694
+ function createBonus(x, y, type) {
695
+ const bonus = document.createElement('div');
696
+ bonus.className = `bonus ${type}`;
697
+ bonus.style.left = x + 'px';
698
+ bonus.style.top = y + 'px';
699
+
700
+ // Set symbol based on type
701
+ switch(type) {
702
+ case 'multiball':
703
+ bonus.textContent = 'M';
704
+ break;
705
+ case 'extralife':
706
+ bonus.textContent = '+';
707
+ break;
708
+ case 'widepaddle':
709
+ bonus.textContent = 'W';
710
+ break;
711
+ case 'slowmo':
712
+ bonus.textContent = 'S';
713
+ break;
714
+ case 'points':
715
+ bonus.textContent = 'P';
716
+ break;
717
+ }
718
+
719
+ gameContainer.appendChild(bonus);
720
+
721
+ // Animate falling
722
+ let speed = 2;
723
+ let spin = (Math.random() - 0.5) * 5;
724
+ let rotation = 0;
725
+
726
+ const fall = () => {
727
+ y += speed;
728
+ rotation += spin;
729
+
730
+ bonus.style.top = y + 'px';
731
+ bonus.style.transform = `rotate(${rotation}deg)`;
732
+
733
+ // Check if caught by paddle
734
+ if (y + 24 >= gameHeight - paddleHeight - 40 &&
735
+ y + 24 <= gameHeight - 40 &&
736
+ x + 24 >= paddleX &&
737
+ x <= paddleX + paddleWidth) {
738
+
739
+ // Apply powerup
740
+ activatePowerup(type);
741
+
742
+ // Remove bonus
743
+ gameContainer.removeChild(bonus);
744
+ return;
745
+ }
746
+
747
+ // Check if fell off screen
748
+ if (y < gameHeight) {
749
+ requestAnimationFrame(fall);
750
+ } else {
751
+ gameContainer.removeChild(bonus);
752
+ }
753
+ };
754
+
755
+ requestAnimationFrame(fall);
756
+ }
757
+
758
+ // Activate a powerup
759
+ function activatePowerup(type) {
760
+ // Create powerup activation effect
761
+ createPowerupActivationEffect(paddleX + paddleWidth/2, gameHeight - paddleHeight - 20, type);
762
+
763
+ switch(type) {
764
+ case 'multiball':
765
+ // Create 2 extra balls
766
+ for (let i = 0; i < 2; i++) {
767
+ const newBall = document.createElement('div');
768
+ newBall.className = 'ball';
769
+ newBall.style.width = ballSize + 'px';
770
+ newBall.style.height = ballSize + 'px';
771
+ newBall.style.left = balls[0].x + 'px';
772
+ newBall.style.top = balls[0].y + 'px';
773
+
774
+ gameContainer.appendChild(newBall);
775
+
776
+ // Random angle for new balls
777
+ const angle = Math.random() * Math.PI/2 - Math.PI/4;
778
+ const speed = 5;
779
+
780
+ balls.push({
781
+ x: balls[0].x,
782
+ y: balls[0].y,
783
+ speedX: Math.cos(angle) * speed,
784
+ speedY: -Math.sin(angle) * speed,
785
+ element: newBall
786
+ });
787
+ }
788
+
789
+ // Show message
790
+ showPowerupMessage("MULTI BALL!");
791
+ break;
792
+
793
+ case 'extralife':
794
+ // Add extra life
795
+ lives++;
796
+ livesDisplay.textContent = `LIVES: ${lives}`;
797
+
798
+ // Show message
799
+ showPowerupMessage("EXTRA LIFE!");
800
+ break;
801
+
802
+ case 'widepaddle':
803
+ // Double paddle width
804
+ paddle.style.width = (originalPaddleWidth * 2) + 'px';
805
+ paddleX = Math.max(0, Math.min(paddleX, gameWidth - originalPaddleWidth * 2));
806
+ paddle.style.left = paddleX + 'px';
807
+
808
+ // Set timeout to return to normal
809
+ const timer = setTimeout(() => {
810
+ paddle.style.width = originalPaddleWidth + 'px';
811
+ paddleX = Math.max(0, Math.min(paddleX, gameWidth - originalPaddleWidth));
812
+ paddle.style.left = paddleX + 'px';
813
+
814
+ // Remove from active powerups
815
+ const index = activePowerups.findIndex(p => p.type === 'widepaddle');
816
+ if (index >= 0) {
817
+ activePowerups.splice(index, 1);
818
+ }
819
+ }, 10000); // 10 seconds
820
+
821
+ activePowerups.push({ type: 'widepaddle', timer });
822
+
823
+ // Show message
824
+ showPowerupMessage("WIDE PADDLE!");
825
+ break;
826
+
827
+ case 'slowmo':
828
+ // Slow down all balls for 8 seconds
829
+ balls.forEach(b => {
830
+ b.speedX *= 0.5;
831
+ b.speedY *= 0.5;
832
+ });
833
+
834
+ const slowmoTimer = setTimeout(() => {
835
+ // Return to normal speed
836
+ balls.forEach(b => {
837
+ b.speedX *= 2;
838
+ b.speedY *= 2;
839
+ });
840
+
841
+ // Remove from active powerups
842
+ const index = activePowerups.findIndex(p => p.type === 'slowmo');
843
+ if (index >= 0) {
844
+ activePowerups.splice(index, 1);
845
+ }
846
+ }, 8000); // 8 seconds
847
+
848
+ activePowerups.push({ type: 'slowmo', timer: slowmoTimer });
849
+
850
+ // Show message
851
+ showPowerupMessage("SLOW MOTION!");
852
+ break;
853
+
854
+ case 'points':
855
+ // Add bonus points
856
+ score += 500;
857
+ scoreDisplay.textContent = `SCORE: ${score}`;
858
+
859
+ // Show message
860
+ showPowerupMessage("500 POINTS!");
861
+ break;
862
+ }
863
+ }
864
+
865
+ // Show powerup activation message
866
+ function showPowerupMessage(text) {
867
+ const message = document.createElement('div');
868
+ message.textContent = text;
869
+ message.style.position = 'absolute';
870
+ message.style.top = '20%';
871
+ message.style.left = '50%';
872
+ message.style.transform = 'translate(-50%, 0) translateZ(40px)';
873
+ message.style.fontSize = '32px';
874
+ message.style.fontWeight = '900';
875
+ message.style.color = '#00dcff';
876
+ message.style.textShadow = '0 0 10px #6b00ff, 0 0 20px rgba(107, 0, 255, 0.8)';
877
+ message.style.opacity = '1';
878
+ message.style.transition = 'all 0.5s ease-out';
879
+ message.style.zIndex = '100';
880
+
881
+ gameContainer.appendChild(message);
882
+
883
+ // Animate
884
+ setTimeout(() => {
885
+ message.style.opacity = '0';
886
+ message.style.transform = 'translate(-50%, -20px) translateZ(40px)';
887
+
888
+ setTimeout(() => {
889
+ if (gameContainer.contains(message)) {
890
+ gameContainer.removeChild(message);
891
+ }
892
+ }, 500);
893
+ }, 1000);
894
+ }
895
+
896
+ // Create powerup activation effect
897
+ function createPowerupActivationEffect(x, y, type) {
898
+ // Create a circular burst effect
899
+ const particles = 20;
900
+ const colors = {
901
+ 'multiball': '#6b00ff',
902
+ 'extralife': '#ff00aa',
903
+ 'widepaddle': '#ffff00',
904
+ 'slowmo': '#00ff00',
905
+ 'points': '#ffffff'
906
+ };
907
+
908
+ for (let i = 0; i < particles; i++) {
909
+ const particle = document.createElement('div');
910
+ particle.className = 'particle';
911
+ particle.style.left = x + 'px';
912
+ particle.style.top = y + 'px';
913
+ particle.style.background = colors[type];
914
+ particle.style.boxShadow = `0 0 10px ${colors[type]}`;
915
+
916
+ // Random angle and speed
917
+ const angle = Math.random() * Math.PI * 2;
918
+ const speed = 1 + Math.random() * 5;
919
+ const size = 4 + Math.random() * 12;
920
+
921
+ // Set random size
922
+ particle.style.width = size + 'px';
923
+ particle.style.height = size + 'px';
924
+
925
+ gameContainer.appendChild(particle);
926
+
927
+ let posX = 0;
928
+ let posY = 0;
929
+ let opacity = 1;
930
+
931
+ const animate = () => {
932
+ posX += Math.cos(angle) * speed;
933
+ posY += Math.sin(angle) * speed;
934
+ opacity -= 0.02;
935
+
936
+ particle.style.transform = `translate(${posX}px, ${posY}px)`;
937
+ particle.style.opacity = opacity;
938
+
939
+ if (opacity > 0) {
940
+ requestAnimationFrame(animate);
941
+ } else {
942
+ if (gameContainer.contains(particle)) {
943
+ gameContainer.removeChild(particle);
944
+ }
945
+ }
946
+ };
947
+
948
+ requestAnimationFrame(animate);
949
+ }
950
+ }
951
+
952
+ // Check collision between ball and brick
953
+ function checkBrickCollision(ballData) {
954
+ for (let i = 0; i < bricks.length; i++) {
955
+ const brick = bricks[i];
956
+ if (!brick || !gameContainer.contains(brick)) continue;
957
+
958
+ if (ballData.x + ballSize > parseInt(brick.style.left) &&
959
+ ballData.x < parseInt(brick.style.left) + brick.offsetWidth &&
960
+ ballData.y + ballSize > parseInt(brick.style.top) &&
961
+ ballData.y < parseInt(brick.style.top) + brick.offsetHeight) {
962
+
963
+ // Hit the brick
964
+ brick.hits--;
965
+
966
+ // Add to combo
967
+ combo++;
968
+ if (combo > 1) {
969
+ showCombo(combo);
970
+ }
971
+
972
+ // Play hit effect
973
+ createBrickHitEffect(brick);
974
+
975
+ if (brick.hits <= 0) {
976
+ // Break the brick
977
+ score += brick.scoreValue * combo; // Multiply by combo
978
+ scoreDisplay.textContent = `SCORE: ${score}`;
979
+ brickCount--;
980
+
981
+ // Check if brick had a powerup
982
+ if (brick.hasPowerup) {
983
+ createBonus(
984
+ parseInt(brick.style.left) + brick.offsetWidth / 2 - 12,
985
+ parseInt(brick.style.top) + brick.offsetHeight,
986
+ brick.powerupType
987
+ );
988
+ }
989
+
990
+ // Create explosion effect
991
+ createExplosion(
992
+ parseInt(brick.style.left) + brick.offsetWidth / 2,
993
+ parseInt(brick.style.top) + brick.offsetHeight / 2,
994
+ brick.style.backgroundColor
995
+ );
996
+
997
+ gameContainer.removeChild(brick);
998
+ bricks[i] = null;
999
+ } else {
1000
+ // Just a hit, change color to show damage
1001
+ brick.style.opacity = '0.6';
1002
+ }
1003
+
1004
+ // Check if all bricks are cleared
1005
+ if (brickCount <= 0) {
1006
+ // Level complete!
1007
+ setTimeout(() => {
1008
+ createBricks();
1009
+ resetBall();
1010
+ gameStarted = false;
1011
+ startMessage.textContent = 'LEVEL COMPLETE!';
1012
+ }, 1000);
1013
+ }
1014
+
1015
+ // Change ball direction based on where it hit the brick
1016
+ const ballCenterX = ballData.x + ballSize / 2;
1017
+ const ballCenterY = ballData.y + ballSize / 2;
1018
+ const brickCenterX = parseInt(brick.style.left) + brick.offsetWidth / 2;
1019
+ const brickCenterY = parseInt(brick.style.top) + brick.offsetHeight / 2;
1020
+
1021
+ // Check which side was hit
1022
+ const overlapX = Math.min(
1023
+ ballData.x + ballSize - parseInt(brick.style.left),
1024
+ parseInt(brick.style.left) + brick.offsetWidth - ballData.x
1025
+ );
1026
+ const overlapY = Math.min(
1027
+ ballData.y + ballSize - parseInt(brick.style.top),
1028
+ parseInt(brick.style.top) + brick.offsetHeight - ballData.y
1029
+ );
1030
+
1031
+ if (overlapX < overlapY) {
1032
+ ballData.speedX = -ballData.speedX;
1033
+ } else {
1034
+ ballData.speedY = -ballData.speedY;
1035
+ }
1036
+
1037
+ return true;
1038
+ }
1039
+ }
1040
+ return false;
1041
+ }
1042
+
1043
+ // Show combo counter
1044
+ function showCombo(comboValue) {
1045
+ comboCounter.textContent = `COMBO x${comboValue}!`;
1046
+ comboCounter.style.opacity = '1';
1047
+ comboCounter.style.transform = `translate(-50%, -50%) translateZ(30px) scale(1.2)`;
1048
+
1049
+ setTimeout(() => {
1050
+ comboCounter.style.opacity = '0';
1051
+ comboCounter.style.transform = `translate(-50%, -50%) translateZ(30px) scale(0.8)`;
1052
+ }, 800);
1053
+ }
1054
+
1055
+ // Create explosion effect when brick is destroyed
1056
+ function createExplosion(x, y, color) {
1057
+ const explosion = document.createElement('div');
1058
+ explosion.className = 'explosion';
1059
+ explosion.style.left = x + 'px';
1060
+ explosion.style.top = y + 'px';
1061
+ explosion.style.background = `radial-gradient(circle, ${color} 0%, transparent 70%)`;
1062
+ explosion.style.width = '1px';
1063
+ explosion.style.height = '1px';
1064
+ explosion.style.opacity = '1';
1065
+ gameContainer.appendChild(explosion);
1066
+
1067
+ // Animate explosion
1068
+ let size = 1;
1069
+ const growthRate = 40;
1070
+ const fadeRate = 0.95;
1071
+
1072
+ const expand = () => {
1073
+ size += growthRate;
1074
+ const currentOpacity = parseFloat(explosion.style.opacity);
1075
+
1076
+ explosion.style.width = size + 'px';
1077
+ explosion.style.height = size + 'px';
1078
+ explosion.style.marginLeft = -size/2 + 'px';
1079
+ explosion.style.marginTop = -size/2 + 'px';
1080
+ explosion.style.opacity = currentOpacity * fadeRate;
1081
+
1082
+ if (currentOpacity > 0.1) {
1083
+ requestAnimationFrame(expand);
1084
+ } else {
1085
+ gameContainer.removeChild(explosion);
1086
+ }
1087
+ };
1088
+
1089
+ requestAnimationFrame(expand);
1090
+
1091
+ // Create particles
1092
+ createParticles(x, y, color);
1093
+ }
1094
+
1095
+ // Create particles when brick is hit
1096
+ function createParticles(x, y, color) {
1097
+ const particleCount = 25;
1098
+
1099
+ for (let i = 0; i < particleCount; i++) {
1100
+ const particle = document.createElement('div');
1101
+ particle.className = 'particle';
1102
+ particle.style.left = x + 'px';
1103
+ particle.style.top = y + 'px';
1104
+ particle.style.background = color;
1105
+ particle.style.boxShadow = `0 0 10px ${color}`;
1106
+
1107
+ // Random angle and speed
1108
+ const angle = Math.random() * Math.PI * 2;
1109
+ const speed = 1 + Math.random() * 3;
1110
+ const size = 2 + Math.random() * 8;
1111
+
1112
+ // Set random size
1113
+ particle.style.width = size + 'px';
1114
+ particle.style.height = size + 'px';
1115
+
1116
+ gameContainer.appendChild(particle);
1117
+
1118
+ let posX = 0;
1119
+ let posY = 0;
1120
+ let opacity = 1;
1121
+
1122
+ const animate = () => {
1123
+ posX += Math.cos(angle) * speed;
1124
+ posY += Math.sin(angle) * speed * 1.5; // More vertical movement
1125
+ opacity -= 0.02;
1126
+
1127
+ // Add gravity effect
1128
+ posY += 0.1;
1129
+
1130
+ particle.style.transform = `translate(${posX}px, ${posY}px)`;
1131
+ particle.style.opacity = opacity;
1132
+
1133
+ if (opacity > 0) {
1134
+ requestAnimationFrame(animate);
1135
+ } else {
1136
+ gameContainer.removeChild(particle);
1137
+ }
1138
+ };
1139
+
1140
+ requestAnimationFrame(animate);
1141
+ }
1142
+ }
1143
+
1144
+ // Create hit effect for brick
1145
+ function createBrickHitEffect(brick) {
1146
+ const hitEffect = document.createElement('div');
1147
+ hitEffect.className = 'particle';
1148
+ hitEffect.style.left = parseInt(brick.style.left) + brick.offsetWidth/2 + 'px';
1149
+ hitEffect.style.top = parseInt(brick.style.top) + brick.offsetHeight/2 + 'px';
1150
+ hitEffect.style.background = brick.style.backgroundColor;
1151
+ hitEffect.style.boxShadow = `0 0 15px ${brick.style.backgroundColor}`;
1152
+ hitEffect.style.width = '30px';
1153
+ hitEffect.style.height = '30px';
1154
+ hitEffect.style.opacity = '0.8';
1155
+ hitEffect.style.transition = 'all 0.3s ease-out';
1156
+
1157
+ gameContainer.appendChild(hitEffect);
1158
+
1159
+ // Animate
1160
+ setTimeout(() => {
1161
+ hitEffect.style.width = '0';
1162
+ hitEffect.style.height = '0';
1163
+ hitEffect.style.opacity = '0';
1164
+
1165
+ setTimeout(() => {
1166
+ if (gameContainer.contains(hitEffect)) {
1167
+ gameContainer.removeChild(hitEffect);
1168
+ }
1169
+ }, 300);
1170
+ }, 50);
1171
+ }
1172
+
1173
+ // Mouse movement handler
1174
+ function mouseMoveHandler(e) {
1175
+ if (!gameActive) return;
1176
+
1177
+ const relativeX = e.clientX - gameContainer.getBoundingClientRect().left;
1178
+ paddleX = Math.max(0, Math.min(relativeX - paddleWidth / 2, gameWidth - paddleWidth));
1179
+ paddle.style.left = paddleX + 'px';
1180
+
1181
+ if (!gameStarted) {
1182
+ // Main ball follows paddle before launch
1183
+ ballX = paddleX + paddleWidth / 2 - ballSize / 2;
1184
+ ball.style.left = ballX + 'px';
1185
+
1186
+ // Update main ball position in array
1187
+ balls[0].x = ballX;
1188
+ }
1189
+ }
1190
+
1191
+ // Keyboard controls
1192
+ function keyDownHandler(e) {
1193
+ if (!gameActive) return;
1194
+
1195
+ if (e.key === ' ') {
1196
+ if (!gameStarted) {
1197
+ launchBall();
1198
+ }
1199
+ }
1200
+ }
1201
+
1202
+ // Game loop
1203
+ function update() {
1204
+ if (!gameActive) {
1205
+ requestAnimationFrame(update);
1206
+ return;
1207
+ }
1208
+
1209
+ // Move all balls if game has started
1210
+ if (gameStarted) {
1211
+ for (let i = balls.length - 1; i >= 0; i--) {
1212
+ const ballData = balls[i];
1213
+
1214
+ ballData.x += ballData.speedX;
1215
+ ballData.y += ballData.speedY;
1216
+
1217
+ // Apply ball position
1218
+ ballData.element.style.left = ballData.x + 'px';
1219
+ ballData.element.style.top = ballData.y + 'px';
1220
+
1221
+ // Wall collision (left/right)
1222
+ if (ballData.x <= 0 || ballData.x + ballSize >= gameWidth) {
1223
+ ballData.speedX = -ballData.speedX;
1224
+ ballData.x = Math.max(0, Math.min(ballData.x, gameWidth - ballSize));
1225
+
1226
+ // Create wall hit effect
1227
+ createWallHitEffect(ballData.x, ballData.y);
1228
+ createBallPulse(ballData.x, ballData.y);
1229
+ }
1230
+
1231
+ // Ceiling collision
1232
+ if (ballData.y <= 0) {
1233
+ ballData.speedY = -ballData.speedY;
1234
+ ballData.y = Math.max(0, ballData.y);
1235
+
1236
+ // Create ceiling hit effect
1237
+ createWallHitEffect(ballData.x, ballData.y);
1238
+ createBallPulse(ballData.x, ballData.y);
1239
+ }
1240
+
1241
+ // Paddle collision
1242
+ if (ballData.y + ballSize >= gameHeight - paddleHeight - 40 &&
1243
+ ballData.y + ballSize <= gameHeight - 40 &&
1244
+ ballData.x + ballSize >= paddleX &&
1245
+ ballData.x <= paddleX + paddleWidth) {
1246
+
1247
+ // Calculate relative position of hit on paddle (from -1 to 1)
1248
+ const hitPos = ((ballData.x + ballSize/2) - (paddleX + paddleWidth/2)) / (paddleWidth/2);
1249
+
1250
+ // Change ball direction based on where it hits the paddle
1251
+ ballData.speedX = hitPos * 5; // Max 5 pixels/frame horizontally
1252
+ ballData.speedY = -Math.abs(ballData.speedY); // Ensure it goes up
1253
+
1254
+ // Create paddle hit effect
1255
+ createPaddleHitEffect();
1256
+ createBallPulse(ballData.x, ballData.y);
1257
+ }
1258
+
1259
+ // Bottom collision (check only for main ball)
1260
+ if (i === 0 && ballData.y + ballSize >= gameHeight) {
1261
+ // Lose a life
1262
+ lives--;
1263
+ livesDisplay.textContent = `LIVES: ${lives}`;
1264
+
1265
+ // Reset or end game
1266
+ if (lives > 0) {
1267
+ resetBall();
1268
+ } else {
1269
+ endGame();
1270
+ }
1271
+
1272
+ // Reset combo
1273
+ combo = 0;
1274
+ }
1275
+
1276
+ // Brick collision
1277
+ if (!checkBrickCollision(ballData)) {
1278
+ // No brick hit this frame - reset combo (for main ball only)
1279
+ if (i === 0) {
1280
+ combo = 0;
1281
+ }
1282
+ }
1283
+
1284
+ // Check for extra balls going off screen
1285
+ if (i > 0 && ballData.y + ballSize >= gameHeight) {
1286
+ // Remove extra ball
1287
+ gameContainer.removeChild(ballData.element);
1288
+ balls.splice(i, 1);
1289
+ }
1290
+ }
1291
+ }
1292
+
1293
+ requestAnimationFrame(update);
1294
+ }
1295
+
1296
+ // Create wall hit effect
1297
+ function createWallHitEffect(x, y) {
1298
+ const particles = 8;
1299
+
1300
+ for (let i = 0; i < particles; i++) {
1301
+ const particle = document.createElement('div');
1302
+ particle.className = 'particle';
1303
+ particle.style.left = x + ballSize/2 + 'px';
1304
+ particle.style.top = y + ballSize/2 + 'px';
1305
+ particle.style.background = '#00dcff';
1306
+ particle.style.boxShadow = '0 0 10px #00dcff';
1307
+
1308
+ // Random angle (away from wall)
1309
+ const angle = x <= 0 ? Math.random() * Math.PI/2 + Math.PI/4 :
1310
+ x + ballSize >= gameWidth ? Math.random() * Math.PI/2 + Math.PI*3/4 :
1311
+ y <= 0 ? Math.random() * Math.PI/2 + Math.PI*3/2 :
1312
+ Math.random() * Math.PI * 2;
1313
+
1314
+ const speed = 1 + Math.random() * 3;
1315
+ const size = 2 + Math.random() * 6;
1316
+
1317
+ particle.style.width = size + 'px';
1318
+ particle.style.height = size + 'px';
1319
+
1320
+ gameContainer.appendChild(particle);
1321
+
1322
+ let posX = 0;
1323
+ let posY = 0;
1324
+ let opacity = 1;
1325
+
1326
+ const animate = () => {
1327
+ posX += Math.cos(angle) * speed;
1328
+ posY += Math.sin(angle) * speed;
1329
+ opacity -= 0.03;
1330
+
1331
+ particle.style.transform = `translate(${posX}px, ${posY}px)`;
1332
+ particle.style.opacity = opacity;
1333
+
1334
+ if (opacity > 0) {
1335
+ requestAnimationFrame(animate);
1336
+ } else {
1337
+ if (gameContainer.contains(particle)) {
1338
+ gameContainer.removeChild(particle);
1339
+ }
1340
+ }
1341
+ };
1342
+
1343
+ requestAnimationFrame(animate);
1344
+ }
1345
+ }
1346
+
1347
+ // Create paddle hit effect
1348
+ function createPaddleHitEffect() {
1349
+ const particles = 12;
1350
+ const x = paddleX + paddleWidth/2;
1351
+ const y = gameHeight - paddleHeight - 40;
1352
+
1353
+ for (let i = 0; i < particles; i++) {
1354
+ const particle = document.createElement('div');
1355
+ particle.className = 'particle';
1356
+ particle.style.left = x + 'px';
1357
+ particle.style.top = y + 'px';
1358
+ particle.style.background = '#6b00ff';
1359
+ particle.style.boxShadow = '0 0 10px #6b00ff';
1360
+
1361
+ // Angle going up
1362
+ const angle = Math.random() * Math.PI/2 - Math.PI/4; // Between -45 and +45 degrees up
1363
+ const speed = 1 + Math.random() * 4;
1364
+ const size = 2 + Math.random() * 8;
1365
+
1366
+ particle.style.width = size + 'px';
1367
+ particle.style.height = size + 'px';
1368
+
1369
+ gameContainer.appendChild(particle);
1370
+
1371
+ let posX = 0;
1372
+ let posY = 0;
1373
+ let opacity = 1;
1374
+
1375
+ const animate = () => {
1376
+ posX += Math.cos(angle) * speed;
1377
+ posY += Math.sin(angle) * speed;
1378
+ opacity -= 0.01;
1379
+
1380
+ particle.style.transform = `translate(${posX}px, ${posY}px)`;
1381
+ particle.style.opacity = opacity;
1382
+
1383
+ if (opacity > 0) {
1384
+ requestAnimationFrame(animate);
1385
+ } else {
1386
+ if (gameContainer.contains(particle)) {
1387
+ gameContainer.removeChild(particle);
1388
+ }
1389
+ }
1390
+ };
1391
+
1392
+ requestAnimationFrame(animate);
1393
+ }
1394
+ }
1395
+
1396
+ // Start game button
1397
+ startBtn.addEventListener('click', () => {
1398
+ if (!gameActive) {
1399
+ // Reset game state
1400
+ score = 0;
1401
+ lives = 3;
1402
+ combo = 0;
1403
+ gameStarted = false;
1404
+
1405
+ initGame();
1406
+ } else if (!gameStarted) {
1407
+ launchBall();
1408
+ }
1409
+ });
1410
+
1411
+ // Event listeners
1412
+ document.addEventListener('mousemove', mouseMoveHandler);
1413
+ document.addEventListener('keydown', keyDownHandler);
1414
+ document.addEventListener('click', launchBall);
1415
+
1416
+ // Start the game loop
1417
+ update();
1418
+ });
1419
+ </script>
1420
+ <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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1421
+ </html>