Created
August 24, 2017 10:36
-
-
Save jshbrntt/643977d2bb77f922eb0a7f54a3747beb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Model } from "engine/mvc"; | |
import { Signal } from "org/osflash/signals"; | |
import { SoundAS } from "treefortress/sound"; | |
import { TileModel } from "tile"; | |
import { CellModel } from "cell"; | |
import { GDTAssets } from "assets"; | |
class GridModel extends Model { | |
protected _width: number; | |
protected _height: number; | |
protected _vector: Array<TileModel>; | |
protected _simulating: boolean; | |
protected _tilesFalling: number; | |
protected _swappedCell1: CellModel; | |
protected _swappedCell2: CellModel; | |
protected _tileMoved: Signal; | |
protected _tileAdded: Signal; | |
protected _tileRemoved: Signal; | |
protected _randomized: Signal; | |
protected _checked: Signal; | |
protected _swapped: Signal; | |
protected _simulated: Signal; | |
constructor(width: number, height: number) { | |
super(); | |
this._width = width; | |
this._height = height; | |
this._vector = new Array<TileModel>(this._width * this._height); | |
this._simulating = false; | |
this._tilesFalling = 0; | |
this._swappedCell1 = null; | |
this._swappedCell2 = null; | |
this._tileMoved = new Signal(); | |
this._tileAdded = new Signal(); | |
this._tileRemoved = new Signal(); | |
this._randomized = new Signal(); | |
this._checked = new Signal(); | |
this._swapped = new Signal(); | |
this._simulated = new Signal(); | |
} | |
public beginSimulation(): void { | |
console.log("simulate"); | |
if (this._simulating) { | |
return; | |
} | |
this._simulating = true; | |
this.check(); | |
} | |
protected check(): void { | |
console.log("check"); | |
let matches: Array<CellModel> = this.getMatches(); | |
this._checked.dispatch(matches.length); | |
if (matches.length > 0) { | |
this.remove(matches); | |
} else { | |
if (!this.fill()) { | |
this.endSimulation(); | |
} | |
} | |
} | |
protected remove(matches: Array<CellModel>): void { | |
console.log("remove"); | |
for (let match in matches) { | |
this.removeTile(match); | |
} | |
this.gravity(); | |
SoundAS.play(GDTAssets.SOUND_SMASH + this.randomTileValue(1, 4), 0.5); | |
} | |
public gravity(): void { | |
console.log("gravity"); | |
let movedTile: boolean = false; | |
for ( | |
let i: number = this._vector.length - this._width; | |
i < this._vector.length; | |
i++ | |
) { | |
let drop: number = 0; | |
let cell: CellModel = this.convert1D2D(i); | |
while (cell.y >= 0) { | |
if (!this.getTileModel(cell)) { | |
drop++; | |
} else if (drop) { | |
this._tilesFalling++; | |
this.moveTile( | |
cell, | |
new CellModel(cell.x, cell.y + drop), | |
this.onTileFallen | |
); | |
movedTile = true; | |
} | |
cell.y--; | |
} | |
} | |
if (!movedTile) { | |
if (!this.fill()) { | |
this.check(); | |
} | |
} | |
} | |
protected onTileFallen(): void { | |
this._tilesFalling--; | |
if (this._tilesFalling == 0) { | |
if (!this.fill()) { | |
this.check(); | |
} | |
} | |
SoundAS.play(GDTAssets.SOUND_GLASS + this.randomTileValue(1, 5), 0.54); | |
} | |
protected fill(): boolean { | |
console.log("this.fill"); | |
let filled: boolean = false; | |
for (let i: number = this._width; i <= this._width * 2; i++) { | |
let cell: CellModel = this.convert1D2D(i); | |
if (!this.getTileModel(cell)) { | |
this.addTile( | |
this.createRandomTileModel(new CellModel(cell.x, cell.y - 1)) | |
); | |
filled = true; | |
} | |
} | |
if (filled) { | |
this.gravity(); | |
} | |
return filled; | |
} | |
public endSimulation(): void { | |
console.log("simulated"); | |
this._simulated.dispatch(); | |
this._simulating = false; | |
} | |
public swapCells(cell1: CellModel, cell2: CellModel): boolean { | |
let tile1: TileModel = this.getTileModel(cell1); | |
let tile2: TileModel = this.getTileModel(cell2); | |
if (!tile1 || !tile2 || this._simulating) { | |
return false; | |
} | |
// After first swap remove matches. | |
this._swappedCell1 = tile1.cell; | |
this._swappedCell2 = tile2.cell; | |
this.swapTiles(tile1, tile2); | |
return true; | |
} | |
protected swapTiles(tile1: TileModel, tile2: TileModel): void { | |
tile1.swap(tile2, this.onSwapped); | |
this.setTileModel(tile1.cell, tile1); | |
this.setTileModel(tile2.cell, tile2); | |
} | |
protected onSwapped(tile1: TileModel, tile2: TileModel): void { | |
if ( | |
!tile1.cell.equals(this._swappedCell1) && | |
!tile2.cell.equals(this._swappedCell2) | |
) { | |
SoundAS.play(GDTAssets.SOUND_SWAP, 0.5); | |
this._checked.addOnce(function(matches: number): void { | |
if (!matches) { | |
this.swapTiles(tile1, tile2); | |
} else { | |
this._swapped.dispatch(); | |
} | |
}); | |
this.beginSimulation(); | |
} else { | |
this._swapped.dispatch(); | |
SoundAS.play(GDTAssets.SOUND_UNDO, 1.0); | |
} | |
} | |
public moveTile( | |
fromCell: CellModel, | |
toCell: CellModel, | |
onMoved: Function = null | |
): void { | |
let movingTile: TileModel = this.getTileModel(fromCell); | |
if (movingTile) { | |
this.removeTile(toCell); | |
this.setTileModel(toCell, movingTile); | |
movingTile.move(toCell, onMoved); | |
this.setTileModel(fromCell, null); | |
this._tileMoved.dispatch(fromCell, toCell); | |
} | |
} | |
public addTile(tileModel: TileModel): void { | |
if (tileModel) { | |
this.setTileModel(tileModel.cell, tileModel); | |
this._tileAdded.dispatch(tileModel); | |
} | |
} | |
public removeTile(fromCell: CellModel): void { | |
let removedModel: TileModel = this.getTileModel(fromCell); | |
if (removedModel) { | |
removedModel.removed.dispatch(); | |
this.setTileModel(fromCell, null); | |
this._tileRemoved.dispatch(removedModel); | |
} | |
} | |
public setTileModel(p: CellModel, v: TileModel): boolean { | |
let i: number = this.convert2D1D(p); | |
if (i < 0) { | |
return false; | |
} | |
this._vector[i] = v; | |
return true; | |
} | |
public getTileModel(p: CellModel): TileModel { | |
let i: number = this.convert2D1D(p); | |
if (i < 0) { | |
return null; | |
} | |
return this._vector[i]; | |
} | |
public randomize(): void { | |
for (let i: number = this._width; i < this._vector.length; ++i) { | |
let currentCell: CellModel; | |
do { | |
currentCell = this.convert1D2D(i); | |
this._vector[i] = this.createRandomTileModel(currentCell); | |
} while ( | |
this.matchedHeight(currentCell).length > 2 || | |
this.matchedWidth(currentCell).length > 2 | |
); | |
} | |
this._randomized.dispatch(); | |
} | |
protected createRandomTileModel(cell: CellModel): TileModel { | |
return new TileModel(this.randomTileValue(0, 4), cell); | |
} | |
protected randomTileValue(min: number, max: number): number { | |
return Math.floor(Math.random() * (1 + max - min)) + min; | |
} | |
public convert1D2D(i: number): CellModel { | |
if (i < 0 || i > this._vector.length - 1) { | |
return null; | |
} | |
return new CellModel(i % this._width, Math.floor(i / this._width)); | |
} | |
public convert2D1D(tile: CellModel): number { | |
if ( | |
!tile || | |
tile.x < 0 || | |
tile.x > this._width - 1 || | |
tile.y < 0 || | |
tile.y > this._height - 1 | |
) { | |
return -1; | |
} | |
return tile.x + tile.y * this._width; | |
} | |
protected unique(cells: Array<CellModel>): Array<CellModel> { | |
let unique: Array<CellModel> = cells.concat(); | |
for (let i: number = 0; i < unique.length; ++i) { | |
for (let j: number = i + 1; j < unique.length; ++j) { | |
if (unique[i].equals(unique[j])) unique.splice(j--, 1); | |
} | |
} | |
return unique; | |
} | |
protected getMatches(): Array<CellModel> { | |
let matches: Array<CellModel> = new Array<CellModel>(); | |
for (let i: number = 0; i < this._vector.length; ++i) { | |
let p: CellModel = this.convert1D2D(i); | |
let horizontalMatch: Array<CellModel> = this.matchedWidth(p); | |
if (horizontalMatch.length > 2) { | |
matches = matches.concat(horizontalMatch); | |
} | |
let verticalMatch: Array<CellModel> = this.matchedHeight(p); | |
if (verticalMatch.length > 2) { | |
matches = matches.concat(verticalMatch); | |
} | |
} | |
return this.unique(matches); | |
} | |
protected matchedHeight(p: CellModel): Array<CellModel> { | |
let matches: Array<CellModel> = new Array<CellModel>(); | |
let tileModel: TileModel = this.getTileModel(p); | |
if (!tileModel) { | |
return matches; | |
} | |
matches.push(p); | |
let cursor: CellModel = new CellModel(p.x, p.y - 1); | |
while (this.valueMatches(tileModel, cursor)) { | |
matches.push(new CellModel(cursor.x, cursor.y)); | |
cursor.y--; | |
} | |
cursor.y = p.y + 1; | |
while (this.valueMatches(tileModel, cursor)) { | |
matches.push(new CellModel(cursor.x, cursor.y)); | |
cursor.y++; | |
} | |
return matches; | |
} | |
protected matchedWidth(p: CellModel): Array<CellModel> { | |
let matches: Array<CellModel> = new Array<CellModel>(); | |
let tileModel: TileModel = this.getTileModel(p); | |
if (!tileModel) { | |
return matches; | |
} | |
matches.push(p); | |
let cursor: CellModel = new CellModel(p.x - 1, p.y); | |
while (this.valueMatches(tileModel, cursor)) { | |
matches.push(new CellModel(cursor.x, cursor.y)); | |
cursor.x--; | |
} | |
cursor.x = p.x + 1; | |
while (this.valueMatches(tileModel, cursor)) { | |
matches.push(new CellModel(cursor.x, cursor.y)); | |
cursor.x++; | |
} | |
return matches; | |
} | |
protected valueMatches(tileModel: TileModel, p: CellModel): boolean { | |
if (!tileModel || !this.getTileModel(p)) { | |
return false; | |
} | |
return tileModel.value == this.getTileModel(p).value; | |
} | |
public toString(): String { | |
let string: String = ""; | |
for (let i: number = 0; i < this._vector.length; ++i) { | |
let tileModel: TileModel = this.getTileModel(this.convert1D2D(i)); | |
string += !tileModel ? "X" : tileModel.value.toString(); | |
if ((i + 1) % this._width == 0) { | |
string += "\n"; | |
} | |
} | |
return string; | |
} | |
public get width(): number { | |
return this._width; | |
} | |
public get height(): number { | |
return this._height; | |
} | |
public get tileMoved(): Signal { | |
return this._tileMoved; | |
} | |
public get tileAdded(): Signal { | |
return this._tileAdded; | |
} | |
public get simulated(): Signal { | |
return this._simulated; | |
} | |
public get swapped(): Signal { | |
return this._swapped; | |
} | |
public get simulating(): boolean { | |
return this._simulating; | |
} | |
public get checked(): Signal { | |
return this._checked; | |
} | |
public get tileRemoved(): Signal { | |
return this._tileRemoved; | |
} | |
public get randomized(): Signal { | |
return this._randomized; | |
} | |
public get size(): number { | |
return this._vector.length; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment