HAL1993 commited on
Commit
172e467
·
verified ·
1 Parent(s): 555aec0

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1010 -1054
index.html CHANGED
@@ -2,41 +2,118 @@
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 Runner - Bitcoin Edition</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Bangers&family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
10
  <style>
 
 
11
  * {
12
  margin: 0;
13
  padding: 0;
14
  box-sizing: border-box;
15
  }
16
-
 
 
 
 
 
 
 
 
17
  body {
 
 
 
 
 
 
18
  overflow: hidden;
19
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
20
- font-family: 'Orbitron', sans-serif;
21
- touch-action: none;
22
  }
23
-
24
- #gameCanvas {
 
 
 
 
 
 
 
 
 
 
 
25
  display: block;
26
- margin: 0 auto;
27
- background: linear-gradient(180deg, #87CEEB 0%, #E0F6FF 100%);
28
  }
29
-
30
  .ui-overlay {
31
- position: fixed;
32
  top: 0;
33
  left: 0;
34
  width: 100%;
35
  height: 100%;
36
  pointer-events: none;
37
- z-index: 100;
 
38
  }
39
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  .screen {
41
  position: absolute;
42
  top: 0;
@@ -45,1163 +122,1042 @@
45
  height: 100%;
46
  display: flex;
47
  flex-direction: column;
48
- align-items: center;
49
  justify-content: center;
50
- background: rgba(0, 0, 0, 0.85);
 
 
 
 
 
 
 
51
  opacity: 0;
52
- transition: opacity 0.5s ease;
53
  pointer-events: none;
54
  }
55
-
56
- .screen.active {
57
- opacity: 1;
58
- pointer-events: all;
 
 
 
 
 
59
  }
60
-
61
- .title {
62
- font-family: 'Bangers', cursive;
63
- font-size: clamp(2.5rem, 8vw, 5rem);
64
- color: #F7931A;
65
- text-shadow: 0 0 20px #F7931A, 0 0 40px #F7931A, 4px 4px 0 #000;
66
  letter-spacing: 4px;
67
- animation: pulse 2s ease-in-out infinite;
68
- }
69
-
70
- .subtitle {
71
- font-size: clamp(1rem, 3vw, 1.5rem);
72
- color: #4ade80;
73
- margin-top: 10px;
74
- text-shadow: 0 0 10px #4ade80;
75
  }
76
-
77
  @keyframes pulse {
78
  0%, 100% { transform: scale(1); }
79
  50% { transform: scale(1.05); }
80
  }
