af2022 commited on
Commit
00c3870
·
verified ·
1 Parent(s): 0bac73b

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1074 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Lunar Lander Deepsite
3
- emoji: 🚀
4
- colorFrom: gray
5
- colorTo: pink
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: lunar-lander-deepsite
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,1074 @@
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>Lunar Lander Challenge</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700&family=Press+Start+2P&display=swap');
9
+
10
+ :root {
11
+ --primary-color: #6a11cb;
12
+ --secondary-color: #2575fc;
13
+ --danger-color: #ff416c;
14
+ --success-color: #07c25e;
15
+ --text-color: #f1f1f1;
16
+ --moon-surface: #4a4e69;
17
+ --stars: rgba(255, 255, 255, 0.8);
18
+ --shadow: rgba(0, 0, 0, 0.5);
19
+ }
20
+
21
+ * {
22
+ margin: 0;
23
+ padding: 0;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ body {
28
+ font-family: 'Orbitron', sans-serif;
29
+ background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
30
+ color: var(--text-color);
31
+ height: 100vh;
32
+ overflow: hidden;
33
+ user-select: none;
34
+ }
35
+
36
+ .game-container {
37
+ position: relative;
38
+ width: 100%;
39
+ height: 100vh;
40
+ display: flex;
41
+ flex-direction: column;
42
+ justify-content: space-between;
43
+ }
44
+
45
+ .game-ui {
46
+ position: absolute;
47
+ top: 0;
48
+ left: 0;
49
+ width: 100%;
50
+ padding: 1rem;
51
+ display: flex;
52
+ justify-content: space-between;
53
+ z-index: 10;
54
+ pointer-events: none;
55
+ }
56
+
57
+ .ui-panel {
58
+ background: rgba(0, 0, 0, 0.7);
59
+ border-radius: 10px;
60
+ padding: 0.8rem 1.2rem;
61
+ box-shadow: 0 4px 10px var(--shadow);
62
+ border: 1px solid rgba(255, 255, 255, 0.1);
63
+ }
64
+
65
+ .ui-title {
66
+ font-size: 0.8rem;
67
+ margin-bottom: 0.3rem;
68
+ color: var(--secondary-color);
69
+ }
70
+
71
+ .ui-value {
72
+ font-size: 1.3rem;
73
+ font-weight: bold;
74
+ }
75
+
76
+ .fuel-bar {
77
+ height: 5px;
78
+ background: linear-gradient(90deg, var(--danger-color), var(--secondary-color));
79
+ border-radius: 3px;
80
+ margin-top: 5px;
81
+ transition: width 0.3s ease;
82
+ }
83
+
84
+ canvas {
85
+ position: absolute;
86
+ top: 0;
87
+ left: 0;
88
+ width: 100%;
89
+ height: 100%;
90
+ }
91
+
92
+ .controls {
93
+ position: absolute;
94
+ bottom: 20px;
95
+ width: 100%;
96
+ display: flex;
97
+ justify-content: center;
98
+ gap: 2rem;
99
+ z-index: 10;
100
+ }
101
+
102
+ .control-btn {
103
+ background: rgba(255, 255, 255, 0.15);
104
+ color: white;
105
+ border: none;
106
+ width: 60px;
107
+ height: 60px;
108
+ border-radius: 50%;
109
+ font-size: 1.5rem;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ cursor: pointer;
114
+ transition: all 0.2s ease;
115
+ box-shadow: 0 4px 8px var(--shadow);
116
+ border: 2px solid rgba(255, 255, 255, 0.2);
117
+ user-select: none;
118
+ }
119
+
120
+ .control-btn:active {
121
+ transform: scale(0.95);
122
+ background: rgba(255, 255, 255, 0.3);
123
+ }
124
+
125
+ .control-btn.left {
126
+ transform: rotate(-90deg);
127
+ }
128
+
129
+ .control-btn.right {
130
+ transform: rotate(90deg);
131
+ }
132
+
133
+ .angle-indicator {
134
+ position: absolute;
135
+ top: 80px;
136
+ left: 50%;
137
+ transform: translateX(-50%);
138
+ width: 240px;
139
+ height: 60px;
140
+ background: rgba(0, 0, 0, 0.7);
141
+ border-radius: 10px;
142
+ display: flex;
143
+ flex-direction: column;
144
+ justify-content: center;
145
+ align-items: center;
146
+ z-index: 10;
147
+ pointer-events: none;
148
+ border: 1px solid rgba(255, 255, 255, 0.1);
149
+ }
150
+
151
+ .angle-meter {
152
+ position: relative;
153
+ width: 200px;
154
+ height: 20px;
155
+ background: rgba(255, 255, 255, 0.1);
156
+ border-radius: 10px;
157
+ margin-bottom: 5px;
158
+ overflow: hidden;
159
+ }
160
+
161
+ .safe-zone {
162
+ position: absolute;
163
+ top: 0;
164
+ left: 50%;
165
+ transform: translateX(-50%);
166
+ width: 60px;
167
+ height: 100%;
168
+ background: rgba(0, 255, 0, 0.3);
169
+ }
170
+
171
+ .warning-zone {
172
+ position: absolute;
173
+ top: 0;
174
+ left: 50%;
175
+ transform: translateX(-50%);
176
+ width: 120px;
177
+ height: 100%;
178
+ background: rgba(255, 255, 0, 0.1);
179
+ }
180
+
181
+ .angle-pointer {
182
+ position: absolute;
183
+ top: 0;
184
+ left: 50%;
185
+ transform: translateX(-50%);
186
+ width: 4px;
187
+ height: 100%;
188
+ background: white;
189
+ transition: transform 0.1s ease;
190
+ }
191
+
192
+ .angle-status {
193
+ display: flex;
194
+ width: 200px;
195
+ justify-content: space-between;
196
+ margin-top: 5px;
197
+ font-size: 0.8rem;
198
+ }
199
+
200
+ .angle-value {
201
+ font-weight: bold;
202
+ color: white;
203
+ }
204
+
205
+ .angle-warning {
206
+ font-weight: bold;
207
+ color: yellow;
208
+ }
209
+
210
+ .angle-danger {
211
+ font-weight: bold;
212
+ color: red;
213
+ }
214
+
215
+ .angle-perfect {
216
+ font-weight: bold;
217
+ color: lime;
218
+ }
219
+
220
+ .start-screen {
221
+ position: absolute;
222
+ top: 0;
223
+ left: 0;
224
+ width: 100%;
225
+ height: 100%;
226
+ background: rgba(0, 0, 0, 0.8);
227
+ display: flex;
228
+ flex-direction: column;
229
+ justify-content: center;
230
+ align-items: center;
231
+ z-index: 20;
232
+ transition: opacity 0.5s ease;
233
+ }
234
+
235
+ .title {
236
+ font-family: 'Press Start 2P', cursive;
237
+ font-size: 3rem;
238
+ color: var(--secondary-color);
239
+ text-shadow: 0 0 10px var(--secondary-color);
240
+ margin-bottom: 2rem;
241
+ text-align: center;
242
+ }
243
+
244
+ .subtitle {
245
+ font-size: 1.2rem;
246
+ margin-bottom: 3rem;
247
+ text-align: center;
248
+ max-width: 600px;
249
+ line-height: 1.5;
250
+ }
251
+
252
+ .start-btn {
253
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
254
+ border: none;
255
+ color: white;
256
+ padding: 1rem 2rem;
257
+ font-size: 1.2rem;
258
+ border-radius: 50px;
259
+ cursor: pointer;
260
+ transition: all 0.3s ease;
261
+ font-family: 'Orbitron', sans-serif;
262
+ font-weight: bold;
263
+ box-shadow: 0 4px 15px var(--shadow);
264
+ }
265
+
266
+ .start-btn:hover {
267
+ transform: translateY(-3px);
268
+ box-shadow: 0 6px 20px rgba(37, 117, 252, 0.6);
269
+ }
270
+
271
+ .end-screen {
272
+ position: absolute;
273
+ top: 0;
274
+ left: 0;
275
+ width: 100%;
276
+ height: 100%;
277
+ background: rgba(0, 0, 0, 0.8);
278
+ display: flex;
279
+ flex-direction: column;
280
+ justify-content: center;
281
+ align-items: center;
282
+ z-index: 20;
283
+ opacity: 0;
284
+ pointer-events: none;
285
+ transition: opacity 0.5s ease;
286
+ }
287
+
288
+ .end-message {
289
+ font-size: 2.5rem;
290
+ margin-bottom: 2rem;
291
+ text-align: center;
292
+ }
293
+
294
+ .landing-score {
295
+ font-size: 1.5rem;
296
+ margin-bottom: 2rem;
297
+ text-align: center;
298
+ }
299
+
300
+ .crash {
301
+ color: var(--danger-color);
302
+ }
303
+
304
+ .success {
305
+ color: var(--success-color);
306
+ }
307
+
308
+ .instructions {
309
+ position: absolute;
310
+ top: 50%;
311
+ left: 50%;
312
+ transform: translate(-50%, -50%);
313
+ background: rgba(0, 0, 0, 0.7);
314
+ padding: 2rem;
315
+ border-radius: 10px;
316
+ max-width: 500px;
317
+ text-align: center;
318
+ z-index: 30;
319
+ display: none;
320
+ }
321
+
322
+ .instructions h2 {
323
+ margin-bottom: 1rem;
324
+ color: var(--secondary-color);
325
+ }
326
+
327
+ .instructions p {
328
+ margin-bottom: 0.5rem;
329
+ }
330
+
331
+ .close-btn {
332
+ margin-top: 1rem;
333
+ background: var(--secondary-color);
334
+ border: none;
335
+ color: white;
336
+ padding: 0.5rem 1rem;
337
+ border-radius: 5px;
338
+ cursor: pointer;
339
+ }
340
+
341
+ .help-btn {
342
+ position: absolute;
343
+ top: 1rem;
344
+ right: 1rem;
345
+ background: rgba(255, 255, 255, 0.2);
346
+ border: none;
347
+ color: white;
348
+ width: 40px;
349
+ height: 40px;
350
+ border-radius: 50%;
351
+ font-size: 1.2rem;
352
+ cursor: pointer;
353
+ z-index: 20;
354
+ }
355
+
356
+ /* Stars background */
357
+ .stars {
358
+ position: absolute;
359
+ top: 0;
360
+ left: 0;
361
+ width: 100%;
362
+ height: 100%;
363
+ z-index: 1;
364
+ }
365
+
366
+ .star {
367
+ position: absolute;
368
+ background: var(--stars);
369
+ border-radius: 50%;
370
+ animation: twinkle var(--duration) infinite ease-in-out;
371
+ }
372
+
373
+ @keyframes twinkle {
374
+ 0% { opacity: 0.3; }
375
+ 50% { opacity: 1; }
376
+ 100% { opacity: 0.3; }
377
+ }
378
+
379
+ @media (max-width: 768px) {
380
+ .title {
381
+ font-size: 2rem;
382
+ }
383
+
384
+ .subtitle {
385
+ font-size: 1rem;
386
+ max-width: 90%;
387
+ }
388
+
389
+ .controls {
390
+ gap: 1rem;
391
+ bottom: 10px;
392
+ }
393
+
394
+ .control-btn {
395
+ width: 50px;
396
+ height: 50px;
397
+ font-size: 1.2rem;
398
+ }
399
+
400
+ .angle-indicator {
401
+ width: 200px;
402
+ height: 50px;
403
+ top: 70px;
404
+ }
405
+
406
+ .angle-meter {
407
+ width: 180px;
408
+ }
409
+ }
410
+ </style>
411
+ </head>
412
+ <body>
413
+ <div class="game-container">
414
+ <!-- Background stars -->
415
+ <div class="stars" id="stars"></div>
416
+
417
+ <!-- Game UI -->
418
+ <div class="game-ui">
419
+ <div class="ui-panel">
420
+ <div class="ui-title">ALTITUDE</div>
421
+ <div class="ui-value" id="altitude">1000m</div>
422
+ </div>
423
+ <div class="ui-panel">
424
+ <div class="ui-title">VELOCITY</div>
425
+ <div class="ui-value" id="velocity">0m/s</div>
426
+ </div>
427
+ <div class="ui-panel">
428
+ <div class="ui-title">FUEL</div>
429
+ <div class="ui-value" id="fuel">100%</div>
430
+ <div class="fuel-bar" id="fuel-bar"></div>
431
+ </div>
432
+ </div>
433
+
434
+ <!-- Improved Angle Indicator -->
435
+ <div class="angle-indicator" id="angleIndicator">
436
+ <div class="angle-meter">
437
+ <div class="warning-zone"></div>
438
+ <div class="safe-zone"></div>
439
+ <div class="angle-pointer" id="anglePointer"></div>
440
+ </div>
441
+ <div class="angle-status">
442
+ <span>-90°</span>
443
+ <span id="angleText" class="angle-value">0°</span>
444
+ <span>+90°</span>
445
+ </div>
446
+ </div>
447
+
448
+ <!-- Game Canvas -->
449
+ <canvas id="gameCanvas"></canvas>
450
+
451
+ <!-- Touch Controls -->
452
+ <div class="controls">
453
+ <button class="control-btn left" id="rotateLeft">↑</button>
454
+ <button class="control-btn" id="thrustBtn">↑</button>
455
+ <button class="control-btn right" id="rotateRight">↑</button>
456
+ </div>
457
+
458
+ <!-- Start Screen -->
459
+ <div class="start-screen" id="startScreen">
460
+ <h1 class="title">LUNAR LANDER</h1>
461
+ <p class="subtitle">Navigate your spacecraft to the moon's surface. Control your descent carefully to avoid crashing. Every thruster burst uses fuel - manage it wisely!</p>
462
+ <button class="start-btn" id="startBtn">START MISSION</button>
463
+ <button class="help-btn" id="helpBtn">?</button>
464
+ </div>
465
+
466
+ <!-- End Screen -->
467
+ <div class="end-screen" id="endScreen">
468
+ <h2 class="end-message" id="endMessage">MISSION FAILED</h2>
469
+ <p class="landing-score" id="landingScore">Impact Velocity: 0m/s</p>
470
+ <button class="start-btn" id="restartBtn">TRY AGAIN</button>
471
+ </div>
472
+
473
+ <!-- Instructions -->
474
+ <div class="instructions" id="instructions">
475
+ <h2>Lunar Lander Instructions</h2>
476
+ <p><strong>Objective:</strong> Land safely on the moon's surface at a speed below 5m/s.</p>
477
+ <p><strong>Controls:</strong></p>
478
+ <p>• Arrow Left/Right or A/D keys to rotate</p>
479
+ <p>• Arrow Up or W key to fire thrusters</p>
480
+ <p>Or use the touch controls on mobile</p>
481
+ <p><strong>Landing Requirements:</strong></p>
482
+ <p>• Velocity below 5m/s</p>
483
+ <p>• Angle within ±10° (Upright position)</p>
484
+ <p>• Land on the marked platform</p>
485
+ <p><strong>Note:</strong> Each thruster burst uses fuel. When you run out, you have no control!</p>
486
+ <button class="close-btn" id="closeBtn">UNDERSTOOD</button>
487
+ </div>
488
+ </div>
489
+
490
+ <script>
491
+ // Game variables
492
+ let canvas, ctx;
493
+ let landerImg, stars = [];
494
+ let lander = {
495
+ x: 0,
496
+ y: 0,
497
+ width: 30,
498
+ height: 40,
499
+ angle: 0,
500
+ velocityX: 0,
501
+ velocityY: 0,
502
+ rotationSpeed: 0,
503
+ fuel: 100,
504
+ thrusting: false,
505
+ crashed: false,
506
+ landed: false
507
+ };
508
+
509
+ let terrain = [];
510
+ let landingZone = { x: 0, y: 0, width: 80 };
511
+ let gameStarted = false;
512
+ let gravity = 0.0015;
513
+ let thrust = 0.05;
514
+ let rotationThrust = 0.08; // Slower rotation
515
+ let lastTime = 0;
516
+ let keys = {};
517
+ let terrainWidth = 0;
518
+ let beaconPulse = 0;
519
+
520
+ // DOM elements
521
+ const altitudeDisplay = document.getElementById('altitude');
522
+ const velocityDisplay = document.getElementById('velocity');
523
+ const fuelDisplay = document.getElementById('fuel');
524
+ const fuelBar = document.getElementById('fuel-bar');
525
+ const anglePointer = document.getElementById('anglePointer');
526
+ const angleText = document.getElementById('angleText');
527
+ const angleIndicator = document.getElementById('angleIndicator');
528
+ const startScreen = document.getElementById('startScreen');
529
+ const endScreen = document.getElementById('endScreen');
530
+ const endMessage = document.getElementById('endMessage');
531
+ const landingScore = document.getElementById('landingScore');
532
+ const startBtn = document.getElementById('startBtn');
533
+ const restartBtn = document.getElementById('restartBtn');
534
+ const thrustBtn = document.getElementById('thrustBtn');
535
+ const rotateLeftBtn = document.getElementById('rotateLeft');
536
+ const rotateRightBtn = document.getElementById('rotateRight');
537
+ const helpBtn = document.getElementById('helpBtn');
538
+ const instructions = document.getElementById('instructions');
539
+ const closeBtn = document.getElementById('closeBtn');
540
+
541
+ // Initialize game
542
+ function init() {
543
+ canvas = document.getElementById('gameCanvas');
544
+ ctx = canvas.getContext('2d');
545
+
546
+ // Set canvas size
547
+ resizeCanvas();
548
+ window.addEventListener('resize', resizeCanvas);
549
+
550
+ // Create stars background
551
+ createStars();
552
+
553
+ // Generate terrain
554
+ generateTerrain();
555
+
556
+ // Position lander
557
+ resetLander();
558
+
559
+ // Event listeners
560
+ document.addEventListener('keydown', handleKeyDown);
561
+ document.addEventListener('keyup', handleKeyUp);
562
+
563
+ startBtn.addEventListener('click', startGame);
564
+ restartBtn.addEventListener('click', restartGame);
565
+ helpBtn.addEventListener('click', showInstructions);
566
+ closeBtn.addEventListener('click', hideInstructions);
567
+
568
+ // Touch controls
569
+ thrustBtn.addEventListener('mousedown', () => keys['ArrowUp'] = true);
570
+ thrustBtn.addEventListener('mouseup', () => keys['ArrowUp'] = false);
571
+ thrustBtn.addEventListener('touchstart', () => keys['ArrowUp'] = true);
572
+ thrustBtn.addEventListener('touchend', () => keys['ArrowUp'] = false);
573
+
574
+ rotateLeftBtn.addEventListener('mousedown', () => keys['ArrowLeft'] = true);
575
+ rotateLeftBtn.addEventListener('mouseup', () => keys['ArrowLeft'] = false);
576
+ rotateLeftBtn.addEventListener('touchstart', () => keys['ArrowLeft'] = true);
577
+ rotateLeftBtn.addEventListener('touchend', () => keys['ArrowLeft'] = false);
578
+
579
+ rotateRightBtn.addEventListener('mousedown', () => keys['ArrowRight'] = true);
580
+ rotateRightBtn.addEventListener('mouseup', () => keys['ArrowRight'] = false);
581
+ rotateRightBtn.addEventListener('touchstart', () => keys['ArrowRight'] = true);
582
+ rotateRightBtn.addEventListener('touchend', () => keys['ArrowRight'] = false);
583
+
584
+ // Start animation loop
585
+ requestAnimationFrame(gameLoop);
586
+ }
587
+
588
+ function resizeCanvas() {
589
+ canvas.width = window.innerWidth;
590
+ canvas.height = window.innerHeight;
591
+ terrainWidth = canvas.width;
592
+
593
+ if (gameStarted) {
594
+ generateTerrain();
595
+ resetLander();
596
+ }
597
+ }
598
+
599
+ function createStars() {
600
+ const starsContainer = document.getElementById('stars');
601
+ starsContainer.innerHTML = '';
602
+
603
+ for (let i = 0; i < 100; i++) {
604
+ const star = document.createElement('div');
605
+ star.classList.add('star');
606
+
607
+ // Random position and size
608
+ const size = Math.random() * 3;
609
+ star.style.width = `${size}px`;
610
+ star.style.height = `${size}px`;
611
+ star.style.left = `${Math.random() * 100}%`;
612
+ star.style.top = `${Math.random() * 100}%`;
613
+
614
+ // Random animation duration for twinkling effect
615
+ const duration = 1 + Math.random() * 4;
616
+ star.style.setProperty('--duration', `${duration}s`);
617
+
618
+ starsContainer.appendChild(star);
619
+ }
620
+ }
621
+
622
+ function generateTerrain() {
623
+ terrain = [];
624
+ const segments = 20;
625
+ const segmentWidth = terrainWidth / segments;
626
+ let prevHeight = canvas.height * 0.7;
627
+
628
+ // Generate random terrain height at each segment
629
+ for (let i = 0; i <= segments; i++) {
630
+ const x = i * segmentWidth;
631
+
632
+ // Add some randomness but smooth transitions
633
+ let height;
634
+ if (i === 0 || i === segments) {
635
+ height = prevHeight;
636
+ } else {
637
+ const maxChange = canvas.height * 0.1;
638
+ height = prevHeight + (Math.random() * maxChange * 2 - maxChange);
639
+ height = Math.max(canvas.height * 0.5, Math.min(canvas.height * 0.9, height));
640
+ }
641
+
642
+ terrain.push({ x, y: height });
643
+ prevHeight = height;
644
+ }
645
+
646
+ // Choose a random flat area for landing zone
647
+ const landingSegment = Math.floor(segments/2) + Math.floor(Math.random() * (segments/3));
648
+ landingZone.x = terrain[landingSegment].x;
649
+ landingZone.y = terrain[landingSegment].y - 5; // Slightly above terrain
650
+ landingZone.width = 100;
651
+
652
+ // Flatten a few segments around landing zone
653
+ for (let i = landingSegment - 2; i <= landingSegment + 2; i++) {
654
+ if (i >= 0 && i < terrain.length) {
655
+ terrain[i].y = landingZone.y + 5;
656
+ }
657
+ }
658
+ }
659
+
660
+ function resetLander() {
661
+ lander.x = canvas.width / 2;
662
+ lander.y = 50;
663
+ lander.angle = 0;
664
+ lander.velocityX = 0;
665
+ lander.velocityY = 0;
666
+ lander.rotationSpeed = 0;
667
+ lander.fuel = 100;
668
+ lander.thrusting = false;
669
+ lander.crashed = false;
670
+ lander.landed = false;
671
+
672
+ updateUI();
673
+ updateAngleIndicator();
674
+ }
675
+
676
+ function startGame() {
677
+ startScreen.style.opacity = '0';
678
+ startScreen.style.pointerEvents = 'none';
679
+ gameStarted = true;
680
+ angleIndicator.style.display = 'flex';
681
+ resetLander();
682
+ }
683
+
684
+ function restartGame() {
685
+ endScreen.style.opacity = '0';
686
+ endScreen.style.pointerEvents = 'none';
687
+ generateTerrain();
688
+ resetLander();
689
+ }
690
+
691
+ function showInstructions() {
692
+ instructions.style.display = 'block';
693
+ }
694
+
695
+ function hideInstructions() {
696
+ instructions.style.display = 'none';
697
+ }
698
+
699
+ function handleKeyDown(e) {
700
+ keys[e.key] = true;
701
+ }
702
+
703
+ function handleKeyUp(e) {
704
+ keys[e.key] = false;
705
+ }
706
+
707
+ function updateUI() {
708
+ // Calculate altitude to nearest terrain point
709
+ let minAltitude = Infinity;
710
+ for (let i = 0; i < terrain.length; i++) {
711
+ const dist = Math.sqrt(
712
+ Math.pow(lander.x - terrain[i].x, 2) +
713
+ Math.pow(lander.y - terrain[i].y, 2)
714
+ );
715
+ if (dist < minAltitude) {
716
+ minAltitude = dist;
717
+ }
718
+ }
719
+
720
+ const velocity = Math.sqrt(lander.velocityX * lander.velocityX + lander.velocityY * lander.velocityY);
721
+
722
+ altitudeDisplay.textContent = `${Math.floor(minAltitude)}m`;
723
+ velocityDisplay.textContent = `${velocity.toFixed(1)}m/s`;
724
+ fuelDisplay.textContent = `${Math.floor(lander.fuel)}%`;
725
+ fuelBar.style.width = `${lander.fuel}%`;
726
+
727
+ // Change fuel bar color as it gets low
728
+ if (lander.fuel < 20) {
729
+ fuelBar.style.background = 'var(--danger-color)';
730
+ } else if (lander.fuel < 50) {
731
+ fuelBar.style.background = 'linear-gradient(90deg, var(--danger-color), #f7b733)';
732
+ }
733
+
734
+ updateAngleIndicator();
735
+ }
736
+
737
+ function updateAngleIndicator() {
738
+ const angleDegrees = Math.round(lander.angle * (180 / Math.PI));
739
+
740
+ // Update angle pointer position (mapped from -90 to +90 degrees)
741
+ const pointerPos = (angleDegrees + 90) / 180 * 200;
742
+ anglePointer.style.transform = `translateX(${pointerPos}%) translateX(-50%)`;
743
+
744
+ // Update angle text with color coding
745
+ angleText.textContent = `${angleDegrees}°`;
746
+
747
+ // Remove all color classes first
748
+ angleText.classList.remove('angle-perfect', 'angle-warning', 'angle-danger', 'angle-value');
749
+
750
+ if (Math.abs(angleDegrees) < 5) {
751
+ angleText.classList.add('angle-perfect');
752
+ } else if (Math.abs(angleDegrees) < 10) {
753
+ angleText.classList.add('angle-warning');
754
+ } else if (Math.abs(angleDegrees) < 20) {
755
+ angleText.classList.add('angle-danger');
756
+ } else {
757
+ angleText.classList.add('angle-danger');
758
+ }
759
+ }
760
+
761
+ function checkCollision() {
762
+ // Check if lander is below terrain at any point
763
+ for (let i = 0; i < terrain.length - 1; i++) {
764
+ const seg = terrain[i];
765
+ const nextSeg = terrain[i + 1];
766
+
767
+ // Skip if lander is not between these two segments
768
+ if (lander.x + lander.width/2 < seg.x || lander.x - lander.width/2 > nextSeg.x) {
769
+ continue;
770
+ }
771
+
772
+ // Linear interpolation to find terrain height at lander's x position
773
+ const t = (lander.x - seg.x) / (nextSeg.x - seg.x);
774
+ const terrainY = seg.y + t * (nextSeg.y - seg.y);
775
+
776
+ // Check if lander has collided with terrain
777
+ if (lander.y + lander.height/2 >= terrainY) {
778
+ // Check if this is the landing zone
779
+ const inLandingZone =
780
+ lander.x >= landingZone.x &&
781
+ lander.x <= landingZone.x + landingZone.width;
782
+
783
+ const velocity = Math.sqrt(lander.velocityX * lander.velocityX + lander.velocityY * lander.velocityY);
784
+ const angleDegrees = Math.abs(lander.angle * (180 / Math.PI));
785
+
786
+ if (inLandingZone && velocity < 5 && angleDegrees < 10) {
787
+ // Successful landing
788
+ lander.landed = true;
789
+ endMessage.textContent = "LANDING SUCCESS!";
790
+ endMessage.className = "end-message success";
791
+ landingScore.textContent = `Landing Velocity: ${velocity.toFixed(1)}m/s | Angle: ${angleDegrees.toFixed(1)}°`;
792
+ } else {
793
+ // Crash
794
+ lander.crashed = true;
795
+ endMessage.textContent = "MISSION FAILED";
796
+ endMessage.className = "end-message crash";
797
+ landingScore.textContent = `Impact Velocity: ${velocity.toFixed(1)}m/s`;
798
+
799
+ if (velocity >= 10) {
800
+ landingScore.textContent += " - You smashed to pieces!";
801
+ } else if (!inLandingZone) {
802
+ landingScore.textContent += " - Wrong landing spot!";
803
+ } else if (angleDegrees >= 10) {
804
+ landingScore.textContent += " - Bad landing angle!";
805
+ }
806
+ }
807
+
808
+ endScreen.style.opacity = '1';
809
+ endScreen.style.pointerEvents = 'all';
810
+ angleIndicator.style.display = 'none';
811
+
812
+ return true;
813
+ }
814
+ }
815
+ return false;
816
+ }
817
+
818
+ function update(delta) {
819
+ // Update beacon pulse animation
820
+ beaconPulse = (beaconPulse + 0.02) % (Math.PI * 2);
821
+
822
+ // Skip if game not started or lander has crashed/landed
823
+ if (!gameStarted || lander.crashed || lander.landed) return;
824
+
825
+ // Apply rotation controls (no auto-stabilization)
826
+ if (keys['ArrowLeft'] || keys['a']) {
827
+ lander.rotationSpeed = -rotationThrust;
828
+ } else if (keys['ArrowRight'] || keys['d']) {
829
+ lander.rotationSpeed = rotationThrust;
830
+ } else {
831
+ lander.rotationSpeed = 0;
832
+ }
833
+
834
+ // Apply thrust if up is pressed and there's fuel
835
+ lander.thrusting = false;
836
+ if ((keys['ArrowUp'] || keys['w']) && lander.fuel > 0) {
837
+ lander.thrusting = true;
838
+ lander.fuel -= 0.1 * delta;
839
+ if (lander.fuel < 0) lander.fuel = 0;
840
+
841
+ // Calculate thrust vector based on angle
842
+ const thrustX = Math.sin(lander.angle) * thrust * delta;
843
+ const thrustY = -Math.cos(lander.angle) * thrust * delta;
844
+
845
+ lander.velocityX += thrustX;
846
+ lander.velocityY += thrustY;
847
+ }
848
+
849
+ // Update angle with damping to prevent excessive spinning
850
+ lander.angle += lander.rotationSpeed * delta;
851
+
852
+ // Apply gravity
853
+ lander.velocityY += gravity * delta;
854
+
855
+ // Update position
856
+ lander.x += lander.velocityX * delta;
857
+ lander.y += lander.velocityY * delta;
858
+
859
+ // Boundary checks
860
+ if (lander.x < 0) {
861
+ lander.x = 0;
862
+ lander.velocityX *= -0.5; // Bounce off edge
863
+ } else if (lander.x > canvas.width) {
864
+ lander.x = canvas.width;
865
+ lander.velocityX *= -0.5; // Bounce off edge
866
+ }
867
+
868
+ // Update UI elements
869
+ updateUI();
870
+
871
+ // Check for collision with terrain
872
+ return checkCollision();
873
+ }
874
+
875
+ function draw() {
876
+ // Clear canvas
877
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
878
+
879
+ // Draw stars background is handled by CSS
880
+
881
+ // Draw terrain
882
+ ctx.beginPath();
883
+ ctx.moveTo(0, canvas.height);
884
+
885
+ for (let i = 0; i < terrain.length; i++) {
886
+ ctx.lineTo(terrain[i].x, terrain[i].y);
887
+ }
888
+
889
+ ctx.lineTo(canvas.width, canvas.height);
890
+ ctx.lineTo(0, canvas.height);
891
+ ctx.fillStyle = '#4a4e69';
892
+ ctx.fill();
893
+ ctx.strokeStyle = '#3a3e59';
894
+ ctx.stroke();
895
+
896
+ // Draw landing zone with enhanced visibility
897
+ const landingCenterX = landingZone.x + landingZone.width / 2;
898
+ const landingCenterY = landingZone.y;
899
+
900
+ // Landing zone glow effect
901
+ ctx.beginPath();
902
+ ctx.moveTo(landingZone.x, landingZone.y);
903
+ ctx.lineTo(landingZone.x + landingZone.width, landingZone.y);
904
+ ctx.lineWidth = 8;
905
+ ctx.strokeStyle = 'rgba(0, 255, 100, 0.5)';
906
+ ctx.stroke();
907
+
908
+ // Landing zone platform
909
+ ctx.beginPath();
910
+ ctx.moveTo(landingZone.x, landingZone.y);
911
+ ctx.lineTo(landingZone.x + landingZone.width, landingZone.y);
912
+ ctx.lineWidth = 2;
913
+ ctx.strokeStyle = '#00ff64';
914
+ ctx.stroke();
915
+
916
+ // Landing zone markings (stripes)
917
+ for (let x = landingZone.x; x < landingZone.x + landingZone.width; x += 15) {
918
+ ctx.beginPath();
919
+ ctx.moveTo(x, landingZone.y);
920
+ ctx.lineTo(x + 8, landingZone.y);
921
+ ctx.lineWidth = 3;
922
+ ctx.strokeStyle = '#00ff64';
923
+ ctx.stroke();
924
+ }
925
+
926
+ // Pulse beacon at landing zone
927
+ const pulseSize = 8 + Math.sin(beaconPulse) * 5;
928
+ ctx.beginPath();
929
+ ctx.arc(landingCenterX, landingCenterY - 8, pulseSize, 0, Math.PI * 2);
930
+ ctx.fillStyle = 'rgba(0, 255, 100, 0.3)';
931
+ ctx.fill();
932
+
933
+ // Beacon center
934
+ ctx.beginPath();
935
+ ctx.arc(landingCenterX, landingCenterY - 8, 4, 0, Math.PI * 2);
936
+ ctx.fillStyle = '#00ff64';
937
+ ctx.fill();
938
+
939
+ // Beacon light beam
940
+ const beamHeight = 30 + Math.sin(beaconPulse * 2) * 10;
941
+ const gradient = ctx.createLinearGradient(
942
+ landingCenterX, landingCenterY - 8,
943
+ landingCenterX, landingCenterY - 8 - beamHeight
944
+ );
945
+ gradient.addColorStop(0, 'rgba(0, 255, 100, 0.5)');
946
+ gradient.addColorStop(1, 'rgba(0, 255, 100, 0)');
947
+ ctx.fillStyle = gradient;
948
+ ctx.fillRect(landingCenterX - 3, landingCenterY - 8, 6, -beamHeight);
949
+
950
+ // Landed flag (visible when approaching)
951
+ if (Math.abs(lander.y - landingCenterY) < 200) {
952
+ ctx.fillStyle = '#ffffff';
953
+ ctx.fillRect(landingCenterX, landingCenterY - 25, 2, -15);
954
+ ctx.fillStyle = '#00ff64';
955
+ ctx.fillRect(landingCenterX + 2, landingCenterY - 25, 12, -10);
956
+ ctx.fillStyle = '#000000';
957
+ ctx.font = 'bold 8px Arial';
958
+ ctx.fillText('LZ', landingCenterX + 4, landingCenterY - 32);
959
+ }
960
+
961
+ // Draw angle guide at landing zone when close
962
+ if (Math.abs(lander.y - landingCenterY) < 300 && Math.abs(lander.x - landingCenterX) < 300) {
963
+ const guideSize = 40 + Math.sin(beaconPulse * 4) * 5;
964
+
965
+ ctx.save();
966
+ ctx.translate(landingCenterX, landingCenterY);
967
+
968
+ // Safe angle zone (green)
969
+ ctx.beginPath();
970
+ ctx.moveTo(0, 0);
971
+ ctx.lineTo(-guideSize * Math.sin(0.17), -guideSize * Math.cos(0.17));
972
+ ctx.lineTo(guideSize * Math.sin(0.17), -guideSize * Math.cos(0.17));
973
+ ctx.closePath();
974
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.2)';
975
+ ctx.fill();
976
+
977
+ // Center line
978
+ ctx.beginPath();
979
+ ctx.moveTo(0, 0);
980
+ ctx.lineTo(0, -guideSize);
981
+ ctx.lineWidth = 2;
982
+ ctx.strokeStyle = '#00ff00';
983
+ ctx.stroke();
984
+
985
+ ctx.restore();
986
+ }
987
+
988
+ // Draw lander
989
+ ctx.save();
990
+ ctx.translate(lander.x, lander.y);
991
+ ctx.rotate(lander.angle);
992
+
993
+ // Lander base
994
+ ctx.fillStyle = '#333';
995
+ ctx.fillRect(-lander.width/2, -lander.height/2, lander.width, lander.height);
996
+
997
+ // Lander window
998
+ ctx.fillStyle = '#6495ed';
999
+ ctx.beginPath();
1000
+ ctx.arc(0, -5, lander.width/3, 0, Math.PI * 2);
1001
+ ctx.fill();
1002
+
1003
+ // Thruster flame
1004
+ if (lander.thrusting) {
1005
+ ctx.fillStyle = '#ff4500';
1006
+ ctx.beginPath();
1007
+ ctx.moveTo(-lander.width/4, lander.height/2);
1008
+ ctx.lineTo(lander.width/4, lander.height/2);
1009
+ ctx.lineTo(0, lander.height/2 + 20 + Math.random() * 10);
1010
+ ctx.closePath();
1011
+ ctx.fill();
1012
+ }
1013
+
1014
+ ctx.restore();
1015
+
1016
+ // Draw altitude marker
1017
+ if (!lander.landed && !lander.crashed) {
1018
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
1019
+ ctx.strokeStyle = 'white';
1020
+ ctx.beginPath();
1021
+ ctx.arc(lander.x, canvas.height * 0.7, 3, 0, Math.PI * 2);
1022
+ ctx.fill();
1023
+ ctx.stroke();
1024
+
1025
+ ctx.beginPath();
1026
+ ctx.moveTo(lander.x, canvas.height * 0.7 + 5);
1027
+ ctx.lineTo(lander.x, lander.y - lander.height/2 - 5);
1028
+ ctx.setLineDash([5, 3]);
1029
+ ctx.stroke();
1030
+ ctx.setLineDash([]);
1031
+ }
1032
+
1033
+ // Draw distance indicator to landing zone when far away
1034
+ if (Math.abs(lander.x - landingCenterX) > 200 || lander.y < landingCenterY - 300) {
1035
+ const arrowX = lander.x > landingCenterX ? canvas.width - 20 : 20;
1036
+ const arrowDir = lander.x > landingCenterX ? -1 : 1;
1037
+
1038
+ ctx.font = '12px Orbitron';
1039
+ ctx.fillStyle = '#00ff64';
1040
+ ctx.fillText('LAND HERE', arrowX + (arrowDir * 30), 50);
1041
+
1042
+ ctx.beginPath();
1043
+ ctx.moveTo(arrowX, 40);
1044
+ ctx.lineTo(arrowX + (arrowDir * 15), 50);
1045
+ ctx.lineTo(arrowX, 60);
1046
+ ctx.lineWidth = 2;
1047
+ ctx.strokeStyle = '#00ff64';
1048
+ ctx.stroke();
1049
+
1050
+ const distance = Math.floor(Math.sqrt(
1051
+ Math.pow(lander.x - landingCenterX, 2) +
1052
+ Math.pow(lander.y - landingCenterY, 2)
1053
+ ));
1054
+
1055
+ ctx.fillText(`${distance}m`, arrowX + (arrowDir * 30), 70);
1056
+ }
1057
+ }
1058
+
1059
+ function gameLoop(timestamp) {
1060
+ if (!lastTime) lastTime = timestamp;
1061
+ const delta = timestamp - lastTime;
1062
+ lastTime = timestamp;
1063
+
1064
+ update(delta / 16); // Normalize delta to ~16ms frames
1065
+ draw();
1066
+
1067
+ requestAnimationFrame(gameLoop);
1068
+ }
1069
+
1070
+ // Start the game
1071
+ window.addEventListener('load', init);
1072
+ </script>
1073
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1074
+ </html>