stroumphs commited on
Commit
3ade157
·
verified ·
1 Parent(s): f577cce

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +884 -968
index.html CHANGED
@@ -3,1101 +3,1017 @@
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>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Roguelike Alien Shooter</title>
 
7
  <style>
8
+ body {
 
 
 
 
 
 
 
 
 
9
  margin: 0;
10
  padding: 0;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ height: 100vh;
15
+ background-color: #111;
16
+ color: #0f0;
17
+ font-family: 'Courier New', monospace;
18
+ overflow: hidden;
 
 
 
 
 
 
 
19
  }
20
 
21
+ #game-container {
 
 
 
 
22
  position: relative;
23
+ width: 800px;
24
+ height: 600px;
25
+ border: 2px solid #0f0;
26
  overflow: hidden;
27
+ box-shadow: 0 0 20px #0f0;
28
+ background-color: #000;
29
+ }
30
+
31
+ #game-canvas {
32
+ background-color: #000;
33
+ display: block;
34
  }
35
 
36
+ #ui-overlay {
 
37
  position: absolute;
38
  top: 0;
39
  left: 0;
40
  width: 100%;
41
  height: 100%;
42
+ display: flex;
43
+ flex-direction: column;
44
+ justify-content: space-between;
45
  pointer-events: none;
46
  }
47
 
48
+ #top-bar {
49
+ padding: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  display: flex;
51
+ justify-content: space-between;
52
+ background-color: rgba(0, 0, 0, 0.7);
53
+ border-bottom: 1px solid #0f0;
 
 
 
 
 
 
 
 
 
54
  }
55
 
56
+ #bottom-bar {
57
+ padding: 10px;
58
+ background-color: rgba(0, 0, 0, 0.7);
59
+ border-top: 1px solid #0f0;
 
 
60
  display: flex;
61
+ justify-content: space-between;
 
 
 
 
62
  }
63
 
64
+ #game-over-screen, #level-up-screen {
 
65
  position: absolute;
66
  top: 0;
67
  left: 0;
68
  width: 100%;
69
  height: 100%;
70
+ background-color: rgba(0, 0, 0, 0.9);
71
+ display: flex;
72
+ flex-direction: column;
73
+ justify-content: center;
74
+ align-items: center;
75
+ display: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
 
78
+ #game-over-screen h1 {
79
+ color: #f00;
80
+ font-size: 48px;
81
+ text-shadow: 0 0 10px #f00;
82
+ margin-bottom: 30px;
83
  }
84
 
85
+ #level-up-screen h1 {
86
+ color: #0f0;
87
+ font-size: 36px;
88
+ margin-bottom: 20px;
 
89
  }
90
 
91
+ .upgrade-option {
92
+ display: flex;
93
+ flex-direction: column;
94
+ align-items: center;
95
+ margin: 10px;
96
+ padding: 10px;
97
+ border: 1px solid #0f0;
98
+ cursor: pointer;
99
+ pointer-events: all;
100
+ transition: all 0.2s;
101
+ background-color: rgba(0, 20, 0, 0.5);
102
  }
103
 
104
+ .upgrade-option:hover {
105
+ background-color: rgba(0, 50, 0, 0.7);
106
+ transform: scale(1.05);
 
107
  }
108
 
109
+ .upgrade-options {
110
  display: flex;
111
+ flex-wrap: wrap;
112
+ justify-content: center;
113
+ max-width: 600px;
114
  }
115
 
116
+ button {
117
+ background-color: #111;
118
+ color: #0f0;
119
+ border: 1px solid #0f0;
120
+ padding: 10px 20px;
121
+ margin: 10px;
122
  cursor: pointer;
123
+ font-family: 'Courier New', monospace;
124
+ font-size: 16px;
125
+ transition: all 0.2s;
126
+ pointer-events: all;
127
  }
128
 
129
+ button:hover {
130
+ background-color: #0f0;
131
+ color: #000;
132
+ box-shadow: 0 0 10px #0f0;
133
  }
134
 
135
+ #start-screen {
 
136
  position: absolute;
137
+ top: 0;
138
  left: 0;
139
  width: 100%;
140
+ height: 100%;
141
+ background-color: rgba(0, 0, 0, 0.9);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  display: flex;
143
+ flex-direction: column;
144
+ justify-content: center;
145
  align-items: center;
146
  }
147
 
148
+ #start-screen h1 {
149
+ font-size: 64px;
150
+ color: #0f0;
151
+ text-shadow: 0 0 10px #0f0;
152
+ margin-bottom: 40px;
153
  }
154
 
155
+ #instructions {
156
+ margin-top: 30px;
157
+ max-width: 600px;
158
+ text-align: center;
159
+ line-height: 1.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  }
161
 
162
+ #player-stats, #game-stats {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 5px;
 
 
166
  }
167
 
168
+ .stat {
169
  display: flex;
170
  justify-content: space-between;
171
+ gap: 10px;
172
  }
173
 
174
+ .stat-name {
175
+ color: #0f0;
176
  }
177
 
178
+ .stat-value {
179
+ color: #fff;
 
 
 
 
 
 
 
 
 
 
180
  }
181
+ </style>
182
+ </head>
183
+ <body>
184
+ <div id="game-container">
185
+ <canvas id="game-canvas" width="800" height="600"></canvas>
186
+
187
+ <div id="ui-overlay">
188
+ <div id="top-bar">
189
+ <div id="player-stats">
190
+ <div class="stat">
191
+ <span class="stat-name">HEALTH:</span>
192
+ <span class="stat-value" id="health-value">100/100</span>
193
+ </div>
194
+ <div class="stat">
195
+ <span class="stat-name">DAMAGE:</span>
196
+ <span class="stat-value" id="damage-value">10</span>
197
+ </div>
198
+ <div class="stat">
199
+ <span class="stat-name">FIRE RATE:</span>
200
+ <span class="stat-value" id="fire-rate-value">0.5s</span>
201
+ </div>
202
+ </div>
203
+ <div id="game-stats">
204
+ <div class="stat">
205
+ <span class="stat-name">LEVEL:</span>
206
+ <span class="stat-value" id="level-value">1</span>
207
+ </div>
208
+ <div class="stat">
209
+ <span class="stat-name">KILLS:</span>
210
+ <span class="stat-value" id="kills-value">0</span>
211
+ </div>
212
+ <div class="stat">
213
+ <span class="stat-name">XP:</span>
214
+ <span class="stat-value" id="xp-value">0/100</span>
215
+ </div>
216
+ </div>
217
+ </div>
218
+ <div id="bottom-bar">
219
+ <div id="weapon-info">Weapon: Pulse Rifle</div>
220
+ <div id="enemies-remaining">Aliens: 0</div>
221
+ </div>
222
+ </div>
223
 
224
+ <div id="game-over-screen">
225
+ <h1>MISSION FAILED</h1>
226
+ <p id="final-stats">You reached level 1 and killed 0 aliens</p>
227
+ <button id="retry-btn">RETRY</button>
228
+ </div>
229
 
230
+ <div id="level-up-screen">
231
+ <h1>UPGRADE SELECTED</h1>
232
+ <p>Choose one of the following upgrades</p>
233
+ <div class="upgrade-options" id="upgrade-options">
234
+ <!-- Will be populated by JavaScript -->
235
+ </div>
236
+ </div>
237
 
