Path: blob/main/projects/flappy-2048/js/game_manager.js
1834 views
function GameManager(size, InputManager, Actuator, ScoreManager) {1this.inputManager = new InputManager;2this.scoreManager = new ScoreManager;3this.actuator = new Actuator;45// hack6if (!Function.prototype.bind) {7Function.prototype.bind = function (oThis) {8var aArgs = Array.prototype.slice.call(arguments, 1),9fToBind = this,10fNOP = function () {},11fBound = function () {12return fToBind.apply(this instanceof fNOP && oThis13? this14: oThis || window,15aArgs.concat(Array.prototype.slice.call(arguments)));16};1718fNOP.prototype = this.prototype;19fBound.prototype = new fNOP();2021return fBound;22};23}2425this.inputManager.on("jump", this.jump.bind(this));2627this.setup();2829this.timer();30}3132// Restart the game33GameManager.prototype.restart = function () {34this.actuator.continue();35this.setup();36};3738// Keep playing after winning39GameManager.prototype.keepPlaying = function () {40this.keepPlaying = true;41this.actuator.continue();42};4344GameManager.prototype.isGameTerminated = function () {45if (this.over || (this.won && !this.keepPlaying)) {46return true;47} else {48return false;49}50};5152// Set up the game53GameManager.prototype.setup = function () {54this.score = 0;55this.birdpos = 0.5;56this.birdspd = 0;57this.ab = 1;58this.cd = 1;59};6061// Set up the initial tiles to start the game with62GameManager.prototype.addStartTiles = function () {63for (var i = 0; i < this.startTiles; i++) {64this.addRandomTile();65}66};6768// Adds a tile in a random position69GameManager.prototype.addRandomTile = function () {70if (this.grid.cellsAvailable()) {71var value = Math.random() < 0.9 ? 2 : 4;72var tile = new Tile(this.grid.randomAvailableCell(), value);7374this.grid.insertTile(tile);75}76};7778// Sends the updated grid to the actuator79GameManager.prototype.actuate = function () {80if (this.scoreManager.get() < this.score) {81this.scoreManager.set(this.score);82}8384this.actuator.actuate(this.grid, {85score: this.score,86bestScore: this.scoreManager.get(),87birdpos: this.birdpos,88ab: this.ab,89cd: this.cd90});9192};9394// Save all tile positions and remove merger info95GameManager.prototype.prepareTiles = function () {96this.grid.eachCell(function (x, y, tile) {97if (tile) {98tile.mergedFrom = null;99tile.savePosition();100}101});102};103104// Move a tile and its representation105GameManager.prototype.moveTile = function (tile, cell) {106this.grid.cells[tile.x][tile.y] = null;107this.grid.cells[cell.x][cell.y] = tile;108tile.updatePosition(cell);109};110111// Move tiles on the grid in the specified direction112GameManager.prototype.move = function (direction) {113// 0: up, 1: right, 2:down, 3: left114var self = this;115116if (this.isGameTerminated()) return; // Don't do anything if the game's over117118var cell, tile;119120var vector = this.getVector(direction);121var traversals = this.buildTraversals(vector);122var moved = false;123124// Save the current tile positions and remove merger information125this.prepareTiles();126127// Traverse the grid in the right direction and move tiles128traversals.x.forEach(function (x) {129traversals.y.forEach(function (y) {130cell = { x: x, y: y };131tile = self.grid.cellContent(cell);132133if (tile) {134var positions = self.findFarthestPosition(cell, vector);135var next = self.grid.cellContent(positions.next);136137// Only one merger per row traversal?138if (next && next.value === tile.value && !next.mergedFrom) {139var merged = new Tile(positions.next, tile.value * 2);140merged.mergedFrom = [tile, next];141142self.grid.insertTile(merged);143self.grid.removeTile(tile);144145// Converge the two tiles' positions146tile.updatePosition(positions.next);147148// Update the score149self.score += merged.value;150151// The mighty 2048 tile152if (merged.value === 2048) self.won = true;153} else {154self.moveTile(tile, positions.farthest);155}156157if (!self.positionsEqual(cell, tile)) {158moved = true; // The tile moved from its original cell!159}160}161});162});163164if (moved) {165this.addRandomTile();166167if (!this.movesAvailable()) {168this.over = true; // Game over!169}170171this.actuate();172}173};174175// Get the vector representing the chosen direction176GameManager.prototype.getVector = function (direction) {177// Vectors representing tile movement178var map = {1790: { x: 0, y: -1 }, // up1801: { x: 1, y: 0 }, // right1812: { x: 0, y: 1 }, // down1823: { x: -1, y: 0 } // left183};184185return map[direction];186};187188// Build a list of positions to traverse in the right order189GameManager.prototype.buildTraversals = function (vector) {190var traversals = { x: [], y: [] };191192for (var pos = 0; pos < this.size; pos++) {193traversals.x.push(pos);194traversals.y.push(pos);195}196197// Always traverse from the farthest cell in the chosen direction198if (vector.x === 1) traversals.x = traversals.x.reverse();199if (vector.y === 1) traversals.y = traversals.y.reverse();200201return traversals;202};203204GameManager.prototype.findFarthestPosition = function (cell, vector) {205var previous;206207// Progress towards the vector direction until an obstacle is found208do {209previous = cell;210cell = { x: previous.x + vector.x, y: previous.y + vector.y };211} while (this.grid.withinBounds(cell) &&212this.grid.cellAvailable(cell));213214return {215farthest: previous,216next: cell // Used to check if a merge is required217};218};219220GameManager.prototype.movesAvailable = function () {221return this.grid.cellsAvailable() || this.tileMatchesAvailable();222};223224// Check for available matches between tiles (more expensive check)225GameManager.prototype.tileMatchesAvailable = function () {226var self = this;227228var tile;229230for (var x = 0; x < this.size; x++) {231for (var y = 0; y < this.size; y++) {232tile = this.grid.cellContent({ x: x, y: y });233234if (tile) {235for (var direction = 0; direction < 4; direction++) {236var vector = self.getVector(direction);237var cell = { x: x + vector.x, y: y + vector.y };238239var other = self.grid.cellContent(cell);240241if (other && other.value === tile.value) {242return true; // These two tiles can be merged243}244}245}246}247}248249return false;250};251252GameManager.prototype.positionsEqual = function (first, second) {253return first.x === second.x && first.y === second.y;254};255256GameManager.prototype.timer = function () {257var self = this;258259// move260this.birdpos += this.birdspd;261this.birdspd += 0.00015 / (this.birdspd + 0.1);262263if (this.birdpos > 1 && this.birdspd > 0) this.birdspd = -this.birdspd;264if (this.birdpos < -0.25 && this.birdspd < 0) this.birdspd = -this.birdspd;265266this.score += 1 / 64;267268// check269270var steppos = this.score - Math.floor(this.score);271272if (steppos > 5 / 12 && steppos < 11 / 12) {273var range = {0: [-0.15, 0.3], 1: [0.2, 0.55], 2: [0.45, 0.9]};274if (this.birdpos < range[this.ab][0] || this.birdpos > range[this.ab][1]) {275this.score = steppos; // cut down the integer part276}277}278279if (steppos == 0) {280this.ab = this.cd;281this.cd = Math.floor(Math.random() * 3);282}283284setTimeout(function () {self.timer();}, 384 / Math.sqrt(this.score + 256));285this.actuate();286}287288GameManager.prototype.jump = function () {289if (this.birdspd < 0) {290this.birdspd = -0.03;291} else {292this.birdspd = -0.025;293}294}295296297