Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3848)

Unified Diff: chrome/renderer/resources/offline.js

Issue 1051433002: Improvements to the offline intersitial and easter egg (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/renderer/resources/offline.js
diff --git a/chrome/renderer/resources/offline.js b/chrome/renderer/resources/offline.js
index 5fabe448cf567c1a09b82eef58a5c1933fffa751..5796f5683246893ae1ccc6f1271a3ef5167b2c49 100644
--- a/chrome/renderer/resources/offline.js
+++ b/chrome/renderer/resources/offline.js
@@ -19,6 +19,7 @@ function Runner(outerContainerId, opt_config) {
this.outerContainerEl = document.querySelector(outerContainerId);
this.containerEl = null;
+ this.snackbarEl = null;
this.detailsButton = this.outerContainerEl.querySelector('#details-button');
this.config = opt_config || Runner.config;
@@ -87,8 +88,8 @@ var FPS = 60;
var IS_HIDPI = window.devicePixelRatio > 1;
/** @const */
-var IS_IOS =
- window.navigator.userAgent.indexOf('UIWebViewForStaticFileContent') > -1;
+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;
@@ -112,7 +113,8 @@ Runner.config = {
INITIAL_JUMP_VELOCITY: 12,
MAX_CLOUDS: 6,
MAX_OBSTACLE_LENGTH: 3,
- MAX_SPEED: 12,
+ MAX_OBSTACLE_DUPLICATION: 2,
+ MAX_SPEED: 13,
MIN_JUMP_HEIGHT: 35,
MOBILE_SPEED_COEFFICIENT: 1.2,
RESOURCE_TEMPLATE_ID: 'audio-resources',
@@ -147,28 +149,30 @@ Runner.classes = {
/**
- * Image source urls.
- * @enum {array<object>}
+ * Sprite definition layout of the spritesheet.
+ * @enum {object}
*/
-Runner.imageSources = {
- LDPI: [
- {name: 'CACTUS_LARGE', id: '1x-obstacle-large'},
- {name: 'CACTUS_SMALL', id: '1x-obstacle-small'},
- {name: 'CLOUD', id: '1x-cloud'},
- {name: 'HORIZON', id: '1x-horizon'},
- {name: 'RESTART', id: '1x-restart'},
- {name: 'TEXT_SPRITE', id: '1x-text'},
- {name: 'TREX', id: '1x-trex'}
- ],
- HDPI: [
- {name: 'CACTUS_LARGE', id: '2x-obstacle-large'},
- {name: 'CACTUS_SMALL', id: '2x-obstacle-small'},
- {name: 'CLOUD', id: '2x-cloud'},
- {name: 'HORIZON', id: '2x-horizon'},
- {name: 'RESTART', id: '2x-restart'},
- {name: 'TEXT_SPRITE', id: '2x-text'},
- {name: 'TREX', id: '2x-trex'}
- ]
+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}
+ }
};
@@ -268,18 +272,18 @@ Runner.prototype = {
},
/**
- * Load and cache the image assets from the page.
+ * Cache the approprate image sprite from the page and get the sprite sheet
arv (Not doing code reviews) 2015/03/31 19:10:56 typo
edwardjung 2015/04/01 10:40:46 Done.
+ * definition.
*/
loadImages: function() {
- var imageSources = IS_HIDPI ? Runner.imageSources.HDPI :
- Runner.imageSources.LDPI;
-
- var numImages = imageSources.length;
-
- for (var i = numImages - 1; i >= 0; i--) {
- var imgSource = imageSources[i];
- this.images[imgSource.name] = document.getElementById(imgSource.id);
+ 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();
},
@@ -289,6 +293,7 @@ Runner.prototype = {
loadSounds: function() {
if (!IS_IOS) {
this.audioContext = new AudioContext();
+
var resourceTemplate =
document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content;
@@ -347,15 +352,15 @@ Runner.prototype = {
Runner.updateCanvasScaling(this.canvas);
// Horizon contains clouds, obstacles and the ground.
- this.horizon = new Horizon(this.canvas, this.images, this.dimensions,
+ this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions,
this.config.GAP_COEFFICIENT);
// Distance meter
this.distanceMeter = new DistanceMeter(this.canvas,
- this.images.TEXT_SPRITE, this.dimensions.WIDTH);
+ this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH);
// Draw t-rex
- this.tRex = new Trex(this.canvas, this.images.TREX);
+ this.tRex = new Trex(this.canvas, this.spriteDef.TREX);
this.outerContainerEl.appendChild(this.containerEl);
@@ -504,7 +509,7 @@ Runner.prototype = {
this.clearCanvas();
if (this.tRex.jumping) {
- this.tRex.updateJump(deltaTime, this.config);
+ this.tRex.updateJump(deltaTime);
}
this.runningTime += deltaTime;
@@ -537,11 +542,6 @@ Runner.prototype = {
this.gameOver();
}
- if (this.distanceMeter.getActualDistance(this.distanceRan) >
- this.distanceMeter.maxScore) {
- this.distanceRan = 0;
- }
-
var playAcheivementSound = this.distanceMeter.update(deltaTime,
Math.ceil(this.distanceRan));
@@ -618,17 +618,23 @@ Runner.prototype = {
* @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[String(e.keyCode)] ||
+ if (!this.crashed && (Runner.keycodes.JUMP[e.keyCode] ||
e.type == Runner.events.TOUCHSTART)) {
if (!this.activated) {
this.loadSounds();
this.activated = true;
+ errorPageController.trackEasterEgg();
}
- if (!this.tRex.jumping) {
+ if (!this.tRex.jumping && !this.tRex.ducking) {
this.playSound(this.soundFx.BUTTON_PRESS);
- this.tRex.startJump();
+ this.tRex.startJump(this.currentSpeed);
}
}
@@ -638,10 +644,15 @@ Runner.prototype = {
}
}
- // Speed drop, activated only when jump key is not pressed.
- if (Runner.keycodes.DUCK[e.keyCode] && this.tRex.jumping) {
+ if (this.activated && !this.crashed && Runner.keycodes.DUCK[e.keyCode]) {
e.preventDefault();
- this.tRex.setSpeedDrop();
+ 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);
+ }
}
},
@@ -660,22 +671,35 @@ Runner.prototype = {
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] ||
- (e.type == Runner.events.MOUSEUP && e.target == this.canvas) ||
- (deltaTime >= this.config.GAMEOVER_CLEAR_TIME &&
- Runner.keycodes.JUMP[keyCode])) {
+ 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.which && e.which < 2 && e.type == Runner.events.MOUSEUP &&
arv (Not doing code reviews) 2015/03/31 19:10:56 Use e.button instead of of e.which.
edwardjung 2015/04/01 10:40:46 Done.
+ e.target == this.canvas;
+ },
+
+ /**
* RequestAnimationFrame wrapper.
*/
raq: function() {
@@ -709,7 +733,7 @@ Runner.prototype = {
// Game over panel.
if (!this.gameOverPanel) {
this.gameOverPanel = new GameOverPanel(this.canvas,
- this.images.TEXT_SPRITE, this.images.RESTART,
+ this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART,
this.dimensions);
} else {
this.gameOverPanel.draw();
@@ -812,7 +836,6 @@ Runner.updateCanvasScaling = function(canvas, opt_width, opt_height) {
// 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;
@@ -910,17 +933,17 @@ function getTimeStamp() {
/**
* Game over panel.
* @param {!HTMLCanvasElement} canvas
- * @param {!HTMLImage} textSprite
- * @param {!HTMLImage} restartImg
+ * @param {object} textImgPos
arv (Not doing code reviews) 2015/03/31 19:10:56 s/object/Object/
+ * @param {object} restartImgPos
* @param {!Object} dimensions Canvas dimensions.
* @constructor
*/
-function GameOverPanel(canvas, textSprite, restartImg, dimensions) {
+function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) {
this.canvas = canvas;
this.canvasCtx = canvas.getContext('2d');
this.canvasDimensions = dimensions;
- this.textSprite = textSprite;
- this.restartImg = restartImg;
+ this.textImgPos = textImgPos;
+ this.restartImgPos = restartImgPos;
this.draw();
};
@@ -985,13 +1008,17 @@ GameOverPanel.prototype = {
restartSourceHeight *= 2;
}
+ textSourceX += this.textImgPos.x;
+ textSourceY += this.textImgPos.y;
+
// Game over text from sprite.
- this.canvasCtx.drawImage(this.textSprite,
+ this.canvasCtx.drawImage(Runner.imageSprite,
textSourceX, textSourceY, textSourceWidth, textSourceHeight,
textTargetX, textTargetY, textTargetWidth, textTargetHeight);
// Restart button.
- this.canvasCtx.drawImage(this.restartImg, 0, 0,
+ this.canvasCtx.drawImage(Runner.imageSprite,
+ this.restartImgPos.x, this.restartImgPos.y,
restartSourceWidth, restartSourceHeight,
restartTargetX, restartTargetY, dimensions.RESTART_WIDTH,
dimensions.RESTART_HEIGHT);
@@ -1034,7 +1061,8 @@ function checkForCollision(obstacle, tRex, opt_canvasCtx) {
// Simple outer bounds check.
if (boxCompare(tRexBox, obstacleBox)) {
var collisionBoxes = obstacle.collisionBoxes;
- var tRexCollisionBoxes = Trex.collisionBoxes;
+ var tRexCollisionBoxes = tRex.ducking ?
+ Trex.collisionBoxes.DUCKING : Trex.collisionBoxes.RUNNING;
// Detailed axis aligned box check.
for (var t = 0; t < tRexCollisionBoxes.length; t++) {
@@ -1082,12 +1110,11 @@ function createAdjustedCollisionBox(box, adjustment) {
function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) {
canvasCtx.save();
canvasCtx.strokeStyle = '#f00';
- canvasCtx.strokeRect(tRexBox.x, tRexBox.y,
- tRexBox.width, tRexBox.height);
+ canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height);
canvasCtx.strokeStyle = '#0f0';
canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y,
- obstacleBox.width, obstacleBox.height);
+ obstacleBox.width, obstacleBox.height);
canvasCtx.restore();
};
@@ -1141,26 +1168,31 @@ function CollisionBox(x, y, w, h) {
* Obstacle.
* @param {HTMLCanvasCtx} canvasCtx
* @param {Obstacle.type} type
- * @param {image} obstacleImg Image sprite.
+ * @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, obstacleImg, dimensions,
+function Obstacle(canvasCtx, type, spriteImgPos, dimensions,
gapCoefficient, speed) {
this.canvasCtx = canvasCtx;
- this.image = obstacleImg;
+ 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 = this.typeConfig.yPos;
+ 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);
};
@@ -1194,6 +1226,15 @@ Obstacle.prototype = {
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,
@@ -1210,6 +1251,12 @@ Obstacle.prototype = {
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);
},
@@ -1225,10 +1272,17 @@ Obstacle.prototype = {
sourceHeight = sourceHeight * 2;
}
- // Sprite
- var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1));
- this.canvasCtx.drawImage(this.image,
- sourceX, 0,
+ // 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);
@@ -1241,7 +1295,21 @@ Obstacle.prototype = {
*/
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()) {
@@ -1292,16 +1360,18 @@ Obstacle.prototype = {
* 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',
- className: ' cactus cactus-small ',
width: 17,
height: 35,
yPos: 105,
- multipleSpeed: 3,
+ multipleSpeed: 4,
minGap: 120,
+ minSpeed: 0,
collisionBoxes: [
new CollisionBox(0, 7, 5, 27),
new CollisionBox(4, 0, 6, 34),
@@ -1310,17 +1380,37 @@ Obstacle.types = [
},
{
type: 'CACTUS_LARGE',
- className: ' cactus cactus-large ',
width: 25,
height: 50,
yPos: 90,
- multipleSpeed: 6,
+ 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',
arv (Not doing code reviews) 2015/03/31 19:10:56 New game elements. Exciting!
edwardjung 2015/04/01 10:40:47 Acknowledged.
+ 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
}
];
@@ -1329,13 +1419,13 @@ Obstacle.types = [
/**
* T-rex game character.
* @param {HTMLCanvas} canvas
- * @param {HTMLImage} image Character image.
+ * @param {Object} spritePos Positioning within image sprite.
* @constructor
*/
-function Trex(canvas, image) {
+function Trex(canvas, spritePos) {
this.canvas = canvas;
this.canvasCtx = canvas.getContext('2d');
- this.image = image;
+ this.spritePos = spritePos;
this.xPos = 0;
this.yPos = 0;
// Position when on the ground.
@@ -1351,6 +1441,7 @@ function Trex(canvas, image) {
this.status = Trex.status.WAITING;
this.jumping = false;
+ this.ducking = false;
this.jumpVelocity = 0;
this.reachedMinHeight = false;
this.speedDrop = false;
@@ -1369,6 +1460,7 @@ Trex.config = {
DROP_VELOCITY: -5,
GRAVITY: 0.6,
HEIGHT: 47,
+ HEIGHT_DUCK: 25,
INIITAL_JUMP_VELOCITY: -10,
INTRO_DURATION: 1500,
MAX_JUMP_HEIGHT: 30,
@@ -1376,7 +1468,8 @@ Trex.config = {
SPEED_DROP_COEFFICIENT: 3,
SPRITE_WIDTH: 262,
START_X_POS: 50,
- WIDTH: 44
+ WIDTH: 44,
+ WIDTH_DUCK: 59
};
@@ -1384,14 +1477,19 @@ Trex.config = {
* Used in collision detection.
* @type {Array<CollisionBox>}
*/
-Trex.collisionBoxes = [
- new CollisionBox(1, -1, 30, 26),
- new CollisionBox(32, 0, 8, 16),
- new CollisionBox(10, 35, 14, 8),
- new CollisionBox(1, 24, 29, 5),
- new CollisionBox(5, 30, 21, 4),
- new CollisionBox(9, 34, 15, 4)
-];
+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)
+ ]
+};
/**
@@ -1400,6 +1498,7 @@ Trex.collisionBoxes = [
*/
Trex.status = {
CRASHED: 'CRASHED',
+ DUCKING: 'DUCKING',
JUMPING: 'JUMPING',
RUNNING: 'RUNNING',
WAITING: 'WAITING'
@@ -1432,6 +1531,10 @@ Trex.animFrames = {
JUMPING: {
frames: [0],
msPerFrame: 1000 / 60
+ },
+ DUCKING: {
+ frames: [262, 321],
+ msPerFrame: 1000 / 8
}
};
@@ -1500,6 +1603,12 @@ Trex.prototype = {
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);
+ }
},
/**
@@ -1510,7 +1619,8 @@ Trex.prototype = {
draw: function(x, y) {
var sourceX = x;
var sourceY = y;
- var sourceWidth = this.config.WIDTH;
+ var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
+ this.config.WIDTH_DUCK : this.config.WIDTH;
var sourceHeight = this.config.HEIGHT;
if (IS_HIDPI) {
@@ -1520,10 +1630,27 @@ Trex.prototype = {
sourceHeight *= 2;
}
- this.canvasCtx.drawImage(this.image, sourceX, sourceY,
- sourceWidth, sourceHeight,
- this.xPos, this.yPos,
- this.config.WIDTH, this.config.HEIGHT);
+ // 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);
+ }
},
/**
@@ -1553,11 +1680,13 @@ Trex.prototype = {
/**
* Initialise a jump.
+ * @param {number} speed
*/
- startJump: function() {
+ startJump: function(speed) {
if (!this.jumping) {
this.update(0, Trex.status.JUMPING);
- this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY;
+ // 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;
@@ -1577,8 +1706,9 @@ Trex.prototype = {
/**
* Update frame for a jump.
* @param {number} deltaTime
+ * @param {number} speed
*/
- updateJump: function(deltaTime) {
+ updateJump: function(deltaTime, speed) {
var msPerFrame = Trex.animFrames[this.status].msPerFrame;
var framesElapsed = deltaTime / msPerFrame;
@@ -1620,12 +1750,26 @@ Trex.prototype = {
},
/**
+ * @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;
@@ -1639,14 +1783,15 @@ Trex.prototype = {
/**
* Handles displaying the distance meter.
* @param {!HTMLCanvasElement} canvas
- * @param {!HTMLImage} spriteSheet Image sprite.
+ * @param {Object} spritePos Image position in sprite.
* @param {number} canvasWidth
* @constructor
*/
-function DistanceMeter(canvas, spriteSheet, canvasWidth) {
+function DistanceMeter(canvas, spritePos, canvasWidth) {
this.canvas = canvas;
this.canvasCtx = canvas.getContext('2d');
- this.image = spriteSheet;
+ this.image = Runner.imageSprite;
+ this.spritePos = spritePos;
this.x = 0;
this.y = 5;
@@ -1662,6 +1807,7 @@ function DistanceMeter(canvas, spriteSheet, canvasWidth) {
this.flashIterations = 0;
this.config = DistanceMeter.config;
+ this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS;
this.init(canvasWidth);
};
@@ -1679,7 +1825,7 @@ DistanceMeter.dimensions = {
/**
* Y positioning of the digits in the sprite sheet.
* X position is always 0.
- * @type {array<number>}
+ * @type {Array<number>}
*/
DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120];
@@ -1715,8 +1861,8 @@ DistanceMeter.prototype = {
var maxDistanceStr = '';
this.calcXPos(width);
- this.maxScore = this.config.MAX_DISTANCE_UNITS;
- for (var i = 0; i < this.config.MAX_DISTANCE_UNITS; i++) {
+ this.maxScore = this.maxScoreUnits;
+ for (var i = 0; i < this.maxScoreUnits; i++) {
this.draw(i, 0);
this.defaultString += '0';
maxDistanceStr += '9';
@@ -1731,7 +1877,7 @@ DistanceMeter.prototype = {
*/
calcXPos: function(canvasWidth) {
this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH *
- (this.config.MAX_DISTANCE_UNITS + 1));
+ (this.maxScoreUnits + 1));
},
/**
@@ -1744,6 +1890,7 @@ DistanceMeter.prototype = {
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;
@@ -1757,18 +1904,21 @@ DistanceMeter.prototype = {
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.config.MAX_DISTANCE_UNITS * 2) *
+ 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, 0,
+ this.canvasCtx.drawImage(this.image, sourceX, sourceY,
sourceWidth, sourceHeight,
targetX, targetY,
targetWidth, targetHeight
@@ -1783,14 +1933,13 @@ DistanceMeter.prototype = {
* @return {number} The 'real' distance ran.
*/
getActualDistance: function(distance) {
- return distance ?
- Math.round(distance * this.config.COEFFICIENT) : 0;
+ return distance ? Math.round(distance * this.config.COEFFICIENT) : 0;
},
/**
* Update the distance meter.
- * @param {number} deltaTime
* @param {number} distance
+ * @param {number} deltaTime
* @return {boolean} Whether the acheivement sound fx should be played.
*/
update: function(deltaTime, distance) {
@@ -1800,6 +1949,15 @@ DistanceMeter.prototype = {
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) {
@@ -1811,7 +1969,7 @@ DistanceMeter.prototype = {
// Create a string representation of the distance with leading 0.
var distanceStr = (this.defaultString +
- distance).substr(-this.config.MAX_DISTANCE_UNITS);
+ distance).substr(-this.maxScoreUnits);
this.digits = distanceStr.split('');
} else {
this.digits = this.defaultString.split('');
@@ -1867,7 +2025,7 @@ DistanceMeter.prototype = {
setHighScore: function(distance) {
distance = this.getActualDistance(distance);
var highScoreStr = (this.defaultString +
- distance).substr(-this.config.MAX_DISTANCE_UNITS);
+ distance).substr(-this.maxScoreUnits);
this.highScore = ['10', '11', ''].concat(highScoreStr.split(''));
},
@@ -1888,13 +2046,13 @@ DistanceMeter.prototype = {
* Cloud background item.
* Similar to an obstacle object but without collision boxes.
* @param {HTMLCanvasElement} canvas Canvas element.
- * @param {Image} cloudImg
+ * @param {Object} spritePos Position of image in sprite.
* @param {number} containerWidth
*/
-function Cloud(canvas, cloudImg, containerWidth) {
+function Cloud(canvas, spritePos, containerWidth) {
this.canvas = canvas;
this.canvasCtx = this.canvas.getContext('2d');
- this.image = cloudImg;
+ this.spritePos = spritePos;
this.containerWidth = containerWidth;
this.xPos = containerWidth;
this.yPos = 0;
@@ -1943,7 +2101,8 @@ Cloud.prototype = {
sourceHeight = sourceHeight * 2;
}
- this.canvasCtx.drawImage(this.image, 0, 0,
+ this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x,
+ this.spritePos.y,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
Cloud.config.WIDTH, Cloud.config.HEIGHT);
@@ -1983,16 +2142,17 @@ Cloud.prototype = {
* Horizon Line.
* Consists of two connecting lines. Randomly assigns a flat / bumpy horizon.
* @param {HTMLCanvasElement} canvas
- * @param {HTMLImage} bgImg Horizon line sprite.
+ * @param {Object} spritePos Horizon position in sprite.
* @constructor
*/
-function HorizonLine(canvas, bgImg) {
- this.image = bgImg;
+function HorizonLine(canvas, spritePos) {
+ this.spritePos = spritePos;
this.canvas = canvas;
this.canvasCtx = canvas.getContext('2d');
this.sourceDimensions = {};
this.dimensions = HorizonLine.dimensions;
- this.sourceXPos = [0, this.dimensions.WIDTH];
+ this.sourceXPos = [this.spritePos.x, this.spritePos.x +
+ this.dimensions.WIDTH];
this.xPos = [];
this.yPos = 0;
this.bumpThreshold = 0.5;
@@ -2047,12 +2207,14 @@ HorizonLine.prototype = {
* Draw the horizon line.
*/
draw: function() {
- this.canvasCtx.drawImage(this.image, this.sourceXPos[0], 0,
+ 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(this.image, this.sourceXPos[1], 0,
+ 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);
@@ -2073,7 +2235,7 @@ HorizonLine.prototype = {
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.sourceXPos[line1] = this.getRandomType() + this.spritePos.x;
}
},
@@ -2108,36 +2270,30 @@ HorizonLine.prototype = {
/**
* Horizon background class.
* @param {HTMLCanvasElement} canvas
- * @param {Array<HTMLImageElement>} images
+ * @param {Object} spritePos Sprite positioning.
* @param {object} dimensions Canvas dimensions.
arv (Not doing code reviews) 2015/03/31 19:10:56 typo here too
edwardjung 2015/04/01 10:40:46 Done.
* @param {number} gapCoefficient
* @constructor
*/
-function Horizon(canvas, images, dimensions, gapCoefficient) {
+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.cloudImg = images.CLOUD;
this.cloudSpeed = this.config.BG_CLOUD_SPEED;
// Horizon
- this.horizonImg = images.HORIZON;
this.horizonLine = null;
- // Obstacles
- this.obstacleImgs = {
- CACTUS_SMALL: images.CACTUS_SMALL,
- CACTUS_LARGE: images.CACTUS_LARGE
- };
-
this.init();
};
@@ -2161,7 +2317,7 @@ Horizon.prototype = {
*/
init: function() {
this.addCloud();
- this.horizonLine = new HorizonLine(this.canvas, this.horizonImg);
+ this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON);
},
/**
@@ -2252,13 +2408,42 @@ Horizon.prototype = {
* @param {number} currentSpeed
*/
addNewObstacle: function(currentSpeed) {
- var obstacleTypeIndex =
- getRandomNum(0, Obstacle.types.length - 1);
+ var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1);
var obstacleType = Obstacle.types[obstacleTypeIndex];
- var obstacleImg = this.obstacleImgs[obstacleType.type];
- this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType,
- obstacleImg, this.dimensions, this.gapCoefficient, currentSpeed));
+ // 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;
},
/**
@@ -2284,7 +2469,7 @@ Horizon.prototype = {
* Add a new cloud to the horizon.
*/
addCloud: function() {
- this.clouds.push(new Cloud(this.canvas, this.cloudImg,
+ this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD,
this.dimensions.WIDTH));
}
};

Powered by Google App Engine
This is Rietveld 408576698