stroumphs commited on
Commit
f577cce
·
verified ·
1 Parent(s): 927b60f

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +965 -1387
index.html CHANGED
@@ -3,1523 +3,1101 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Paddlebound: Rogue Bounce</title>
 
7
  <style>
8
- /* Google Fonts Import */
9
- @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Ubuntu+Mono:wght@400;700&display=swap');
10
-
11
- /* Base Styles */
12
  :root {
13
- --primary: #FF3E41;
14
- --secondary: #39A0ED;
15
- --accent: #4CC9F0;
16
- --dark: #14213D;
17
- --light: #F8F9FA;
18
- --success: #4ADF86;
19
- --warning: #F9C846;
20
- --danger: #E63946;
21
- --scoreboard: #333;
22
- }
23
-
24
  * {
25
  margin: 0;
26
  padding: 0;
27
  box-sizing: border-box;
 
28
  }
29
-
30
  body {
31
- font-family: 'Ubuntu Mono', monospace;
32
  background-color: var(--dark);
33
  color: var(--light);
34
- overflow: hidden;
35
- height: 100vh;
36
- display: flex;
37
- flex-direction: column;
38
- align-items: center;
39
- justify-content: center;
40
- background-image:
41
- radial-gradient(circle at 25% 25%, rgba(57, 160, 237, 0.2) 0%, transparent 50%),
42
- radial-gradient(circle at 75% 75%, rgba(255, 62, 65, 0.2) 0%, transparent 50%);
43
- background-position: center;
44
- background-size: cover;
45
- }
46
-
47
- /* Game Main Container */
48
- .game-container {
49
  position: relative;
50
- width: 800px;
51
- height: 500px;
52
- background-color: var(--dark);
53
- border: 4px solid var(--light);
54
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
55
- border-radius: 8px;
56
  overflow: hidden;
57
  }
58
-
59
- /* Game Canvas */
60
- #gameCanvas {
 
 
 
61
  width: 100%;
62
  height: 100%;
63
- background-color: var(--dark);
 
64
  }
65
-
66
- /* Score Boards */
67
- .score-board {
68
- position: absolute;
69
- top: 20px;
70
- width: 100%;
71
- display: flex;
72
- justify-content: space-between;
73
- padding: 0 30px;
74
- z-index: 10;
75
- font-family: 'Press Start 2P', cursive;
76
- font-size: 20px;
77
- color: var(--light);
78
- text-shadow: 0 0 5px var(--primary);
79
  }
80
-
81
- .player-score, .opponent-score {
82
- background-color: rgba(20, 33, 61, 0.8);
83
- padding: 10px 20px;
84
- border-radius: 5px;
85
- border: 2px solid var(--light);
 
 
86
  }
87
-
88
- /* Power-up HUD */
89
- .powerups-hud {
90
- position: absolute;
91
- bottom: 20px;
92
- left: 50%;
93
- transform: translateX(-50%);
94
- display: flex;
95
- gap: 10px;
96
- z-index: 10;
97
  }
98
-
99
- .powerup-icon {
100
- width: 30px;
101
- height: 30px;
102
- border-radius: 50%;
103
- border: 2px solid var(--light);
104
  display: flex;
105
- align-items: center;
106
  justify-content: center;
107
- font-size: 16px;
108
- background-color: rgba(76, 201, 240, 0.2);
109
- opacity: 0;
110
- transition: opacity 0.3s;
111
  }
112
-
113
- .powerup-icon.active {
114
- opacity: 1;
115
- animation: pulse 1s infinite;
 
 
 
 
116
  }
117
-
118
- @keyframes pulse {
119
- 0% { transform: scale(1); }
120
- 50% { transform: scale(1.1); }
121
- 100% { transform: scale(1); }
 
 
 
 
 
 
 
 
122
  }
