HAL1993 commited on
Commit
25cd4fd
·
verified ·
1 Parent(s): 372a3f5

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +935 -1160
index.html CHANGED
@@ -2,62 +2,52 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
6
- <title>Dino Run - Bitcoin Edition</title>
 
7
  <style>
8
- @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Exo+2:wght@300;400;600;700&display=swap');
 
 
 
 
 
 
9
 
10
  * {
11
- margin: 0;
12
- padding: 0;
13
  box-sizing: border-box;
 
 
 
14
  }
15
 
16
- :root {
17
- --primary: #F7931A;
18
- --primary-dark: #E88A0C;
19
- --secondary: #4CAF50;
20
- --accent: #00E5FF;
21
- --dark: #1a1a2e;
22
- --light: #ffffff;
23
- --glass: rgba(255, 255, 255, 0.1);
24
- --glass-border: rgba(255, 255, 255, 0.2);
25
- }
26
-
27
- body {
28
- font-family: 'Exo 2', sans-serif;
29
- background: var(--dark);
30
  overflow: hidden;
31
- touch-action: none;
32
- user-select: none;
 
33
  }
34
 
35
- #gameContainer {
36
  position: relative;
37
- width: 100vw;
38
- height: 100vh;
39
  display: flex;
40
  justify-content: center;
41
  align-items: center;
42
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
43
  }
44
 
45
- #gameCanvas {
46
  display: block;
47
- background: transparent;
48
  }
49
 
50
- .ui-overlay {
51
- position: absolute;
52
- top: 0;
53
- left: 0;
54
- width: 100%;
55
- height: 100%;
56
- pointer-events: none;
57
- z-index: 10;
58
- }
59
-
60
- .screen {
61
  position: absolute;
62
  top: 0;
63
  left: 0;
@@ -67,1256 +57,1041 @@
67
  flex-direction: column;
68
  justify-content: center;
69
  align-items: center;
70
- background: rgba(26, 26, 46, 0.95);
71
- backdrop-filter: blur(10px);
72
- transition: opacity 0.5s ease, transform 0.5s ease;
73
- pointer-events: auto;
74
  }
75
 
76
- .screen.hidden {
77
  opacity: 0;
78
- transform: scale(1.1);
79
  pointer-events: none;
 
80
  }
81
 
82
- .title {
83
- font-family: 'Orbitron', monospace;
84
- font-size: clamp(2rem, 8vw, 5rem);
85
- font-weight: 900;
86
- background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 50%, var(--secondary) 100%);
87
- -webkit-background-clip: text;
88
- -webkit-text-fill-color: transparent;
89
- background-clip: text;
90
- text-shadow: 0 0 60px rgba(247, 147, 26, 0.5);
91
- margin-bottom: 1rem;
92
- animation: titleGlow 2s ease-in-out infinite;
93
- }
94
-
95
- @keyframes titleGlow {
96
- 0%, 100% { filter: brightness(1); }
97
- 50% { filter: brightness(1.3); }
98
- }
99
-
100
- .subtitle {
101
- font-size: clamp(1rem, 3vw, 1.5rem);
102
- color: rgba(255, 255, 255, 0.7);
103
- margin-bottom: 3rem;
104
- letter-spacing: 0.3em;
105
- text-transform: uppercase;
106
  }
107
 
108
- .btn {
109
- font-family: 'Orbitron', monospace;
110
- font-size: clamp(1rem, 2.5vw, 1.3rem);
111
- font-weight: 700;
112
- padding: 1rem 3rem;
113
- border: 2px solid var(--primary);
114
- background: transparent;
115
- color: var(--primary);
116
- cursor: pointer;
117
- position: relative;
118
- overflow: hidden;
119
- transition: all 0.3s ease;
120
- text-transform: uppercase;
121
- letter-spacing: 0.1em;
122
- margin: 0.5rem;
123
  }
124
 
125
- .btn::before {
126
- content: '';
127
- position: absolute;
128
- top: 0;
129
- left: -100%;
130
- width: 100%;
131
- height: 100%;
132
- background: linear-gradient(90deg, transparent, var(--primary), transparent);
133
- transition: left 0.5s ease;
134
  }
135
 
136
- .btn:hover::before {
137
- left: 100%;
 
 
 
 
 
 
 
 
138
  }
139
 
140
- .btn:hover {
141
- background: var(--primary);
142
- color: var(--dark);
143
- box-shadow: 0 0 30px rgba(247, 147, 26, 0.5);
144
- transform: scale(1.05);
 
 
 
 
 
 
 
145
  }
146
 
147
- .btn-secondary {
148
- border-color: var(--accent);
149
- color: var(--accent);
150
  }
151
 
152
- .btn-secondary:hover {
153
- background: var(--accent);
154
- box-shadow: 0 0 30px rgba(0, 229, 255, 0.5);
155
  }
156
 
157
- .hud {
 
158
  position: absolute;
159
  top: 0;
160
  left: 0;
161
  width: 100%;
162
- padding: 1rem 2rem;
163
  display: flex;
164
  justify-content: space-between;
165
  align-items: flex-start;
166
  pointer-events: none;
 
167
  }
168
 
169
- .hud-left, .hud-right {
 
 
 
 
170
  display: flex;
171
  flex-direction: column;
172
- gap: 0.5rem;
173
- }
174
-
175
- .hud-item {
176
- background: var(--glass);
177
- border: 1px solid var(--glass-border);
178
- padding: 0.5rem 1rem;
179
- border-radius: 8px;
180
- backdrop-filter: blur(5px);
181
  }
182
 
183
  .hud-label {
184
  font-size: 0.7rem;
185
- color: rgba(255, 255, 255, 0.6);
186
  text-transform: uppercase;
187
- letter-spacing: 0.1em;
 
188
  }
189
 
190
  .hud-value {
191
- font-family: 'Orbitron', monospace;
192
- font-size: 1.2rem;
193
- font-weight: 700;
194
- color: var(--light);
195
- }
196
-
197
- .hud-value.btc {
198
- color: var(--primary);
199
- }
200
-
201
- .hud-value.speed {
202
- color: var(--accent);
203
- }
204
-
205
- .hud-center {
206
- position: absolute;
207
- top: 1rem;
208
- left: 50%;
209
- transform: translateX(-50%);
210
- display: flex;
211
- gap: 1rem;
212
  }
213
 
214
- .powerup-indicator {
215
- width: 50px;
216
- height: 50px;
217
  border-radius: 50%;
 
 
218
  display: flex;
219
  justify-content: center;
220
  align-items: center;
221
- font-size: 1.5rem;
222
- opacity: 0.3;
223
- transition: all 0.3s ease;
224
- background: var(--glass);
225
- border: 2px solid var(--glass-border);
226
- }
227
-
228
- .powerup-indicator.active {
229
- opacity: 1;
230
- animation: pulse 1s ease-in-out infinite;
231
  }
232
-
233
- .powerup-indicator.magnet { border-color: var(--accent); color: var(--accent); }
234
- .powerup-indicator.shield { border-color: #E91E63; color: #E91E63; }
235
- .powerup-indicator.multiplier { border-color: var(--primary); color: var(--primary); }
236
- .powerup-indicator.speed { border-color: var(--secondary); color: var(--secondary); }
237
-
238
- @keyframes pulse {
239
- 0%, 100% { transform: scale(1); }
240
- 50% { transform: scale(1.1); }
241
  }
242
 
243
- .pause-btn {
 
244
  position: absolute;
245
- top: 1rem;
246
- right: 1rem;
247
- width: 50px;
248
- height: 50px;
249
- border-radius: 50%;
250
- background: var(--glass);
251
- border: 2px solid var(--glass-border);
252
- color: white;
253
- font-size: 1.5rem;
254
- cursor: pointer;
255
- pointer-events: auto;
256
- transition: all 0.3s ease;
257
- display: flex;
258
- justify-content: center;
259
- align-items: center;
260
  }
261
-
262
- .pause-btn:hover {
263
- background: var(--primary);
264
- border-color: var(--primary);
265
  }
266
 
267
- .controls-hint {
 
268
  position: absolute;
269
- bottom: 2rem;
270
  left: 50%;
271
  transform: translateX(-50%);
272
  display: flex;
273
- gap: 2rem;
274
- color: rgba(255, 255, 255, 0.5);
275
- font-size: 0.8rem;
276
- }
277
-
278
- .control-key {
279
- display: flex;
280
- align-items: center;
281
- gap: 0.5rem;
282
- }
283
-
284
- .key {
285
- background: var(--glass);
286
- border: 1px solid var(--glass-border);
287
- padding: 0.3rem 0.6rem;
288
- border-radius: 4px;
289
- font-family: 'Orbitron', monospace;
290
- font-size: 0.7rem;
291
- }
292
-
293
- .final-score {
294
- font-family: 'Orbitron', monospace;
295
- font-size: clamp(1.5rem, 5vw, 3rem);
296
- color: var(--primary);
297
- margin: 1rem 0;
298
- }
299
-
300
- .final-btc {
301
- font-family: 'Orbitron', monospace;
302
- font-size: clamp(1rem, 3vw, 1.5rem);
303
- color: var(--accent);
304
- margin-bottom: 2rem;
305
- }
306
-
307
- .game-over-stats {
308
- display: flex;
309
- gap: 2rem;
310
- margin-bottom: 2rem;
311
- }
312
-
313
- .stat-item {
314
- text-align: center;
315
- }
316
-
317
- .stat-value {
318
- font-family: 'Orbitron', monospace;
319
- font-size: 1.5rem;
320
- color: white;
321
- }
322
-
323
- .stat-label {
324
- font-size: 0.8rem;
325
- color: rgba(255, 255, 255, 0.6);
326
- }
327
-
328
- .new-record {
329
- color: var(--secondary);
330
- font-family: 'Orbitron', monospace;
331
- animation: recordPulse 0.5s ease-in-out infinite;
332
- }
333
-
334
- @keyframes recordPulse {
335
- 0%, 100% { transform: scale(1); }
336
- 50% { transform: scale(1.05); }
337
- }
338
-
339
- .mobile-controls {
340
- display: none;
341
- position: absolute;
342
- bottom: 2rem;
343
- left: 0;
344
- width: 100%;
345
- justify-content: space-between;
346
- padding: 0 2rem;
347
- pointer-events: auto;
348
- }
349
-
350
- @media (max-width: 768px) {
351
- .mobile-controls {
352
- display: flex;
353
- }
354
- .controls-hint {
355
- display: none;
356
- }
357
  }
358
-
359
- .mobile-btn {
360
- width: 70px;
361
- height: 70px;
 
362
  border-radius: 50%;
363
- background: var(--glass);
364
- border: 2px solid var(--glass-border);
365
- color: white;
366
- font-size: 1.5rem;
367
  display: flex;
368
  justify-content: center;
369
  align-items: center;
370
- transition: all 0.2s ease;
371
- }
372
-
373
- .mobile-btn:active {
374
- transform: scale(0.9);
375
- background: var(--primary);
376
- border-color: var(--primary);
377
- }
378
-
379
- .mobile-btn.vertical {
380
- flex-direction: column;
381
- gap: 0.5rem;
382
- }
383
-
384
- .powered-by {
385
- position: absolute;
386
- bottom: 1rem;
387
- left: 50%;
388
- transform: translateX(-50%);
389
- font-size: 0.8rem;
390
- color: rgba(255, 255, 255, 0.4);
391
- text-decoration: none;
392
- transition: color 0.3s ease;
393
- }
394
-
395
- .powered-by:hover {
396
- color: var(--primary);
397
- }
398
-
399
- .combo-display {
400
- position: absolute;
401
- top: 50%;
402
- left: 50%;
403
- transform: translate(-50%, -50%);
404
- font-family: 'Orbitron', monospace;
405
- font-size: 2rem;
406
- color: var(--primary);
407
- text-shadow: 0 0 20px var(--primary);
408
  opacity: 0;
409
- transition: all 0.3s ease;
410
- pointer-events: none;
411
  }
412
-
413
- .combo-display.show {
414
  opacity: 1;
415
- animation: comboAnim 0.5s ease-out;
416
  }
417
 
418
- @keyframes comboAnim {
419
- 0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0; }
420
- 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 1; }
421
- 100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }
422
  }
 
