From 24176412042989f596617f730a7f7c76fd0a980b Mon Sep 17 00:00:00 2001 From: TrapjawwS <145371179+TrapjawwS@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:32:20 +0000 Subject: [PATCH] added dino --- google dino/assets/offline-sprite-1x.png | Bin 0 -> 2520 bytes google dino/assets/offline-sprite-2x.png | Bin 0 -> 3110 bytes google dino/assets/screenshot.gif | Bin 0 -> 138027 bytes google dino/css/main.css | 155 ++ google dino/index.html | 25 + google dino/js/game.js | 2514 ++++++++++++++++++++++ google dino/readme.md | 6 + index.html | 2 + 8 files changed, 2702 insertions(+) create mode 100644 google dino/assets/offline-sprite-1x.png create mode 100644 google dino/assets/offline-sprite-2x.png create mode 100644 google dino/assets/screenshot.gif create mode 100644 google dino/css/main.css create mode 100644 google dino/index.html create mode 100644 google dino/js/game.js create mode 100644 google dino/readme.md diff --git a/google dino/assets/offline-sprite-1x.png b/google dino/assets/offline-sprite-1x.png new file mode 100644 index 0000000000000000000000000000000000000000..068d4a1b259b57c6ad8370ca6ca05adf067c27cb GIT binary patch literal 2520 zcmV;}2`Bc6P)Px#4^T{0MF0Q*Q&Us-_xJz*|JvHxd>)cO00001bW%=J z06^y0W&i*QT1iAfRCwC$ooj;PAPj&h&H)N@0h+ylt{0de-v3Nn-r-TiDp+^O52prf zD|{wtNIQfK88T$ZhY5v1MAe7BZa=mAhuk4;n>pI)8Yx_l>)G!Jo!ffSXnd*t()>?R!Sz{%2%~DPt0~%+tpjyZ|Bg84%*|%!UEwHAGzIbq?#?r9a0YowMV;u%*Beh9tViQwpuKQ&4Wux|}6ir+sJzelm|1@`V$2gFf_fx?O1bu|*8D z(mJvZ!&VtBuZ9FI6>{xGi*iHKB2MhOqILRqiiLqXjWy7cXkE?)g4Sr`DYUFFoOEpO z&{8v{r|TKC7FsNe7CR(bOC?%gKiM2a z1GGd1TFT57(4sc#LTE7y3Ujf%zYt8&nw~MD*gdpdjzOa&@@V0PELu7rfnA%;Kj?Vg=Hf|af{z-T6Kix9<(q)fItzO4Fj#ipvB&Y)~PdEc;r>K!GbY+NRr73aEi90KGaYU zF7>t29(M^biG{YKxYsgQ0-@At|0I zfTz$o#;n?*B@9}sct&;jYZVqPq4j&v0_%vgXt69Lg{Ud+IcIz7Js%V;RC$+9XPer> z_n^gPoz-7cS0Epe5Gooz!aOE!9NotX3S-qe!0-gMb}8iGa$oHPSk? z0%y4PRWyb?xlN`f0u`+{M9T?63oLMz_OsS6ngGj`x2mtA&^kMXM@xrcq=*)j&|0&k zoKX9t`Ne3V*ai}`z(;SdWXmt~m?N}Ci&{UmV6?ukUO`PTuhF`m%V;sSi56ETP}mx6 ze}%XeHC3V&r7ZlmtXidNSp%X3K`X5;l#LS)GCdJ=j?mK9#O)2?C8nn#$Z=oeiz;t0) zSG4eO+914*7P-P8`H*8Pjk{9mfELSpw>q?gIQEaHoJWo=-sRahuoEpH zjnFd7PPSkP^3Qxgritdz>QtvR(E2E}>K-S8m|Nb57Gi*B>yBTmMG4c;8!aqa-SLJ$ zWQ+UKm-coYClX$cmV1n!ds5%kJp7OPB1GNebiI-X&_WFq23o%mt=*R!51?gk%*P>q zH?$lB;Z(KKTDIbH4a^oKm+64LEA9A{L3o%f0!_)kzrCp6^Q5K>M$IBF+{cpSF$ z6WTCAqS%l_OANG*ht~9Fuqf5F@oF@LE=T4`gL$SK-$0A@Tk5g*B#TOIVK?{pP|6_{ z{vFmE9HL4zxVyOzVzc!9ugW-4{V*_XgK&dkPTq?jq2(F|T5m<`h(am4NET?t0ykLZ zhq*{oku}d&tqrGXK-;xMZOUiflE-kmK5fL$jqx z(yt6@c02zey~O^{bX;CJ9q9xS6Z3O?3_J3EBQYjnNSo`$-+2)>u}AyY4AYZiIrW{3 z+QW^p${w!LF-~DHS{t-xc3`CHduQXwxoSp>1H1Lf$sRG^VT@J%SiQpKL^6KAh-y!{i?e53KTREb*cM00006Q literal 0 HcmV?d00001 diff --git a/google dino/assets/offline-sprite-2x.png b/google dino/assets/offline-sprite-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f321c354acf5a9a81412c96911645519ed707fab GIT binary patch literal 3110 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 literal 0 HcmV?d00001 diff --git a/google dino/assets/screenshot.gif b/google dino/assets/screenshot.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2ad954b8c65fe27112569108f6ecd0619c563f1 GIT binary patch literal 138027 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�*ZVUf`5K%zI8d3Y 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%0A(5zXO6Ym) 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 #control-buttons, + .suggested-right > #control-buttons { + float: none; + } + + .snackbar { + left: 0; + bottom: 0; + width: 100%; + border-radius: 0; + } +} + +@media (max-height: 350px) { + h1 { + margin: 0 0 15px; + } + + .icon-offline { + margin: 0 0 10px; + } + + .interstitial-wrapper { + margin-top: 5%; + } + + .nav-wrapper { + margin-top: 30px; + } +} + +@media (min-width: 600px) and (max-width: 736px) and (orientation: landscape) { + .offline .interstitial-wrapper { + margin-left: 0; + margin-right: 0; + } +} + +@media (min-width: 420px) and (max-width: 736px) and + (min-height: 240px) and (max-height: 420px) and + (orientation:landscape) { + .interstitial-wrapper { + margin-bottom: 100px; + } +} + +@media (min-height: 240px) and (orientation: landscape) { + .offline .interstitial-wrapper { + margin-bottom: 90px; + } + + .icon-offline { + margin-bottom: 20px; + } +} + +@media (max-height: 320px) and (orientation: landscape) { + .icon-offline { + margin-bottom: 0; + } + + .offline .runner-container { + top: 10px; + } +} + +@media (max-width: 240px) { + button { + padding-left: 12px; + padding-right: 12px; + } + + .interstitial-wrapper { + overflow: inherit; + padding: 0 8px; + } +} + +@media (max-width: 120px) { + button { + width: auto; + } +} diff --git a/google dino/index.html b/google dino/index.html new file mode 100644 index 00000000..5e9ab191 --- /dev/null +++ b/google dino/index.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+
+
+
+
+ + + +
+ + + diff --git a/google dino/js/game.js b/google dino/js/game.js new file mode 100644 index 00000000..59d7c13d --- /dev/null +++ b/google dino/js/game.js @@ -0,0 +1,2514 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +(function() { +'use strict'; +/** + * T-Rex runner. + * @param {string} outerContainerId Outer containing element id. + * @param {Object} opt_config + * @constructor + * @export + */ +function Runner(outerContainerId, opt_config) { + // Singleton + if (Runner.instance_) { + return Runner.instance_; + } + Runner.instance_ = this; + + this.outerContainerEl = document.querySelector(outerContainerId); + this.containerEl = null; + this.snackbarEl = null; + // this.detailsButton = this.outerContainerEl.querySelector('#details-button'); + + this.config = opt_config || Runner.config; + + this.dimensions = Runner.defaultDimensions; + + this.canvas = null; + this.canvasCtx = null; + + this.tRex = null; + + this.distanceMeter = null; + this.distanceRan = 0; + + this.highestScore = 0; + + this.time = 0; + this.runningTime = 0; + this.msPerFrame = 1000 / FPS; + this.currentSpeed = this.config.SPEED; + + this.obstacles = []; + + this.started = false; + this.activated = false; + this.crashed = false; + this.paused = false; + + this.resizeTimerId_ = null; + + this.playCount = 0; + + // Sound FX. + this.audioBuffer = null; + this.soundFx = {}; + + // Global web audio context for playing sounds. + this.audioContext = null; + + // Images. + this.images = {}; + this.imagesLoaded = 0; + + // if (this.isDisabled()) { + // this.setupDisabledRunner(); + // } else { + this.loadImages(); + // } + + this.gamepadPreviousKeyDown = false; +} +window['Runner'] = Runner; + + +/** + * Default game width. + * @const + */ +var DEFAULT_WIDTH = 600; + +/** + * Frames per second. + * @const + */ +var FPS = 60; + +/** @const */ +var IS_HIDPI = window.devicePixelRatio > 1; + +/** @const */ +var IS_IOS = window.navigator.userAgent.indexOf('CriOS') > -1 || + window.navigator.userAgent == 'UIWebViewForStaticFileContent'; + +/** @const */ +var IS_MOBILE = window.navigator.userAgent.indexOf('Mobi') > -1 || IS_IOS; + +/** @const */ +var IS_TOUCH_ENABLED = 'ontouchstart' in window; + +/** + * Default game configuration. + * @enum {number} + */ +Runner.config = { + ACCELERATION: 0.001, + BG_CLOUD_SPEED: 0.2, + BOTTOM_PAD: 10, + CLEAR_TIME: 3000, + CLOUD_FREQUENCY: 0.5, + GAMEOVER_CLEAR_TIME: 750, + GAP_COEFFICIENT: 0.6, + GRAVITY: 0.6, + INITIAL_JUMP_VELOCITY: 12, + MAX_CLOUDS: 6, + MAX_OBSTACLE_LENGTH: 3, + MAX_OBSTACLE_DUPLICATION: 2, + MAX_SPEED: 13, + MIN_JUMP_HEIGHT: 35, + MOBILE_SPEED_COEFFICIENT: 1.2, + RESOURCE_TEMPLATE_ID: 'audio-resources', + SPEED: 6, + SPEED_DROP_COEFFICIENT: 3 +}; + + +/** + * Default dimensions. + * @enum {string} + */ +Runner.defaultDimensions = { + WIDTH: DEFAULT_WIDTH, + HEIGHT: 150 +}; + + +/** + * CSS class names. + * @enum {string} + */ +Runner.classes = { + CANVAS: 'runner-canvas', + CONTAINER: 'runner-container', + CRASHED: 'crashed', + ICON: 'icon-offline', + SNACKBAR: 'snackbar', + SNACKBAR_SHOW: 'snackbar-show', + TOUCH_CONTROLLER: 'controller' +}; + + +/** + * Sprite definition layout of the spritesheet. + * @enum {Object} + */ +Runner.spriteDefinition = { + LDPI: { + CACTUS_LARGE: {x: 332, y: 2}, + CACTUS_SMALL: {x: 228, y: 2}, + CLOUD: {x: 86, y: 2}, + HORIZON: {x: 2, y: 54}, + PTERODACTYL: {x: 134, y: 2}, + RESTART: {x: 2, y: 2}, + TEXT_SPRITE: {x: 484, y: 2}, + TREX: {x: 677, y: 2} + }, + HDPI: { + CACTUS_LARGE: {x: 652,y: 2}, + CACTUS_SMALL: {x: 446,y: 2}, + CLOUD: {x: 166,y: 2}, + HORIZON: {x: 2,y: 104}, + PTERODACTYL: {x: 260,y: 2}, + RESTART: {x: 2,y: 2}, + TEXT_SPRITE: {x: 954,y: 2}, + TREX: {x: 1338,y: 2} + } +}; + + +/** + * Sound FX. Reference to the ID of the audio tag on interstitial page. + * @enum {string} + */ +Runner.sounds = { + BUTTON_PRESS: 'offline-sound-press', + HIT: 'offline-sound-hit', + SCORE: 'offline-sound-reached' +}; + + +/** + * Key code mapping. + * @enum {Object} + */ +Runner.keycodes = { + JUMP: {'38': 1, '32': 1}, // Up, spacebar + DUCK: {'40': 1}, // Down + RESTART: {'13': 1} // Enter +}; + + +/** + * Runner event names. + * @enum {string} + */ +Runner.events = { + ANIM_END: 'webkitAnimationEnd', + CLICK: 'click', + KEYDOWN: 'keydown', + KEYUP: 'keyup', + MOUSEDOWN: 'mousedown', + MOUSEUP: 'mouseup', + RESIZE: 'resize', + TOUCHEND: 'touchend', + TOUCHSTART: 'touchstart', + VISIBILITY: 'visibilitychange', + BLUR: 'blur', + FOCUS: 'focus', + LOAD: 'load', + GAMEPADCONNECTED: 'gamepadconnected' +}; + + +Runner.prototype = { + /** + * Whether the easter egg has been disabled. CrOS enterprise enrolled devices. + * @return {boolean} + */ + isDisabled: function() { + return loadTimeData && loadTimeData.valueExists('disabledEasterEgg'); + }, + + /** + * For disabled instances, set up a snackbar with the disabled message. + */ + setupDisabledRunner: function() { + this.containerEl = document.createElement('div'); + this.containerEl.className = Runner.classes.SNACKBAR; + this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg'); + this.outerContainerEl.appendChild(this.containerEl); + + // Show notification when the activation key is pressed. + document.addEventListener(Runner.events.KEYDOWN, function(e) { + if (Runner.keycodes.JUMP[e.keyCode]) { + this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW); + document.querySelector('.icon').classList.add('icon-disabled'); + } + }.bind(this)); + }, + + /** + * Setting individual settings for debugging. + * @param {string} setting + * @param {*} value + */ + updateConfigSetting: function(setting, value) { + if (setting in this.config && value != undefined) { + this.config[setting] = value; + + switch (setting) { + case 'GRAVITY': + case 'MIN_JUMP_HEIGHT': + case 'SPEED_DROP_COEFFICIENT': + this.tRex.config[setting] = value; + break; + case 'INITIAL_JUMP_VELOCITY': + this.tRex.setJumpVelocity(value); + break; + case 'SPEED': + this.setSpeed(value); + break; + } + } + }, + + /** + * Cache the appropriate image sprite from the page and get the sprite sheet + * definition. + */ + loadImages: function() { + if (IS_HIDPI) { + Runner.imageSprite = document.getElementById('offline-resources-2x'); + this.spriteDef = Runner.spriteDefinition.HDPI; + } else { + Runner.imageSprite = document.getElementById('offline-resources-1x'); + this.spriteDef = Runner.spriteDefinition.LDPI; + } + + this.init(); + }, + + /** + * Load and decode base 64 encoded sounds. + */ + loadSounds: function() { + if (!IS_IOS) { + this.audioContext = new AudioContext(); + + var resourceTemplate = + document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content; + + for (var sound in Runner.sounds) { + var soundSrc = + resourceTemplate.getElementById(Runner.sounds[sound]).src; + soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1); + var buffer = decodeBase64ToArrayBuffer(soundSrc); + + // Async, so no guarantee of order in array. + this.audioContext.decodeAudioData(buffer, function(index, audioData) { + this.soundFx[index] = audioData; + }.bind(this, sound)); + } + } + }, + + /** + * Sets the game speed. Adjust the speed accordingly if on a smaller screen. + * @param {number} opt_speed + */ + setSpeed: function(opt_speed) { + var speed = opt_speed || this.currentSpeed; + + // Reduce the speed on smaller mobile screens. + if (this.dimensions.WIDTH < DEFAULT_WIDTH) { + var mobileSpeed = speed * this.dimensions.WIDTH / DEFAULT_WIDTH * + this.config.MOBILE_SPEED_COEFFICIENT; + this.currentSpeed = mobileSpeed > speed ? speed : mobileSpeed; + } else if (opt_speed) { + this.currentSpeed = opt_speed; + } + }, + + /** + * Game initialiser. + */ + init: function() { + // Hide the static icon. + // document.querySelector('.' + Runner.classes.ICON).style.visibility = + // 'hidden'; + + this.adjustDimensions(); + this.setSpeed(); + + this.containerEl = document.createElement('div'); + this.containerEl.className = Runner.classes.CONTAINER; + + // Player canvas container. + this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH, + this.dimensions.HEIGHT, Runner.classes.PLAYER); + + this.canvasCtx = this.canvas.getContext('2d'); + this.canvasCtx.fillStyle = '#f7f7f7'; + this.canvasCtx.fill(); + Runner.updateCanvasScaling(this.canvas); + + // Horizon contains clouds, obstacles and the ground. + this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions, + this.config.GAP_COEFFICIENT); + + // Distance meter + this.distanceMeter = new DistanceMeter(this.canvas, + this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH); + + // Draw t-rex + this.tRex = new Trex(this.canvas, this.spriteDef.TREX); + + this.outerContainerEl.appendChild(this.containerEl); + + if (IS_MOBILE) { + this.createTouchController(); + } + + this.startListening(); + this.update(); + + window.addEventListener(Runner.events.RESIZE, + this.debounceResize.bind(this)); + }, + + /** + * Create the touch controller. A div that covers whole screen. + */ + createTouchController: function() { + this.touchController = document.createElement('div'); + this.touchController.className = Runner.classes.TOUCH_CONTROLLER; + }, + + /** + * Debounce the resize event. + */ + debounceResize: function() { + if (!this.resizeTimerId_) { + this.resizeTimerId_ = + setInterval(this.adjustDimensions.bind(this), 250); + } + }, + + /** + * Adjust game space dimensions on resize. + */ + adjustDimensions: function() { + clearInterval(this.resizeTimerId_); + this.resizeTimerId_ = null; + + var boxStyles = window.getComputedStyle(this.outerContainerEl); + var padding = Number(boxStyles.paddingLeft.substr(0, + boxStyles.paddingLeft.length - 2)); + + this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2; + + // Redraw the elements back onto the canvas. + if (this.canvas) { + this.canvas.width = this.dimensions.WIDTH; + this.canvas.height = this.dimensions.HEIGHT; + + Runner.updateCanvasScaling(this.canvas); + + this.distanceMeter.calcXPos(this.dimensions.WIDTH); + this.clearCanvas(); + this.horizon.update(0, 0, true); + this.tRex.update(0); + + // Outer container and distance meter. + if (this.activated || this.crashed || this.paused) { + this.containerEl.style.width = this.dimensions.WIDTH + 'px'; + this.containerEl.style.height = this.dimensions.HEIGHT + 'px'; + this.distanceMeter.update(0, Math.ceil(this.distanceRan)); + this.stop(); + } else { + this.tRex.draw(0, 0); + } + + // Game over panel. + if (this.crashed && this.gameOverPanel) { + this.gameOverPanel.updateDimensions(this.dimensions.WIDTH); + this.gameOverPanel.draw(); + } + } + }, + + /** + * Play the game intro. + * Canvas container width expands out to the full width. + */ + playIntro: function() { + if (!this.started && !this.crashed) { + this.playingIntro = true; + this.tRex.playingIntro = true; + + // CSS animation definition. + var keyframes = '@-webkit-keyframes intro { ' + + 'from { width:' + Trex.config.WIDTH + 'px }' + + 'to { width: ' + this.dimensions.WIDTH + 'px }' + + '}'; + document.styleSheets[0].insertRule(keyframes, 0); + + this.containerEl.addEventListener(Runner.events.ANIM_END, + this.startGame.bind(this)); + + this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both'; + this.containerEl.style.width = this.dimensions.WIDTH + 'px'; + + if (this.touchController) { + this.outerContainerEl.appendChild(this.touchController); + } + this.activated = true; + this.started = true; + } else if (this.crashed) { + this.restart(); + } + }, + + + /** + * Update the game status to started. + */ + startGame: function() { + this.runningTime = 0; + this.playingIntro = false; + this.tRex.playingIntro = false; + this.containerEl.style.webkitAnimation = ''; + this.playCount++; + + // Handle tabbing off the page. Pause the current game. + document.addEventListener(Runner.events.VISIBILITY, + this.onVisibilityChange.bind(this)); + + window.addEventListener(Runner.events.BLUR, + this.onVisibilityChange.bind(this)); + + window.addEventListener(Runner.events.FOCUS, + this.onVisibilityChange.bind(this)); + }, + + clearCanvas: function() { + this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, + this.dimensions.HEIGHT); + }, + + /** + * Update the game frame. + */ + update: function() { + this.drawPending = false; + + var now = getTimeStamp(); + var deltaTime = now - (this.time || now); + this.time = now; + + if (this.activated) { + this.clearCanvas(); + + if (this.tRex.jumping) { + this.tRex.updateJump(deltaTime); + } + + this.runningTime += deltaTime; + var hasObstacles = this.runningTime > this.config.CLEAR_TIME; + + // First jump triggers the intro. + if (this.tRex.jumpCount == 1 && !this.playingIntro) { + this.playIntro(); + } + + // The horizon doesn't move until the intro is over. + if (this.playingIntro) { + this.horizon.update(0, this.currentSpeed, hasObstacles); + } else { + deltaTime = !this.started ? 0 : deltaTime; + this.horizon.update(deltaTime, this.currentSpeed, hasObstacles); + } + + // Check for collisions. + var collision = hasObstacles && + checkForCollision(this.horizon.obstacles[0], this.tRex); + + if (!collision) { + this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame; + + if (this.currentSpeed < this.config.MAX_SPEED) { + this.currentSpeed += this.config.ACCELERATION; + } + } else { + this.gameOver(); + } + + var playAcheivementSound = this.distanceMeter.update(deltaTime, + Math.ceil(this.distanceRan)); + + if (playAcheivementSound) { + this.playSound(this.soundFx.SCORE); + } + } + + if (!this.crashed) { + this.tRex.update(deltaTime); + this.raq(); + } + }, + + /** + * Event handler. + */ + handleEvent: function(e) { + return (function(evtType, events) { + switch (evtType) { + case events.KEYDOWN: + case events.TOUCHSTART: + case events.MOUSEDOWN: + case events.GAMEPADCONNECTED: + this.onKeyDown(e); + break; + case events.KEYUP: + case events.TOUCHEND: + case events.MOUSEUP: + this.onKeyUp(e); + break; + } + }.bind(this))(e.type, Runner.events); + }, + + /** + * Bind relevant key / mouse / touch listeners. + */ + startListening: function() { + // Keys. + document.addEventListener(Runner.events.KEYDOWN, this); + document.addEventListener(Runner.events.KEYUP, this); + + if (IS_MOBILE) { + // Mobile only touch devices. + this.touchController.addEventListener(Runner.events.TOUCHSTART, this); + this.touchController.addEventListener(Runner.events.TOUCHEND, this); + this.containerEl.addEventListener(Runner.events.TOUCHSTART, this); + } else { + // Mouse. + document.addEventListener(Runner.events.MOUSEDOWN, this); + document.addEventListener(Runner.events.MOUSEUP, this); + } + window.addEventListener(Runner.events.GAMEPADCONNECTED, this); + window.setInterval(this.pollGamepads.bind(this), 10); + }, + + /** + * Convert Gamepad input events to keydown/up events (spacebar) + */ + pollGamepads: function() { + var gamepads = navigator.getGamepads(); + var keydown = false; + for(var i = 0; i < gamepads.length; i++) { + if (gamepads[i] != undefined) { + if (gamepads[i].buttons.filter(function(e){return e.pressed == true}).length > 0) { + keydown = true; + } + } + } + if (keydown != this.gamepadPreviousKeyDown) { + this.gamepadPreviousKeyDown = keydown; + + var event = new Event(keydown ? 'keydown' : 'keyup'); + event.keyCode = 32;//keys(Runner.keycodes.JUMP)[0]; + event.which = event.keyCode; + event.altKey = false; + event.ctrlKey = true; + event.shiftKey = false; + event.metaKey = false; + document.dispatchEvent(event); + } + }, + + /** + * Remove all listeners. + */ + stopListening: function() { + document.removeEventListener(Runner.events.KEYDOWN, this); + document.removeEventListener(Runner.events.KEYUP, this); + + if (IS_MOBILE) { + this.touchController.removeEventListener(Runner.events.TOUCHSTART, this); + this.touchController.removeEventListener(Runner.events.TOUCHEND, this); + this.containerEl.removeEventListener(Runner.events.TOUCHSTART, this); + } else { + document.removeEventListener(Runner.events.MOUSEDOWN, this); + document.removeEventListener(Runner.events.MOUSEUP, this); + } + }, + + /** + * Process keydown. + * @param {Event} e + */ + onKeyDown: function(e) { + // Prevent native page scrolling whilst tapping on mobile. + if (IS_MOBILE) { + e.preventDefault(); + } + + // if (e.target != this.detailsButton) { + if (!this.crashed && (Runner.keycodes.JUMP[e.keyCode] || + e.type == Runner.events.TOUCHSTART || e.type == Runner.events.GAMEPADCONNECTED)) { + if (!this.activated) { + this.loadSounds(); + this.activated = true; + // errorPageController.trackEasterEgg(); + } + + if (!this.tRex.jumping && !this.tRex.ducking) { + this.playSound(this.soundFx.BUTTON_PRESS); + this.tRex.startJump(this.currentSpeed); + } + } + + if (this.crashed && e.type == Runner.events.TOUCHSTART && + e.currentTarget == this.containerEl) { + this.restart(); + } + // } + + if (this.activated && !this.crashed && Runner.keycodes.DUCK[e.keyCode]) { + e.preventDefault(); + if (this.tRex.jumping) { + // Speed drop, activated only when jump key is not pressed. + this.tRex.setSpeedDrop(); + } else if (!this.tRex.jumping && !this.tRex.ducking) { + // Duck. + this.tRex.setDuck(true); + } + } + }, + + + /** + * Process key up. + * @param {Event} e + */ + onKeyUp: function(e) { + var keyCode = String(e.keyCode); + var isjumpKey = Runner.keycodes.JUMP[keyCode] || + e.type == Runner.events.TOUCHEND || + e.type == Runner.events.MOUSEDOWN; + + if (this.isRunning() && isjumpKey) { + this.tRex.endJump(); + } else if (Runner.keycodes.DUCK[keyCode]) { + this.tRex.speedDrop = false; + this.tRex.setDuck(false); + } else if (this.crashed) { + // Check that enough time has elapsed before allowing jump key to restart. + var deltaTime = getTimeStamp() - this.time; + + if (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) || + (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && + Runner.keycodes.JUMP[keyCode])) { + this.restart(); + } + } else if (this.paused && isjumpKey) { + // Reset the jump state + this.tRex.reset(); + this.play(); + } + }, + + /** + * Returns whether the event was a left click on canvas. + * On Windows right click is registered as a click. + * @param {Event} e + * @return {boolean} + */ + isLeftClickOnCanvas: function(e) { + return e.button != null && e.button < 2 && + e.type == Runner.events.MOUSEUP && e.target == this.canvas; + }, + + /** + * RequestAnimationFrame wrapper. + */ + raq: function() { + if (!this.drawPending) { + this.drawPending = true; + this.raqId = requestAnimationFrame(this.update.bind(this)); + } + }, + + /** + * Whether the game is running. + * @return {boolean} + */ + isRunning: function() { + return !!this.raqId; + }, + + /** + * Game over state. + */ + gameOver: function() { + this.playSound(this.soundFx.HIT); + vibrate(200); + + this.stop(); + this.crashed = true; + this.distanceMeter.acheivement = false; + + this.tRex.update(100, Trex.status.CRASHED); + + // Game over panel. + if (!this.gameOverPanel) { + this.gameOverPanel = new GameOverPanel(this.canvas, + this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART, + this.dimensions); + } else { + this.gameOverPanel.draw(); + } + + // Update the high score. + if (this.distanceRan > this.highestScore) { + this.highestScore = Math.ceil(this.distanceRan); + this.distanceMeter.setHighScore(this.highestScore); + } + + // Reset the time clock. + this.time = getTimeStamp(); + }, + + stop: function() { + this.activated = false; + this.paused = true; + cancelAnimationFrame(this.raqId); + this.raqId = 0; + }, + + play: function() { + if (!this.crashed) { + this.activated = true; + this.paused = false; + this.tRex.update(0, Trex.status.RUNNING); + this.time = getTimeStamp(); + this.update(); + } + }, + + restart: function() { + if (!this.raqId) { + this.playCount++; + this.runningTime = 0; + this.activated = true; + this.crashed = false; + this.distanceRan = 0; + this.setSpeed(this.config.SPEED); + + this.time = getTimeStamp(); + this.containerEl.classList.remove(Runner.classes.CRASHED); + this.clearCanvas(); + this.distanceMeter.reset(this.highestScore); + this.horizon.reset(); + this.tRex.reset(); + this.playSound(this.soundFx.BUTTON_PRESS); + + this.update(); + } + }, + + /** + * Pause the game if the tab is not in focus. + */ + onVisibilityChange: function(e) { + if (document.hidden || document.webkitHidden || e.type == 'blur') { + this.stop(); + } else if (!this.crashed) { + this.tRex.reset(); + this.play(); + } + }, + + /** + * Play a sound. + * @param {SoundBuffer} soundBuffer + */ + playSound: function(soundBuffer) { + if (soundBuffer) { + var sourceNode = this.audioContext.createBufferSource(); + sourceNode.buffer = soundBuffer; + sourceNode.connect(this.audioContext.destination); + sourceNode.start(0); + } + } +}; + + +/** + * Updates the canvas size taking into + * account the backing store pixel ratio and + * the device pixel ratio. + * + * See article by Paul Lewis: + * http://www.html5rocks.com/en/tutorials/canvas/hidpi/ + * + * @param {HTMLCanvasElement} canvas + * @param {number} opt_width + * @param {number} opt_height + * @return {boolean} Whether the canvas was scaled. + */ +Runner.updateCanvasScaling = function(canvas, opt_width, opt_height) { + var context = canvas.getContext('2d'); + + // Query the various pixel ratios + var devicePixelRatio = Math.floor(window.devicePixelRatio) || 1; + var backingStoreRatio = Math.floor(context.webkitBackingStorePixelRatio) || 1; + var ratio = devicePixelRatio / backingStoreRatio; + + // Upscale the canvas if the two ratios don't match + if (devicePixelRatio !== backingStoreRatio) { + var oldWidth = opt_width || canvas.width; + var oldHeight = opt_height || canvas.height; + + canvas.width = oldWidth * ratio; + canvas.height = oldHeight * ratio; + + canvas.style.width = oldWidth + 'px'; + canvas.style.height = oldHeight + 'px'; + + // Scale the context to counter the fact that we've manually scaled + // our canvas element. + context.scale(ratio, ratio); + return true; + } else if (devicePixelRatio == 1) { + // Reset the canvas width / height. Fixes scaling bug when the page is + // zoomed and the devicePixelRatio changes accordingly. + canvas.style.width = canvas.width + 'px'; + canvas.style.height = canvas.height + 'px'; + } + return false; +}; + + +/** + * Get random number. + * @param {number} min + * @param {number} max + * @param {number} + */ +function getRandomNum(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + + +/** + * Vibrate on mobile devices. + * @param {number} duration Duration of the vibration in milliseconds. + */ +function vibrate(duration) { + if (IS_MOBILE && window.navigator.vibrate) { + window.navigator.vibrate(duration); + } +} + + +/** + * Create canvas element. + * @param {HTMLElement} container Element to append canvas to. + * @param {number} width + * @param {number} height + * @param {string} opt_classname + * @return {HTMLCanvasElement} + */ +function createCanvas(container, width, height, opt_classname) { + var canvas = document.createElement('canvas'); + canvas.className = opt_classname ? Runner.classes.CANVAS + ' ' + + opt_classname : Runner.classes.CANVAS; + canvas.width = width; + canvas.height = height; + container.appendChild(canvas); + + return canvas; +} + + +/** + * Decodes the base 64 audio to ArrayBuffer used by Web Audio. + * @param {string} base64String + */ +function decodeBase64ToArrayBuffer(base64String) { + var len = (base64String.length / 4) * 3; + var str = atob(base64String); + var arrayBuffer = new ArrayBuffer(len); + var bytes = new Uint8Array(arrayBuffer); + + for (var i = 0; i < len; i++) { + bytes[i] = str.charCodeAt(i); + } + return bytes.buffer; +} + + +/** + * Return the current timestamp. + * @return {number} + */ +function getTimeStamp() { + return IS_IOS ? new Date().getTime() : performance.now(); +} + + +//****************************************************************************** + + +/** + * Game over panel. + * @param {!HTMLCanvasElement} canvas + * @param {Object} textImgPos + * @param {Object} restartImgPos + * @param {!Object} dimensions Canvas dimensions. + * @constructor + */ +function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.canvasDimensions = dimensions; + this.textImgPos = textImgPos; + this.restartImgPos = restartImgPos; + this.draw(); +}; + + +/** + * Dimensions used in the panel. + * @enum {number} + */ +GameOverPanel.dimensions = { + TEXT_X: 0, + TEXT_Y: 13, + TEXT_WIDTH: 191, + TEXT_HEIGHT: 11, + RESTART_WIDTH: 36, + RESTART_HEIGHT: 32 +}; + + +GameOverPanel.prototype = { + /** + * Update the panel dimensions. + * @param {number} width New canvas width. + * @param {number} opt_height Optional new canvas height. + */ + updateDimensions: function(width, opt_height) { + this.canvasDimensions.WIDTH = width; + if (opt_height) { + this.canvasDimensions.HEIGHT = opt_height; + } + }, + + /** + * Draw the panel. + */ + draw: function() { + var dimensions = GameOverPanel.dimensions; + + var centerX = this.canvasDimensions.WIDTH / 2; + + // Game over text. + var textSourceX = dimensions.TEXT_X; + var textSourceY = dimensions.TEXT_Y; + var textSourceWidth = dimensions.TEXT_WIDTH; + var textSourceHeight = dimensions.TEXT_HEIGHT; + + var textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2)); + var textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3); + var textTargetWidth = dimensions.TEXT_WIDTH; + var textTargetHeight = dimensions.TEXT_HEIGHT; + + var restartSourceWidth = dimensions.RESTART_WIDTH; + var restartSourceHeight = dimensions.RESTART_HEIGHT; + var restartTargetX = centerX - (dimensions.RESTART_WIDTH / 2); + var restartTargetY = this.canvasDimensions.HEIGHT / 2; + + if (IS_HIDPI) { + textSourceY *= 2; + textSourceX *= 2; + textSourceWidth *= 2; + textSourceHeight *= 2; + restartSourceWidth *= 2; + restartSourceHeight *= 2; + } + + textSourceX += this.textImgPos.x; + textSourceY += this.textImgPos.y; + + // Game over text from sprite. + this.canvasCtx.drawImage(Runner.imageSprite, + textSourceX, textSourceY, textSourceWidth, textSourceHeight, + textTargetX, textTargetY, textTargetWidth, textTargetHeight); + + // Restart button. + this.canvasCtx.drawImage(Runner.imageSprite, + this.restartImgPos.x, this.restartImgPos.y, + restartSourceWidth, restartSourceHeight, + restartTargetX, restartTargetY, dimensions.RESTART_WIDTH, + dimensions.RESTART_HEIGHT); + } +}; + + +//****************************************************************************** + +/** + * Check for a collision. + * @param {!Obstacle} obstacle + * @param {!Trex} tRex T-rex object. + * @param {HTMLCanvasContext} opt_canvasCtx Optional canvas context for drawing + * collision boxes. + * @return {Array} + */ +function checkForCollision(obstacle, tRex, opt_canvasCtx) { + var obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos; + + // Adjustments are made to the bounding box as there is a 1 pixel white + // border around the t-rex and obstacles. + var tRexBox = new CollisionBox( + tRex.xPos + 1, + tRex.yPos + 1, + tRex.config.WIDTH - 2, + tRex.config.HEIGHT - 2); + + var obstacleBox = new CollisionBox( + obstacle.xPos + 1, + obstacle.yPos + 1, + obstacle.typeConfig.width * obstacle.size - 2, + obstacle.typeConfig.height - 2); + + // Debug outer box + if (opt_canvasCtx) { + drawCollisionBoxes(opt_canvasCtx, tRexBox, obstacleBox); + } + + // Simple outer bounds check. + if (boxCompare(tRexBox, obstacleBox)) { + var collisionBoxes = obstacle.collisionBoxes; + var tRexCollisionBoxes = tRex.ducking ? + Trex.collisionBoxes.DUCKING : Trex.collisionBoxes.RUNNING; + + // Detailed axis aligned box check. + for (var t = 0; t < tRexCollisionBoxes.length; t++) { + for (var i = 0; i < collisionBoxes.length; i++) { + // Adjust the box to actual positions. + var adjTrexBox = + createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox); + var adjObstacleBox = + createAdjustedCollisionBox(collisionBoxes[i], obstacleBox); + var crashed = boxCompare(adjTrexBox, adjObstacleBox); + + // Draw boxes for debug. + if (opt_canvasCtx) { + drawCollisionBoxes(opt_canvasCtx, adjTrexBox, adjObstacleBox); + } + + if (crashed) { + return [adjTrexBox, adjObstacleBox]; + } + } + } + } + return false; +}; + + +/** + * Adjust the collision box. + * @param {!CollisionBox} box The original box. + * @param {!CollisionBox} adjustment Adjustment box. + * @return {CollisionBox} The adjusted collision box object. + */ +function createAdjustedCollisionBox(box, adjustment) { + return new CollisionBox( + box.x + adjustment.x, + box.y + adjustment.y, + box.width, + box.height); +}; + + +/** + * Draw the collision boxes for debug. + */ +function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) { + canvasCtx.save(); + canvasCtx.strokeStyle = '#f00'; + canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height); + + canvasCtx.strokeStyle = '#0f0'; + canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y, + obstacleBox.width, obstacleBox.height); + canvasCtx.restore(); +}; + + +/** + * Compare two collision boxes for a collision. + * @param {CollisionBox} tRexBox + * @param {CollisionBox} obstacleBox + * @return {boolean} Whether the boxes intersected. + */ +function boxCompare(tRexBox, obstacleBox) { + var crashed = false; + var tRexBoxX = tRexBox.x; + var tRexBoxY = tRexBox.y; + + var obstacleBoxX = obstacleBox.x; + var obstacleBoxY = obstacleBox.y; + + // Axis-Aligned Bounding Box method. + if (tRexBox.x < obstacleBoxX + obstacleBox.width && + tRexBox.x + tRexBox.width > obstacleBoxX && + tRexBox.y < obstacleBox.y + obstacleBox.height && + tRexBox.height + tRexBox.y > obstacleBox.y) { + crashed = true; + } + + return crashed; +}; + + +//****************************************************************************** + +/** + * Collision box object. + * @param {number} x X position. + * @param {number} y Y Position. + * @param {number} w Width. + * @param {number} h Height. + */ +function CollisionBox(x, y, w, h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; +}; + + +//****************************************************************************** + +/** + * Obstacle. + * @param {HTMLCanvasCtx} canvasCtx + * @param {Obstacle.type} type + * @param {Object} spritePos Obstacle position in sprite. + * @param {Object} dimensions + * @param {number} gapCoefficient Mutipler in determining the gap. + * @param {number} speed + */ +function Obstacle(canvasCtx, type, spriteImgPos, dimensions, + gapCoefficient, speed) { + + this.canvasCtx = canvasCtx; + this.spritePos = spriteImgPos; + this.typeConfig = type; + this.gapCoefficient = gapCoefficient; + this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH); + this.dimensions = dimensions; + this.remove = false; + this.xPos = 0; + this.yPos = 0; + this.width = 0; + this.collisionBoxes = []; + this.gap = 0; + this.speedOffset = 0; + + // For animated obstacles. + this.currentFrame = 0; + this.timer = 0; + + this.init(speed); +}; + +/** + * Coefficient for calculating the maximum gap. + * @const + */ +Obstacle.MAX_GAP_COEFFICIENT = 1.5; + +/** + * Maximum obstacle grouping count. + * @const + */ +Obstacle.MAX_OBSTACLE_LENGTH = 3, + + +Obstacle.prototype = { + /** + * Initialise the DOM for the obstacle. + * @param {number} speed + */ + init: function(speed) { + this.cloneCollisionBoxes(); + + // Only allow sizing if we're at the right speed. + if (this.size > 1 && this.typeConfig.multipleSpeed > speed) { + this.size = 1; + } + + this.width = this.typeConfig.width * this.size; + this.xPos = this.dimensions.WIDTH - this.width; + + // Check if obstacle can be positioned at various heights. + if (Array.isArray(this.typeConfig.yPos)) { + var yPosConfig = IS_MOBILE ? this.typeConfig.yPosMobile : + this.typeConfig.yPos; + this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)]; + } else { + this.yPos = this.typeConfig.yPos; + } + + this.draw(); + + // Make collision box adjustments, + // Central box is adjusted to the size as one box. + // ____ ______ ________ + // _| |-| _| |-| _| |-| + // | |<->| | | |<--->| | | |<----->| | + // | | 1 | | | | 2 | | | | 3 | | + // |_|___|_| |_|_____|_| |_|_______|_| + // + if (this.size > 1) { + this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - + this.collisionBoxes[2].width; + this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width; + } + + // For obstacles that go at a different speed from the horizon. + if (this.typeConfig.speedOffset) { + this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset : + -this.typeConfig.speedOffset; + } + + this.gap = this.getGap(this.gapCoefficient, speed); + }, + + /** + * Draw and crop based on size. + */ + draw: function() { + var sourceWidth = this.typeConfig.width; + var sourceHeight = this.typeConfig.height; + + if (IS_HIDPI) { + sourceWidth = sourceWidth * 2; + sourceHeight = sourceHeight * 2; + } + + // X position in sprite. + var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) + + this.spritePos.x; + + // Animation frames. + if (this.currentFrame > 0) { + sourceX += sourceWidth * this.currentFrame; + } + + 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); + }, + + /** + * Obstacle frame update. + * @param {number} deltaTime + * @param {number} speed + */ + update: function(deltaTime, speed) { + if (!this.remove) { + if (this.typeConfig.speedOffset) { + speed += this.speedOffset; + } + this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime); + + // Update frame + if (this.typeConfig.numFrames) { + this.timer += deltaTime; + if (this.timer >= this.typeConfig.frameRate) { + this.currentFrame = + this.currentFrame == this.typeConfig.numFrames - 1 ? + 0 : this.currentFrame + 1; + this.timer = 0; + } + } + this.draw(); + + if (!this.isVisible()) { + this.remove = true; + } + } + }, + + /** + * Calculate a random gap size. + * - Minimum gap gets wider as speed increses + * @param {number} gapCoefficient + * @param {number} speed + * @return {number} The gap size. + */ + getGap: function(gapCoefficient, speed) { + var minGap = Math.round(this.width * speed + + this.typeConfig.minGap * gapCoefficient); + var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT); + return getRandomNum(minGap, maxGap); + }, + + /** + * Check if obstacle is visible. + * @return {boolean} Whether the obstacle is in the game area. + */ + isVisible: function() { + return this.xPos + this.width > 0; + }, + + /** + * Make a copy of the collision boxes, since these will change based on + * obstacle type and size. + */ + cloneCollisionBoxes: function() { + var collisionBoxes = this.typeConfig.collisionBoxes; + + for (var i = collisionBoxes.length - 1; i >= 0; i--) { + this.collisionBoxes[i] = new CollisionBox(collisionBoxes[i].x, + collisionBoxes[i].y, collisionBoxes[i].width, + collisionBoxes[i].height); + } + } +}; + + +/** + * Obstacle definitions. + * minGap: minimum pixel space betweeen obstacles. + * multipleSpeed: Speed at which multiples are allowed. + * speedOffset: speed faster / slower than the horizon. + * minSpeed: Minimum speed which the obstacle can make an appearance. + */ +Obstacle.types = [ + { + type: 'CACTUS_SMALL', + width: 17, + height: 35, + yPos: 105, + multipleSpeed: 4, + minGap: 120, + minSpeed: 0, + collisionBoxes: [ + new CollisionBox(0, 7, 5, 27), + new CollisionBox(4, 0, 6, 34), + new CollisionBox(10, 4, 7, 14) + ] + }, + { + type: 'CACTUS_LARGE', + width: 25, + height: 50, + yPos: 90, + multipleSpeed: 7, + minGap: 120, + minSpeed: 0, + collisionBoxes: [ + new CollisionBox(0, 12, 7, 38), + new CollisionBox(8, 0, 7, 49), + new CollisionBox(13, 10, 10, 38) + ] + }, + { + type: 'PTERODACTYL', + width: 46, + height: 40, + yPos: [ 100, 75, 50 ], // Variable height. + yPosMobile: [ 100, 50 ], // Variable height mobile. + multipleSpeed: 999, + minSpeed: 8.5, + minGap: 150, + 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) + ], + numFrames: 2, + frameRate: 1000/6, + speedOffset: .8 + } +]; + + +//****************************************************************************** +/** + * T-rex game character. + * @param {HTMLCanvas} canvas + * @param {Object} spritePos Positioning within image sprite. + * @constructor + */ +function Trex(canvas, spritePos) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.spritePos = spritePos; + this.xPos = 0; + this.yPos = 0; + // Position when on the ground. + this.groundYPos = 0; + this.currentFrame = 0; + this.currentAnimFrames = []; + this.blinkDelay = 0; + this.animStartTime = 0; + this.timer = 0; + this.msPerFrame = 1000 / FPS; + this.config = Trex.config; + // Current status. + this.status = Trex.status.WAITING; + + this.jumping = false; + this.ducking = false; + this.jumpVelocity = 0; + this.reachedMinHeight = false; + this.speedDrop = false; + this.jumpCount = 0; + this.jumpspotX = 0; + + this.init(); +}; + + +/** + * T-rex player config. + * @enum {number} + */ +Trex.config = { + DROP_VELOCITY: -5, + GRAVITY: 0.6, + HEIGHT: 47, + HEIGHT_DUCK: 25, + INIITAL_JUMP_VELOCITY: -10, + INTRO_DURATION: 1500, + MAX_JUMP_HEIGHT: 30, + MIN_JUMP_HEIGHT: 30, + SPEED_DROP_COEFFICIENT: 3, + SPRITE_WIDTH: 262, + START_X_POS: 50, + WIDTH: 44, + WIDTH_DUCK: 59 +}; + + +/** + * Used in collision detection. + * @type {Array} + */ +Trex.collisionBoxes = { + DUCKING: [ + new CollisionBox(1, 18, 55, 25) + ], + 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) + ] +}; + + +/** + * Animation states. + * @enum {string} + */ +Trex.status = { + CRASHED: 'CRASHED', + DUCKING: 'DUCKING', + JUMPING: 'JUMPING', + RUNNING: 'RUNNING', + WAITING: 'WAITING' +}; + +/** + * Blinking coefficient. + * @const + */ +Trex.BLINK_TIMING = 7000; + + +/** + * Animation config for different states. + * @enum {Object} + */ +Trex.animFrames = { + WAITING: { + frames: [44, 0], + msPerFrame: 1000 / 3 + }, + RUNNING: { + frames: [88, 132], + msPerFrame: 1000 / 12 + }, + CRASHED: { + frames: [220], + msPerFrame: 1000 / 60 + }, + JUMPING: { + frames: [0], + msPerFrame: 1000 / 60 + }, + DUCKING: { + frames: [262, 321], + msPerFrame: 1000 / 8 + } +}; + + +Trex.prototype = { + /** + * T-rex player initaliser. + * Sets the t-rex to blink at random intervals. + */ + init: function() { + this.blinkDelay = this.setBlinkDelay(); + this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT - + Runner.config.BOTTOM_PAD; + this.yPos = this.groundYPos; + this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT; + + this.draw(0, 0); + this.update(0, Trex.status.WAITING); + }, + + /** + * Setter for the jump velocity. + * The approriate drop velocity is also set. + */ + setJumpVelocity: function(setting) { + this.config.INIITAL_JUMP_VELOCITY = -setting; + this.config.DROP_VELOCITY = -setting / 2; + }, + + /** + * Set the animation status. + * @param {!number} deltaTime + * @param {Trex.status} status Optional status to switch to. + */ + update: function(deltaTime, opt_status) { + this.timer += deltaTime; + + // Update the status. + if (opt_status) { + this.status = opt_status; + this.currentFrame = 0; + this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; + this.currentAnimFrames = Trex.animFrames[opt_status].frames; + + if (opt_status == Trex.status.WAITING) { + this.animStartTime = getTimeStamp(); + this.setBlinkDelay(); + } + } + + // Game intro animation, T-rex moves in from the left. + if (this.playingIntro && this.xPos < this.config.START_X_POS) { + this.xPos += Math.round((this.config.START_X_POS / + this.config.INTRO_DURATION) * deltaTime); + } + + if (this.status == Trex.status.WAITING) { + this.blink(getTimeStamp()); + } else { + this.draw(this.currentAnimFrames[this.currentFrame], 0); + } + + // Update the frame position. + if (this.timer >= this.msPerFrame) { + this.currentFrame = this.currentFrame == + this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; + this.timer = 0; + } + + // Speed drop becomes duck if the down key is still being pressed. + if (this.speedDrop && this.yPos == this.groundYPos) { + this.speedDrop = false; + this.setDuck(true); + } + }, + + /** + * Draw the t-rex to a particular position. + * @param {number} x + * @param {number} y + */ + draw: function(x, y) { + var sourceX = x; + var sourceY = y; + var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ? + this.config.WIDTH_DUCK : this.config.WIDTH; + var sourceHeight = this.config.HEIGHT; + + if (IS_HIDPI) { + sourceX *= 2; + sourceY *= 2; + sourceWidth *= 2; + sourceHeight *= 2; + } + + // Adjustments for sprite sheet position. + sourceX += this.spritePos.x; + sourceY += this.spritePos.y; + + // Ducking. + if (this.ducking && this.status != Trex.status.CRASHED) { + this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, + sourceWidth, sourceHeight, + this.xPos, this.yPos, + this.config.WIDTH_DUCK, this.config.HEIGHT); + } else { + // Crashed whilst ducking. Trex is standing up so needs adjustment. + if (this.ducking && this.status == Trex.status.CRASHED) { + this.xPos++; + } + // Standing / running + this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, + sourceWidth, sourceHeight, + this.xPos, this.yPos, + this.config.WIDTH, this.config.HEIGHT); + } + }, + + /** + * Sets a random time for the blink to happen. + */ + setBlinkDelay: function() { + this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING); + }, + + /** + * Make t-rex blink at random intervals. + * @param {number} time Current time in milliseconds. + */ + blink: function(time) { + var deltaTime = time - this.animStartTime; + + if (deltaTime >= this.blinkDelay) { + this.draw(this.currentAnimFrames[this.currentFrame], 0); + + if (this.currentFrame == 1) { + // Set new random delay to blink. + this.setBlinkDelay(); + this.animStartTime = time; + } + } + }, + + /** + * Initialise a jump. + * @param {number} speed + */ + startJump: function(speed) { + if (!this.jumping) { + this.update(0, Trex.status.JUMPING); + // Tweak the jump velocity based on the speed. + this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10); + this.jumping = true; + this.reachedMinHeight = false; + this.speedDrop = false; + } + }, + + /** + * Jump is complete, falling down. + */ + endJump: function() { + if (this.reachedMinHeight && + this.jumpVelocity < this.config.DROP_VELOCITY) { + this.jumpVelocity = this.config.DROP_VELOCITY; + } + }, + + /** + * Update frame for a jump. + * @param {number} deltaTime + * @param {number} speed + */ + updateJump: function(deltaTime, speed) { + var msPerFrame = Trex.animFrames[this.status].msPerFrame; + var framesElapsed = deltaTime / msPerFrame; + + // Speed drop makes Trex fall faster. + if (this.speedDrop) { + this.yPos += Math.round(this.jumpVelocity * + this.config.SPEED_DROP_COEFFICIENT * framesElapsed); + } else { + this.yPos += Math.round(this.jumpVelocity * framesElapsed); + } + + this.jumpVelocity += this.config.GRAVITY * framesElapsed; + + // Minimum height has been reached. + if (this.yPos < this.minJumpHeight || this.speedDrop) { + this.reachedMinHeight = true; + } + + // Reached max height + if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) { + this.endJump(); + } + + // Back down at ground level. Jump completed. + if (this.yPos > this.groundYPos) { + this.reset(); + this.jumpCount++; + } + + this.update(deltaTime); + }, + + /** + * Set the speed drop. Immediately cancels the current jump. + */ + setSpeedDrop: function() { + this.speedDrop = true; + this.jumpVelocity = 1; + }, + + /** + * @param {boolean} isDucking. + */ + setDuck: function(isDucking) { + if (isDucking && this.status != Trex.status.DUCKING) { + this.update(0, Trex.status.DUCKING); + this.ducking = true; + } else if (this.status == Trex.status.DUCKING) { + this.update(0, Trex.status.RUNNING); + this.ducking = false; + } + }, + + /** + * Reset the t-rex to running at start of game. + */ + reset: function() { + this.yPos = this.groundYPos; + this.jumpVelocity = 0; + this.jumping = false; + this.ducking = false; + this.update(0, Trex.status.RUNNING); + this.midair = false; + this.speedDrop = false; + this.jumpCount = 0; + } +}; + + +//****************************************************************************** + +/** + * Handles displaying the distance meter. + * @param {!HTMLCanvasElement} canvas + * @param {Object} spritePos Image position in sprite. + * @param {number} canvasWidth + * @constructor + */ +function DistanceMeter(canvas, spritePos, canvasWidth) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.image = Runner.imageSprite; + this.spritePos = spritePos; + this.x = 0; + this.y = 5; + + this.currentDistance = 0; + this.maxScore = 0; + this.highScore = 0; + this.container = null; + + this.digits = []; + this.acheivement = false; + this.defaultString = ''; + this.flashTimer = 0; + this.flashIterations = 0; + + this.config = DistanceMeter.config; + this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS; + this.init(canvasWidth); +}; + + +/** + * @enum {number} + */ +DistanceMeter.dimensions = { + WIDTH: 10, + HEIGHT: 13, + DEST_WIDTH: 11 +}; + + +/** + * Y positioning of the digits in the sprite sheet. + * X position is always 0. + * @type {Array} + */ +DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120]; + + +/** + * Distance meter config. + * @enum {number} + */ +DistanceMeter.config = { + // Number of digits. + MAX_DISTANCE_UNITS: 5, + + // Distance that causes achievement animation. + ACHIEVEMENT_DISTANCE: 100, + + // Used for conversion from pixel distance to a scaled unit. + COEFFICIENT: 0.025, + + // Flash duration in milliseconds. + FLASH_DURATION: 1000 / 4, + + // Flash iterations for achievement animation. + FLASH_ITERATIONS: 3 +}; + + +DistanceMeter.prototype = { + /** + * Initialise the distance meter to '00000'. + * @param {number} width Canvas width in px. + */ + init: function(width) { + var maxDistanceStr = ''; + + this.calcXPos(width); + this.maxScore = this.maxScoreUnits; + for (var i = 0; i < this.maxScoreUnits; i++) { + this.draw(i, 0); + this.defaultString += '0'; + maxDistanceStr += '9'; + } + + this.maxScore = parseInt(maxDistanceStr); + }, + + /** + * Calculate the xPos in the canvas. + * @param {number} canvasWidth + */ + calcXPos: function(canvasWidth) { + this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH * + (this.maxScoreUnits + 1)); + }, + + /** + * Draw a digit to canvas. + * @param {number} digitPos Position of the digit. + * @param {number} value Digit value 0-9. + * @param {boolean} opt_highScore Whether drawing the high score. + */ + draw: function(digitPos, value, opt_highScore) { + var sourceWidth = DistanceMeter.dimensions.WIDTH; + var sourceHeight = DistanceMeter.dimensions.HEIGHT; + var sourceX = DistanceMeter.dimensions.WIDTH * value; + var sourceY = 0; + + var targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH; + var targetY = this.y; + var targetWidth = DistanceMeter.dimensions.WIDTH; + var targetHeight = DistanceMeter.dimensions.HEIGHT; + + // For high DPI we 2x source values. + if (IS_HIDPI) { + sourceWidth *= 2; + sourceHeight *= 2; + sourceX *= 2; + } + + sourceX += this.spritePos.x; + sourceY += this.spritePos.y; + + this.canvasCtx.save(); + + if (opt_highScore) { + // Left of the current score. + var highScoreX = this.x - (this.maxScoreUnits * 2) * + DistanceMeter.dimensions.WIDTH; + this.canvasCtx.translate(highScoreX, this.y); + } else { + this.canvasCtx.translate(this.x, this.y); + } + + this.canvasCtx.drawImage(this.image, sourceX, sourceY, + sourceWidth, sourceHeight, + targetX, targetY, + targetWidth, targetHeight + ); + + this.canvasCtx.restore(); + }, + + /** + * Covert pixel distance to a 'real' distance. + * @param {number} distance Pixel distance ran. + * @return {number} The 'real' distance ran. + */ + getActualDistance: function(distance) { + return distance ? Math.round(distance * this.config.COEFFICIENT) : 0; + }, + + /** + * Update the distance meter. + * @param {number} distance + * @param {number} deltaTime + * @return {boolean} Whether the acheivement sound fx should be played. + */ + update: function(deltaTime, distance) { + var paint = true; + var playSound = false; + + if (!this.acheivement) { + distance = this.getActualDistance(distance); + + // Score has gone beyond the initial digit count. + if (distance > this.maxScore && this.maxScoreUnits == + this.config.MAX_DISTANCE_UNITS) { + this.maxScoreUnits++; + this.maxScore = parseInt(this.maxScore + '9'); + } else { + this.distance = 0; + } + + if (distance > 0) { + // Acheivement unlocked + if (distance % this.config.ACHIEVEMENT_DISTANCE == 0) { + // Flash score and play sound. + this.acheivement = true; + this.flashTimer = 0; + playSound = true; + } + + // Create a string representation of the distance with leading 0. + var distanceStr = (this.defaultString + + distance).substr(-this.maxScoreUnits); + this.digits = distanceStr.split(''); + } else { + this.digits = this.defaultString.split(''); + } + } else { + // Control flashing of the score on reaching acheivement. + if (this.flashIterations <= this.config.FLASH_ITERATIONS) { + this.flashTimer += deltaTime; + + if (this.flashTimer < this.config.FLASH_DURATION) { + paint = false; + } else if (this.flashTimer > + this.config.FLASH_DURATION * 2) { + this.flashTimer = 0; + this.flashIterations++; + } + } else { + this.acheivement = false; + this.flashIterations = 0; + this.flashTimer = 0; + } + } + + // Draw the digits if not flashing. + if (paint) { + for (var i = this.digits.length - 1; i >= 0; i--) { + this.draw(i, parseInt(this.digits[i])); + } + } + + this.drawHighScore(); + + return playSound; + }, + + /** + * Draw the high score. + */ + drawHighScore: function() { + this.canvasCtx.save(); + this.canvasCtx.globalAlpha = .8; + for (var i = this.highScore.length - 1; i >= 0; i--) { + this.draw(i, parseInt(this.highScore[i], 10), true); + } + this.canvasCtx.restore(); + }, + + /** + * Set the highscore as a array string. + * Position of char in the sprite: H - 10, I - 11. + * @param {number} distance Distance ran in pixels. + */ + setHighScore: function(distance) { + distance = this.getActualDistance(distance); + var highScoreStr = (this.defaultString + + distance).substr(-this.maxScoreUnits); + + this.highScore = ['10', '11', ''].concat(highScoreStr.split('')); + }, + + /** + * Reset the distance meter back to '00000'. + */ + reset: function() { + this.update(0); + this.acheivement = false; + } +}; + + +//****************************************************************************** + +/** + * Cloud background item. + * Similar to an obstacle object but without collision boxes. + * @param {HTMLCanvasElement} canvas Canvas element. + * @param {Object} spritePos Position of image in sprite. + * @param {number} containerWidth + */ +function Cloud(canvas, spritePos, containerWidth) { + this.canvas = canvas; + this.canvasCtx = this.canvas.getContext('2d'); + this.spritePos = spritePos; + this.containerWidth = containerWidth; + this.xPos = containerWidth; + this.yPos = 0; + this.remove = false; + this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP, + Cloud.config.MAX_CLOUD_GAP); + + this.init(); +}; + + +/** + * Cloud object config. + * @enum {number} + */ +Cloud.config = { + HEIGHT: 14, + MAX_CLOUD_GAP: 400, + MAX_SKY_LEVEL: 30, + MIN_CLOUD_GAP: 100, + MIN_SKY_LEVEL: 71, + WIDTH: 46 +}; + + +Cloud.prototype = { + /** + * Initialise the cloud. Sets the Cloud height. + */ + init: function() { + this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, + Cloud.config.MIN_SKY_LEVEL); + this.draw(); + }, + + /** + * Draw the cloud. + */ + draw: function() { + this.canvasCtx.save(); + var sourceWidth = Cloud.config.WIDTH; + var sourceHeight = Cloud.config.HEIGHT; + + if (IS_HIDPI) { + sourceWidth = sourceWidth * 2; + sourceHeight = sourceHeight * 2; + } + + this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x, + this.spritePos.y, + sourceWidth, sourceHeight, + this.xPos, this.yPos, + Cloud.config.WIDTH, Cloud.config.HEIGHT); + + this.canvasCtx.restore(); + }, + + /** + * Update the cloud position. + * @param {number} speed + */ + update: function(speed) { + if (!this.remove) { + this.xPos -= Math.ceil(speed); + this.draw(); + + // Mark as removeable if no longer in the canvas. + if (!this.isVisible()) { + this.remove = true; + } + } + }, + + /** + * Check if the cloud is visible on the stage. + * @return {boolean} + */ + isVisible: function() { + return this.xPos + Cloud.config.WIDTH > 0; + } +}; + + +//****************************************************************************** + +/** + * Horizon Line. + * Consists of two connecting lines. Randomly assigns a flat / bumpy horizon. + * @param {HTMLCanvasElement} canvas + * @param {Object} spritePos Horizon position in sprite. + * @constructor + */ +function HorizonLine(canvas, spritePos) { + this.spritePos = spritePos; + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.sourceDimensions = {}; + this.dimensions = HorizonLine.dimensions; + this.sourceXPos = [this.spritePos.x, this.spritePos.x + + this.dimensions.WIDTH]; + this.xPos = []; + this.yPos = 0; + this.bumpThreshold = 0.5; + + this.setSourceDimensions(); + this.draw(); +}; + + +/** + * Horizon line dimensions. + * @enum {number} + */ +HorizonLine.dimensions = { + WIDTH: 600, + HEIGHT: 12, + YPOS: 127 +}; + + +HorizonLine.prototype = { + /** + * Set the source dimensions of the horizon line. + */ + setSourceDimensions: function() { + + for (var dimension in HorizonLine.dimensions) { + if (IS_HIDPI) { + if (dimension != 'YPOS') { + this.sourceDimensions[dimension] = + HorizonLine.dimensions[dimension] * 2; + } + } else { + this.sourceDimensions[dimension] = + HorizonLine.dimensions[dimension]; + } + this.dimensions[dimension] = HorizonLine.dimensions[dimension]; + } + + this.xPos = [0, HorizonLine.dimensions.WIDTH]; + this.yPos = HorizonLine.dimensions.YPOS; + }, + + /** + * Return the crop x position of a type. + */ + getRandomType: function() { + return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0; + }, + + /** + * Draw the horizon line. + */ + draw: function() { + 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); + + 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); + }, + + /** + * Update the x position of an indivdual piece of the line. + * @param {number} pos Line position. + * @param {number} increment + */ + updateXPos: function(pos, increment) { + var line1 = pos; + var line2 = pos == 0 ? 1 : 0; + + this.xPos[line1] -= increment; + this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH; + + if (this.xPos[line1] <= -this.dimensions.WIDTH) { + this.xPos[line1] += this.dimensions.WIDTH * 2; + this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH; + this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x; + } + }, + + /** + * Update the horizon line. + * @param {number} deltaTime + * @param {number} speed + */ + update: function(deltaTime, speed) { + var increment = Math.floor(speed * (FPS / 1000) * deltaTime); + + if (this.xPos[0] <= 0) { + this.updateXPos(0, increment); + } else { + this.updateXPos(1, increment); + } + this.draw(); + }, + + /** + * Reset horizon to the starting position. + */ + reset: function() { + this.xPos[0] = 0; + this.xPos[1] = HorizonLine.dimensions.WIDTH; + } +}; + + +//****************************************************************************** + +/** + * Horizon background class. + * @param {HTMLCanvasElement} canvas + * @param {Object} spritePos Sprite positioning. + * @param {Object} dimensions Canvas dimensions. + * @param {number} gapCoefficient + * @constructor + */ +function Horizon(canvas, spritePos, dimensions, gapCoefficient) { + this.canvas = canvas; + this.canvasCtx = this.canvas.getContext('2d'); + this.config = Horizon.config; + this.dimensions = dimensions; + this.gapCoefficient = gapCoefficient; + this.obstacles = []; + this.obstacleHistory = []; + this.horizonOffsets = [0, 0]; + this.cloudFrequency = this.config.CLOUD_FREQUENCY; + this.spritePos = spritePos; + + // Cloud + this.clouds = []; + this.cloudSpeed = this.config.BG_CLOUD_SPEED; + + // Horizon + this.horizonLine = null; + + this.init(); +}; + + +/** + * Horizon config. + * @enum {number} + */ +Horizon.config = { + BG_CLOUD_SPEED: 0.2, + BUMPY_THRESHOLD: .3, + CLOUD_FREQUENCY: .5, + HORIZON_HEIGHT: 16, + MAX_CLOUDS: 6 +}; + + +Horizon.prototype = { + /** + * Initialise the horizon. Just add the line and a cloud. No obstacles. + */ + init: function() { + this.addCloud(); + this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON); + }, + + /** + * @param {number} deltaTime + * @param {number} currentSpeed + * @param {boolean} updateObstacles Used as an override to prevent + * the obstacles from being updated / added. This happens in the + * ease in section. + */ + update: function(deltaTime, currentSpeed, updateObstacles) { + this.runningTime += deltaTime; + this.horizonLine.update(deltaTime, currentSpeed); + this.updateClouds(deltaTime, currentSpeed); + + if (updateObstacles) { + this.updateObstacles(deltaTime, currentSpeed); + } + }, + + /** + * Update the cloud positions. + * @param {number} deltaTime + * @param {number} currentSpeed + */ + updateClouds: function(deltaTime, speed) { + var cloudSpeed = this.cloudSpeed / 1000 * deltaTime * speed; + var numClouds = this.clouds.length; + + if (numClouds) { + for (var i = numClouds - 1; i >= 0; i--) { + this.clouds[i].update(cloudSpeed); + } + + var lastCloud = this.clouds[numClouds - 1]; + + // Check for adding a new cloud. + if (numClouds < this.config.MAX_CLOUDS && + (this.dimensions.WIDTH - lastCloud.xPos) > lastCloud.cloudGap && + this.cloudFrequency > Math.random()) { + this.addCloud(); + } + + // Remove expired clouds. + this.clouds = this.clouds.filter(function(obj) { + return !obj.remove; + }); + } + }, + + /** + * Update the obstacle positions. + * @param {number} deltaTime + * @param {number} currentSpeed + */ + updateObstacles: function(deltaTime, currentSpeed) { + // Obstacles, move to Horizon layer. + var updatedObstacles = this.obstacles.slice(0); + + for (var i = 0; i < this.obstacles.length; i++) { + var obstacle = this.obstacles[i]; + obstacle.update(deltaTime, currentSpeed); + + // Clean up existing obstacles. + if (obstacle.remove) { + updatedObstacles.shift(); + } + } + this.obstacles = updatedObstacles; + + if (this.obstacles.length > 0) { + var lastObstacle = this.obstacles[this.obstacles.length - 1]; + + if (lastObstacle && !lastObstacle.followingObstacleCreated && + lastObstacle.isVisible() && + (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) < + this.dimensions.WIDTH) { + this.addNewObstacle(currentSpeed); + lastObstacle.followingObstacleCreated = true; + } + } else { + // Create new obstacles. + this.addNewObstacle(currentSpeed); + } + }, + + /** + * Add a new obstacle. + * @param {number} currentSpeed + */ + addNewObstacle: function(currentSpeed) { + var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1); + var obstacleType = Obstacle.types[obstacleTypeIndex]; + + // Check for multiples of the same type of obstacle. + // Also check obstacle is available at current speed. + if (this.duplicateObstacleCheck(obstacleType.type) || + currentSpeed < obstacleType.minSpeed) { + this.addNewObstacle(currentSpeed); + } else { + var obstacleSpritePos = this.spritePos[obstacleType.type]; + + this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType, + obstacleSpritePos, this.dimensions, + this.gapCoefficient, currentSpeed)); + + this.obstacleHistory.unshift(obstacleType.type); + + if (this.obstacleHistory.length > 1) { + this.obstacleHistory.splice(Runner.config.MAX_OBSTACLE_DUPLICATION); + } + } + }, + + /** + * Returns whether the previous two obstacles are the same as the next one. + * Maximum duplication is set in config value MAX_OBSTACLE_DUPLICATION. + * @return {boolean} + */ + duplicateObstacleCheck: function(nextObstacleType) { + var duplicateCount = 0; + + for (var i = 0; i < this.obstacleHistory.length; i++) { + duplicateCount = this.obstacleHistory[i] == nextObstacleType ? + duplicateCount + 1 : 0; + } + return duplicateCount >= Runner.config.MAX_OBSTACLE_DUPLICATION; + }, + + /** + * Reset the horizon layer. + * Remove existing obstacles and reposition the horizon line. + */ + reset: function() { + this.obstacles = []; + this.horizonLine.reset(); + }, + + /** + * Update the canvas width and scaling. + * @param {number} width Canvas width. + * @param {number} height Canvas height. + */ + resize: function(width, height) { + this.canvas.width = width; + this.canvas.height = height; + }, + + /** + * Add a new cloud to the horizon. + */ + addCloud: function() { + this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD, + this.dimensions.WIDTH)); + } +}; +})(); + +//start the game +new Runner('.interstitial-wrapper'); + diff --git a/google dino/readme.md b/google dino/readme.md new file mode 100644 index 00000000..7cb85450 --- /dev/null +++ b/google dino/readme.md @@ -0,0 +1,6 @@ +The Chrome Dinosaur Game from when your wifi goes out, now unblocked and embedable. + +Credits: +http://wayou.github.io/t-rex-runner/ +Google - They made the game! + diff --git a/index.html b/index.html index 5b321d3d..cc115a41 100644 --- a/index.html +++ b/index.html @@ -36,6 +36,8 @@

Secret Stuff

  │
  ├ Google Snake Game
  │
+   ├ Google Dino Game
+   │
  ├ Cookie Clicker
  │
  ├ Eagler Craft (Minecraft)