238
+ <div id="start-screen">
239
+ <h1>ROGUELIKE ALIEN SHOOTER</h1>
240
+ <button id="start-btn">START GAME</button>
241
+ <div id="instructions">
242
+ <p>WASD to move, Mouse to aim and shoot</p>
243
+ <p>Survive as long as possible in procedurally generated alien-infested levels</p>
244
+ <p>Collect random upgrades when you level up</p>
245
+ <p>Permadeath - when you die, you lose everything!</p>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <script>
251
+ // Game constants
252
+ const TILE_SIZE = 40;
253
+ const MAP_WIDTH = 40;
254
+ const MAP_HEIGHT = 30;
255
+ const PLAYER_SPEED = 3;
256
+ const BASE_FIRE_RATE = 500; // ms
257
+
258
+ // Game state
259
+ const gameState = {
260
+ player: null,
261
+ enemies: [],
262
+ bullets: [],
263
+ walls: [],
264
+ level: 1,
265
+ kills: 0,
266
+ xp: 0,
267
+ xpToNextLevel: 100,
268
+ gameOver: false,
269
+ gameStarted: false,
270
+ lastFireTime: 0,
271
+ playerStats: {
272
+ health: 100,
273
+ maxHealth: 100,
274
+ damage: 10,
275
+ fireRate: BASE_FIRE_RATE,
276
+ movementSpeed: PLAYER_SPEED,
277
+ bulletSpeed: 8,
278
+ lifeSteal: 0
279
+ }
280
+ };
281
+
282
+ // DOM elements
283
+ const canvas = document.getElementById('game-canvas');
284
+ const ctx = canvas.getContext('2d');
285
+ const startScreen = document.getElementById('start-screen');
286
+ const gameOverScreen = document.getElementById('game-over-screen');
287
+ const levelUpScreen = document.getElementById('level-up-screen');
288
+ const startBtn = document.getElementById('start-btn');
289
+ const retryBtn = document.getElementById('retry-btn');
290
+ const healthValue = document.getElementById('health-value');
291
+ const damageValue = document.getElementById('damage-value');
292
+ const fireRateValue = document.getElementById('fire-rate-value');
293
+ const levelValue = document.getElementById('level-value');
294
+ const killsValue = document.getElementById('kills-value');
295
+ const xpValue = document.getElementById('xp-value');
296
+ const finalStats = document.getElementById('final-stats');
297
+ const enemiesRemaining = document.getElementById('enemies-remaining');
298
+ const upgradeOptions = document.getElementById('upgrade-options');
299
+
300
+ // Initialize game
301
+ function init() {
302
+ startBtn.addEventListener('click', startGame);
303
+ retryBtn.addEventListener('click', startGame);
304
+ window.addEventListener('keydown', handleKeyDown);
305
+ window.addEventListener('keyup', handleKeyUp);
306
+ canvas.addEventListener('mousemove', handleMouseMove);
307
+ canvas.addEventListener('click', handleMouseClick);
308
+
309
+ // Start the game loop
310
+ gameLoop();
311
+ }
312
+
313
+ // Start a new game
314
+ function startGame() {
315
+ // Reset game state
316
+ gameState.player = {
317
+ x: 400,
318
+ y: 300,
319
+ width: 24,
320
+ height: 24,
321
+ angle: 0,
322
+ keys: {
323
+ up: false,
324
+ down: false,
325
+ left: false,
326
+ right: false
327
+ }
328
+ };
329
+
330
+ gameState.enemies = [];
331
+ gameState.bullets = [];
332
+ gameState.walls = [];
333
+ gameState.level = 1;
334
+ gameState.kills = 0;
335
+ gameState.xp = 0;
336
+ gameState.xpToNextLevel = 100;
337
+ gameState.gameOver = false;
338
+ gameState.gameStarted = true;
339
+ gameState.lastFireTime = 0;
340
+
341
+ // Reset player stats (with some base upgrades as you progress)
342
+ gameState.playerStats = {
343
+ health: 100,
344
+ maxHealth: 100,
345
+ damage: 10,
346
+ fireRate: BASE_FIRE_RATE,
347
+ movementSpeed: PLAYER_SPEED,
348
+ bulletSpeed: 8,
349
+ lifeSteal: 0
350
+ };
351
+
352
+ // Generate first level
353
+ generateLevel();
354
+
355
+ // Hide screens
356
+ startScreen.style.display = 'none';
357
+ gameOverScreen.style.display = 'none';
358
+ levelUpScreen.style.display = 'none';
359
+
360
+ // Update UI
361
+ updateUI();
362
  }
363
 
364
+ // Generate a procedurally generated level
365
+ function generateLevel() {
366
+ // Clear existing walls
367
+ gameState.walls = [];
368
+
369
+ // Generate a random level layout
370
+ // Start with a simple room-based system
371
+ const roomWidth = Math.floor(Math.random() * 8) + 6;
372
+ const roomHeight = Math.floor(Math.random() * 8) + 6;
373
+ const roomX = Math.floor(Math.random() * (MAP_WIDTH - roomWidth - 4)) + 2;
374
+ const roomY = Math.floor(Math.random() * (MAP_HEIGHT - roomHeight - 4)) + 2;
375
+
376
+ // Create outer walls
377
+ for (let x = 0; x < MAP_WIDTH; x++) {
378
+ for (let y = 0; y < MAP_HEIGHT; y++) {
379
+ // Outer walls
380
+ if (x === 0 || y === 0 || x === MAP_WIDTH - 1 || y === MAP_HEIGHT - 1) {
381
+ gameState.walls.push({
382
+ x: x * TILE_SIZE,
383
+ y: y * TILE_SIZE,
384
+ width: TILE_SIZE,
385
+ height: TILE_SIZE
386
+ });
387
+ }
388
+
389
+ // Random obstacles
390
+ if (Math.random() < 0.05 &&
391
+ x > roomX && x < roomX + roomWidth &&
392
+ y > roomY && y < roomY + roomHeight) {
393
+ gameState.walls.push({
394
+ x: x * TILE_SIZE,
395
+ y: y * TILE_SIZE,
396
+ width: TILE_SIZE,
397
+ height: TILE_SIZE
398
+ });
399
+ }
400
+ }
401
+ }
402
+
403
+ // Place the player in the center of the room
404
+ const centerX = (roomX + roomWidth / 2) * TILE_SIZE;
405
+ const centerY = (roomY + roomHeight / 2) * TILE_SIZE;
406
+ gameState.player.x = centerX;
407
+ gameState.player.y = centerY;
408
+
409
+ // Spawn enemies based on level
410
+ const enemyCount = Math.floor(Math.random() * 3) + 2 + gameState.level;
411
+ for (let i = 0; i < enemyCount; i++) {
412
+ spawnEnemy();
413
+ }
414
+
415
+ enemiesRemaining.textContent = `Aliens: ${gameState.enemies.length}`;
416
  }
