“d7ae2bdef307b995ec9b8427c4d668f819645dcd”上不存在“projects/tcxdgit”
提交 5afb368c 编写于 作者: G Gabriele Cirulli

merge new game button

...@@ -13,7 +13,9 @@ Many thanks to [rayhaanj](https://github.com/rayhaanj), [Mechazawa](https://gith ...@@ -13,7 +13,9 @@ Many thanks to [rayhaanj](https://github.com/rayhaanj), [Mechazawa](https://gith
### Screenshot ### Screenshot
[![Screenshot](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)](http://pictures.gabrielecirulli.com/2048-20140309-234100.png) <p align="center">
<img src="http://pictures.gabrielecirulli.com/2048-20140309-234100.png" alt="Screenshot"/>
</p>
That screenshot is fake, by the way. I never reached 2048 :smile: That screenshot is fake, by the way. I never reached 2048 :smile:
......
...@@ -3,7 +3,7 @@ CACHE MANIFEST ...@@ -3,7 +3,7 @@ CACHE MANIFEST
# Adds the ability to play the game online. # Adds the ability to play the game online.
# The following comment needs to be updated whenever a change is made. # The following comment needs to be updated whenever a change is made.
# Run `rake appcache:update` to do so # Run `rake appcache:update` to do so
# Updated: 2014-03-21T13:04:30+01:00 # Updated: 2014-03-22T17:11:53+01:00
# Main page # Main page
index.html index.html
...@@ -36,7 +36,7 @@ js/game_manager.js ...@@ -36,7 +36,7 @@ js/game_manager.js
js/grid.js js/grid.js
js/html_actuator.js js/html_actuator.js
js/keyboard_input_manager.js js/keyboard_input_manager.js
js/local_score_manager.js js/local_storage_manager.js
js/tile.js js/tile.js
favicon.ico favicon.ico
......
...@@ -28,7 +28,11 @@ ...@@ -28,7 +28,11 @@
<div class="best-container">0</div> <div class="best-container">0</div>
</div> </div>
</div> </div>
<p class="game-intro">Join the numbers and get to the <strong>2048 tile!</strong></p>
<div class="above-game">
<p class="game-intro">Join the numbers and get to the <strong>2048 tile!</strong></p>
<a class="restart-button">New Game</a>
</div>
<div class="game-container"> <div class="game-container">
<div class="game-message"> <div class="game-message">
...@@ -109,7 +113,7 @@ ...@@ -109,7 +113,7 @@
<script src="js/html_actuator.js"></script> <script src="js/html_actuator.js"></script>
<script src="js/grid.js"></script> <script src="js/grid.js"></script>
<script src="js/tile.js"></script> <script src="js/tile.js"></script>
<script src="js/local_score_manager.js"></script> <script src="js/local_storage_manager.js"></script>
<script src="js/game_manager.js"></script> <script src="js/game_manager.js"></script>
<script src="js/application.js"></script> <script src="js/application.js"></script>
......
// Wait till the browser is ready to render the game (avoids glitches) // Wait till the browser is ready to render the game (avoids glitches)
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
new GameManager(4, KeyboardInputManager, HTMLActuator, LocalScoreManager); new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
}); });
function GameManager(size, InputManager, Actuator, ScoreManager) { function GameManager(size, InputManager, Actuator, StorageManager) {
this.size = size; // Size of the grid this.size = size; // Size of the grid
this.inputManager = new InputManager; this.inputManager = new InputManager;
this.scoreManager = new ScoreManager; this.storageManager = new StorageManager;
this.actuator = new Actuator; this.actuator = new Actuator;
this.startTiles = 2; this.startTiles = 2;
this.inputManager.on("move", this.move.bind(this)); this.inputManager.on("move", this.move.bind(this));
this.inputManager.on("restart", this.restart.bind(this)); this.inputManager.on("restart", this.restart.bind(this));
...@@ -15,6 +15,7 @@ function GameManager(size, InputManager, Actuator, ScoreManager) { ...@@ -15,6 +15,7 @@ function GameManager(size, InputManager, Actuator, ScoreManager) {
// Restart the game // Restart the game
GameManager.prototype.restart = function () { GameManager.prototype.restart = function () {
this.storageManager.clearGameState();
this.actuator.continue(); this.actuator.continue();
this.setup(); this.setup();
}; };
...@@ -35,15 +36,25 @@ GameManager.prototype.isGameTerminated = function () { ...@@ -35,15 +36,25 @@ GameManager.prototype.isGameTerminated = function () {
// Set up the game // Set up the game
GameManager.prototype.setup = function () { GameManager.prototype.setup = function () {
this.grid = new Grid(this.size); var previousState = this.storageManager.getGameState();
this.score = 0; if (previousState) {
this.over = false; this.grid = new Grid(previousState.grid.size,
this.won = false; previousState.grid.cells); // Reload grid
this.keepPlaying = false; this.score = previousState.score;
this.over = previousState.over;
// Add the initial tiles this.won = previousState.won;
this.addStartTiles(); this.keepPlaying = previousState.keepPlaying;
} else {
this.grid = new Grid(this.size);
this.score = 0;
this.over = false;
this.won = false;
this.keepPlaying = false;
// Add the initial tiles
this.addStartTiles();
}
// Update the actuator // Update the actuator
this.actuate(); this.actuate();
...@@ -68,20 +79,32 @@ GameManager.prototype.addRandomTile = function () { ...@@ -68,20 +79,32 @@ GameManager.prototype.addRandomTile = function () {
// Sends the updated grid to the actuator // Sends the updated grid to the actuator
GameManager.prototype.actuate = function () { GameManager.prototype.actuate = function () {
if (this.scoreManager.get() < this.score) { if (this.storageManager.getBestScore() < this.score) {
this.scoreManager.set(this.score); this.storageManager.setBestScore(this.score);
} }
this.storageManager.setGameState(this.serialize());
this.actuator.actuate(this.grid, { this.actuator.actuate(this.grid, {
score: this.score, score: this.score,
over: this.over, over: this.over,
won: this.won, won: this.won,
bestScore: this.scoreManager.get(), bestScore: this.storageManager.getBestScore(),
terminated: this.isGameTerminated() terminated: this.isGameTerminated()
}); });
}; };
GameManager.prototype.serialize = function () {
return {
grid: this.grid.serialize(),
score: this.score,
over: this.over,
won: this.won,
keepPlaying: this.keepPlaying
};
};
// Save all tile positions and remove merger info // Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () { GameManager.prototype.prepareTiles = function () {
this.grid.eachCell(function (x, y, tile) { this.grid.eachCell(function (x, y, tile) {
......
function Grid(size) { function Grid(size, previousState) {
this.size = size; this.size = size;
this.cells = previousState ? this.fromState(previousState) : this.empty();
this.cells = [];
this.build();
} }
// Build a grid of the specified size // Build a grid of the specified size
Grid.prototype.build = function () { Grid.prototype.empty = function () {
var cells = [];
for (var x = 0; x < this.size; x++) { for (var x = 0; x < this.size; x++) {
var row = this.cells[x] = []; var row = cells[x] = [];
for (var y = 0; y < this.size; y++) { for (var y = 0; y < this.size; y++) {
row.push(null); row.push(null);
} }
} }
return cells;
};
Grid.prototype.fromState = function (state) {
var cells = [];
for (var x = 0; x < this.size; x++) {
var row = cells[x] = [];
for (var y = 0; y < this.size; y++) {
var tile = state[x][y];
row.push(tile ? new Tile(tile.position, tile.value) : null);
}
}
return cells;
}; };
// Find the first available random position // Find the first available random position
...@@ -82,3 +98,20 @@ Grid.prototype.withinBounds = function (position) { ...@@ -82,3 +98,20 @@ Grid.prototype.withinBounds = function (position) {
return position.x >= 0 && position.x < this.size && return position.x >= 0 && position.x < this.size &&
position.y >= 0 && position.y < this.size; position.y >= 0 && position.y < this.size;
}; };
Grid.prototype.serialize = function () {
var cellState = [];
for (var x = 0; x < this.size; x++) {
var row = cellState[x] = [];
for (var y = 0; y < this.size; y++) {
row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null);
}
}
return {
size: this.size,
cells: cellState
};
};
...@@ -39,16 +39,17 @@ KeyboardInputManager.prototype.listen = function () { ...@@ -39,16 +39,17 @@ KeyboardInputManager.prototype.listen = function () {
39: 1, // Right 39: 1, // Right
40: 2, // Down 40: 2, // Down
37: 3, // Left 37: 3, // Left
75: 0, // vim keybindings 75: 0, // Vim up
76: 1, 76: 1, // Vim right
74: 2, 74: 2, // Vim down
72: 3, 72: 3, // Vim left
87: 0, // W 87: 0, // W
68: 1, // D 68: 1, // D
83: 2, // S 83: 2, // S
65: 3 // A 65: 3 // A
}; };
// Respond to direction keys
document.addEventListener("keydown", function (event) { document.addEventListener("keydown", function (event) {
var modifiers = event.altKey || event.ctrlKey || event.metaKey || var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
event.shiftKey; event.shiftKey;
...@@ -59,34 +60,37 @@ KeyboardInputManager.prototype.listen = function () { ...@@ -59,34 +60,37 @@ KeyboardInputManager.prototype.listen = function () {
event.preventDefault(); event.preventDefault();
self.emit("move", mapped); self.emit("move", mapped);
} }
}
if (event.which === 32) self.restart.bind(self)(event); // R key restarts the game
if (!modifiers && event.which === 82) {
self.restart.call(self, event);
} }
}); });
var retry = document.querySelector(".retry-button"); // Respond to button presses
retry.addEventListener("click", this.restart.bind(this)); this.bindButtonPress(".retry-button", this.restart);
retry.addEventListener(this.eventTouchend, this.restart.bind(this)); this.bindButtonPress(".restart-button", this.restart);
this.bindButtonPress(".keep-playing-button", this.keepPlaying);
var keepPlaying = document.querySelector(".keep-playing-button");
keepPlaying.addEventListener("click", this.keepPlaying.bind(this));
keepPlaying.addEventListener("touchend", this.keepPlaying.bind(this));
// Listen to swipe events // Respond to swipe events
var touchStartClientX, touchStartClientY; var touchStartClientX, touchStartClientY;
var gameContainer = document.getElementsByClassName("game-container")[0]; var gameContainer = document.getElementsByClassName("game-container")[0];
gameContainer.addEventListener(this.eventTouchstart, function (event) { gameContainer.addEventListener(this.eventTouchstart, function (event) {
if (( !window.navigator.msPointerEnabled && event.touches.length > 1) || event.targetTouches > 1) return; if ((!window.navigator.msPointerEnabled && event.touches.length > 1) ||
event.targetTouches > 1) {
if(window.navigator.msPointerEnabled){ return; // Ignore if touching with more than 1 finger
touchStartClientX = event.pageX; }
touchStartClientY = event.pageY;
if (window.navigator.msPointerEnabled) {
touchStartClientX = event.pageX;
touchStartClientY = event.pageY;
} else { } else {
touchStartClientX = event.touches[0].clientX; touchStartClientX = event.touches[0].clientX;
touchStartClientY = event.touches[0].clientY; touchStartClientY = event.touches[0].clientY;
} }
event.preventDefault(); event.preventDefault();
}); });
...@@ -95,15 +99,19 @@ KeyboardInputManager.prototype.listen = function () { ...@@ -95,15 +99,19 @@ KeyboardInputManager.prototype.listen = function () {
}); });
gameContainer.addEventListener(this.eventTouchend, function (event) { gameContainer.addEventListener(this.eventTouchend, function (event) {
if (( !window.navigator.msPointerEnabled && event.touches.length > 0) || event.targetTouches > 0) return; if ((!window.navigator.msPointerEnabled && event.touches.length > 0) ||
event.targetTouches > 0) {
return; // Ignore if still touching with one or more fingers
}
var touchEndClientX, touchEndClientY; var touchEndClientX, touchEndClientY;
if(window.navigator.msPointerEnabled){
touchEndClientX = event.pageX; if (window.navigator.msPointerEnabled) {
touchEndClientY = event.pageY; touchEndClientX = event.pageX;
touchEndClientY = event.pageY;
} else { } else {
touchEndClientX = event.changedTouches[0].clientX; touchEndClientX = event.changedTouches[0].clientX;
touchEndClientY = event.changedTouches[0].clientY; touchEndClientY = event.changedTouches[0].clientY;
} }
var dx = touchEndClientX - touchStartClientX; var dx = touchEndClientX - touchStartClientX;
...@@ -128,3 +136,9 @@ KeyboardInputManager.prototype.keepPlaying = function (event) { ...@@ -128,3 +136,9 @@ KeyboardInputManager.prototype.keepPlaying = function (event) {
event.preventDefault(); event.preventDefault();
this.emit("keepPlaying"); this.emit("keepPlaying");
}; };
KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) {
var button = document.querySelector(selector);
button.addEventListener("click", fn.bind(this));
button.addEventListener(this.eventTouchend, fn.bind(this));
};
...@@ -18,14 +18,15 @@ window.fakeStorage = { ...@@ -18,14 +18,15 @@ window.fakeStorage = {
} }
}; };
function LocalScoreManager() { function LocalStorageManager() {
this.key = "bestScore"; this.bestScoreKey = "bestScore";
this.gameStateKey = "gameState";
var supported = this.localStorageSupported(); var supported = this.localStorageSupported();
this.storage = supported ? window.localStorage : window.fakeStorage; this.storage = supported ? window.localStorage : window.fakeStorage;
} }
LocalScoreManager.prototype.localStorageSupported = function () { LocalStorageManager.prototype.localStorageSupported = function () {
var testKey = "test"; var testKey = "test";
var storage = window.localStorage; var storage = window.localStorage;
...@@ -38,11 +39,25 @@ LocalScoreManager.prototype.localStorageSupported = function () { ...@@ -38,11 +39,25 @@ LocalScoreManager.prototype.localStorageSupported = function () {
} }
}; };
LocalScoreManager.prototype.get = function () { // Best score getters/setters
return this.storage.getItem(this.key) || 0; LocalStorageManager.prototype.getBestScore = function () {
return this.storage.getItem(this.bestScoreKey) || 0;
}; };
LocalScoreManager.prototype.set = function (score) { LocalStorageManager.prototype.setBestScore = function (score) {
this.storage.setItem(this.key, score); this.storage.setItem(this.bestScoreKey, score);
}; };
// Game state getters/setters and clearing
LocalStorageManager.prototype.getGameState = function () {
var stateJSON = this.storage.getItem(this.gameStateKey);
return stateJSON ? JSON.parse(stateJSON) : null;
};
LocalStorageManager.prototype.setGameState = function (gameState) {
this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));
};
LocalStorageManager.prototype.clearGameState = function () {
this.storage.removeItem(this.gameStateKey);
};
...@@ -15,3 +15,13 @@ Tile.prototype.updatePosition = function (position) { ...@@ -15,3 +15,13 @@ Tile.prototype.updatePosition = function (position) {
this.x = position.x; this.x = position.x;
this.y = position.y; this.y = position.y;
}; };
Tile.prototype.serialize = function () {
return {
position: {
x: this.x,
y: this.y
},
value: this.value
};
};
...@@ -77,3 +77,12 @@ ...@@ -77,3 +77,12 @@
-moz-appearance: $args; -moz-appearance: $args;
appearance: $args; appearance: $args;
} }
// Clearfix
@mixin clearfix {
&:after {
content: "";
display: block;
clear: both;
}
}
...@@ -30,7 +30,6 @@ h1.title { ...@@ -30,7 +30,6 @@ h1.title {
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
@-moz-keyframes move-up { @-moz-keyframes move-up {
0% { 0% {
top: 25px; top: 25px;
...@@ -39,7 +38,6 @@ h1.title { ...@@ -39,7 +38,6 @@ h1.title {
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
@keyframes move-up { @keyframes move-up {
0% { 0% {
top: 25px; top: 25px;
...@@ -48,7 +46,6 @@ h1.title { ...@@ -48,7 +46,6 @@ h1.title {
100% { 100% {
top: -50px; top: -50px;
opacity: 0; } } opacity: 0; } }
.scores-container { .scores-container {
float: right; float: right;
text-align: right; } text-align: right; }
...@@ -128,21 +125,18 @@ hr { ...@@ -128,21 +125,18 @@ hr {
100% { 100% {
opacity: 1; } } opacity: 1; } }
@-moz-keyframes fade-in { @-moz-keyframes fade-in {
0% { 0% {
opacity: 0; } opacity: 0; }
100% { 100% {
opacity: 1; } } opacity: 1; } }
@keyframes fade-in { @keyframes fade-in {
0% { 0% {
opacity: 0; } opacity: 0; }
100% { 100% {
opacity: 1; } } opacity: 1; } }
.game-container { .game-container {
margin-top: 40px; margin-top: 40px;
position: relative; position: relative;
...@@ -397,7 +391,6 @@ hr { ...@@ -397,7 +391,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
@-moz-keyframes appear { @-moz-keyframes appear {
0% { 0% {
opacity: 0; opacity: 0;
...@@ -410,7 +403,6 @@ hr { ...@@ -410,7 +403,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
@keyframes appear { @keyframes appear {
0% { 0% {
opacity: 0; opacity: 0;
...@@ -423,7 +415,6 @@ hr { ...@@ -423,7 +415,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
.tile-new .tile-inner { .tile-new .tile-inner {
-webkit-animation: appear 200ms ease 100ms; -webkit-animation: appear 200ms ease 100ms;
-moz-animation: appear 200ms ease 100ms; -moz-animation: appear 200ms ease 100ms;
...@@ -447,7 +438,6 @@ hr { ...@@ -447,7 +438,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
@-moz-keyframes pop { @-moz-keyframes pop {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
...@@ -463,7 +453,6 @@ hr { ...@@ -463,7 +453,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
@keyframes pop { @keyframes pop {
0% { 0% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
...@@ -479,7 +468,6 @@ hr { ...@@ -479,7 +468,6 @@ hr {
-webkit-transform: scale(1); -webkit-transform: scale(1);
-moz-transform: scale(1); -moz-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
.tile-merged .tile-inner { .tile-merged .tile-inner {
z-index: 20; z-index: 20;
-webkit-animation: pop 200ms ease 100ms; -webkit-animation: pop 200ms ease 100ms;
...@@ -489,8 +477,27 @@ hr { ...@@ -489,8 +477,27 @@ hr {
-moz-animation-fill-mode: backwards; -moz-animation-fill-mode: backwards;
animation-fill-mode: backwards; } animation-fill-mode: backwards; }
.above-game:after {
content: "";
display: block;
clear: both; }
.game-intro { .game-intro {
margin-bottom: 0; } float: left;
line-height: 42px; }
.restart-button {
display: inline-block;
background: #8f7a66;
border-radius: 3px;
padding: 0 20px;
text-decoration: none;
color: #f9f6f2;
height: 40px;
line-height: 42px;
display: block;
text-align: center;
float: right; }
.game-explanation { .game-explanation {
margin-top: 50px; } margin-top: 50px; }
...@@ -526,6 +533,19 @@ hr { ...@@ -526,6 +533,19 @@ hr {
.heading { .heading {
margin-bottom: 10px; } margin-bottom: 10px; }
.game-intro {
width: 55%;
display: block;
box-sizing: border-box;
line-height: 1.65; }
.restart-button {
width: 42%;
padding: 0;
display: block;
box-sizing: border-box;
margin-top: 2px; }
.game-container { .game-container {
margin-top: 40px; margin-top: 40px;
position: relative; position: relative;
......
...@@ -34,10 +34,8 @@ body { ...@@ -34,10 +34,8 @@ body {
margin: 80px 0; margin: 80px 0;
} }
.heading:after { .heading {
content: ""; @include clearfix;
display: block;
clear: both;
} }
h1.title { h1.title {
...@@ -453,8 +451,24 @@ hr { ...@@ -453,8 +451,24 @@ hr {
@include animation-fill-mode(backwards); @include animation-fill-mode(backwards);
} }
.above-game {
@include clearfix;
// margin-bottom: 10px;
}
.game-intro { .game-intro {
margin-bottom: 0; float: left;
line-height: 42px;
// margin-bottom: 0;
}
.restart-button {
@include button;
display: block;
// width: 100px;
// margin: 10px auto 10px auto;
text-align: center;
float: right;
} }
.game-explanation { .game-explanation {
...@@ -508,6 +522,22 @@ hr { ...@@ -508,6 +522,22 @@ hr {
margin-bottom: 10px; margin-bottom: 10px;
} }
// Show intro and restart button side by side
.game-intro {
width: 55%;
display: block;
box-sizing: border-box;
line-height: 1.65;
}
.restart-button {
width: 42%;
padding: 0;
display: block;
box-sizing: border-box;
margin-top: 2px;
}
// Render the game field at the right width // Render the game field at the right width
@include game-field; @include game-field;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册