2020-03-15 18:44:32 +01:00
|
|
|
// @flow
|
|
|
|
|
2020-03-23 11:32:50 +01:00
|
|
|
import Piece from "./Piece";
|
2020-03-23 18:10:45 +01:00
|
|
|
import ScoreManager from "./ScoreManager";
|
|
|
|
import GridManager from "./GridManager";
|
2020-03-15 18:44:32 +01:00
|
|
|
|
|
|
|
export default class GameLogic {
|
|
|
|
|
2020-03-17 11:00:45 +01:00
|
|
|
static levelTicks = [
|
|
|
|
1000,
|
|
|
|
800,
|
|
|
|
600,
|
|
|
|
400,
|
|
|
|
300,
|
|
|
|
200,
|
|
|
|
150,
|
|
|
|
100,
|
|
|
|
];
|
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#scoreManager: ScoreManager;
|
|
|
|
#gridManager: GridManager;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#height: number;
|
|
|
|
#width: number;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#gameRunning: boolean;
|
|
|
|
#gamePaused: boolean;
|
|
|
|
#gameTime: number;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#currentObject: Piece;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#gameTick: number;
|
|
|
|
#gameTickInterval: IntervalID;
|
|
|
|
#gameTimeInterval: IntervalID;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#pressInInterval: TimeoutID;
|
|
|
|
#isPressedIn: boolean;
|
|
|
|
#autoRepeatActivationDelay: number;
|
|
|
|
#autoRepeatDelay: number;
|
2020-03-17 00:24:57 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#nextPieces: Array<Piece>;
|
|
|
|
#nextPiecesCount: number;
|
2020-03-17 00:24:57 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#onTick: Function;
|
|
|
|
#onClock: Function;
|
2020-03-15 19:28:41 +01:00
|
|
|
endCallback: Function;
|
2020-03-15 18:44:32 +01:00
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
#colors: Object;
|
2020-03-15 20:34:20 +01:00
|
|
|
|
|
|
|
constructor(height: number, width: number, colors: Object) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#height = height;
|
|
|
|
this.#width = width;
|
|
|
|
this.#gameRunning = false;
|
|
|
|
this.#gamePaused = false;
|
|
|
|
this.#colors = colors;
|
|
|
|
this.#autoRepeatActivationDelay = 300;
|
|
|
|
this.#autoRepeatDelay = 50;
|
|
|
|
this.#nextPieces = [];
|
|
|
|
this.#nextPiecesCount = 3;
|
|
|
|
this.#scoreManager = new ScoreManager();
|
|
|
|
this.#gridManager = new GridManager(this.getWidth(), this.getHeight(), this.#colors);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getHeight(): number {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#height;
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getWidth(): number {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#width;
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 18:10:45 +01:00
|
|
|
getCurrentGrid() {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#gridManager.getCurrentGrid();
|
2020-03-23 18:10:45 +01:00
|
|
|
}
|
|
|
|
|
2020-03-15 18:44:32 +01:00
|
|
|
isGameRunning(): boolean {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#gameRunning;
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-16 19:10:32 +01:00
|
|
|
isGamePaused(): boolean {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#gamePaused;
|
2020-03-16 19:10:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 18:10:45 +01:00
|
|
|
onFreeze() {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gridManager.freezeTetromino(this.#currentObject, this.#scoreManager);
|
2020-03-23 18:10:45 +01:00
|
|
|
this.createTetromino();
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-16 20:10:54 +01:00
|
|
|
setNewGameTick(level: number) {
|
2020-03-17 11:00:45 +01:00
|
|
|
if (level >= GameLogic.levelTicks.length)
|
2020-03-16 20:10:54 +01:00
|
|
|
return;
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gameTick = GameLogic.levelTicks[level];
|
|
|
|
clearInterval(this.#gameTickInterval);
|
|
|
|
this.#gameTickInterval = setInterval(this.#onTick, this.#gameTick);
|
2020-03-16 20:10:54 +01:00
|
|
|
}
|
|
|
|
|
2020-03-15 18:44:32 +01:00
|
|
|
onTick(callback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#currentObject.tryMove(0, 1,
|
|
|
|
this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(),
|
2020-03-23 11:32:50 +01:00
|
|
|
() => this.onFreeze());
|
2020-03-23 18:10:45 +01:00
|
|
|
callback(
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#scoreManager.getScore(),
|
|
|
|
this.#scoreManager.getLevel(),
|
|
|
|
this.#gridManager.getCurrentGrid());
|
|
|
|
if (this.#scoreManager.canLevelUp())
|
|
|
|
this.setNewGameTick(this.#scoreManager.getLevel());
|
2020-03-16 19:40:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
onClock(callback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gameTime++;
|
|
|
|
callback(this.#gameTime);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-16 19:10:32 +01:00
|
|
|
canUseInput() {
|
2020-03-28 12:08:08 +01:00
|
|
|
return this.#gameRunning && !this.#gamePaused
|
2020-03-16 19:10:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-15 18:44:32 +01:00
|
|
|
rightPressed(callback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#isPressedIn = true;
|
2020-03-17 00:24:57 +01:00
|
|
|
this.movePressedRepeat(true, callback, 1, 0);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-17 00:24:57 +01:00
|
|
|
leftPressedIn(callback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#isPressedIn = true;
|
2020-03-17 00:24:57 +01:00
|
|
|
this.movePressedRepeat(true, callback, -1, 0);
|
|
|
|
}
|
2020-03-16 19:10:32 +01:00
|
|
|
|
2020-03-17 00:24:57 +01:00
|
|
|
downPressedIn(callback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#isPressedIn = true;
|
2020-03-17 00:24:57 +01:00
|
|
|
this.movePressedRepeat(true, callback, 0, 1);
|
2020-03-16 19:26:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-17 00:24:57 +01:00
|
|
|
movePressedRepeat(isInitial: boolean, callback: Function, x: number, y: number) {
|
2020-03-28 12:08:08 +01:00
|
|
|
if (!this.canUseInput() || !this.#isPressedIn)
|
2020-03-16 19:26:42 +01:00
|
|
|
return;
|
2020-03-28 12:08:08 +01:00
|
|
|
const moved = this.#currentObject.tryMove(x, y,
|
|
|
|
this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(),
|
2020-03-23 11:32:50 +01:00
|
|
|
() => this.onFreeze());
|
|
|
|
if (moved) {
|
2020-03-17 00:24:57 +01:00
|
|
|
if (y === 1) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#scoreManager.incrementScore();
|
|
|
|
callback(this.#gridManager.getCurrentGrid(), this.#scoreManager.getScore());
|
2020-03-17 00:24:57 +01:00
|
|
|
} else
|
2020-03-28 12:08:08 +01:00
|
|
|
callback(this.#gridManager.getCurrentGrid());
|
2020-03-16 19:26:42 +01:00
|
|
|
}
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#pressInInterval = setTimeout(() =>
|
|
|
|
this.movePressedRepeat(false, callback, x, y),
|
|
|
|
isInitial ? this.#autoRepeatActivationDelay : this.#autoRepeatDelay
|
|
|
|
);
|
2020-03-17 00:24:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pressedOut() {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#isPressedIn = false;
|
|
|
|
clearTimeout(this.#pressInInterval);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-15 19:28:41 +01:00
|
|
|
rotatePressed(callback: Function) {
|
2020-03-16 19:10:32 +01:00
|
|
|
if (!this.canUseInput())
|
|
|
|
return;
|
|
|
|
|
2020-03-28 12:08:08 +01:00
|
|
|
if (this.#currentObject.tryRotate(this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
|
|
|
callback(this.#gridManager.getCurrentGrid());
|
2020-03-23 18:10:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getNextPiecesPreviews() {
|
|
|
|
let finalArray = [];
|
2020-03-28 12:08:08 +01:00
|
|
|
for (let i = 0; i < this.#nextPieces.length; i++) {
|
|
|
|
finalArray.push(this.#gridManager.getEmptyGrid(4, 4));
|
|
|
|
this.#nextPieces[i].toGrid(finalArray[i], true);
|
2020-03-23 18:10:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return finalArray;
|
2020-03-15 19:28:41 +01:00
|
|
|
}
|
|
|
|
|
2020-03-17 14:22:49 +01:00
|
|
|
recoverNextPiece() {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#currentObject = this.#nextPieces.shift();
|
2020-03-17 14:22:49 +01:00
|
|
|
this.generateNextPieces();
|
|
|
|
}
|
|
|
|
|
|
|
|
generateNextPieces() {
|
2020-03-28 12:08:08 +01:00
|
|
|
while (this.#nextPieces.length < this.#nextPiecesCount) {
|
|
|
|
this.#nextPieces.push(new Piece(this.#colors));
|
2020-03-17 14:22:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-15 18:44:32 +01:00
|
|
|
createTetromino() {
|
2020-03-17 00:24:57 +01:00
|
|
|
this.pressedOut();
|
2020-03-17 14:22:49 +01:00
|
|
|
this.recoverNextPiece();
|
2020-03-28 12:08:08 +01:00
|
|
|
if (!this.#currentObject.isPositionValid(this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
2020-03-16 19:10:32 +01:00
|
|
|
this.endGame(false);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-16 19:10:32 +01:00
|
|
|
togglePause() {
|
2020-03-28 12:08:08 +01:00
|
|
|
if (!this.#gameRunning)
|
2020-03-16 19:10:32 +01:00
|
|
|
return;
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gamePaused = !this.#gamePaused;
|
|
|
|
if (this.#gamePaused) {
|
|
|
|
clearInterval(this.#gameTickInterval);
|
|
|
|
clearInterval(this.#gameTimeInterval);
|
2020-03-16 19:10:32 +01:00
|
|
|
} else {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gameTickInterval = setInterval(this.#onTick, this.#gameTick);
|
|
|
|
this.#gameTimeInterval = setInterval(this.#onClock, 1000);
|
2020-03-16 19:10:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endGame(isRestart: boolean) {
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gameRunning = false;
|
|
|
|
this.#gamePaused = false;
|
|
|
|
clearInterval(this.#gameTickInterval);
|
|
|
|
clearInterval(this.#gameTimeInterval);
|
|
|
|
this.endCallback(this.#gameTime, this.#scoreManager.getScore(), isRestart);
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-16 19:40:52 +01:00
|
|
|
startGame(tickCallback: Function, clockCallback: Function, endCallback: Function) {
|
2020-03-28 12:08:08 +01:00
|
|
|
if (this.#gameRunning)
|
2020-03-16 19:10:32 +01:00
|
|
|
this.endGame(true);
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#gameRunning = true;
|
|
|
|
this.#gamePaused = false;
|
|
|
|
this.#gameTime = 0;
|
|
|
|
this.#scoreManager = new ScoreManager();
|
|
|
|
this.#gameTick = GameLogic.levelTicks[this.#scoreManager.getLevel()];
|
|
|
|
this.#gridManager = new GridManager(this.getWidth(), this.getHeight(), this.#colors);
|
|
|
|
this.#nextPieces = [];
|
2020-03-17 14:22:49 +01:00
|
|
|
this.generateNextPieces();
|
2020-03-15 18:44:32 +01:00
|
|
|
this.createTetromino();
|
2020-03-23 18:10:45 +01:00
|
|
|
tickCallback(
|
2020-03-28 12:08:08 +01:00
|
|
|
this.#scoreManager.getScore(),
|
|
|
|
this.#scoreManager.getLevel(),
|
|
|
|
this.#gridManager.getCurrentGrid());
|
|
|
|
clockCallback(this.#gameTime);
|
|
|
|
this.#onTick = this.onTick.bind(this, tickCallback);
|
|
|
|
this.#onClock = this.onClock.bind(this, clockCallback);
|
|
|
|
this.#gameTickInterval = setInterval(this.#onTick, this.#gameTick);
|
|
|
|
this.#gameTimeInterval = setInterval(this.#onClock, 1000);
|
2020-03-15 19:28:41 +01:00
|
|
|
this.endCallback = endCallback;
|
2020-03-15 18:44:32 +01:00
|
|
|
}
|
|
|
|
}
|