From f18f7cee2268de6189f3129b7e68faa9df43862c Mon Sep 17 00:00:00 2001 From: Gabriele Cirulli Date: Sun, 9 Mar 2014 23:03:13 +0100 Subject: [PATCH] add score and endgame --- index.html | 5 +- js/game_manager.js | 52 ++++++++++++++- js/html_actuator.js | 20 +++++- style/main.css | 152 ++++++++++++++++++++++++++++++++------------ style/main.scss | 96 +++++++++++++++++++++++++--- 5 files changed, 271 insertions(+), 54 deletions(-) diff --git a/index.html b/index.html index e741dd4..d2009ff 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,10 @@
-

2048

+
+

2048

+
0
+

Join the numbers and get to the 2048 tile!

diff --git a/js/game_manager.js b/js/game_manager.js index ad30fc2..c2695e8 100644 --- a/js/game_manager.js +++ b/js/game_manager.js @@ -6,6 +6,9 @@ function GameManager(size, InputManager, Actuator) { this.startTiles = 2; this.grid = new Grid(this.size); + this.score = 0; + this.over = false; + this.inputManager.on("move", this.move.bind(this)); this.setup(); @@ -38,7 +41,10 @@ GameManager.prototype.addRandomTile = function () { // Sends the updated grid to the actuator GameManager.prototype.actuate = function () { - this.actuator.actuate(this.grid); + this.actuator.actuate(this.grid, { + score: this.score, + over: this.over + }); }; // Save all tile positions and remove merger info @@ -63,6 +69,8 @@ GameManager.prototype.move = function (direction) { // 0: up, 1: right, 2:down, 3: left var self = this; + if (this.over) return; // Don't do anything if the game's over + var cell, tile; var vector = this.getVector(direction); @@ -92,6 +100,9 @@ GameManager.prototype.move = function (direction) { // Converge the two tiles' positions tile.updatePosition(positions.next); + + // Update the score + self.score += merged.value; } else { self.moveTile(tile, positions.farthest); } @@ -103,6 +114,11 @@ GameManager.prototype.move = function (direction) { if (moved) { this.addRandomTile(); + + if (!this.movesAvailable()) { + this.over = true; // Game over! + } + this.actuate(); } }; @@ -151,3 +167,37 @@ GameManager.prototype.findFarthestPosition = function (cell, vector) { next: cell // Used to check if a merge is required }; }; + +GameManager.prototype.movesAvailable = function () { + return this.grid.cellsAvailable() || this.tileMatchesAvailable(); +}; + +// Check for available matches between tiles (more expensive check) +GameManager.prototype.tileMatchesAvailable = function () { + var self = this; + + var tile; + + for (var x = 0; x < this.size; x++) { + for (var y = 0; y < this.size; y++) { + tile = this.grid.cellContent({ x: x, y: y }); + + if (tile) { + for (var direction = 0; direction < 4; direction++) { + var vector = self.getVector(direction); + var cell = { x: x + vector.x, y: y + vector.y }; + + var other = self.grid.cellContent(cell); + if (other) { + } + + if (other && other.value === tile.value) { + return true; // These two tiles can be merged + } + } + } + } + } + + return false; +}; diff --git a/js/html_actuator.js b/js/html_actuator.js index 8bdb384..18e530a 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -1,8 +1,10 @@ function HTMLActuator() { - this.tileContainer = document.getElementsByClassName("tile-container")[0]; + this.tileContainer = document.getElementsByClassName("tile-container")[0]; + this.gameContainer = document.getElementsByClassName("game-container")[0]; + this.scoreContainer = document.getElementsByClassName("score-container")[0]; } -HTMLActuator.prototype.actuate = function (grid) { +HTMLActuator.prototype.actuate = function (grid, metadata) { var self = this; window.requestAnimationFrame(function () { @@ -15,6 +17,12 @@ HTMLActuator.prototype.actuate = function (grid) { } }); }); + + self.updateScore(metadata.score); + + if (metadata.over) { + self.gameOver(); + } }); }; @@ -59,3 +67,11 @@ HTMLActuator.prototype.positionClass = function (position) { position = this.normalizePosition(position); return "tile-position-" + position.x + "-" + position.y; }; + +HTMLActuator.prototype.updateScore = function (score) { + this.scoreContainer.textContent = score; +}; + +HTMLActuator.prototype.gameOver = function () { + this.gameContainer.classList.add("game-over"); +}; diff --git a/style/main.css b/style/main.css index a44c111..b8626fa 100644 --- a/style/main.css +++ b/style/main.css @@ -10,10 +10,41 @@ html, body { body { margin: 80px 0; } -h1 { +.heading:after { + content: ""; + display: block; + clear: both; } + +h1.title { font-size: 80px; font-weight: bold; - margin: 0; } + margin: 0; + display: block; + float: left; } + +.score-container { + position: relative; + float: right; + background: #bbada0; + padding: 15px 30px; + font-size: 25px; + height: 25px; + line-height: 47px; + font-weight: bold; + border-radius: 3px; + color: white; + margin-top: 8px; } + .score-container:after { + position: absolute; + width: 100%; + top: 10px; + left: 0; + content: "Score"; + text-transform: uppercase; + font-size: 13px; + line-height: 13px; + text-align: center; + color: #eee4da; } p { margin-top: 0; @@ -38,6 +69,27 @@ hr { width: 500px; margin: 0 auto; } +@-webkit-keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + +@-moz-keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + +@keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + .game-container { margin-top: 40px; position: relative; @@ -51,6 +103,25 @@ hr { width: 500px; height: 500px; box-sizing: border-box; } + .game-container.game-over:after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + content: "Game over!"; + display: block; + background: rgba(238, 228, 218, 0.5); + text-align: center; + height: 500px; + line-height: 500px; + z-index: 100; + font-size: 60px; + font-weight: bold; + -webkit-animation: fade-in 800ms ease 1200ms; + -moz-animation: fade-in 800ms ease 1200ms; + -webkit-animation-fill-mode: both; + -moz-animation-fill-mode: both; } .grid-container { position: absolute; @@ -160,78 +231,74 @@ hr { top: 364px; } .tile.tile-2 { background: #eee4da; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0); } + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } .tile.tile-4 { background: #ede0c8; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0); } + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } .tile.tile-8 { color: #f9f6f2; - background: #f2b179; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0); } + background: #f2b179; } .tile.tile-16 { color: #f9f6f2; - background: #f59563; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0); } + background: #f59563; } .tile.tile-32 { color: #f9f6f2; - background: #f67c5f; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.07937); } + background: #f67c5f; } .tile.tile-64 { color: #f9f6f2; - background: #f65e3b; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.15873); } + background: #f65e3b; } .tile.tile-128 { color: #f9f6f2; background: #edcf72; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381); + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286); font-size: 45px; } .tile.tile-256 { color: #f9f6f2; background: #edcc61; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746); + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048); font-size: 45px; } .tile.tile-512 { color: #f9f6f2; background: #edc850; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683); + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381); font-size: 45px; } .tile.tile-1024 { color: #f9f6f2; background: #edc53f; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619); + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571); font-size: 35px; } .tile.tile-2048 { color: #f9f6f2; background: #edc22e; - box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556); + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333); font-size: 35px; } @-webkit-keyframes appear { 0% { - -webkit-transform: scale(1.5); - opacity: 0; } + opacity: 0; + -webkit-transform: scale(0); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + opacity: 1; + -webkit-transform: scale(1); } } @-moz-keyframes appear { 0% { - -webkit-transform: scale(1.5); - opacity: 0; } + opacity: 0; + -webkit-transform: scale(0); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + opacity: 1; + -webkit-transform: scale(1); } } @keyframes appear { 0% { - -webkit-transform: scale(1.5); - opacity: 0; } + opacity: 0; + -webkit-transform: scale(0); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + opacity: 1; + -webkit-transform: scale(1); } } .tile-new { -webkit-animation: appear 200ms ease 100ms; @@ -241,30 +308,33 @@ hr { @-webkit-keyframes pop { 0% { - -webkit-transform: scale(0.5); - opacity: 0; } + -webkit-transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + -webkit-transform: scale(1); } } @-moz-keyframes pop { 0% { - -webkit-transform: scale(0.5); - opacity: 0; } + -webkit-transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + -webkit-transform: scale(1); } } @keyframes pop { 0% { - -webkit-transform: scale(0.5); - opacity: 0; } + -webkit-transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); } 100% { - -webkit-transform: scale(1); - opacity: 1; } } + -webkit-transform: scale(1); } } .tile-merged { z-index: 20; diff --git a/style/main.scss b/style/main.scss index b9b44c4..8967630 100644 --- a/style/main.scss +++ b/style/main.scss @@ -14,6 +14,8 @@ $tile-color: #eee4da; $tile-gold-color: #edc22e; $tile-gold-glow-color: lighten($tile-gold-color, 15%); +$game-container-background: #bbada0; + $transition-speed: 100ms; html, body { @@ -30,10 +32,47 @@ body { margin: 80px 0; } -h1 { +.heading:after { + content: ""; + display: block; + clear: both; +} + +h1.title { font-size: 80px; font-weight: bold; margin: 0; + display: block; + float: left; +} + +.score-container { + $height: 25px; + + position: relative; + float: right; + background: $game-container-background; + padding: 15px 30px; + font-size: $height; + height: $height; + line-height: $height + 22px; + font-weight: bold; + border-radius: 3px; + color: white; + margin-top: 8px; + + &:after { + position: absolute; + width: 100%; + top: 10px; + left: 0; + content: "Score"; + text-transform: uppercase; + font-size: 13px; + line-height: 13px; + text-align: center; + color: $tile-color; + } } p { @@ -66,6 +105,16 @@ hr { margin: 0 auto; } +@include keyframes(fade-in) { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + .game-container { margin-top: 40px; position: relative; @@ -76,11 +125,31 @@ hr { -webkit-user-select: none; -moz-user-select: none; - background: #bbada0; + background: $game-container-background; border-radius: $tile-border-radius * 2; width: $field-width; height: $field-width; box-sizing: border-box; + + &.game-over:after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + content: "Game over!"; + display: block; + background: rgba($tile-color, .5); + text-align: center; + height: $field-width; + line-height: $field-width; + z-index: 100; + font-size: 60px; + font-weight: bold; + + @include animation(fade-in 800ms ease $transition-speed * 12); + @include animation-fill-mode(both); + } } .grid-container { @@ -193,8 +262,11 @@ hr { // Add glow $glow-opacity: max($exponent - 4, 0) / ($limit - 4); - box-shadow: 0 0 30px 10px rgba($tile-gold-glow-color, $glow-opacity / 1.8); //, - // inset 0 0 0 1px rgba(white, $glow-opacity / 3); + + @if not $special-background { + box-shadow: 0 0 30px 10px rgba($tile-gold-glow-color, $glow-opacity / 1.8), + inset 0 0 0 1px rgba(white, $glow-opacity / 3); + } // Adjust font size for bigger numbers @if $power >= 100 and $power < 1000 { @@ -210,13 +282,15 @@ hr { @include keyframes(appear) { 0% { - -webkit-transform: scale(1.5); + // -webkit-transform: scale(1.5); opacity: 0; + -webkit-transform: scale(0); } 100% { - -webkit-transform: scale(1); + // -webkit-transform: scale(1); opacity: 1; + -webkit-transform: scale(1); } } @@ -227,13 +301,17 @@ hr { @include keyframes(pop) { 0% { - -webkit-transform: scale(.5); - opacity: 0; + -webkit-transform: scale(0); + // opacity: 0; + } + + 50% { + -webkit-transform: scale(1.2); } 100% { -webkit-transform: scale(1); - opacity: 1; + // opacity: 1; } } -- GitLab