// @flow import Piece from "./Piece"; import ScoreManager from "./ScoreManager"; import GridManager from "./GridManager"; export default class GameLogic { static levelTicks = [ 1000, 800, 600, 400, 300, 200, 150, 100, ]; #scoreManager: ScoreManager; #gridManager: GridManager; #height: number; #width: number; #gameRunning: boolean; #gamePaused: boolean; #gameTime: number; #currentObject: Piece; #gameTick: number; #gameTickInterval: IntervalID; #gameTimeInterval: IntervalID; #pressInInterval: TimeoutID; #isPressedIn: boolean; #autoRepeatActivationDelay: number; #autoRepeatDelay: number; #nextPieces: Array; #nextPiecesCount: number; #onTick: Function; #onClock: Function; endCallback: Function; #colors: Object; constructor(height: number, width: number, colors: Object) { 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); } getHeight(): number { return this.#height; } getWidth(): number { return this.#width; } getCurrentGrid() { return this.#gridManager.getCurrentGrid(); } isGameRunning(): boolean { return this.#gameRunning; } isGamePaused(): boolean { return this.#gamePaused; } onFreeze() { this.#gridManager.freezeTetromino(this.#currentObject, this.#scoreManager); this.createTetromino(); } setNewGameTick(level: number) { if (level >= GameLogic.levelTicks.length) return; this.#gameTick = GameLogic.levelTicks[level]; clearInterval(this.#gameTickInterval); this.#gameTickInterval = setInterval(this.#onTick, this.#gameTick); } onTick(callback: Function) { this.#currentObject.tryMove(0, 1, this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(), () => this.onFreeze()); callback( this.#scoreManager.getScore(), this.#scoreManager.getLevel(), this.#gridManager.getCurrentGrid()); if (this.#scoreManager.canLevelUp()) this.setNewGameTick(this.#scoreManager.getLevel()); } onClock(callback: Function) { this.#gameTime++; callback(this.#gameTime); } canUseInput() { return this.#gameRunning && !this.#gamePaused } rightPressed(callback: Function) { this.#isPressedIn = true; this.movePressedRepeat(true, callback, 1, 0); } leftPressedIn(callback: Function) { this.#isPressedIn = true; this.movePressedRepeat(true, callback, -1, 0); } downPressedIn(callback: Function) { this.#isPressedIn = true; this.movePressedRepeat(true, callback, 0, 1); } movePressedRepeat(isInitial: boolean, callback: Function, x: number, y: number) { if (!this.canUseInput() || !this.#isPressedIn) return; const moved = this.#currentObject.tryMove(x, y, this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(), () => this.onFreeze()); if (moved) { if (y === 1) { this.#scoreManager.incrementScore(); callback(this.#gridManager.getCurrentGrid(), this.#scoreManager.getScore()); } else callback(this.#gridManager.getCurrentGrid()); } this.#pressInInterval = setTimeout(() => this.movePressedRepeat(false, callback, x, y), isInitial ? this.#autoRepeatActivationDelay : this.#autoRepeatDelay ); } pressedOut() { this.#isPressedIn = false; clearTimeout(this.#pressInInterval); } rotatePressed(callback: Function) { if (!this.canUseInput()) return; if (this.#currentObject.tryRotate(this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight())) callback(this.#gridManager.getCurrentGrid()); } getNextPiecesPreviews() { let finalArray = []; for (let i = 0; i < this.#nextPieces.length; i++) { finalArray.push(this.#gridManager.getEmptyGrid(4, 4)); this.#nextPieces[i].toGrid(finalArray[i], true); } return finalArray; } recoverNextPiece() { this.#currentObject = this.#nextPieces.shift(); this.generateNextPieces(); } generateNextPieces() { while (this.#nextPieces.length < this.#nextPiecesCount) { this.#nextPieces.push(new Piece(this.#colors)); } } createTetromino() { this.pressedOut(); this.recoverNextPiece(); if (!this.#currentObject.isPositionValid(this.#gridManager.getCurrentGrid(), this.getWidth(), this.getHeight())) this.endGame(false); } togglePause() { if (!this.#gameRunning) return; this.#gamePaused = !this.#gamePaused; if (this.#gamePaused) { clearInterval(this.#gameTickInterval); clearInterval(this.#gameTimeInterval); } else { this.#gameTickInterval = setInterval(this.#onTick, this.#gameTick); this.#gameTimeInterval = setInterval(this.#onClock, 1000); } } endGame(isRestart: boolean) { this.#gameRunning = false; this.#gamePaused = false; clearInterval(this.#gameTickInterval); clearInterval(this.#gameTimeInterval); this.endCallback(this.#gameTime, this.#scoreManager.getScore(), isRestart); } startGame(tickCallback: Function, clockCallback: Function, endCallback: Function) { if (this.#gameRunning) this.endGame(true); 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 = []; this.generateNextPieces(); this.createTetromino(); tickCallback( 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); this.endCallback = endCallback; } }