Facebook
From chrome, 5 Years ago, written in JavaScript.
Embed
Download Paste or View Raw
Hits: 406
  1. // Function Fix for "requestAnimationFrame" 12.06.17
  2. (function() {
  3. var lastTime = 0;
  4. var vendors = ['ms', 'moz', 'webkit', 'o'];
  5. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  6. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  7. window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
  8. || window[vendors[x]+'CancelRequestAnimationFrame'];
  9. }
  10. if (!window.requestAnimationFrame)
  11. window.requestAnimationFrame = function(callback, element) {
  12. var currTime = new Date().getTime();
  13. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  14. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  15. timeToCall);
  16. lastTime = currTime + timeToCall;
  17. return id;
  18. };
  19. if (!window.cancelAnimationFrame)
  20. window.cancelAnimationFrame = function(id) {
  21. clearTimeout(id);
  22. };
  23. }());
  24.  
  25. (function() {
  26.     'use strict';
  27.     var currentScore = 0;
  28.  
  29.     // Fix for AudioContext 12.06.17
  30.         var AudioContext = window.AudioContext // Default
  31.             || window.webkitAudioContext // Safari and old versions of Chrome
  32.             || false;
  33.  
  34.     function Runner(outerContainerId, opt_config) {
  35.         if (Runner.instance_) {
  36.             return Runner.instance_
  37.         }
  38.         Runner.instance_ = this;
  39.         this.outerContainerEl = document.querySelector(outerContainerId);
  40.         this.containerEl = null;
  41.         this.snackbarEl = null;
  42.         this.config = opt_config || Runner.config;
  43.         this.dimensions = Runner.defaultDimensions;
  44.         this.canvas = null;
  45.         this.canvasCtx = null;
  46.         this.tRex = null;
  47.         this.distanceMeter = null;
  48.         this.distanceRan = 0;
  49.         this.highestScore = 0;
  50.         this.time = 0;
  51.         this.runningTime = 0;
  52.         this.msPerFrame = 1000 / FPS;
  53.         this.currentSpeed = this.config.SPEED;
  54.         this.obstacles = [];
  55.         this.started = false;
  56.         this.activated = false;
  57.         this.crashed = false;
  58.         this.paused = false;
  59.         this.resizeTimerId_ = null;
  60.         this.playCount = 0;
  61.         this.audioBuffer = null;
  62.         this.soundFx = {};
  63.         this.audioContext = null;
  64.         this.images = {};
  65.         this.imagesLoaded = 0;
  66.         this.loadImages();
  67.         this.gamepadPreviousKeyDown = false
  68.     }
  69.     window['Runner'] = Runner;
  70.     var DEFAULT_WIDTH = 600;
  71.     var FPS = 60;
  72.     var IS_HIDPI = window.devicePixelRatio > 1;
  73.     var IS_IOS = window.navigator.userAgent.indexOf('CriOS') > -1 || window.navigator.userAgent == 'UIWebViewForStaticFileContent';
  74.     var IS_MOBILE = window.navigator.userAgent.indexOf('Mobi') > -1 || IS_IOS;
  75.     var IS_TOUCH_ENABLED = 'ontouchstart' in window;
  76.     Runner.config = {
  77.         ACCELERATION: 0.001,
  78.         BG_CLOUD_SPEED: 0.2,
  79.         BOTTOM_PAD: 10,
  80.         CLEAR_TIME: 3000,
  81.         CLOUD_FREQUENCY: 0.5,
  82.         GAMEOVER_CLEAR_TIME: 750,
  83.         GAP_COEFFICIENT: 0.6,
  84.         GRAVITY: 0.6,
  85.         INITIAL_JUMP_VELOCITY: 12,
  86.         MAX_CLOUDS: 6,
  87.         MAX_OBSTACLE_LENGTH: 3,
  88.         MAX_OBSTACLE_DUPLICATION: 2,
  89.         MAX_SPEED: 13,
  90.         MIN_JUMP_HEIGHT: 35,
  91.         MOBILE_SPEED_COEFFICIENT: 1.2,
  92.         RESOURCE_TEMPLATE_ID: 'audio-resources',
  93.         SPEED: 6,
  94.         SPEED_DROP_COEFFICIENT: 3
  95.     };
  96.     Runner.defaultDimensions = {
  97.         WIDTH: DEFAULT_WIDTH,
  98.         HEIGHT: 150
  99.     };
  100.     Runner.classes = {
  101.         CANVAS: 'runner-canvas',
  102.         CONTAINER: 'runner-container',
  103.         CRASHED: 'crashed',
  104.         ICON: 'icon-offline',
  105.         SNACKBAR: 'snackbar',
  106.         SNACKBAR_SHOW: 'snackbar-show',
  107.         TOUCH_CONTROLLER: 'controller'
  108.     };
  109.     Runner.spriteDefinition = {
  110.         LDPI: {
  111.             CACTUS_LARGE: {
  112.                 x: 332,
  113.                 y: 2
  114.             },
  115.             CACTUS_SMALL: {
  116.                 x: 228,
  117.                 y: 2
  118.             },
  119.             CLOUD: {
  120.                 x: 86,
  121.                 y: 2
  122.             },
  123.             HORIZON: {
  124.                 x: 2,
  125.                 y: 54
  126.             },
  127.             PTERODACTYL: {
  128.                 x: 134,
  129.                 y: 2
  130.             },
  131.             RESTART: {
  132.                 x: 2,
  133.                 y: 2
  134.             },
  135.             TEXT_SPRITE: {
  136.                 x: 484,
  137.                 y: 2
  138.             },
  139.             TREX: {
  140.                 x: 677,
  141.                 y: 2
  142.             }
  143.         },
  144.         HDPI: {
  145.             CACTUS_LARGE: {
  146.                 x: 652,
  147.                 y: 2
  148.             },
  149.             CACTUS_SMALL: {
  150.                 x: 446,
  151.                 y: 2
  152.             },
  153.             CLOUD: {
  154.                 x: 166,
  155.                 y: 2
  156.             },
  157.             HORIZON: {
  158.                 x: 2,
  159.                 y: 104
  160.             },
  161.             PTERODACTYL: {
  162.                 x: 260,
  163.                 y: 2
  164.             },
  165.             RESTART: {
  166.                 x: 2,
  167.                 y: 2
  168.             },
  169.             TEXT_SPRITE: {
  170.                 x: 954,
  171.                 y: 2
  172.             },
  173.             TREX: {
  174.                 x: 1338,
  175.                 y: 2
  176.             }
  177.         }
  178.     };
  179.     Runner.sounds = {
  180.         BUTTON_PRESS: 'offline-sound-press',
  181.         HIT: 'offline-sound-hit',
  182.         SCORE: 'offline-sound-reached'
  183.     };
  184.     Runner.keycodes = {
  185.         JUMP: {
  186.             '38': 1,
  187.             '32': 1
  188.         },
  189.         DUCK: {
  190.             '40': 1
  191.         },
  192.         RESTART: {
  193.             '13': 1
  194.         }
  195.     };
  196.     Runner.events = {
  197.         ANIM_END: 'webkitAnimationEnd',
  198.         CLICK: 'click',
  199.         KEYDOWN: 'keydown',
  200.         KEYUP: 'keyup',
  201.         MOUSEDOWN: 'mousedown',
  202.         MOUSEUP: 'mouseup',
  203.         RESIZE: 'resize',
  204.         TOUCHEND: 'touchend',
  205.         TOUCHSTART: 'touchstart',
  206.         VISIBILITY: 'visibilitychange',
  207.         BLUR: 'blur',
  208.         FOCUS: 'focus',
  209.         LOAD: 'load',
  210.         GAMEPADCONNECTED: 'gamepadconnected'
  211.     };
  212.     Runner.prototype = {
  213.         isDisabled: function() {
  214.             return loadTimeData && loadTimeData.valueExists('disabledEasterEgg')
  215.         },
  216.         setupDisabledRunner: function() {
  217.             this.containerEl = document.createElement('div');
  218.             this.containerEl.className = Runner.classes.SNACKBAR;
  219.             this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg');
  220.             this.outerContainerEl.appendChild(this.containerEl);
  221.             document.addEventListener(Runner.events.KEYDOWN, function(e) {
  222.                 if (Runner.keycodes.JUMP[e.keyCode]) {
  223.                     this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW);
  224.                     document.querySelector('.icon').classList.add('icon-disabled')
  225.                 }
  226.             }.bind(this))
  227.         },
  228.         updateConfigSetting: function(setting, value) {
  229.             if (setting in this.config && value != undefined) {
  230.                 this.config[setting] = value;
  231.                 switch (setting) {
  232.                     case 'GRAVITY':
  233.                     case 'MIN_JUMP_HEIGHT':
  234.                     case 'SPEED_DROP_COEFFICIENT':
  235.                         this.tRex.config[setting] = value;
  236.                         break;
  237.                     case 'INITIAL_JUMP_VELOCITY':
  238.                         this.tRex.setJumpVelocity(value);
  239.                         break;
  240.                     case 'SPEED':
  241.                         this.setSpeed(value);
  242.                         break
  243.                 }
  244.             }
  245.         },
  246.         loadImages: function() {
  247.             if (IS_HIDPI) {
  248.                 Runner.imageSprite = document.getElementById('offline-resources-2x');
  249.                 this.spriteDef = Runner.spriteDefinition.HDPI
  250.             } else {
  251.                 Runner.imageSprite = document.getElementById('offline-resources-1x');
  252.                 this.spriteDef = Runner.spriteDefinition.LDPI
  253.             }
  254.             this.init()
  255.         },
  256.         loadSounds: function() {
  257.             if (!IS_IOS && AudioContext) { // Fix 12.06.17
  258.                 this.audioContext = new AudioContext();
  259.                 var resourceTemplate = document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content;
  260.                 for (var sound in Runner.sounds) {
  261.                     var soundSrc = resourceTemplate.getElementById(Runner.sounds[sound]).src;
  262.                     soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1);
  263.                     var buffer = decodeBase64ToArrayBuffer(soundSrc);
  264.                     this.audioContext.decodeAudioData(buffer, function(index, audioData) {
  265.                         this.soundFx[index] = audioData
  266.                     }.bind(this, sound))
  267.                 }
  268.             }
  269.         },
  270.         setSpeed: function(opt_speed) {
  271.             var speed = opt_speed || this.currentSpeed;
  272.             if (this.dimensions.WIDTH < DEFAULT_WIDTH) {
  273.                 var mobileSpeed = speed * this.dimensions.WIDTH / DEFAULT_WIDTH * this.config.MOBILE_SPEED_COEFFICIENT;
  274.                 this.currentSpeed = mobileSpeed > speed ? speed : mobileSpeed
  275.             } else if (opt_speed) {
  276.                 this.currentSpeed = opt_speed
  277.             }
  278.         },
  279.         init: function() {
  280.             this.adjustDimensions();
  281.             this.setSpeed();
  282.             this.containerEl = document.createElement('div');
  283.             this.containerEl.className = Runner.classes.CONTAINER;
  284.             this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH, this.dimensions.HEIGHT, Runner.classes.PLAYER);
  285.             this.canvasCtx = this.canvas.getContext('2d');
  286.             this.canvasCtx.fillStyle = '#f7f7f7';
  287.             this.canvasCtx.fill();
  288.             Runner.updateCanvasScaling(this.canvas);
  289.             this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions, this.config.GAP_COEFFICIENT);
  290.             this.distanceMeter = new DistanceMeter(this.canvas, this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH);
  291.             this.tRex = new Trex(this.canvas, this.spriteDef.TREX);
  292.             this.outerContainerEl.appendChild(this.containerEl);
  293.             if (IS_MOBILE) {
  294.                 this.createTouchController()
  295.             }
  296.             this.startListening();
  297.             this.update();
  298.             window.addEventListener(Runner.events.RESIZE, this.debounceResize.bind(this))
  299.         },
  300.         createTouchController: function() {
  301.             this.touchController = document.createElement('div');
  302.             this.touchController.className = Runner.classes.TOUCH_CONTROLLER
  303.         },
  304.         debounceResize: function() {
  305.             if (!this.resizeTimerId_) {
  306.                 this.resizeTimerId_ = setInterval(this.adjustDimensions.bind(this), 250)
  307.             }
  308.         },
  309.         adjustDimensions: function() {
  310.             clearInterval(this.resizeTimerId_);
  311.             this.resizeTimerId_ = null;
  312.             var boxStyles = window.getComputedStyle(this.outerContainerEl);
  313.             var padding = Number(boxStyles.paddingLeft.substr(0, boxStyles.paddingLeft.length - 2));
  314.             this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2;
  315.             if (this.canvas) {
  316.                 this.canvas.width = this.dimensions.WIDTH;
  317.                 this.canvas.height = this.dimensions.HEIGHT;
  318.                 Runner.updateCanvasScaling(this.canvas);
  319.                 this.distanceMeter.calcXPos(this.dimensions.WIDTH);
  320.                 this.clearCanvas();
  321.                 this.horizon.update(0, 0, true);
  322.                 this.tRex.update(0);
  323.                 if (this.activated || this.crashed || this.paused) {
  324.                     this.containerEl.style.width = this.dimensions.WIDTH + 'px';
  325.                     this.containerEl.style.height = this.dimensions.HEIGHT + 'px';
  326.                     this.distanceMeter.update(0, Math.ceil(this.distanceRan));
  327.                     this.stop()
  328.                 } else {
  329.                     this.tRex.draw(0, 0)
  330.                 }
  331.                 if (this.crashed && this.gameOverPanel) {
  332.                     this.gameOverPanel.updateDimensions(this.dimensions.WIDTH);
  333.                     this.gameOverPanel.draw()
  334.                 }
  335.             }
  336.         },
  337.         playIntro: function() {
  338.             if (!this.started && !this.crashed) {
  339.                 this.playingIntro = true;
  340.                 this.tRex.playingIntro = true;
  341.                 var keyframes = '@-webkit-keyframes intro { ' + 'from { width:' + Trex.config.WIDTH + 'px }' + 'to { width: ' + this.dimensions.WIDTH + 'px }' + '}';
  342.                 document.styleSheets[0].insertRule(keyframes, 0);
  343.                 this.containerEl.addEventListener(Runner.events.ANIM_END, this.startGame.bind(this));
  344.                 this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both';
  345.                 this.containerEl.style.width = this.dimensions.WIDTH + 'px';
  346.                 if (this.touchController) {
  347.                     this.outerContainerEl.appendChild(this.touchController)
  348.                 }
  349.                 this.activated = true;
  350.                 this.started = true
  351.             } else if (this.crashed) {
  352.                 this.restart()
  353.             }
  354.         },
  355.         startGame: function() {
  356.             this.runningTime = 0;
  357.             this.playingIntro = false;
  358.             this.tRex.playingIntro = false;
  359.             this.containerEl.style.webkitAnimation = '';
  360.             this.playCount++;
  361.             document.addEventListener(Runner.events.VISIBILITY, this.onVisibilityChange.bind(this));
  362.             window.addEventListener(Runner.events.BLUR, this.onVisibilityChange.bind(this));
  363.             window.addEventListener(Runner.events.FOCUS, this.onVisibilityChange.bind(this))
  364.         },
  365.         clearCanvas: function() {
  366.             this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, this.dimensions.HEIGHT)
  367.         },
  368.         update: function() {
  369.             this.drawPending = false;
  370.             var now = getTimeStamp();
  371.             var deltaTime = now - (this.time || now);
  372.             this.time = now;
  373.             if (this.activated) {
  374.                 this.clearCanvas();
  375.                 if (this.tRex.jumping) {
  376.                     this.tRex.updateJump(deltaTime)
  377.                 }
  378.                 this.runningTime += deltaTime;
  379.                 var hasObstacles = this.runningTime > this.config.CLEAR_TIME;
  380.                 if (this.tRex.jumpCount == 1 && !this.playingIntro) {
  381.                     this.playIntro()
  382.                 }
  383.                 if (this.playingIntro) {
  384.                     this.horizon.update(0, this.currentSpeed, hasObstacles)
  385.                 } else {
  386.                     deltaTime = !this.started ? 0 : deltaTime;
  387.                     this.horizon.update(deltaTime, this.currentSpeed, hasObstacles)
  388.                 }
  389.                 var collision = hasObstacles && checkForCollision(this.horizon.obstacles[0], this.tRex);
  390.                 if (!collision) {
  391.                     this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame;
  392.                     if (this.currentSpeed < this.config.MAX_SPEED) {
  393.                         this.currentSpeed += this.config.ACCELERATION
  394.                     }
  395.                 } else {
  396.                     this.gameOver()
  397.                 }
  398.                 var playAcheivementSound = this.distanceMeter.update(deltaTime, Math.ceil(this.distanceRan));
  399.                 if (playAcheivementSound) {
  400.                     this.playSound(this.soundFx.SCORE)
  401.                 }
  402.             }
  403.             if (!this.crashed) {
  404.                 this.tRex.update(deltaTime);
  405.                 this.raq()
  406.             }
  407.         },
  408.         handleEvent: function(e) {
  409.             return (function(evtType, events) {
  410.                 switch (evtType) {
  411.                     case events.KEYDOWN:
  412.                     case events.TOUCHSTART:
  413.                     case events.MOUSEDOWN:
  414.                     case events.GAMEPADCONNECTED:
  415.                         this.onKeyDown(e);
  416.                         break;
  417.                     case events.KEYUP:
  418.                     case events.TOUCHEND:
  419.                     case events.MOUSEUP:
  420.                         this.onKeyUp(e);
  421.                         break
  422.                 }
  423.             }.bind(this))(e.type, Runner.events)
  424.         },
  425.         startListening: function() {
  426.             document.addEventListener(Runner.events.KEYDOWN, this);
  427.             document.addEventListener(Runner.events.KEYUP, this);
  428.             if (IS_MOBILE) {
  429.                 this.touchController.addEventListener(Runner.events.TOUCHSTART, this);
  430.                 this.touchController.addEventListener(Runner.events.TOUCHEND, this);
  431.                 this.containerEl.addEventListener(Runner.events.TOUCHSTART, this)
  432.             } else {
  433.                 document.addEventListener(Runner.events.MOUSEDOWN, this);
  434.                 document.addEventListener(Runner.events.MOUSEUP, this)
  435.             }
  436.             window.addEventListener(Runner.events.GAMEPADCONNECTED, this);
  437.             window.setInterval(this.pollGamepads.bind(this), 10)
  438.         },
  439.         pollGamepads: function() {
  440.             var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []); //  Fix for navigator.getGamepads() 12.06.17
  441.             var keydown = false;
  442.             for (var i = 0; i < gamepads.length; i++) {
  443.                 if (gamepads[i] != undefined) {
  444.                     if (gamepads[i].buttons.filter(function(e) {
  445.                             return e.pressed == true
  446.                         }).length > 0) {
  447.                         keydown = true
  448.                     }
  449.                 }
  450.             }
  451.             if (keydown != this.gamepadPreviousKeyDown) {
  452.                 this.gamepadPreviousKeyDown = keydown;
  453.                 var event = new Event(keydown ? 'keydown' : 'keyup');
  454.                 event.keyCode = 32;
  455.                 event.which = event.keyCode;
  456.                 event.altKey = false;
  457.                 event.ctrlKey = true;
  458.                 event.shiftKey = false;
  459.                 event.metaKey = false;
  460.                 document.dispatchEvent(event)
  461.             }
  462.         },
  463.         stopListening: function() {
  464.             document.removeEventListener(Runner.events.KEYDOWN, this);
  465.             document.removeEventListener(Runner.events.KEYUP, this);
  466.             if (IS_MOBILE) {
  467.                 this.touchController.removeEventListener(Runner.events.TOUCHSTART, this);
  468.                 this.touchController.removeEventListener(Runner.events.TOUCHEND, this);
  469.                 this.containerEl.removeEventListener(Runner.events.TOUCHSTART, this)
  470.             } else {
  471.                 document.removeEventListener(Runner.events.MOUSEDOWN, this);
  472.                 document.removeEventListener(Runner.events.MOUSEUP, this)
  473.             }
  474.         },
  475.         onKeyDown: function(e) {
  476.             if (IS_MOBILE) {
  477.                 e.preventDefault()
  478.             }
  479.             if (!this.crashed && (Runner.keycodes.JUMP[e.keyCode] || e.type == Runner.events.TOUCHSTART || e.type == Runner.events.GAMEPADCONNECTED)) {
  480.                 if (!this.activated) {
  481.                     this.loadSounds();
  482.                     this.activated = true
  483.                 }
  484.                 if (!this.tRex.jumping && !this.tRex.ducking) {
  485.                     this.playSound(this.soundFx.BUTTON_PRESS);
  486.                     this.tRex.startJump(this.currentSpeed)
  487.                 }
  488.             }
  489.             if (this.crashed && e.type == Runner.events.TOUCHSTART && e.currentTarget == this.containerEl) {
  490.                 this.restart()
  491.             }
  492.             if (this.activated && !this.crashed && Runner.keycodes.DUCK[e.keyCode]) {
  493.                 e.preventDefault();
  494.                 if (this.tRex.jumping) {
  495.                     this.tRex.setSpeedDrop()
  496.                 } else if (!this.tRex.jumping && !this.tRex.ducking) {
  497.                     this.tRex.setDuck(true)
  498.                 }
  499.             }
  500.         },
  501.         onKeyUp: function(e) {
  502.             var keyCode = String(e.keyCode);
  503.             var isjumpKey = Runner.keycodes.JUMP[keyCode] || e.type == Runner.events.TOUCHEND || e.type == Runner.events.MOUSEDOWN;
  504.             if (this.isRunning() && isjumpKey) {
  505.                 this.tRex.endJump()
  506.             } else if (Runner.keycodes.DUCK[keyCode]) {
  507.                 this.tRex.speedDrop = false;
  508.                 this.tRex.setDuck(false)
  509.             } else if (this.crashed) {
  510.                 var deltaTime = getTimeStamp() - this.time;
  511.                 if (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) || (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && Runner.keycodes.JUMP[keyCode])) {
  512.                     this.restart()
  513.                 }
  514.             } else if (this.paused && isjumpKey) {
  515.                 this.tRex.reset();
  516.                 this.play()
  517.             }
  518.         },
  519.         isLeftClickOnCanvas: function(e) {
  520.             return e.button != null && e.button < 2 && e.type == Runner.events.MOUSEUP && e.target == this.canvas
  521.         },
  522.         raq: function() {
  523.             if (!this.drawPending) {
  524.                 this.drawPending = true;
  525.                 this.raqId = requestAnimationFrame(this.update.bind(this))
  526.             }
  527.         },
  528.         isRunning: function() {
  529.             return !!this.raqId
  530.         },
  531.         gameOver: function() {
  532.             this.playSound(this.soundFx.HIT);
  533.             vibrate(200);
  534.             this.stop();
  535.             this.crashed = true;
  536.             this.distanceMeter.acheivement = false;
  537.             this.tRex.update(100, Trex.status.CRASHED);
  538.             if (!this.gameOverPanel) {
  539.                 this.gameOverPanel = new GameOverPanel(this.canvas, this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART, this.dimensions)
  540.             } else {
  541.                 this.gameOverPanel.draw()
  542.             }
  543.             if (this.distanceRan > this.highestScore) {
  544.                 this.highestScore = Math.ceil(this.distanceRan);
  545.                 this.distanceMeter.setHighScore(this.highestScore);
  546.                 currentScore = Math.round(this.highestScore * 0.025);
  547.                 var score_d = 0;
  548.                 if (document.getElementById("score-5") !== null) {
  549.                     score_d = document.getElementById("score-5").innerHTML
  550.                 }
  551.                 if (currentScore > score_d) {
  552.                     var xhr = new XMLHttpRequest();
  553.                     xhr.open('GET', '/inc/check.php?score=' + currentScore, false);
  554.                     xhr.send();
  555.                     if (xhr.status != 200) {} else {
  556.                         if (xhr.responseText != '') {
  557.                             if (user_name == '') {
  558.                                 user_name = prompt(xhr.responseText, 'Anonym');
  559.                                 if (user_name == 'null' || user_name == '') {
  560.                                     user_name = 'Anonym'
  561.                                 }
  562.                             } else {
  563.                                 alert(xhr.responseText)
  564.                             }
  565.                             xhr.open('GET', '/inc/set.php?name=' + user_name + '&score=' + currentScore, false);
  566.                             xhr.send();
  567.                             location.reload()
  568.                         }
  569.                     }
  570.                 }
  571.             }
  572.             this.time = getTimeStamp()
  573.         },
  574.         stop: function() {
  575.             this.activated = false;
  576.             this.paused = true;
  577.             cancelAnimationFrame(this.raqId);
  578.             this.raqId = 0
  579.         },
  580.         play: function() {
  581.             if (!this.crashed) {
  582.                 this.activated = true;
  583.                 this.paused = false;
  584.                 this.tRex.update(0, Trex.status.RUNNING);
  585.                 this.time = getTimeStamp();
  586.                 this.update()
  587.             }
  588.         },
  589.         restart: function() {
  590.             if (!this.raqId) {
  591.                 this.playCount++;
  592.                 this.runningTime = 0;
  593.                 this.activated = true;
  594.                 this.crashed = false;
  595.                 this.distanceRan = 0;
  596.                 this.setSpeed(this.config.SPEED);
  597.                 this.time = getTimeStamp();
  598.                 this.containerEl.classList.remove(Runner.classes.CRASHED);
  599.                 this.clearCanvas();
  600.                 this.distanceMeter.reset(this.highestScore);
  601.                 this.horizon.reset();
  602.                 this.tRex.reset();
  603.                 this.playSound(this.soundFx.BUTTON_PRESS);
  604.                 this.update()
  605.             }
  606.         },
  607.         onVisibilityChange: function(e) {
  608.             if (document.hidden || document.webkitHidden || e.type == 'blur') {
  609.                 this.stop()
  610.             } else if (!this.crashed) {
  611.                 this.tRex.reset();
  612.                 this.play()
  613.             }
  614.         },
  615.         playSound: function(soundBuffer) {
  616.             if (soundBuffer) {
  617.                 var sourceNode = this.audioContext.createBufferSource();
  618.                 sourceNode.buffer = soundBuffer;
  619.                 sourceNode.connect(this.audioContext.destination);
  620.                 sourceNode.start(0)
  621.             }
  622.         }
  623.     };
  624.     Runner.updateCanvasScaling = function(canvas, opt_width, opt_height) {
  625.         var context = canvas.getContext('2d');
  626.         var devicePixelRatio = Math.floor(window.devicePixelRatio) || 1;
  627.         var backingStoreRatio = Math.floor(context.webkitBackingStorePixelRatio) || 1;
  628.         var ratio = devicePixelRatio / backingStoreRatio;
  629.         if (devicePixelRatio !== backingStoreRatio) {
  630.             var oldWidth = opt_width || canvas.width;
  631.             var oldHeight = opt_height || canvas.height;
  632.             canvas.width = oldWidth * ratio;
  633.             canvas.height = oldHeight * ratio;
  634.             canvas.style.width = oldWidth + 'px';
  635.             canvas.style.height = oldHeight + 'px';
  636.             context.scale(ratio, ratio);
  637.             return true
  638.         } else if (devicePixelRatio == 1) {
  639.             canvas.style.width = canvas.width + 'px';
  640.             canvas.style.height = canvas.height + 'px'
  641.         }
  642.         return false
  643.     };
  644.  
  645.     function getRandomNum(min, max) {
  646.         return Math.floor(Math.random() * (max - min + 1)) + min
  647.     }
  648.  
  649.     function vibrate(duration) {
  650.         if (IS_MOBILE && window.navigator.vibrate) {
  651.             window.navigator.vibrate(duration)
  652.         }
  653.     }
  654.  
  655.     function createCanvas(container, width, height, opt_classname) {
  656.         var canvas = document.createElement('canvas');
  657.         canvas.className = opt_classname ? Runner.classes.CANVAS + ' ' + opt_classname : Runner.classes.CANVAS;
  658.         canvas.width = width;
  659.         canvas.height = height;
  660.         container.appendChild(canvas);
  661.         return canvas
  662.     }
  663.  
  664.     function decodeBase64ToArrayBuffer(base64String) {
  665.         var len = (base64String.length / 4) * 3;
  666.         var str = atob(base64String);
  667.         var arrayBuffer = new ArrayBuffer(len);
  668.         var bytes = new Uint8Array(arrayBuffer);
  669.         for (var i = 0; i < len; i++) {
  670.             bytes[i] = str.charCodeAt(i)
  671.         }
  672.         return bytes.buffer
  673.     }
  674.  
  675.     function getTimeStamp() {
  676.         //return IS_IOS ? new Date().getTime() : performance.now()
  677.         return new Date().getTime(); // Safari 5.17 Fix 12.06.17
  678.     }
  679.  
  680.     function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) {
  681.         this.canvas = canvas;
  682.         this.canvasCtx = canvas.getContext('2d');
  683.         this.canvasDimensions = dimensions;
  684.         this.textImgPos = textImgPos;
  685.         this.restartImgPos = restartImgPos;
  686.         this.draw()
  687.     };
  688.     GameOverPanel.dimensions = {
  689.         TEXT_X: 0,
  690.         TEXT_Y: 13,
  691.         TEXT_WIDTH: 191,
  692.         TEXT_HEIGHT: 11,
  693.         RESTART_WIDTH: 36,
  694.         RESTART_HEIGHT: 32
  695.     };
  696.     GameOverPanel.prototype = {
  697.         updateDimensions: function(width, opt_height) {
  698.             this.canvasDimensions.WIDTH = width;
  699.             if (opt_height) {
  700.                 this.canvasDimensions.HEIGHT = opt_height
  701.             }
  702.         },
  703.         draw: function() {
  704.             var dimensions = GameOverPanel.dimensions;
  705.             var centerX = this.canvasDimensions.WIDTH / 2;
  706.             var textSourceX = dimensions.TEXT_X;
  707.             var textSourceY = dimensions.TEXT_Y;
  708.             var textSourceWidth = dimensions.TEXT_WIDTH;
  709.             var textSourceHeight = dimensions.TEXT_HEIGHT;
  710.             var textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2));
  711.             var textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3);
  712.             var textTargetWidth = dimensions.TEXT_WIDTH;
  713.             var textTargetHeight = dimensions.TEXT_HEIGHT;
  714.             var restartSourceWidth = dimensions.RESTART_WIDTH;
  715.             var restartSourceHeight = dimensions.RESTART_HEIGHT;
  716.             var restartTargetX = centerX - (dimensions.RESTART_WIDTH / 2);
  717.             var restartTargetY = this.canvasDimensions.HEIGHT / 2;
  718.             if (IS_HIDPI) {
  719.                 textSourceY *= 2;
  720.                 textSourceX *= 2;
  721.                 textSourceWidth *= 2;
  722.                 textSourceHeight *= 2;
  723.                 restartSourceWidth *= 2;
  724.                 restartSourceHeight *= 2
  725.             }
  726.             textSourceX += this.textImgPos.x;
  727.             textSourceY += this.textImgPos.y;
  728.             this.canvasCtx.drawImage(Runner.imageSprite, textSourceX, textSourceY, textSourceWidth, textSourceHeight, textTargetX, textTargetY, textTargetWidth, textTargetHeight);
  729.             this.canvasCtx.drawImage(Runner.imageSprite, this.restartImgPos.x, this.restartImgPos.y, restartSourceWidth, restartSourceHeight, restartTargetX, restartTargetY, dimensions.RESTART_WIDTH, dimensions.RESTART_HEIGHT)
  730.         }
  731.     };
  732.  
  733.     function checkForCollision(obstacle, tRex, opt_canvasCtx) {
  734.         var obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos;
  735.         var tRexBox = new CollisionBox(tRex.xPos + 1, tRex.yPos + 1, tRex.config.WIDTH - 2, tRex.config.HEIGHT - 2);
  736.         var obstacleBox = new CollisionBox(obstacle.xPos + 1, obstacle.yPos + 1, obstacle.typeConfig.width * obstacle.size - 2, obstacle.typeConfig.height - 2);
  737.         if (opt_canvasCtx) {
  738.             drawCollisionBoxes(opt_canvasCtx, tRexBox, obstacleBox)
  739.         }
  740.         if (boxCompare(tRexBox, obstacleBox)) {
  741.             var collisionBoxes = obstacle.collisionBoxes;
  742.             var tRexCollisionBoxes = tRex.ducking ? Trex.collisionBoxes.DUCKING : Trex.collisionBoxes.RUNNING;
  743.             for (var t = 0; t < tRexCollisionBoxes.length; t++) {
  744.                 for (var i = 0; i < collisionBoxes.length; i++) {
  745.                     var adjTrexBox = createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox);
  746.                     var adjObstacleBox = createAdjustedCollisionBox(collisionBoxes[i], obstacleBox);
  747.                     var crashed = boxCompare(adjTrexBox, adjObstacleBox);
  748.                     if (opt_canvasCtx) {
  749.                         drawCollisionBoxes(opt_canvasCtx, adjTrexBox, adjObstacleBox)
  750.                     }
  751.                     if (crashed) {
  752.                         return [adjTrexBox, adjObstacleBox]
  753.                     }
  754.                 }
  755.             }
  756.         }
  757.         return false
  758.     };
  759.  
  760.     function createAdjustedCollisionBox(box, adjustment) {
  761.         return new CollisionBox(box.x + adjustment.x, box.y + adjustment.y, box.width, box.height)
  762.     };
  763.  
  764.     function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) {
  765.         canvasCtx.save();
  766.         canvasCtx.strokeStyle = '#f00';
  767.         canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height);
  768.         canvasCtx.strokeStyle = '#0f0';
  769.         canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y, obstacleBox.width, obstacleBox.height);
  770.         canvasCtx.restore()
  771.     };
  772.  
  773.     function boxCompare(tRexBox, obstacleBox) {
  774.         var crashed = false;
  775.         var tRexBoxX = tRexBox.x;
  776.         var tRexBoxY = tRexBox.y;
  777.         var obstacleBoxX = obstacleBox.x;
  778.         var obstacleBoxY = obstacleBox.y;
  779.         if (tRexBox.x < obstacleBoxX + obstacleBox.width && tRexBox.x + tRexBox.width > obstacleBoxX && tRexBox.y < obstacleBox.y + obstacleBox.height && tRexBox.height + tRexBox.y > obstacleBox.y) {
  780.             crashed = true
  781.         }
  782.         return crashed
  783.     };
  784.  
  785.     function CollisionBox(x, y, w, h) {
  786.         this.x = x;
  787.         this.y = y;
  788.         this.width = w;
  789.         this.height = h
  790.     };
  791.  
  792.     function Obstacle(canvasCtx, type, spriteImgPos, dimensions, gapCoefficient, speed) {
  793.         this.canvasCtx = canvasCtx;
  794.         this.spritePos = spriteImgPos;
  795.         this.typeConfig = type;
  796.         this.gapCoefficient = gapCoefficient;
  797.         this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH);
  798.         this.dimensions = dimensions;
  799.         this.remove = false;
  800.         this.xPos = 0;
  801.         this.yPos = 0;
  802.         this.width = 0;
  803.         this.collisionBoxes = [];
  804.         this.gap = 0;
  805.         this.speedOffset = 0;
  806.         this.currentFrame = 0;
  807.         this.timer = 0;
  808.         this.init(speed)
  809.     };
  810.     Obstacle.MAX_GAP_COEFFICIENT = 1.5;
  811.     Obstacle.MAX_OBSTACLE_LENGTH = 3, Obstacle.prototype = {
  812.         init: function(speed) {
  813.             this.cloneCollisionBoxes();
  814.             if (this.size > 1 && this.typeConfig.multipleSpeed > speed) {
  815.                 this.size = 1
  816.             }
  817.             this.width = this.typeConfig.width * this.size;
  818.             this.xPos = this.dimensions.WIDTH - this.width;
  819.             if (Array.isArray(this.typeConfig.yPos)) {
  820.                 var yPosConfig = IS_MOBILE ? this.typeConfig.yPosMobile : this.typeConfig.yPos;
  821.                 this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)]
  822.             } else {
  823.                 this.yPos = this.typeConfig.yPos
  824.             }
  825.             this.draw();
  826.             if (this.size > 1) {
  827.                 this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - this.collisionBoxes[2].width;
  828.                 this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width
  829.             }
  830.             if (this.typeConfig.speedOffset) {
  831.                 this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset : -this.typeConfig.speedOffset
  832.             }
  833.             this.gap = this.getGap(this.gapCoefficient, speed)
  834.         },
  835.         draw: function() {
  836.             var sourceWidth = this.typeConfig.width;
  837.             var sourceHeight = this.typeConfig.height;
  838.             if (IS_HIDPI) {
  839.                 sourceWidth = sourceWidth * 2;
  840.                 sourceHeight = sourceHeight * 2
  841.             }
  842.             var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) + this.spritePos.x;
  843.             if (this.currentFrame > 0) {
  844.                 sourceX += sourceWidth * this.currentFrame
  845.             }
  846.             this.canvasCtx.drawImage(Runner.imageSprite, sourceX, this.spritePos.y, sourceWidth * this.size, sourceHeight, this.xPos, this.yPos, this.typeConfig.width * this.size, this.typeConfig.height)
  847.         },
  848.         update: function(deltaTime, speed) {
  849.             if (!this.remove) {
  850.                 if (this.typeConfig.speedOffset) {
  851.                     speed += this.speedOffset
  852.                 }
  853.                 this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime);
  854.                 if (this.typeConfig.numFrames) {
  855.                     this.timer += deltaTime;
  856.                     if (this.timer >= this.typeConfig.frameRate) {
  857.                         this.currentFrame = this.currentFrame == this.typeConfig.numFrames - 1 ? 0 : this.currentFrame + 1;
  858.                         this.timer = 0
  859.                     }
  860.                 }
  861.                 this.draw();
  862.                 if (!this.isVisible()) {
  863.                     this.remove = true
  864.                 }
  865.             }
  866.         },
  867.         getGap: function(gapCoefficient, speed) {
  868.             var minGap = Math.round(this.width * speed + this.typeConfig.minGap * gapCoefficient);
  869.             var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT);
  870.             return getRandomNum(minGap, maxGap)
  871.         },
  872.         isVisible: function() {
  873.             return this.xPos + this.width > 0
  874.         },
  875.         cloneCollisionBoxes: function() {
  876.             var collisionBoxes = this.typeConfig.collisionBoxes;
  877.             for (var i = collisionBoxes.length - 1; i >= 0; i--) {
  878.                 this.collisionBoxes[i] = new CollisionBox(collisionBoxes[i].x, collisionBoxes[i].y, collisionBoxes[i].width, collisionBoxes[i].height)
  879.             }
  880.         }
  881.     };
  882.     Obstacle.types = [{
  883.         type: 'CACTUS_SMALL',
  884.         width: 17,
  885.         height: 35,
  886.         yPos: 105,
  887.         multipleSpeed: 4,
  888.         minGap: 120,
  889.         minSpeed: 0,
  890.         collisionBoxes: [new CollisionBox(0, 7, 5, 27), new CollisionBox(4, 0, 6, 34), new CollisionBox(10, 4, 7, 14)]
  891.     }, {
  892.         type: 'CACTUS_LARGE',
  893.         width: 25,
  894.         height: 50,
  895.         yPos: 90,
  896.         multipleSpeed: 7,
  897.         minGap: 120,
  898.         minSpeed: 0,
  899.         collisionBoxes: [new CollisionBox(0, 12, 7, 38), new CollisionBox(8, 0, 7, 49), new CollisionBox(13, 10, 10, 38)]
  900.     }, {
  901.         type: 'PTERODACTYL',
  902.         width: 46,
  903.         height: 40,
  904.         yPos: [100, 75, 50],
  905.         yPosMobile: [100, 50],
  906.         multipleSpeed: 999,
  907.         minSpeed: 8.5,
  908.         minGap: 150,
  909.         collisionBoxes: [new CollisionBox(15, 15, 16, 5), new CollisionBox(18, 21, 24, 6), new CollisionBox(2, 14, 4, 3), new CollisionBox(6, 10, 4, 7), new CollisionBox(10, 8, 6, 9)],
  910.         numFrames: 2,
  911.         frameRate: 1000 / 6,
  912.         speedOffset: .8
  913.     }];
  914.  
  915.     function Trex(canvas, spritePos) {
  916.         this.canvas = canvas;
  917.         this.canvasCtx = canvas.getContext('2d');
  918.         this.spritePos = spritePos;
  919.         this.xPos = 0;
  920.         this.yPos = 0;
  921.         this.groundYPos = 0;
  922.         this.currentFrame = 0;
  923.         this.currentAnimFrames = [];
  924.         this.blinkDelay = 0;
  925.         this.animStartTime = 0;
  926.         this.timer = 0;
  927.         this.msPerFrame = 1000 / FPS;
  928.         this.config = Trex.config;
  929.         this.status = Trex.status.WAITING;
  930.         this.jumping = false;
  931.         this.ducking = false;
  932.         this.jumpVelocity = 0;
  933.         this.reachedMinHeight = false;
  934.         this.speedDrop = false;
  935.         this.jumpCount = 0;
  936.         this.jumpspotX = 0;
  937.         this.init()
  938.     };
  939.     Trex.config = {
  940.         DROP_VELOCITY: -5,
  941.         GRAVITY: 0.6,
  942.         HEIGHT: 47,
  943.         HEIGHT_DUCK: 25,
  944.         INIITAL_JUMP_VELOCITY: -10,
  945.         INTRO_DURATION: 1500,
  946.         MAX_JUMP_HEIGHT: 30,
  947.         MIN_JUMP_HEIGHT: 30,
  948.         SPEED_DROP_COEFFICIENT: 3,
  949.         SPRITE_WIDTH: 262,
  950.         START_X_POS: 50,
  951.         WIDTH: 44,
  952.         WIDTH_DUCK: 59
  953.     };
  954.     Trex.collisionBoxes = {
  955.         DUCKING: [new CollisionBox(1, 18, 55, 25)],
  956.         RUNNING: [new CollisionBox(22, 0, 17, 16), new CollisionBox(1, 18, 30, 9), new CollisionBox(10, 35, 14, 8), new CollisionBox(1, 24, 29, 5), new CollisionBox(5, 30, 21, 4), new CollisionBox(9, 34, 15, 4)]
  957.     };
  958.     Trex.status = {
  959.         CRASHED: 'CRASHED',
  960.         DUCKING: 'DUCKING',
  961.         JUMPING: 'JUMPING',
  962.         RUNNING: 'RUNNING',
  963.         WAITING: 'WAITING'
  964.     };
  965.     Trex.BLINK_TIMING = 7000;
  966.     Trex.animFrames = {
  967.         WAITING: {
  968.             frames: [44, 0],
  969.             msPerFrame: 1000 / 3
  970.         },
  971.         RUNNING: {
  972.             frames: [88, 132],
  973.             msPerFrame: 1000 / 12
  974.         },
  975.         CRASHED: {
  976.             frames: [220],
  977.             msPerFrame: 1000 / 60
  978.         },
  979.         JUMPING: {
  980.             frames: [0],
  981.             msPerFrame: 1000 / 60
  982.         },
  983.         DUCKING: {
  984.             frames: [262, 321],
  985.             msPerFrame: 1000 / 8
  986.         }
  987.     };
  988.     Trex.prototype = {
  989.         init: function() {
  990.             this.blinkDelay = this.setBlinkDelay();
  991.             this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT - Runner.config.BOTTOM_PAD;
  992.             this.yPos = this.groundYPos;
  993.             this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT;
  994.             this.draw(0, 0);
  995.             this.update(0, Trex.status.WAITING)
  996.         },
  997.         setJumpVelocity: function(setting) {
  998.             this.config.INIITAL_JUMP_VELOCITY = -setting;
  999.             this.config.DROP_VELOCITY = -setting / 2
  1000.         },
  1001.         update: function(deltaTime, opt_status) {
  1002.             this.timer += deltaTime;
  1003.             if (opt_status) {
  1004.                 this.status = opt_status;
  1005.                 this.currentFrame = 0;
  1006.                 this.msPerFrame = Trex.animFrames[opt_status].msPerFrame;
  1007.                 this.currentAnimFrames = Trex.animFrames[opt_status].frames;
  1008.                 if (opt_status == Trex.status.WAITING) {
  1009.                     this.animStartTime = getTimeStamp();
  1010.                     this.setBlinkDelay()
  1011.                 }
  1012.             }
  1013.             if (this.playingIntro && this.xPos < this.config.START_X_POS) {
  1014.                 this.xPos += Math.round((this.config.START_X_POS / this.config.INTRO_DURATION) * deltaTime)
  1015.             }
  1016.             if (this.status == Trex.status.WAITING) {
  1017.                 this.blink(getTimeStamp())
  1018.             } else {
  1019.                 this.draw(this.currentAnimFrames[this.currentFrame], 0)
  1020.             }
  1021.             if (this.timer >= this.msPerFrame) {
  1022.                 this.currentFrame = this.currentFrame == this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1;
  1023.                 this.timer = 0
  1024.             }
  1025.             if (this.speedDrop && this.yPos == this.groundYPos) {
  1026.                 this.speedDrop = false;
  1027.                 this.setDuck(true)
  1028.             }
  1029.         },
  1030.         draw: function(x, y) {
  1031.             var sourceX = x;
  1032.             var sourceY = y;
  1033.             var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ? this.config.WIDTH_DUCK : this.config.WIDTH;
  1034.             var sourceHeight = this.config.HEIGHT;
  1035.             if (IS_HIDPI) {
  1036.                 sourceX *= 2;
  1037.                 sourceY *= 2;
  1038.                 sourceWidth *= 2;
  1039.                 sourceHeight *= 2
  1040.             }
  1041.             sourceX += this.spritePos.x;
  1042.             sourceY += this.spritePos.y;
  1043.             if (this.ducking && this.status != Trex.status.CRASHED) {
  1044.                 this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, sourceWidth, sourceHeight, this.xPos, this.yPos, this.config.WIDTH_DUCK, this.config.HEIGHT)
  1045.             } else {
  1046.                 if (this.ducking && this.status == Trex.status.CRASHED) {
  1047.                     this.xPos++
  1048.                 }
  1049.                 this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, sourceWidth, sourceHeight, this.xPos, this.yPos, this.config.WIDTH, this.config.HEIGHT)
  1050.             }
  1051.         },
  1052.         setBlinkDelay: function() {
  1053.             this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING)
  1054.         },
  1055.         blink: function(time) {
  1056.             var deltaTime = time - this.animStartTime;
  1057.             if (deltaTime >= this.blinkDelay) {
  1058.                 this.draw(this.currentAnimFrames[this.currentFrame], 0);
  1059.                 if (this.currentFrame == 1) {
  1060.                     this.setBlinkDelay();
  1061.                     this.animStartTime = time
  1062.                 }
  1063.             }
  1064.         },
  1065.         startJump: function(speed) {
  1066.             if (!this.jumping) {
  1067.                 this.update(0, Trex.status.JUMPING);
  1068.                 this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10);
  1069.                 this.jumping = true;
  1070.                 this.reachedMinHeight = false;
  1071.                 this.speedDrop = false
  1072.             }
  1073.         },
  1074.         endJump: function() {
  1075.             if (this.reachedMinHeight && this.jumpVelocity < this.config.DROP_VELOCITY) {
  1076.                 this.jumpVelocity = this.config.DROP_VELOCITY
  1077.             }
  1078.         },
  1079.         updateJump: function(deltaTime, speed) {
  1080.             var msPerFrame = Trex.animFrames[this.status].msPerFrame;
  1081.             var framesElapsed = deltaTime / msPerFrame;
  1082.             if (this.speedDrop) {
  1083.                 this.yPos += Math.round(this.jumpVelocity * this.config.SPEED_DROP_COEFFICIENT * framesElapsed)
  1084.             } else {
  1085.                 this.yPos += Math.round(this.jumpVelocity * framesElapsed)
  1086.             }
  1087.             this.jumpVelocity += this.config.GRAVITY * framesElapsed;
  1088.             if (this.yPos < this.minJumpHeight || this.speedDrop) {
  1089.                 this.reachedMinHeight = true
  1090.             }
  1091.             if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) {
  1092.                 this.endJump()
  1093.             }
  1094.             if (this.yPos > this.groundYPos) {
  1095.                 this.reset();
  1096.                 this.jumpCount++
  1097.             }
  1098.             this.update(deltaTime)
  1099.         },
  1100.         setSpeedDrop: function() {
  1101.             this.speedDrop = true;
  1102.             this.jumpVelocity = 1
  1103.         },
  1104.         setDuck: function(isDucking) {
  1105.             if (isDucking && this.status != Trex.status.DUCKING) {
  1106.                 this.update(0, Trex.status.DUCKING);
  1107.                 this.ducking = true
  1108.             } else if (this.status == Trex.status.DUCKING) {
  1109.                 this.update(0, Trex.status.RUNNING);
  1110.                 this.ducking = false
  1111.             }
  1112.         },
  1113.         reset: function() {
  1114.             this.yPos = this.groundYPos;
  1115.             this.jumpVelocity = 0;
  1116.             this.jumping = false;
  1117.             this.ducking = false;
  1118.             this.update(0, Trex.status.RUNNING);
  1119.             this.midair = false;
  1120.             this.speedDrop = false;
  1121.             this.jumpCount = 0
  1122.         }
  1123.     };
  1124.  
  1125.     function DistanceMeter(canvas, spritePos, canvasWidth) {
  1126.         this.canvas = canvas;
  1127.         this.canvasCtx = canvas.getContext('2d');
  1128.         this.image = Runner.imageSprite;
  1129.         this.spritePos = spritePos;
  1130.         this.x = 0;
  1131.         this.y = 5;
  1132.         this.currentDistance = 0;
  1133.         this.maxScore = 0;
  1134.         this.highScore = 0;
  1135.         this.container = null;
  1136.         this.digits = [];
  1137.         this.acheivement = false;
  1138.         this.defaultString = '';
  1139.         this.flashTimer = 0;
  1140.         this.flashIterations = 0;
  1141.         this.config = DistanceMeter.config;
  1142.         this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS;
  1143.         this.init(canvasWidth)
  1144.     };
  1145.     DistanceMeter.dimensions = {
  1146.         WIDTH: 10,
  1147.         HEIGHT: 13,
  1148.         DEST_WIDTH: 11
  1149.     };
  1150.     DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120];
  1151.     DistanceMeter.config = {
  1152.         MAX_DISTANCE_UNITS: 5,
  1153.         ACHIEVEMENT_DISTANCE: 100,
  1154.         COEFFICIENT: 0.025,
  1155.         FLASH_DURATION: 1000 / 4,
  1156.         FLASH_ITERATIONS: 3
  1157.     };
  1158.     DistanceMeter.prototype = {
  1159.         init: function(width) {
  1160.             var maxDistanceStr = '';
  1161.             this.calcXPos(width);
  1162.             this.maxScore = this.maxScoreUnits;
  1163.             for (var i = 0; i < this.maxScoreUnits; i++) {
  1164.                 this.draw(i, 0);
  1165.                 this.defaultString += '0';
  1166.                 maxDistanceStr += '9'
  1167.             }
  1168.             this.maxScore = parseInt(maxDistanceStr)
  1169.         },
  1170.         calcXPos: function(canvasWidth) {
  1171.             this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH * (this.maxScoreUnits + 1))
  1172.         },
  1173.         draw: function(digitPos, value, opt_highScore) {
  1174.             var sourceWidth = DistanceMeter.dimensions.WIDTH;
  1175.             var sourceHeight = DistanceMeter.dimensions.HEIGHT;
  1176.             var sourceX = DistanceMeter.dimensions.WIDTH * value;
  1177.             var sourceY = 0;
  1178.             var targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH;
  1179.             var targetY = this.y;
  1180.             var targetWidth = DistanceMeter.dimensions.WIDTH;
  1181.             var targetHeight = DistanceMeter.dimensions.HEIGHT;
  1182.             if (IS_HIDPI) {
  1183.                 sourceWidth *= 2;
  1184.                 sourceHeight *= 2;
  1185.                 sourceX *= 2
  1186.             }
  1187.             sourceX += this.spritePos.x;
  1188.             sourceY += this.spritePos.y;
  1189.             this.canvasCtx.save();
  1190.             if (opt_highScore) {
  1191.                 var highScoreX = this.x - (this.maxScoreUnits * 2) * DistanceMeter.dimensions.WIDTH;
  1192.                 this.canvasCtx.translate(highScoreX, this.y)
  1193.             } else {
  1194.                 this.canvasCtx.translate(this.x, this.y)
  1195.             }
  1196.             this.canvasCtx.drawImage(this.image, sourceX, sourceY, sourceWidth, sourceHeight, targetX, targetY, targetWidth, targetHeight);
  1197.             this.canvasCtx.restore()
  1198.         },
  1199.         getActualDistance: function(distance) {
  1200.             return distance ? Math.round(distance * this.config.COEFFICIENT) : 0
  1201.         },
  1202.         update: function(deltaTime, distance) {
  1203.             var paint = true;
  1204.             var playSound = false;
  1205.             if (!this.acheivement) {
  1206.                 distance = this.getActualDistance(distance);
  1207.                 if (distance > this.maxScore && this.maxScoreUnits == this.config.MAX_DISTANCE_UNITS) {
  1208.                     this.maxScoreUnits++;
  1209.                     this.maxScore = parseInt(this.maxScore + '9')
  1210.                 } else {
  1211.                     this.distance = 0
  1212.                 }
  1213.                 if (distance > 0) {
  1214.                     if (distance % this.config.ACHIEVEMENT_DISTANCE == 0) {
  1215.                         this.acheivement = true;
  1216.                         this.flashTimer = 0;
  1217.                         playSound = true
  1218.                     }
  1219.                     var distanceStr = (this.defaultString + distance).substr(-this.maxScoreUnits);
  1220.                     this.digits = distanceStr.split('')
  1221.                 } else {
  1222.                     this.digits = this.defaultString.split('')
  1223.                 }
  1224.             } else {
  1225.                 if (this.flashIterations <= this.config.FLASH_ITERATIONS) {
  1226.                     this.flashTimer += deltaTime;
  1227.                     if (this.flashTimer < this.config.FLASH_DURATION) {
  1228.                         paint = false
  1229.                     } else if (this.flashTimer > this.config.FLASH_DURATION * 2) {
  1230.                         this.flashTimer = 0;
  1231.                         this.flashIterations++
  1232.                     }
  1233.                 } else {
  1234.                     this.acheivement = false;
  1235.                     this.flashIterations = 0;
  1236.                     this.flashTimer = 0
  1237.                 }
  1238.             }
  1239.             if (paint) {
  1240.                 for (var i = this.digits.length - 1; i >= 0; i--) {
  1241.                     this.draw(i, parseInt(this.digits[i]))
  1242.                 }
  1243.             }
  1244.             this.drawHighScore();
  1245.             return playSound
  1246.         },
  1247.         drawHighScore: function() {
  1248.             this.canvasCtx.save();
  1249.             this.canvasCtx.globalAlpha = .8;
  1250.             for (var i = this.highScore.length - 1; i >= 0; i--) {
  1251.                 this.draw(i, parseInt(this.highScore[i], 10), true)
  1252.             }
  1253.             this.canvasCtx.restore()
  1254.         },
  1255.         setHighScore: function(distance) {
  1256.             distance = this.getActualDistance(distance);
  1257.             var highScoreStr = (this.defaultString + distance).substr(-this.maxScoreUnits);
  1258.             this.highScore = ['10', '11', ''].concat(highScoreStr.split(''))
  1259.         },
  1260.         reset: function() {
  1261.             this.update(0);
  1262.             this.acheivement = false
  1263.         }
  1264.     };
  1265.  
  1266.     function Cloud(canvas, spritePos, containerWidth) {
  1267.         this.canvas = canvas;
  1268.         this.canvasCtx = this.canvas.getContext('2d');
  1269.         this.spritePos = spritePos;
  1270.         this.containerWidth = containerWidth;
  1271.         this.xPos = containerWidth;
  1272.         this.yPos = 0;
  1273.         this.remove = false;
  1274.         this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP, Cloud.config.MAX_CLOUD_GAP);
  1275.         this.init()
  1276.     };
  1277.     Cloud.config = {
  1278.         HEIGHT: 14,
  1279.         MAX_CLOUD_GAP: 400,
  1280.         MAX_SKY_LEVEL: 30,
  1281.         MIN_CLOUD_GAP: 100,
  1282.         MIN_SKY_LEVEL: 71,
  1283.         WIDTH: 46
  1284.     };
  1285.     Cloud.prototype = {
  1286.         init: function() {
  1287.             this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, Cloud.config.MIN_SKY_LEVEL);
  1288.             this.draw()
  1289.         },
  1290.         draw: function() {
  1291.             this.canvasCtx.save();
  1292.             var sourceWidth = Cloud.config.WIDTH;
  1293.             var sourceHeight = Cloud.config.HEIGHT;
  1294.             if (IS_HIDPI) {
  1295.                 sourceWidth = sourceWidth * 2;
  1296.                 sourceHeight = sourceHeight * 2
  1297.             }
  1298.             this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x, this.spritePos.y, sourceWidth, sourceHeight, this.xPos, this.yPos, Cloud.config.WIDTH, Cloud.config.HEIGHT);
  1299.             this.canvasCtx.restore()
  1300.         },
  1301.         update: function(speed) {
  1302.             if (!this.remove) {
  1303.                 this.xPos -= Math.ceil(speed);
  1304.                 this.draw();
  1305.                 if (!this.isVisible()) {
  1306.                     this.remove = true
  1307.                 }
  1308.             }
  1309.         },
  1310.         isVisible: function() {
  1311.             return this.xPos + Cloud.config.WIDTH > 0
  1312.         }
  1313.     };
  1314.  
  1315.     function HorizonLine(canvas, spritePos) {
  1316.         this.spritePos = spritePos;
  1317.         this.canvas = canvas;
  1318.         this.canvasCtx = canvas.getContext('2d');
  1319.         this.sourceDimensions = {};
  1320.         this.dimensions = HorizonLine.dimensions;
  1321.         this.sourceXPos = [this.spritePos.x, this.spritePos.x + this.dimensions.WIDTH];
  1322.         this.xPos = [];
  1323.         this.yPos = 0;
  1324.         this.bumpThreshold = 0.5;
  1325.         this.setSourceDimensions();
  1326.         this.draw()
  1327.     };
  1328.     HorizonLine.dimensions = {
  1329.         WIDTH: 600,
  1330.         HEIGHT: 12,
  1331.         YPOS: 127
  1332.     };
  1333.     HorizonLine.prototype = {
  1334.         setSourceDimensions: function() {
  1335.             for (var dimension in HorizonLine.dimensions) {
  1336.                 if (IS_HIDPI) {
  1337.                     if (dimension != 'YPOS') {
  1338.                         this.sourceDimensions[dimension] = HorizonLine.dimensions[dimension] * 2
  1339.                     }
  1340.                 } else {
  1341.                     this.sourceDimensions[dimension] = HorizonLine.dimensions[dimension]
  1342.                 }
  1343.                 this.dimensions[dimension] = HorizonLine.dimensions[dimension]
  1344.             }
  1345.             this.xPos = [0, HorizonLine.dimensions.WIDTH];
  1346.             this.yPos = HorizonLine.dimensions.YPOS
  1347.         },
  1348.         getRandomType: function() {
  1349.             return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0
  1350.         },
  1351.         draw: function() {
  1352.             this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[0], this.spritePos.y, this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, this.xPos[0], this.yPos, this.dimensions.WIDTH, this.dimensions.HEIGHT);
  1353.             this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[1], this.spritePos.y, this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, this.xPos[1], this.yPos, this.dimensions.WIDTH, this.dimensions.HEIGHT)
  1354.         },
  1355.         updateXPos: function(pos, increment) {
  1356.             var line1 = pos;
  1357.             var line2 = pos == 0 ? 1 : 0;
  1358.             this.xPos[line1] -= increment;
  1359.             this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;
  1360.             if (this.xPos[line1] <= -this.dimensions.WIDTH) {
  1361.                 this.xPos[line1] += this.dimensions.WIDTH * 2;
  1362.                 this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH;
  1363.                 this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x
  1364.             }
  1365.         },
  1366.         update: function(deltaTime, speed) {
  1367.             var increment = Math.floor(speed * (FPS / 1000) * deltaTime);
  1368.             if (this.xPos[0] <= 0) {
  1369.                 this.updateXPos(0, increment)
  1370.             } else {
  1371.                 this.updateXPos(1, increment)
  1372.             }
  1373.             this.draw()
  1374.         },
  1375.         reset: function() {
  1376.             this.xPos[0] = 0;
  1377.             this.xPos[1] = HorizonLine.dimensions.WIDTH
  1378.         }
  1379.     };
  1380.  
  1381.     function Horizon(canvas, spritePos, dimensions, gapCoefficient) {
  1382.         this.canvas = canvas;
  1383.         this.canvasCtx = this.canvas.getContext('2d');
  1384.         this.config = Horizon.config;
  1385.         this.dimensions = dimensions;
  1386.         this.gapCoefficient = gapCoefficient;
  1387.         this.obstacles = [];
  1388.         this.obstacleHistory = [];
  1389.         this.horizonOffsets = [0, 0];
  1390.         this.cloudFrequency = this.config.CLOUD_FREQUENCY;
  1391.         this.spritePos = spritePos;
  1392.         this.clouds = [];
  1393.         this.cloudSpeed = this.config.BG_CLOUD_SPEED;
  1394.         this.horizonLine = null;
  1395.         this.init()
  1396.     };
  1397.     Horizon.config = {
  1398.         BG_CLOUD_SPEED: 0.2,
  1399.         BUMPY_THRESHOLD: .3,
  1400.         CLOUD_FREQUENCY: .5,
  1401.         HORIZON_HEIGHT: 16,
  1402.         MAX_CLOUDS: 6
  1403.     };
  1404.     Horizon.prototype = {
  1405.         init: function() {
  1406.             this.addCloud();
  1407.             this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON)
  1408.         },
  1409.         update: function(deltaTime, currentSpeed, updateObstacles) {
  1410.             this.runningTime += deltaTime;
  1411.             this.horizonLine.update(deltaTime, currentSpeed);
  1412.             this.updateClouds(deltaTime, currentSpeed);
  1413.             if (updateObstacles) {
  1414.                 this.updateObstacles(deltaTime, currentSpeed)
  1415.             }
  1416.         },
  1417.         updateClouds: function(deltaTime, speed) {
  1418.             var cloudSpeed = this.cloudSpeed / 1000 * deltaTime * speed;
  1419.             var numClouds = this.clouds.length;
  1420.             if (numClouds) {
  1421.                 for (var i = numClouds - 1; i >= 0; i--) {
  1422.                     this.clouds[i].update(cloudSpeed)
  1423.                 }
  1424.                 var lastCloud = this.clouds[numClouds - 1];
  1425.                 if (numClouds < this.config.MAX_CLOUDS && (this.dimensions.WIDTH - lastCloud.xPos) > lastCloud.cloudGap && this.cloudFrequency > Math.random()) {
  1426.                     this.addCloud()
  1427.                 }
  1428.                 this.clouds = this.clouds.filter(function(obj) {
  1429.                     return !obj.remove
  1430.                 })
  1431.             }
  1432.         },
  1433.         updateObstacles: function(deltaTime, currentSpeed) {
  1434.             var updatedObstacles = this.obstacles.slice(0);
  1435.             for (var i = 0; i < this.obstacles.length; i++) {
  1436.                 var obstacle = this.obstacles[i];
  1437.                 obstacle.update(deltaTime, currentSpeed);
  1438.                 if (obstacle.remove) {
  1439.                     updatedObstacles.shift()
  1440.                 }
  1441.             }
  1442.             this.obstacles = updatedObstacles;
  1443.             if (this.obstacles.length > 0) {
  1444.                 var lastObstacle = this.obstacles[this.obstacles.length - 1];
  1445.                 if (lastObstacle && !lastObstacle.followingObstacleCreated && lastObstacle.isVisible() && (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) < this.dimensions.WIDTH) {
  1446.                     this.addNewObstacle(currentSpeed);
  1447.                     lastObstacle.followingObstacleCreated = true
  1448.                 }
  1449.             } else {
  1450.                 this.addNewObstacle(currentSpeed)
  1451.             }
  1452.         },
  1453.         addNewObstacle: function(currentSpeed) {
  1454.             var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1);
  1455.             var obstacleType = Obstacle.types[obstacleTypeIndex];
  1456.             if (this.duplicateObstacleCheck(obstacleType.type) || currentSpeed < obstacleType.minSpeed) {
  1457.                 this.addNewObstacle(currentSpeed)
  1458.             } else {
  1459.                 var obstacleSpritePos = this.spritePos[obstacleType.type];
  1460.                 this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType, obstacleSpritePos, this.dimensions, this.gapCoefficient, currentSpeed));
  1461.                 this.obstacleHistory.unshift(obstacleType.type);
  1462.                 if (this.obstacleHistory.length > 1) {
  1463.                     this.obstacleHistory.splice(Runner.config.MAX_OBSTACLE_DUPLICATION)
  1464.                 }
  1465.             }
  1466.         },
  1467.         duplicateObstacleCheck: function(nextObstacleType) {
  1468.             var duplicateCount = 0;
  1469.             for (var i = 0; i < this.obstacleHistory.length; i++) {
  1470.                 duplicateCount = this.obstacleHistory[i] == nextObstacleType ? duplicateCount + 1 : 0
  1471.             }
  1472.             return duplicateCount >= Runner.config.MAX_OBSTACLE_DUPLICATION
  1473.         },
  1474.         reset: function() {
  1475.             this.obstacles = [];
  1476.             this.horizonLine.reset()
  1477.         },
  1478.         resize: function(width, height) {
  1479.             this.canvas.width = width;
  1480.             this.canvas.height = height
  1481.         },
  1482.         addCloud: function() {
  1483.             this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD, this.dimensions.WIDTH))
  1484.         }
  1485.     }
  1486. })();
  1487. new Runner('.interstitial-wrapper');