Spaces:
Running
Running
Add 1 files
Browse files- 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>
|
| 7 |
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 8 |
<style>
|
| 9 |
-
|
| 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 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
overflow-x: hidden;
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
.container {
|
| 33 |
-
max-width: 1200px;
|
| 34 |
-
margin: 0 auto;
|
| 35 |
-
padding: 0 20px;
|
| 36 |
}
|
| 37 |
|
| 38 |
-
|
| 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 |
-
|
| 48 |
-
content: "";
|
| 49 |
position: absolute;
|
| 50 |
top: 0;
|
| 51 |
left: 0;
|
| 52 |
width: 100%;
|
| 53 |
height: 100%;
|
| 54 |
-
|
|
|
|
|
|
|
| 55 |
pointer-events: none;
|
| 56 |
}
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
| 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:
|
| 82 |
-
|
| 83 |
-
|
| 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 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
border-radius: 10px;
|
| 100 |
-
margin: 2rem 0;
|
| 101 |
display: flex;
|
| 102 |
-
|
| 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 |
-
|
| 110 |
-
content: "";
|
| 111 |
position: absolute;
|
| 112 |
top: 0;
|
| 113 |
left: 0;
|
| 114 |
width: 100%;
|
| 115 |
height: 100%;
|
| 116 |
-
background:
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 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 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
| 207 |
}
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
margin-top: 2rem;
|
| 214 |
}
|
| 215 |
|
| 216 |
-
.
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
}
|
| 222 |
|
| 223 |
-
.
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
margin-bottom: 0.5rem;
|
| 227 |
}
|
| 228 |
|
| 229 |
-
.
|
| 230 |
display: flex;
|
| 231 |
-
|
| 232 |
-
|
|
|
|
| 233 |
}
|
| 234 |
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
border:
|
| 239 |
-
|
| 240 |
-
|
| 241 |
cursor: pointer;
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
transition: all 0.
|
|
|
|
| 245 |
}
|
| 246 |
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
color:
|
|
|
|
| 250 |
}
|
| 251 |
|
| 252 |
-
|
| 253 |
-
content: "";
|
| 254 |
position: absolute;
|
| 255 |
-
|
| 256 |
left: 0;
|
| 257 |
width: 100%;
|
| 258 |
-
height:
|
| 259 |
-
background:
|
| 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 |
-
|
| 387 |
-
|
| 388 |
-
|
|
|
|
|
|
|
| 389 |
}
|
| 390 |
|
| 391 |
-
|
| 392 |
-
margin-top:
|
| 393 |
-
|
| 394 |
-
|
| 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 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
transition: width 0.5s ease-in-out;
|
| 433 |
-
width: 0;
|
| 434 |
}
|
| 435 |
|
| 436 |
-
.
|
| 437 |
display: flex;
|
| 438 |
justify-content: space-between;
|
| 439 |
-
|
| 440 |
}
|
| 441 |
|
| 442 |
-
.
|
| 443 |
-
|
| 444 |
}
|
| 445 |
|
| 446 |
-
.
|
| 447 |
-
|
| 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 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
|
|
|
| 464 |
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
}
|
| 478 |
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
}
|
| 483 |
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
|
|
|
| 488 |
|
| 489 |
-
|
| 490 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
}
|
| 492 |
|
| 493 |
-
|
| 494 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
}
|
| 496 |
|
| 497 |
-
|
| 498 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 500 |
}
|
| 501 |
|
| 502 |
-
/
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
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 |
-
|
| 543 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 566 |
-
|
| 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 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 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 |
-
|
| 618 |
-
|
| 619 |
-
|
|
|
|
| 620 |
|
| 621 |
-
|
| 622 |
-
|
| 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 |
-
|
| 629 |
-
|
| 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 |
-
|
| 640 |
-
|
| 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 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
</div>
|
| 656 |
-
<div class="progress-bar">
|
| 657 |
-
<div class="progress-fill" style="width: 50%"></div>
|
| 658 |
-
</div>
|
| 659 |
-
</div>
|
| 660 |
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 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 |
-
|
| 674 |
-
|
| 675 |
-
|
|
|
|
| 676 |
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 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 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 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 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 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 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 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 |
-
|
| 848 |
-
|
| 849 |
-
|
|
|
|
| 850 |
|
| 851 |
-
|
| 852 |
-
|
| 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 |
-
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 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 |
-
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
<span class="weapon-stat">ROF: Very Slow</span>
|
| 889 |
-
<span class="weapon-stat">Range: Medium</span>
|
| 890 |
-
</div>
|
| 891 |
-
</div>
|
| 892 |
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
|
| 911 |
-
|
| 912 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 913 |
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
<
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
</div>
|
| 922 |
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 933 |
|
| 934 |
-
|
| 935 |
-
|
| 936 |
-
|
| 937 |
|
| 938 |
-
|
| 939 |
-
|
| 940 |
-
|
| 941 |
-
|
| 942 |
-
|
| 943 |
-
|
| 944 |
-
|
| 945 |
-
|
| 946 |
-
|
| 947 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 948 |
|
| 949 |
-
|
| 950 |
-
|
| 951 |
-
|
| 952 |
-
|
| 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 |
-
|
| 960 |
-
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
|
| 964 |
-
|
| 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 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
-
|
| 990 |
-
|
| 991 |
-
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
| 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 |
-
//
|
| 1012 |
-
|
| 1013 |
-
for (
|
| 1014 |
-
|
|
|
|
|
|
|
| 1015 |
}
|
| 1016 |
|
| 1017 |
-
//
|
| 1018 |
-
|
| 1019 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
}
|
| 1021 |
|
| 1022 |
-
//
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1027 |
|
| 1028 |
-
|
| 1029 |
-
|
| 1030 |
-
|
| 1031 |
-
|
| 1032 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1033 |
}
|
| 1034 |
-
|
| 1035 |
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
|
| 1039 |
-
link.classList.add('active');
|
| 1040 |
-
}
|
| 1041 |
-
});
|
| 1042 |
|
| 1043 |
-
//
|
| 1044 |
-
|
| 1045 |
-
|
| 1046 |
-
|
| 1047 |
-
|
| 1048 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1049 |
}
|
| 1050 |
-
|
|
|
|
|
|
|
| 1051 |
|
| 1052 |
-
//
|
| 1053 |
-
|
| 1054 |
-
|
| 1055 |
-
|
| 1056 |
-
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
|
|
|
|
|
|
|
|
|
| 1067 |
});
|
| 1068 |
-
}
|
| 1069 |
|
| 1070 |
-
//
|
| 1071 |
-
function
|
| 1072 |
-
const
|
| 1073 |
-
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1081 |
}
|
| 1082 |
|
| 1083 |
-
|
| 1084 |
-
|
| 1085 |
-
|
| 1086 |
-
|
| 1087 |
-
|
| 1088 |
-
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1092 |
|
| 1093 |
-
|
| 1094 |
-
|
| 1095 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1096 |
|
| 1097 |
-
//
|
| 1098 |
-
|
| 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>
|