423
  </style>
424
  </head>
425
  <body>
426
- <div id="gameContainer">
427
- <canvas id="gameCanvas"></canvas>
428
-
429
- <!-- Start Screen -->
430
- <div id="startScreen" class="screen">
431
- <h1 class="title">DINO RUN</h1>
432
- <p class="subtitle">Bitcoin Edition</p>
433
- <button class="btn" id="startBtn">START GAME</button>
434
- <div class="controls-hint">
435
- <div class="control-key"><span class="key">←</span><span class="key">→</span> Move</div>
436
- <div class="control-key"><span class="key">↑</span> Jump</div>
437
- <div class="control-key"><span class="key">↓</span> Slide</div>
438
- </div>
439
- </div>
440
 
441
- <!-- HUD -->
442
- <div id="hud" class="ui-overlay" style="display: none;">
443
- <div class="hud">
444
- <div class="hud-left">
445
- <div class="hud-item">
446
- <div class="hud-label">Distance</div>
447
- <div class="hud-value" id="distanceDisplay">0m</div>
448
- </div>
449
- </div>
450
- <div class="hud-center">
451
- <div class="powerup-indicator magnet" id="magnetIndicator">🧲</div>
452
- <div class="powerup-indicator shield" id="shieldIndicator">🛡️</div>
453
- <div class="powerup-indicator multiplier" id="multiplierIndicator">×2</div>
454
- <div class="powerup-indicator speed" id="speedIndicator">⚡</div>
455
- </div>
456
- <div class="hud-right">
457
- <div class="hud-item">
458
- <div class="hud-label">Balance</div>
459
- <div class="hud-value btc" id="btcDisplay">₿ 0.00000000</div>
460
- </div>
461
- <div class="hud-item">
462
- <div class="hud-label">Speed</div>
463
- <div class="hud-value speed" id="speedDisplay">0 km/h</div>
464
- </div>
465
- </div>
466
- </div>
467
- <button class="pause-btn" id="pauseBtn">⏸</button>
468
-
469
- <div class="mobile-controls">
470
- <div class="mobile-btn vertical">
471
- <span id="mobileJump">↑</span>
472
- <span>Jump</span>
473
- </div>
474
- <div class="mobile-btn" id="mobileLeft">←</div>
475
- <div class="mobile-btn" id="mobileRight">→</div>
476
- <div class="mobile-btn vertical">
477
- <span id="mobileSlide">↓</span>
478
- <span>Slide</span>
479
- </div>
480
- </div>
481
-
482
- <div class="combo-display" id="comboDisplay"></div>
483
- </div>
484
 
485
- <!-- Pause Screen -->
486
- <div id="pauseScreen" class="screen hidden">
487
- <h1 class="title">PAUSED</h1>
488
- <button class="btn" id="resumeBtn">RESUME</button>
489
- <button class="btn btn-secondary" id="quitBtn">QUIT</button>
490
  </div>
491
-
492
- <!-- Game Over Screen -->
493
- <div id="gameOverScreen" class="screen hidden">
494
- <h1 class="title">GAME OVER</h1>
495
- <div class="game-over-stats">
496
- <div class="stat-item">
497
- <div class="stat-value" id="finalDistance">0m</div>
498
- <div class="stat-label">Distance</div>
499
- </div>
500
- <div class="stat-item">
501
- <div class="stat-value" id="finalBtc">₿ 0.00000000</div>
502
- <div class="stat-label">Earned</div>
503
- </div>
504
- </div>
505
- <div class="final-score" id="newRecordText" style="display: none;">NEW RECORD!</div>
506
- <button class="btn" id="restartBtn">PLAY AGAIN</button>
507
- <button class="btn btn-secondary" id="homeBtn">HOME</button>
508
  </div>
 
509
 
510
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="powered-by" target="_blank">Built with anycoder</a>
 
 
 
511
  </div>
512
 
513
- <script>
514
- // ============================================
515
- // GAME CONFIGURATION
516
- // ============================================
517
- const CONFIG = {
518
- CANVAS_WIDTH: 1200,
519
- CANVAS_HEIGHT: 700,
520
- LANE_COUNT: 3,
521
- LANE_WIDTH: 200,
522
- PLAYER_WIDTH: 80,
523
- PLAYER_HEIGHT: 100,
524
- BASE_SPEED: 400,
525
- MAX_SPEED: 1200,
526
- SPEED_INCREMENT: 10,
527
- GRAVITY: 1800,
528
- JUMP_FORCE: -650,
529
- SLIDE_DURATION: 600,
530
- COIN_VALUE: 0.00000001,
531
- BONUS_COIN_VALUE: 0.00000005,
532
- SPAWN_INTERVAL_BASE: 1500,
533
- POWERUP_CHANCE: 0.08,
534
- MAGNET_DURATION: 8000,
535
- SHIELD_DURATION: 10000,
536
- MULTIPLIER_DURATION: 10000,
537
- SPEED_BOOST_DURATION: 5000,
538
- COIN_SPIN_SPEED: 3,
539
- PARTICLE_COUNT: 5
540
- };
541
 