81
-
82
  .btn {
83
- margin-top: 30px;
84
- padding: 15px 50px;
85
- font-family: 'Bangers', cursive;
86
- font-size: 1.8rem;
87
- color: #fff;
88
- background: linear-gradient(180deg, #F7931A 0%, #c77a15 100%);
89
  border: none;
90
  border-radius: 50px;
91
  cursor: pointer;
92
- transition: all 0.3s ease;
93
- box-shadow: 0 6px 0 #8c5a10, 0 10px 20px rgba(247, 147, 26, 0.4);
94
  text-transform: uppercase;
95
- letter-spacing: 2px;
 
96
  }
97
-
98
- .btn:hover {
99
- transform: translateY(-3px);
100
- box-shadow: 0 9px 0 #8c5a10, 0 15px 30px rgba(247, 147, 26, 0.5);
 
101
  }
102
-
103
- .btn:active {
104
- transform: translateY(3px);
105
- box-shadow: 0 3px 0 #8c5a10, 0 5px 10px rgba(247, 147, 26, 0.4);
106
  }
107
-
108
- .controls-info {
 
 
 
 
 
 
 
 
 
 
 
109
  margin-top: 40px;
110
- color: #aaa;
 
111
  text-align: center;
112
- font-size: 0.9rem;
113
  line-height: 2;
114
  }
115
-
116
- .controls-info span {
117
- color: #4ade80;
118
- font-weight: bold;
 
 
 
 
 
119
  }
120
-
121
- .hud {
122
- position: fixed;
123
- top: 20px;
124
- left: 50%;
125
- transform: translateX(-50%);
126
- display: flex;
127
- gap: 20px;
128
- z-index: 50;
129
- width: 90%;
130
- max-width: 600px;
131
- justify-content: space-between;
132
  }
133
-
134
- .hud-item {
135
- background: rgba(0, 0, 0, 0.7);
136
- padding: 10px 20px;
137
- border-radius: 25px;
138
- border: 2px solid #F7931A;
139
- box-shadow: 0 0 15px rgba(247, 147, 26, 0.3);
140
  }
141
-
142
- .hud-label {
143
- font-size: 0.7rem;
144
- color: #888;
145
- text-transform: uppercase;
146
- letter-spacing: 1px;
 
147
  }
148
-
149
- .hud-value {
150
- font-size: 1.2rem;
151
- color: #F7931A;
152
- font-weight: bold;
153
- text-shadow: 0 0 10px #F7931A;
 
154
  }
155
-
156
- .hud-value.distance {
157
- color: #4ade80;
158
- text-shadow: 0 0 10px #4ade80;
 
 
 
 
 
 
159
  }
160
-
161
- .powerup-indicator {
162
- position: fixed;
163
- top: 80px;
164
- left: 50%;
165
- transform: translateX(-50%);
166
- display: flex;
167
- gap: 10px;
168
- z-index: 50;
169
  }
170
-
171
- .powerup-badge {
172
- padding: 8px 16px;
 
 
 
 
 
173
  border-radius: 20px;
174
- font-size: 0.8rem;
175
- font-weight: bold;
176
- opacity: 0;
177
- transform: translateY(-20px);
178
- transition: all 0.3s ease;
179
- }
180
-
181
- .powerup-badge.active {
182
- opacity: 1;
183
- transform: translateY(0);
184
- }
185
-
186
- .powerup-badge.magnet { background: #9333ea; color: #fff; }
187
- .powerup-badge.multiplier { background: #eab308; color: #000; }
188
- .powerup-badge.shield { background: #3b82f6; color: #fff; }
189
- .powerup-badge.speed { background: #ef4444; color: #fff; }
190
-
191
- .game-over-stats {
192
  text-align: center;
193
- margin: 20px 0;
 
194
  }
195
-
196
- .stat-row {
197
- display: flex;
198
- justify-content: space-between;
199
- padding: 10px 30px;
200
- font-size: 1.2rem;
201
- color: #fff;
202
- }
203
-
204
- .stat-value {
205
- color: #F7931A;
206
- font-weight: bold;
207
- }
208
-
209
- .pause-btn {
210
- position: fixed;
211
- top: 20px;
212
- right: 20px;
213
- width: 50px;
214
- height: 50px;
215
- background: rgba(0, 0, 0, 0.7);
216
- border: 2px solid #F7931A;
217
- border-radius: 50%;
218
- color: #F7931A;
219
- font-size: 1.5rem;
220
- cursor: pointer;
221
- z-index: 60;
222
- transition: all 0.3s ease;
223
  }
224
-
225
- .pause-btn:hover {
226
- background: #F7931A;
227
- color: #000;
 
 
228
  }
229
-
230
- .built-with {
231
- position: fixed;
232
  bottom: 10px;
233
  left: 50%;
234
  transform: translateX(-50%);
235
- color: rgba(255, 255, 255, 0.5);
236
- font-size: 0.8rem;
237
- z-index: 200;
238
- }
239
-
240
- .built-with a {
241
- color: #F7931A;
242
  text-decoration: none;
 
243
  }
244
-
245
- .combo-display {
246
- position: fixed;
247
- top: 50%;
248
- left: 50%;
249
- transform: translate(-50%, -50%);
250
- font-family: 'Bangers', cursive;
251
- font-size: 3rem;
252
- color: #eab308;
253
- text-shadow: 0 0 20px #eab308, 0 0 40px #eab308;
254
- opacity: 0;
255
- z-index: 70;
256
- pointer-events: none;
257
- }
258
-
259
- .combo-display.show {
260
- animation: comboPopup 1s ease-out forwards;
261
- }
262
-
263
- @keyframes comboPopup {
264
- 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
265
- 20% { opacity: 1; transform: translate(-50%, -50%) scale(1.2); }
266
- 100% { opacity: 0; transform: translate(-50%, -80%) scale(1); }
267
- }
268
-
269
- @media (max-width: 600px) {
270
- .hud { gap: 10px; }
271
- .hud-item { padding: 8px 12px; }
272
- .hud-value { font-size: 1rem; }
273
  }
274
  </style>
275
  </head>
276
  <body>
277
- <canvas id="gameCanvas"></canvas>
278
-
279
- <div class="hud" id="hud" style="display: none;">
280
- <div class="hud-item">
281
- <div class="hud-label">Distance</div>
282
- <div class="hud-value distance" id="distanceDisplay">0m</div>
283
- </div>
284
- <div class="hud-item">
285
- <div class="hud-label">₿alance</div>
286
- <div class="hud-value" id="btcDisplay">₿ 0.00000000</div>
287
- </div>
288
- <div class="hud-item">
289
- <div class="hud-label">Speed</div>
290
- <div class="hud-value" id="speedDisplay">1x</div>
291
- </div>
292
- </div>
293
-
294
- <div class="powerup-indicator" id="powerupIndicator" style="display: none;"></div>
295
-
296
- <button class="pause-btn" id="pauseBtn" style="display: none;">⏸</button>
297
-
298
- <div class="combo-display" id="comboDisplay"></div>
299
-
300
- <div class="ui-overlay">
301
- <div class="screen active" id="startScreen">
302
- <div class="title">🦖 DINO RUNNER</div>
303
- <div class="subtitle">₿ITCOIN EDITION</div>
304
- <button class="btn" id="startBtn">PLAY</button>
305
- <div class="controls-info">
306
- <span>← →</span> Move Lanes<br>
307
- <span>↑</span> Jump | <span>↓</span> Slide<br>
308
- <span>SPACE</span> Jump | <span>SHIFT</span> Slide
309
- </div>
310
- </div>
311
-
312
- <div class="screen" id="pauseScreen">
313
- <div class="title" style="font-size: 3rem;">⏸ PAUSED</div>
314
- <button class="btn" id="resumeBtn">RESUME</button>
315
- <button class="btn" id="quitBtn" style="margin-top: 15px; background: linear-gradient(180deg, #ef4444 0%, #b91c1c 100%);">QUIT</button>
316
- </div>
317
-
318
- <div class="screen" id="gameOverScreen">
319
- <div class="title" style="color: #ef4444;">GAME OVER</div>
320
- <div class="game-over-stats">
321
- <div class="stat-row">
322
- <span>Distance</span>
323
- <span class="stat-value" id="finalDistance">0m</span>
324
  </div>
325
- <div class="stat-row">
326
- <span>Total BTC</span>
327
- <span class="stat-value" id="finalBtc">₿ 0.00000000</span>
328
  </div>
329
- <div class="stat-row">
330
- <span>Best Combo</span>
331
- <span class="stat-value" id="bestCombo">x1</span>
 
 
 
 
 
332
  </div>
333
  </div>
334
- <button class="btn" id="restartBtn">PLAY AGAIN</button>
335
  </div>
336
- </div>
337
-
338
- <div class="built-with">
339
- Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  </div>
341
 
342
  <script>
343
- // ============================================
344
- // GAME CONSTANTS & CONFIGURATION
345
- // ============================================
346
- const CANVAS_WIDTH = 800;
347
- const CANVAS_HEIGHT = 500;
348
- const LANE_WIDTH = 120;
349
- const LANES = [-1, 0, 1]; // Left, Center, Right
350
- const LANE_POSITIONS = [
351
- CANVAS_WIDTH / 2 - LANE_WIDTH,
352
- CANVAS_WIDTH / 2,
353
- CANVAS_WIDTH / 2 + LANE_WIDTH
354
- ];
355
-
356
- const GROUND_Y = CANVAS_HEIGHT - 80;
357
- const GRAVITY = 0.8;
358
- const JUMP_FORCE = -16;
359
- const BASE_SPEED = 5;
360
- const MAX_SPEED = 15;
361
- const SPEED_INCREMENT = 0.001;
362
-
363
- // ============================================
364
- // GAME STATE
365
- // ============================================
366
- const game = {
367
- canvas: null,
368
- ctx: null,
369
- running: false,
370
- paused: false,
371
- gameOver: false,
372
- distance: 0,
373
- speed: BASE_SPEED,
374
- lastTime: 0,
375
- deltaTime: 0,
376
- screenShake: 0,
377
- combo: 1,
378
- bestCombo: 1,
379
- comboTimer: 0
380
- };
381
-
382
- // Bitcoin economy
383
- const btc = {
384
- balance: 0,
385
- perCoin: 0.00000001,
386
- bonusCoinValue: 0.00000005
387
- };
388
-
389
- // Power-up states
390
- const powerups = {
391
- magnet: { active: false, duration: 0 },
392
- multiplier: { active: false, duration: 0, value: 2 },
393
- shield: { active: false },
394
- speed: { active: false, duration: 0 }
395
  };
396
-
397
- // ============================================
398
- // DINOSAUR CHARACTER
399
- // ============================================
400
- const dino = {
401
- x: CANVAS_WIDTH / 2,
402
- y: GROUND_Y,
403
- lane: 1,
404
- targetLane: 1,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  width: 60,
406
  height: 80,
407
- velocityY: 0,
 
 
408
  isJumping: false,
409
  isSliding: false,
410
  slideTimer: 0,
411
  runFrame: 0,
412
  runTimer: 0,
413
- bobOffset: 0,
414
- eyeBlink: 0,
415
- isHit: false,
416
- hitTimer: 0,
417
-
418
- reset() {
419
- this.lane = 1;
420
- this.targetLane = 1;
421
- this.x = LANE_POSITIONS[1];
422
- this.y = GROUND_Y;
423
- this.velocityY = 0;
424
- this.isJumping = false;
425
- this.isSliding = false;
426
- this.slideTimer = 0;
427
- this.runFrame = 0;
428
- this.bobOffset = 0;
429
- this.eyeBlink = 0;
430
- this.isHit = false;
431
- this.hitTimer = 0;
432
- },
433
-
434
- update(dt) {
435
- // Lane movement with smooth interpolation
436
- this.targetLane = Math.max(0, Math.min(2, this.targetLane));
437
- const targetX = LANE_POSITIONS[this.targetLane];
438
- this.x += (targetX - this.x) * 0.15;
439
- this.lane = this.targetLane;
440
-
441
- // Jump physics
442
- if (this.isJumping) {
443
- this.velocityY += GRAVITY;
444
- this.y += this.velocityY;
445
- if (this.y >= GROUND_Y) {
446
- this.y = GROUND_Y;
447
- this.isJumping = false;
448
- this.velocityY = 0;
449
- }
450
- }
451
-
452
- // Slide mechanics
453
- if (this.isSliding) {
454
- this.slideTimer -= dt;
455
- if (this.slideTimer <= 0) {
456
- this.isSliding = false;
457
- }
458
- }
459
-
460
- // Running animation
461
- this.runTimer += dt * 0.01 * game.speed;
462
- if (this.runTimer > 1) {
463
- this.runTimer = 0;
464
- this.runFrame = (this.runFrame + 1) % 4;
465
- }
466
-
467
- // Bouncy idle animation
468
- if (!this.isJumping && !this.isSliding) {
469
- this.bobOffset = Math.sin(Date.now() * 0.01) * 3;
470
- } else {
471
- this.bobOffset = 0;
472
- }
473
-
474
- // Eye blink
475
- this.eyeBlink = Math.random() < 0.005 ? 10 : Math.max(0, this.eyeBlink - 1);
476
-
477
- // Hit effect
478
- if (this.isHit) {
479
- this.hitTimer -= dt;
480
- if (this.hitTimer <= 0) {
481
- this.isHit = false;
 
 
 
 
 
 
 
 
 
 
482
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  }
484
- },
485
-
486
- jump() {
487
- if (!this.isJumping && !this.isSliding) {
488
- this.isJumping = true;
489
- this.velocityY = JUMP_FORCE;
490
- }
491
- },
492
-
493
- slide() {
494
- if (!this.isJumping && !this.isSliding) {
495
- this.isSliding = true;
496
- this.slideTimer = 800;
497
- }
498
- },
499
-
500
- moveLeft() {
501
- if (this.targetLane > 0) {
502
- this.targetLane--;
503
- }
504
- },
505
-
506
- moveRight() {
507
- if (this.targetLane < 2) {
508
- this.targetLane++;
509
- }
510
- },
511
-
512
- draw(ctx) {
513
- ctx.save();
514
- ctx.translate(this.x, this.y + this.bobOffset);
515
-
516
- // Hit flash effect
517
- if (this.isHit && Math.floor(this.hitTimer / 100) % 2 === 0) {
518
- ctx.globalAlpha = 0.5;
519
- }
520
-
521
- const slideOffset = this.isSliding ? 30 : 0;
522
- const height = this.isSliding ? this.height - 30 : this.height;
523
-
524
- // Body (green dinosaur)
525
- const bodyColor = '#22c55e';
526
- const bodyDark = '#15803d';
527
 
528
- // Main body
529
- ctx.fillStyle = bodyColor;
530
- ctx.beginPath();
531
- ctx.ellipse(0, -height/2 + slideOffset, 28, 35, 0, 0, Math.PI * 2);
532
- ctx.fill();
533
-
534
- // Tail
535
- ctx.beginPath();
536
- ctx.moveTo(-25, -height/2 + slideOffset);
537
- ctx.quadraticCurveTo(-45, -height/2 + slideOffset - 10, -40, -height/2 + slideOffset + 15);
538
- ctx.quadraticCurveTo(-35, -height/2 + slideOffset + 20, -25, -height/2 + slideOffset + 10);
539
- ctx.fill();
540
-
541
- // Legs (animated)
542
- const legOffset = this.isSliding ? 0 : Math.sin(this.runFrame * Math.PI / 2) * 8;
543
- ctx.fillStyle = bodyDark;
544
- // Left leg
545
- ctx.beginPath();
546
- ctx.ellipse(-12, -10 + slideOffset + legOffset, 8, 15, 0.2, 0, Math.PI * 2);
547
- ctx.fill();
548
- // Right leg
549
- ctx.beginPath();
550
- ctx.ellipse(12, -10 + slideOffset - legOffset, 8, 15, -0.2, 0, Math.PI * 2);
551
- ctx.fill();
552
-
553
- // Head
554
- ctx.fillStyle = bodyColor;
555
- ctx.beginPath();
556
- ctx.ellipse(0, -height - 5, 25, 22, 0, 0, Math.PI * 2);
557
- ctx.fill();
558
-
559
- // Snout
560
- ctx.beginPath();
561
- ctx.ellipse(20, -height - 5, 15, 12, 0, 0, Math.PI * 2);
562
- ctx.fill();
563
-
564
- // Nostril
565
- ctx.fillStyle = bodyDark;
566
- ctx.beginPath();
567
- ctx.ellipse(28, -height - 8, 3, 2, 0, 0, Math.PI * 2);
568
- ctx.fill();
569
-
570
- // Eye white
571
- ctx.fillStyle = '#fff';
572
- ctx.beginPath();
573
- ctx.ellipse(8, -height - 12, 10, 11, 0, 0, Math.PI * 2);
574
- ctx.fill();
575
-
576
- // Eye pupil
577
- ctx.fillStyle = '#000';
578
- ctx.beginPath();
579
- ctx.ellipse(10, -height - 10, 5, 6, 0, 0, Math.PI * 2);
580
- ctx.fill();
581
-
582
- // Eye shine
583
- ctx.fillStyle = '#fff';
584
  ctx.beginPath();
585
- ctx.ellipse(12, -height - 13, 2, 2, 0, 0, Math.PI * 2);
586
  ctx.fill();
587
-
588
- // Eyelid (blink)
589
- if (this.eyeBlink > 0) {
590
- ctx.fillStyle = bodyDark;
591
- ctx.beginPath();
592
- ctx.ellipse(8, -height - 12, 11, this.eyeBlink, 0, 0, Math.PI * 2);
593
- ctx.fill();
594
- }
595
-
596
- // Headband
597
- ctx.fillStyle = '#F7931A';
598
- ctx.fillRect(-28, -height - 22, 56, 10);
599
 
600
- // Headband knot
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  ctx.beginPath();
602
- ctx.moveTo(-28, -height - 22);
603
- ctx.lineTo(-35, -height - 15);
604
- ctx.lineTo(-28, -height - 12);
605
- ctx.fill();
606
-
607
- // Bitcoin symbol on headband
608
- ctx.fillStyle = '#fff';
609
- ctx.font = 'bold 8px Arial';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
610
  ctx.textAlign = 'center';
611
- ctx.fillText('₿', 0, -height - 15);
612
-
613
- // Shield effect
614
- if (powerups.shield.active) {
615
- ctx.strokeStyle = '#3b82f6';
616
- ctx.lineWidth = 3;
617
- ctx.globalAlpha = 0.6 + Math.sin(Date.now() * 0.01) * 0.3;
618
- ctx.beginPath();
619
- ctx.arc(0, -height/2, 50, 0, Math.PI * 2);
620
- ctx.stroke();
621
- ctx.globalAlpha = 1;
622
- }
623
-
624
- ctx.restore();
625
- },
626
-
627
- getBounds() {
628
- const slideOffset = this.isSliding ? 30 : 0;
629
- return {
630
- x: this.x - 25,
631
- y: this.y - this.height + slideOffset,
632
- width: 50,
633
- height: this.height - slideOffset
634
- };
635
  }
636
- };
637
-
638
- // ============================================
639
- // OBSTACLE SYSTEM
640
- // ============================================
641
- const obstacles = [];
642
- const obstacleTypes = [
643
- { name: 'barrier', width: 80, height: 60, color: '#ef4444', yOffset: 0 },
644
- { name: 'lowBarrier', width: 80, height: 40, color: '#f97316', yOffset: 0 },
645
- { name: 'arch', width: 100, height: 120, color: '#8b5cf6', yOffset: -40 }
646
- ];
647
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
  function spawnObstacle() {
649
- const type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
 
650
  const lane = Math.floor(Math.random() * 3);
651
 
 
 
 
 
 
652
  obstacles.push({
653
- x: LANE_POSITIONS[lane],
654
- y: GROUND_Y,
655
  lane: lane,
656
- ...type,
657
- passed: false
 
 
 
 
658
  });
659
  }
660
-
661
- function updateObstacles(dt) {
662
- // Spawn new obstacles based on speed
663
- const spawnChance = 0.02 + game.speed * 0.001;
664
- if (Math.random() < spawnChance && obstacles.length < 5) {
665
- const lastObstacle = obstacles[obstacles.length - 1];
666
- if (!lastObstacle || lastObstacle.x < CANVAS_WIDTH - 200) {
667
- spawnObstacle();
668
- }
669
- }
670
-
671
- // Move and remove obstacles
672
- for (let i = obstacles.length - 1; i >= 0; i--) {
673
- obstacles[i].x -= game.speed * 60 * (dt / 1000);
674
 
675
- if (obstacles[i].x < -100) {
676
- obstacles.splice(i, 1);
 
 
 
 
 
 
 
677
  }
 
 
 
 
 
 
 
 
 
678
  }
679
  }
680
-
681
- function drawObstacles(ctx) {
682
- obstacles.forEach(obs => {
683
- ctx.save();
684
- ctx.translate(obs.x, obs.y + obs.yOffset);
685
-
686
- // Glow effect
687
- ctx.shadowColor = obs.color;
688
- ctx.shadowBlur = 15;
689
-
690
- if (obs.name === 'arch') {
691
- // Draw arch obstacle (must slide to pass)
692
- ctx.fillStyle = obs.color;
693
- // Left pillar
694
- ctx.fillRect(-50, -120, 20, 120);
695
- // Right pillar
696
- ctx.fillRect(30, -120, 20, 120);
697
- // Top bar
698
- ctx.fillRect(-50, -120, 100, 20);
699
-
700
- // Warning stripes
701
- ctx.fillStyle = '#fbbf24';
702
- for (let i = 0; i < 3; i++) {
703
- ctx.fillRect(-40 + i * 30, -110, 10, 100);
704
- }
705
- } else if (obs.name === 'lowBarrier') {
706
- // Draw low barrier (must jump)
707
- ctx.fillStyle = obs.color;
708
- ctx.fillRect(-40, -40, 80, 40);
709
-
710
- // Hazard stripes
711
- ctx.fillStyle = '#fbbf24';
712
- ctx.fillRect(-35, -35, 30, 5);
713
- ctx.fillRect(-5, -25, 30, 5);
714
- ctx.fillRect(-35, -15, 30, 5);
715
- } else {
716
- // Draw regular barrier
717
- ctx.fillStyle = obs.color;
718
- ctx.fillRect(-40, -60, 80, 60);
719
-
720
- // Warning symbol
721
- ctx.fillStyle = '#fbbf24';
722
- ctx.beginPath();
723
- ctx.moveTo(0, -45);
724
- ctx.lineTo(-15, -25);
725
- ctx.lineTo(15, -25);
726
- ctx.closePath();
727
- ctx.fill();
728
-
729
- ctx.fillStyle = '#000';
730
- ctx.font = 'bold 12px Arial';
731
- ctx.textAlign = 'center';
732
- ctx.fillText('!', 0, -30);
733
- }
734
-
735
- ctx.restore();
736
  });
737
  }
738
-
739
- function checkObstacleCollision() {
740
- const dinoBounds = dino.getBounds();
741
-
742
- for (const obs of obstacles) {
743
- const obsBounds = {
744
- x: obs.x - obs.width / 2,
745
- y: obs.y + obs.yOffset - (obs.name === 'arch' ? 120 : obs.height),
746
- width: obs.width,
747
- height: obs.name === 'arch' ? 120 : obs.height
748
- };
749
-
750
- // Adjust for low barrier (can jump over)
751
- if (obs.name === 'lowBarrier' && dino.isJumping && dino.y < GROUND_Y - 30) {
752
- continue;
753
- }
754
-
755
- // Adjust for arch (can slide under)
756
- if (obs.name === 'arch' && dino.isSliding) {
757
- continue;
758
- }
759
-
760
- // Check collision
761
- if (dinoBounds.x < obsBounds.x + obsBounds.width &&
762
- dinoBounds.x + dinoBounds.width > obsBounds.x &&
763
- dinoBounds.y < obsBounds.y + obsBounds.height &&
764
- dinoBounds.y + dinoBounds.height > obsBounds.y) {
765
-
766
- if (powerups.shield.active) {
767
- powerups.shield.active = false;
768
- game.screenShake = 10;
769
- updatePowerupUI();
770
- return false;
771
- }
772
- return true;
773
- }
774
  }
775
- return false;
776
  }
777
-
778
- // ============================================
779
- // COIN SYSTEM (BITCOIN)
780
- // ============================================
781
- const coins = [];
782
- const coinParticles = [];
783
-
784
- function spawnCoins() {
785
- const lane = Math.floor(Math.random() * 3);
786
- const pattern = Math.random() < 0.3 ? 'arc' : 'line';
787
- const isBonus = Math.random() < 0.1;
788
-
789
- if (pattern === 'arc') {
790
- for (let i = 0; i < 5; i++) {
791
- coins.push({
792
- x: LANE_POSITIONS[lane],
793
- y: GROUND_Y - 40 - Math.sin(i * 0.5) * 30,
794
- lane: lane,
795
- rotation: Math.random() * Math.PI * 2,
796
- glow: 0,
797
- value: isBonus ? btc.bonusCoinValue : btc.perCoin,
798
- isBonus: isBonus,
799
- collected: false
800
- });
801
- }
802
- } else {
803
- const count = isBonus ? 3 : 5;
804
- for (let i = 0; i < count; i++) {
805
- coins.push({
806
- x: LANE_POSITIONS[lane] - i * 40,
807
- y: GROUND_Y - 30,
808
- lane: lane,
809
- rotation: Math.random() * Math.PI * 2,
810
- glow: 0,
811
- value: isBonus ? btc.bonusCoinValue : btc.perCoin,
812
- isBonus: isBonus,
813
- collected: false
814
- });
815
  }
816
  }
817
- }
818
-
819
- function updateCoins(dt) {
820
  // Spawn coins
821
  if (Math.random() < 0.015) {
822
- spawnCoins();
 
 
 
823
  }
824
-
825
- // Magnet effect
826
- if (powerups.magnet.active) {
827
- coins.forEach(coin => {
828
- if (!coin.collected) {
829
- const dx = dino.x - coin.x;
830
- const dy = (dino.y - 30) - coin.y;
831
- const dist = Math.sqrt(dx * dx + dy * dy);
832
- if (dist < 200) {
833
- coin.x += dx * 0.1;
834
- coin.y += dy * 0.1;
835
- }
836
- }
837
- });
838
  }
839
-
840
- // Update coins
841
- for (let i = coins.length - 1; i >= 0; i--) {
842
- const coin = coins[i];
843
- coin.x -= game.speed * 60 * (dt / 1000);
844
- coin.rotation += 0.05;
845
- coin.glow = (Math.sin(Date.now() * 0.005) + 1) / 2;
846
-
847
- // Collection check
848
- if (!coin.collected) {
849
- const dx = dino.x - coin.x;
850
- const dy = (dino.y - 30) - coin.y;
851
- const dist = Math.sqrt(dx * dx + dy * dy);
852
 
853
- if (dist < 40) {
854
- coin.collected = true;
855
- collectCoin(coin);
856
-
857
- // Spawn particles
858
- for (let p = 0; p < 10; p++) {
859
- coinParticles.push({
860
- x: coin.x,
861
- y: coin.y,
862
- vx: (Math.random() - 0.5) * 8,
863
- vy: (Math.random() - 0.5) * 8,
864
- life: 1,
865
- color: coin.isBonus ? '#eab308' : '#F7931A'
866
- });
 
 
 
 
 
 
 
 
867
  }
868
  }
869
  }
870
-
871
- if (coin.x < -50 || coin.collected) {
872
- coins.splice(i, 1);
873
- }
874
- }
875
-
876
- // Update particles
877
- for (let i = coinParticles.length - 1; i >= 0; i--) {
878
- const p = coinParticles[i];
879
- p.x += p.vx;
880
- p.y += p.vy;
881
- p.life -= 0.03;
882
- if (p.life <= 0) {
883
- coinParticles.splice(i, 1);
884
  }
885
- }
886
- }
887
-
888
- function collectCoin(coin) {
889
- const value = coin.value * powerups.multiplier.value;
890
- btc.balance += value;
891
 
892
- // Combo system
893
- game.comboTimer = 2000;
894
- game.combo = Math.min(game.combo + 1, 10);
895
- if (game.combo > game.bestCombo) {
896
- game.bestCombo = game.combo;
897
- }
898
-
899
- // Show combo
900
- if (game.combo >= 3) {
901
- showCombo(game.combo);
902
- }
903
-
904
- updateHUD();
905
- }
906
-
907
- function showCombo(combo) {
908
- const comboEl = document.getElementById('comboDisplay');
909
- comboEl.textContent = `x${combo} COMBO!`;
910
- comboEl.classList.remove('show');
911
- void comboEl.offsetWidth;
912
- comboEl.classList.add('show');
913
- }
914
-
915
- function drawCoins(ctx) {
916
- // Draw coins
917
- coins.forEach(coin => {
918
- if (coin.collected) return;
919
 
920
- ctx.save();
921
- ctx.translate(coin.x, coin.y);
922
-
923
- // Glow effect
924
- const glowIntensity = coin.isBonus ? 25 : 15;
925
- ctx.shadowColor = coin.isBonus ? '#eab308' : '#F7931A';
926
- ctx.shadowBlur = glowIntensity + coin.glow * 10;
927
-
928
- // Coin body (3D rotation effect)
929
- const scaleX = Math.cos(coin.rotation);
930
 
931
- ctx.fillStyle = coin.isBonus ? '#eab308' : '#F7931A';
932
- ctx.beginPath();
933
- ctx.ellipse(0, 0, 18 * Math.abs(scaleX), 18, 0, 0, Math.PI * 2);
934
- ctx.fill();
935
-
936
- // Bitcoin symbol
937
- if (Math.abs(scaleX) > 0.3) {
938
- ctx.fillStyle = '#fff';
939
- ctx.font = 'bold 14px Arial';
940
- ctx.textAlign = 'center';
941
- ctx.textBaseline = 'middle';
942
- ctx.fillText('₿', 0, 0);
943
- }
944
-
945
- // Shine
946
- if (scaleX > 0.5) {
947
- ctx.fillStyle = 'rgba(255,255,255,0.4)';
948
- ctx.beginPath();
949
- ctx.ellipse(-5, -5, 5, 3, -0.5, 0, Math.PI * 2);
950
- ctx.fill();
951
  }
952
-
953
- ctx.restore();
954
  });
955
-
956
- // Draw particles
957
- coinParticles.forEach(p => {
958
- ctx.globalAlpha = p.life;
959
- ctx.fillStyle = p.color;
960
- ctx.beginPath();
961
- ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
962
- ctx.fill();
 
 
 
 
 
 
963
  });
964
- ctx.globalAlpha = 1;
965
- }
966
-
967
- // ============================================
968
- // POWER-UP SYSTEM
969
- // ============================================
970
- const powerupItems = [];
971
- const powerupTypes = [
972
- { name: 'magnet', color: '#9333ea', symbol: '���', duration: 8000 },
973
- { name: 'multiplier', color: '#eab308', symbol: 'x2', duration: 10000 },
974
- { name: 'shield', color: '#3b82f6', symbol: '🛡', duration: 0 },
975
- { name: 'speed', color: '#ef4444', symbol: '⚡', duration: 5000 }
976
- ];
977
-
978
- function spawnPowerup() {
979
- const type = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
980
- const lane = Math.floor(Math.random() * 3);
981
 
982
- powerupItems.push({
983
- x: LANE_POSITIONS[lane],
984
- y: GROUND_Y - 50,
985
- lane: lane,
986
- ...type,
987
- rotation: 0,
988
- bobOffset: 0
 
 
 
989
  });
990
- }
991
-
992
- function updatePowerups(dt) {
993
- // Spawn powerups
994
- if (Math.random() < 0.002) {
995
- spawnPowerup();
996
- }
997
-
998
  // Update powerup timers
999
- if (powerups.magnet.active) {
1000
- powerups.magnet.duration -= dt;
1001
- if (powerups.magnet.duration <= 0) {
1002
- powerups.magnet.active = false;
1003
- updatePowerupUI();
 
 
 
 
 
 
 
1004
  }
 
 
 
 
 
 
 
1005
  }
1006
-
1007
- if (powerups.multiplier.active) {
1008
- powerups.multiplier.duration -= dt;
1009
- if (powerups.multiplier.duration <= 0) {
1010
- powerups.multiplier.active = false;
1011
- powerups.multiplier.value = 1;
1012
- updatePowerupUI();
 
 
 
 
 
 
 
 
 
 
 
 
 
1013
  }
 
 
1014
  }
1015
-
1016
- if (powerups.speed.active) {
1017
- powerups.speed.duration -= dt;
1018
- if (powerups.speed.duration <= 0) {
1019
- powerups.speed.active = false;
1020
- game.speed = Math.min(game.speed - 3, MAX_SPEED);
1021
- updatePowerupUI();
1022
  }
1023
  }
1024
-
1025
- // Update powerup items
1026
- for (let i = powerupItems.length - 1; i >= 0; i--) {
1027
- const p = powerupItems[i];
1028
- p.x -= game.speed * 60 * (dt / 1000);
1029
- p.rotation += 0.03;
1030
- p.bobOffset = Math.sin(Date.now() * 0.005) * 5;
1031
-
1032
- // Collection check
1033
- const dx = dino.x - p.x;
1034
- const dy = (dino.y - 30) - (p.y + p.bobOffset);
1035
- const dist = Math.sqrt(dx * dx + dy * dy);
1036
-
1037
- if (dist < 40) {
1038
- activatePowerup(p.name, p.duration);
1039
- powerupItems.splice(i, 1);
1040
- continue;
1041
- }
1042
-
1043
- if (p.x < -50) {
1044
- powerupItems.splice(i, 1);
1045
- }
1046
  }
1047
  }
1048
-
1049
- function activatePowerup(name, duration) {
1050
- switch(name) {
1051
  case 'magnet':
1052
- powerups.magnet.active = true;
1053
- powerups.magnet.duration = duration;
1054
- break;
1055
- case 'multiplier':
1056
- powerups.multiplier.active = true;
1057
- powerups.multiplier.value = 2;
1058
- powerups.multiplier.duration = duration;
1059
  break;
1060
  case 'shield':
1061
- powerups.shield.active = true;
 
 
 
 
 
1062
  break;
1063
- case 'speed':
1064
- powerups.speed.active = true;
1065
- powerups.speed.duration = duration;
1066
- game.speed = Math.min(game.speed + 3, MAX_SPEED);
1067
  break;
1068
  }
1069
- updatePowerupUI();
1070
  }
1071
-
1072
- function updatePowerupUI() {
1073
- const indicator = document.getElementById('powerupIndicator');
1074
- indicator.innerHTML = '';
 
 
 
 
1075
 
1076
- if (powerups.magnet.active) {
1077
- indicator.innerHTML += '<div class="powerup-badge magnet active">🧲 MAGNET</div>';
1078
- }
1079
- if (powerups.multiplier.active) {
1080
- indicator.innerHTML += '<div class="powerup-badge multiplier active">x2 MULTIPLIER</div>';
1081
- }
1082
- if (powerups.shield.active) {
1083
- indicator.innerHTML += '<div class="powerup-badge shield active">🛡 SHIELD</div>';
1084
- }
1085
- if (powerups.speed.active) {
1086
- indicator.innerHTML += '<div class="powerup-badge speed active">⚡ SPEED</div>';
1087
- }
1088
  }
1089
-
1090
- function drawPowerups(ctx) {
1091
- powerupItems.forEach(p => {
1092
- ctx.save();
1093
- ctx.translate(p.x, p.y + p.bobOffset);
1094
- ctx.rotate(p.rotation);
1095
-
1096
- // Glow
1097
- ctx.shadowColor = p.color;
1098
- ctx.shadowBlur = 20;
1099
-
1100
- // Background circle
1101
- ctx.fillStyle = p.color;
1102
- ctx.beginPath();
1103
- ctx.arc(0, 0, 25, 0, Math.PI * 2);
1104
- ctx.fill();
1105
-
1106
- // Inner circle
1107
- ctx.fillStyle = '#fff';
1108
- ctx.beginPath();
1109
- ctx.arc(0, 0, 18, 0, Math.PI * 2);
1110
- ctx.fill();
1111
-
1112
- // Symbol
1113
- ctx.fillStyle = p.color;
1114
- ctx.font = 'bold 16px Arial';
1115
- ctx.textAlign = 'center';
1116
- ctx.textBaseline = 'middle';
1117
- ctx.fillText(p.symbol, 0, 0);
1118
-
1119
- ctx.restore();
1120
- });
1121
  }
1122
-
1123
- // ============================================
1124
- // BACKGROUND & ENVIRONMENT
1125
- // ============================================
1126
- const background = {
1127
- layers: [
1128
- { speed: 0.2, y: 0, color: '#1e3a5f' },
1129
- { speed: 0.4, y: 50, color: '#2563eb' },
1130
- { speed: 0.6, y: 100, color: '#3b82f6' }
1131
- ],
1132
- clouds: [],
1133
- buildings: [],
1134
-
1135
- init() {
1136
- // Generate clouds
1137
- for (let i = 0; i < 10; i++) {
1138
- this.clouds.push({
1139
- x: Math.random() * CANVAS_WIDTH,
1140
- y: 30 + Math.random() * 80,
1141
- size: 30 + Math.random() * 40,
1142
- speed: 0.3 + Math.random() * 0.3
1143
- });
1144
- }
1145
-
1146
- // Generate buildings
1147
- for (let i = 0; i < 15; i++) {
1148
- this.buildings.push({
1149
- x: i * 80,
1150
- width: 40 + Math.random() * 40,
1151
- height: 60 + Math.random() * 100,
1152
- color: `hsl(${200 + Math.random() * 40}, 60%, ${20 + Math.random() * 20}%)`
1153
- });
1154
- }
1155
- },
1156
-
1157
- update(dt) {
1158
- const moveAmount = game.speed * 60 * (dt / 1000);
1159
-
1160
- // Move clouds
1161
- this.clouds.forEach(cloud => {
1162
- cloud.x -= cloud.speed * moveAmount * 0.5;
1163
- if (cloud.x < -100) {
1164
- cloud.x = CANVAS_WIDTH + 50;
1165
- cloud.y = 30 + Math.random() * 80;
1166
- }
1167
- });
1168
-
1169
- // Move buildings
1170
- this.buildings.forEach(building => {
1171
- building.x -= moveAmount * 0.3;
1172
- if (building.x < -building.width) {
1173
- building.x = CANVAS_WIDTH + Math.random() * 50;
1174
- building.height = 60 + Math.random() * 100;
1175
- }
1176
- });
1177
- },
1178
-
1179
- draw(ctx) {
1180
- // Sky gradient
1181
- const gradient = ctx.createLinearGradient(0, 0, 0, CANVAS_HEIGHT);
1182
- gradient.addColorStop(0, '#1e3a5f');
1183
- gradient.addColorStop(0.5, '#3b82f6');
1184
- gradient.addColorStop(1, '#60a5fa');
1185
- ctx.fillStyle = gradient;
1186
- ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
1187
-
1188
- // Clouds
1189
- ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
1190
- this.clouds.forEach(cloud => {
1191
- ctx.beginPath();
1192
- ctx.arc(cloud.x, cloud.y, cloud.size * 0.6, 0, Math.PI * 2);
1193
- ctx.arc(cloud.x + cloud.size * 0.4, cloud.y - cloud.size * 0.2, cloud.size * 0.4, 0, Math.PI * 2);
1194
- ctx.arc(cloud.x + cloud.size * 0.8, cloud.y, cloud.size * 0.5, 0, Math.PI * 2);
1195
- ctx.fill();
1196
- });
1197
-
1198
- // Distant buildings (parallax)
1199
- this.buildings.forEach(building => {
1200
- ctx.fillStyle = building.color;
1201
- ctx.fillRect(building.x, GROUND_Y - building.height, building.width, building.height);
1202
-
1203
- // Windows
1204
- ctx.fillStyle = 'rgba(255, 255, 150, 0.6)';
1205
- for (let wy = GROUND_Y - building.height + 10; wy < GROUND_Y - 10; wy += 15) {
1206
- for (let wx = building.x + 5; wx < building.x + building.width - 10; wx += 12) {
1207
- if
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Dino Runner - Bitcoin Edition</title>
 
 
 
7
  <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@500;700&display=swap');
9
+
10
  * {
11
  margin: 0;
12
  padding: 0;
13
  box-sizing: border-box;
14
  }
15
+
16
+ :root {
17
+ --primary: #F7931A;
18
+ --secondary: #1a1a2e;
19
+ --accent: #00ff88;
20
+ --danger: #ff4757;
21
+ --gold: #ffd700;
22
+ }
23
+
24
  body {
25
+ background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
26
+ min-height: 100vh;
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ font-family: 'Rajdhani', sans-serif;
31
  overflow: hidden;
 
 
 
32
  }
33
+
34
+ #gameContainer {
35
+ position: relative;
36
+ width: 100%;
37
+ max-width: 800px;
38
+ aspect-ratio: 16/9;
39
+ background: linear-gradient(180deg, #87CEEB 0%, #98D8C8 50%, #7CB342 100%);
40
+ border-radius: 16px;
41
+ overflow: hidden;
42
+ box-shadow: 0 0 60px rgba(247, 147, 26, 0.3), 0 20px 60px rgba(0,0,0,0.5);
43
+ }
44
+
45
+ canvas {
46
  display: block;
47
+ width: 100%;
48
+ height: 100%;
49
  }
50
+
51
  .ui-overlay {
52
+ position: absolute;
53
  top: 0;
54
  left: 0;
55
  width: 100%;
56
  height: 100%;
57
  pointer-events: none;
58
+ display: flex;
59
+ flex-direction: column;
60
  }
61
+
62
+ .hud {
63
+ display: flex;
64
+ justify-content: space-between;
65
+ padding: 15px 25px;
66
+ background: linear-gradient(180deg, rgba(0,0,0,0.6) 0%, transparent 100%);
67
+ }
68
+
69
+ .hud-item {
70
+ color: white;
71
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
72
+ font-family: 'Orbitron', monospace;
73
+ }
74
+
75
+ .hud-label {
76
+ font-size: 10px;
77
+ color: var(--primary);
78
+ letter-spacing: 2px;
79
+ margin-bottom: 2px;
80
+ }
81
+
82
+ .hud-value {
83
+ font-size: 18px;
84
+ font-weight: 700;
85
+ }
86
+
87
+ .hud-value.btc {
88
+ color: var(--primary);
89
+ font-size: 20px;
90
+ }
91
+
92
+ .powerup-bar {
93
+ display: flex;
94
+ gap: 10px;
95
+ margin-top: 5px;
96
+ }
97
+
98
+ .powerup-indicator {
99
+ width: 30px;
100
+ height: 30px;
101
+ border-radius: 50%;
102
+ border: 2px solid white;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ font-size: 16px;
107
+ opacity: 0.3;
108
+ transition: all 0.3s;
109
+ }
110
+
111
+ .powerup-indicator.active {
112
+ opacity: 1;
113
+ transform: scale(1.1);
114
+ box-shadow: 0 0 15px currentColor;
115
+ }
116
+
117
  .screen {
118
  position: absolute;
119
  top: 0;
 
122
  height: 100%;
123
  display: flex;
124
  flex-direction: column;
 
125
  justify-content: center;
126
+ align-items: center;
127
+ background: rgba(0,0,0,0.85);
128
+ backdrop-filter: blur(10px);
129
+ pointer-events: auto;
130
+ transition: opacity 0.5s;
131
+ }
132
+
133
+ .screen.hidden {
134
  opacity: 0;
 
135
  pointer-events: none;
136
  }
137
+
138
+ .game-title {
139
+ font-family: 'Orbitron', monospace;
140
+ font-size: 48px;
141
+ font-weight: 900;
142
+ color: white;
143
+ text-shadow: 0 0 30px var(--primary), 0 0 60px var(--primary);
144
+ margin-bottom: 10px;
145
+ animation: pulse 2s infinite;
146
  }
147
+
148
+ .game-subtitle {
149
+ font-size: 24px;
150
+ color: var(--primary);
151
+ margin-bottom: 40px;
 
152
  letter-spacing: 4px;
 
 
 
 
 
 
 
 
153
  }
154
+
155
  @keyframes pulse {
156
  0%, 100% { transform: scale(1); }
157
  50% { transform: scale(1.05); }
158
  }
159
+
160
  .btn {
161
+ padding: 18px 50px;
162
+ font-family: 'Orbitron', monospace;
163
+ font-size: 18px;
164
+ font-weight: 700;
 
 
165
  border: none;
166
  border-radius: 50px;
167
  cursor: pointer;
168
+ transition: all 0.3s;
 
169
  text-transform: uppercase;
170
+ letter-spacing: 3px;
171
+ margin: 10px;
172
  }
173
+
174
+ .btn-primary {
175
+ background: linear-gradient(135deg, var(--primary) 0%, #ff9f43 100%);
176
+ color: white;
177
+ box-shadow: 0 10px 30px rgba(247, 147, 26, 0.4);
178
  }
179
+
180
+ .btn-primary:hover {
181
+ transform: translateY(-3px) scale(1.05);
182
+ box-shadow: 0 15px 40px rgba(247, 147, 26, 0.6);
183
  }
184
+
185
+ .btn-secondary {
186
+ background: transparent;
187
+ color: white;
188
+ border: 2px solid white;
189
+ }
190
+
191
+ .btn-secondary:hover {
192
+ background: white;
193
+ color: var(--secondary);
194
+ }
195
+
196
+ .controls-hint {
197
  margin-top: 40px;
198
+ color: rgba(255,255,255,0.6);
199
+ font-size: 14px;
200
  text-align: center;
 
201
  line-height: 2;
202
  }
203
+
204
+ .controls-hint span {
205
+ display: inline-block;
206
+ background: rgba(255,255,255,0.1);
207
+ padding: 5px 15px;
208
+ border-radius: 5px;
209
+ margin: 0 5px;
210
+ font-family: 'Orbitron', monospace;
211
+ font-size: 12px;
212
  }
213
+
214
+ .dino-preview {
215
+ width: 120px;
216
+ height: 120px;
217
+ margin-bottom: 30px;
218
+ animation: bounce 1s infinite;
 
 
 
 
 
 
219
  }
220
+
221
+ @keyframes bounce {
222
+ 0%, 100% { transform: translateY(0); }
223
+ 50% { transform: translateY(-20px); }
 
 
 
224
  }
225
+
226
+ .final-score {
227
+ font-family: 'Orbitron', monospace;
228
+ font-size: 60px;
229
+ color: var(--primary);
230
+ text-shadow: 0 0 30px var(--primary);
231
+ margin: 20px 0;
232
  }
233
+
234
+ .final-stats {
235
+ color: white;
236
+ font-size: 20px;
237
+ margin-bottom: 30px;
238
+ text-align: center;
239
+ line-height: 1.8;
240
  }
241
+
242
+ .combo-popup {
243
+ position: absolute;
244
+ font-family: 'Orbitron', monospace;
245
+ font-weight: 900;
246
+ font-size: 32px;
247
+ color: var(--gold);
248
+ text-shadow: 0 0 20px var(--gold), 2px 2px 0 #000;
249
+ pointer-events: none;
250
+ animation: comboFade 1s forwards;
251
  }
252
+
253
+ @keyframes comboFade {
254
+ 0% { opacity: 1; transform: translateY(0) scale(1); }
255
+ 100% { opacity: 0; transform: translateY(-50px) scale(1.5); }
 
 
 
 
 
256
  }
257
+
258
+ .pause-overlay {
259
+ position: absolute;
260
+ top: 50%;
261
+ left: 50%;
262
+ transform: translate(-50%, -50%);
263
+ background: rgba(0,0,0,0.9);
264
+ padding: 40px 60px;
265
  border-radius: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  text-align: center;
267
+ pointer-events: auto;
268
+ display: none;
269
  }
270
+
271
+ .pause-overlay.visible {
272
+ display: block;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
+
275
+ .pause-title {
276
+ font-family: 'Orbitron', monospace;
277
+ font-size: 36px;
278
+ color: white;
279
+ margin-bottom: 30px;
280
  }
281
+
282
+ .powered-by {
283
+ position: absolute;
284
  bottom: 10px;
285
  left: 50%;
286
  transform: translateX(-50%);
287
+ font-family: 'Rajdhani', sans-serif;
288
+ font-size: 12px;
289
+ color: rgba(255,255,255,0.5);
 
 
 
 
290
  text-decoration: none;
291
+ transition: color 0.3s;
292
  }
293
+
294
+ .powered-by:hover {
295
+ color: var(--primary);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  }
297
  </style>
298
  </head>
299
  <body>
300
+ <div id="gameContainer">
301
+ <canvas id="gameCanvas"></canvas>
302
+
303
+ <div class="ui-overlay" id="uiOverlay">
304
+ <div class="hud" id="hud" style="display: none;">
305
+ <div class="hud-item">
306
+ <div class="hud-label">DISTANCE</div>
307
+ <div class="hud-value" id="distanceDisplay">0m</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  </div>
309
+ <div class="hud-item">
310
+ <div class="hud-label"> BALANCE</div>
311
+ <div class="hud-value btc" id="btcDisplay">₿ 0.00000000</div>
312
  </div>
313
+ <div class="hud-item">
314
+ <div class="hud-label">SPEED</div>
315
+ <div class="hud-value" id="speedDisplay">0 km/h</div>
316
+ </div>
317
+ <div class="powerup-bar">
318
+ <div class="powerup-indicator" id="magnetInd" style="color: #00ffff;">🧲</div>
319
+ <div class="powerup-indicator" id="shieldInd" style="color: #ff6b6b;">🛡️</div>
320
+ <div class="powerup-indicator" id="multiInd" style="color: #ffd700;">✖️2</div>
321
  </div>
322
  </div>
 
323
  </div>
324
+
325
+ <div class="screen" id="startScreen">
326
+ <canvas class="dino-preview" id="dinoPreview"></canvas>
327
+ <div class="game-title">DINO RUNNER</div>
328
+ <div class="game-subtitle">BITCOIN EDITION</div>
329
+ <button class="btn btn-primary" id="startBtn">START GAME</button>
330
+ <div class="controls-hint">
331
+ <span>← →</span> Move &nbsp;&nbsp;
332
+ <span>↑</span> Jump &nbsp;&nbsp;
333
+ <span>↓</span> Slide<br>
334
+ <span>SPACE</span> Pause
335
+ </div>
336
+ </div>
337
+
338
+ <div class="screen hidden" id="gameOverScreen">
339
+ <div class="game-title" style="font-size: 36px;">GAME OVER</div>
340
+ <div class="final-score" id="finalScore">0m</div>
341
+ <div class="final-stats" id="finalStats">
342
+ Distance: 0m<br>
343
+ Coins: 0 (₿0.00000000)<br>
344
+ Best: 0m
345
+ </div>
346
+ <button class="btn btn-primary" id="restartBtn">PLAY AGAIN</button>
347
+ <button class="btn btn-secondary" id="menuBtn">MAIN MENU</button>
348
+ </div>
349
+
350
+ <div class="pause-overlay" id="pauseOverlay">
351
+ <div class="pause-title">PAUSED</div>
352
+ <button class="btn btn-primary" id="resumeBtn">RESUME</button>
353
+ </div>
354
+
355
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="powered-by">Built with anycoder</a>
356
  </div>
357
 
358
  <script>
359
+ const canvas = document.getElementById('gameCanvas');
360
+ const ctx = canvas.getContext('2d');
361
+ const dinoPreviewCanvas = document.getElementById('dinoPreview');
362
+ const dinoPreviewCtx = dinoPreviewCanvas.getContext('2d');
363
+
364
+ dinoPreviewCanvas.width = 120;
365
+ dinoPreviewCanvas.height = 120;
366
+
367
+ let width, height;
368
+
369
+ function resize() {
370
+ const container = document.getElementById('gameContainer');
371
+ width = container.clientWidth;
372
+ height = container.clientHeight;
373
+ canvas.width = width;
374
+ canvas.height = height;
375
+ }
376
+
377
+ resize();
378
+ window.addEventListener('resize', resize);
379
+
380
+ // Game State
381
+ const GameState = {
382
+ START: 'start',
383
+ PLAYING: 'playing',
384
+ PAUSED: 'paused',
385
+ GAMEOVER: 'gameover'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  };
387
+
388
+ let gameState = GameState.START;
389
+ let lastTime = 0;
390
+ let deltaTime = 0;
391
+
392
+ // Game Variables
393
+ let distance = 0;
394
+ let btcBalance = 0;
395
+ let speed = 0;
396
+ let baseSpeed = 400;
397
+ let maxSpeed = 1200;
398
+ let score = 0;
399
+ let combo = 0;
400
+ let comboTimer = 0;
401
+ let bestDistance = parseFloat(localStorage.getItem('dinoBestDistance')) || 0;
402
+
403
+ // Lanes
404
+ const lanes = [-1, 0, 1];
405
+ let currentLane = 1;
406
+
407
+ // Player
408
+ const player = {
409
+ x: 0,
410
+ y: 0,
411
  width: 60,
412
  height: 80,
413
+ lane: 1,
414
+ targetLane: 1,
415
+ yVelocity: 0,
416
  isJumping: false,
417
  isSliding: false,
418
  slideTimer: 0,
419
  runFrame: 0,
420
  runTimer: 0,
421
+ bounceOffset: 0,
422
+ bounceTimer: 0,
423
+ hasShield: false,
424
+ magnetActive: false,
425
+ multiplierActive: false,
426
+ speedBoostActive: false,
427
+ powerupTimers: {
428
+ magnet: 0,
429
+ multiplier: 0,
430
+ shield: 0,
431
+ speedBoost: 0
432
+ }
433
+ };
434
+
435
+ // Collections
436
+ let obstacles = [];
437
+ let coins = [];
438
+ let powerups = [];
439
+ let particles = [];
440
+ let backgroundLayers = [];
441
+
442
+ // Input
443
+ const keys = {};
444
+
445
+ // Lane positions
446
+ function getLaneX(lane) {
447
+ const laneWidth = width / 3;
448
+ return width / 2 + (lane - 1) * laneWidth;
449
+ }
450
+
451
+ // Initialize background layers
452
+ function initBackground() {
453
+ backgroundLayers = [
454
+ { speed: 0.1, elements: [], color: '#4a90d9' },
455
+ { speed: 0.3, elements: [], color: '#5ba85f' },
456
+ { speed: 0.5, elements: [], color: '#7cb342' }
457
+ ];
458
+
459
+ // Add mountains/clouds
460
+ for (let i = 0; i < 5; i++) {
461
+ backgroundLayers[0].elements.push({
462
+ x: Math.random() * width * 2,
463
+ y: height * 0.3,
464
+ size: 80 + Math.random() * 60,
465
+ type: 'cloud'
466
+ });
467
+ }
468
+
469
+ // Add trees
470
+ for (let i = 0; i < 10; i++) {
471
+ backgroundLayers[1].elements.push({
472
+ x: Math.random() * width * 2,
473
+ y: height * 0.6,
474
+ size: 40 + Math.random() * 30,
475
+ type: Math.random() > 0.5 ? 'tree' : 'bush'
476
+ });
477
+ }
478
+ }
479
+
480
+ // Draw parallax background
481
+ function drawBackground() {
482
+ // Sky gradient
483
+ const gradient = ctx.createLinearGradient(0, 0, 0, height);
484
+ gradient.addColorStop(0, '#87CEEB');
485
+ gradient.addColorStop(0.5, '#98D8C8');
486
+ gradient.addColorStop(1, '#7CB342');
487
+ ctx.fillStyle = gradient;
488
+ ctx.fillRect(0, 0, width, height);
489
+
490
+ // Draw background layers
491
+ backgroundLayers.forEach((layer, idx) => {
492
+ ctx.fillStyle = layer.color;
493
+ layer.elements.forEach(el => {
494
+ if (el.type === 'cloud') {
495
+ drawCloud(el.x, el.y, el.size);
496
+ } else if (el.type === 'tree') {
497
+ drawTree(el.x, el.y, el.size);
498
+ } else if (el.type === 'bush') {
499
+ drawBush(el.x, el.y, el.size);
500
  }
501
+ });
502
+ });
503
+
504
+ // Ground
505
+ ctx.fillStyle = '#5d4037';
506
+ ctx.fillRect(0, height * 0.75, width, height * 0.25);
507
+
508
+ // Lane lines
509
+ const laneWidth = width / 3;
510
+ ctx.strokeStyle = 'rgba(255,255,255,0.3)';
511
+ ctx.lineWidth = 2;
512
+ ctx.setLineDash([20, 20]);
513
+
514
+ for (let i = 0; i < 2; i++) {
515
+ ctx.beginPath();
516
+ ctx.moveTo(laneWidth * (i + 1), height * 0.75);
517
+ ctx.lineTo(laneWidth * (i + 1), height);
518
+ ctx.stroke();
519
+ }
520
+ ctx.setLineDash([]);
521
+ }
522
+
523
+ function drawCloud(x, y, size) {
524
+ ctx.fillStyle = 'rgba(255,255,255,0.8)';
525
+ ctx.beginPath();
526
+ ctx.arc(x, y, size * 0.5, 0, Math.PI * 2);
527
+ ctx.arc(x + size * 0.4, y - size * 0.2, size * 0.4, 0, Math.PI * 2);
528
+ ctx.arc(x + size * 0.8, y, size * 0.5, 0, Math.PI * 2);
529
+ ctx.fill();
530
+ }
531
+
532
+ function drawTree(x, y, size) {
533
+ ctx.fillStyle = '#2e7d32';
534
+ ctx.beginPath();
535
+ ctx.moveTo(x, y - size);
536
+ ctx.lineTo(x - size * 0.5, y);
537
+ ctx.lineTo(x + size * 0.5, y);
538
+ ctx.fill();
539
+ ctx.fillStyle = '#5d4037';
540
+ ctx.fillRect(x - size * 0.1, y, size * 0.2, size * 0.3);
541
+ }
542
+
543
+ function drawBush(x, y, size) {
544
+ ctx.fillStyle = '#388e3c';
545
+ ctx.beginPath();
546
+ ctx.arc(x, y, size * 0.5, 0, Math.PI * 2);
547
+ ctx.arc(x - size * 0.3, y + size * 0.2, size * 0.4, 0, Math.PI * 2);
548
+ ctx.arc(x + size * 0.3, y + size * 0.2, size * 0.4, 0, Math.PI * 2);
549
+ ctx.fill();
550
+ }
551
+
552
+ // Draw the dinosaur
553
+ function drawDinosaur(x, y, w, h, preview = false) {
554
+ const c = preview ? dinoPreviewCtx : ctx;
555
+ const isSliding = player.isSliding;
556
+ const actualH = isSliding ? h * 0.5 : h;
557
+ const actualY = isSliding ? y + h * 0.5 : y;
558
+
559
+ // Body
560
+ c.fillStyle = '#4CAF50';
561
+ c.beginPath();
562
+ c.ellipse(x, actualY + actualH * 0.4, w * 0.5, actualH * 0.4, 0, 0, Math.PI * 2);
563
+ c.fill();
564
+
565
+ // Head
566
+ c.beginPath();
567
+ c.ellipse(x + w * 0.3, actualY + actualH * 0.2, w * 0.4, actualH * 0.3, 0.2, 0, Math.PI * 2);
568
+ c.fill();
569
+
570
+ // Eyes
571
+ const eyeY = actualY + actualH * 0.15;
572
+ c.fillStyle = 'white';
573
+ c.beginPath();
574
+ c.ellipse(x + w * 0.35, eyeY, w * 0.12, h * 0.12, 0, 0, Math.PI * 2);
575
+ c.fill();
576
+
577
+ c.fillStyle = '#1a1a1a';
578
+ c.beginPath();
579
+ c.ellipse(x + w * 0.38, eyeY, w * 0.06, h * 0.06, 0, 0, Math.PI * 2);
580
+ c.fill();
581
+
582
+ // Eye shine
583
+ c.fillStyle = 'white';
584
+ c.beginPath();
585
+ c.arc(x + w * 0.4, eyeY - h * 0.02, w * 0.02, 0, Math.PI * 2);
586
+ c.fill();
587
+
588
+ // Mouth
589
+ c.strokeStyle = '#2e7d32';
590
+ c.lineWidth = 2;
591
+ c.beginPath();
592
+ if (gameState === GameState.PLAYING) {
593
+ c.arc(x + w * 0.4, actualY + actualH * 0.3, w * 0.1, 0, Math.PI);
594
+ } else {
595
+ c.moveTo(x + w * 0.3, actualY + actualH * 0.28);
596
+ c.lineTo(x + w * 0.5, actualY + actualH * 0.28);
597
+ }
598
+ c.stroke();
599
+
600
+ // Headband
601
+ c.fillStyle = '#f44336';
602
+ c.fillRect(x + w * 0.1, actualY + actualH * 0.05, w * 0.6, h * 0.08);
603
+
604
+ // Bitcoin symbol on headband
605
+ c.fillStyle = '#F7931A';
606
+ c.font = `bold ${h * 0.15}px Arial`;
607
+ c.textAlign = 'center';
608
+ c.fillText('₿', x + w * 0.4, actualY + actualH * 0.15);
609
+
610
+ // Tail
611
+ c.fillStyle = '#4CAF50';
612
+ c.beginPath();
613
+ c.moveTo(x - w * 0.4, actualY + actualH * 0.5);
614
+ c.quadraticCurveTo(x - w * 0.7, actualY + actualH * 0.3, x - w * 0.5, actualY + actualH * 0.6);
615
+ c.quadraticCurveTo(x - w * 0.3, actualY + actualH * 0.7, x - w * 0.4, actualY + actualH * 0.5);
616
+ c.fill();
617
+
618
+ // Legs (animated)
619
+ const legOffset = Math.sin(player.runTimer * 15) * 10;
620
+
621
+ if (!player.isJumping && !isSliding) {
622
+ // Front leg
623
+ c.fillStyle = '#388e3c';
624
+ c.beginPath();
625
+ c.ellipse(x + w * 0.1, actualY + actualH * 0.7 + legOffset, w * 0.12, h * 0.15, 0, 0, Math.PI * 2);
626
+ c.fill();
627
+
628
+ // Back leg
629
+ c.beginPath();
630
+ c.ellipse(x - w * 0.1, actualY + actualH * 0.7 - legOffset, w * 0.12, h * 0.15, 0, 0, Math.PI * 2);
631
+ c.fill();
632
+ }
633
+
634
+ // Shield effect
635
+ if (player.hasShield) {
636
+ c.strokeStyle = '#ff6b6b';
637
+ c.lineWidth = 3;
638
+ c.globalAlpha = 0.5 + Math.sin(Date.now() * 0.01) * 0.3;
639
+ c.beginPath();
640
+ c.arc(x, actualY + actualH * 0.4, w * 0.8, 0, Math.PI * 2);
641
+ c.stroke();
642
+ c.globalAlpha = 1;
643
+ }
644
+
645
+ // Magnet effect
646
+ if (player.magnetActive) {
647
+ c.strokeStyle = '#00ffff';
648
+ c.lineWidth = 2;
649
+ c.globalAlpha = 0.5 + Math.sin(Date.now() * 0.015) * 0.3;
650
+ c.beginPath();
651
+ c.arc(x, actualY + actualH * 0.4, w * 1.2, 0, Math.PI * 2);
652
+ c.stroke();
653
+ c.globalAlpha = 1;
654
+ }
655
+ }
656
+
657
+ // Draw obstacles
658
+ function drawObstacle(obs) {
659
+ const x = getLaneX(obs.lane);
660
+ const y = height * 0.75 - obs.height;
661
+
662
+ ctx.fillStyle = obs.color || '#e74c3c';
663
+
664
+ if (obs.type === 'barrier') {
665
+ // Barrier with stripes
666
+ ctx.fillRect(x - 30, y, 60, obs.height);
667
+
668
+ ctx.fillStyle = '#f39c12';
669
+ for (let i = 0; i < 3; i++) {
670
+ ctx.fillRect(x - 30 + i * 20, y + i * 15, 10, obs.height / 3);
671
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
 
673
+ // Top light
674
+ ctx.fillStyle = obs.passed ? '#27ae60' : '#e74c3c';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  ctx.beginPath();
676
+ ctx.arc(x, y - 10, 8, 0, Math.PI * 2);
677
  ctx.fill();
 
 
 
 
 
 
 
 
 
 
 
 
678
 
679
+ } else if (obs.type === 'low') {
680
+ // Low barrier - need to jump
681
+ ctx.fillStyle = '#9b59b6';
682
+ ctx.fillRect(x - 25, y + 20, 50, obs.height - 20);
683
+
684
+ // Warning stripes
685
+ ctx.fillStyle = '#f1c40f';
686
+ ctx.fillRect(x - 25, y + 20, 50, 8);
687
+
688
+ } else if (obs.type === 'high') {
689
+ // High barrier - need to slide
690
+ ctx.fillStyle = '#3498db';
691
+ ctx.fillRect(x - 25, y, 50, obs.height - 30);
692
+
693
+ // Hanging effect
694
+ ctx.strokeStyle = '#3498db';
695
+ ctx.lineWidth = 3;
696
  ctx.beginPath();
697
+ ctx.moveTo(x - 20, y);
698
+ ctx.lineTo(x - 20, y - 30);
699
+ ctx.moveTo(x + 20, y);
700
+ ctx.lineTo(x + 20, y - 30);
701
+ ctx.stroke();
702
+ }
703
+ }
704
+
705
+ // Draw Bitcoin coin
706
+ function drawCoin(coin) {
707
+ const x = coin.x;
708
+ const y = coin.y;
709
+ const size = 25;
710
+
711
+ // Glow effect
712
+ const glow = ctx.createRadialGradient(x, y, 0, x, y, size * 1.5);
713
+ glow.addColorStop(0, 'rgba(247, 147, 26, 0.4)');
714
+ glow.addColorStop(1, 'transparent');
715
+ ctx.fillStyle = glow;
716
+ ctx.beginPath();
717
+ ctx.arc(x, y, size * 1.5, 0, Math.PI * 2);
718
+ ctx.fill();
719
+
720
+ // Spinning effect (scale X)
721
+ const spin = Math.cos(coin.rotation || 0);
722
+
723
+ ctx.save();
724
+ ctx.translate(x, y);
725
+ ctx.scale(Math.abs(spin), 1);
726
+
727
+ // Coin body
728
+ const coinGrad = ctx.createLinearGradient(-size, -size, size, size);
729
+ coinGrad.addColorStop(0, '#ffd700');
730
+ coinGrad.addColorStop(0.5, '#ffec8b');
731
+ coinGrad.addColorStop(1, '#daa520');
732
+ ctx.fillStyle = coinGrad;
733
+
734
+ ctx.beginPath();
735
+ ctx.arc(0, 0, size, 0, Math.PI * 2);
736
+ ctx.fill();
737
+
738
+ // Bitcoin symbol
739
+ if (Math.abs(spin) > 0.3) {
740
+ ctx.fillStyle = '#F7931A';
741
+ ctx.font = 'bold 20px Arial';
742
  ctx.textAlign = 'center';
743
+ ctx.textBaseline = 'middle';
744
+ ctx.fillText('₿', 0, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
745
  }
746
+
747
+ ctx.restore();
748
+ }
749
+
750
+ // Draw powerups
751
+ function drawPowerup(pup) {
752
+ const x = pup.x;
753
+ const y = pup.y;
754
+
755
+ ctx.save();
756
+
757
+ // Pulsing effect
758
+ const pulse = 1 + Math.sin(Date.now() * 0.005) * 0.2;
759
+ ctx.translate(x, y);
760
+ ctx.scale(pulse, pulse);
761
+
762
+ // Background circle
763
+ let color;
764
+ let symbol;
765
+ switch(pup.type) {
766
+ case 'magnet': color = '#00ffff'; symbol = '🧲'; break;
767
+ case 'shield': color = '#ff6b6b'; symbol = '🛡️'; break;
768
+ case 'multiplier': color = '#ffd700'; symbol = '✖️2'; break;
769
+ case 'speedBoost': color = '#ff9f43'; symbol = '⚡'; break;
770
+ }
771
+
772
+ ctx.fillStyle = color;
773
+ ctx.globalAlpha = 0.3;
774
+ ctx.beginPath();
775
+ ctx.arc(0, 0, 25, 0, Math.PI * 2);
776
+ ctx.fill();
777
+
778
+ ctx.globalAlpha = 1;
779
+ ctx.strokeStyle = color;
780
+ ctx.lineWidth = 3;
781
+ ctx.beginPath();
782
+ ctx.arc(0, 0, 25, 0, Math.PI * 2);
783
+ ctx.stroke();
784
+
785
+ ctx.font = '20px Arial';
786
+ ctx.textAlign = 'center';
787
+ ctx.textBaseline = 'middle';
788
+ ctx.fillText(symbol, 0, 0);
789
+
790
+ ctx.restore();
791
+ }
792
+
793
+ // Draw particles
794
+ function drawParticles() {
795
+ particles.forEach((p, i) => {
796
+ ctx.globalAlpha = p.life;
797
+ ctx.fillStyle = p.color;
798
+ ctx.beginPath();
799
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
800
+ ctx.fill();
801
+ ctx.globalAlpha = 1;
802
+ });
803
+ }
804
+
805
+ // Spawn obstacles
806
  function spawnObstacle() {
807
+ const types = ['barrier', 'low', 'high'];
808
+ const type = types[Math.floor(Math.random() * types.length)];
809
  const lane = Math.floor(Math.random() * 3);
810
 
811
+ let height = 60;
812
+ if (type === 'barrier') height = 60;
813
+ else if (type === 'low') height = 40;
814
+ else if (type === 'high') height = 100;
815
+
816
  obstacles.push({
 
 
817
  lane: lane,
818
+ x: getLaneX(lane),
819
+ y: height * 0.75,
820
+ height: height,
821
+ type: type,
822
+ passed: false,
823
+ color: type === 'barrier' ? '#e74c3c' : type === 'low' ? '#9b59b6' : '#3498db'
824
  });
825
  }
826
+
827
+ // Spawn coin
828
+ function spawnCoin() {
829
+ const lane = Math.floor(Math.random() * 3);
830
+ const x = getLaneX(lane);
831
+
832
+ // Random pattern: straight line, arc, or scattered
833
+ const pattern = Math.floor(Math.random() * 3);
834
+ const count = 3 + Math.floor(Math.random() * 5);
835
+
836
+ for (let i = 0; i < count; i++) {
837
+ let y = height * 0.65;
838
+ let offsetX = 0;
 
839
 
840
+ if (pattern === 1) {
841
+ // Arc
842
+ offsetX = Math.sin(i / count * Math.PI) * 60 - 30;
843
+ } else if (pattern === 2) {
844
+ // Scattered
845
+ y = height * 0.55 - Math.random() * 100;
846
+ } else {
847
+ // Line
848
+ offsetX = i * 40;
849
  }
850
+
851
+ coins.push({
852
+ x: x + offsetX,
853
+ y: y + i * 35,
854
+ lane: lane,
855
+ value: (0.00000001 + Math.random() * 0.00000009).toFixed(8),
856
+ rotation: Math.random() * Math.PI * 2,
857
+ collected: false
858
+ });
859
  }
860
  }
861
+
862
+ // Spawn powerup
863
+ function spawnPowerup() {
864
+ const types = ['magnet', 'shield', 'multiplier', 'speedBoost'];
865
+ const type = types[Math.floor(Math.random() * types.length)];
866
+ const lane = Math.floor(Math.random() * 3);
867
+
868
+ powerups.push({
869
+ x: getLaneX(lane),
870
+ y: height * 0.5,
871
+ lane: lane,
872
+ type: type
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  });
874
  }
875
+
876
+ // Create particles
877
+ function createParticles(x, y, color, count = 10) {
878
+ for (let i = 0; i < count; i++) {
879
+ particles.push({
880
+ x: x,
881
+ y: y,
882
+ vx: (Math.random() - 0.5) * 200,
883
+ vy: (Math.random() - 0.5) * 200 - 100,
884
+ size: Math.random() * 5 + 2,
885
+ color: color,
886
+ life: 1
887
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888
  }
 
889
  }
890
+
891
+ // Update game
892
+ function update(dt) {
893
+ if (gameState !== GameState.PLAYING) return;
894
+
895
+ // Update speed
896
+ const targetSpeed = player.speedBoostActive ? maxSpeed : Math.min(baseSpeed + distance * 0.1, maxSpeed);
897
+ speed += (targetSpeed - speed) * 0.01;
898
+
899
+ // Update distance
900
+ distance += speed * dt / 1000;
901
+ score = Math.floor(distance);
902
+
903
+ // Update player
904
+ updatePlayer(dt);
905
+
906
+ // Update background
907
+ backgroundLayers.forEach(layer => {
908
+ layer.elements.forEach(el => {
909
+ el.x -= speed * dt / 1000 * layer.speed;
910
+ if (el.x < -100) {
911
+ el.x = width + 100 + Math.random() * 200;
912
+ }
913
+ });
914
+ });
915
+
916
+ // Spawn obstacles
917
+ if (Math.random() < 0.02 + distance * 0.0001) {
918
+ const minDist = 300;
919
+ const lastObs = obstacles[obstacles.length - 1];
920
+ if (!lastObs || lastObs.x < width - minDist - speed * 0.3) {
921
+ spawnObstacle();
 
 
 
 
 
 
922
  }
923
  }
924
+
 
 
925
  // Spawn coins
926
  if (Math.random() < 0.015) {
927
+ const lastCoin = coins[coins.length - 1];
928
+ if (!lastCoin || lastCoin.x < width - 200) {
929
+ spawnCoin();
930
+ }
931
  }
932
+
933
+ // Spawn powerups (rare)
934
+ if (Math.random() < 0.002) {
935
+ const lastPup = powerups[powerups.length - 1];
936
+ if (!lastPup || lastPup.x < width - 300) {
937
+ spawnPowerup();
938
+ }
 
 
 
 
 
 
 
939
  }
940
+
941
+ // Update obstacles
942
+ obstacles.forEach((obs, i) => {
943
+ obs.x -= speed * dt / 1000;
944
+
945
+ // Check collision
946
+ const playerLane = player.targetLane;
947
+ const inSameLane = obs.lane === playerLane;
948
+
949
+ if (inSameLane && !obs.passed) {
950
+ const playerY = height * 0.75 - player.height;
951
+ const obsTop = height * 0.75 - obs.height;
 
952
 
953
+ let collision = false;
954
+
955
+ if (obs.type === 'barrier') {
956
+ collision = playerY + player.height > obsTop + 10;
957
+ } else if (obs.type === 'low') {
958
+ // Low obstacles - need to jump
959
+ collision = playerY + player.height > obsTop + 10 && !player.isJumping;
960
+ } else if (obs.type === 'high') {
961
+ // High obstacles - need to slide
962
+ const slideHit = player.isSliding && playerY + player.height * 0.5 > obsTop;
963
+ const standHit = !player.isSliding && playerY + player.height > obsTop + 30;
964
+ collision = slideHit || standHit;
965
+ }
966
+
967
+ if (collision) {
968
+ if (player.hasShield) {
969
+ player.hasShield = false;
970
+ player.powerupTimers.shield = 0;
971
+ createParticles(obs.x, obs.y, '#ff6b6b', 20);
972
+ obs.passed = true;
973
+ } else {
974
+ gameOver();
975
  }
976
  }
977
  }
978
+
979
+ if (obs.x < -50) {
980
+ obs.passed = true;
 
 
 
 
 
 
 
 
 
 
 
981
  }
982
+ });
 
 
 
 
 
983
 
984
+ // Update coins
985
+ coins.forEach((coin, i) => {
986
+ // Magnet effect
987
+ if (player.magnetActive && Math.abs(coin.x - player.x) < 300) {
988
+ const dx = player.x - coin.x;
989
+ const dy = player.y - coin.y;
990
+ const dist = Math.sqrt(dx * dx + dy * dy);
991
+ coin.x += (dx / dist) * 400 * dt / 1000;
992
+ coin.y += (dy / dist) * 400 * dt / 1000;
993
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994
 
995
+ coin.x -= speed * dt / 1000;
996
+ coin.rotation += dt * 0.005;
 
 
 
 
 
 
 
 
997
 
998
+ // Collection
999
+ const dx = coin.x - player.x;
1000
+ const dy = coin.y - (player.y + player.height * 0.3);
1001
+ const dist = Math.sqrt(dx * dx + dy * dy);
1002
+
1003
+ if (dist < 40 && !coin.collected) {
1004
+ coin.collected = true;
1005
+ let value = parseFloat(coin.value);
1006
+ if (player.multiplierActive) {
1007
+ value *= 2;
1008
+ }
1009
+ btcBalance += value;
1010
+
1011
+ combo++;
1012
+ comboTimer = 2;
1013
+
1014
+ createParticles(coin.x, coin.y, '#F7931A', 15);
1015
+ showComboPopup(`+₿${coin.value}`);
 
 
1016
  }
 
 
1017
  });
1018
+
1019
+ // Update powerups
1020
+ powerups.forEach((pup, i) => {
1021
+ pup.x -= speed * dt / 1000;
1022
+
1023
+ const dx = pup.x - player.x;
1024
+ const dy = pup.y - (player.y + player.height * 0.3);
1025
+ const dist = Math.sqrt(dx * dx + dy * dy);
1026
+
1027
+ if (dist < 50) {
1028
+ activatePowerup(pup.type);
1029
+ powerups.splice(i, 1);
1030
+ createParticles(pup.x, pup.y, '#fff', 20);
1031
+ }
1032
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1033
 
1034
+ // Update particles
1035
+ particles.forEach((p, i) => {
1036
+ p.x += p.vx * dt / 1000;
1037
+ p.y += p.vy * dt / 1000;
1038
+ p.vy += 500 * dt / 1000;
1039
+ p.life -= dt / 1000;
1040
+
1041
+ if (p.life <= 0) {
1042
+ particles.splice(i, 1);
1043
+ }
1044
  });
1045
+
1046
+ // Clean up off-screen objects
1047
+ obstacles = obstacles.filter(obs => obs.x > -100);
1048
+ coins = coins.filter(c => c.x > -50 && !c.collected);
1049
+ powerups = powerups.filter(p => p.x > -50);
1050
+
 
 
1051
  // Update powerup timers
1052
+ Object.keys(player.powerupTimers).forEach(key => {
1053
+ if (player.powerupTimers[key] > 0) {
1054
+ player.powerupTimers[key] -= dt / 1000;
1055
+
1056
+ if (key === 'shield') {
1057
+ player.hasShield = player.powerupTimers.shield > 0;
1058
+ }
1059
+ if (player.powerupTimers[key] <= 0) {
1060
+ player[key === 'multiplier' ? 'multiplierActive' :
1061
+ key === 'speedBoost' ? 'speedBoostActive' :
1062
+ key + 'Active'] = false;
1063
+ }
1064
  }
1065
+ });
1066
+
1067
+ // Update combo
1068
+ if (comboTimer > 0) {
1069
+ comboTimer -= dt / 1000;
1070
+ } else {
1071
+ combo = 0;
1072
  }
1073
+
1074
+ // Update HUD
1075
+ updateHUD();
1076
+ }
1077
+
1078
+ function updatePlayer(dt) {
1079
+ // Lane movement
1080
+ const targetX = getLaneX(player.targetLane);
1081
+ player.x += (targetX - player.x) * 0.15;
1082
+
1083
+ // Jump physics
1084
+ if (player.isJumping) {
1085
+ player.yVelocity -= 1800 * dt / 1000;
1086
+ player.y += player.yVelocity * dt / 1000;
1087
+
1088
+ const groundY = height * 0.75 - player.height;
1089
+ if (player.y >= groundY) {
1090
+ player.y = groundY;
1091
+ player.isJumping = false;
1092
+ player.yVelocity = 0;
1093
  }
1094
+ } else {
1095
+ player.y = height * 0.75 - player.height;
1096
  }
1097
+
1098
+ // Slide
1099
+ if (player.isSliding) {
1100
+ player.slideTimer -= dt / 1000;
1101
+ if (player.slideTimer <= 0) {
1102
+ player.isSliding = false;
 
1103
  }
1104
  }
1105
+
1106
+ // Running animation
1107
+ player.runTimer += dt / 1000;
1108
+
1109
+ // Bounce
1110
+ if (!player.isJumping && !player.isSliding) {
1111
+ player.bounceTimer += dt / 1000;
1112
+ player.bounceOffset = Math.sin(player.bounceTimer * 15) * 3;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1113
  }
1114
  }
1115
+
1116
+ function activatePowerup(type) {
1117
+ switch(type) {
1118
  case 'magnet':
1119
+ player.magnetActive = true;
1120
+ player.powerupTimers.magnet = 8;
 
 
 
 
 
1121
  break;
1122
  case 'shield':
1123
+ player.hasShield = true;
1124
+ player.powerupTimers.shield = 10;
1125
+ break;
1126
+ case 'multiplier':
1127
+ player.multiplierActive = true;
1128
+ player.powerupTimers.multiplier = 15;
1129
  break;
1130
+ case 'speedBoost':
1131
+ player.speedBoostActive = true;
1132
+ player.powerupTimers.speedBoost = 5;
 
1133
  break;
1134
  }
 
1135
  }
1136
+
1137
+ function showComboPopup(text) {
1138
+ const popup = document.createElement('div');
1139
+ popup.className = 'combo-popup';
1140
+ popup.textContent = text;
1141
+ popup.style.left = (player.x + 50) + 'px';
1142
+ popup.style.top = (player.y - 20) + 'px';
1143
+ document.getElementById('gameContainer').appendChild(popup);
1144
 
1145
+ setTimeout(() => popup.remove(), 1000);
 
 
 
 
 
 
 
 
 
 
 
1146
  }
1147
+
1148
+ function updateHUD() {
1149
+ document.getElementById('distanceDisplay').textContent = Math.floor(distance) + 'm';
1150
+ document.getElementById('btcDisplay').textContent = '₿ ' + btcBalance.toFixed(8);
1151
+ document.getElementById('speedDisplay').textContent = Math.floor(speed * 0.36) + ' km/h';
1152
+
1153
+ // Powerup indicators
1154
+ document.getElementById('magnetInd').classList.toggle('active', player.magnetActive);
1155
+ document.getElementById('shieldInd').classList.toggle('active', player.hasShield);
1156
+ document.getElementById('multiInd').classList.toggle('active', player.multiplierActive);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1157
  }
1158
+
1159
+ // Draw everything
1160
+ function draw() {
1161
+ ctx.clearRect(0, 0, width, height);
1162
+
1163
+ drawBackground