new file: Files/flashplayer_32_sa.exe new file: favicon.ico new file: globe.gif new file: imgs/download.png new file: imgs/zuck.jpg new file: index.html new file: other.ico new file: script.js new file: site.webmanifest new file: sitemap.html new file: styles/backround.css new file: styles/border.css new file: styles/fonts/Titillium_Web/OFL.txt new file: styles/fonts/Titillium_Web/TitilliumWeb-Black.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Bold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Italic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Light.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Regular.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf new file: styles/fonts/webfontkit-20221027-163353/generator_config.txt new file: styles/fonts/webfontkit-20221027-163353/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-163353/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-demo.html new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff2 new file: styles/fonts/webfontkit-20221027-165950/generator_config.txt new file: styles/fonts/webfontkit-20221027-165950/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-165950/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-demo.html new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff2 new file: styles/style.css new file: tools/2048/.gitignore new file: tools/2048/.jshintrc new file: tools/2048/CONTRIBUTING.md new file: tools/2048/LICENSE.txt new file: tools/2048/README.md new file: tools/2048/Rakefile new file: tools/2048/favicon.ico new file: tools/2048/index.html new file: tools/2048/js/animframe_polyfill.js new file: tools/2048/js/application.js new file: tools/2048/js/bind_polyfill.js new file: tools/2048/js/classlist_polyfill.js new file: tools/2048/js/game_manager.js new file: tools/2048/js/grid.js new file: tools/2048/js/html_actuator.js new file: tools/2048/js/keyboard_input_manager.js new file: tools/2048/js/local_storage_manager.js new file: tools/2048/js/tile.js new file: tools/2048/meta/apple-touch-icon.png new file: tools/webretro/cores/neocd_libretro.js new file: tools/webretro/cores/neocd_libretro.wasm new file: tools/webretro/cores/nestopia_libretro.js new file: tools/webretro/cores/nestopia_libretro.wasm new file: tools/webretro/cores/o2em_libretro.js new file: tools/webretro/cores/o2em_libretro.wasm new file: tools/webretro/cores/opera_libretro.js new file: tools/webretro/cores/opera_libretro.wasm
296 lines
7.5 KiB
JavaScript
296 lines
7.5 KiB
JavaScript
function GameManager(size, InputManager, Actuator, ScoreManager) {
|
|
this.inputManager = new InputManager;
|
|
this.scoreManager = new ScoreManager;
|
|
this.actuator = new Actuator;
|
|
|
|
// hack
|
|
if (!Function.prototype.bind) {
|
|
Function.prototype.bind = function (oThis) {
|
|
var aArgs = Array.prototype.slice.call(arguments, 1),
|
|
fToBind = this,
|
|
fNOP = function () {},
|
|
fBound = function () {
|
|
return fToBind.apply(this instanceof fNOP && oThis
|
|
? this
|
|
: oThis || window,
|
|
aArgs.concat(Array.prototype.slice.call(arguments)));
|
|
};
|
|
|
|
fNOP.prototype = this.prototype;
|
|
fBound.prototype = new fNOP();
|
|
|
|
return fBound;
|
|
};
|
|
}
|
|
|
|
this.inputManager.on("jump", this.jump.bind(this));
|
|
|
|
this.setup();
|
|
|
|
this.timer();
|
|
}
|
|
|
|
// Restart the game
|
|
GameManager.prototype.restart = function () {
|
|
this.actuator.continue();
|
|
this.setup();
|
|
};
|
|
|
|
// Keep playing after winning
|
|
GameManager.prototype.keepPlaying = function () {
|
|
this.keepPlaying = true;
|
|
this.actuator.continue();
|
|
};
|
|
|
|
GameManager.prototype.isGameTerminated = function () {
|
|
if (this.over || (this.won && !this.keepPlaying)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Set up the game
|
|
GameManager.prototype.setup = function () {
|
|
this.score = 0;
|
|
this.birdpos = 0.5;
|
|
this.birdspd = 0;
|
|
this.ab = 1;
|
|
this.cd = 1;
|
|
};
|
|
|
|
// Set up the initial tiles to start the game with
|
|
GameManager.prototype.addStartTiles = function () {
|
|
for (var i = 0; i < this.startTiles; i++) {
|
|
this.addRandomTile();
|
|
}
|
|
};
|
|
|
|
// Adds a tile in a random position
|
|
GameManager.prototype.addRandomTile = function () {
|
|
if (this.grid.cellsAvailable()) {
|
|
var value = Math.random() < 0.9 ? 2 : 4;
|
|
var tile = new Tile(this.grid.randomAvailableCell(), value);
|
|
|
|
this.grid.insertTile(tile);
|
|
}
|
|
};
|
|
|
|
// Sends the updated grid to the actuator
|
|
GameManager.prototype.actuate = function () {
|
|
if (this.scoreManager.get() < this.score) {
|
|
this.scoreManager.set(this.score);
|
|
}
|
|
|
|
this.actuator.actuate(this.grid, {
|
|
score: this.score,
|
|
bestScore: this.scoreManager.get(),
|
|
birdpos: this.birdpos,
|
|
ab: this.ab,
|
|
cd: this.cd
|
|
});
|
|
|
|
};
|
|
|
|
// Save all tile positions and remove merger info
|
|
GameManager.prototype.prepareTiles = function () {
|
|
this.grid.eachCell(function (x, y, tile) {
|
|
if (tile) {
|
|
tile.mergedFrom = null;
|
|
tile.savePosition();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Move a tile and its representation
|
|
GameManager.prototype.moveTile = function (tile, cell) {
|
|
this.grid.cells[tile.x][tile.y] = null;
|
|
this.grid.cells[cell.x][cell.y] = tile;
|
|
tile.updatePosition(cell);
|
|
};
|
|
|
|
// Move tiles on the grid in the specified direction
|
|
GameManager.prototype.move = function (direction) {
|
|
// 0: up, 1: right, 2:down, 3: left
|
|
var self = this;
|
|
|
|
if (this.isGameTerminated()) return; // Don't do anything if the game's over
|
|
|
|
var cell, tile;
|
|
|
|
var vector = this.getVector(direction);
|
|
var traversals = this.buildTraversals(vector);
|
|
var moved = false;
|
|
|
|
// Save the current tile positions and remove merger information
|
|
this.prepareTiles();
|
|
|
|
// Traverse the grid in the right direction and move tiles
|
|
traversals.x.forEach(function (x) {
|
|
traversals.y.forEach(function (y) {
|
|
cell = { x: x, y: y };
|
|
tile = self.grid.cellContent(cell);
|
|
|
|
if (tile) {
|
|
var positions = self.findFarthestPosition(cell, vector);
|
|
var next = self.grid.cellContent(positions.next);
|
|
|
|
// Only one merger per row traversal?
|
|
if (next && next.value === tile.value && !next.mergedFrom) {
|
|
var merged = new Tile(positions.next, tile.value * 2);
|
|
merged.mergedFrom = [tile, next];
|
|
|
|
self.grid.insertTile(merged);
|
|
self.grid.removeTile(tile);
|
|
|
|
// Converge the two tiles' positions
|
|
tile.updatePosition(positions.next);
|
|
|
|
// Update the score
|
|
self.score += merged.value;
|
|
|
|
// The mighty 2048 tile
|
|
if (merged.value === 2048) self.won = true;
|
|
} else {
|
|
self.moveTile(tile, positions.farthest);
|
|
}
|
|
|
|
if (!self.positionsEqual(cell, tile)) {
|
|
moved = true; // The tile moved from its original cell!
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
if (moved) {
|
|
this.addRandomTile();
|
|
|
|
if (!this.movesAvailable()) {
|
|
this.over = true; // Game over!
|
|
}
|
|
|
|
this.actuate();
|
|
}
|
|
};
|
|
|
|
// Get the vector representing the chosen direction
|
|
GameManager.prototype.getVector = function (direction) {
|
|
// Vectors representing tile movement
|
|
var map = {
|
|
0: { x: 0, y: -1 }, // up
|
|
1: { x: 1, y: 0 }, // right
|
|
2: { x: 0, y: 1 }, // down
|
|
3: { x: -1, y: 0 } // left
|
|
};
|
|
|
|
return map[direction];
|
|
};
|
|
|
|
// Build a list of positions to traverse in the right order
|
|
GameManager.prototype.buildTraversals = function (vector) {
|
|
var traversals = { x: [], y: [] };
|
|
|
|
for (var pos = 0; pos < this.size; pos++) {
|
|
traversals.x.push(pos);
|
|
traversals.y.push(pos);
|
|
}
|
|
|
|
// Always traverse from the farthest cell in the chosen direction
|
|
if (vector.x === 1) traversals.x = traversals.x.reverse();
|
|
if (vector.y === 1) traversals.y = traversals.y.reverse();
|
|
|
|
return traversals;
|
|
};
|
|
|
|
GameManager.prototype.findFarthestPosition = function (cell, vector) {
|
|
var previous;
|
|
|
|
// Progress towards the vector direction until an obstacle is found
|
|
do {
|
|
previous = cell;
|
|
cell = { x: previous.x + vector.x, y: previous.y + vector.y };
|
|
} while (this.grid.withinBounds(cell) &&
|
|
this.grid.cellAvailable(cell));
|
|
|
|
return {
|
|
farthest: previous,
|
|
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 && other.value === tile.value) {
|
|
return true; // These two tiles can be merged
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
GameManager.prototype.positionsEqual = function (first, second) {
|
|
return first.x === second.x && first.y === second.y;
|
|
};
|
|
|
|
GameManager.prototype.timer = function () {
|
|
var self = this;
|
|
|
|
// move
|
|
this.birdpos += this.birdspd;
|
|
this.birdspd += 0.00015 / (this.birdspd + 0.1);
|
|
|
|
if (this.birdpos > 1 && this.birdspd > 0) this.birdspd = -this.birdspd;
|
|
if (this.birdpos < -0.25 && this.birdspd < 0) this.birdspd = -this.birdspd;
|
|
|
|
this.score += 1 / 64;
|
|
|
|
// check
|
|
|
|
var steppos = this.score - Math.floor(this.score);
|
|
|
|
if (steppos > 5 / 12 && steppos < 11 / 12) {
|
|
var range = {0: [-0.15, 0.3], 1: [0.2, 0.55], 2: [0.45, 0.9]};
|
|
if (this.birdpos < range[this.ab][0] || this.birdpos > range[this.ab][1]) {
|
|
this.score = steppos; // cut down the integer part
|
|
}
|
|
}
|
|
|
|
if (steppos == 0) {
|
|
this.ab = this.cd;
|
|
this.cd = Math.floor(Math.random() * 3);
|
|
}
|
|
|
|
setTimeout(function () {self.timer();}, 384 / Math.sqrt(this.score + 256));
|
|
this.actuate();
|
|
}
|
|
|
|
GameManager.prototype.jump = function () {
|
|
if (this.birdspd < 0) {
|
|
this.birdspd = -0.03;
|
|
} else {
|
|
this.birdspd = -0.025;
|
|
}
|
|
}
|