542
- // ============================================
543
- // GAME STATE
544
- // ============================================
545
- const game = {
546
- canvas: null,
547
- ctx: null,
548
- width: 0,
549
- height: 0,
550
- state: 'start', // start, playing, paused, gameover
551
- deltaTime: 0,
552
- lastTime: 0,
553
- distance: 0,
554
- btcBalance: 0,
555
- speed: CONFIG.BASE_SPEED,
556
- currentLane: 1,
557
- targetLane: 1,
558
- player: {
559
- x: 0,
560
- y: 0,
561
- width: CONFIG.PLAYER_WIDTH,
562
- height: CONFIG.PLAYER_HEIGHT,
563
- velocityY: 0,
564
- isJumping: false,
565
- isSliding: false,
566
- slideTimer: 0,
567
- animFrame: 0,
568
- animTimer: 0,
569
- runCycle: 0
570
- },
571
- obstacles: [],
572
- coins: [],
573
- powerups: [],
574
- particles: [],
575
- background: {
576
- layers: [],
577
- groundOffset: 0
578
- },
579
- effects: {
580
- shake: 0,
581
- speedLines: []
582
- },
583
- powerups: {
584
- magnet: { active: false, timer: 0 },
585
- shield: { active: false, timer: 0 },
586
- multiplier: { active: false, timer: 0 },
587
- speedBoost: { active: false, timer: 0 }
588
- },
589
- combo: {
590
- count: 0,
591
- timer: 0,
592
- multiplier: 1
593
- },
594
- spawnTimer: 0,
595
- highScore: 0,
596
- keys: {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  };
 
 
598
 
599
- // ============================================
600
- // INITIALIZATION
601
- // ============================================
602
- function init() {
603
- game.canvas = document.getElementById('gameCanvas');
604
- game.ctx = game.canvas.getContext('2d');
605
-
606
- resizeCanvas();
607
- window.addEventListener('resize', resizeCanvas);
608
-
609
- setupEventListeners();
610
- initBackground();
611
-
612
- game.lastTime = performance.now();
613
- requestAnimationFrame(gameLoop);
614
- }
615
-
616
- function resizeCanvas() {
617
- const container = document.getElementById('gameContainer');
618
- const aspectRatio = CONFIG.CANVAS_WIDTH / CONFIG.CANVAS_HEIGHT;
619
-
620
- let width = container.clientWidth;
621
- let height = container.clientHeight;
622
-
623
- if (width / height > aspectRatio) {
624
- width = height * aspectRatio;
625
- } else {
626
- height = width / aspectRatio;
627
- }
628
-
629
- game.canvas.width = CONFIG.CANVAS_WIDTH;
630
- game.canvas.height = CONFIG.CANVAS_HEIGHT;
631
- game.canvas.style.width = width + 'px';
632
- game.canvas.style.height = height + 'px';
633
-
634
- game.width = CONFIG.CANVAS_WIDTH;
635
- game.height = CONFIG.CANVAS_HEIGHT;
636
- }
637
-
638
- function setupEventListeners() {
639
- // Keyboard
640
- window.addEventListener('keydown', (e) => {
641
- game.keys[e.code] = true;
642
- if (game.state === 'playing') {
643
- handleInput(e.code);
644
- }
645
- });
646
-
647
- window.addEventListener('keyup', (e) => {
648
- game.keys[e.code] = false;
649
- });
650
-
651
- // Buttons
652
- document.getElementById('startBtn').addEventListener('click', startGame);
653
- document.getElementById('pauseBtn').addEventListener('click', pauseGame);
654
- document.getElementById('resumeBtn').addEventListener('click', resumeGame);
655
- document.getElementById('quitBtn').addEventListener('click', quitToMenu);
656
- document.getElementById('restartBtn').addEventListener('click', restartGame);
657
- document.getElementById('homeBtn').addEventListener('click', quitToMenu);
658
-
659
- // Mobile controls
660
- document.getElementById('mobileLeft').addEventListener('touchstart', () => {
661
- if (game.state === 'playing') changeLane(-1);
662
- });
663
- document.getElementById('mobileRight').addEventListener('touchstart', () => {
664
- if (game.state === 'playing') changeLane(1);
665
- });
666
- document.getElementById('mobileJump').parentElement.addEventListener('touchstart', (e) => {
667
- e.preventDefault();
668
- if (game.state === 'playing') jump();
669
- });
670
- document.getElementById('mobileSlide').parentElement.addEventListener('touchstart', (e) => {
671
- e.preventDefault();
672
- if (game.state === 'playing') slide();
673
- });
674
- }
675
-
676
- // ============================================
677
- // INPUT HANDLING
678
- // ============================================
679
- function handleInput(code) {
680
- switch(code) {
681
- case 'ArrowLeft':
682
- case 'KeyA':
683
- changeLane(-1);
684
- break;
685
- case 'ArrowRight':
686
- case 'KeyD':
687
- changeLane(1);
688
- break;
689
- case 'ArrowUp':
690
- case 'KeyW':
691
- case 'Space':
692
- jump();
693
- break;
694
- case 'ArrowDown':
695
- case 'KeyS':
696
- slide();
697
- break;
698
- case 'Escape':
699
- pauseGame();
700
- break;
701
  }
702
- }
703
-
704
- function changeLane(direction) {
705
- const newLane = game.targetLane + direction;
706
- if (newLane >= 0 && newLane < CONFIG.LANE_COUNT) {
707
- game.targetLane = newLane;
708
  }
709
  }
710
-
711
- function jump() {
712
- if (!game.player.isJumping && !game.player.isSliding) {
713
- game.player.isJumping = true;
714
- game.player.velocityY = CONFIG.JUMP_FORCE;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  }
716
  }
717
 
718
- function slide() {
719
- if (!game.player.isJumping && !game.player.isSliding) {
720
- game.player.isSliding = true;
721
- game.player.slideTimer = CONFIG.SLIDE_DURATION;
 
722
  }
723
  }
724
 
725
- // ============================================
726
- // GAME STATE MANAGEMENT
727
- // ============================================
728
- function startGame() {
729
- document.getElementById('startScreen').classList.add('hidden');
730
- document.getElementById('hud').style.display = 'block';
731
- resetGame();
732
- game.state = 'playing';
733
- }
734
 
735
- function pauseGame() {
736
- if (game.state === 'playing') {
737
- game.state = 'paused';
738
- document.getElementById('pauseScreen').classList.remove('hidden');
739
- }
740
  }
 
741
 
742
- function resumeGame() {
743
- game.state = 'playing';
744
- document.getElementById('pauseScreen').classList.add('hidden');
745
- game.lastTime = performance.now();
 
746
  }
 
747
 
748
- function quitToMenu() {
749
- game.state = 'start';
750
- document.getElementById('pauseScreen').classList.add('hidden');
751
- document.getElementById('gameOverScreen').classList.add('hidden');
752
- document.getElementById('hud').style.display = 'none';
753
- document.getElementById('startScreen').classList.remove('hidden');
754
  }
 
755
 
756
- function restartGame() {
757
- document.getElementById('gameOverScreen').classList.add('hidden');
758
- resetGame();
759
- game.state = 'playing';
760
- }
 
761
 
762
- function gameOver() {
763
- game.state = 'gameover';
764
-
765
- const isNewRecord = game.distance > game.highScore;
766
- if (isNewRecord) {
767
- game.highScore = Math.floor(game.distance);
768
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
 
770
- document.getElementById('finalDistance').textContent = Math.floor(game.distance) + 'm';
771
- document.getElementById('finalBtc').textContent = '₿ ' + game.btcBalance.toFixed(8);
772
- document.getElementById('newRecordText').style.display = isNewRecord ? 'block' : 'none';
 
 
773
 
774
- document.getElementById('gameOverScreen').classList.remove('hidden');
 
 
 
 
775
  }
776
 
777
- function resetGame() {
778
- game.distance = 0;
779
- game.btcBalance = 0;
780
- game.speed = CONFIG.BASE_SPEED;
781
- game.currentLane = 1;
782
- game.targetLane = 1;
783
- game.spawnTimer = 0;
784
- game.effects.shake = 0;
785
- game.combo = { count: 0, timer: 0, multiplier: 1 };
786
-
787
- game.player = {
788
- x: getLaneX(1),
789
- y: game.height - 180,
790
- width: CONFIG.PLAYER_WIDTH,
791
- height: CONFIG.PLAYER_HEIGHT,
792
- velocityY: 0,
793
- isJumping: false,
794
- isSliding: false,
795
- slideTimer: 0,
796
- animFrame: 0,
797
- animTimer: 0,
798
- runCycle: 0
799
- };
800
-
801
- game.obstacles = [];
802
- game.coins = [];
803
- game.powerups = {
804
- magnet: { active: false, timer: 0 },
805
- shield: { active: false, timer: 0 },
806
- multiplier: { active: false, timer: 0 },
807
- speedBoost: { active: false, timer: 0 }
808
- };
809
- game.particles = [];
810
-
811
- updateHUD();
812
- }
813
 
814
- // ============================================
815
- // GAME LOOP
816
- // ============================================
817
- function gameLoop(timestamp) {
818
- game.deltaTime = Math.min((timestamp - game.lastTime) / 1000, 0.1);
819
- game.lastTime = timestamp;
820
-
821
- if (game.state === 'playing') {
822
- update();
823
- render();
824
- }
825
-
826
- requestAnimationFrame(gameLoop);
827
- }
828
 
829
- function update() {
830
- // Update speed
831
- if (game.powerups.speedBoost.active) {
832
- game.speed = CONFIG.MAX_SPEED * 1.3;
833
- } else {
834
- game.speed = Math.min(CONFIG.MAX_SPEED, CONFIG.BASE_SPEED + game.distance * 0.5);
835
- }
836
-
837
- // Update distance
838
- game.distance += game.speed * game.deltaTime / 60;
839
-
840
- // Update player
841
- updatePlayer();
842
-
843
- // Update lane position
844
- const targetX = getLaneX(game.targetLane);
845
- game.player.x += (targetX - game.player.x) * 10 * game.deltaTime;
846
- game.currentLane = game.player.x;
847
-
848
- // Spawn objects
849
- game.spawnTimer -= game.deltaTime * 1000;
850
- if (game.spawnTimer <= 0) {
851
- spawnObjects();
852
- const spawnInterval = Math.max(800, CONFIG.SPAWN_INTERVAL_BASE - game.distance * 0.5);
853
- game.spawnTimer = spawnInterval;
854
- }
855
-
856
- // Update objects
857
- updateObstacles();
858
- updateCoins();
859
- updatePowerups();
860
- updateParticles();
861
-
862
- // Update powerup timers
863
- updatePowerupTimers();
864
-
865
- // Update combo
866
- if (game.combo.timer > 0) {
867
- game.combo.timer -= game.deltaTime * 1000;
868
- } else {
869
- game.combo.count = 0;
870
- game.combo.multiplier = 1;
871
- }
872
-
873
- // Update background
874
- updateBackground();
875
-
876
- // Update screen shake
877
- if (game.effects.shake > 0) {
878
- game.effects.shake *= 0.9;
879
- }
880
-
881
- // Speed lines
882
- if (game.speed > CONFIG.BASE_SPEED * 1.5) {
883
- if (Math.random() < 0.3) {
884
- game.effects.speedLines.push({
885
- x: game.width,
886
- y: Math.random() * game.height,
887
- length: 50 + Math.random() * 100,
888
- speed: 1500 + Math.random() * 500,
889
- alpha: 0.3 + Math.random() * 0.4
890
- });
891
- }
892
- }
893
-
894
- game.effects.speedLines = game.effects.speedLines.filter(line => {
895
- line.x -= line.speed * game.deltaTime;
896
- return line.x + line.length > 0;
897
- });
898
-
899
- // Check collisions
900
- checkCollisions();
901
-
902
- // Update HUD
903
- updateHUD();
904
  }
905
 
906
- function updatePlayer() {
907
- const groundY = game.height - 180;
908
-
909
- // Jump physics
910
- if (game.player.isJumping) {
911
- game.player.velocityY += CONFIG.GRAVITY * game.deltaTime;
912
- game.player.y += game.player.velocityY * game.deltaTime;
913
-
914
- if (game.player.y >= groundY) {
915
- game.player.y = groundY;
916
- game.player.isJumping = false;
917
- game.player.velocityY = 0;
918
- }
919
- } else {
920
- game.player.y = groundY;
921
- }
922
-
923
- // Slide
924
- if (game.player.isSliding) {
925
- game.player.slideTimer -= game.deltaTime * 1000;
926
- if (game.player.slideTimer <= 0) {
927
- game.player.isSliding = false;
928
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
929
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
930
 
931
- // Animation
932
- game.player.animTimer += game.deltaTime * 1000;
933
- if (game.player.animTimer > 100) {
934
- game.player.animTimer = 0;
935
- game.player.runCycle = (game.player.runCycle + 0.2) % (Math.PI * 2);
936
  }
937
  }
 
938
 
939
- function getLaneX(lane) {
940
- const totalWidth = CONFIG.LANE_COUNT * CONFIG.LANE_WIDTH;
941
- const startX = (game.width - totalWidth) / 2;
942
- return startX + lane * CONFIG.LANE_WIDTH + CONFIG.LANE_WIDTH / 2;
943
- }
944
 
945
- // ============================================
946
- // SPAWNING
947
- // ============================================
948
- function spawnObjects() {
949
- const lane = Math.floor(Math.random() * CONFIG.LANE_COUNT);
950
- const type = Math.random();
951
-
952
- // Spawn obstacle
953
- if (type < 0.6) {
954
- spawnObstacle(lane);
955
- }
956
-
957
- // Spawn coins
958
- if (Math.random() < 0.7) {
959
- spawnCoinPattern(lane);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  }
961
-
962
- // Spawn powerup
963
- if (Math.random() < CONFIG.POWERUP_CHANCE) {
964
- spawnPowerup(lane);
965
  }
966
- }
967
-
968
- function spawnObstacle(lane) {
969
- const obstacleTypes = ['barrier', 'barrier_tall', 'gap'];
970
- const type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
971
-
972
- game.obstacles.push({
973
- x: getLaneX(lane),
974
- y: game.height - 180,
975
- width: type === 'barrier_tall' ? 60 : 80,
976
- height: type === 'barrier_tall' ? 120 : 80,
977
- type: type,
978
- lane: lane,
979
- passed: false
980
- });
981
- }
982
-
983
- function spawnCoinPattern(lane) {
984
- const pattern = Math.floor(Math.random() * 3);
985
- const startX = getLaneX(lane);
986
-
987
- if (pattern === 0) {
988
- // Horizontal line
989
- for (let i = 0; i < 5; i++) {
990
- game.coins.push({
991
- x: startX + (i - 2) * 50,
992
- y: game.height - 200 - Math.random() * 100,
993
- radius: 25,
994
- value: CONFIG.COIN_VALUE,
995
- rotation: Math.random() * Math.PI * 2,
996
- glow: 0
997
- });
998
- }
999
- } else if (pattern === 1) {
1000
- // Arc
1001
- for (let i = 0; i < 5; i++) {
1002
- const angle = (i / 4) * Math.PI - Math.PI / 2;
1003
- game.coins.push({
1004
- x: startX + Math.cos(angle) * 80,
1005
- y: game.height - 200 + Math.sin(angle) * 50,
1006
- radius: 25,
1007
- value: CONFIG.COIN_VALUE,
1008
- rotation: Math.random() * Math.PI * 2,
1009
- glow: 0
1010
- });
1011
- }
1012
- } else {
1013
- // Single bonus coin
1014
- game.coins.push({
1015
- x: startX,
1016
- y: game.height - 250,
1017
- radius: 35,
1018
- value: CONFIG.BONUS_COIN_VALUE,
1019
- rotation: Math.random() * Math.PI * 2,
1020
- glow: 1
1021
- });
1022
  }
1023
- }
1024
-
1025
- function spawnPowerup(lane) {
1026
- const types = ['magnet', 'shield', 'multiplier', 'speedBoost'];
1027
- const type = types[Math.floor(Math.random() * types.length)];
1028
-
1029
- game.powerups.push({
1030
- x: getLaneX(lane),
1031
- y: game.height - 250,
1032
- width: 50,
1033
- height: 50,
1034
- type: type,
1035
- rotation: 0,
1036
- bounce: 0
1037
- });
1038
- }
1039
-
1040
- // ============================================
1041
- // UPDATE OBJECTS
1042
- // ============================================
1043
- function updateObstacles() {
1044
- game.obstacles = game.obstacles.filter(obs => {
1045
- obs.x -= game.speed * game.deltaTime;
1046
-
1047
- // Remove if off screen
1048
- if (obs.x < -100) {
1049
- return false;
1050
- }
1051
-
1052
- // Check if passed
1053
- if (!obs.passed && obs.x < game.player.x - 50) {
1054
- obs.passed = true;
1055
- addCombo();
1056
- }
1057
-
1058
- return true;
1059
- });
1060
- }
1061
-
1062
- function updateCoins() {
1063
- game.coins = game.coins.filter(coin => {
1064
- coin.x -= game.speed * game.deltaTime;
1065
- coin.rotation += CONFIG.COIN_SPIN_SPEED * game.deltaTime;
1066
- coin.glow = (coin.glow + 0.1) % (Math.PI * 2);
1067
-
1068
- // Magnet effect
1069
- if (game.powerups.magnet.active) {
1070
- const dx = game.player.x - coin.x;
1071
- const dy = (game.player.y + game.player.height / 2) - coin.y;
1072
- const dist = Math.sqrt(dx * dx + dy * dy);
1073
-
1074
- if (dist < 300) {
1075
- coin.x += dx * 0.1;
1076
- coin.y += dy * 0.1;
1077
- }
1078
- }
1079
-
1080
- // Remove if off screen
1081
- return coin.x > -50;
1082
- });
1083
- }
1084
-
1085
- function updatePowerups() {
1086
- game.powerups = game.powerups.filter(pu => {
1087
- pu.x -= game.speed * game.deltaTime;
1088
- pu.rotation += 2 * game.deltaTime;
1089
- pu.bounce += 5 * game.deltaTime;
1090
-
1091
- return pu.x > -50;
1092
- });
1093
- }
1094
 
1095
- function updateParticles() {
1096
- game.particles = game.particles.filter(p => {
1097
- p.x += p.vx * game.deltaTime;
1098
- p.y += p.vy * game.deltaTime;
1099
- p.life -= game.deltaTime;
1100
- p.vy += 500 * game.deltaTime;
1101
- return p.life > 0;
1102
- });
1103
- }
1104
-
1105
- function updatePowerupTimers() {
1106
- const updatePowerup = (name, duration) => {
1107
- if (game.powerups[name].active) {
1108
- game.powerups[name].timer -= game.deltaTime * 1000;
1109
- if (game.powerups[name].timer <= 0) {
1110
- game.powerups[name].active = false;
1111
- }
 
 
1112
  }
1113
- };
1114
-
1115
- updatePowerup('magnet', CONFIG.MAGNET_DURATION);
1116
- updatePowerup('shield', CONFIG.SHIELD_DURATION);
1117
- updatePowerup('multiplier', CONFIG.MULTIPLIER_DURATION);
1118
- updatePowerup('speedBoost', CONFIG.SPEED_BOOST_DURATION);
1119
-
1120
- // Update indicators
1121
- document.getElementById('magnetIndicator').classList.toggle('active', game.powerups.magnet.active);
1122
- document.getElementById('shieldIndicator').classList.toggle('active', game.powerups.shield.active);
1123
- document.getElementById('multiplierIndicator').classList.toggle('active', game.powerups.multiplier.active);
1124
- document.getElementById('speedIndicator').classList.toggle('active', game.powerups.speedBoost.active);
1125
- }
1126
-
1127
- function addCombo() {
1128
- game.combo.count++;
1129
- game.combo.timer = 2000;
1130
- game.combo.multiplier = Math.min(5, 1 + Math.floor(game.combo.count / 5));
1131
-
1132
- if (game.combo.count > 0 && game.combo.count % 5 === 0) {
1133
- showCombo(game.combo.multiplier + 'x COMBO!');
1134
  }
1135
  }
1136
 
1137
- function showCombo(text) {
1138
- const comboEl = document.getElementById('comboDisplay');
1139
- comboEl.textContent = text;
1140
- comboEl.classList.remove('show');
1141
- void comboEl.offsetWidth;
1142
- comboEl.classList.add('show');
1143
- }
1144
-
1145
- // ============================================
1146
- // COLLISION DETECTION
1147
- // ============================================
1148
- function checkCollisions() {
1149
- const playerBox = {
1150
- x: game.player.x - game.player.width / 2,
1151
- y: game.player.y,
1152
- width: game.player.width,
1153
- height: game.player.isSliding ? game.player.height / 2 : game.player.height
1154
- };
1155
-
1156
- // Check obstacle collisions
1157
- for (const obs of game.obstacles) {
1158
- const obsBox = {
1159
- x: obs.x - obs.width / 2,
1160
- y: obs.y - obs.height,
1161
- width: obs.width,
1162
- height: obs.height
1163
- };
1164
 
1165
- if (boxCollision(playerBox, obsBox)) {
1166
- if (game.powerups.shield.active) {
1167
- // Use shield
1168
- game.powerups.shield.active = false;
1169
- game.effects.shake = 10;
1170
- createParticles(obs.x, obs.y - obs.height / 2, '#E91E63', 10);
1171
- } else {
1172
- gameOver();
1173
- return;
1174
- }
1175
- }
1176
  }
1177
-
1178
- // Check coin collisions
1179
- game.coins = game.coins.filter(coin => {
1180
- const dx = coin.x - game.player.x;
1181
- const dy = coin.y - (game.player.y + game.player.height / 2);
1182
- const dist = Math.sqrt(dx * dx + dy * dy);
1183
-
1184
- if (dist < coin.radius + 40) {
1185
- // Collect coin
1186
- let value = coin.value;
1187
- if (game.powerups.multiplier.active) {
1188
- value *= 2;
1189
- }
1190
- value *= game.combo.multiplier;
1191
-
1192
- game.btcBalance += value;
1193
- createParticles(coin.x, coin.y, '#F7931A', CONFIG.PARTICLE_COUNT);
1194
- return false;
1195
- }
1196
-
1197
- return true;
1198
- });
1199
-
1200
- // Check powerup collisions
1201
- game.powerups = game.powerups.filter(pu => {
1202
- const dx = pu.x - game.player.x;
1203
- const dy = pu.y - (game.player.y + game.player.height / 2);
1204
- const dist = Math.sqrt(dx * dx + dy * dy);
1205
-
1206
- if (dist < 50) {
1207
- activatePowerup(pu.type);
1208
- createParticles(pu.x, pu.y, getPowerupColor(pu.type), 15);
1209
- return false;
1210
- }
1211
-
1212
- return true;
1213
- });
1214
- }
1215
-
1216
- function boxCollision(a, b) {
1217
- return a.x < b.x + b.width &&
1218
- a.x + a.width > b.x &&
1219
- a.y < b.y + b.height &&
1220
- a.y + a.height > b.y;
1221
  }
1222
 
1223
- function activatePowerup(type) {
1224
- switch(type) {
1225
- case 'magnet':
1226
- game.powerups.magnet.active = true;
1227
- game.powerups.magnet.timer = CONFIG.MAGNET_DURATION;
1228
- break;
1229
- case 'shield':
1230
- game.powerups.shield.active = true;
1231
- game.powerups.shield.timer = CONFIG.SHIELD_DURATION;
1232
- break;
1233
- case 'multiplier':
1234
- game.powerups.multiplier.active = true;
1235
- game.powerups.multiplier.timer = CONFIG.MULTIPLIER_DURATION;
1236
- break;
1237
- case 'speedBoost':
1238
- game.powerups.speedBoost.active = true;
1239
- game.powerups.speedBoost.timer = CONFIG.SPEED_BOOST_DURATION;
1240
- break;
1241
  }
1242
  }
 
1243
 
1244
- function getPowerupColor(type) {
1245
- const colors = {
1246
- magnet: '#00E5FF',
1247
- shield: '#E91E63',
1248
- multiplier: '#F7931A',
1249
- speedBoost: '#4CAF50'
1250
- };
1251
- return colors[type] || '#ffffff';
1252
- }
1253
-
1254
- function createParticles(x, y, color, count) {
1255
- for (let i = 0; i < count; i++) {
1256
- const angle = (Math.PI * 2 * i) / count + Math.random() * 0.5;
1257
- const speed = 200 + Math.random() * 300;
1258
- game.particles.push({
1259
- x: x,
1260
- y: y,
1261
- vx: Math.cos(angle) * speed,
1262
- vy: Math.sin(angle) * speed - 200,
1263
- color: color,
1264
- size: 3 + Math.random() * 5,
1265
- life: 1
1266
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1267
  }
1268
  }
 
1269
 
1270
- // ============================================
1271
- // BACKGROUND
1272
- // ============================================
1273
- function initBackground() {
1274
- game.background.layers = [
1275
- { color: '#1a1a3e', speed: 0.2, y: 0, height: game.height },
1276
- { color: '#252550', speed: 0.4, y: game.height * 0.3, height: game.height * 0.7 },
1277
- { color: '#2d2d5a', speed: 0.6, y: game.height * 0.5, height: game.height * 0.5 }
1278
- ];
1279
- }
1280
-
1281
- function updateBackground() {
1282
- game.background.groundOffset += game.speed * game.deltaTime;
1283
- if (game.background.groundOffset > 100) {
1284
- game.background.groundOffset = 0;
1285
- }
1286
- }
1287
 
1288
- // ============================================
1289
- // RENDERING
1290
- // ============================================
1291
- function render() {
1292
- const ctx = game.ctx;
1293
-
1294
- // Apply screen shake
1295
- ctx.save();
1296
- if (game.effects.shake > 0.5) {
1297
- ctx.translate(
1298
- (Math.random() - 0.5) * game.effects.shake,
1299
- (Math.random() - 0.5) * game.effects.shake
1300
- );
1301
- }
1302
-
1303
- // Clear
1304
- ctx.fillStyle = '#1a1a2e';
1305
- ctx.fillRect(0, 0, game.width, game.height);
1306
-
1307
- // Draw background
1308
- renderBackground(ctx);
1309
-
1310
- // Draw lanes
1311
- renderLanes(ctx);
1312
-
1313
- // Draw ground
1314
- renderGround(ctx);
1315
-
1316
- // Draw obstacles
1317
- renderObstacles(ctx);
1318
-
1319
- // Draw coins
1320
- renderCoins(ctx);
1321
-
1322
- // Draw
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Bitcoin Dash: Dino Runner</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Fredoka+One&family=Press+Start+2P&display=swap" rel="stylesheet">
8
  <style>
9
+ :root {
10
+ --primary: #4CAF50;
11
+ --accent: #FFD700;
12
+ --bg-dark: #1a1a2e;
13
+ --ui-bg: rgba(0, 0, 0, 0.85);
14
+ --text-color: #ffffff;
15
+ }
16
 
17
  * {
 
 
18
  box-sizing: border-box;
19
+ user-select: none;
20
+ -webkit-user-select: none;
21
+ touch-action: none;
22
  }
23
 
24
+ body, html {
25
+ margin: 0;
26
+ padding: 0;
27
+ width: 100%;
28
+ height: 100%;
 
 
 
 
 
 
 
 
 
29
  overflow: hidden;
30
+ background-color: var(--bg-dark);
31
+ font-family: 'Fredoka One', cursive;
32
+ color: var(--text-color);
33
  }
34
 
35
+ #game-container {
36
  position: relative;
37
+ width: 100%;
38
+ height: 100%;
39
  display: flex;
40
  justify-content: center;
41
  align-items: center;
 
42
  }
43
 
44
+ canvas {
45
  display: block;
46
+ box-shadow: 0 0 50px rgba(0, 0, 0, 0.5);
47
  }
48
 
49
+ /* UI Overlays */
50
+ .ui-screen {
 
 
 
 
 
 
 
 
 
51
  position: absolute;
52
  top: 0;
53
  left: 0;
 
57
  flex-direction: column;
58
  justify-content: center;
59
  align-items: center;
60
+ background: rgba(0, 0, 0, 0.6);
61
+ backdrop-filter: blur(5px);
62
+ z-index: 10;
63
+ transition: opacity 0.3s ease;
64
  }
65
 
66
+ .hidden {
67
  opacity: 0;
 
68
  pointer-events: none;
69
+ z-index: -1;
70
  }
71
 
72
+ h1 {
73
+ font-size: 3rem;
74
+ color: var(--primary);
75
+ text-shadow: 4px 4px 0px #2E7D32;
76
+ margin-bottom: 0.5rem;
77
+ text-align: center;
78
+ line-height: 1.2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
+ .btc-display {
82
+ font-family: 'Press Start 2P', monospace;
83
+ font-size: 1.5rem;
84
+ color: var(--accent);
85
+ margin: 1rem 0;
86
+ text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
+ .instructions {
90
+ font-size: 1rem;
91
+ color: #ccc;
92
+ margin-bottom: 2rem;
93
+ text-align: center;
94
+ line-height: 1.6;
95
+ max-width: 600px;
96
+ padding: 0 20px;
 
97
  }
98
 
99
+ .key-badge {
100
+ display: inline-block;
101
+ background: #444;
102
+ padding: 4px 8px;
103
+ border-radius: 4px;
104
+ border-bottom: 2px solid #222;
105
+ color: white;
106
+ font-family: sans-serif;
107
+ font-size: 0.8rem;
108
+ margin: 0 2px;
109
  }
110
 
111
+ button {
112
+ background: linear-gradient(180deg, #4CAF50 0%, #2E7D32 100%);
113
+ border: none;
114
+ border-radius: 50px;
115
+ padding: 15px 40px;
116
+ font-size: 1.5rem;
117
+ color: white;
118
+ font-family: 'Fredoka One', cursive;
119
+ cursor: pointer;
120
+ box-shadow: 0 6px 0 #1B5E20, 0 10px 10px rgba(0,0,0,0.3);
121
+ transition: transform 0.1s, box-shadow 0.1s;
122
+ text-transform: uppercase;
123
  }
124
 
125
+ button:active {
126
+ transform: translateY(4px);
127
+ box-shadow: 0 2px 0 #1B5E20, 0 4px 4px rgba(0,0,0,0.3);
128
  }
129
 
130
+ button:hover {
131
+ filter: brightness(1.1);
 
132
  }
133
 
134
+ /* HUD */
135
+ #hud {
136
  position: absolute;
137
  top: 0;
138
  left: 0;
139
  width: 100%;
140
+ padding: 15px 20px;
141
  display: flex;
142
  justify-content: space-between;
143
  align-items: flex-start;
144
  pointer-events: none;
145
+ z-index: 5;
146
  }
147
 
148
+ .hud-item {
149
+ background: rgba(0, 0, 0, 0.5);
150
+ padding: 8px 15px;
151
+ border-radius: 12px;
152
+ border: 1px solid rgba(255, 255, 255, 0.1);
153
  display: flex;
154
  flex-direction: column;
155
+ align-items: center;
 
 
 
 
 
 
 
 
156
  }
157
 
158
  .hud-label {
159
  font-size: 0.7rem;
 
160
  text-transform: uppercase;
161
+ color: #aaa;
162
+ letter-spacing: 1px;
163
  }
164
 
165
  .hud-value {
166
+ font-family: 'Press Start 2P', monospace;
167
+ font-size: 1rem;
168
+ color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
+ #pause-btn {
172
+ pointer-events: auto;
173
+ background: rgba(255, 255, 255, 0.2);
174
  border-radius: 50%;
175
+ width: 40px;
176
+ height: 40px;
177
  display: flex;
178
  justify-content: center;
179
  align-items: center;
180
+ font-size: 1.2rem;
181
+ padding: 0;
182
+ box-shadow: none;
183
+ border: 2px solid white;
 
 
 
 
 
 
184
  }
185
+
186
+ #pause-btn:active {
187
+ transform: scale(0.95);
 
 
 
 
 
 
188
  }
189
 
190
+ /* Built with anycoder */
191
+ .footer-link {
192
  position: absolute;
193
+ bottom: 10px;
194
+ right: 10px;
195
+ font-size: 0.7rem;
196
+ color: rgba(255, 255, 255, 0.4);
197
+ text-decoration: none;
198
+ z-index: 20;
 
 
 
 
 
 
 
 
 
199
  }
200
+
201
+ .footer-link:hover {
202
+ color: rgba(255, 255, 255, 0.8);
 
203
  }
204
 
205
+ /* Power-up Indicators */
206
+ .powerup-indicator {
207
  position: absolute;
208
+ top: 80px;
209
  left: 50%;
210
  transform: translateX(-50%);
211
  display: flex;
212
+ gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  }
214
+
215
+ .pu-icon {
216
+ width: 30px;
217
+ height: 30px;
218
+ background: rgba(0,0,0,0.6);
219
  border-radius: 50%;
 
 
 
 
220
  display: flex;
221
  justify-content: center;
222
  align-items: center;
223
+ font-size: 14px;
224
+ border: 2px solid rgba(255,255,255,0.3);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  opacity: 0;
226
+ transition: opacity 0.3s;
 
227
  }
228
+
229
+ .pu-icon.active {
230
  opacity: 1;
231
+ animation: pulse 1s infinite;
232
  }
233
 
234
+ @keyframes pulse {
235
+ 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 215, 0, 0.7); }
236
+ 70% { transform: scale(1.1); box-shadow: 0 0 0 10px rgba(255, 215, 0, 0); }
237
+ 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 215, 0, 0); }
238
  }
239
+
240
  </style>
241
  </head>
242
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
+ <div id="game-container">
245
+ <canvas id="gameCanvas"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ <!-- HUD -->
248
+ <div id="hud" class="hidden">
249
+ <div class="hud-item">
250
+ <span class="hud-label">Distance</span>
251
+ <span class="hud-value" id="dist-val">0m</span>
252
  </div>
253
+ <div id="pause-btn">❚❚</div>
254
+ <div class="hud-item">
255
+ <span class="hud-label">BTC Balance</span>
256
+ <span class="hud-value" id="btc-val">0.00000000</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  </div>
258
+ </div>
259
 
260
+ <div id="powerup-container" class="powerup-indicator hidden">
261
+ <div id="pu-shield" class="pu-icon" title="Shield">🛡️</div>
262
+ <div id="pu-magnet" class="pu-icon" title="Magnet">🧲</div>
263
+ <div id="pu-mul" class="pu-icon" title="Multiplier x2">2x</div>
264
  </div>
265
 
266
+ <!-- Start Screen -->
267
+ <div id="start-screen" class="ui-screen">
268
+ <h1>BITCOIN DASH</h1>
269
+ <div class="btc-display" id="start-btc-display">0.00000000</div>
270
+ <div class="instructions">
271
+ <p>RUN, DUCK, & JUMP to collect Bitcoin.</p>
272
+ <p>Use <span class="key-badge">←</span> <span class="key-badge">→</span> to Switch Lanes</p>
273
+ <p>Press <span class="key-badge">↑</span> or <span class="key-badge">SPACE</span> to Jump</p>
274
+ <p>Press <span class="key-badge">↓</span> to Slide</p>
275
+ <p style="margin-top:10px; color: var(--primary)">Avoid barriers and gaps!</p>
276
+ </div>
277
+ <button id="start-btn">START RUN</button>
278
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
+ <!-- Game Over Screen -->
281
+ <div id="game-over-screen" class="ui-screen hidden">
282
+ <h1 style="color: #ff5252; text-shadow: 4px 4px 0 #b71c1c;">CRASHED!</h1>
283
+ <div class="btc-display" id="final-btc-display">0.00000000</div>
284
+ <p style="margin: 10px 0 20px 0; font-size: 1.2rem;">Distance: <span id="final-dist">0</span>m</p>
285
+ <button id="restart-btn">TRY AGAIN</button>
286
+ </div>
287
+
288
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="footer-link">Built with anycoder</a>
289
+ </div>
290
+
291
+ <script>
292
+ /**
293
+ * BITCOIN DASH - AAA Game Engine
294
+ * Pure HTML5 Canvas Implementation
295
+ */
296
+
297
+ // --- CONFIGURATION & CONSTANTS ---
298
+ const CONSTANTS = {
299
+ LANE_COUNT: 3,
300
+ LANE_WIDTH_BASE: 100, // Will be scaled by screen width
301
+ RUN_SPEED_BASE: 0.5, // World units per frame
302
+ GRAVITY: 0.015,
303
+ JUMP_FORCE: -0.35,
304
+ SLIDE_DURATION: 40,
305
+ COIN_VALUE: 0.00000001,
306
+ BONUS_COIN_VALUE: 0.00000005,
307
+ COLORS: {
308
+ PLAYER: '#4CAF50',
309
+ PLAYER_OUTLINE: '#2E7D32',
310
+ COIN: '#FFD700',
311
+ COIN_OUTLINE: '#F57F17',
312
+ OBSTACLE: '#263238',
313
+ OBSTACLE_ACCENT: '#546E7A',
314
+ GROUND: '#ECEFF1',
315
+ SKY: '#87CEEB'
316
+ }
317
+ };
318
+
319
+ // --- UTILS ---
320
+ const Utils = {
321
+ random: (min, max) => Math.random() * (max - min) + min,
322
+ randomInt: (min, max) => Math.floor(Math.random() * (max - min + 1) + min),
323
+ lerp: (start, end, t) => start * (1 - t) + end * t,
324
+ clamp: (val, min, max) => Math.min(Math.max(val, min), max),
325
+ formatBTC: (num) => num.toFixed(8),
326
+ // Draw Bitcoin Symbol
327
+ drawBitcoinSymbol: (ctx, x, y, size, rotation) => {
328
+ ctx.save();
329
+ ctx.translate(x, y);
330
+ ctx.rotate(rotation);
331
+ ctx.font = `${size}px Arial`;
332
+ ctx.textAlign = 'center';
333
+ ctx.textBaseline = 'middle';
334
+ ctx.fillStyle = CONSTANTS.COLORS.COIN;
335
+ ctx.shadowColor = 'rgba(0,0,0,0.3)';
336
+ ctx.shadowBlur = 5;
337
+ ctx.fillText('₿', 0, 0);
338
+ ctx.restore();
339
+ }
340
+ };
341
+
342
+ // --- INPUT HANDLER ---
343
+ class InputHandler {
344
+ constructor() {
345
+ this.keys = {
346
+ left: false,
347
+ right: false,
348
+ up: false,
349
+ down: false
350
  };
351
+ this.touchStartX = 0;
352
+ this.touchStartY = 0;
353
 
354
+ window.addEventListener('keydown', (e) => this.handleKey(e, true));
355
+ window.addEventListener('keyup', (e) => this.handleKey(e, false));
356
+
357
+ // Touch handling
358
+ window.addEventListener('touchstart', (e) => {
359
+ this.touchStartX = e.touches[0].clientX;
360
+ this.touchStartY = e.touches[0].clientY;
361
+ }, {passive: false});
362
+
363
+ window.addEventListener('touchend', (e) => {
364
+ if (!game.isPlaying) return;
365
+ const touchEndX = e.changedTouches[0].clientX;
366
+ const touchEndY = e.changedTouches[0].clientY;
367
+ this.handleSwipe(touchEndX, touchEndY);
368
+ }, {passive: false});
369
+ }
370
+
371
+ handleKey(e, isDown) {
372
+ switch(e.code) {
373
+ case 'ArrowLeft':
374
+ case 'KeyA': this.keys.left = isDown; break;
375
+ case 'ArrowRight':
376
+ case 'KeyD': this.keys.right = isDown; break;
377
+ case 'ArrowUp':
378
+ case 'Space':
379
+ case 'KeyW':
380
+ if (isDown && !this.keys.up) game.player.jump();
381
+ this.keys.up = isDown;
382
+ break;
383
+ case 'ArrowDown':
384
+ case 'KeyS':
385
+ if (isDown && !this.keys.down) game.player.slide();
386
+ this.keys.down = isDown;
387
+ break;
388
+ }
389
+ }
390
+
391
+ handleSwipe(endX, endY) {
392
+ const dx = endX - this.touchStartX;
393
+ const dy = endY - this.touchStartY;
394
+ const absDx = Math.abs(dx);
395
+ const absDy = Math.abs(dy);
396
+
397
+ if (absDx > absDy) {
398
+ // Horizontal
399
+ if (absDx > 30) {
400
+ if (dx > 0) game.player.switchLane(1);
401
+ else game.player.switchLane(-1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  }
403
+ } else {
404
+ // Vertical
405
+ if (absDy > 30) {
406
+ if (dy > 0) game.player.slide();
407
+ else game.player.jump();
 
408
  }
409
  }
410
+ }
411
+ }
412
+
413
+ // --- PARTICLE SYSTEM ---
414
+ class Particle {
415
+ constructor(x, y, color, speed) {
416
+ this.x = x;
417
+ this.y = y;
418
+ this.color = color;
419
+ this.size = Utils.random(2, 5);
420
+ this.speedX = Utils.random(-speed, speed);
421
+ this.speedY = Utils.random(-speed, speed);
422
+ this.life = 1.0;
423
+ this.decay = Utils.random(0.02, 0.05);
424
+ }
425
+
426
+ update() {
427
+ this.x += this.speedX;
428
+ this.y += this.speedY;
429
+ this.life -= this.decay;
430
+ this.size *= 0.95;
431
+ }
432
+
433
+ draw(ctx) {
434
+ ctx.globalAlpha = this.life;
435
+ ctx.fillStyle = this.color;
436
+ ctx.beginPath();
437
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
438
+ ctx.fill();
439
+ ctx.globalAlpha = 1.0;
440
+ }
441
+ }
442
+
443
+ // --- ENTITIES ---
444
+
445
+ class Player {
446
+ constructor(game) {
447
+ this.game = game;
448
+ this.width = 40;
449
+ this.height = 60;
450
+ this.lane = 1; // 0: Left, 1: Center, 2: Right
451
+ this.targetX = 0;
452
+ this.x = 0;
453
+ this.y = 0;
454
+ this.vy = 0;
455
+ this.isJumping = false;
456
+ this.isSliding = false;
457
+ this.slideTimer = 0;
458
+ this.color = CONSTANTS.COLORS.PLAYER;
459
+
460
+ // Animation properties
461
+ this.runCycle = 0;
462
+ }
463
+
464
+ update() {
465
+ // Lane Switching Logic
466
+ const laneWidth = this.game.laneWidth;
467
+ const targetLX = (this.lane - 1) * laneWidth;
468
+ this.targetX = Utils.lerp(this.x, targetLX, 0.2);
469
+ this.x = this.targetX;
470
+
471
+ // Jump Physics
472
+ if (this.isJumping) {
473
+ this.y += this.vy;
474
+ this.vy += CONSTANTS.GRAVITY;
475
+
476
+ if (this.y >= 0) {
477
+ this.y = 0;
478
+ this.isJumping = false;
479
+ this.vy = 0;
480
  }
481
  }
482
 
483
+ // Slide Logic
484
+ if (this.isSliding) {
485
+ this.slideTimer--;
486
+ if (this.slideTimer <= 0) {
487
+ this.isSliding = false;
488
  }
489
  }
490
 
491
+ // Run Animation
492
+ this.runCycle += 0.15 + (this.game.speed / 1000);
493
+ }
 
 
 
 
 
 
494
 
495
+ jump() {
496
+ if (!this.isJumping && !this.isSliding) {
497
+ this.isJumping = true;
498
+ this.vy = CONSTANTS.JUMP_FORCE;
499
+ this.game.spawnParticles(this.x + this.width/2, this.y + this.height, 5, '#fff');
500
  }
501
+ }
502
 
503
+ slide() {
504
+ if (!this.isJumping && !this.isSliding) {
505
+ this.isSliding = true;
506
+ this.slideTimer = CONSTANTS.SLIDE_DURATION;
507
+ this.height = 30; // Shrink height
508
  }
509
+ }
510
 
511
+ switchLane(dir) {
512
+ const newLane = this.lane + dir;
513
+ if (newLane >= 0 && newLane < CONSTANTS.LANE_COUNT) {
514
+ this.lane = newLane;
 
 
515
  }
516
+ }
517
 
518
+ draw(ctx) {
519
+ ctx.save();
520
+
521
+ // Position calculation
522
+ const drawX = this.x + this.width / 2;
523
+ const drawY = this.game.groundY - this.height + this.y;
524
 
525
+ // Squash and stretch based on state
526
+ let scaleX = 1;
527
+ let scaleY = 1;
528
+
529
+ if (this.isJumping) {
530
+ scaleX = 0.8;
531
+ scaleY = 1.2;
532
+ } else if (this.isSliding) {
533
+ scaleX = 0.8;
534
+ scaleY = 0.5;
535
+ } else {
536
+ // Run bobbing
537
+ const bob = Math.sin(this.runCycle) * 2;
538
+ ctx.translate(0, bob);
539
+ }
540
+
541
+ ctx.translate(drawX, drawY);
542
+ ctx.scale(scaleX, scaleY);
543
+
544
+ // Draw Dino (Stylized)
545
+ // Body
546
+ ctx.fillStyle = this.color;
547
+ ctx.strokeStyle = CONSTANTS.COLORS.PLAYER_OUTLINE;
548
+ ctx.lineWidth = 3;
549
+
550
+ // Round body
551
+ ctx.beginPath();
552
+ ctx.ellipse(0, 10, 20, 25, 0, 0, Math.PI * 2);
553
+ ctx.fill();
554
+ ctx.stroke();
555
+
556
+ // Head
557
+ ctx.beginPath();
558
+ ctx.arc(15, -10, 18, 0, Math.PI * 2);
559
+ ctx.fill();
560
+ ctx.stroke();
561
+
562
+ // Eyes
563
+ ctx.fillStyle = 'white';
564
+ ctx.beginPath();
565
+ ctx.arc(20, -15, 6, 0, Math.PI * 2);
566
+ ctx.fill();
567
+ ctx.stroke();
568
+
569
+ ctx.fillStyle = 'black';
570
+ ctx.beginPath();
571
+ ctx.arc(22, -15, 2.5, 0, Math.PI * 2);
572
+ ctx.fill();
573
+
574
+ // Headband with Bitcoin
575
+ ctx.fillStyle = '#FFD700'; // Gold
576
+ ctx.beginPath();
577
+ ctx.ellipse(15, -10, 18, 8, 0, 0, Math.PI * 2);
578
+ ctx.fill();
579
+ ctx.stroke();
580
+
581
+ // Bitcoin Symbol on Headband
582
+ ctx.save();
583
+ ctx.rotate(Math.PI / 4);
584
+ ctx.fillStyle = '#000';
585
+ ctx.font = 'bold 14px Arial';
586
+ ctx.textAlign = 'center';
587
+ ctx.textBaseline = 'middle';
588
+ ctx.fillText('₿', 0, 0);
589
+ ctx.restore();
590
+
591
+ // Legs (Simple animation)
592
+ if (!this.isSliding) {
593
+ ctx.strokeStyle = this.color;
594
+ ctx.lineWidth = 4;
595
+ ctx.lineCap = 'round';
596
+ const legOffset = Math.sin(this.runCycle) * 10;
597
 
598
+ // Back Leg
599
+ ctx.beginPath();
600
+ ctx.moveTo(-5, 25);
601
+ ctx.lineTo(-5 - legOffset, 40);
602
+ ctx.stroke();
603
 
604
+ // Front Leg
605
+ ctx.beginPath();
606
+ ctx.moveTo(5, 25);
607
+ ctx.lineTo(5 + (legOffset * -1), 40);
608
+ ctx.stroke();
609
  }
610
 
611
+ // Tail
612
+ ctx.beginPath();
613
+ ctx.moveTo(-20, 10);
614
+ ctx.quadraticCurveTo(-35, 5, -40, 20);
615
+ ctx.stroke();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
 
617
+ ctx.restore();
 
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
+ // Shield Effect
620
+ if (this.game.playerPowerups.shield) {
621
+ ctx.save();
622
+ ctx.strokeStyle = 'rgba(100, 200, 255, 0.6)';
623
+ ctx.lineWidth = 3;
624
+ ctx.setLineDash([5, 5]);
625
+ ctx.beginPath();
626
+ ctx.arc(drawX, drawY + this.height/2, 45, 0, Math.PI * 2);
627
+ ctx.stroke();
628
+ ctx.restore();
629
+ }
630
+ }
631
+
632
+ getBounds() {
633
+ // Return collision box
634
+ let h = this.height;
635
+ let y = this.y;
636
+
637
+ // If sliding, hitbox is lower (ducking under bars)
638
+ if (this.isSliding) {
639
+ h = 30;
640
+ y = this.y + 30; // Shift up
641
+ }
642
+
643
+ // If jumping, hitbox is smaller (don't hit low bars)
644
+ if (this.isJumping) {
645
+ h = 30;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  }
647
 
648
+ return {
649
+ x: this.x + 10,
650
+ y: y + 10,
651
+ width: this.width - 20,
652
+ height: h - 20
653
+ };
654
+ }
655
+ }
656
+
657
+ class Obstacle {
658
+ constructor(game, lane, type) {
659
+ this.game = game;
660
+ this.lane = lane;
661
+ this.x = 0;
662
+ this.y = 0;
663
+ this.type = type; // 'barrier', 'low', 'high', 'gap'
664
+ this.passed = false;
665
+
666
+ // Dimensions
667
+ this.width = game.laneWidth * 0.8;
668
+ this.height = type === 'gap' ? 0 : 50;
669
+
670
+ // Positioning
671
+ const laneW = game.laneWidth;
672
+ this.x = (lane - 1) * laneW + (laneW - this.width) / 2;
673
+ this.y = game.groundY - this.height;
674
+ }
675
+
676
+ update() {
677
+ this.y -= this.game.speed;
678
+ }
679
+
680
+ draw(ctx) {
681
+ if (this.type === 'gap') {
682
+ // Draw hole in ground
683
+ ctx.fillStyle = '#1a1a2e'; // Sky color
684
+ ctx.fillRect(this.x, this.y, this.width, 20);
685
+ // Edge
686
+ ctx.fillStyle = '#546E7A';
687
+ ctx.fillRect(this.x, this.y + 20, this.width, 5);
688
+ return;
689
+ }
690
+
691
+ ctx.fillStyle = CONSTANTS.COLORS.OBSTACLE;
692
+ ctx.strokeStyle = CONSTANTS.COLORS.OBSTACLE_ACCENT;
693
+ ctx.lineWidth = 3;
694
+
695
+ // Draw based on type
696
+ if (this.type === 'low') {
697
+ // Low barrier (jumpable)
698
+ ctx.fillRect(this.x, this.y, this.width, 20);
699
+ ctx.strokeRect(this.x, this.y, this.width, 20);
700
+ // Decorative stripes
701
+ ctx.fillStyle = '#37474F';
702
+ ctx.fillRect(this.x + 5, this.y + 5, this.width - 10, 10);
703
+ } else if (this.type === 'high') {
704
+ // High barrier (cannot duck under, must jump)
705
+ ctx.fillRect(this.x, this.y, this.width, this.height);
706
+ ctx.strokeRect(this.x, this.y, this.width, this.height);
707
+ // Warning stripes
708
+ ctx.strokeStyle = '#FF5252';
709
+ ctx.beginPath();
710
+ for(let i=0; i<this.width; i+=20) {
711
+ ctx.moveTo(this.x + i, this.y);
712
+ ctx.lineTo(this.x + i + 10, this.y + this.height);
713
  }
714
+ ctx.stroke();
715
+ } else {
716
+ // Standard barrier
717
+ ctx.fillRect(this.x, this.y, this.width, this.height);
718
+ ctx.strokeRect(this.x, this.y, this.width, this.height);
719
+ // Gold trim
720
+ ctx.fillStyle = '#B0BEC5';
721
+ ctx.fillRect(this.x + 5, this.y + 5, this.width - 10, 5);
722
+ }
723
+ }
724
+
725
+ getBounds() {
726
+ return { x: this.x, y: this.y, width: this.width, height: this.height };
727
+ }
728
+ }
729
+
730
+ class Coin {
731
+ constructor(game, lane, isBonus) {
732
+ this.game = game;
733
+ this.lane = lane;
734
+ this.isBonus = isBonus;
735
+ this.x = 0;
736
+ this.y = 0;
737
+ this.size = isBonus ? 25 : 15;
738
+ this.rotation = 0;
739
+ this.collected = false;
740
+
741
+ const laneW = game.laneWidth;
742
+ this.x = (lane - 1) * laneW + laneW / 2;
743
+ this.y = game.groundY - 80; // Float slightly
744
+ }
745
+
746
+ update() {
747
+ this.y -= this.game.speed;
748
+ this.rotation += 0.1;
749
+
750
+ // Magnet effect
751
+ if (this.game.playerPowerups.magnet && !this.collected) {
752
+ const pBounds = this.game.player.getBounds();
753
+ const dx = (pBounds.x + pBounds.width/2) - (this.x);
754
+ const dy = (pBounds.y) - (this.y);
755
+ const dist = Math.sqrt(dx*dx + dy*dy);
756
 
757
+ if (dist < 150) {
758
+ this.x += dx * 0.1;
759
+ this.y += dy * 0.1;
 
 
760
  }
761
  }
762
+ }
763
 
764
+ draw(ctx) {
765
+ if (this.collected) return;
 
 
 
766
 
767
+ ctx.save();
768
+ ctx.translate(this.x, this.y);
769
+
770
+ // Glow
771
+ ctx.shadowColor = CONSTANTS.COLORS.COIN;
772
+ ctx.shadowBlur = 15;
773
+
774
+ Utils.drawBitcoinSymbol(ctx, 0, 0, this.size, this.rotation);
775
+
776
+ ctx.restore();
777
+ }
778
+
779
+ getBounds() {
780
+ return { x: this.x - this.size/2, y: this.y - this.size/2, width: this.size, height: this.size };
781
+ }
782
+ }
783
+
784
+ class PowerUp {
785
+ constructor(game, lane, type) {
786
+ this.game = game;
787
+ this.lane = lane;
788
+ this.type = type; // 'shield', 'magnet', 'mul'
789
+ this.x = 0;
790
+ this.y = 0;
791
+ this.width = 30;
792
+ this.height = 30;
793
+ this.timer = 0;
794
+ this.collected = false;
795
+
796
+ const laneW = game.laneWidth;
797
+ this.x = (lane - 1) * laneW + laneW / 2;
798
+ this.y = game.groundY - 60;
799
+ }
800
+
801
+ update() {
802
+ this.y -= this.game.speed;
803
+ this.timer++;
804
+ }
805
+
806
+ draw(ctx) {
807
+ if (this.collected) return;
808
+
809
+ ctx.save();
810
+ ctx.translate(this.x, this.y);
811
+ ctx.rotate(this.timer * 0.05);
812
+
813
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
814
+ ctx.strokeStyle = '#fff';
815
+ ctx.lineWidth = 2;
816
+ ctx.beginPath();
817
+ ctx.arc(0, 0, 15, 0, Math.PI * 2);
818
+ ctx.fill();
819
+ ctx.stroke();
820
+
821
+ // Icon
822
+ ctx.font = '16px Arial';
823
+ ctx.textAlign = 'center';
824
+ ctx.textBaseline = 'middle';
825
+ ctx.fillStyle = '#fff';
826
+ let icon = '';
827
+ if(this.type === 'shield') icon = '🛡️';
828
+ if(this.type === 'magnet') icon = '🧲';
829
+ if(this.type === 'mul') icon = 'x2';
830
+ ctx.fillText(icon, 0, 0);
831
+
832
+ ctx.restore();
833
+ }
834
+
835
+ getBounds() {
836
+ return { x: this.x - 15, y: this.y - 15, width: 30, height: 30 };
837
+ }
838
+ }
839
+
840
+ // --- MAIN GAME ENGINE ---
841
+
842
+ class Game {
843
+ constructor() {
844
+ this.canvas = document.getElementById('gameCanvas');
845
+ this.ctx = this.canvas.getContext('2d');
846
+ this.input = new InputHandler();
847
+
848
+ // State
849
+ this.isRunning = false;
850
+ this.isPaused = false;
851
+ this.lastTime = 0;
852
+ this.score = 0;
853
+ this.distance = 0;
854
+ this.speed = CONSTANTS.RUN_SPEED_BASE;
855
+ this.btcBalance = 0.00000000;
856
+
857
+ // Entities
858
+ this.player = null;
859
+ this.obstacles = [];
860
+ this.coins = [];
861
+ this.particles = [];
862
+ this.powerups = [];
863
+
864
+ // Spawning
865
+ this.spawnTimer = 0;
866
+ this.spawnRate = 60;
867
+
868
+ // Visuals
869
+ this.laneWidth = 100;
870
+ this.groundY = 0;
871
+ this.bgOffset = 0;
872
+ this.screenShake = 0;
873
+
874
+ // Powerups
875
+ this.playerPowerups = {
876
+ shield: false,
877
+ magnet: false,
878
+ mul: false
879
+ };
880
+ this.powerupTimers = {};
881
+
882
+ this.resize();
883
+ window.addEventListener('resize', () => this.resize());
884
+
885
+ // UI Bindings
886
+ document.getElementById('start-btn').addEventListener('click', () => this.start());
887
+ document.getElementById('restart-btn').addEventListener('click', () => this.start());
888
+ document.getElementById('pause-btn').addEventListener('click', () => this.togglePause());
889
+
890
+ // Initial Draw
891
+ this.drawBackground();
892
+ }
893
+
894
+ resize() {
895
+ this.canvas.width = window.innerWidth;
896
+ this.canvas.height = window.innerHeight;
897
+ this.laneWidth = this.canvas.width / 3;
898
+ this.groundY = this.canvas.height * 0.75;
899
+
900
+ if (!this.isRunning) {
901
+ this.drawBackground();
902
+ }
903
+ }
904
+
905
+ start() {
906
+ // Reset Game State
907
+ this.isRunning = true;
908
+ this.isPaused = false;
909
+ this.score = 0;
910
+ this.distance = 0;
911
+ this.speed = CONSTANTS.RUN_SPEED_BASE;
912
+ this.obstacles = [];
913
+ this.coins = [];
914
+ this.particles = [];
915
+ this.powerups = [];
916
+ this.btcBalance = parseFloat(document.getElementById('start-btc-display').innerText);
917
+ this.spawnTimer = 0;
918
+
919
+ // Reset Player
920
+ this.player = new Player(this);
921
+ this.playerPowerups = { shield: false, magnet: false, mul: false };
922
+
923
+ // UI
924
+ document.getElementById('start-screen').classList.add('hidden');
925
+ document.getElementById('game-over-screen').classList.add('hidden');
926
+ document.getElementById('hud').classList.remove('hidden');
927
+ document.getElementById('powerup-container').classList.remove('hidden');
928
+ this.updateHUD();
929
+ this.updatePowerupUI();
930
+
931
+ this.lastTime = performance.now();
932
+ requestAnimationFrame((t) => this.loop(t));
933
+ }
934
+
935
+ togglePause() {
936
+ if (!this.isRunning) return;
937
+ this.isPaused = !this.isPaused;
938
+ document.getElementById('pause-btn').innerText = this.isPaused ? '▶' : '❚❚';
939
+ if (!this.isPaused) {
940
+ this.lastTime = performance.now();
941
+ requestAnimationFrame((t) => this.loop(t));
942
+ }
943
+ }
944
+
945
+ spawnParticles(x, y, count, color) {
946
+ for (let i = 0; i < count; i++) {
947
+ this.particles.push(new Particle(x, y, color, 5));
948
+ }
949
+ }
950
+
951
+ spawn() {
952
+ this.spawnTimer--;
953
+
954
+ // Difficulty Scaling
955
+ const difficultyMod = Math.min(this.distance / 5000, 1); // Cap at 2x speed
956
+ const currentSpawnRate = Math.max(20, 60 - Math.floor(this.distance / 200));
957
+
958
+ if (this.spawnTimer <= 0) {
959
+ const lane = Utils.randomInt(0, 2);
960
+ const rand = Math.random();
961
+
962
+ // Spawn Obstacle
963
+ if (rand < 0.4) {
964
+ const type = Math.random() > 0.7 ? 'high' : (Math.random() > 0.5 ? 'low' : 'barrier');
965
+ this.obstacles.push(new Obstacle(this, lane, type));
966
  }
967
+ // Spawn Coins
968
+ else if (rand < 0.8) {
969
+ const isBonus = Math.random() > 0.8;
970
+ this.coins.push(new Coin(this, lane, isBonus));
971
  }
972
+ // Spawn Powerup
973
+ else if (rand < 0.9) {
974
+ const pTypes = ['shield', 'magnet', 'mul'];
975
+ const pType = pTypes[Utils.randomInt(0, 2)];
976
+ this.powerups.push(new PowerUp(this, lane, pType));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
977
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
978
 
979
+ this.spawnTimer = currentSpawnRate;
980
+ }
981
+ }
982
+
983
+ checkCollisions() {
984
+ const pBounds = this.player.getBounds();
985
+
986
+ // Obstacles
987
+ for (let obs of this.obstacles) {
988
+ const oBounds = obs.getBounds();
989
+ if (this.rectIntersect(pBounds, oBounds)) {
990
+ if (this.playerPowerups.shield) {
991
+ // Destroy obstacle, keep shield
992
+ this.playerPowerups.shield = false;
993
+ this.spawnParticles(obs.x + obs.width/2, obs.y + obs.height/2, 10, '#87CEEB');
994
+ this.obstacles = this.obstacles.filter(o => o !== obs);
995
+ this.screenShake = 5;
996
+ } else {
997
+ this.gameOver();
998
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999
  }
1000
  }
1001
 
1002
+ // Coins
1003
+ for (let coin of this.coins) {
1004
+ if (coin.collected) continue;
1005
+ const cBounds = coin.getBounds();
1006
+ if (this.rectIntersect(pBounds, cBounds)) {
1007
+ coin.collected = true;
1008
+ let val = coin.isBonus ? CONSTANTS.BONUS_COIN_VALUE : CONSTANTS.COIN_VALUE;
1009
+ if (this.playerPowerups.mul) val *= 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1010
 
1011
+ this.btcBalance += val;
1012
+ this.score += 10;
1013
+ this.spawnParticles(coin.x, coin.y, 5, CONSTANTS.COLORS.COIN);
 
 
 
 
 
 
 
 
1014
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
  }
1016
 
1017
+ // Powerups
1018
+ for (let pu of this.powerups) {
1019
+ if (pu.collected) continue;
1020
+ const pBounds = pu.getBounds();
1021
+ if (this.rectIntersect(pBounds, pBounds)) { // Use static bounds for powerups
1022
+ pu.collected = true;
1023
+ this.activatePowerup(pu.type);
 
 
 
 
 
 
 
 
 
 
 
1024
  }
1025
  }
1026
+ }
1027
 
1028
+ activatePowerup(type) {
1029
+ this.spawnParticles(this.player.x, this.player.y, 20, '#fff');
1030
+ const duration = 600; // 10 seconds at 60fps
1031
+
1032
+ if (type === 'shield') {
1033
+ this.playerPowerups.shield = true;
1034
+ this.powerupTimers.shield = duration;
1035
+ document.getElementById('pu-shield').classList.add('active');
1036
+ } else if (type === 'magnet') {
1037
+ this.playerPowerups.magnet = true;
1038
+ this.powerupTimers.magnet = duration;
1039
+ document.getElementById('pu-magnet').classList.add('active');
1040
+ } else if (type === 'mul') {
1041
+ this.playerPowerups.mul = true;
1042
+ this.powerupTimers.mul = duration;
1043
+ document.getElementById('pu-mul').classList.add('active');
1044
+ }
1045
+ }
1046
+
1047
+ updatePowerupUI() {
1048
+ const ids = ['pu-shield', 'pu-magnet', 'pu-mul'];
1049
+ ids.forEach(id => document.getElementById(id).classList.remove('active'));
1050
+ }
1051
+
1052
+ rectIntersect(r1, r2) {
1053
+ return !(r2.x > r1.x + r1.width ||
1054
+ r2.x + r2.width < r1.x ||
1055
+ r2.y > r1.y + r1.height ||
1056
+ r2.y + r2.height < r1.y);
1057
+ }
1058
+
1059
+ update(deltaTime) {
1060
+ if (this.isPaused) return;
1061
+
1062
+ // Speed Increase
1063
+ this.speed += 0.0001;
1064
+ this.distance += this.speed;
1065
+ this.score += 1;
1066
+
1067
+ // Update Entities
1068
+ this.player.update();
1069
+ this.spawn();
1070
+
1071
+ this.obstacles.forEach(o => o.update());
1072
+ this.obstacles = this.obstacles.filter(o => o.y > -100);
1073
+
1074
+ this.coins.forEach(c => c.update());
1075
+ this.coins = this.coins.filter(c => c.y > -100);
1076
+
1077
+ this.powerups.forEach(p => p.update());
1078
+ this.powerups = this.powerups.filter(p => p.y > -100);
1079
+
1080
+ this.particles.forEach(p => p.update());
1081
+ this.particles = this.particles.filter(p => p.life > 0);
1082
+
1083
+ // Powerup Timers
1084
+ for (let key in this.powerupTimers) {
1085
+ if (this.powerupTimers[key] > 0) {
1086
+ this.powerupTimers[key]--;
1087
+ } else {
1088
+ this.playerPowerups[key] = false;
1089
+ delete this.powerupTimers[key];
1090
  }
1091
  }
1092
+ this.updatePowerupUI();
1093
 
1094
+ this.checkCollisions();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1095
 
1096
+ // Screen Shake Decay
1097
+ if (this.screenShake >