417
 
418
+ // Spawn an enemy in a random position
419
+ function spawnEnemy() {
420
+ // Try random positions until we find a valid one
421
+ let validPosition = false;
422
+ let x, y;
423
 
424
+ while (!validPosition) {
425
+ x = Math.floor(Math.random() * (MAP_WIDTH - 4)) * TILE_SIZE + TILE_SIZE * 2;
426
+ y = Math.floor(Math.random() * (MAP_HEIGHT - 4)) * TILE_SIZE + TILE_SIZE * 2;
427
+
428
+ // Check if position is too close to player
429
+ const distToPlayer = Math.sqrt(Math.pow(x - gameState.player.x, 2) + Math.pow(y - gameState.player.y, 2));
430
+ if (distToPlayer > 200) {
431
+ validPosition = true;
432
+ }
433
  }
434
 
435
+ // Random enemy type based on level
436
+ let enemyType;
437
+ const rand = Math.random();
438
+
439
+ if (gameState.level < 3 || rand < 0.7) {
440
+ enemyType = 'normal'; // 70% chance or always at low levels
441
+ } else if (rand < 0.9) {
442
+ enemyType = 'fast'; // 20% chance
443
+ } else {
444
+ enemyType = 'tank'; // 10% chance
445
  }
446
 
447
+ // Create enemy settings
448
+ let settings;
449
+ switch (enemyType) {
450
+ case 'fast':
451
+ settings = {
452
+ speed: 2.5,
453
+ health: 30,
454
+ damage: 8,
455
+ color: '#00f',
456
+ value: 20
457
+ };
458
+ break;
459
+ case 'tank':
460
+ settings = {
461
+ speed: 0.8,
462
+ health: 150,
463
+ damage: 15,
464
+ color: '#f00',
465
+ value: 50
466
+ };
467
+ break;
468
+ default: // normal
469
+ settings = {
470
+ speed: 1.5,
471
+ health: 60,
472
+ damage: 10,
473
+ color: '#0f0',
474
+ value: 15
475
+ };
476
  }
477
+
478
+ gameState.enemies.push({
479
+ x: x,
480
+ y: y,
481
+ width: 24,
482
+ height: 24,
483
+ health: settings.health,
484
+ maxHealth: settings.health,
485
+ speed: settings.speed,
486
+ damage: settings.damage,
487
+ color: settings.color,
488
+ type: enemyType,
489
+ value: settings.value,
490
+ lastAttackTime: 0,
491
+ attackCooldown: 1000
492
+ });
493
  }
494
 