123
-
124
- /* Game Overlay Screens */
125
- .overlay {
126
  position: absolute;
127
  top: 0;
128
  left: 0;
129
  width: 100%;
130
  height: 100%;
131
- background-color: rgba(20, 33, 61, 0.9);
132
- display: flex;
133
- flex-direction: column;
134
- align-items: center;
135
- justify-content: center;
136
- z-index: 20;
137
- font-family: 'Press Start 2P', cursive;
138
  text-align: center;
139
- padding: 20px;
140
- }
141
-
142
- .title {
143
- font-size: 3rem;
144
- color: var(--primary);
145
- margin-bottom: 20px;
146
- text-shadow: 3px 3px 0 var(--light);
147
- animation: titleGlow 2s infinite alternate;
148
- }
149
-
150
- @keyframes titleGlow {
151
- from { text-shadow: 0 0 5px var(--light), 3px 3px 0 var(--light); }
152
- to { text-shadow: 0 0 20px var(--accent), 3px 3px 0 var(--light); }
153
- }
154
-
155
- .subtitle {
156
- font-size: 1.5rem;
157
  color: var(--accent);
158
- margin-bottom: 30px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
-
161
- .btn {
162
- background-color: var(--primary);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  color: var(--light);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  border: none;
165
- padding: 15px 30px;
166
- font-size: 1.2rem;
167
- font-family: 'Press Start 2P', cursive;
168
  cursor: pointer;
169
- margin: 10px;
170
- border-radius: 5px;
171
  transition: all 0.3s;
172
  }
173
-
174
- .btn:hover {
175
- background-color: var(--danger);
176
- transform: translateY(-3px);
177
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
178
  }
179
-
180
- .btn:active {
181
- transform: translateY(0);
 
 
 
 
 
 
182
  }
183
-
184
- .btn-secondary {
185
- background-color: var(--secondary);
 
186
  }
187
-
188
- .btn-secondary:hover {
189
- background-color: var(--accent);
190
  }
191
-
192
- /* Game Over Screen */
193
- .game-over .title {
194
- color: var(--danger);
195
  }
196
-
197
- /* Opponent Introduction */
198
- .opponent-intro {
199
- position: absolute;
200
- top: 50%;
201
- left: 50%;
202
- transform: translate(-50%, -50%);
203
- background-color: rgba(20, 33, 61, 0.9);
204
- border: 3px solid var(--light);
205
- padding: 20px;
206
  border-radius: 10px;
207
- z-index: 20;
208
- text-align: center;
209
- max-width: 500px;
210
  }
211
-
212
- .opponent-name {
213
- font-size: 1.5rem;
214
- color: var(--warning);
215
- margin-bottom: 10px;
216
  }
217
-
218
- .opponent-description {
219
- font-size: 1rem;
220
- margin-bottom: 20px;
221
- font-family: 'Ubuntu Mono', monospace;
222
  }
223
-
224
- .opponent-special {
225
- color: var(--accent);
226
  font-weight: bold;
 
227
  }
228
-
229
- /* Stage Modifier Indicator */
230
- .modifier-indicator {
231
- position: absolute;
232
- top: 50%;
233
- left: 50%;
234
- transform: translate(-50%, -50%);
235
- background-color: rgba(20, 33, 61, 0.9);
236
- border: 3px solid var(--light);
237
- padding: 15px;
238
- border-radius: 10px;
239
- z-index: 20;
240
- text-align: center;
241
- max-width: 500px;
242
- animation: modifierFadeIn 0.5s;
243
  }
244
-
245
- @keyframes modifierFadeIn {
246
- from { opacity: 0; transform: translate(-50%, -40%); }
247
- to { opacity: 1; transform: translate(-50%, -50%); }
248
  }
249
-
250
- .modifier-title {
251
- font-size: 1.3rem;
252
- color: var(--success);
253
- margin-bottom: 10px;
254
  }
255
-
256
- .modifier-effect {
257
- font-size: 1rem;
258
- font-style: italic;
 
259
  }
260
-
261
- /* Power-up Notification */
262
- .powerup-notification {
263
- position: absolute;
264
- top: 50%;
265
- left: 50%;
266
- transform: translate(-50%, -50%);
267
- background-color: rgba(76, 201, 240, 0.2);
268
- border: 2px solid var(--light);
269
- padding: 10px 20px;
270
- border-radius: 5px;
271
- z-index: 20;
272
- text-align: center;
273
- opacity: 0;
274
- transition: all 0.3s;
275
  }
276
-
277
- .powerup-notification.show {
278
- opacity: 1;
279
- animation: powerUpFade 2s forwards;
 
 
280
  }
281
-
282
- @keyframes powerUpFade {
283
- 0% { opacity: 1; transform: translate(-50%, -50%); }
284
- 80% { opacity: 1; transform: translate(-50%, -60%); }
285
- 100% { opacity: 0; transform: translate(-50%, -70%); }
286
  }
287
-
288
- /* Run Stats */
289
- .run-stats {
290
  display: flex;
291
  flex-wrap: wrap;
292
- justify-content: center;
293
- gap: 20px;
294
- margin: 20px 0;
295
  }
296
-
297
- .stat-item {
298
- background-color: rgba(57, 160, 237, 0.2);
299
- padding: 10px 20px;
300
- border-radius: 5px;
301
- border: 1px solid var(--accent);
302
- min-width: 150px;
303
  }
304
-
305
- .stat-value {
306
- font-size: 1.3rem;
307
- color: var(--warning);
 
 
308
  }
309
-
310
- .stat-label {
311
- font-size: 0.8rem;
312
- color: var(--accent);
313
- text-transform: uppercase;
 
314
  }
315
-
316
- /* Permanent Upgrades */
317
- .upgrades-container {
 
 
318
  display: flex;
319
- flex-wrap: wrap;
320
- justify-content: center;
321
- gap: 15px;
322
- margin-top: 20px;
323
- max-width: 600px;
324
- }
325
-
326
- .upgrade-item {
327
- background-color: rgba(20, 33, 61, 0.8);
328
- border: 2px solid var(--accent);
329
- border-radius: 5px;
330
- padding: 15px;
331
- width: 180px;
332
- cursor: pointer;
333
- transition: all 0.3s;
334
  }
335
-
336
- .upgrade-item:hover {
337
- transform: translateY(-5px);
338
- box-shadow: 0 5px 15px rgba(76, 201, 240, 0.3);
339
- border-color: var(--success);
340
- }
341
-
342
- .upgrade-item.purchased {
343
- border-color: var(--success);
344
- background-color: rgba(74, 223, 134, 0.1);
345
- }
346
-
347
- .upgrade-item.disabled {
348
- opacity: 0.6;
349
- cursor: not-allowed;
350
- }
351
-
352
- .upgrade-name {
353
- color: var(--warning);
354
- margin-bottom: 5px;
355
- font-size: 0.9rem;
356
  }
357
-
358
- .upgrade-description {
359
- font-size: 0.8rem;
360
- margin-bottom: 10px;
361
- font-family: 'Ubuntu Mono', monospace;
362
  }
363
-
364
- .upgrade-cost {
365
- color: var(--success);
366
- font-size: 0.9rem;
 
367
  }
368
-
369
- .currency-display {
 
370
  position: absolute;
371
- top: 20px;
372
- right: 20px;
373
- background-color: rgba(20, 33, 61, 0.8);
374
- padding: 8px 15px;
375
- border-radius: 5px;
376
- border: 2px solid var(--warning);
377
- font-family: 'Press Start 2P', cursive;
378
- font-size: 0.9rem;
379
- color: var(--warning);
380
- }
381
-
382
- /* Responsive */
383
- @media (max-width: 850px) {
384
- .game-container {
385
- width: 95%;
386
- height: 400px;
387
- }
388
-
389
- .title {
390
- font-size: 2rem;
391
- }
392
-
393
- .subtitle {
394
- font-size: 1.2rem;
395
- }
396
-
397
- .btn {
398
- padding: 10px 20px;
399
- font-size: 1rem;
400
- }
401
-
402
- .opponent-intro, .modifier-indicator {
403
- width: 90%;
404
- }
405
-
406
- .opponent-name {
407
- font-size: 1.2rem;
408
- }
409
- }
410
-
411
- @media (max-height: 700px) {
412
- .game-container {
413
- height: 350px;
414
- }
415
  }
416
- </style>
417
- </head>
418
- <body>
419
- <div class="game-container">
420
- <canvas id="gameCanvas"></canvas>
421
-
422
- <!-- Score UI -->
423
- <div class="score-board">
424
- <div class="player-score">YOU: 0</div>
425
- <div class="opponent-score">CPU: 0</div>
426
- </div>
427
 
428
- <!-- Power-up HUD -->
429
- <div class="powerups-hud">
430
- <div class="powerup-icon" id="pup1">?</div>
431
- <div class="powerup-icon" id="pup2">?</div>
432
- <div class="powerup-icon" id="pup3">?</div>
433
- </div>
 
434
 
435
- <!-- Main Menu Overlay -->
436
- <div class="overlay" id="mainMenu">
437
- <h1 class="title">Paddlebound:<br>Rogue Bounce</h1>
438
- <p class="subtitle">A Roguelike Ping Pong Adventure</p>
439
- <button class="btn" id="startGame">New Run</button>
440
- <button class="btn btn-secondary" id="upgradesBtn">Permanent Upgrades</button>
441
- </div>
 
442
 
443
- <!-- Game Over Overlay -->
444
- <div class="overlay game-over" id="gameOver" style="display: none;">
445
- <h1 class="title">Game Over</h1>
446
- <p class="subtitle">Run Completed</p>
447
-
448
- <div class="run-stats">
449
- <div class="stat-item">
450
- <div class="stat-value" id="stat-rounds">0</div>
451
- <div class="stat-label">Opponents Defeated</div>
452
- </div>
453
- <div class="stat-item">
454
- <div class="stat-value" id="stat-score">0</div>
455
- <div class="stat-label">Total Points</div>
456
- </div>
457
- <div class="stat-item">
458
- <div class="stat-value" id="stat-time">0:00</div>
459
- <div class="stat-label">Time Played</div>
460
- </div>
461
- </div>
462
-
463
- <p>Earned <span id="earned-currency" style="color: var(--warning);">0</span> upgrade points</p>
464
-
465
- <button class="btn" id="restartGame">Try Again</button>
466
- <button class="btn btn-secondary" id="backToMenu">Main Menu</button>
467
- </div>
468
 
469
- <!-- Upgrades Screen -->
470
- <div class="overlay" id="upgradesScreen" style="display: none;">
471
- <h1 class="title">Permanent Upgrades</h1>
472
- <div class="currency-display">
473
- Points: <span id="currency-amount">0</span>
474
- </div>
475
-
476
- <div class="upgrades-container">
477
- <!-- Upgrade items will be added here dynamically -->
478
- </div>
479
-
480
- <button class="btn" id="backFromUpgrades">Back</button>
481
- </div>
482
- </div>
483
-
484
- <script>
485
- // Game Constants
486
- const GAME_WIDTH = 800;
487
- const GAME_HEIGHT = 500;
488
- const PADDLE_WIDTH = 100;
489
- const PADDLE_HEIGHT = 15;
490
- const PADDLE_OFFSET = 20;
491
- const BALL_SIZE = 10;
492
- const BALL_INITIAL_SPEED = 5;
493
- const WIN_SCORE = 11;
494
- const POWERUP_SIZE = 15;
495
- const POWERUP_SPAWN_CHANCE = 0.02; // 2% chance per frame
496
- const POWERUP_DURATION = 10000; // 10 seconds
497
-
498
- // Game Variables
499
- let canvas, ctx;
500
- let gameRunning = false;
501
- let gameOver = false;
502
- let animationFrameId;
503
- let runStarted = false;
504
- let winScore = WIN_SCORE;
505
-
506
- // Game Objects
507
- let player = {
508
- x: GAME_WIDTH / 2 - PADDLE_WIDTH / 2,
509
- y: GAME_HEIGHT - PADDLE_HEIGHT - PADDLE_OFFSET,
510
- width: PADDLE_WIDTH,
511
- height: PADDLE_HEIGHT,
512
- color: '#4CC9F0',
513
- speed: 8
514
- };
515
-
516
- let opponent = {
517
- x: GAME_WIDTH / 2 - PADDLE_WIDTH / 2,
518
- y: PADDLE_OFFSET,
519
- width: PADDLE_WIDTH,
520
- height: PADDLE_HEIGHT,
521
- color: '#FF3E41',
522
- speed: 5,
523
- reactionTime: 0.5 // 0-1, where 1 is perfect reaction
524
- };
525
-
526
- let ball = {
527
- x: GAME_WIDTH / 2,
528
- y: GAME_HEIGHT / 2,
529
- size: BALL_SIZE,
530
- color: '#F8F9FA',
531
- speedX: 0,
532
- speedY: 0,
533
- speed: BALL_INITIAL_SPEED,
534
- active: false,
535
- trail: []
536
- };
537
-
538
- // Power-ups
539
- let powerups = [];
540
- let activePowerups = [];
541
- let powerupTypes = [
542
- { id: 'paddleXL', name: 'Paddle XL', color: '#4ADF86', effect: 'Increase paddle size by 50%', duration: POWERUP_DURATION, icon: 'XL' },
543
- { id: 'paddleXS', name: 'Paddle XS', color: '#E63946', effect: 'Decrease paddle size by 50%', duration: POWERUP_DURATION, icon: 'XS' },
544
- { id: 'speedBoost', name: 'Speed Boost', color: '#F9C846', effect: 'Increase ball speed for opponent', duration: POWERUP_DURATION, icon: '⚡' },
545
- { id: 'multiBall', name: 'Multi Ball', color: '#39A0ED', effect: 'Spawn 3 extra balls', duration: POWERUP_DURATION, icon: '③' },
546
- { id: 'slowMotion', name: 'Slow Mo', color: '#9B59B6', effect: 'Slow down time', duration: POWERUP_DURATION, icon: '⌛' },
547
- { id: 'magnet', name: 'Ball Magnet', color: '#FF7F50', effect: 'Ball gravitates to your paddle', duration: POWERUP_DURATION, icon: '🧲' }
548
- ];
549
-
550
- // Opponent Types
551
- let opponents = [
552
- {
553
- name: "The Wall",
554
- description: "A defensive opponent that covers a wide area.",
555
- width: 150,
556
- speed: 4,
557
- reactionTime: 0.7,
558
- color: "#2ECC71"
559
- },
560
- {
561
- name: "Speed Demon",
562
- description: "Moves extremely fast but has a smaller paddle.",
563
- width: 70,
564
- speed: 9,
565
- reactionTime: 0.8,
566
- color: "#E74C3C"
567
- },
568
- {
569
- name: "The Trickster",
570
- description: "Adjusts reaction time unpredictably.",
571
- width: 100,
572
- speed: 6,
573
- reactionTime: 0.5,
574
- color: "#9B59B6",
575
- special: "Changes reaction time randomly during the match"
576
- },
577
- {
578
- name: "The Turtle",
579
- description: "Very slow but has perfect reaction time.",
580
- width: 120,
581
- speed: 3,
582
- reactionTime: 1.0,
583
- color: "#F39C12",
584
- special: "Never misses the ball if it's within reach"
585
- },
586
- {
587
- name: "The Juggernaut",
588
- description: "Massive paddle but moves slowly.",
589
- width: 200,
590
- speed: 2.5,
591
- reactionTime: 0.6,
592
- color: "#16A085"
593
- }
594
- ];
595
-
596
- // Stage Modifiers
597
- let stageModifiers = [
598
- {
599
- name: "Wind Gusts",
600
- description: "Random wind affects ball movement.",
601
- effect: function() {
602
- let windForce = (Math.random() - 0.5) * 0.5;
603
- ball.speedX += windForce;
604
- if (frameCount % 60 === 0) windForce = (Math.random() - 0.5) * 0.5;
605
- },
606
- color: "#A2D9CE"
607
- },
608
- {
609
- name: "Gravity Well",
610
- description: "Ball accelerates downward faster.",
611
- effect: function() {
612
- if (ball.speedY > 0) ball.speedY *= 1.02;
613
- else ball.speedY /= 1.02;
614
- },
615
- color: "#95A5A6"
616
- },
617
- {
618
- name: "Rebound Chaos",
619
- description: "Ball speed increases unpredictably on bounces.",
620
- effect: function() {
621
- ball.trail.forEach((pos, i) => {
622
- ctx.fillStyle = `rgba(76, 201, 240, ${0.2 + (i*0.1)})`;
623
- ctx.fillRect(pos.x, pos.y, ball.size, ball.size);
624
- });
625
- },
626
- color: "#D2B4DE"
627
- },
628
- {
629
- name: "Multi-Ball Madness",
630
- description: "Random chance for extra balls to spawn.",
631
- effect: function() {
632
- if (Math.random() < 0.005) spawnPowerup('multiBall');
633
- },
634
- color: "#3498DB",
635
- special: "Watch out for unexpected extra balls!"
636
- }
637
- ];
638
-
639
- // Game State
640
- let gameState = {
641
- playerScore: 0,
642
- opponentScore: 0,
643
- currentOpponent: 0,
644
- currentModifier: null,
645
- runStartTime: 0,
646
- runStats: {
647
- opponentsDefeated: 0,
648
- totalPoints: 0,
649
- powerupsCollected: 0,
650
- roundsPlayed: 0
651
- },
652
- powerupsActive: {},
653
- frameCount: 0
654
- };
655
-
656
- // Player progression
657
- let playerMeta = {
658
- currency: 0,
659
- upgrades: {
660
- paddleStartSize: 0, // 0-2 (0: default, 1: +20%, 2: +40%)
661
- ballStartSpeed: 0, // 0-2 (0: default, 1: -10%, 2: -20%)
662
- powerupDuration: 0, // 0-2 (0: default, 1: +25%, 2: +50%)
663
- extraLife: false, // Start with an extra life
664
- powerupSlots: 1 // Up to 3
665
- }
666
- };
667
-
668
- // Upgrade definitions
669
- let upgradeDefinitions = [
670
- {
671
- id: 'paddleSize1',
672
- name: 'Paddle +20%',
673
- description: 'Start with a 20% larger paddle',
674
- cost: 100,
675
- maxLevel: 2,
676
- apply: function() {
677
- playerMeta.upgrades.paddleStartSize = Math.min(playerMeta.upgrades.paddleStartSize + 1, this.maxLevel);
678
- resetPaddleSizes();
679
- },
680
- getCurrentLevel: function() {
681
- return playerMeta.upgrades.paddleStartSize;
682
- }
683
- },
684
- {
685
- id: 'ballSpeed1',
686
- name: 'Slower Ball',
687
- description: 'Start with 10% slower ball speed',
688
- cost: 150,
689
- maxLevel: 2,
690
- apply: function() {
691
- playerMeta.upgrades.ballStartSpeed = Math.min(playerMeta.upgrades.ballStartSpeed + 1, this.maxLevel);
692
- },
693
- getCurrentLevel: function() {
694
- return playerMeta.upgrades.ballStartSpeed;
695
- }
696
- },
697
- {
698
- id: 'powerupDuration1',
699
- name: 'Powerup +25%',
700
- description: 'Powerups last 25% longer',
701
- cost: 125,
702
- maxLevel: 2,
703
- apply: function() {
704
- playerMeta.upgrades.powerupDuration = Math.min(playerMeta.upgrades.powerupDuration + 1, this.maxLevel);
705
- },
706
- getCurrentLevel: function() {
707
- return playerMeta.upgrades.powerupDuration;
708
- }
709
- },
710
- {
711
- id: 'extraLife',
712
- name: 'Extra Life',
713
- description: 'Start with one additional chance per run',
714
- cost: 250,
715
- maxLevel: 1,
716
- apply: function() {
717
- playerMeta.upgrades.extraLife = true;
718
- },
719
- getCurrentLevel: function() {
720
- return playerMeta.upgrades.extraLife ? 1 : 0;
721
- }
722
- },
723
- {
724
- id: 'powerupSlot2',
725
- name: 'Powerup Slot #2',
726
- description: 'Unlock an additional powerup slot',
727
- cost: 200,
728
- maxLevel: 1,
729
- apply: function() {
730
- playerMeta.upgrades.powerupSlots = Math.min(playerMeta.upgrades.powerupSlots + 1, 3);
731
- updatePowerupHud();
732
- },
733
- getCurrentLevel: function() {
734
- return playerMeta.upgrades.powerupSlots >= 2 ? 1 : 0;
735
- }
736
- },
737
- {
738
- id: 'powerupSlot3',
739
- name: 'Powerup Slot #3',
740
- description: 'Unlock a third powerup slot',
741
- cost: 300,
742
- maxLevel: 1,
743
- prereq: 'powerupSlot2',
744
- apply: function() {
745
- playerMeta.upgrades.powerupSlots = Math.min(playerMeta.upgrades.powerupSlots + 1, 3);
746
- updatePowerupHud();
747
- },
748
- getCurrentLevel: function() {
749
- return playerMeta.upgrades.powerupSlots >= 3 ? 1 : 0;
750
- }
751
- }
752
- ];
753
-
754
- // DOM Elements
755
- let mainMenu, gameOverScreen, upgradesScreen;
756
- let startGameBtn, restartGameBtn, backToMenuBtn, upgradesBtn, backFromUpgradesBtn;
757
-
758
- // Initialize the game
759
- function init() {
760
- canvas = document.getElementById('gameCanvas');
761
- ctx = canvas.getContext('2d');
762
- canvas.width = GAME_WIDTH;
763
- canvas.height = GAME_HEIGHT;
764
-
765
- // Get DOM elements
766
- mainMenu = document.getElementById('mainMenu');
767
- gameOverScreen = document.getElementById('gameOver');
768
- upgradesScreen = document.getElementById('upgradesScreen');
769
-
770
- startGameBtn = document.getElementById('startGame');
771
- restartGameBtn = document.getElementById('restartGame');
772
- backToMenuBtn = document.getElementById('backToMenu');
773
- upgradesBtn = document.getElementById('upgradesBtn');
774
- backFromUpgradesBtn = document.getElementById('backFromUpgrades');
775
-
776
- // Event listeners
777
- startGameBtn.addEventListener('click', startGame);
778
- restartGameBtn.addEventListener('click', startGame);
779
- backToMenuBtn.addEventListener('click', showMainMenu);
780
- upgradesBtn.addEventListener('click', showUpgradesScreen);
781
- backFromUpgradesBtn.addEventListener('click', showMainMenu);
782
-
783
- // Load saved data
784
- loadGame();
785
-
786
- // Draw initial state
787
- draw();
788
-
789
- // Set up keyboard controls
790
- document.addEventListener('keydown', keyDownHandler);
791
- document.addEventListener('keyup', keyUpHandler);
792
-
793
- // Initialize powerup HUD
794
- updatePowerupHud();
795
- }
796
-
797
- // Load game state from localStorage
798
- function loadGame() {
799
- const savedGame = localStorage.getItem('paddleboundSave');
800
- if (savedGame) {
801
- try {
802
- const savedData = JSON.parse(savedGame);
803
- playerMeta.currency = savedData.currency || 0;
804
- playerMeta.upgrades = savedData.upgrades || {
805
- paddleStartSize: 0,
806
- ballStartSpeed: 0,
807
- powerupDuration: 0,
808
- extraLife: false,
809
- powerupSlots: 1
810
- };
811
-
812
- // Update currency display
813
- updateCurrencyDisplay();
814
- } catch (e) {
815
- console.error("Failed to load save data:", e);
816
- }
817
- }
818
  }
819
 
820
- // Save game state to localStorage
821
- function saveGame() {
822
- const saveData = {
823
- currency: playerMeta.currency,
824
- upgrades: playerMeta.upgrades
825
- };
826
- localStorage.setItem('paddleboundSave', JSON.stringify(saveData));
827
- }
828
-
829
- // Reset game state for a new run
830
- function resetGame() {
831
- gameState = {
832
- playerScore: 0,
833
- opponentScore: 0,
834
- currentOpponent: 0,
835
- currentModifier: null,
836
- runStartTime: Date.now(),
837
- runStats: {
838
- opponentsDefeated: 0,
839
- totalPoints: 0,
840
- powerupsCollected: 0,
841
- roundsPlayed: 0
842
- },
843
- powerupsActive: {},
844
- frameCount: 0
845
- };
846
-
847
- powerups = [];
848
- activePowerups = [];
849
- ball.trail = [];
850
-
851
- // Reset player and opponent
852
- resetPaddleSizes();
853
-
854
- // Apply upgrades
855
- if (playerMeta.upgrades.paddleStartSize > 0) {
856
- player.width = PADDLE_WIDTH * (1 + 0.2 * playerMeta.upgrades.paddleStartSize);
857
- opponent.width = PADDLE_WIDTH * (1 + 0.2 * playerMeta.upgrades.paddleStartSize);
858
- }
859
-
860
- // Position player and opponent
861
- player.x = GAME_WIDTH / 2 - player.width / 2;
862
- player.y = GAME_HEIGHT - PADDLE_HEIGHT - PADDLE_OFFSET;
863
-
864
- opponent.x = GAME_WIDTH / 2 - opponent.width / 2;
865
- opponent.y = PADDLE_OFFSET;
866
-
867
- // Setup first opponent
868
- setupOpponent(gameState.currentOpponent);
869
-
870
- // Setup stage modifier
871
- if (Math.random() > 0.3) { // 70% chance to have a modifier
872
- gameState.currentModifier = stageModifiers[Math.floor(Math.random() * stageModifiers.length)];
873
- showModifier(gameState.currentModifier);
874
- }
875
-
876
- // Reset ball
877
- resetBall();
878
-
879
- // Start with ball inactive (waits for player serve)
880
- ball.active = false;
881
  }
882
 
883
- function resetPaddleSizes() {
884
- player.width = PADDLE_WIDTH;
885
- opponent.width = PADDLE_WIDTH;
 
 
 
 
 
 
 
 
 
886
  }
887
 
888
- // Set up a new opponent
889
- function setupOpponent(index) {
890
- const opponentType = opponents[index % opponents.length];
891
-
892
- opponent = {
893
- ...opponent,
894
- width: opponentType.width,
895
- speed: opponentType.speed,
896
- reactionTime: opponentType.reactionTime,
897
- color: opponentType.color,
898
- name: opponentType.name,
899
- description: opponentType.description,
900
- special: opponentType.special
901
- };
902
-
903
- opponent.x = GAME_WIDTH / 2 - opponent.width / 2;
904
- opponent.y = PADDLE_OFFSET;
905
-
906
- // Show opponent introduction
907
- showOpponentIntro(opponentType);
908
- }
909
-
910
- // Show opponent introduction
911
- function showOpponentIntro(opponent) {
912
- const introElement = document.createElement('div');
913
- introElement.className = 'opponent-intro';
914
- introElement.innerHTML = `
915
- <div class="opponent-name">${opponent.name}</div>
916
- <div class="opponent-description">${opponent.description}</div>
917
- ${opponent.special ? `<div class="opponent-special">Special: ${opponent.special}</div>` : ''}
918
- <button class="btn" id="startRound" style="margin-top: 15px;">Start Round</button>
919
- `;
920
-
921
- document.querySelector('.game-container').appendChild(introElement);
922
-
923
- document.getElementById('startRound').addEventListener('click', () => {
924
- introElement.remove();
925
- ball.active = true;
926
- });
927
  }
928
 
929
- // Show stage modifier
930
- function showModifier(modifier) {
931
- const modifierElement = document.createElement('div');
932
- modifierElement.className = 'modifier-indicator';
933
- modifierElement.innerHTML = `
934
- <div class="modifier-title">Stage Modifier: ${modifier.name}</div>
935
- <div class="modifier-effect">${modifier.description}</div>
936
- ${modifier.special ? `<div class="opponent-special">Effect: ${modifier.special}</div>` : ''}
937
- <button class="btn" id="continueGame" style="margin-top: 15px;">Continue</button>
938
- `;
939
-
940
- document.querySelector('.game-container').appendChild(modifierElement);
941
-
942
- document.getElementById('continueGame').addEventListener('click', () => {
943
- modifierElement.remove();
944
- if (!ball.active) {
945
- // If the ball was waiting to be served
946
- ball.active = true;
947
- }
948
- });
949
  }
950
 
951
- // Start the game
952
- function startGame() {
953
- resetGame();
954
-
955
- mainMenu.style.display = 'none';
956
- gameOverScreen.style.display = 'none';
957
-
958
- gameRunning = true;
959
- gameOver = false;
960
-
961
- // Reset scores
962
- gameState.playerScore = 0;
963
- gameState.opponentScore = 0;
964
- gameState.currentOpponent = 0;
965
-
966
- updateScores();
967
-
968
- // Start game loop
969
- if (animationFrameId) {
970
- cancelAnimationFrame(animationFrameId);
971
- }
972
-
973
- gameLoop();
974
  }
975
 
976
- // Main game loop
977
- function gameLoop() {
978
- if (!gameRunning) return;
979
-
980
- update();
981
- draw();
982
-
983
- animationFrameId = requestAnimationFrame(gameLoop);
984
  }
985
 
986
- // Update game state
987
- function update() {
988
- if (!gameRunning) return;
989
-
990
- gameState.frameCount++;
991
-
992
- // Apply stage modifier if active
993
- if (gameState.currentModifier) {
994
- gameState.currentModifier.effect();
995
  }
996
 
997
- // Update active power-ups and check for expiration
998
- updatePowerups();
999
-
1000
- // Ball movement
1001
- if (ball.active) {
1002
- // Store current position for trail
1003
- ball.trail.push({x: ball.x, y: ball.y});
1004
- if (ball.trail.length > 10) {
1005
- ball.trail.shift();
1006
- }
1007
-
1008
- // Move ball
1009
- ball.x += ball.speedX;
1010
- ball.y += ball.speedY;
1011
-
1012
- // Ball collisions with walls
1013
- if (ball.x + ball.size > GAME_WIDTH || ball.x < 0) {
1014
- ball.speedX = -ball.speedX;
1015
- ball.x = Math.max(0, Math.min(GAME_WIDTH - ball.size, ball.x));
1016
- }
1017
-
1018
- // Ball collisions with player paddle
1019
- if (
1020
- ball.y + ball.size > player.y &&
1021
- ball.y < player.y + player.height &&
1022
- ball.x + ball.size > player.x &&
1023
- ball.x < player.x + player.width
1024
- ) {
1025
- // Calculate angle based on where it hits the paddle
1026
- const hitPosition = (ball.x - player.x) / player.width;
1027
- const angle = hitPosition * Math.PI - Math.PI / 2; // -90 to 90 degrees
1028
-
1029
- ball.speedX = Math.sin(angle) * ball.speed;
1030
- ball.speedY = -Math.cos(angle) * ball.speed;
1031
-
1032
- // Ball magnet effect
1033
- if (gameState.powerupsActive.magnet) {
1034
- ball.speedY *= 0.8; // Reduce vertical speed to make it easier to hit back
1035
- }
1036
-
1037
- // Powerup spawn chance
1038
- if (Math.random() < POWERUP_SPAWN_CHANCE) {
1039
- spawnPowerup();
1040
- }
1041
- }
1042
-
1043
- // Ball collisions with opponent paddle
1044
- if (
1045
- ball.y < opponent.y + opponent.height &&
1046
- ball.y + ball.size > opponent.y &&
1047
- ball.x + ball.size > opponent.x &&
1048
- ball.x < opponent.x + opponent.width
1049
- ) {
1050
- // Basic AI reaction calculation
1051
- const paddleCenter = opponent.x + opponent.width / 2;
1052
- const ballCenter = ball.x + ball.size / 2;
1053
- const distanceFromCenter = ballCenter - paddleCenter;
1054
-
1055
- // Calculate angle based on where it hits the paddle (simplified for AI)
1056
- const angle = (distanceFromCenter / (opponent.width / 2)) * (Math.PI / 3); // -60 to 60 degrees
1057
-
1058
- ball.speedX = Math.sin(angle) * ball.speed;
1059
- ball.speedY = Math.cos(angle) * ball.speed;
1060
- }
1061
-
1062
- // Ball out of bounds - scoring
1063
- if (ball.y < 0) {
1064
- // Player scores
1065
- gameState.playerScore++;
1066
- gameState.runStats.totalPoints++;
1067
- updateScores();
1068
- resetBall();
1069
-
1070
- // Check for win
1071
- if (gameState.playerScore >= winScore && gameState.playerScore - gameState.opponentScore >= 2) {
1072
- roundWon();
1073
- }
1074
- } else if (ball.y + ball.size > GAME_HEIGHT) {
1075
- // Opponent scores
1076
- gameState.opponentScore++;
1077
- updateScores();
1078
- resetBall();
1079
-
1080
- // Check for loss
1081
- if (gameState.opponentScore >= winScore && gameState.opponentScore - gameState.playerScore >= 2) {
1082
- roundLost();
1083
- }
1084
- }
1085
  }
1086
 
1087
- // Opponent AI - simple tracking of ball with some imperfection
1088
- if (ball.active && Math.random() > 0.3) { // 70% chance to react to allow for "human-like" imperfection
1089
- const paddleCenter = opponent.x + opponent.width / 2;
1090
- const ballFutureX = ball.x + ball.speedX * 5; // Predict future position slightly
1091
- const targetX = ballFutureX - opponent.width / 2;
1092
-
1093
- // Apply reaction time
1094
- const reactionSpeed = opponent.reactionTime * opponent.speed;
1095
-
1096
- if (paddleCenter < ballFutureX - 10) {
1097
- opponent.x += reactionSpeed;
1098
- } else if (paddleCenter > ballFutureX + 10) {
1099
- opponent.x -= reactionSpeed;
1100
- }
1101
-
1102
- // Keep opponent within bounds
1103
- opponent.x = Math.max(0, Math.min(GAME_WIDTH - opponent.width, opponent.x));
1104
  }
1105
 
1106
- // Move powerups
1107
- updatePowerupPositions();
1108
- }
1109
-
1110
- // Update active powerups
1111
- function updatePowerups() {
1112
- const now = Date.now();
1113
- for (const powerupId in gameState.powerupsActive) {
1114
- if (gameState.powerupsActive[powerupId].endTime <= now) {
1115
- removePowerup(powerupId);
1116
- }
1117
- }
1118
-
1119
- // Update powerup HUD
1120
- updatePowerupHud();
1121
- }
1122
-
1123
- // Update powerup positions and check for collection
1124
- function updatePowerupPositions() {
1125
- for (let i = powerups.length - 1; i >= 0; i--) {
1126
- const powerup = powerups[i];
1127
-
1128
- // Move powerup down
1129
- powerup.y += 1;
1130
-
1131
- // Check if collected by player
1132
- if (
1133
- powerup.y + POWERUP_SIZE > player.y &&
1134
- powerup.y < player.y + player.height &&
1135
- powerup.x + POWERUP_SIZE > player.x &&
1136
- powerup.x < player.x + player.width
1137
- ) {
1138
- // Apply powerup effect
1139
- applyPowerup(powerup.type);
1140
-
1141
- // Remove from array
1142
- powerups.splice(i, 1);
1143
- gameState.runStats.powerupsCollected++;
1144
-
1145
- // Show collection notification
1146
- showPowerupNotification(powerupTypes.find(p => p.id === powerup.type).name);
1147
- }
1148
-
1149
- // Remove if out of bounds
1150
- if (powerup.y > GAME_HEIGHT) {
1151
- powerups.splice(i, 1);
1152
- }
1153
  }
1154
  }
1155
 
1156
- // Spawn a new powerup at random position
1157
- function spawnPowerup(specificType = null) {
1158
- const type = specificType || powerupTypes[Math.floor(Math.random() * powerupTypes.length)].id;
1159
-
1160
- powerups.push({
1161
- x: Math.random() * (GAME_WIDTH - POWERUP_SIZE - 20) + 10,
1162
- y: 10,
1163
- type: type,
1164
- size: POWERUP_SIZE
1165
- });
1166
  }
1167
 
1168
- // Apply a powerup effect
1169
- function applyPowerup(powerupId) {
1170
- const now = Date.now();
1171
- let duration = POWERUP_DURATION;
1172
-
1173
- // Apply duration upgrade
1174
- if (playerMeta.upgrades.powerupDuration > 0) {
1175
- duration *= 1 + 0.25 * playerMeta.upgrades.powerupDuration;
1176
- }
1177
-
1178
- // Handle powerup effects
1179
- switch (powerupId) {
1180
- case 'paddleXL':
1181
- gameState.powerupsActive.paddleXL = {
1182
- originalWidth: player.width,
1183
- endTime: now + duration
1184
- };
1185
- player.width *= 1.5;
1186
- break;
1187
-
1188
- case 'paddleXS':
1189
- gameState.powerupsActive.paddleXS = {
1190
- originalWidth: player.width,
1191
- endTime: now + duration
1192
- };
1193
- player.width *= 0.5;
1194
- break;
1195
-
1196
- case 'speedBoost':
1197
- gameState.powerupsActive.speedBoost = {
1198
- originalSpeed: ball.speed,
1199
- endTime: now + duration
1200
- };
1201
- ball.speed *= 1.5;
1202
- break;
1203
-
1204
- case 'multiBall':
1205
- // Spawn 3 extra balls
1206
- for (let i = 0; i < 3; i++) {
1207
- const angle = Math.random() * Math.PI * 2;
1208
- const speed = ball.speed * (0.8 + Math.random() * 0.4);
1209
-
1210
- powerups.push({
1211
- x: ball.x,
1212
- y: ball.y,
1213
- type: 'extraBall',
1214
- size: BALL_SIZE,
1215
- speedX: Math.cos(angle) * speed,
1216
- speedY: Math.sin(angle) * speed,
1217
- active: true,
1218
- lifetime: 300 // frames
1219
- });
1220
- }
1221
- break;
1222
-
1223
- case 'slowMotion':
1224
- gameState.powerupsActive.slowMotion = {
1225
- endTime: now + duration
1226
- };
1227
- // Effect handled in the update loop
1228
- break;
1229
-
1230
- case 'magnet':
1231
- gameState.powerupsActive.magnet = {
1232
- endTime: now + duration
1233
- };
1234
- break;
1235
- }
1236
  }
1237
 
1238
- // Remove a powerup effect
1239
- function removePowerup(powerupId) {
1240
- switch (powerupId) {
1241
- case 'paddleXL':
1242
- case 'paddleXS':
1243
- player.width = gameState.powerupsActive[powerupId].originalWidth;
1244
- break;
1245
-
1246
- case 'speedBoost':
1247
- ball.speed = gameState.powerupsActive[powerupId].originalSpeed;
1248
- break;
1249
- }
1250
-
1251
- delete gameState.powerupsActive[powerupId];
1252
- updatePowerupHud();
1253
  }
1254
 
1255
- // Show powerup notification
1256
- function showPowerupNotification(powerupName) {
1257
- const notification = document.querySelector('.powerup-notification');
1258
-
1259
- if (!notification) {
1260
- const notifElement = document.createElement('div');
1261
- notifElement.className = 'powerup-notification';
1262
- notifElement.textContent = `Powerup Collected: ${powerupName}`;
1263
- document.querySelector('.game-container').appendChild(notifElement);
1264
-
1265
- setTimeout(() => {
1266
- notifElement.classList.add('show');
1267
- }, 10);
1268
-
1269
- setTimeout(() => {
1270
- notifElement.remove();
1271
- }, 2000);
1272
- } else {
1273
- notification.textContent = `Powerup Collected: ${powerupName}`;
1274
- notification.classList.remove('show');
1275
-
1276
- setTimeout(() => {
1277
- notification.classList.add('show');
1278
- }, 10);
1279
- }
1280
  }
1281
 
1282
- // Reset ball to center
1283
- function resetBall() {
1284
- ball.x = GAME_WIDTH / 2 - ball.size / 2;
1285
- ball.y = GAME_HEIGHT / 2 - ball.size / 2;
1286
-
1287
- // Initial direction - slightly randomized
1288
- const angle = Math.random() * Math.PI / 2 - Math.PI / 4; // -45 to 45 degrees
1289
- const speed = BALL_INITIAL_SPEED;
1290
-
1291
- if (playerMeta.upgrades.ballStartSpeed > 0) {
1292
- speed *= 1 - 0.1 * playerMeta.upgrades.ballStartSpeed;
1293
  }
1294
-
1295
- ball.speed = speed;
1296
- ball.speedX = Math.sin(angle) * ball.speed;
1297
- ball.speedY = Math.cos(angle) * ball.speed;
1298
-
1299
- // Randomly choose who serves (positive or negative Y direction)
1300
- if (Math.random() > 0.5) {
1301
- ball.speedY = -ball.speedY;
1302
  }
1303
-
1304
- ball.active = false; // Wait for player input (space) to serve
1305
- ball.trail = [];
1306
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1307
 
1308
- // Player won the round
1309
- function roundWon() {
1310
- gameState.runStats.opponentsDefeated++;
1311
- gameState.runStats.roundsPlayed++;
 
 
 
 
 
 
1312
 
1313
- // Next opponent
1314
- gameState.currentOpponent++;
 
 
 
 
 
 
 
 
1315
 
1316
- // Reset scores
1317
- gameState.playerScore = 0;
1318
- gameState.opponentScore = 0;
 
 
 
 
 
 
 
1319
 
1320
- // Setup next opponent
1321
- setupOpponent(gameState.currentOpponent);
 
 
 
 
 
 
 
 
1322
 
1323
- // Reset ball (but don't serve yet)
1324
- resetBall();
1325
- updateScores();
1326
- }
 
 
 
 
 
 
 
1327
 
1328
- // Player lost the round
1329
- function roundLost() {
1330
- gameState.runStats.roundsPlayed++;
1331
- gameOver = true;
1332
- gameRunning = false;
1333
-
1334
- // Calculate earned currency (1 per point + 10 per opponent defeated)
1335
- const earnedCurrency = Math.floor(gameState.runStats.totalPoints + gameState.runStats.opponentsDefeated * 10);
1336
- playerMeta.currency += earnedCurrency;
1337
- saveGame();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1338
 
1339
- // Update game over screen
1340
- document.getElementById('stat-rounds').textContent = gameState.runStats.opponentsDefeated;
1341
- document.getElementById('stat-score').textContent = gameState.runStats.totalPoints;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1342
 
1343
- // Calculate time played
1344
- const timePlayed = Math.floor((Date.now() - gameState.runStartTime) / 1000);
1345
- const minutes = Math.floor(timePlayed / 60);
1346
- const seconds = timePlayed % 60;
1347
- document.getElementById('stat-time').textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1348
 
1349
- document.getElementById('earned-currency').textContent = earnedCurrency;
 
 
 
 
1350
 
1351
- // Show game over screen
1352
- gameOverScreen.style.display = 'flex';
 
1353
  }
1354
 
1355
- // Update score display
1356
- function updateScores() {
1357
- document.querySelector('.player-score').textContent = `YOU: ${gameState.playerScore}`;
1358
- document.querySelector('.opponent-score').textContent = `${opponent.name.toUpperCase().substring(0, 5)}: ${gameState.opponentScore}`;
1359
- }
1360
-
1361
- // Update powerup HUD display
1362
- function updatePowerupHud() {
1363
- const activePowerups = Object.keys(gameState.powerupsActive);
1364
- const powerupElements = document.querySelectorAll('.powerup-icon');
1365
-
1366
- // Reset all icons
1367
- powerupElements.forEach(el => {
1368
- el.textContent = '?';
1369
- el.style.backgroundColor = 'rgba(76, 201, 240, 0.2)';
1370
- el.classList.remove('active');
1371
  });
1372
 
1373
- // Update active icons
1374
- activePowerups.forEach((powerupId, index) => {
1375
- if (index < playerMeta.upgrades.powerupSlots) {
1376
- const powerup = powerupTypes.find(p => p.id === powerupId);
1377
- if (powerup) {
1378
- powerupElements[index].textContent = powerup.icon;
1379
- powerupElements[index].style.backgroundColor = powerup.color;
1380
- powerupElements[index].classList.add('active');
1381
-
1382
- // Calculate remaining time percentage
1383
- const remaining = (gameState.powerupsActive[powerupId].endTime - Date.now()) / POWERUP_DURATION;
1384
- powerupElements[index].style.backgroundImage =
1385
- `linear-gradient(to top, ${powerup.color} ${remaining * 100}%, rgba(76, 201, 240, 0.2) ${remaining * 100}%)`;
1386
- }
1387
  }
1388
  });
1389
- }
1390
-
1391
- // Draw everything
1392
- function draw() {
1393
- // Clear canvas
1394
- ctx.fillStyle = '#14213D';
1395
- ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
1396
 
1397
- // Draw center line
1398
- ctx.setLineDash([5, 10]);
1399
- ctx.beginPath();
1400
- ctx.moveTo(GAME_WIDTH / 2, 0);
1401
- ctx.lineTo(GAME_WIDTH / 2, GAME_HEIGHT);
1402
- ctx.strokeStyle = 'rgba(248, 249, 250, 0.5)';
1403
- ctx.lineWidth = 2;
1404
- ctx.stroke();
1405
- ctx.setLineDash([]);
1406
-
1407
- // Draw ball trail
1408
- if (ball.active) {
1409
- ball.trail.forEach((pos, i) => {
1410
- const alpha = 0.1 + (i / ball.trail.length) * 0.4;
1411
- ctx.fillStyle = `rgba(248, 249, 250, ${alpha})`;
1412
- ctx.fillRect(pos.x, pos.y, ball.size, ball.size);
1413
- });
1414
  }
1415
-
1416
- // Draw powerups
1417
- powerups.forEach(powerup => {
1418
- const powerupType = powerupTypes.find(p => p.id === powerup.type);
1419
- if (powerupType) {
1420
- ctx.fillStyle = powerupType.color;
1421
- ctx.fillRect(powerup.x, powerup.y, powerup.size, powerup.size);
1422
-
1423
- // Draw icon/text
1424
- ctx.fillStyle = '#FFFFFF';
1425
- ctx.font = '10px Arial';
1426
- ctx.textAlign = 'center';
1427
- ctx.textBaseline = 'middle';
1428
- ctx.fillText(powerupType.icon, powerup.x + powerup.size / 2, powerup.y + powerup.size / 2);
1429
- } else if (powerup.type === 'extraBall') {
1430
- // Draw extra balls (from multi-ball)
1431
- ctx.fillStyle = '#FFFFFF';
1432
- ctx.fillRect(powerup.x, powerup.y, powerup.size, powerup.size);
1433
  }
1434
  });
1435
-
1436
- // Draw ball
1437
- ctx.fillStyle = ball.color;
1438
- ctx.fillRect(ball.x, ball.y, ball.size, ball.size);
1439
-
1440
- // Draw player paddle
1441
- ctx.fillStyle = player.color;
1442
- ctx.fillRect(player.x, player.y, player.width, player.height);
1443
-
1444
- // Draw opponent paddle
1445
- ctx.fillStyle = opponent.color;
1446
- ctx.fillRect(opponent.x, opponent.y, opponent.width, opponent.height);
1447
-
1448
- // Draw ball direction indicator when waiting to serve
1449
- if (!ball.active) {
1450
- ctx.setLineDash([3, 3]);
1451
- ctx.beginPath();
1452
- ctx.moveTo(ball.x + ball.size / 2, ball.y + ball.size / 2);
1453
- ctx.lineTo(
1454
- ball.x + ball.size / 2 + ball.speedX * 30,
1455
- ball.y + ball.size / 2 + ball.speedY * 30
1456
- );
1457
- ctx.strokeStyle = 'rgba(248, 249, 250, 0.7)';
1458
- ctx.lineWidth = 2;
1459
- ctx.stroke();
1460
- ctx.setLineDash([]);
1461
-
1462
- // Draw serve prompt
1463
- ctx.fillStyle = 'rgba(248, 249, 250, 0.8)';
1464
- ctx.font = '16px "Press Start 2P"';
1465
- ctx.textAlign = 'center';
1466
- ctx.fillText('PRESS SPACE TO SERVE', GAME_WIDTH / 2, 50);
1467
- }
1468
-
1469
- // If game is over but not showing game over screen (transition)
1470
- if (gameOver && gameOverScreen.style.display === 'none') {
1471
- ctx.fillStyle = 'rgba(230, 57, 70, 0.7)';
1472
- ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
1473
 
1474
- ctx.fillStyle = '#FFFFFF';
1475
- ctx.font = '30px "Press Start 2P"';
1476
- ctx.textAlign = 'center';
1477
- ctx.fillText('GAME OVER', GAME_WIDTH / 2, GAME_HEIGHT / 2);
1478
- }
1479
  }
1480
 
1481
- // Keyboard controls
1482
- let rightPressed = false;
1483
- let leftPressed = false;
1484
-
1485
- function keyDownHandler(e) {
1486
- if (e.key === 'Right' || e.key === 'ArrowRight') {
1487
- rightPressed = true;
1488
- } else if (e.key === 'Left' || e.key === 'ArrowLeft') {
1489
- leftPressed = true;
1490
- } else if (e.key === ' ' && !ball.active) {
1491
- // Serve ball on space
1492
- if (gameRunning && !gameOver) {
1493
- ball.active = true;
1494
  }
1495
- } else if (e.key === 'Escape') {
1496
- if (gameRunning) {
1497
- // Pause game
1498
- gameRunning = false;
1499
- } else if (gameOverScreen.style.display === 'flex') {
1500
- showMainMenu();
1501
- } else if (upgradesScreen.style.display === 'flex') {
1502
- showMainMenu();
1503
- } else {
1504
- // Resume game from pause
1505
- gameRunning = true;
1506
- gameLoop();
1507
- }
1508
- }
1509
- }
1510
 
1511
- function keyUpHandler(e) {
1512
- if (e.key === 'Right' || e.key === 'ArrowRight') {
1513
- rightPressed = false;
1514
- } else if (e.key === 'Left' || e.key === 'ArrowLeft') {
1515
- leftPressed = false;
1516
- }
1517
- }
1518
 
1519
- // Show main menu
1520
- function showMainMenu(){
1521
- console.log('show main menu');
1522
- }
1523
- </script>
1524
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <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>
1525
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Nebula Storm - Game Design Document</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <style>
 
 
 
 
9
  :root {
10
+ --primary: #6a00f4;
11
+ --secondary: #ff3e7f;
12
+ --dark: #0f0526;
13
+ --darker: #080117;
14
+ --light: #e0d6ff;
15
+ --accent: #00e1ff;
16
+ }
17
+
 
 
 
18
  * {
19
  margin: 0;
20
  padding: 0;
21
  box-sizing: border-box;
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
  }
24
+
25
  body {
 
26
  background-color: var(--dark);
27
  color: var(--light);
28
+ line-height: 1.6;
29
+ overflow-x: hidden;
30
+ }
31
+
32
+ .container {
33
+ max-width: 1200px;
34
+ margin: 0 auto;
35
+ padding: 0 20px;
36
+ }
37
+
38
+ header {
39
+ background: linear-gradient(135deg, var(--primary), var(--darker));
40
+ padding: 2rem 0;
41
+ text-align: center;
42
+ border-bottom: 3px solid var(--accent);
43
  position: relative;
 
 
 
 
 
 
44
  overflow: hidden;
45
  }
46
+
47
+ header::before {
48
+ content: "";
49
+ position: absolute;
50
+ top: 0;
51
+ left: 0;
52
  width: 100%;
53
  height: 100%;
54
+ background: radial-gradient(circle at center, transparent 0%, rgba(0,0,0,0.7) 100%);
55
+ pointer-events: none;
56
  }
57
+
58
+ .header-content {
59
+ position: relative;
60
+ z-index: 2;
 
 
 
 
 
 
 
 
 
 
61
  }
62
+
63
+ h1 {
64
+ font-size: 3.5rem;
65
+ margin-bottom: 1rem;
66
+ background: linear-gradient(to right, var(--accent), var(--secondary));
67
+ -webkit-background-clip: text;
68
+ -webkit-text-fill-color: transparent;
69
+ text-shadow: 0 0 15px rgba(106, 0, 244, 0.3);
70
  }
71
+
72
+ .subtitle {
73
+ font-size: 1.5rem;
74
+ color: var(--light);
75
+ margin-bottom: 2rem;
76
+ opacity: 0.9;
 
 
 
 
77
  }
78
+
79
+ .tag-list {
 
 
 
 
80
  display: flex;
 
81
  justify-content: center;
82
+ gap: 1rem;
83
+ margin-bottom: 2rem;
 
 
84
  }
85
+
86
+ .tag {
87
+ background-color: rgba(255, 255, 255, 0.1);
88
+ padding: 0.5rem 1rem;
89
+ border-radius: 50px;
90
+ font-size: 0.9rem;
91
+ border: 1px solid var(--accent);
92
+ backdrop-filter: blur(5px);
93
  }
94
+
95
+ .screenshot-placeholder {
96
+ width: 100%;
97
+ height: 400px;
98
+ background: linear-gradient(135deg, #2a0064, #4a00a0);
99
+ border-radius: 10px;
100
+ margin: 2rem 0;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ position: relative;
105
+ overflow: hidden;
106
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
107
  }
108
+
109
+ .screenshot-placeholder::before {
110
+ content: "";
111
  position: absolute;
112
  top: 0;
113
  left: 0;
114
  width: 100%;
115
  height: 100%;
116
+ background:
117
+ radial-gradient(circle at 30% 70%, rgba(0, 225, 255, 0.2) 0%, transparent 50%),
118
+ radial-gradient(circle at 70% 30%, rgba(255, 62, 127, 0.2) 0%, transparent 50%);
119
+ }
120
+
121
+ .placeholder-content {
 
122
  text-align: center;
123
+ z-index: 2;
124
+ }
125
+
126
+ .placeholder-content i {
127
+ font-size: 5rem;
128
+ color: rgba(255, 255, 255, 0.1);
129
+ margin-bottom: 1rem;
130
+ }
131
+
132
+ section {
133
+ padding: 3rem 0;
134
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
135
+ }
136
+
137
+ h2 {
138
+ font-size: 2.5rem;
139
+ margin-bottom: 1.5rem;
 
140
  color: var(--accent);
141
+ position: relative;
142
+ display: inline-block;
143
+ }
144
+
145
+ h2::after {
146
+ content: "";
147
+ position: absolute;
148
+ bottom: -10px;
149
+ left: 0;
150
+ width: 100px;
151
+ height: 3px;
152
+ background: var(--secondary);
153
+ }
154
+
155
+ h3 {
156
+ font-size: 1.8rem;
157
+ margin: 2rem 0 1rem;
158
+ color: var(--secondary);
159
+ }
160
+
161
+ p {
162
+ margin-bottom: 1.5rem;
163
+ font-size: 1.1rem;
164
+ }
165
+
166
+ ul, ol {
167
+ margin-bottom: 1.5rem;
168
+ padding-left: 2rem;
169
  }
170
+
171
+ li {
172
+ margin-bottom: 0.5rem;
173
+ }
174
+
175
+ .features {
176
+ display: grid;
177
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
178
+ gap: 2rem;
179
+ margin-top: 2rem;
180
+ }
181
+
182
+ .feature-card {
183
+ background: rgba(15, 5, 38, 0.7);
184
+ border-radius: 10px;
185
+ padding: 1.5rem;
186
+ border: 1px solid rgba(106, 0, 244, 0.3);
187
+ transition: transform 0.3s, box-shadow 0.3s;
188
+ backdrop-filter: blur(5px);
189
+ }
190
+
191
+ .feature-card:hover {
192
+ transform: translateY(-5px);
193
+ box-shadow: 0 10px 20px rgba(106, 0, 244, 0.3);
194
+ border-color: var(--accent);
195
+ }
196
+
197
+ .feature-icon {
198
+ font-size: 2rem;
199
+ color: var(--accent);
200
+ margin-bottom: 1rem;
201
+ }
202
+
203
+ .feature-title {
204
+ font-size: 1.3rem;
205
+ margin-bottom: 0.5rem;
206
  color: var(--light);
207
+ }
208
+
209
+ .control-scheme {
210
+ display: grid;
211
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
212
+ gap: 1.5rem;
213
+ margin-top: 2rem;
214
+ }
215
+
216
+ .control-item {
217
+ background: rgba(15, 5, 38, 0.7);
218
+ border-radius: 10px;
219
+ padding: 1.5rem;
220
+ border-left: 4px solid var(--accent);
221
+ }
222
+
223
+ .control-action {
224
+ font-weight: bold;
225
+ color: var(--secondary);
226
+ margin-bottom: 0.5rem;
227
+ }
228
+
229
+ .tabs {
230
+ display: flex;
231
+ margin-bottom: 2rem;
232
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
233
+ }
234
+
235
+ .tab-button {
236
+ padding: 1rem 2rem;
237
+ background: transparent;
238
  border: none;
239
+ color: var(--light);
240
+ font-size: 1.1rem;
 
241
  cursor: pointer;
242
+ position: relative;
243
+ opacity: 0.7;
244
  transition: all 0.3s;
245
  }
246
+
247
+ .tab-button.active {
248
+ opacity: 1;
249
+ color: var(--accent);
 
250
  }
251
+
252
+ .tab-button.active::after {
253
+ content: "";
254
+ position: absolute;
255
+ bottom: -1px;
256
+ left: 0;
257
+ width: 100%;
258
+ height: 3px;
259
+ background: var(--accent);
260
  }
261
+
262
+ .tab-content {
263
+ display: none;
264
+ animation: fadeIn 0.5s ease-in-out;
265
  }
266
+
267
+ .tab-content.active {
268
+ display: block;
269
  }
270
+
271
+ @keyframes fadeIn {
272
+ from { opacity: 0; transform: translateY(10px); }
273
+ to { opacity: 1; transform: translateY(0); }
274
  }
275
+
276
+ .enemy-grid {
277
+ display: grid;
278
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
279
+ gap: 2rem;
280
+ margin-top: 2rem;
281
+ }
282
+
283
+ .enemy-card {
284
+ background: rgba(15, 5, 38, 0.7);
285
  border-radius: 10px;
286
+ overflow: hidden;
287
+ border: 1px solid rgba(106, 0, 244, 0.3);
288
+ transition: transform 0.3s;
289
  }
290
+
291
+ .enemy-card:hover {
292
+ transform: translateY(-5px);
293
+ box-shadow: 0 10px 20px rgba(106, 0, 244, 0.3);
 
294
  }
295
+
296
+ .enemy-header {
297
+ background: linear-gradient(135deg, var(--secondary), var(--primary));
298
+ padding: 1rem;
299
+ text-align: center;
300
  }
301
+
302
+ .enemy-name {
303
+ font-size: 1.2rem;
304
  font-weight: bold;
305
+ margin-bottom: 0.5rem;
306
  }
307
+
308
+ .enemy-difficulty {
309
+ font-size: 0.9rem;
310
+ display: inline-block;
311
+ padding: 0.2rem 0.5rem;
312
+ border-radius: 50px;
313
+ background-color: rgba(0, 0, 0, 0.3);
 
 
 
 
 
 
 
 
314
  }
315
+
316
+ .enemy-body {
317
+ padding: 1.5rem;
 
318
  }
319
+
320
+ .enemy-attribute {
321
+ margin-bottom: 1rem;
 
 
322
  }
323
+
324
+ .attribute-title {
325
+ font-weight: bold;
326
+ color: var(--accent);
327
+ margin-bottom: 0.3rem;
328
  }
329
+
330
+ .weapons-list {
331
+ display: grid;
332
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
333
+ gap: 1.5rem;
334
+ margin-top: 2rem;
 
 
 
 
 
 
 
 
 
335
  }
336
+
337
+ .weapon-card {
338
+ background: rgba(15, 5, 38, 0.7);
339
+ border-radius: 10px;
340
+ padding: 1.5rem;
341
+ border-left: 4px solid var(--secondary);
342
  }
343
+
344
+ .weapon-name {
345
+ font-size: 1.2rem;
346
+ margin-bottom: 0.5rem;
347
+ color: var(--secondary);
348
  }
349
+
350
+ .weapon-stats {
 
351
  display: flex;
352
  flex-wrap: wrap;
353
+ gap: 0.5rem;
354
+ margin-top: 1rem;
 
355
  }
356
+
357
+ .weapon-stat {
358
+ background: rgba(106, 0, 244, 0.2);
359
+ padding: 0.3rem 0.7rem;
360
+ border-radius: 50px;
361
+ font-size: 0.8rem;
 
362
  }
363
+
364
+ .art-style-cards {
365
+ display: grid;
366
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
367
+ gap: 2rem;
368
+ margin-top: 2rem;
369
  }
370
+
371
+ .art-style-card {
372
+ background: rgba(15, 5, 38, 0.7);
373
+ border-radius: 10px;
374
+ padding: 1.5rem;
375
+ border: 1px solid rgba(106, 0, 244, 0.3);
376
  }
377
+
378
+ .art-style-title {
379
+ font-size: 1.3rem;
380
+ margin-bottom: 1rem;
381
+ color: var(--accent);
382
  display: flex;
383
+ align-items: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  }
385
+
386
+ .art-style-title i {
387
+ margin-right: 1rem;
388
+ font-size: 1.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  }
390
+
391
+ .art-style-pros {
392
+ margin-top: 1rem;
393
+ padding-left: 1.5rem;
394
+ border-left: 3px solid var(--accent);
395
  }
396
+
397
+ .pro-item {
398
+ position: relative;
399
+ padding-left: 1.5rem;
400
+ margin-bottom: 0.5rem;
401
  }
402
+
403
+ .pro-item::before {
404
+ content: "✓";
405
  position: absolute;
406
+ left: 0;
407
+ color: var(--accent);
408
+ font-weight: bold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  }
 
 
 
 
 
 
 
 
 
 
 
410
 
411
+ .cons-item::before {
412
+ content: "";
413
+ position: absolute;
414
+ left: 0;
415
+ color: var(--secondary);
416
+ font-weight: bold;
417
+ }
418
 
419
+ .progress-bar {
420
+ width: 100%;
421
+ height: 20px;
422
+ background-color: rgba(255, 255, 255, 0.1);
423
+ border-radius: 10px;
424
+ margin: 1rem 0;
425
+ overflow: hidden;
426
+ }
427
 
428
+ .progress-fill {
429
+ height: 100%;
430
+ background: linear-gradient(90deg, var(--primary), var(--accent));
431
+ border-radius: 10px;
432
+ transition: width 0.5s ease-in-out;
433
+ width: 0;
434
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
+ .progress-labels {
437
+ display: flex;
438
+ justify-content: space-between;
439
+ margin-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  }
441
 
442
+ .progress-label {
443
+ font-size: 0.9rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  }
445
 
446
+ .section-nav {
447
+ position: fixed;
448
+ top: 50%;
449
+ right: 2rem;
450
+ transform: translateY(-50%);
451
+ background: rgba(15, 5, 38, 0.8);
452
+ border-radius: 10px;
453
+ padding: 1rem;
454
+ border: 1px solid rgba(106, 0, 244, 0.5);
455
+ backdrop-filter: blur(5px);
456
+ z-index: 1000;
457
+ display: none;
458
  }
459
 
460
+ .section-nav ul {
461
+ list-style: none;
462
+ padding: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  }
464
 
465
+ .section-nav li {
466
+ margin-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
467
  }
468
 
469
+ .section-nav a {
470
+ color: var(--light);
471
+ text-decoration: none;
472
+ font-size: 0.9rem;
473
+ transition: color 0.3s;
474
+ display: block;
475
+ padding: 0.5rem;
476
+ border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  }
478
 
479
+ .section-nav a:hover, .section-nav a.active {
480
+ color: var(--accent);
481
+ background: rgba(106, 0, 244, 0.3);
 
 
 
 
 
482
  }
483
 
484
+ @media (max-width: 768px) {
485
+ h1 {
486
+ font-size: 2.5rem;
 
 
 
 
 
 
487
  }
488
 
489
+ .subtitle {
490
+ font-size: 1.2rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  }
492
 
493
+ .features, .control-scheme, .enemy-grid, .weapons-list, .art-style-cards {
494
+ grid-template-columns: 1fr;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
496
 
497
+ .section-nav {
498
+ display: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  }
500
  }
501
 
502
+ /* Animated background elements */
503
+ .bg-element {
504
+ position: fixed;
505
+ border-radius: 50%;
506
+ filter: blur(60px);
507
+ opacity: 0.1;
508
+ z-index: -1;
 
 
 
509
  }
510
 
511
+ .bg-element-1 {
512
+ width: 300px;
513
+ height: 300px;
514
+ background: var(--primary);
515
+ top: -100px;
516
+ left: -100px;
517
+ animation: float 15s infinite alternate ease-in-out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  }
519
 
520
+ .bg-element-2 {
521
+ width: 400px;
522
+ height: 400px;
523
+ background: var(--secondary);
524
+ bottom: -150px;
525
+ right: -150px;
526
+ animation: float 20s infinite alternate-reverse ease-in-out;
 
 
 
 
 
 
 
 
527
  }
528
 
529
+ .bg-element-3 {
530
+ width: 200px;
531
+ height: 200px;
532
+ background: var(--accent);
533
+ top: 40%;
534
+ right: 20%;
535
+ animation: float 25s infinite alternate ease-in-out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  }
537
 
538
+ @keyframes float {
539
+ 0%, 100% {
540
+ transform: translate(0, 0);
 
 
 
 
 
 
 
 
541
  }
542
+ 50% {
543
+ transform: translate(50px, 50px);
 
 
 
 
 
 
544
  }
 
 
 
545
  }
546
+ </style>
547
+ </head>
548
+ <body>
549
+ <div class="bg-element bg-element-1"></div>
550
+ <div class="bg-element bg-element-2"></div>
551
+ <div class="bg-element bg-element-3"></div>
552
+
553
+ <header>
554
+ <div class="container header-content">
555
+ <h1>NEBULA STORM</h1>
556
+ <p class="subtitle">A fast-paced sci-fi roguelike shooter with endless alien threats</p>
557
+ <div class="tag-list">
558
+ <span class="tag">Roguelike</span>
559
+ <span class="tag">Twin-stick Shooter</span>
560
+ <span class="tag">Procedural Generation</span>
561
+ <span class="tag">Permadeath</span>
562
+ <span class="tag">Sci-fi</span>
563
+ </div>
564
+
565
+ <div class="screenshot-placeholder">
566
+ <div class="placeholder-content">
567
+ <i class="fas fa-gamepad"></i>
568
+ <h3>Intense Alien Combat</h3>
569
+ <p>Concept screenshot placeholder</p>
570
+ </div>
571
+ </div>
572
+ </div>
573
+ </header>
574
+
575
+ <div class="section-nav">
576
+ <ul>
577
+ <li><a href="#overview" class="active">Overview</a></li>
578
+ <li><a href="#gameplay">Gameplay</a></li>
579
+ <li><a href="#controls">Controls</a></li>
580
+ <li><a href="#progression">Progression</a></li>
581
+ <li><a href="#enemies">Enemies</a></li>
582
+ <li><a href="#weapons">Weapons</a></li>
583
+ <li><a href="#art-style">Art Style</a></li>
584
+ </ul>
585
+ </div>
586
+
587
+ <main class="container">
588
+ <section id="overview">
589
+ <h2>Game Overview</h2>
590
+ <p>Nebula Storm is a fast-paced roguelike shooter set in a distant future where humanity has awakened an ancient alien threat while exploring the edge of known space. As one of the last elite Void Marines, players must battle through procedurally generated alien-infested sectors, upgrading their arsenal and abilities while facing increasingly deadly threats.</p>
591
+ <p>Each run is unique with randomized levels, enemies, and loot, but successful runs unlock permanent upgrades that make subsequent attempts easier. The game combines intense twin-stick shooter action with strategic decision-making about weapon and ability upgrades to create a deeply replayable experience.</p>
592
+
593
+ <div class="features">
594
+ <div class="feature-card">
595
+ <div class="feature-icon"><i class="fas fa-random"></i></div>
596
+ <h3 class="feature-title">Procedural Generation</h3>
597
+ <p>Every run features unique levels, enemy placements, and loot distribution ensuring no two playthroughs are the same.</p>
598
+ </div>
599
+ <div class="feature-card">
600
+ <div class="feature-icon"><i class="fas fa-bomb"></i></div>
601
+ <h3 class="feature-title">Intense Combat</h3>
602
+ <p>Fast-paced, skill-based shooting with satisfying weapon feedback and impactful dodging mechanics.</p>
603
+ </div>
604
+ <div class="feature-card">
605
+ <div class="feature-icon"><i class="fas fa-sync-alt"></i></div>
606
+ <h3 class="feature-title">Permadeath</h3>
607
+ <p>Death is permanent, but you unlock persistent upgrades that carry over between runs.</p>
608
+ </div>
609
+ <div class="feature-card">
610
+ <div class="feature-icon"><i class="fas fa-cogs"></i></div>
611
+ <h3 class="feature-title">Deep Progression</h3>
612
+ <p>A complex web of upgrades, abilities, and weapon modifications to keep players engaged for hours.</p>
613
+ </div>
614
+ </div>
615
+ </section>
616
 
617
+ <section id="gameplay">
618
+ <h2>Core Gameplay Loop</h2>
619
+ <p>The game follows a tight roguelike loop where each run presents new challenges and opportunities. Here's how a typical playthrough progresses:</p>
620
+
621
+ <div class="tabs">
622
+ <button class="tab-button active" onclick="openTab(event, 'gameplay-tab-1')">Level Start</button>
623
+ <button class="tab-button" onclick="openTab(event, 'gameplay-tab-2')">Exploration</button>
624
+ <button class="tab-button" onclick="openTab(event, 'gameplay-tab-3')">Combat</button>
625
+ <button class="tab-button" onclick="openTab(event, 'gameplay-tab-4')">Progression</button>
626
+ </div>
627
 
628
+ <div id="gameplay-tab-1" class="tab-content active">
629
+ <h3>Begin Your Run</h3>
630
+ <p>Each run starts with basic equipment scaled to your permanent upgrade progress. The procedural generation system creates a unique sector layout with interconnected rooms and corridors containing various enemy types and loot.</p>
631
+ <div class="progress-labels">
632
+ <span class="progress-label">Difficulty Spread</span>
633
+ </div>
634
+ <div class="progress-bar">
635
+ <div class="progress-fill" style="width: 30%"></div>
636
+ </div>
637
+ </div>
638
 
639
+ <div id="gameplay-tab-2" class="tab-content">
640
+ <h3>Explore the Sector</h3>
641
+ <p>As you navigate the level, you'll encounter various challenges and opportunities. Rooms contain different combinations of enemies, environmental hazards, and potential rewards like chests, shops, or special events.</p>
642
+ <div class="progress-labels">
643
+ <span class="progress-label">Exploration Intensity</span>
644
+ </div>
645
+ <div class="progress-bar">
646
+ <div class="progress-fill" style="width: 20%"></div>
647
+ </div>
648
+ </div>
649
 
650
+ <div id="gameplay-tab-3" class="tab-content">
651
+ <h3>Engage in Combat</h3>
652
+ <p>When encountering enemies, the game shifts to fast-paced combat sequences. Multiple enemy types with different behaviors require players to adapt their playstyle using movement, dodging, and smart weapon choice.</p>
653
+ <div class="progress-labels">
654
+ <span class="progress-label">Combat Intensity</span>
655
+ </div>
656
+ <div class="progress-bar">
657
+ <div class="progress-fill" style="width: 50%"></div>
658
+ </div>
659
+ </div>
660
 
661
+ <div id="gameplay-tab-4" class="tab-content">
662
+ <h3>Progress Your Build</h3>
663
+ <p>After clearing areas, players gain loot drops that can significantly change their power level. Careful choices about which weapons, upgrades, and abilities to take create emergent synergies that define each run's playstyle.</p>
664
+ <div class="progress-labels">
665
+ <span class="progress-label">Build Potential</span>
666
+ </div>
667
+ <div class="progress-bar">
668
+ <div class="progress-fill" style="width: 80%"></div>
669
+ </div>
670
+ </div>
671
+ </section>
672
 
673
+ <section id="controls">
674
+ <h2>Control Scheme</h2>
675
+ <p>Nebula Storm offers two control schemes to accommodate different player preferences. Both provide precise control for the game's demanding combat scenarios.</p>
676
+
677
+ <h3>Control Options</h3>
678
+ <div class="control-scheme">
679
+ <div class="control-item">
680
+ <div class="control-action">Twin-Stick Controls</div>
681
+ <p>Left stick: Movement<br>
682
+ Right stick: Aim/shooting direction<br>
683
+ RT/R2: Fire weapon<br>
684
+ A: Dodge/evade<br>
685
+ X: Use ability 1<br>
686
+ Y: Use ability 2<br>
687
+ B: Interact/pickup</p>
688
+ </div>
689
+ <div class="control-item">
690
+ <div class="control-action">Mouse & Keyboard</div>
691
+ <p>WASD: Movement<br>
692
+ Mouse: Aim/shooting direction<br>
693
+ Left click: Fire weapon<br>
694
+ Shift: Dodge/evade<br>
695
+ Q: Use ability 1<br>
696
+ E: Use ability 2<br>
697
+ F: Interact/pickup</p>
698
+ </div>
699
+ <div class="control-item">
700
+ <div class="control-action">Common Controls</div>
701
+ <p>Tab: Open map<br>
702
+ ESC: Pause menu<br>
703
+ 1-4: Quick select weapons<br>
704
+ Mouse wheel: Cycle weapons<br>
705
+ Space: Confirm/select (menus)</p>
706
+ </div>
707
+ <div class="control-item">
708
+ <div class="control-action">Gameplay Actions</div>
709
+ <p>Dodging grants brief invulnerability<br>
710
+ Perfect dodges (just before hit) slow time briefly<br>
711
+ Some weapons have alt-fire modes (press L3)<br>
712
+ Toggle sprint on/off for precision movement</p>
713
+ </div>
714
+ </div>
715
+ </section>
716
+
717
+ <section id="progression">
718
+ <h2>Progression Systems</h2>
719
+ <p>Nebula Storm features a dual progression system with temporary run-based upgrades and permanent meta-progression that carries between attempts.</p>
720
+
721
+ <h3>In-Run Progression</h3>
722
+ <p>During each playthrough, players collect various upgrades that enhance their capabilities for that specific run:</p>
723
+ <ul>
724
+ <li><strong>Weapons:</strong> Different weapon types with unique properties and upgrade paths</li>
725
+ <li><strong>Passive Modifiers:</strong> Stat boosts and special effects that enhance your capabilities</li>
726
+ <li><strong>Abilities:</strong> Special powers with cooldowns that provide combat advantages</li>
727
+ <li><strong>Nanite Infusions:</strong> Temporary buffs that last until death or can be replaced</li>
728
+ </ul>
729
+
730
+ <h3>Permanent Progression</h3>
731
+ <p>Between runs, players spend collected Void Crystals on upgrades that persist indefinitely:</p>
732
+ <div class="features">
733
+ <div class="feature-card">
734
+ <div class="feature-icon"><i class="fas fa-shield-alt"></i></div>
735
+ <h3 class="feature-title">Base Stats</h3>
736
+ <p>Increase starting health, movement speed, damage resistance, etc.</p>
737
+ </div>
738
+ <div class="feature-card">
739
+ <div class="feature-icon"><i class="fas fa-box-open"></i></div>
740
+ <h3 class="feature-title">Starting Options</h3>
741
+ <p>Unlock new starting weapons, abilities, or equipment loadouts.</p>
742
+ </div>
743
+ <div class="feature-card">
744
+ <div class="feature-icon"><i class="fas fa-dice"></i></div>
745
+ <h3 class="feature-title">Loot Quality</h3>
746
+ <p>Improve the chances of finding higher rarity items during runs.</p>
747
+ </div>
748
+ <div class="feature-card">
749
+ <div class="feature-icon"><i class="fas fa-trophy"></i></div>
750
+ <h3 class="feature-title">Content Unlocks</h3>
751
+ <p>Discover new enemy variants, levels, and challenges as you progress.</p>
752
+ </div>
753
+ </div>
754
+ </section>
755
+
756
+ <section id="enemies">
757
+ <h2>Alien Enemy Types</h2>
758
+ <p>The game features a roster of unique alien enemies, each with distinct behaviors and attack patterns that require different strategies to defeat.</p>
759
+
760
+ <div class="enemy-grid">
761
+ <div class="enemy-card">
762
+ <div class="enemy-header">
763
+ <div class="enemy-name">Screecher</div>
764
+ <div class="enemy-difficulty">Low Threat</div>
765
+ </div>
766
+ <div class="enemy-body">
767
+ <div class="enemy-attribute">
768
+ <div class="attribute-title">Behavior</div>
769
+ <p>Fast-moving melee attacker that rushes the player when sighted.</p>
770
+ </div>
771
+ <div class="enemy-attribute">
772
+ <div class="attribute-title">Attacks</div>
773
+ <p>Close-range claw swipe that causes bleeding (damage over time).</p>
774
+ </div>
775
+ <div class="enemy-attribute">
776
+ <div class="attribute-title">Tactics</div>
777
+ <p>Dodge when they leap, then punish while they recover.</p>
778
+ </div>
779
+ </div>
780
+ </div>
781
+
782
+ <div class="enemy-card">
783
+ <div class="enemy-header">
784
+ <div class="enemy-name">Spitter</div>
785
+ <div class="enemy-difficulty">Medium Threat</div>
786
+ </div>
787
+ <div class="enemy-body">
788
+ <div class="enemy-attribute">
789
+ <div class="attribute-title">Behavior</div>
790
+ <p>Ranged attacker that prefers to keep distance and fire projectile volleys.</p>
791
+ </div>
792
+ <div class="enemy-attribute">
793
+ <div class="attribute-title">Attacks</div>
794
+ <p>Acid globs that explode into pools of damaging liquid.</p>
795
+ </div>
796
+ <div class="enemy-attribute">
797
+ <div class="attribute-title">Tactics</div>
798
+ <p>Move perpendicular to Spitters when they telegraph attacks.</p>
799
+ </div>
800
+ </div>
801
+ </div>
802
+
803
+ <div class="enemy-card">
804
+ <div class="enemy-header">
805
+ <div class="enemy-name">Charger</div>
806
+ <div class="enemy-difficulty">High Threat</div>
807
+ </div>
808
+ <div class="enemy-body">
809
+ <div class="enemy-attribute">
810
+ <div class="attribute-title">Behavior</div>
811
+ <p>Bulky enemy that periodically rushes the player in straight lines.</p>
812
+ </div>
813
+ <div class="enemy-attribute">
814
+ <div class="attribute-title">Attacks</div>
815
+ <p>Devastating charge attack that knocks back and stuns the player.</p>
816
+ </div>
817
+ <div class="enemy-attribute">
818
+ <div class="attribute-title">Tactics</div>
819
+ <p>Dodge sideways at the last moment before impact.</p>
820
+ </div>
821
+ </div>
822
+ </div>
823
+
824
+ <div class="enemy-card">
825
+ <div class="enemy-header">
826
+ <div class="enemy-name">Broodmother</div>
827
+ <div class="enemy-difficulty">Boss</div>
828
+ </div>
829
+ <div class="enemy-body">
830
+ <div class="enemy-attribute">
831
+ <div class="attribute-title">Behavior</div>
832
+ <p>Spawns additional enemies and requires repositioning during combat.</p>
833
+ </div>
834
+ <div class="enemy-attribute">
835
+ <div class="attribute-title">Attacks</div>
836
+ <p>Multiple stage battle with area-denial acid pools and energy projectiles.</p>
837
+ </div>
838
+ <div class="enemy-attribute">
839
+ <div class="attribute-title">Tactics</div>
840
+ <p>Focus on clearing minions between damage phases on the boss.</p>
841
+ </div>
842
+ </div>
843
+ </div>
844
+ </div>
845
+ </section>
846
+
847
+ <section id="weapons">
848
+ <h2>Weapons & Abilities</h2>
849
+ <p>The game features a wide arsenal of weapons and abilities, each with multiple upgrade paths that allow for diverse builds and playstyles.</p>
850
+
851
+ <h3>Weapon Types</h3>
852
+ <div class="weapons-list">
853
+ <div class="weapon-card">
854
+ <div class="weapon-name">Pulse Pistol</div>
855
+ <p>Standard semi-auto sidearm with good accuracy and moderate damage.</p>
856
+ <div class="weapon-stats">
857
+ <span class="weapon-stat">Damage: Medium</span>
858
+ <span class="weapon-stat">ROF: Medium</span>
859
+ <span class="weapon-stat">Range: Medium</span>
860
+ </div>
861
+ </div>
862
+
863
+ <div class="weapon-card">
864
+ <div class="weapon-name">Plasma Cutter</div>
865
+ <p>Short-range weapon that emits a continuous beam of superheated plasma.</p>
866
+ <div class="weapon-stats">
867
+ <span class="weapon-stat">Damage: High</span>
868
+ <span class="weapon-stat">ROF: Continuous</span>
869
+ <span class="weapon-stat">Range: Short</span>
870
+ </div>
871
+ </div>
872
+
873
+ <div class="weapon-card">
874
+ <div class="weapon-name">Gauss Rifle</div>
875
+ <p>High-velocity railgun with armor-piercing rounds and slight recoil.</p>
876
+ <div class="weapon-stats">
877
+ <span class="weapon-stat">Damage: Very High</span>
878
+ <span class="weapon-stat">ROF: Slow</span>
879
+ <span class="weapon-stat">Range: Long</span>
880
+ </div>
881
+ </div>
882
+
883
+ <div class="weapon-card">
884
+ <div class="weapon-name">Nova Launcher</div>
885
+ <p>Explosive weapon that fires energy orbs with splash damage.</p>
886
+ <div class="weapon-stats">
887
+ <span class="weapon-stat">Damage: High AOE</span>
888
+ <span class="weapon-stat">ROF: Very Slow</span>
889
+ <span class="weapon-stat">Range: Medium</span>
890
+ </div>
891
+ </div>
892
+
893
+ <div class="weapon-card">
894
+ <div class="weapon-name">Shock Baton</div>
895
+ <p>Melee weapon with chain lightning and stagger effects.</p>
896
+ <div class="weapon-stats">
897
+ <span class="weapon-stat">Damage: Medium</span>
898
+ <span class="weapon-stat">ROF: Fast</span>
899
+ <span class="weapon-stat">Range: Melee</span>
900
+ </div>
901
+ </div>
902
+ </div>
903
 
904
+ <h3>Ability Examples</h3>
905
+ <div class="weapons-list">
906
+ <div class="weapon-card">
907
+ <div class="weapon-name">Blink</div>
908
+ <p>Teleport short distances to reposition or bypass obstacles.</p>
909
+ <div class="weapon-stats">
910
+ <span class="weapon-stat">Cooldown: 6s</span>
911
+ </div>
912
+ </div>
913
+
914
+ <div class="weapon-card">
915
+ <div class="weapon-name">Power Shield</div>
916
+ <p>Creates a temporary barrier that blocks incoming projectiles.</p>
917
+ <div class="weapon-stats">
918
+ <span class="weapon-stat">Duration: 4s</span>
919
+ <span class="weapon-stat">Cooldown: 12s</span>
920
+ </div>
921
+ </div>
922
+
923
+ <div class="weapon-card">
924
+ <div class="weapon-name">Gravity Well</div>
925
+ <p>Creates a zone that slows and damages enemies.</p>
926
+ <div class="weapon-stats">
927
+ <span class="weapon-stat">Duration: 5s</span>
928
+ <span class="weapon-stat">Cooldown: 18s</span>
929
+ </div>
930
+ </div>
931
+ </div>
932
+ </section>
933
+
934
+ <section id="art-style">
935
+ <h2>Art Style Direction</h2>
936
+ <p>The game's visual style aims to create a compelling sci-fi atmosphere with clear readability during frantic combat situations.</p>
937
+
938
+ <div class="art-style-cards">
939
+ <div class="art-style-card">
940
+ <h3 class="art-style-title"><i class="fas fa-moon"></i>Dark Sci-Fi Realism</h3>
941
+ <p>A gritty, high-detail aesthetic emphasizing realistic lighting and materials with a dark color palette. Perfect for creating tension and horror elements.</p>
942
+ <div class="art-style-pros">
943
+ <div class="pro-item">Immersion and tension</div>
944
+ <div class="pro-item">Natural dynamic lighting effects</div>
945
+ <div class="pro-item">Strong environmental storytelling</div>
946
+ </div>
947
+ </div>
948
+
949
+ <div class="art-style-card">
950
+ <h3 class="art-style-title"><i class="fas fa-atom"></i>Neon Cyberpunk</h3>
951
+ <p>Vibrant neon colors against dark backgrounds with synthetic lighting effects. Cyberpunk influences make the alien structures feel artificial and high-tech.</p>
952
+ <div class="art-style-pros">
953
+ <div class="pro-item">Vibrant visual feedback</div>
954
+ <div class="pro-item">Clear enemy/player contrast</div>
955
+ <div class="pro-item">Modern, stylish appeal</div>
956
+ </div>
957
+ </div>
958
+
959
+ <div class="art-style-card">
960
+ <h3 class="art-style-title"><i class="fas fa-paint-brush"></i>Stylized Sci-Fi</h3>
961
+ <p>A more artistic approach with exaggerated shapes and simplified details. Uses bold colors and clear silhouettes to ensure excellent gameplay readability.</p>
962
+ <div class="art-style-pros">
963
+ <div class="pro-item">Excellent gameplay clarity</div>
964
+ <div class="pro-item">More forgiving asset production</div>
965
+ <div class="pro-item">Timeless artistic appeal</div>
966
+ </div>
967
+ </div>
968
+ </div>
969
 
970
+ <h3>Sound Design</h3>
971
+ <p>The audio experience will complement the visual style with:</p>
972
+ <ul>
973
+ <li><strong>Distinct Weapon Sounds:</strong> Each weapon type has unique auditory feedback that matches its visual effects</li>
974
+ <li><strong>Alien Vocalizations:</strong> Enemy sounds telegraph their actions and increase tension</li>
975
+ <li><strong>Dynamic Music:</strong> Adaptive soundtrack that shifts intensity with combat encounters</li>
976
+ <li><strong>Environmental Audio:</strong> Ambient sounds that sell the sci-fi setting and alert players to dangers</li>
977
+ </ul>
978
+
979
+ <h3>Dynamic Lighting</h3>
980
+ <p>The game will use lighting strategically to:</p>
981
+ <ul>
982
+ <li>Guide player attention toward important elements</li>
983
+ <li>Create atmosphere and mood</li>
984
+ <li>Enhance gameplay clarity (e.g. enemy attack wind-ups)</li>
985
+ <li>Support dramatic moments (pulsing emergency lights, etc.)</li>
986
+ </ul>
987
+ </section>
988
+ </main>
989
+
990
+ <footer style="background: var(--darker); padding: 3rem 0; text-align: center; margin-top: 3rem;">
991
+ <div class="container">
992
+ <h2>NEBULA STORM</h2>
993
+ <p>© 2024 Cosmic Forge Games</p>
994
+ <div style="margin-top: 2rem; display: flex; justify-content: center; gap: 2rem;">
995
+ <a href="#" style="color: var(--light); font-size: 1.5rem;"><i class="fab fa-twitter"></i></a>
996
+ <a href="#" style="color: var(--light); font-size: 1.5rem;"><i class="fab fa-discord"></i></a>
997
+ <a href="#" style="color: var(--light); font-size: 1.5rem;"><i class="fab fa-steam"></i></a>
998
+ </div>
999
+ </div>
1000
+ </footer>
1001
+
1002
+ <script>
1003
+ // Tab functionality
1004
+ function openTab(evt, tabName) {
1005
+ // Get all tab content and hide them
1006
+ var tabcontent = document.getElementsByClassName("tab-content");
1007
+ for (var i = 0; i < tabcontent.length; i++) {
1008
+ tabcontent[i].classList.remove("active");
1009
+ }
1010
 
1011
+ // Get all tab buttons and remove 'active' class
1012
+ var tabbuttons = document.getElementsByClassName("tab-button");
1013
+ for (var i = 0; i < tabbuttons.length; i++) {
1014
+ tabbuttons[i].classList.remove("active");
1015
+ }
1016
 
1017
+ // Show the current tab and add 'active' class to the button
1018
+ document.getElementById(tabName).classList.add("active");
1019
+ evt.currentTarget.classList.add("active");
1020
  }
1021
 
1022
+ // Section navigation highlighting
1023
+ window.addEventListener('scroll', function() {
1024
+ var sections = document.querySelectorAll('section');
1025
+ var navLinks = document.querySelectorAll('.section-nav a');
1026
+ var currentSection = '';
1027
+
1028
+ sections.forEach(function(section) {
1029
+ var sectionTop = section.offsetTop;
1030
+ var sectionHeight = section.clientHeight;
1031
+ if (window.pageYOffset >= (sectionTop - 200)) {
1032
+ currentSection = section.getAttribute('id');
1033
+ }
 
 
 
 
1034
  });
1035
 
1036
+ navLinks.forEach(function(link) {
1037
+ link.classList.remove('active');
1038
+ if (link.getAttribute('href') === '#' + currentSection) {
1039
+ link.classList.add('active');
 
 
 
 
 
 
 
 
 
 
1040
  }
1041
  });
 
 
 
 
 
 
 
1042
 
1043
+ // Show/hide nav based on scroll position
1044
+ var sectionNav = document.querySelector('.section-nav');
1045
+ if (window.pageYOffset > 600) {
1046
+ sectionNav.style.display = 'block';
1047
+ } else {
1048
+ sectionNav.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
1049
  }
1050
+ });
1051
+
1052
+ // Smooth scrolling for navigation
1053
+ document.querySelectorAll('.section-nav a').forEach(anchor => {
1054
+ anchor.addEventListener('click', function(e) {
1055
+ e.preventDefault();
1056
+
1057
+ const targetId = this.getAttribute('href');
1058
+ if (targetId === '#') return;
1059
+
1060
+ const targetElement = document.querySelector(targetId);
1061
+ if (targetElement) {
1062
+ window.scrollTo({
1063
+ top: targetElement.offsetTop - 80,
1064
+ behavior: 'smooth'
1065
+ });
 
 
1066
  }
1067
  });
1068
+ });
1069
+
1070
+ // Animate progress bars on scroll
1071
+ function animateProgressBars() {
1072
+ const progressBars = document.querySelectorAll('.progress-fill');
1073
+ progressBars.forEach(bar => {
1074
+ const width = bar.style.width;
1075
+ bar.style.width = '0';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1076
 
1077
+ setTimeout(() => {
1078
+ bar.style.width = width;
1079
+ }, 100);
1080
+ });
 
1081
  }
1082
 
1083
+ // Intersection Observer for progress bars
1084
+ const observer = new IntersectionObserver((entries) => {
1085
+ entries.forEach(entry => {
1086
+ if (entry.isIntersecting) {
1087
+ animateProgressBars();
1088
+ observer.unobserve(entry.target);
 
 
 
 
 
 
 
1089
  }
1090
+ });
1091
+ }, {threshold: 0.5});
 
 
 
 
 
 
 
 
 
 
 
 
 
1092
 
1093
+ document.querySelectorAll('.progress-bar').forEach(bar => {
1094
+ observer.observe(bar);
1095
+ });
 
 
 
 
1096
 
1097
+ // Initialize - make sure first tab is active
1098
+ document.addEventListener('DOMContentLoaded', function() {
1099
+ animateProgressBars();
1100
+ });
1101
+ </script>
1102
+ </body>
1103
  </html>