engerl commited on
Commit
b92c118
·
verified ·
1 Parent(s): 5e84889

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +593 -565
index.html CHANGED
@@ -3,662 +3,690 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Luxury Snake Game</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
10
- @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap');
11
-
12
  body {
13
- font-family: 'Montserrat', sans-serif;
14
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
15
- color: #e6e6e6;
16
- min-height: 100vh;
17
- overflow-x: hidden;
18
- }
19
-
20
- .game-container {
21
- perspective: 1000px;
22
- }
23
-
24
- .game-board {
25
- background: rgba(255, 255, 255, 0.05);
26
- border-radius: 16px;
27
- box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
28
- backdrop-filter: blur(5px);
29
- -webkit-backdrop-filter: blur(5px);
30
- border: 1px solid rgba(255, 255, 255, 0.1);
31
- transform-style: preserve-3d;
32
- transition: all 0.3s ease;
33
  }
34
-
35
- .game-board:hover {
36
- transform: translateY(-5px) rotateX(5deg);
37
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
38
  }
39
-
40
- .snake-cell {
41
- background: rgba(255, 255, 255, 0.1);
42
- border-radius: 4px;
43
- transition: all 0.2s ease;
 
 
44
  }
45
-
46
- .snake-head {
47
- background: linear-gradient(135deg, #00b4db 0%, #0083b0 100%);
48
- box-shadow: 0 0 15px rgba(0, 180, 219, 0.7);
49
- border-radius: 6px;
50
- transform: scale(1.1);
51
  }
52
-
53
- .snake-body {
54
- background: linear-gradient(135deg, #00b4db 0%, #0083b0 100%);
55
- border-radius: 4px;
56
- opacity: 0.8;
57
  }
58
-
59
- .food {
60
- background: linear-gradient(135deg, #f12711 0%, #f5af19 100%);
 
61
  border-radius: 50%;
62
- animation: pulse 1.5s infinite;
63
- box-shadow: 0 0 15px rgba(241, 39, 17, 0.7);
64
  }
65
-
66
- @keyframes pulse {
67
- 0% { transform: scale(0.95); }
68
- 50% { transform: scale(1.1); }
69
- 100% { transform: scale(0.95); }
 
 
 
70
  }
71
-
72
- .score-display {
73
- background: rgba(255, 255, 255, 0.1);
74
- border-radius: 12px;
75
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
76
  }
77
-
78
- .modal {
79
- background: rgba(26, 26, 46, 0.9);
80
- backdrop-filter: blur(10px);
81
- -webkit-backdrop-filter: blur(10px);
 
 
 
 
 
 
 
 
82
  }
83
-
84
- .btn-glow {
85
- background: linear-gradient(135deg, #00b4db 0%, #0083b0 100%);
86
- box-shadow: 0 0 15px rgba(0, 180, 219, 0.5);
87
- transition: all 0.3s ease;
 
 
88
  }
89
-
90
- .btn-glow:hover {
91
- transform: translateY(-2px);
92
- box-shadow: 0 0 20px rgba(0, 180, 219, 0.8);
 
 
 
 
 
 
 
 
 
 
93
  }
94
-
95
- .btn-danger {
96
- background: linear-gradient(135deg, #f12711 0%, #f5af19 100%);
97
- box-shadow: 0 0 15px rgba(241, 39, 17, 0.5);
 
98
  }
99
-
100
- .btn-danger:hover {
101
- box-shadow: 0 0 20px rgba(241, 39, 17, 0.8);
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
103
-
104
- .controls-btn {
105
- background: rgba(255, 255, 255, 0.1);
106
  border-radius: 50%;
107
- width: 50px;
108
- height: 50px;
109
- display: flex;
110
- align-items: center;
111
- justify-content: center;
112
- transition: all 0.2s ease;
113
  }
114
-
115
- .controls-btn:hover {
116
- background: rgba(255, 255, 255, 0.2);
117
- transform: scale(1.1);
 
 
 
118
  }
119
-
120
- .grid-cell {
121
- transition: all 0.1s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
- .grid-cell:hover {
125
- background: rgba(255, 255, 255, 0.15);
 
 
 
 
 
 
 
126
  }
127
  </style>
128
  </head>
129
- <body class="flex flex-col items-center justify-center p-4">
130
- <div class="text-center mb-6">
131
- <h1 class="text-4xl md:text-5xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 to-blue-600">
132
- <i class="fas fa-snake mr-3"></i>Luxury Snake
133
- </h1>
134
- <p class="text-gray-400">A modern twist on the classic game</p>
135
  </div>
136
-
137
- <div class="game-container w-full max-w-2xl mb-8">
138
- <div class="flex justify-between items-center mb-4">
139
- <div class="score-display px-6 py-3 flex items-center">
140
- <i class="fas fa-star text-yellow-400 mr-2"></i>
141
- <span class="font-bold text-xl" id="score">0</span>
142
- </div>
143
- <div class="flex space-x-3">
144
- <button id="sound-btn" class="controls-btn">
145
- <i class="fas fa-volume-up text-blue-400"></i>
146
- </button>
147
- <button id="pause-btn" class="controls-btn">
148
- <i class="fas fa-pause text-blue-400"></i>
149
- </button>
150
- <button id="restart-btn" class="controls-btn">
151
- <i class="fas fa-redo text-blue-400"></i>
152
- </button>
153
- </div>
154
- </div>
155
-
156
- <div class="game-board w-full aspect-square relative overflow-hidden rounded-xl">
157
- <canvas id="gameCanvas" class="w-full h-full"></canvas>
158
- </div>
159
  </div>
160
-
161
- <div class="grid grid-cols-3 gap-3 mb-8 w-full max-w-xs">
162
- <div></div>
163
- <button id="up-btn" class="controls-btn">
164
- <i class="fas fa-arrow-up"></i>
 
 
 
165
  </button>
166
- <div></div>
167
- <button id="left-btn" class="controls-btn">
168
- <i class="fas fa-arrow-left"></i>
169
  </button>
170
- <button id="down-btn" class="controls-btn">
171
- <i class="fas fa-arrow-down"></i>
172
  </button>
173
- <button id="right-btn" class="controls-btn">
174
- <i class="fas fa-arrow-right"></i>
 
 
 
 
 
 
175
  </button>
176
  </div>
177
 
178
- <div class="text-center text-gray-400 text-sm mb-4">
179
- <p>Use arrow keys or buttons to control the snake</p>
180
- <p>Collect the golden food to grow and earn points</p>
181
- </div>
182
-
183
- <!-- Game Over Modal -->
184
- <div id="game-over-modal" class="fixed inset-0 flex items-center justify-center z-50 modal hidden">
185
- <div class="bg-gray-800 rounded-xl p-8 max-w-md w-full mx-4 text-center transform transition-all duration-300 scale-95 opacity-0">
186
- <div class="text-6xl mb-4 text-red-500">
187
- <i class="fas fa-skull"></i>
188
- </div>
189
- <h2 class="text-3xl font-bold mb-2">Game Over!</h2>
190
- <p class="text-xl mb-6">Your score: <span id="final-score" class="font-bold text-blue-400">0</span></p>
191
- <div class="flex justify-center space-x-4">
192
- <button id="play-again-btn" class="btn-glow px-6 py-3 rounded-lg font-bold">
193
- <i class="fas fa-play mr-2"></i>Play Again
194
- </button>
195
- <button id="quit-btn" class="btn-danger px-6 py-3 rounded-lg font-bold">
196
- <i class="fas fa-times mr-2"></i>Quit
197
- </button>
198
- </div>
199
- </div>
200
- </div>
201
 
202
- <!-- Start Modal -->
203
- <div id="start-modal" class="fixed inset-0 flex items-center justify-center z-50 modal">
204
- <div class="bg-gray-800 rounded-xl p-8 max-w-md w-full mx-4 text-center">
205
- <div class="text-6xl mb-4 text-blue-400">
206
- <i class="fas fa-snake"></i>
207
- </div>
208
- <h2 class="text-3xl font-bold mb-4">Luxury Snake</h2>
209
- <p class="mb-6 text-gray-300">A modern take on the classic snake game with beautiful visuals and smooth gameplay.</p>
210
- <div class="mb-6">
211
- <div class="flex items-center justify-center mb-2">
212
- <div class="w-6 h-6 rounded-full bg-gradient-to-r from-red-500 to-yellow-500 mr-2"></div>
213
- <span>Collect food to grow</span>
214
- </div>
215
- <div class="flex items-center justify-center mb-2">
216
- <div class="w-6 h-6 rounded-full bg-gradient-to-r from-cyan-400 to-blue-600 mr-2"></div>
217
- <span>Don't hit walls or yourself</span>
218
- </div>
219
- </div>
220
- <button id="start-btn" class="btn-glow px-8 py-4 rounded-lg font-bold text-lg w-full">
221
- <i class="fas fa-play mr-2"></i>Start Game
222
- </button>
223
- </div>
224
- </div>
225
 
226
- <audio id="eat-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3" preload="auto"></audio>
227
- <audio id="game-over-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3" preload="auto"></audio>
228
- <audio id="move-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3" preload="auto"></audio>
 
 
 
 
 
 
 
 
 
229
 
230
- <script>
231
- document.addEventListener('DOMContentLoaded', () => {
232
- // Game elements
233
- const canvas = document.getElementById('gameCanvas');
234
- const ctx = canvas.getContext('2d');
235
- const scoreDisplay = document.getElementById('score');
236
- const finalScoreDisplay = document.getElementById('final-score');
237
- const startModal = document.getElementById('start-modal');
238
- const gameOverModal = document.getElementById('game-over-modal');
239
- const startBtn = document.getElementById('start-btn');
240
- const playAgainBtn = document.getElementById('play-again-btn');
241
- const quitBtn = document.getElementById('quit-btn');
242
- const pauseBtn = document.getElementById('pause-btn');
243
- const restartBtn = document.getElementById('restart-btn');
244
- const soundBtn = document.getElementById('sound-btn');
245
- const upBtn = document.getElementById('up-btn');
246
- const downBtn = document.getElementById('down-btn');
247
- const leftBtn = document.getElementById('left-btn');
248
- const rightBtn = document.getElementById('right-btn');
249
-
250
- // Sound elements
251
- const eatSound = document.getElementById('eat-sound');
252
- const gameOverSound = document.getElementById('game-over-sound');
253
- const moveSound = document.getElementById('move-sound');
254
-
255
- // Game variables
256
- let gridSize = 20;
257
- let tileCount = 20;
258
- let tileSize;
259
- let speed = 7;
260
- let score = 0;
261
- let isPaused = false;
262
- let isGameOver = false;
263
- let soundEnabled = true;
264
- let gameLoop;
265
-
266
- // Snake variables
267
- let snake = [];
268
- let snakeLength = 3;
269
- let xVelocity = 0;
270
- let yVelocity = 0;
271
- let nextXVelocity = 0;
272
- let nextYVelocity = 0;
273
 
274
- // Food variables
275
- let foodX = 5;
276
- let foodY = 5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
- // Initialize game
279
- function initGame() {
280
- // Set canvas size
281
- const container = canvas.parentElement;
282
- const size = Math.min(container.clientWidth, container.clientHeight);
283
- canvas.width = size;
284
- canvas.height = size;
285
- tileSize = size / tileCount;
286
-
287
- // Reset game state
288
- score = 0;
289
- scoreDisplay.textContent = score;
290
- isGameOver = false;
291
- isPaused = false;
292
- pauseBtn.innerHTML = '<i class="fas fa-pause text-blue-400"></i>';
293
-
294
- // Reset snake
295
- snake = [];
296
- snakeLength = 3;
297
- for (let i = snakeLength - 1; i >= 0; i--) {
298
- snake.push({ x: i, y: 0 });
299
- }
300
 
301
- // Reset velocity
302
- xVelocity = 1;
303
- yVelocity = 0;
304
- nextXVelocity = 1;
305
- nextYVelocity = 0;
306
 
307
- // Place food
308
- placeFood();
309
 
310
- // Start game loop
311
- if (gameLoop) clearInterval(gameLoop);
312
- gameLoop = setInterval(gameTick, 1000 / speed);
 
 
 
313
  }
314
 
315
- // Game tick
316
- function gameTick() {
317
- if (isPaused || isGameOver) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
- // Update snake direction
320
- xVelocity = nextXVelocity;
321
- yVelocity = nextYVelocity;
 
 
 
 
 
 
 
322
 
323
- // Move snake
324
- const head = { x: snake[0].x + xVelocity, y: snake[0].y + yVelocity };
 
 
 
 
325
 
326
- // Check collision with walls
327
- if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
328
- gameOver();
329
- return;
 
 
 
 
 
 
 
 
 
330
  }
331
 
332
- // Check collision with self
333
- for (let i = 0; i < snake.length; i++) {
334
- if (head.x === snake[i].x && head.y === snake[i].y) {
335
- gameOver();
336
- return;
337
- }
 
 
 
 
 
338
  }
339
 
340
- // Add new head
341
- snake.unshift(head);
 
 
 
 
 
 
 
 
 
342
 
343
- // Check if snake ate food
344
- if (head.x === foodX && head.y === foodY) {
345
- // Play sound
346
- if (soundEnabled) {
347
- eatSound.currentTime = 0;
348
- eatSound.play();
349
- }
 
 
 
 
 
 
 
 
 
 
 
350
 
351
- // Increase score and speed
352
- score += 10;
353
- scoreDisplay.textContent = score;
354
- snakeLength++;
355
 
356
- // Increase speed every 50 points
357
- if (score % 50 === 0 && speed < 15) {
358
- speed += 1;
359
- clearInterval(gameLoop);
360
- gameLoop = setInterval(gameTick, 1000 / speed);
361
  }
362
-
363
- // Place new food
364
- placeFood();
365
- } else {
366
- // Remove tail if no food eaten
367
- snake.pop();
368
- }
369
 
370
- // Play move sound
371
- if (soundEnabled && (xVelocity !== 0 || yVelocity !== 0)) {
372
- moveSound.currentTime = 0;
373
- moveSound.play();
374
- }
375
-
376
- // Draw game
377
- drawGame();
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  }
379
 
380
- // Draw game
381
- function drawGame() {
382
- // Clear canvas
383
- ctx.fillStyle = 'rgba(26, 26, 46, 0.7)';
384
- ctx.fillRect(0, 0, canvas.width, canvas.height);
385
-
386
- // Draw grid
387
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
388
- ctx.lineWidth = 0.5;
389
- for (let i = 0; i < tileCount; i++) {
390
- for (let j = 0; j < tileCount; j++) {
391
- ctx.strokeRect(i * tileSize, j * tileSize, tileSize, tileSize);
392
- }
393
- }
394
 
395
- // Draw snake
396
- for (let i = 0; i < snake.length; i++) {
397
- const segment = snake[i];
398
-
399
- // Head
400
- if (i === 0) {
401
- const gradient = ctx.createLinearGradient(
402
- segment.x * tileSize,
403
- segment.y * tileSize,
404
- segment.x * tileSize + tileSize,
405
- segment.y * tileSize + tileSize
406
- );
407
- gradient.addColorStop(0, '#00b4db');
408
- gradient.addColorStop(1, '#0083b0');
409
- ctx.fillStyle = gradient;
410
- }
411
- // Body
412
- else {
413
- const opacity = 0.7 - (i / snake.length * 0.5);
414
- const gradient = ctx.createLinearGradient(
415
- segment.x * tileSize,
416
- segment.y * tileSize,
417
- segment.x * tileSize + tileSize,
418
- segment.y * tileSize + tileSize
419
- );
420
- gradient.addColorStop(0, `rgba(0, 180, 219, ${opacity})`);
421
- gradient.addColorStop(1, `rgba(0, 131, 176, ${opacity})`);
422
- ctx.fillStyle = gradient;
423
- }
424
-
425
- // Draw segment with rounded corners
426
- const x = segment.x * tileSize;
427
- const y = segment.y * tileSize;
428
- const radius = i === 0 ? 6 : 4;
429
-
430
- ctx.beginPath();
431
- ctx.moveTo(x + radius, y);
432
- ctx.lineTo(x + tileSize - radius, y);
433
- ctx.quadraticCurveTo(x + tileSize, y, x + tileSize, y + radius);
434
- ctx.lineTo(x + tileSize, y + tileSize - radius);
435
- ctx.quadraticCurveTo(x + tileSize, y + tileSize, x + tileSize - radius, y + tileSize);
436
- ctx.lineTo(x + radius, y + tileSize);
437
- ctx.quadraticCurveTo(x, y + tileSize, x, y + tileSize - radius);
438
- ctx.lineTo(x, y + radius);
439
- ctx.quadraticCurveTo(x, y, x + radius, y);
440
- ctx.closePath();
441
- ctx.fill();
442
-
443
- // Add shine effect to head
444
- if (i === 0) {
445
- ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
446
- ctx.beginPath();
447
- ctx.moveTo(x + radius, y);
448
- ctx.lineTo(x + tileSize * 0.3, y + tileSize * 0.3);
449
- ctx.lineTo(x + radius, y + tileSize * 0.3);
450
- ctx.quadraticCurveTo(x, y + tileSize * 0.3, x, y + radius);
451
- ctx.quadraticCurveTo(x, y, x + radius, y);
452
- ctx.closePath();
453
- ctx.fill();
454
- }
455
  }
456
 
457
- // Draw food
458
- const foodGradient = ctx.createRadialGradient(
459
- foodX * tileSize + tileSize / 2,
460
- foodY * tileSize + tileSize / 2,
461
- 0,
462
- foodX * tileSize + tileSize / 2,
463
- foodY * tileSize + tileSize / 2,
464
- tileSize / 2
465
- );
466
- foodGradient.addColorStop(0, '#f5af19');
467
- foodGradient.addColorStop(1, '#f12711');
468
-
469
- ctx.fillStyle = foodGradient;
470
- ctx.beginPath();
471
- ctx.arc(
472
- foodX * tileSize + tileSize / 2,
473
- foodY * tileSize + tileSize / 2,
474
- tileSize / 2 - 2,
475
- 0,
476
- Math.PI * 2
477
- );
478
- ctx.fill();
479
-
480
- // Add shine to food
481
- ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
482
- ctx.beginPath();
483
- ctx.arc(
484
- foodX * tileSize + tileSize / 3,
485
- foodY * tileSize + tileSize / 3,
486
- tileSize / 6,
487
- 0,
488
- Math.PI * 2
489
- );
490
- ctx.fill();
491
  }
492
 
493
- // Place food randomly
494
- function placeFood() {
495
- let validPlacement = false;
 
 
 
 
 
 
 
 
 
 
496
 
497
- while (!validPlacement) {
498
- foodX = Math.floor(Math.random() * tileCount);
499
- foodY = Math.floor(Math.random() * tileCount);
500
-
501
- validPlacement = true;
502
-
503
- // Check if food is on snake
504
- for (let i = 0; i < snake.length; i++) {
505
- if (snake[i].x === foodX && snake[i].y === foodY) {
506
- validPlacement = false;
507
- break;
508
- }
509
- }
510
- }
511
- }
512
 
513
- // Game over
514
- function gameOver() {
515
- isGameOver = true;
516
- clearInterval(gameLoop);
517
-
518
- // Play sound
519
- if (soundEnabled) {
520
- gameOverSound.currentTime = 0;
521
- gameOverSound.play();
522
- }
523
 
524
- // Show game over modal
525
- finalScoreDisplay.textContent = score;
526
- const modalContent = gameOverModal.querySelector('div');
527
- modalContent.classList.remove('scale-95', 'opacity-0');
528
- modalContent.classList.add('scale-100', 'opacity-100');
529
- gameOverModal.classList.remove('hidden');
530
- }
 
 
 
 
 
531
 
532
- // Event listeners
533
- document.addEventListener('keydown', (e) => {
534
- switch (e.key) {
535
- case 'ArrowUp':
536
- if (yVelocity !== 1) {
537
- nextXVelocity = 0;
538
- nextYVelocity = -1;
539
- }
540
- break;
541
- case 'ArrowDown':
542
- if (yVelocity !== -1) {
543
- nextXVelocity = 0;
544
- nextYVelocity = 1;
545
- }
546
- break;
547
- case 'ArrowLeft':
548
- if (xVelocity !== 1) {
549
- nextXVelocity = -1;
550
- nextYVelocity = 0;
551
- }
552
- break;
553
- case 'ArrowRight':
554
- if (xVelocity !== -1) {
555
- nextXVelocity = 1;
556
- nextYVelocity = 0;
557
- }
558
- break;
559
- case ' ':
560
- togglePause();
561
- break;
562
- case 'r':
563
- initGame();
564
- break;
565
- }
566
  });
567
 
568
- // Button controls
569
- upBtn.addEventListener('click', () => {
570
- if (yVelocity !== 1) {
571
- nextXVelocity = 0;
572
- nextYVelocity = -1;
573
- }
574
  });
575
 
576
- downBtn.addEventListener('click', () => {
577
- if (yVelocity !== -1) {
578
- nextXVelocity = 0;
579
- nextYVelocity = 1;
580
- }
 
 
581
  });
582
 
583
- leftBtn.addEventListener('click', () => {
584
- if (xVelocity !== 1) {
585
- nextXVelocity = -1;
586
- nextYVelocity = 0;
587
- }
 
 
 
 
 
 
 
 
 
588
  });
589
 
590
- rightBtn.addEventListener('click', () => {
591
- if (xVelocity !== -1) {
592
- nextXVelocity = 1;
593
- nextYVelocity = 0;
594
- }
595
  });
 
 
 
 
 
 
 
 
 
 
 
596
 
597
- // Toggle pause
598
- function togglePause() {
599
- if (isGameOver) return;
600
-
601
- isPaused = !isPaused;
602
- if (isPaused) {
603
- pauseBtn.innerHTML = '<i class="fas fa-play text-blue-400"></i>';
604
- } else {
605
- pauseBtn.innerHTML = '<i class="fas fa-pause text-blue-400"></i>';
606
- }
607
- }
 
 
 
608
 
609
- pauseBtn.addEventListener('click', togglePause);
 
 
 
 
610
 
611
- // Restart game
612
- restartBtn.addEventListener('click', () => {
613
- initGame();
614
- gameOverModal.classList.add('hidden');
615
  });
616
 
617
- // Toggle sound
618
- soundBtn.addEventListener('click', () => {
619
- soundEnabled = !soundEnabled;
620
- if (soundEnabled) {
621
- soundBtn.innerHTML = '<i class="fas fa-volume-up text-blue-400"></i>';
622
- } else {
623
- soundBtn.innerHTML = '<i class="fas fa-volume-mute text-blue-400"></i>';
624
- }
625
  });
626
 
627
- // Start game
628
- startBtn.addEventListener('click', () => {
629
- startModal.classList.add('hidden');
630
- initGame();
 
 
 
 
 
 
 
 
 
 
631
  });
632
 
633
- // Play again
634
- playAgainBtn.addEventListener('click', () => {
635
- gameOverModal.classList.add('hidden');
636
- initGame();
637
  });
638
 
639
- // Quit game
640
- quitBtn.addEventListener('click', () => {
641
- gameOverModal.classList.add('hidden');
642
- startModal.classList.remove('hidden');
643
  });
644
 
645
- // Initialize canvas size on load and resize
646
- function handleResize() {
647
- if (!isGameOver && !startModal.classList.contains('hidden')) {
648
- const container = canvas.parentElement;
649
- const size = Math.min(container.clientWidth, container.clientHeight);
650
- canvas.width = size;
651
- canvas.height = size;
652
- tileSize = size / tileCount;
653
- drawGame();
654
- }
655
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
 
657
  window.addEventListener('resize', handleResize);
658
-
659
- // Initial setup
660
- handleResize();
661
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  </script>
663
  <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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=engerl/luxury-snake" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
664
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Interactive 3D Solar System</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
9
  <style>
 
 
10
  body {
11
+ overflow: hidden;
12
+ background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
13
+ height: 100vh;
14
+ margin: 0;
15
+ font-family: 'Arial', sans-serif;
16
+ cursor: grab;
17
+ user-select: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
+
20
+ body.grabbing {
21
+ cursor: grabbing;
 
22
  }
23
+
24
+ #stars {
25
+ position: absolute;
26
+ width: 300%;
27
+ height: 300%;
28
+ z-index: 0;
29
+ transform-origin: center;
30
  }
31
+
32
+ .star {
33
+ position: absolute;
34
+ background-color: white;
35
+ border-radius: 50%;
36
+ animation: twinkle var(--duration) infinite ease-in-out;
37
  }
38
+
39
+ @keyframes twinkle {
40
+ 0%, 100% { opacity: 0.2; }
41
+ 50% { opacity: 1; }
 
42
  }
43
+
44
+ .orbit {
45
+ position: absolute;
46
+ border: 1px dashed rgba(255, 255, 255, 0.1);
47
  border-radius: 50%;
48
+ transform-origin: center;
 
49
  }
50
+
51
+ .planet {
52
+ position: absolute;
53
+ border-radius: 50%;
54
+ transform-origin: center;
55
+ cursor: pointer;
56
+ transition: transform 0.3s, box-shadow 0.3s;
57
+ z-index: 5;
58
  }
59
+
60
+ .planet:hover {
61
+ transform: scale(1.5);
62
+ box-shadow: 0 0 15px currentColor;
63
+ z-index: 10;
64
  }
65
+
66
+ .planet-info {
67
+ position: absolute;
68
+ background: rgba(0, 0, 0, 0.8);
69
+ border: 1px solid #444;
70
+ border-radius: 8px;
71
+ padding: 15px;
72
+ color: white;
73
+ max-width: 250px;
74
+ display: none;
75
+ z-index: 100;
76
+ backdrop-filter: blur(5px);
77
+ pointer-events: none;
78
  }
79
+
80
+ .sun {
81
+ position: absolute;
82
+ border-radius: 50%;
83
+ background: radial-gradient(circle at center, #ffde00, #ff8c00, #ff3d00);
84
+ box-shadow: 0 0 60px #ff8c00, 0 0 98px #ff4500;
85
+ z-index: 2;
86
  }
87
+
88
+ .controls {
89
+ position: fixed;
90
+ bottom: 20px;
91
+ left: 50%;
92
+ transform: translateX(-50%);
93
+ background: rgba(0, 0, 0, 0.7);
94
+ padding: 15px;
95
+ border-radius: 10px;
96
+ display: flex;
97
+ gap: 15px;
98
+ align-items: center;
99
+ z-index: 100;
100
+ backdrop-filter: blur(5px);
101
  }
102
+
103
+ .speed-display {
104
+ color: white;
105
+ min-width: 80px;
106
+ text-align: center;
107
  }
108
+
109
+ .title {
110
+ position: fixed;
111
+ top: 20px;
112
+ left: 50%;
113
+ transform: translateX(-50%);
114
+ color: white;
115
+ font-size: 2.5rem;
116
+ text-align: center;
117
+ z-index: 100;
118
+ text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
119
+ background: rgba(0, 0, 0, 0.5);
120
+ padding: 10px 20px;
121
+ border-radius: 10px;
122
+ pointer-events: none;
123
  }
124
+
125
+ .ring {
126
+ position: absolute;
127
  border-radius: 50%;
128
+ opacity: 0.7;
129
+ transform-origin: center;
130
+ pointer-events: none;
 
 
 
131
  }
132
+
133
+ .solar-system-container {
134
+ position: absolute;
135
+ width: 100%;
136
+ height: 100%;
137
+ transform-origin: center;
138
+ transition: transform 0.1s ease-out;
139
  }
140
+
141
+ .tooltip {
142
+ position: absolute;
143
+ background: rgba(0, 0, 0, 0.7);
144
+ color: white;
145
+ padding: 5px 10px;
146
+ border-radius: 5px;
147
+ font-size: 12px;
148
+ pointer-events: none;
149
+ opacity: 0;
150
+ transition: opacity 0.3s;
151
+ z-index: 20;
152
+ }
153
+
154
+ .asteroid {
155
+ position: absolute;
156
+ background-color: #888;
157
+ border-radius: 50%;
158
+ pointer-events: none;
159
+ }
160
+
161
+ .comet {
162
+ position: absolute;
163
+ width: 10px;
164
+ height: 3px;
165
+ background: linear-gradient(to right, rgba(200,200,255,1), transparent);
166
+ transform-origin: center;
167
+ pointer-events: none;
168
  }
169
 
170
+ .drag-indicator {
171
+ position: fixed;
172
+ bottom: 20px;
173
+ left: 20px;
174
+ background: rgba(0, 0, 0, 0.7);
175
+ color: white;
176
+ padding: 10px;
177
+ border-radius: 8px;
178
+ z-index: 100;
179
  }
180
  </style>
181
  </head>
182
+ <body>
183
+ <div class="solar-system-container" id="solarSystem">
184
+ <div id="stars"></div>
185
+ <div class="sun" id="sun"></div>
 
 
186
  </div>
187
+
188
+ <div class="title">Interactive 3D Solar System</div>
189
+ <div id="positionIndicator" class="fixed bottom-20 right-4 bg-black bg-opacity-50 text-white p-2 rounded-md z-50">
190
+ X: 0 | Y: 0 | Zoom: 1x
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  </div>
192
+
193
+ <div class="drag-indicator">
194
+ <i class="fas fa-arrows-alt"></i> Hold left mouse button to drag view
195
+ </div>
196
+
197
+ <div class="controls">
198
+ <button id="slowDown" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors">
199
+ <i class="fas fa-minus"></i> Slower
200
  </button>
201
+ <div class="speed-display" id="speedDisplay">1x</div>
202
+ <button id="speedUp" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors">
203
+ <i class="fas fa-plus"></i> Faster
204
  </button>
205
+ <button id="pauseBtn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition-colors">
206
+ <i class="fas fa-pause"></i> Pause
207
  </button>
208
+ <button id="resetBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded transition-colors">
209
+ <i class="fas fa-sync-alt"></i> Reset
210
+ </button>
211
+ <button id="zoomIn" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded transition-colors">
212
+ <i class="fas fa-search-plus"></i>
213
+ </button>
214
+ <button id="zoomOut" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded transition-colors">
215
+ <i class="fas fa-search-minus"></i>
216
  </button>
217
  </div>
218
 
219
+ <div id="tooltip" class="tooltip"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
+ <script>
222
+ // Configuration
223
+ const config = {
224
+ scale: 0.5, // Initial scale factor for distances
225
+ speed: 1, // Initial speed multiplier
226
+ paused: false,
227
+ zoom: 1,
228
+ offsetX: 0,
229
+ offsetY: 0,
230
+ isDragging: false,
231
+ lastX: 0,
232
+ lastY: 0
233
+ };
 
 
 
 
 
 
 
 
 
 
234
 
235
+ // Planet data (relative sizes and distances)
236
+ const planets = [
237
+ { name: "Mercury", color: "#B8B8B8", size: 4, distance: 58, orbitTime: 88, info: "The smallest planet in our solar system and closest to the Sun." },
238
+ { name: "Venus", color: "#E6C229", size: 9.5, distance: 108, orbitTime: 225, info: "Similar in size to Earth but with a toxic atmosphere of carbon dioxide." },
239
+ { name: "Earth", color: "#6B93D6", size: 10, distance: 150, orbitTime: 365, hasMoon: true, info: "Our home planet, the only known place in the universe confirmed to host life." },
240
+ { name: "Mars", color: "#C1440E", size: 5.3, distance: 228, orbitTime: 687, info: "Known as the Red Planet due to iron oxide on its surface." },
241
+ { name: "Jupiter", color: "#D39C7E", size: 112, distance: 778, orbitTime: 4333, info: "The largest planet in our solar system, a gas giant with a Great Red Spot." },
242
+ { name: "Saturn", color: "#E4D191", size: 94, distance: 1427, orbitTime: 10759, hasRings: true, info: "Famous for its beautiful ring system made of ice and rock." },
243
+ { name: "Uranus", color: "#D1E7E7", size: 40, distance: 2871, orbitTime: 30687, info: "An ice giant that rotates on its side, with a blue-green color." },
244
+ { name: "Neptune", color: "#5B5DDF", size: 38, distance: 4498, orbitTime: 60190, info: "The windiest planet with the strongest winds in the solar system." },
245
+ { name: "Pluto", color: "#D1B7A3", size: 1.8, distance: 5906, orbitTime: 90560, info: "A dwarf planet in the Kuiper belt, smaller than Earth's moon." }
246
+ ];
247
 
248
+ // Create stars
249
+ function createStars() {
250
+ const starsContainer = document.getElementById('stars');
251
+ const starCount = 1000;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ for (let i = 0; i < starCount; i++) {
254
+ const star = document.createElement('div');
255
+ star.className = 'star';
256
+
257
+ // Random position
258
+ const x = Math.random() * 300;
259
+ const y = Math.random() * 300;
260
+
261
+ // Random size (0.5px to 2px)
262
+ const size = Math.random() * 1.5 + 0.5;
263
+
264
+ // Random twinkle duration (3s to 10s)
265
+ const duration = Math.random() * 7 + 3;
266
+
267
+ star.style.left = `${x - 50}%`;
268
+ star.style.top = `${y - 50}%`;
269
+ star.style.width = `${size}px`;
270
+ star.style.height = `${size}px`;
271
+ star.style.setProperty('--duration', `${duration}s`);
272
+
273
+ starsContainer.appendChild(star);
274
+ }
275
+ }
276
+
277
+ // Create asteroid belt
278
+ function createAsteroidBelt() {
279
+ const container = document.getElementById('solarSystem');
280
+ const centerX = window.innerWidth / 2;
281
+ const centerY = window.innerHeight / 2;
282
+ const asteroidCount = 100;
283
 
284
+ for (let i = 0; i < asteroidCount; i++) {
285
+ const asteroid = document.createElement('div');
286
+ asteroid.className = 'asteroid';
287
+
288
+ // Random position in asteroid belt area (between Mars and Jupiter)
289
+ const distance = Math.random() * 230 + 350;
290
+ const angle = Math.random() * Math.PI * 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
+ const x = centerX + Math.cos(angle) * distance * config.scale;
293
+ const y = centerY + Math.sin(angle) * distance * config.scale;
 
 
 
294
 
295
+ // Random size (0.5px to 3px)
296
+ const size = Math.random() * 2.5 + 0.5;
297
 
298
+ asteroid.style.left = `${x}px`;
299
+ asteroid.style.top = `${y}px`;
300
+ asteroid.style.width = `${size}px`;
301
+ asteroid.style.height = `${size}px`;
302
+
303
+ container.appendChild(asteroid);
304
  }
305
 
306
+ // Create a comet
307
+ const comet = document.createElement('div');
308
+ comet.className = 'comet';
309
+ comet.id = 'comet';
310
+ comet.style.left = `${centerX - 500}px`;
311
+ comet.style.top = `${centerY - 300}px`;
312
+ container.appendChild(comet);
313
+ }
314
+
315
+ // Create solar system
316
+ function createSolarSystem() {
317
+ const container = document.getElementById('solarSystem');
318
+ const centerX = window.innerWidth / 2;
319
+ const centerY = window.innerHeight / 2;
320
+
321
+ // Create Sun
322
+ const sun = document.getElementById('sun');
323
+ sun.style.width = '50px';
324
+ sun.style.height = '50px';
325
+ sun.style.left = `${centerX - 25}px`;
326
+ sun.style.top = `${centerY - 25}px`;
327
+
328
+ // Create planets and orbits
329
+ planets.forEach((planet, index) => {
330
+ // Create orbit path
331
+ const orbit = document.createElement('div');
332
+ orbit.className = 'orbit';
333
+ const orbitRadius = planet.distance * config.scale;
334
+ orbit.style.width = `${orbitRadius * 2}px`;
335
+ orbit.style.height = `${orbitRadius * 2}px`;
336
+ orbit.style.left = `${centerX - orbitRadius}px`;
337
+ orbit.style.top = `${centerY - orbitRadius}px`;
338
+ container.appendChild(orbit);
339
 
340
+ // Create planet
341
+ const planetElement = document.createElement('div');
342
+ planetElement.className = 'planet';
343
+ planetElement.id = `planet-${index}`;
344
+ planetElement.style.backgroundColor = planet.color;
345
+ planetElement.style.boxShadow = `0 0 10px ${planet.color}`;
346
+ planetElement.style.color = planet.color; // For glow effect
347
+ const planetSize = planet.size * 0.5;
348
+ planetElement.style.width = `${planetSize}px`;
349
+ planetElement.style.height = `${planetSize}px`;
350
 
351
+ // Initial position
352
+ const angle = Math.random() * Math.PI * 2;
353
+ const x = centerX + Math.cos(angle) * orbitRadius - planetSize / 2;
354
+ const y = centerY + Math.sin(angle) * orbitRadius - planetSize / 2;
355
+ planetElement.style.left = `${x}px`;
356
+ planetElement.style.top = `${y}px`;
357
 
358
+ container.appendChild(planetElement);
359
+
360
+ // Create moon for Earth
361
+ if (planet.hasMoon) {
362
+ const moon = document.createElement('div');
363
+ moon.className = 'planet';
364
+ moon.style.backgroundColor = '#ddd';
365
+ moon.style.width = '3px';
366
+ moon.style.height = '3px';
367
+ moon.style.left = `${x + planetSize/2 + 8}px`;
368
+ moon.style.top = `${y + planetSize/2 - 3}px`;
369
+ container.appendChild(moon);
370
+ planet.moon = moon;
371
  }
372
 
373
+ // Create rings for Saturn
374
+ if (planet.hasRings) {
375
+ const ring = document.createElement('div');
376
+ ring.className = 'ring';
377
+ ring.style.border = `15px solid #C0C0C0`;
378
+ ring.style.width = `${planetSize * 2.5}px`;
379
+ ring.style.height = `${planetSize * 0.5}px`;
380
+ ring.style.left = `${x - planetSize * 0.75}px`;
381
+ ring.style.top = `${y + planetSize * 0.25}px`;
382
+ container.appendChild(ring);
383
+ planet.ringElement = ring;
384
  }
385
 
386
+ // Create info panel
387
+ const infoPanel = document.createElement('div');
388
+ infoPanel.className = 'planet-info';
389
+ infoPanel.id = `info-${index}`;
390
+ infoPanel.innerHTML = `
391
+ <h3 class="font-bold text-lg mb-2">${planet.name}</h3>
392
+ <p class="text-sm mb-1">Distance from Sun: ${planet.distance} million km</p>
393
+ <p class="text-sm mb-1">Orbit Period: ${planet.orbitTime} Earth days</p>
394
+ <p class="text-sm">${planet.info}</p>
395
+ `;
396
+ container.appendChild(infoPanel);
397
 
398
+ // Add hover event for tooltip
399
+ planetElement.addEventListener('mousemove', (e) => {
400
+ const tooltip = document.getElementById('tooltip');
401
+ tooltip.textContent = planet.name;
402
+ tooltip.style.left = `${e.pageX + 15}px`;
403
+ tooltip.style.top = `${e.pageY + 15}px`;
404
+ tooltip.style.opacity = '1';
405
+ });
406
+
407
+ planetElement.addEventListener('mouseleave', () => {
408
+ document.getElementById('tooltip').style.opacity = '0';
409
+ });
410
+
411
+ // Add click event to show info
412
+ planetElement.addEventListener('click', (e) => {
413
+ e.stopPropagation();
414
+ hideAllInfoPanels();
415
+ infoPanel.style.display = 'block';
416
 
417
+ // Position the info panel
418
+ const rect = planetElement.getBoundingClientRect();
419
+ infoPanel.style.left = `${rect.right + 10}px`;
420
+ infoPanel.style.top = `${rect.top}px`;
421
 
422
+ // Adjust if it goes off screen
423
+ if (parseInt(infoPanel.style.left) + infoPanel.offsetWidth > window.innerWidth) {
424
+ infoPanel.style.left = `${rect.left - infoPanel.offsetWidth - 10}px`;
 
 
425
  }
426
+ });
 
 
 
 
 
 
427
 
428
+ // Store planet data for animation
429
+ planet.element = planetElement;
430
+ planet.angle = angle;
431
+ planet.orbitRadius = orbitRadius;
432
+ });
433
+
434
+ // Hide info panels when clicking anywhere else
435
+ document.addEventListener('click', hideAllInfoPanels);
436
+ }
437
+
438
+ function hideAllInfoPanels() {
439
+ document.querySelectorAll('.planet-info').forEach(panel => {
440
+ panel.style.display = 'none';
441
+ });
442
+ }
443
+
444
+ // Animate planets
445
+ function animatePlanets() {
446
+ if (config.paused) {
447
+ requestAnimationFrame(animatePlanets);
448
+ return;
449
  }
450
 
451
+ const container = document.getElementById('solarSystem');
452
+ const centerX = window.innerWidth / 2;
453
+ const centerY = window.innerHeight / 2;
454
+
455
+ planets.forEach((planet) => {
456
+ // Update angle based on orbit time and speed
457
+ planet.angle += (0.01 / planet.orbitTime) * config.speed;
 
 
 
 
 
 
 
458
 
459
+ // Calculate new position (accounting for offset and zoom)
460
+ const effectiveOrbitRadius = planet.orbitRadius * config.zoom;
461
+ const x = centerX + config.offsetX + Math.cos(planet.angle) * effectiveOrbitRadius - planet.element.offsetWidth / 2;
462
+ const y = centerY + config.offsetY + Math.sin(planet.angle) * effectiveOrbitRadius - planet.element.offsetHeight / 2;
463
+
464
+ // Update planet position
465
+ planet.element.style.left = `${x}px`;
466
+ planet.element.style.top = `${y}px`;
467
+
468
+ // Update moon position for Earth
469
+ if (planet.hasMoon) {
470
+ planet.moon.style.left = `${x + planet.size * 0.5 / 2 + 8}px`;
471
+ planet.moon.style.top = `${y + planet.size * 0.5 / 2 - 3}px`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  }
473
 
474
+ // Update ring position for Saturn
475
+ if (planet.hasRings) {
476
+ planet.ringElement.style.left = `${x - planet.size * 0.75 * 0.5}px`;
477
+ planet.ringElement.style.top = `${y + planet.size * 0.25 * 0.5}px`;
478
+ planet.ringElement.style.transform = `rotate(${planet.angle * 2}rad)`;
479
+ }
480
+ });
481
+
482
+ // Animate comet
483
+ const comet = document.getElementById('comet');
484
+ if (comet) {
485
+ const cometAngle = performance.now() * 0.0001;
486
+ const cometDistance = 1000;
487
+ const cometX = centerX + config.offsetX + Math.cos(cometAngle) * cometDistance * config.scale;
488
+ const cometY = centerY + config.offsetY + Math.sin(cometAngle * 1.5) * cometDistance * config.scale;
489
+ comet.style.left = `${cometX}px`;
490
+ comet.style.top = `${cometY}px`;
491
+ comet.style.transform = `rotate(${cometAngle}rad)`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
493
 
494
+ // Update solar system container transform
495
+ container.style.transform = `translate(${config.offsetX}px, ${config.offsetY}px) scale(${config.zoom})`;
496
+
497
+ requestAnimationFrame(animatePlanets);
498
+ }
499
+
500
+ // Handle mouse drag for panning
501
+ function setupDragControls() {
502
+ const container = document.body;
503
+
504
+ container.addEventListener('mousedown', (e) => {
505
+ // Only respond to left mouse button
506
+ if (e.button !== 0) return;
507
 
508
+ if (e.target.closest('button') || e.target.closest('.planet-info')) return;
509
+ config.isDragging = true;
510
+ config.lastX = e.clientX;
511
+ config.lastY = e.clientY;
512
+ container.classList.add('grabbing');
513
+ e.preventDefault();
514
+ });
 
 
 
 
 
 
 
 
515
 
516
+ container.addEventListener('mousemove', (e) => {
517
+ if (!config.isDragging) return;
 
 
 
 
 
 
 
 
518
 
519
+ const dx = e.clientX - config.lastX;
520
+ const dy = e.clientY - config.lastY;
521
+
522
+ config.offsetX += dx;
523
+ config.offsetY += dy;
524
+
525
+ config.lastX = e.clientX;
526
+ config.lastY = e.clientY;
527
+
528
+ updatePositionIndicator();
529
+ e.preventDefault();
530
+ });
531
 
532
+ container.addEventListener('mouseup', (e) => {
533
+ if (e.button !== 0) return;
534
+ config.isDragging = false;
535
+ container.classList.remove('grabbing');
536
+ e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  });
538
 
539
+ container.addEventListener('mouseleave', () => {
540
+ config.isDragging = false;
541
+ container.classList.remove('grabbing');
 
 
 
542
  });
543
 
544
+ // Touch support
545
+ container.addEventListener('touchstart', (e) => {
546
+ if (e.target.closest('button') || e.target.closest('.planet-info')) return;
547
+ config.isDragging = true;
548
+ config.lastX = e.touches[0].clientX;
549
+ config.lastY = e.touches[0].clientY;
550
+ e.preventDefault();
551
  });
552
 
553
+ container.addEventListener('touchmove', (e) => {
554
+ if (!config.isDragging) return;
555
+
556
+ const dx = e.touches[0].clientX - config.lastX;
557
+ const dy = e.touches[0].clientY - config.lastY;
558
+
559
+ config.offsetX += dx;
560
+ config.offsetY += dy;
561
+
562
+ config.lastX = e.touches[0].clientX;
563
+ config.lastY = e.touches[0].clientY;
564
+
565
+ updatePositionIndicator();
566
+ e.preventDefault();
567
  });
568
 
569
+ container.addEventListener('touchend', () => {
570
+ config.isDragging = false;
 
 
 
571
  });
572
+ }
573
+
574
+ function updatePositionIndicator() {
575
+ const indicator = document.getElementById('positionIndicator');
576
+ indicator.textContent = `X: ${Math.round(config.offsetX)} | Y: ${Math.round(config.offsetY)} | Zoom: ${config.zoom.toFixed(1)}x`;
577
+ }
578
+
579
+ // Handle window resize
580
+ function handleResize() {
581
+ const centerX = window.innerWidth / 2;
582
+ const centerY = window.innerHeight / 2;
583
 
584
+ // Update Sun position
585
+ const sun = document.getElementById('sun');
586
+ sun.style.left = `${centerX - 25}px`;
587
+ sun.style.top = `${centerY - 25}px`;
588
+ }
589
+
590
+ // Initialize
591
+ window.addEventListener('load', () => {
592
+ createStars();
593
+ createSolarSystem();
594
+ createAsteroidBelt();
595
+ setupDragControls();
596
+ animatePlanets();
597
+ updatePositionIndicator();
598
 
599
+ // Set up controls
600
+ document.getElementById('speedUp').addEventListener('click', () => {
601
+ config.speed *= 1.5;
602
+ updateControls();
603
+ });
604
 
605
+ document.getElementById('slowDown').addEventListener('click', () => {
606
+ config.speed /= 1.5;
607
+ if (config.speed < 0.01) config.speed = 0.01;
608
+ updateControls();
609
  });
610
 
611
+ document.getElementById('pauseBtn').addEventListener('click', () => {
612
+ config.paused = !config.paused;
613
+ updateControls();
614
+ if (!config.paused) animatePlanets();
 
 
 
 
615
  });
616
 
617
+ document.getElementById('resetBtn').addEventListener('click', () => {
618
+ config.speed = 1;
619
+ config.paused = false;
620
+ config.zoom = 1;
621
+ config.offsetX = 0;
622
+ config.offsetY = 0;
623
+ updateControls();
624
+
625
+ // Reset planet positions to random angles
626
+ planets.forEach(planet => {
627
+ planet.angle = Math.random() * Math.PI * 2;
628
+ });
629
+
630
+ animatePlanets();
631
  });
632
 
633
+ document.getElementById('zoomIn').addEventListener('click', () => {
634
+ config.zoom *= 1.2;
635
+ if (config.zoom > 5) config.zoom = 5;
636
+ updatePositionIndicator();
637
  });
638
 
639
+ document.getElementById('zoomOut').addEventListener('click', () => {
640
+ config.zoom /= 1.2;
641
+ if (config.zoom < 0.5) config.zoom = 0.5;
642
+ updatePositionIndicator();
643
  });
644
 
645
+ // Mouse wheel zoom
646
+ document.addEventListener('wheel', (e) => {
647
+ e.preventDefault();
648
+ const delta = e.deltaY < 0 ? 1.2 : 1/1.2;
649
+
650
+ // Zoom toward mouse position
651
+ const mouseX = e.clientX;
652
+ const mouseY = e.clientY;
653
+
654
+ const newZoom = config.zoom * delta;
655
+ if (newZoom > 5) return;
656
+ if (newZoom < 0.5) return;
657
+
658
+ // Calculate the offset needed to zoom toward mouse position
659
+ const containerX = window.innerWidth / 2;
660
+ const containerY = window.innerHeight / 2;
661
+
662
+ const offsetX = mouseX - containerX;
663
+ const offsetY = mouseY - containerY;
664
+
665
+ config.offsetX = offsetX + (config.offsetX - offsetX) * delta;
666
+ config.offsetY = offsetY + (config.offsetY - offsetY) * delta;
667
+
668
+ config.zoom = newZoom;
669
+ updatePositionIndicator();
670
+ }, { passive: false });
671
 
672
  window.addEventListener('resize', handleResize);
 
 
 
673
  });
674
+
675
+ function updateControls() {
676
+ document.getElementById('speedDisplay').textContent =
677
+ config.speed.toFixed(2) + 'x';
678
+
679
+ const btn = document.getElementById('pauseBtn');
680
+ if (config.paused) {
681
+ btn.innerHTML = '<i class="fas fa-play"></i> Play';
682
+ btn.classList.remove('bg-red-600');
683
+ btn.classList.add('bg-green-600');
684
+ } else {
685
+ btn.innerHTML = '<i class="fas fa-pause"></i> Pause';
686
+ btn.classList.remove('bg-green-600');
687
+ btn.classList.add('bg-red-600');
688
+ }
689
+ }
690
  </script>
691
  <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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=engerl/luxury-snake" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
692
  </html>