Altaire commited on
Commit
252f1eb
·
verified ·
1 Parent(s): 4dae630

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1331 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Norfolk
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: norfolk
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1331 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Altaire - Battle AI Monsters in Norfolk!</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ /* ZX Spectrum inspired color palette */
10
+ :root {
11
+ --zx-black: #000000;
12
+ --zx-blue: #0000d0;
13
+ --zx-red: #d00000;
14
+ --zx-magenta: #d000d0;
15
+ --zx-green: #00d000;
16
+ --zx-cyan: #00d0d0;
17
+ --zx-yellow: #d0d000;
18
+ --zx-white: #d0d0d0;
19
+ --zx-bright-blue: #0000ff;
20
+ --zx-bright-red: #ff0000;
21
+ --zx-bright-green: #00ff00;
22
+ }
23
+
24
+ body {
25
+ margin: 0;
26
+ padding: 0;
27
+ background-color: var(--zx-blue);
28
+ font-family: 'Courier New', monospace;
29
+ color: var(--zx-white);
30
+ overflow: hidden;
31
+ image-rendering: pixelated;
32
+ }
33
+
34
+ #game-container {
35
+ width: 640px;
36
+ height: 480px;
37
+ margin: 20px auto;
38
+ position: relative;
39
+ border: 8px solid var(--zx-black);
40
+ box-shadow: 0 0 0 8px var(--zx-white);
41
+ background-color: var(--zx-black);
42
+ overflow: hidden;
43
+ }
44
+
45
+ #loading-screen {
46
+ position: absolute;
47
+ width: 100%;
48
+ height: 100%;
49
+ background-color: var(--zx-blue);
50
+ color: var(--zx-white);
51
+ display: flex;
52
+ flex-direction: column;
53
+ justify-content: center;
54
+ align-items: center;
55
+ z-index: 100;
56
+ }
57
+
58
+ #loading-bar {
59
+ width: 80%;
60
+ height: 20px;
61
+ border: 2px solid var(--zx-white);
62
+ margin-top: 20px;
63
+ }
64
+
65
+ #loading-progress {
66
+ height: 100%;
67
+ width: 0;
68
+ background-color: var(--zx-bright-green);
69
+ transition: width 0.3s;
70
+ }
71
+
72
+ #game-screen {
73
+ position: relative;
74
+ width: 100%;
75
+ height: 100%;
76
+ display: none;
77
+ }
78
+
79
+ #title-screen {
80
+ position: absolute;
81
+ width: 100%;
82
+ height: 100%;
83
+ background-color: var(--zx-black);
84
+ color: var(--zx-white);
85
+ text-align: center;
86
+ display: flex;
87
+ flex-direction: column;
88
+ justify-content: center;
89
+ align-items: center;
90
+ z-index: 10;
91
+ }
92
+
93
+ #title {
94
+ font-size: 48px;
95
+ color: var(--zx-bright-red);
96
+ text-shadow: 0 0 10px var(--zx-red);
97
+ margin-bottom: 30px;
98
+ transform: rotate(-5deg);
99
+ animation: flicker 0.5s infinite alternate;
100
+ }
101
+
102
+ @keyframes flicker {
103
+ 0% { opacity: 1; }
104
+ 20% { opacity: 0.8; }
105
+ 40% { opacity: 0.7; }
106
+ 60% { opacity: 0.9; }
107
+ 100% { opacity: 1; }
108
+ }
109
+
110
+ #press-start {
111
+ font-size: 24px;
112
+ color: var(--zx-white);
113
+ margin-top: 40px;
114
+ animation: pulse 2s infinite;
115
+ }
116
+
117
+ @keyframes pulse {
118
+ 0% { opacity: 0.7; }
119
+ 50% { opacity: 1; }
120
+ 100% { opacity: 0.7; }
121
+ }
122
+
123
+ #menu {
124
+ margin-top: 30px;
125
+ }
126
+
127
+ .menu-item {
128
+ margin: 10px 0;
129
+ padding: 8px 20px;
130
+ background-color: var(--zx-blue);
131
+ color: var(--zx-white);
132
+ cursor: pointer;
133
+ border: 2px solid var(--zx-cyan);
134
+ transition: all 0.2s;
135
+ }
136
+
137
+ .menu-item:hover {
138
+ background-color: var(--zx-bright-blue);
139
+ transform: scale(1.05);
140
+ }
141
+
142
+ #game-area {
143
+ position: relative;
144
+ width: 100%;
145
+ height: 100%;
146
+ background-color: var(--zx-black);
147
+ }
148
+
149
+ #player {
150
+ position: absolute;
151
+ width: 32px;
152
+ height: 32px;
153
+ background-color: var(--zx-bright-green);
154
+ z-index: 5;
155
+ }
156
+
157
+ .monster {
158
+ position: absolute;
159
+ width: 32px;
160
+ height: 32px;
161
+ z-index: 4;
162
+ animation: monsterFloat 2s infinite ease-in-out;
163
+ }
164
+
165
+ @keyframes monsterFloat {
166
+ 0%, 100% { transform: translateY(0); }
167
+ 50% { transform: translateY(-5px); }
168
+ }
169
+
170
+ .projectile {
171
+ position: absolute;
172
+ width: 8px;
173
+ height: 8px;
174
+ background-color: var(--zx-yellow);
175
+ z-index: 3;
176
+ }
177
+
178
+ #hud {
179
+ position: absolute;
180
+ top: 10px;
181
+ left: 10px;
182
+ padding: 5px 10px;
183
+ background-color: rgba(0, 0, 0, 0.7);
184
+ border: 2px solid var(--zx-white);
185
+ z-index: 10;
186
+ }
187
+
188
+ #stats {
189
+ display: flex;
190
+ justify-content: space-between;
191
+ }
192
+
193
+ .stat {
194
+ margin-right: 20px;
195
+ color: var(--zx-white);
196
+ }
197
+
198
+ #health-bar {
199
+ width: 100px;
200
+ height: 10px;
201
+ background-color: var(--zx-red);
202
+ margin-top: 5px;
203
+ }
204
+
205
+ #health-fill {
206
+ height: 100%;
207
+ width: 100%;
208
+ background-color: var(--zx-bright-green);
209
+ transition: width 0.3s;
210
+ }
211
+
212
+ #location-display {
213
+ position: absolute;
214
+ bottom: 10px;
215
+ left: 10px;
216
+ background-color: rgba(0, 0, 0, 0.7);
217
+ padding: 5px 10px;
218
+ border: 2px solid var(--zx-white);
219
+ }
220
+
221
+ #controls {
222
+ position: absolute;
223
+ bottom: 10px;
224
+ right: 10px;
225
+ background-color: rgba(0, 0, 0, 0.7);
226
+ padding: 5px 10px;
227
+ border: 2px solid var(--zx-white);
228
+ }
229
+
230
+ #game-over {
231
+ position: absolute;
232
+ width: 100%;
233
+ height: 100%;
234
+ background-color: rgba(0, 0, 0, 0.8);
235
+ display: none;
236
+ flex-direction: column;
237
+ justify-content: center;
238
+ align-items: center;
239
+ z-index: 20;
240
+ }
241
+
242
+ #game-over h1 {
243
+ color: var(--zx-bright-red);
244
+ font-size: 48px;
245
+ margin-bottom: 20px;
246
+ }
247
+
248
+ #btn-retry {
249
+ margin-top: 20px;
250
+ padding: 10px 20px;
251
+ background-color: var(--zx-blue);
252
+ color: var(--zx-white);
253
+ border: 2px solid var(--zx-cyan);
254
+ cursor: pointer;
255
+ }
256
+
257
+ #btn-retry:hover {
258
+ background-color: var(--zx-bright-blue);
259
+ }
260
+
261
+ #victory-screen {
262
+ position: absolute;
263
+ width: 100%;
264
+ height: 100%;
265
+ background-color: rgba(0, 0, 0, 0.8);
266
+ display: none;
267
+ flex-direction: column;
268
+ justify-content: center;
269
+ align-items: center;
270
+ z-index: 20;
271
+ }
272
+
273
+ #victory-screen h1 {
274
+ color: var(--zx-bright-green);
275
+ font-size: 48px;
276
+ margin-bottom: 20px;
277
+ animation: pulse 1s infinite;
278
+ }
279
+
280
+ #norfolk-map {
281
+ width: 400px;
282
+ height: 300px;
283
+ background-color: var(--zx-blue);
284
+ border: 2px solid var(--zx-white);
285
+ position: relative;
286
+ margin: 20px 0;
287
+ }
288
+
289
+ .location-marker {
290
+ position: absolute;
291
+ width: 10px;
292
+ height: 10px;
293
+ background-color: var(--zx-red);
294
+ border-radius: 50%;
295
+ transform: translate(-50%, -50%);
296
+ }
297
+
298
+ .completed-marker {
299
+ background-color: var(--zx-bright-green);
300
+ }
301
+
302
+ .active-marker {
303
+ background-color: var(--zx-yellow);
304
+ box-shadow: 0 0 10px var(--zx-yellow);
305
+ animation: pulse 0.5s infinite;
306
+ }
307
+
308
+ #locations-list {
309
+ margin-top: 20px;
310
+ text-align: left;
311
+ max-height: 150px;
312
+ overflow-y: auto;
313
+ }
314
+
315
+ .location-item {
316
+ padding: 5px 10px;
317
+ margin: 5px 0;
318
+ background-color: rgba(0, 0, 0, 0.5);
319
+ border-left: 3px solid var(--zx-blue);
320
+ }
321
+
322
+ .location-item.completed {
323
+ border-left-color: var(--zx-bright-green);
324
+ }
325
+
326
+ .location-item.active {
327
+ border-left-color: var(--zx-yellow);
328
+ animation: active-border 1s infinite;
329
+ }
330
+
331
+ @keyframes active-border {
332
+ 0% { border-left-color: var(--zx-yellow); }
333
+ 50% { border-left-color: var(--zx-bright-red); }
334
+ 100% { border-left-color: var(--zx-yellow); }
335
+ }
336
+
337
+ #next-location-btn {
338
+ margin-top: 20px;
339
+ padding: 10px 20px;
340
+ background-color: var(--zx-blue);
341
+ color: var(--zx-white);
342
+ border: 2px solid var(--zx-cyan);
343
+ cursor: pointer;
344
+ }
345
+
346
+ #next-location-btn:hover {
347
+ background-color: var(--zx-bright-blue);
348
+ }
349
+
350
+ #final-boss {
351
+ position: absolute;
352
+ width: 48px;
353
+ height: 48px;
354
+ background-color: var(--zx-bright-red);
355
+ z-index: 4;
356
+ animation: bossGlow 1s infinite alternate;
357
+ display: none;
358
+ }
359
+
360
+ @keyframes bossGlow {
361
+ from { box-shadow: 0 0 10px var(--zx-bright-red); }
362
+ to { box-shadow: 0 0 20px var(--zx-bright-red); }
363
+ }
364
+
365
+ #cheats {
366
+ position: absolute;
367
+ bottom: 10px;
368
+ left: 50%;
369
+ transform: translateX(-50%);
370
+ background-color: rgba(0, 0, 0, 0.7);
371
+ padding: 5px 10px;
372
+ border: 2px solid var(--zx-red);
373
+ z-index: 30;
374
+ display: none;
375
+ }
376
+
377
+ #cheats input {
378
+ background-color: var(--zx-black);
379
+ color: var(--zx-white);
380
+ border: 1px solid var(--zx-red);
381
+ padding: 2px 5px;
382
+ }
383
+
384
+ #cheats button {
385
+ background-color: var(--zx-red);
386
+ color: var(--zx-white);
387
+ border: none;
388
+ padding: 2px 5px;
389
+ cursor: pointer;
390
+ margin-left: 5px;
391
+ }
392
+
393
+ /* ZX Spectrum style scanlines effect */
394
+ .scanlines {
395
+ position: absolute;
396
+ top: 0;
397
+ left: 0;
398
+ width: 100%;
399
+ height: 100%;
400
+ background: repeating-linear-gradient(
401
+ to bottom,
402
+ transparent 0%,
403
+ transparent 1px,
404
+ rgba(0, 0, 0, 0.3) 2px,
405
+ rgba(0, 0, 0, 0.3) 3px
406
+ );
407
+ pointer-events: none;
408
+ z-index: 5;
409
+ }
410
+
411
+ /* Pixel grid overlay */
412
+ .pixel-grid {
413
+ position: absolute;
414
+ top: 0;
415
+ left: 0;
416
+ width: 100%;
417
+ height: 100%;
418
+ background-image:
419
+ linear-gradient(to right, rgba(192, 192, 192, 0.1) 1px, transparent 1px),
420
+ linear-gradient(to bottom, rgba(192, 192, 192, 0.1) 1px, transparent 1px);
421
+ background-size: 4px 4px;
422
+ pointer-events: none;
423
+ z-index: 6;
424
+ }
425
+
426
+ /* CRT curvature effect */
427
+ .crt-effect::before {
428
+ content: "";
429
+ position: absolute;
430
+ top: 0;
431
+ left: 0;
432
+ right: 0;
433
+ bottom: 0;
434
+ background: radial-gradient(
435
+ ellipse at center,
436
+ rgba(0, 0, 0, 0) 0%,
437
+ rgba(0, 0, 0, 0) 45%,
438
+ rgba(0, 0, 0, 0.3) 46%,
439
+ rgba(0, 0, 0, 0.3) 54%,
440
+ rgba(0, 0, 0, 0) 55%,
441
+ rgba(0, 0, 0, 0) 100%
442
+ );
443
+ pointer-events: none;
444
+ z-index: 7;
445
+ }
446
+ </style>
447
+ </head>
448
+ <body>
449
+ <div id="game-container">
450
+ <div id="loading-screen">
451
+ <h1>ALT<span style="color: var(--zx-bright-red);">AI</span>RE</h1>
452
+ <p>Loading digital defenses...</p>
453
+ <div id="loading-bar">
454
+ <div id="loading-progress"></div>
455
+ </div>
456
+ </div>
457
+
458
+ <div id="game-screen">
459
+ <div id="title-screen">
460
+ <div id="title">ALT<span style="color: var(--zx-bright-red);">AI</span>RE</div>
461
+ <p>BATTLE AI MONSTERS IN NORFOLK</p>
462
+ <div id="menu">
463
+ <div class="menu-item" id="start-game">START GAME</div>
464
+ <div class="menu-item" id="locations">NORFOLK LOCATIONS</div>
465
+ <div class="menu-item" id="instructions">INSTRUCTIONS</div>
466
+ </div>
467
+ <div id="press-start">PRESS START TO DEFEND NORFOLK</div>
468
+ </div>
469
+
470
+ <div id="game-area">
471
+ <div id="hud">
472
+ <div id="stats">
473
+ <div class="stat">HEALTH: <span id="health-value">100%</span></div>
474
+ <div class="stat">SCORE: <span id="score-value">0</span></div>
475
+ <div class="stat">LEVEL: <span id="level-value">1</span></div>
476
+ <div class="stat">MONSTERS: <span id="monsters-left">5</span></div>
477
+ </div>
478
+ <div id="health-bar">
479
+ <div id="health-fill"></div>
480
+ </div>
481
+ </div>
482
+
483
+ <div id="player"></div>
484
+ <div id="final-boss"></div>
485
+
486
+ <div id="location-display">
487
+ CURRENT LOCATION: <span id="location-name">GREAT YARMOUTH</span>
488
+ </div>
489
+
490
+ <div id="controls">
491
+ WASD: MOVE | SPACE: SHOOT | ESC: MENU
492
+ </div>
493
+
494
+ <div id="cheats">
495
+ <input type="text" id="cheat-input" placeholder="Enter cheat code">
496
+ <button id="submit-cheat">SUBMIT</button>
497
+ </div>
498
+ </div>
499
+
500
+ <div id="game-over">
501
+ <h1>GAME OVER</h1>
502
+ <p>THE AI HAVE OVERRUN NORFOLK!</p>
503
+ <p>YOUR FINAL SCORE: <span id="final-score">0</span></p>
504
+ <div id="btn-retry">TRY AGAIN</div>
505
+ </div>
506
+
507
+ <div id="victory-screen">
508
+ <h1>VICTORY!</h1>
509
+ <p>YOU'VE SAVED NORFOLK FROM THE AI MENACE!</p>
510
+ <div id="norfolk-map"></div>
511
+ <div id="locations-list"></div>
512
+ <p>TOTAL SCORE: <span id="total-score">0</span></p>
513
+ <div id="next-location-btn" style="display: none;">NEXT LOCATION</div>
514
+ </div>
515
+ </div>
516
+
517
+ <div class="scanlines"></div>
518
+ <div class="pixel-grid"></div>
519
+ </div>
520
+
521
+ <audio id="bgm" loop>
522
+ <source src="https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3" type="audio/mpeg">
523
+ </audio>
524
+ <audio id="shoot-sound">
525
+ <source src="https://assets.mixkit.co/sfx/preview/mixkit-short-laser-gun-shot-1670.mp3" type="audio/mpeg">
526
+ </audio>
527
+ <audio id="hit-sound">
528
+ <source src="https://assets.mixkit.co/sfx/preview/mixkit-unlock-game-notification-253.mp3" type="audio/mpeg">
529
+ </audio>
530
+ <audio id="monster-hit-sound">
531
+ <source src="https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-explosion-2759.mp3" type="audio/mpeg">
532
+ </audio>
533
+ <audio id="game-over-sound">
534
+ <source src="https://assets.mixkit.co/sfx/preview/mixkit-ominous-drums-561.mp3" type="audio/mpeg">
535
+ </audio>
536
+ <audio id="victory-sound">
537
+ <source src="https://assets.mixkit.co/sfx/preview/mixkit-winning-chimes-2015.mp3" type="audio/mpeg">
538
+ </audio>
539
+
540
+ <script>
541
+ // Game state
542
+ const gameState = {
543
+ health: 100,
544
+ score: 0,
545
+ level: 1,
546
+ monstersKilled: 0,
547
+ monstersSpawned: 0,
548
+ gameActive: false,
549
+ playerX: 0,
550
+ playerY: 0,
551
+ playerWidth: 32,
552
+ playerHeight: 32,
553
+ keys: {},
554
+ projectiles: [],
555
+ monsters: [],
556
+ currentLocation: 0,
557
+ locationsCompleted: [],
558
+ cheatCodes: {
559
+ 'NORFOLK': () => { gameState.health = 100; updateHealth(); },
560
+ 'YARMOUTH': () => { gameState.score += 1000; updateScore(); },
561
+ 'KINGSLYNN': () => { spawnMonsters(3); },
562
+ 'SUFFOLK': () => { document.getElementById('game-over').style.display = 'none'; gameState.gameActive = true; }
563
+ }
564
+ };
565
+
566
+ // Norfolk locations with positions for the map
567
+ const norfolkLocations = [
568
+ { name: 'GREAT YARMOUTH', x: 80, y: 240, monsters: 5, boss: false },
569
+ { name: 'NORWICH', x: 120, y: 160, monsters: 8, boss: false },
570
+ { name: 'CROMER', x: 60, y: 80, monsters: 6, boss: false },
571
+ { name: 'HOLT', x: 100, y: 60, monsters: 7, boss: false },
572
+ { name: 'FAKENHAM', x: 150, y: 100, monsters: 6, boss: false },
573
+ { name: 'DEREHAM', x: 180, y: 140, monsters: 7, boss: false },
574
+ { name: 'ATTLEBOROUGH', x: 210, y: 180, monsters: 8, boss: false },
575
+ { name: 'WYMONDHAM', x: 190, y: 200, monsters: 7, boss: false },
576
+ { name: 'LOWESTOFT', x: 100, y: 230, monsters: 6, boss: false },
577
+ { name: 'BECCLES', x: 130, y: 250, monsters: 5, boss: false },
578
+ { name: 'KING\'S LYNN', x: 50, y: 150, monsters: 10, boss: true }
579
+ ];
580
+
581
+ // DOM elements
582
+ const gameContainer = document.getElementById('game-container');
583
+ const loadingScreen = document.getElementById('loading-screen');
584
+ const loadingProgress = document.getElementById('loading-progress');
585
+ const gameScreen = document.getElementById('game-screen');
586
+ const titleScreen = document.getElementById('title-screen');
587
+ const gameArea = document.getElementById('game-area');
588
+ const player = document.getElementById('player');
589
+ const finalBoss = document.getElementById('final-boss');
590
+ const hud = document.getElementById('hud');
591
+ const healthValue = document.getElementById('health-value');
592
+ const healthFill = document.getElementById('health-fill');
593
+ const scoreValue = document.getElementById('score-value');
594
+ const levelValue = document.getElementById('level-value');
595
+ const monstersLeft = document.getElementById('monsters-left');
596
+ const locationName = document.getElementById('location-name');
597
+ const gameOverScreen = document.getElementById('game-over');
598
+ const finalScore = document.getElementById('final-score');
599
+ const btnRetry = document.getElementById('btn-retry');
600
+ const victoryScreen = document.getElementById('victory-screen');
601
+ const totalScore = document.getElementById('total-score');
602
+ const norfolkMap = document.getElementById('norfolk-map');
603
+ const locationsList = document.getElementById('locations-list');
604
+ const nextLocationBtn = document.getElementById('next-location-btn');
605
+ const cheatInput = document.getElementById('cheat-input');
606
+ const submitCheat = document.getElementById('submit-cheat');
607
+ const cheatsPanel = document.getElementById('cheats');
608
+
609
+ // Audio elements
610
+ const bgm = document.getElementById('bgm');
611
+ const shootSound = document.getElementById('shoot-sound');
612
+ const hitSound = document.getElementById('hit-sound');
613
+ const monsterHitSound = document.getElementById('monster-hit-sound');
614
+ const gameOverSound = document.getElementById('game-over-sound');
615
+ const victorySound = document.getElementById('victory-sound');
616
+
617
+ // Menu buttons
618
+ document.getElementById('start-game').addEventListener('click', startGame);
619
+ document.getElementById('locations').addEventListener('click', showLocations);
620
+ document.getElementById('instructions').addEventListener('click', showInstructions);
621
+ btnRetry.addEventListener('click', restartGame);
622
+ nextLocationBtn.addEventListener('click', nextLocation);
623
+ submitCheat.addEventListener('click', submitCheatCode);
624
+
625
+ // Simulate loading
626
+ simulateLoading();
627
+
628
+ // Keyboard controls
629
+ document.addEventListener('keydown', (e) => {
630
+ gameState.keys[e.key.toUpperCase()] = true;
631
+
632
+ // Cheats activation
633
+ if (e.key === '`') {
634
+ cheatsPanel.style.display = cheatsPanel.style.display === 'none' ? 'block' : 'none';
635
+ if (cheatsPanel.style.display === 'block') {
636
+ cheatInput.focus();
637
+ }
638
+ }
639
+
640
+ // Pause game with ESC
641
+ if (e.key === 'Escape') {
642
+ if (gameState.gameActive) {
643
+ pauseGame();
644
+ } else if (titleScreen.style.display !== 'flex') {
645
+ resumeGame();
646
+ }
647
+ }
648
+
649
+ // Start game with SPACE from title screen
650
+ if (e.key === ' ' && titleScreen.style.display === 'flex') {
651
+ startGame();
652
+ }
653
+ });
654
+
655
+ document.addEventListener('keyup', (e) => {
656
+ gameState.keys[e.key.toUpperCase()] = false;
657
+ });
658
+
659
+ function submitCheatCode() {
660
+ const code = cheatInput.value.trim().toUpperCase();
661
+ if (gameState.cheatCodes[code]) {
662
+ gameState.cheatCodes[code]();
663
+ cheatInput.value = '';
664
+ cheatsPanel.style.display = 'none';
665
+ } else {
666
+ cheatInput.value = 'INVALID CODE';
667
+ setTimeout(() => {
668
+ cheatInput.value = '';
669
+ }, 1000);
670
+ }
671
+ }
672
+
673
+ function simulateLoading() {
674
+ let progress = 0;
675
+ const interval = setInterval(() => {
676
+ progress += Math.random() * 10;
677
+ if (progress > 100) progress = 100;
678
+ loadingProgress.style.width = `${progress}%`;
679
+
680
+ if (progress === 100) {
681
+ clearInterval(interval);
682
+ setTimeout(() => {
683
+ loadingScreen.style.display = 'none';
684
+ gameScreen.style.display = 'block';
685
+ // Start title music
686
+ bgm.volume = 0.3;
687
+ bgm.play();
688
+ }, 500);
689
+ }
690
+ }, 200);
691
+ }
692
+
693
+ function startGame() {
694
+ titleScreen.style.display = 'none';
695
+ gameState.gameActive = true;
696
+
697
+ // Reset game state
698
+ gameState.health = 100;
699
+ gameState.score = 0;
700
+ gameState.level = 1;
701
+ gameState.monstersKilled = 0;
702
+ gameState.monstersSpawned = 0;
703
+ gameState.currentLocation = 0;
704
+ gameState.locationsCompleted = [];
705
+ gameState.projectiles = [];
706
+ gameState.monsters = [];
707
+
708
+ // Clear any existing monsters
709
+ document.querySelectorAll('.monster').forEach(m => m.remove());
710
+ document.querySelectorAll('.projectile').forEach(p => p.remove());
711
+
712
+ // Update UI
713
+ updateHealth();
714
+ updateScore();
715
+ levelValue.textContent = gameState.level;
716
+
717
+ // Position player
718
+ gameState.playerX = (gameArea.clientWidth - gameState.playerWidth) / 2;
719
+ gameState.playerY = gameArea.clientHeight - gameState.playerHeight - 50;
720
+ player.style.left = `${gameState.playerX}px`;
721
+ player.style.top = `${gameState.playerY}px`;
722
+
723
+ // Set initial location
724
+ startLocation();
725
+
726
+ // Start game loop
727
+ requestAnimationFrame(gameLoop);
728
+ }
729
+
730
+ function startLocation() {
731
+ const location = norfolkLocations[gameState.currentLocation];
732
+ locationName.textContent = location.name;
733
+
734
+ const totalMonsters = location.monsters;
735
+ monstersLeft.textContent = totalMonsters;
736
+ gameState.monstersKilled = 0;
737
+ gameState.monstersSpawned = 0;
738
+
739
+ // Spawn initial monsters
740
+ if (location.boss) {
741
+ // Spawn the final boss
742
+ finalBoss.style.display = 'block';
743
+ finalBoss.style.left = `${(gameArea.clientWidth - 48) / 2}px`;
744
+ finalBoss.style.top = '50px';
745
+ gameState.monsters.push({
746
+ element: finalBoss,
747
+ x: (gameArea.clientWidth - 48) / 2,
748
+ y: 50,
749
+ width: 48,
750
+ height: 48,
751
+ health: 20,
752
+ speed: 2,
753
+ isBoss: true
754
+ });
755
+ gameState.monstersSpawned = 1;
756
+ } else {
757
+ spawnMonsters(2);
758
+ }
759
+ }
760
+
761
+ function spawnMonsters(count) {
762
+ for (let i = 0; i < count; i++) {
763
+ if (gameState.monstersSpawned >= norfolkLocations[gameState.currentLocation].monsters) {
764
+ return;
765
+ }
766
+
767
+ gameState.monstersSpawned++;
768
+
769
+ const monster = document.createElement('div');
770
+ monster.className = 'monster';
771
+
772
+ const monsterSize = 24 + Math.random() * 16;
773
+ const monsterX = Math.random() * (gameArea.clientWidth - monsterSize);
774
+ const monsterY = Math.random() * (gameArea.clientHeight / 2 - monsterSize);
775
+
776
+ monster.style.width = `${monsterSize}px`;
777
+ monster.style.height = `${monsterSize}px`;
778
+ monster.style.left = `${monsterX}px`;
779
+ monster.style.top = `${monsterY}px`;
780
+
781
+ // Random monster color
782
+ const colors = ['var(--zx-red)', 'var(--zx-magenta)', 'var(--zx-cyan)', 'var(--zx-yellow)'];
783
+ monster.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
784
+
785
+ gameArea.appendChild(monster);
786
+
787
+ gameState.monsters.push({
788
+ element: monster,
789
+ x: monsterX,
790
+ y: monsterY,
791
+ width: monsterSize,
792
+ height: monsterSize,
793
+ health: 2,
794
+ speedX: (Math.random() - 0.5) * 4,
795
+ speedY: Math.random() * 2 + 1,
796
+ isBoss: false
797
+ });
798
+
799
+ monstersLeft.textContent = norfolkLocations[gameState.currentLocation].monsters - gameState.monstersKilled;
800
+ }
801
+ }
802
+
803
+ function gameLoop() {
804
+ if (!gameState.gameActive) return;
805
+
806
+ // Player movement
807
+ const moveSpeed = 5;
808
+ if (gameState.keys['W'] || gameState.keys['ARROWUP']) {
809
+ gameState.playerY = Math.max(0, gameState.playerY - moveSpeed);
810
+ }
811
+ if (gameState.keys['S'] || gameState.keys['ARROWDOWN']) {
812
+ gameState.playerY = Math.min(gameArea.clientHeight - gameState.playerHeight, gameState.playerY + moveSpeed);
813
+ }
814
+ if (gameState.keys['A'] || gameState.keys['ARROWLEFT']) {
815
+ gameState.playerX = Math.max(0, gameState.playerX - moveSpeed);
816
+ }
817
+ if (gameState.keys['D'] || gameState.keys['ARROWRIGHT']) {
818
+ gameState.playerX = Math.min(gameArea.clientWidth - gameState.playerWidth, gameState.playerX + moveSpeed);
819
+ }
820
+
821
+ // Update player position
822
+ player.style.left = `${gameState.playerX}px`;
823
+ player.style.top = `${gameState.playerY}px`;
824
+
825
+ // Shooting
826
+ if (gameState.keys[' ']) {
827
+ if (gameState.lastShot === undefined || Date.now() - gameState.lastShot > 300) {
828
+ shoot();
829
+ gameState.lastShot = Date.now();
830
+ }
831
+ }
832
+
833
+ // Update projectiles
834
+ updateProjectiles();
835
+
836
+ // Update monsters
837
+ updateMonsters();
838
+
839
+ // Check for collisions
840
+ checkCollisions();
841
+
842
+ // Spawn more monsters if needed
843
+ if (gameState.monsters.length < 3 && gameState.monstersSpawned < norfolkLocations[gameState.currentLocation].monsters) {
844
+ spawnMonsters(1);
845
+ }
846
+
847
+ requestAnimationFrame(gameLoop);
848
+ }
849
+
850
+ function shoot() {
851
+ const projectile = document.createElement('div');
852
+ projectile.className = 'projectile';
853
+
854
+ const projectileX = gameState.playerX + gameState.playerWidth / 2 - 4;
855
+ const projectileY = gameState.playerY;
856
+
857
+ projectile.style.left = `${projectileX}px`;
858
+ projectile.style.top = `${projectileY}px`;
859
+
860
+ gameArea.appendChild(projectile);
861
+
862
+ gameState.projectiles.push({
863
+ element: projectile,
864
+ x: projectileX,
865
+ y: projectileY,
866
+ width: 8,
867
+ height: 8,
868
+ speed: 8
869
+ });
870
+
871
+ // Play shoot sound
872
+ shootSound.currentTime = 0;
873
+ shootSound.play();
874
+ }
875
+
876
+ function updateProjectiles() {
877
+ for (let i = gameState.projectiles.length - 1; i >= 0; i--) {
878
+ const projectile = gameState.projectiles[i];
879
+ projectile.y -= projectile.speed;
880
+ projectile.element.style.top = `${projectile.y}px`;
881
+
882
+ // Remove if out of bounds
883
+ if (projectile.y < 0) {
884
+ gameArea.removeChild(projectile.element);
885
+ gameState.projectiles.splice(i, 1);
886
+ }
887
+ }
888
+ }
889
+
890
+ function updateMonsters() {
891
+ for (let i = gameState.monsters.length - 1; i >= 0; i--) {
892
+ const monster = gameState.monsters[i];
893
+
894
+ if (!monster.isBoss) {
895
+ // Regular monster movement
896
+ monster.x += monster.speedX;
897
+ monster.y += monster.speedY;
898
+
899
+ // Bounce off walls
900
+ if (monster.x <= 0 || monster.x + monster.width >= gameArea.clientWidth) {
901
+ monster.speedX *= -1;
902
+ }
903
+
904
+ // Reverse direction when hitting bottom
905
+ if (monster.y + monster.height >= gameArea.clientHeight) {
906
+ monster.speedY *= -1;
907
+ }
908
+
909
+ monster.element.style.left = `${monster.x}px`;
910
+ monster.element.style.top = `${monster.y}px`;
911
+ } else {
912
+ // Boss movement pattern
913
+ if (monster.moveDirection === undefined) {
914
+ monster.moveDirection = Math.random() < 0.5 ? 1 : -1;
915
+ monster.moveCounter = Math.random() * 100 + 50;
916
+ }
917
+
918
+ monster.x += monster.speed * monster.moveDirection;
919
+ monster.moveCounter--;
920
+
921
+ if (monster.moveCounter <= 0 ||
922
+ monster.x <= 0 ||
923
+ monster.x + monster.width >= gameArea.clientWidth) {
924
+ monster.moveDirection *= -1;
925
+ monster.moveCounter = Math.random() * 100 + 50;
926
+
927
+ // Boss occasionally shoots
928
+ if (Math.random() < 0.2) {
929
+ bossShoot(monster);
930
+ }
931
+ }
932
+
933
+ monster.element.style.left = `${monster.x}px`;
934
+ }
935
+ }
936
+ }
937
+
938
+ function bossShoot(boss) {
939
+ for (let i = 0; i < 3; i++) {
940
+ setTimeout(() => {
941
+ const projectile = document.createElement('div');
942
+ projectile.className = 'projectile';
943
+ projectile.style.backgroundColor = 'var(--zx-bright-red)';
944
+
945
+ const projectileX = boss.x + boss.width / 2 - 4;
946
+ const projectileY = boss.y + boss.height;
947
+
948
+ projectile.style.left = `${projectileX}px`;
949
+ projectile.style.top = `${projectileY}px`;
950
+
951
+ gameArea.appendChild(projectile);
952
+
953
+ gameState.projectiles.push({
954
+ element: projectile,
955
+ x: projectileX,
956
+ y: projectileY,
957
+ width: 8,
958
+ height: 8,
959
+ speed: -5, // Moves downward
960
+ fromMonster: true
961
+ });
962
+
963
+ // Play shoot sound
964
+ shootSound.currentTime = 0;
965
+ shootSound.play();
966
+ }, i * 300);
967
+ }
968
+ }
969
+
970
+ function checkCollisions() {
971
+ // Check projectile to monster collisions
972
+ for (let i = gameState.projectiles.length - 1; i >= 0; i--) {
973
+ const projectile = gameState.projectiles[i];
974
+
975
+ if (projectile.fromMonster) {
976
+ // Monsters shooting at player
977
+ if (checkCollision(projectile, {
978
+ x: gameState.playerX,
979
+ y: gameState.playerY,
980
+ width: gameState.playerWidth,
981
+ height: gameState.playerHeight
982
+ })) {
983
+ gameArea.removeChild(projectile.element);
984
+ gameState.projectiles.splice(i, 1);
985
+ takeDamage(10);
986
+ continue;
987
+ }
988
+ } else {
989
+ // Player shooting at monsters
990
+ for (let j = gameState.monsters.length - 1; j >= 0; j--) {
991
+ const monster = gameState.monsters[j];
992
+
993
+ if (checkCollision(projectile, monster)) {
994
+ gameArea.removeChild(projectile.element);
995
+ gameState.projectiles.splice(i, 1);
996
+
997
+ monster.health--;
998
+ if (monster.health <= 0) {
999
+ gameArea.removeChild(monster.element);
1000
+ gameState.monsters.splice(j, 1);
1001
+ gameState.monstersKilled++;
1002
+ gameState.score += monster.isBoss ? 500 : 100;
1003
+ updateScore();
1004
+
1005
+ // Show explosion effect for boss
1006
+ if (monster.isBoss) {
1007
+ const explosion = document.createElement('div');
1008
+ explosion.style.position = 'absolute';
1009
+ explosion.style.left = `${monster.x}px`;
1010
+ explosion.style.top = `${monster.y}px`;
1011
+ explosion.style.width = `${monster.width}px`;
1012
+ explosion.style.height = `${monster.height}px`;
1013
+ explosion.style.backgroundColor = 'var(--zx-bright-red)';
1014
+ explosion.style.zIndex = '100';
1015
+ explosion.style.borderRadius = '50%';
1016
+ explosion.style.animation = 'flicker 0.1s 10 alternate';
1017
+ gameArea.appendChild(explosion);
1018
+
1019
+ setTimeout(() => {
1020
+ gameArea.removeChild(explosion);
1021
+ }, 1000);
1022
+ }
1023
+ }
1024
+
1025
+ // Play hit sound
1026
+ monsterHitSound.currentTime = 0;
1027
+ monsterHitSound.play();
1028
+ break;
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+
1034
+ // Check monster to player collisions
1035
+ for (let i = gameState.monsters.length - 1; i >= 0; i--) {
1036
+ const monster = gameState.monsters[i];
1037
+ if (!monster.isBoss && checkCollision({
1038
+ x: gameState.playerX,
1039
+ y: gameState.playerY,
1040
+ width: gameState.playerWidth,
1041
+ height: gameState.playerHeight
1042
+ }, monster)) {
1043
+ takeDamage(5);
1044
+
1045
+ // Push monster away
1046
+ monster.speedX = (monster.x < gameState.playerX) ? -3 : 3;
1047
+ monster.speedY = -2;
1048
+ }
1049
+ }
1050
+
1051
+ // Update monsters left display
1052
+ monstersLeft.textContent = norfolkLocations[gameState.currentLocation].monsters - gameState.monstersKilled;
1053
+
1054
+ // Check if location is cleared
1055
+ if (gameState.monstersKilled >= norfolkLocations[gameState.currentLocation].monsters) {
1056
+ locationCleared();
1057
+ }
1058
+ }
1059
+
1060
+ function checkCollision(obj1, obj2) {
1061
+ return obj1.x < obj2.x + obj2.width &&
1062
+ obj1.x + obj1.width > obj2.x &&
1063
+ obj1.y < obj2.y + obj2.height &&
1064
+ obj1.y + obj1.height > obj2.y;
1065
+ }
1066
+
1067
+ function takeDamage(amount) {
1068
+ gameState.health -= amount;
1069
+ updateHealth();
1070
+
1071
+ // Play damage sound
1072
+ hitSound.currentTime = 0;
1073
+ hitSound.play();
1074
+
1075
+ // Flash player
1076
+ player.style.backgroundColor = 'var(--zx-bright-red)';
1077
+ setTimeout(() => {
1078
+ player.style.backgroundColor = 'var(--zx-bright-green)';
1079
+ }, 100);
1080
+
1081
+ if (gameState.health <= 0) {
1082
+ gameOver();
1083
+ }
1084
+ }
1085
+
1086
+ function updateHealth() {
1087
+ const healthPercent = Math.max(0, gameState.health);
1088
+ healthValue.textContent = `${healthPercent}%`;
1089
+ healthFill.style.width = `${healthPercent}%`;
1090
+
1091
+ // Change color based on health
1092
+ if (healthPercent > 60) {
1093
+ healthFill.style.backgroundColor = 'var(--zx-bright-green)';
1094
+ } else if (healthPercent > 30) {
1095
+ healthFill.style.backgroundColor = 'var(--zx-yellow)';
1096
+ } else {
1097
+ healthFill.style.backgroundColor = 'var(--zx-bright-red)';
1098
+ }
1099
+ }
1100
+
1101
+ function updateScore() {
1102
+ scoreValue.textContent = gameState.score;
1103
+ }
1104
+
1105
+ function locationCleared() {
1106
+ gameState.gameActive = false;
1107
+ gameState.locationsCompleted.push(gameState.currentLocation);
1108
+
1109
+ // Flash background
1110
+ gameArea.style.backgroundColor = 'var(--zx-bright-green)';
1111
+ setTimeout(() => {
1112
+ gameArea.style.backgroundColor = 'var(--zx-black)';
1113
+ }, 100);
1114
+ setTimeout(() => {
1115
+ gameArea.style.backgroundColor = 'var(--zx-bright-green)';
1116
+ }, 200);
1117
+ setTimeout(() => {
1118
+ gameArea.style.backgroundColor = 'var(--zx-black)';
1119
+ }, 300);
1120
+
1121
+ if (gameState.currentLocation === norfolkLocations.length - 1) {
1122
+ // Final victory
1123
+ victory();
1124
+ } else {
1125
+ // Show next location button
1126
+ nextLocationBtn.style.display = 'block';
1127
+ }
1128
+ }
1129
+
1130
+ function nextLocation() {
1131
+ gameState.currentLocation++;
1132
+ gameState.level++;
1133
+ levelValue.textContent = gameState.level;
1134
+
1135
+ // Reset player position
1136
+ gameState.playerX = (gameArea.clientWidth - gameState.playerWidth) / 2;
1137
+ gameState.playerY = gameArea.clientHeight - gameState.playerHeight - 50;
1138
+ player.style.left = `${gameState.playerX}px`;
1139
+ player.style.top = `${gameState.playerY}px`;
1140
+
1141
+ // Hide victory screen and show game
1142
+ victoryScreen.style.display = 'none';
1143
+ nextLocationBtn.style.display = 'none';
1144
+
1145
+ // Restore 25% health or to 100%, whichever is smaller
1146
+ gameState.health = Math.min(100, gameState.health + 25);
1147
+ updateHealth();
1148
+
1149
+ // Start new location
1150
+ startLocation();
1151
+
1152
+ // Resume game
1153
+ gameState.gameActive = true;
1154
+ requestAnimationFrame(gameLoop);
1155
+ }
1156
+
1157
+ function gameOver() {
1158
+ gameState.gameActive = false;
1159
+ gameOverScreen.style.display = 'flex';
1160
+ finalScore.textContent = gameState.score;
1161
+
1162
+ // Play game over sound
1163
+ bgm.pause();
1164
+ gameOverSound.currentTime = 0;
1165
+ gameOverSound.play();
1166
+ }
1167
+
1168
+ function victory() {
1169
+ gameState.gameActive = false;
1170
+ victoryScreen.style.display = 'flex';
1171
+ totalScore.textContent = gameState.score;
1172
+ nextLocationBtn.style.display = 'none';
1173
+
1174
+ // Create Norfolk map with completion markers
1175
+ norfolkMap.innerHTML = '';
1176
+
1177
+ // Draw Norfolk coastline approximation
1178
+ const coastline = document.createElement('div');
1179
+ coastline.style.position = 'absolute';
1180
+ coastline.style.backgroundColor = 'var(--zx-green)';
1181
+ coastline.style.width = '340px';
1182
+ coastline.style.height = '240px';
1183
+ coastline.style.top = '20px';
1184
+ coastline.style.left = '20px';
1185
+ coastline.style.borderRadius = '0 120px 120px 0';
1186
+ norfolkMap.appendChild(coastline);
1187
+
1188
+ // Add location markers
1189
+ for (let i = 0; i < norfolkLocations.length; i++) {
1190
+ const marker = document.createElement('div');
1191
+ marker.className = 'location-marker';
1192
+
1193
+ if (gameState.locationsCompleted.includes(i)) {
1194
+ marker.classList.add('completed-marker');
1195
+ }
1196
+
1197
+ if (i === gameState.currentLocation) {
1198
+ marker.classList.add('active-marker');
1199
+ }
1200
+
1201
+ marker.style.left = `${norfolkLocations[i].x}px`;
1202
+ marker.style.top = `${norfolkLocations[i].y}px`;
1203
+ marker.title = norfolkLocations[i].name;
1204
+
1205
+ norfolkMap.appendChild(marker);
1206
+ }
1207
+
1208
+ // Populate locations list
1209
+ locationsList.innerHTML = '';
1210
+ for (let i = 0; i < norfolkLocations.length; i++) {
1211
+ const item = document.createElement('div');
1212
+ item.className = 'location-item';
1213
+
1214
+ if (gameState.locationsCompleted.includes(i)) {
1215
+ item.classList.add('completed');
1216
+ item.innerHTML = `<i class="fas fa-check"></i> ${norfolkLocations[i].name} (CLEARED)`;
1217
+ } else if (i === gameState.currentLocation) {
1218
+ item.classList.add('active');
1219
+ item.innerHTML = `<i class="fas fa-skull-crossbones"></i> ${norfolkLocations[i].name} (CURRENT)`;
1220
+ } else {
1221
+ item.innerHTML = `<i class="fas fa-lock"></i> ${norfolkLocations[i].name}`;
1222
+ }
1223
+
1224
+ locationsList.appendChild(item);
1225
+ }
1226
+
1227
+ // Play victory sound
1228
+ bgm.pause();
1229
+ victorySound.currentTime = 0;
1230
+ victorySound.play();
1231
+ }
1232
+
1233
+ function restartGame() {
1234
+ gameOverScreen.style.display = 'none';
1235
+ startGame();
1236
+ }
1237
+
1238
+ function pauseGame() {
1239
+ gameState.gameActive = false;
1240
+ titleScreen.style.display = 'flex';
1241
+ document.getElementById('start-game').textContent = 'RESUME GAME';
1242
+ }
1243
+
1244
+ function resumeGame() {
1245
+ titleScreen.style.display = 'none';
1246
+ gameState.gameActive = true;
1247
+ requestAnimationFrame(gameLoop);
1248
+ }
1249
+
1250
+ function showLocations() {
1251
+ titleScreen.innerHTML = `
1252
+ <h2>NORFOLK LOCATIONS</h2>
1253
+ <div id="location-preview">
1254
+ <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Norfolk_UK_location_map.svg/1200px-Norfolk_UK_location_map.svg.png" style="width: 300px; height: auto; opacity: 0.7;">
1255
+ <div style="position: relative; margin-top: 20px;">
1256
+ ${norfolkLocations.map((loc, i) => `
1257
+ <div style="position: absolute; left: ${loc.x/2}px; top: ${loc.y/2}px;
1258
+ background: var(--zx-red); width: 8px; height: 8px; border-radius: 50%;"></div>
1259
+ `).join('')}
1260
+ </div>
1261
+ </div>
1262
+ <div style="margin-top: 20px;">
1263
+ ${norfolkLocations.map(loc => `
1264
+ <div>${loc.name} - ${loc.monsters} monsters${loc.boss ? ' + BOSS' : ''}</div>
1265
+ `).join('')}
1266
+ </div>
1267
+ <div class="menu-item" id="back-to-menu" style="margin-top: 20px;">BACK</div>
1268
+ `;
1269
+ document.getElementById('back-to-menu').addEventListener('click', () => {
1270
+ titleScreen.innerHTML = `
1271
+ <div id="title">ALT<span style="color: var(--zx-bright-red);">AI</span>RE</div>
1272
+ <p>BATTLE AI MONSTERS IN NORFOLK</p>
1273
+ <div id="menu">
1274
+ <div class="menu-item" id="start-game">START GAME</div>
1275
+ <div class="menu-item" id="locations">NORFOLK LOCATIONS</div>
1276
+ <div class="menu-item" id="instructions">INSTRUCTIONS</div>
1277
+ </div>
1278
+ <div id="press-start">PRESS START TO DEFEND NORFOLK</div>
1279
+ `;
1280
+
1281
+ // Reattach event listeners
1282
+ document.getElementById('start-game').addEventListener('click', startGame);
1283
+ document.getElementById('locations').addEventListener('click', showLocations);
1284
+ document.getElementById('instructions').addEventListener('click', showInstructions);
1285
+ });
1286
+ }
1287
+
1288
+ function showInstructions() {
1289
+ titleScreen.innerHTML = `
1290
+ <h2>HOW TO PLAY</h2>
1291
+ <div style="max-width: 500px; text-align: left; margin: 20px auto;">
1292
+ <p><strong>OBJECTIVE:</strong> Defeat all AI monsters invading Norfolk's locations!</p>
1293
+ <p><strong>MOVEMENT:</strong> Use WASD or arrow keys to move your ship</p>
1294
+ <p><strong>SHOOTING:</strong> Press SPACE to fire at the AI monsters</p>
1295
+ <p><strong>LOCATIONS:</strong> Clear all monsters in each Norfolk location to progress</p>
1296
+ <p><strong>FINAL BOSS:</strong> Defeat the rogue AI in King's Lynn to win!</p>
1297
+
1298
+ <div style="margin-top: 30px; text-align: center;">
1299
+ <div style="display: inline-block; width: 80px; height: 80px; background: var(--zx-bright-green); margin: 10px;"></div>
1300
+ <p>PLAYER SHIP</p>
1301
+
1302
+ <div style="display: inline-block; width: 60px; height: 60px; background: var(--zx-red); margin: 10px; border-radius: 50%;"></div>
1303
+ <p>REGULAR MONSTERS</p>
1304
+
1305
+ <div style="display: inline-block; width: 80px; height: 80px; background: var(--zx-bright-red); margin: 10px; box-shadow: 0 0 10px var(--zx-bright-red);"></div>
1306
+ <p>FINAL BOSS</p>
1307
+ </div>
1308
+ </div>
1309
+ <div class="menu-item" id="back-to-menu" style="margin-top: 20px;">BACK</div>
1310
+ `;
1311
+ document.getElementById('back-to-menu').addEventListener('click', () => {
1312
+ titleScreen.innerHTML = `
1313
+ <div id="title">ALT<span style="color: var(--zx-bright-red);">AI</span>RE</div>
1314
+ <p>BATTLE AI MONSTERS IN NORFOLK</p>
1315
+ <div id="menu">
1316
+ <div class="menu-item" id="start-game">START GAME</div>
1317
+ <div class="menu-item" id="locations">NORFOLK LOCATIONS</div>
1318
+ <div class="menu-item" id="instructions">INSTRUCTIONS</div>
1319
+ </div>
1320
+ <div id="press-start">PRESS START TO DEFEND NORFOLK</div>
1321
+ `;
1322
+
1323
+ // Reattach event listeners
1324
+ document.getElementById('start-game').addEventListener('click', startGame);
1325
+ document.getElementById('locations').addEventListener('click', showLocations);
1326
+ document.getElementById('instructions').addEventListener('click', showInstructions);
1327
+ });
1328
+ }
1329
+ </script>
1330
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1331
+ </html>