495
+ // Main game loop
496
+ function gameLoop() {
497
+ if (!gameState.gameStarted) {
498
+ requestAnimationFrame(gameLoop);
499
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  }
501
+
502
+ if (gameState.gameOver) {
503
+ render();
504
+ requestAnimationFrame(gameLoop);
505
+ return;
506
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
 
508
+ // Update game state
509
+ update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
+ // Render the game
512
+ render();
513
+
514
+ // Continue the loop
515
+ requestAnimationFrame(gameLoop);
516
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
+ // Update game state
519
+ function update() {
520
+ // Update player position
521
+ updatePlayer();
522
 
523
+ // Update enemies
524
+ updateEnemies();
 
 
 
 
525
 
526
+ // Update bullets
527
+ updateBullets();
 
 
 
 
 
 
 
 
528
 
529
+ // Check collisions
530
+ checkCollisions();
 
 
 
 
 
 
 
 
531
 
532
+ // Check if level is complete
533
+ if (gameState.enemies.length === 0) {
534
+ gameState.level++;
535
+ generateLevel();
536
+ }
 
 
 
 
 
537
 
538
+ // Update UI
539
+ updateUI();
540
+ }
 
 
 
 
 
 
 
 
541
 
542
+ // Update player position based on input
543
+ function updatePlayer() {
544
+ const player = gameState.player;
545
+ const speed = gameState.playerStats.movementSpeed;
546
 
547
+ // Calculate movement vector
548
+ let dx = 0;
549
+ let dy = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
+ if (player.keys.up) dy -= speed;
552
+ if (player.keys.down) dy += speed;
553
+ if (player.keys.left) dx -= speed;
554
+ if (player.keys.right) dx += speed;
 
 
 
 
555
 
556
+ // Normalize diagonal movement
557
+ if (dx !== 0 && dy !== 0) {
558
+ const invDiag = 1 / Math.sqrt(2);
559
+ dx *= invDiag;
560
+ dy *= invDiag;
561
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
 
563
+ // Calculate new position
564
+ let newX = player.x + dx;
565
+ let newY = player.y + dy;
566
+
567
+ // Check wall collisions
568
+ const playerRect = {
569
+ x: newX - player.width/2,
570
+ y: newY - player.height/2,
571
+ width: player.width,
572
+ height: player.height
573
+ };
574
+
575
+ let canMove = true;
576
+ for (const wall of gameState.walls) {
577
+ if (checkCollision(playerRect, wall)) {
578
+ canMove = false;
579
+ break;
580
+ }
581
+ }
582
+
583
+ // Only move if there's no collision
584
+ if (canMove) {
585
+ player.x = newX;
586
+ player.y = newY;
587
+ }
588
+
589
+ // Keep player in bounds
590
+ player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
591
+ player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
592
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
+ // Update all enemies
595
+ function updateEnemies() {
596
+ const now = Date.now();
597
+ const player = gameState.player;
598
 
599
+ for (let i = 0; i < gameState.enemies.length; i++) {
600
+ const enemy = gameState.enemies[i];
 
 
 
 
 
 
 
 
 
601
 
602
+ // Calculate direction to player
603
+ const dx = player.x - enemy.x;
604
+ const dy = player.y - enemy.y;
605
+ const dist = Math.sqrt(dx*dx + dy*dy);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
 
607
+ // Normalize direction and move
608
+ if (dist > 0) {
609
+ enemy.x += (dx / dist) * enemy.speed;
610
+ enemy.y += (dy / dist) * enemy.speed;
611
+ }
 
 
 
 
612
 
613
+ // Attack player if close enough
614
+ if (dist < 30 && now - enemy.lastAttackTime > enemy.attackCooldown) {
615
+ enemy.lastAttackTime = now;
616
+ gameState.playerStats.health -= enemy.damage;
617
+
618
+ // Check if player died
619
+ if (gameState.playerStats.health <= 0) {
620
+ gameState.playerStats.health = 0;
621
+ gameState.gameOver = true;
622
+ showGameOver();
623
+ break;
624
+ }
625
+ }
626
+ }
627
 
628
+ // Remove dead enemies and add XP
629
+ gameState.enemies = gameState.enemies.filter(enemy => {
630
+ if (enemy.health <= 0) {
631
+ gameState.kills++;
632
+ gameState.xp += enemy.value;
633
+
634
+ // Check for level up
635
+ if (gameState.xp >= gameState.xpToNextLevel) {
636
+ gameState.xp -= gameState.xpToNextLevel;
637
+ gameState.xpToNextLevel = Math.floor(gameState.xpToNextLevel * 1.2);
638
+ gameState.level++;
639
+ showLevelUp();
640
+ }
641
+
642
+ return false;
643
+ }
644
+ return true;
645
+ });
646
+
647
+ enemiesRemaining.textContent = `Aliens: ${gameState.enemies.length}`;
648
+ }
649
+
650
+ // Update all bullets
651
+ function updateBullets() {
652
+ // Move bullets
653
+ for (let i = 0; i < gameState.bullets.length; i++) {
654
+ const bullet = gameState.bullets[i];
655
+ bullet.x += bullet.dx;
656
+ bullet.y += bullet.dy;
657
 
658
+ // Remove bullets that go off screen
659
+ if (bullet.x < 0 || bullet.x > canvas.width ||
660
+ bullet.y < 0 || bullet.y > canvas.height) {
661
+ gameState.bullets.splice(i, 1);
662
+ i--;
663
+ continue;
664
+ }
 
665
 
666
+ // Check wall collisions
667
+ for (const wall of gameState.walls) {
668
+ const bulletRect = {
669
+ x: bullet.x - bullet.size/2,
670
+ y: bullet.y - bullet.size/2,
671
+ width: bullet.size,
672
+ height: bullet.size
673
+ };
674
+
675
+ if (checkCollision(bulletRect, wall)) {
676
+ gameState.bullets.splice(i, 1);
677
+ i--;
678
+ break;
679
+ }
680
+ }
681
+ }
682
+ }
683
+
684
+ // Check all collisions
685
+ function checkCollisions() {
686
+ // Check bullet-enemy collisions
687
+ for (let i = 0; i < gameState.bullets.length; i++) {
688
+ const bullet = gameState.bullets[i];
689
+ const bulletRect = {
690
+ x: bullet.x - bullet.size/2,
691
+ y: bullet.y - bullet.size/2,
692
+ width: bullet.size,
693
+ height: bullet.size
694
+ };
695
+
696
+ for (let j = 0; j < gameState.enemies.length; j++) {
697
+ const enemy = gameState.enemies[j];
698
+ const enemyRect = {
699
+ x: enemy.x - enemy.width/2,
700
+ y: enemy.y - enemy.height/2,
701
+ width: enemy.width,
702
+ height: enemy.height
703
+ };
704
+
705
+ if (checkCollision(bulletRect, enemyRect)) {
706
+ // Damage enemy
707
+ enemy.health -= gameState.playerStats.damage;
708
+
709
+ // Life steal
710
+ if (gameState.playerStats.lifeSteal > 0) {
711
+ const healAmount = gameState.playerStats.damage * (gameState.playerStats.lifeSteal / 100);
712
+ gameState.playerStats.health = Math.min(
713
+ gameState.playerStats.maxHealth,
714
+ gameState.playerStats.health + healAmount
715
+ );
716
+ }
717
+
718
+ // Remove bullet
719
+ gameState.bullets.splice(i, 1);
720
+ i--;
721
+ break;
722
+ }
723
+ }
724
+ }
725
+ }
726
+
727
+ // Check collision between two rectangles
728
+ function checkCollision(rect1, rect2) {
729
+ return rect1.x < rect2.x + rect2.width &&
730
+ rect1.x + rect1.width > rect2.x &&
731
+ rect1.y < rect2.y + rect2.height &&
732
+ rect1.y + rect1.height > rect2.y;
733
+ }
734
 
735
+ // Render the game
736
+ function render() {
737
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
738
 
739
+ // Draw background grid
740
+ ctx.strokeStyle = '#030';
741
+ ctx.lineWidth = 1;
742
+ for (let x = 0; x < canvas.width; x += TILE_SIZE) {
743
+ ctx.beginPath();
744
+ ctx.moveTo(x, 0);
745
+ ctx.lineTo(x, canvas.height);
746
+ ctx.stroke();
747
+ }
748
+ for (let y = 0; y < canvas.height; y += TILE_SIZE) {
749
+ ctx.beginPath();
750
+ ctx.moveTo(0, y);
751
+ ctx.lineTo(canvas.width, y);
752
+ ctx.stroke();
753
+ }
754
+
755
+ // Draw walls
756
+ ctx.fillStyle = '#0f0';
757
+ for (const wall of gameState.walls) {
758
+ ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
759
+ }
760
+
761
+ // Draw player
762
+ if (!gameState.gameOver) {
763
+ ctx.save();
764
+ ctx.translate(gameState.player.x, gameState.player.y);
765
+ ctx.rotate(gameState.player.angle);
766
 
767
+ // Player body
768
+ ctx.fillStyle = '#fff';
769
+ ctx.fillRect(-gameState.player.width/2, -gameState.player.height/2,
770
+ gameState.player.width, gameState.player.height);
 
 
 
 
 
771
 
772
+ // Player gun
773
+ ctx.fillStyle = '#f00';
774
+ ctx.fillRect(gameState.player.width/2 - 4, -4, 10, 8);
775
+
776
+ ctx.restore();
777
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
778
 
779
+ // Draw enemies
780
+ for (const enemy of gameState.enemies) {
781
+ // Enemy body
782
+ ctx.fillStyle = enemy.color;
783
+ ctx.fillRect(
784
+ enemy.x - enemy.width/2,
785
+ enemy.y - enemy.height/2,
786
+ enemy.width,
787
+ enemy.height
788
+ );
789
+
790
+ // Health bar
791
+ const healthPercent = enemy.health / enemy.maxHealth;
792
+ ctx.fillStyle = '#f00';
793
+ ctx.fillRect(
794
+ enemy.x - enemy.width/2,
795
+ enemy.y - enemy.height/2 - 6,
796
+ enemy.width * healthPercent,
797
+ 4
798
+ );
 
 
 
 
 
 
 
 
 
 
799
  }
800
 
801
+ // Draw bullets
802
+ ctx.fillStyle = '#ff0';
803
+ for (const bullet of gameState.bullets) {
804
+ ctx.beginPath();
805
+ ctx.arc(bullet.x, bullet.y, bullet.size/2, 0, Math.PI * 2);
806
+ ctx.fill();
807
  }
808
 
809
+ // Draw player health bar
810
+ if (!gameState.gameOver) {
811
+ const healthPercent = gameState.playerStats.health / gameState.playerStats.maxHealth;
812
+ const barWidth = 200;
813
+ const barHeight = 10;
814
+
815
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
816
+ ctx.fillRect(canvas.width/2 - barWidth/2, 20, barWidth, barHeight);
817
+
818
+ ctx.fillStyle = '#f00';
819
+ ctx.fillRect(canvas.width/2 - barWidth/2, 20, barWidth * healthPercent, barHeight);
820
+
821
+ ctx.strokeStyle = '#0f0';
822
+ ctx.strokeRect(canvas.width/2 - barWidth/2, 20, barWidth, barHeight);
823
+ }
824
+ }
825
+
826
+ // Update UI elements
827
+ function updateUI() {
828
+ healthValue.textContent = `${Math.floor(gameState.playerStats.health)}/${gameState.playerStats.maxHealth}`;
829
+ damageValue.textContent = gameState.playerStats.damage;
830
+ fireRateValue.textContent = `${(gameState.playerStats.fireRate / 1000).toFixed(1)}s`;
831
+ levelValue.textContent = gameState.level;
832
+ killsValue.textContent = gameState.kills;
833
+ xpValue.textContent = `${gameState.xp}/${gameState.xpToNextLevel}`;
834
  }
835
 
836
+ // Show game over screen
837
+ function showGameOver() {
838
+ finalStats.textContent = `You reached level ${gameState.level} and killed ${gameState.kills} aliens`;
839
+ gameOverScreen.style.display = 'flex';
840
+ }
841
+
842
+ // Show level up screen with upgrade options
843
+ function showLevelUp() {
844
+ // Clear previous options
845
+ upgradeOptions.innerHTML = '';
846
 
847
+ // Generate 3 random upgrade options
848
+ const upgrades = [
849
+ {
850
+ title: 'Damage Boost',
851
+ description: 'Increase damage by +5',
852
+ icon: '⚔️',
853
+ apply: () => { gameState.playerStats.damage += 5; }
854
+ },
855
+ {
856
+ title: 'Health Boost',
857
+ description: 'Increase max health by +30',
858
+ icon: '❤️',
859
+ apply: () => {
860
+ gameState.playerStats.maxHealth += 30;
861
+ gameState.playerStats.health += 30;
862
+ }
863
+ },
864
+ {
865
+ title: 'Faster Reload',
866
+ description: 'Increase fire rate by 20%',
867
+ icon: '🚀',
868
+ apply: () => {
869
+ gameState.playerStats.fireRate = Math.max(200, gameState.playerStats.fireRate * 0.8);
870
+ }
871
+ },
872
+ {
873
+ title: 'Life Steal',
874
+ description: 'Heal for 10% of damage dealt',
875
+ icon: '🩸',
876
+ apply: () => {
877
+ gameState.playerStats.lifeSteal += 10;
878
+ }
879
+ },
880
+ {
881
+ title: 'Faster Movement',
882
+ description: 'Increase movement speed by 20%',
883
+ icon: '👟',
884
+ apply: () => {
885
+ gameState.playerStats.movementSpeed *= 1.2;
886
+ }
887
+ },
888
+ {
889
+ title: 'Piercing Shots',
890
+ description: 'Bullets pass through enemies',
891
+ icon: '🔫',
892
+ apply: () => {
893
+ // Add piercing flag to bullets
894
+ for (const bullet of gameState.bullets) {
895
+ bullet.piercing = true;
896
+ }
897
+ // Modify fireBullet to include piercing
898
+ gameState.fireBullet = function(x, y, dx, dy) {
899
+ gameState.bullets.push({
900
+ x: x,
901
+ y: y,
902
+ dx: dx * gameState.playerStats.bulletSpeed,
903
+ dy: dy * gameState.playerStats.bulletSpeed,
904
+ size: 8,
905
+ piercing: true
906
+ });
907
+ };
908
+ }
909
  }
910
+ ];
911
 
912
+ // Shuffle and pick 3
913
+ const shuffled = [...upgrades].sort(() => 0.5 - Math.random());
914
+ const selectedUpgrades = shuffled.slice(0, 3);
 
 
 
915
 
916
+ // Create option elements
917
+ for (const upgrade of selectedUpgrades) {
918
+ const option = document.createElement('div');
919
+ option.className = 'upgrade-option';
920
+ option.innerHTML = `
921
+ <h3>${upgrade.icon} ${upgrade.title}</h3>
922
+ <p>${upgrade.description}</p>
923
+ `;
924
+ option.addEventListener('click', () => {
925
+ upgrade.apply();
926
+ levelUpScreen.style.display = 'none';
927
+ });
928
+ upgradeOptions.appendChild(option);
929
  }
930
+
931
+ levelUpScreen.style.display = 'flex';
932
+ }
933
 
934
+ // Fire a bullet from player
935
+ function fireBullet() {
936
+ const now = Date.now();
937
+ if (now - gameState.lastFireTime < gameState.playerStats.fireRate) {
938
+ return;
939
+ }
940
+
941
+ gameState.lastFireTime = now;
942
+
943
+ const dx = Math.cos(gameState.player.angle);
944
+ const dy = Math.sin(gameState.player.angle);
945
+
946
+ gameState.bullets.push({
947
+ x: gameState.player.x + dx * 20,
948
+ y: gameState.player.y + dy * 20,
949
+ dx: dx * gameState.playerStats.bulletSpeed,
950
+ dy: dy * gameState.playerStats.bulletSpeed,
951
+ size: 8
952
  });
953
+ }
954
 
955
+ // Handle keyboard input
956
+ function handleKeyDown(e) {
957
+ const player = gameState.player;
958
+ if (!player) return;
959
+
960
+ switch (e.key.toLowerCase()) {
961
+ case 'w':
962
+ player.keys.up = true;
963
+ break;
964
+ case 's':
965
+ player.keys.down = true;
966
+ break;
967
+ case 'a':
968
+ player.keys.left = true;
969
+ break;
970
+ case 'd':
971
+ player.keys.right = true;
972
+ break;
973
+ }
974
  }
975
 
976
+ function handleKeyUp(e) {
977
+ const player = gameState.player;
978
+ if (!player) return;
979
+
980
+ switch (e.key.toLowerCase()) {
981
+ case 'w':
982
+ player.keys.up = false;
983
+ break;
984
+ case 's':
985
+ player.keys.down = false;
986
+ break;
987
+ case 'a':
988
+ player.keys.left = false;
989
+ break;
990
+ case 'd':
991
+ player.keys.right = false;
992
+ break;
993
+ }
994
+ }
995
 
996
+ // Handle mouse input
997
+ function handleMouseMove(e) {
998
+ if (!gameState.player || gameState.gameOver) return;
999
+
1000
+ const rect = canvas.getBoundingClientRect();
1001
+ const mouseX = e.clientX - rect.left;
1002
+ const mouseY = e.clientY - rect.top;
1003
+
1004
+ // Calculate angle from player to mouse
1005
+ const dx = mouseX - gameState.player.x;
1006
+ const dy = mouseY - gameState.player.y;
1007
+ gameState.player.angle = Math.atan2(dy, dx);
1008
+ }
1009
+
1010
+ function handleMouseClick(e) {
1011
+ if (gameState.gameOver || !gameState.gameStarted) return;
1012
+ fireBullet();
1013
+ }
1014
 
1015
+ // Start the game
1016
+ init();
 
 
1017
  </script>
1018
  </body>
1019
  </html>