application-amicale/screens/Tetris/GameLogic.js

316 lines
8.6 KiB
JavaScript
Raw Normal View History

2020-03-15 18:44:32 +01:00
// @flow
2020-03-23 11:32:50 +01:00
import Piece from "./Piece";
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,
];
static scoreLinesModifier = [40, 100, 300, 1200];
2020-03-16 20:10:54 +01:00
2020-03-15 18:44:32 +01:00
currentGrid: Array<Array<Object>>;
height: number;
width: number;
gameRunning: boolean;
2020-03-16 19:10:32 +01:00
gamePaused: boolean;
2020-03-15 18:44:32 +01:00
gameTime: number;
score: number;
2020-03-16 20:10:54 +01:00
level: number;
2020-03-15 18:44:32 +01:00
2020-03-23 11:32:50 +01:00
currentObject: Piece;
2020-03-15 18:44:32 +01:00
gameTick: number;
gameTickInterval: IntervalID;
2020-03-16 19:40:52 +01:00
gameTimeInterval: IntervalID;
2020-03-15 18:44:32 +01:00
2020-03-17 00:24:57 +01:00
pressInInterval: TimeoutID;
isPressedIn: boolean;
autoRepeatActivationDelay: number;
autoRepeatDelay: number;
2020-03-23 11:32:50 +01:00
nextPieces: Array<Piece>;
2020-03-17 14:22:49 +01:00
nextPiecesCount: number;
2020-03-17 00:24:57 +01:00
2020-03-15 18:44:32 +01:00
onTick: Function;
2020-03-16 19:40:52 +01:00
onClock: Function;
2020-03-15 19:28:41 +01:00
endCallback: Function;
2020-03-15 18:44:32 +01:00
2020-03-15 20:34:20 +01:00
colors: Object;
2020-03-17 11:00:45 +01:00
levelProgression: number;
2020-03-15 20:34:20 +01:00
constructor(height: number, width: number, colors: Object) {
2020-03-15 18:44:32 +01:00
this.height = height;
this.width = width;
this.gameRunning = false;
2020-03-16 19:10:32 +01:00
this.gamePaused = false;
2020-03-15 20:34:20 +01:00
this.colors = colors;
2020-03-17 00:24:57 +01:00
this.autoRepeatActivationDelay = 300;
2020-03-17 11:12:55 +01:00
this.autoRepeatDelay = 50;
2020-03-17 14:22:49 +01:00
this.nextPieces = [];
this.nextPiecesCount = 3;
}
2020-03-23 11:32:50 +01:00
getNextPiecesPreviews() {
2020-03-17 14:22:49 +01:00
let finalArray = [];
for (let i = 0; i < this.nextPieces.length; i++) {
finalArray.push(this.getEmptyGrid(4, 4));
2020-03-23 11:32:50 +01:00
this.nextPieces[i].toGrid(finalArray[i], true);
2020-03-17 14:22:49 +01:00
}
return finalArray;
2020-03-15 18:44:32 +01:00
}
getHeight(): number {
return this.height;
}
getWidth(): number {
return this.width;
}
isGameRunning(): boolean {
return this.gameRunning;
}
2020-03-16 19:10:32 +01:00
isGamePaused(): boolean {
return this.gamePaused;
}
2020-03-17 14:22:49 +01:00
getEmptyLine(width: number) {
2020-03-16 14:58:13 +01:00
let line = [];
2020-03-17 14:22:49 +01:00
for (let col = 0; col < width; col++) {
2020-03-16 14:58:13 +01:00
line.push({
color: this.colors.tetrisBackground,
isEmpty: true,
});
}
return line;
}
2020-03-17 14:22:49 +01:00
getEmptyGrid(height: number, width: number) {
2020-03-15 18:44:32 +01:00
let grid = [];
2020-03-17 14:22:49 +01:00
for (let row = 0; row < height; row++) {
grid.push(this.getEmptyLine(width));
2020-03-15 18:44:32 +01:00
}
return grid;
}
getGridCopy() {
return JSON.parse(JSON.stringify(this.currentGrid));
}
getFinalGrid() {
let finalGrid = this.getGridCopy();
2020-03-23 11:32:50 +01:00
this.currentObject.toGrid(finalGrid, false);
2020-03-15 18:44:32 +01:00
return finalGrid;
}
2020-03-17 11:00:45 +01:00
getLinesRemovedPoints(numberRemoved: number) {
if (numberRemoved < 1 || numberRemoved > 4)
return 0;
return GameLogic.scoreLinesModifier[numberRemoved-1] * (this.level + 1);
}
canLevelUp() {
2020-03-17 11:12:55 +01:00
let canLevel = this.levelProgression > this.level * 5;
if (canLevel)
this.levelProgression -= this.level * 5;
return canLevel;
2020-03-17 11:00:45 +01:00
}
2020-03-17 14:22:49 +01:00
freezeTetromino() {
2020-03-23 11:32:50 +01:00
this.currentObject.toGrid(this.currentGrid, false);
this.clearLines(this.getLinesToClear(this.currentObject.getCoordinates()));
2020-03-16 14:58:13 +01:00
}
clearLines(lines: Array<number>) {
lines.sort();
for (let i = 0; i < lines.length; i++) {
this.currentGrid.splice(lines[i], 1);
2020-03-17 14:22:49 +01:00
this.currentGrid.unshift(this.getEmptyLine(this.getWidth()));
2020-03-16 14:58:13 +01:00
}
2020-03-17 11:00:45 +01:00
switch (lines.length) {
case 1:
this.levelProgression += 1;
break;
case 2:
this.levelProgression += 3;
break;
case 3:
this.levelProgression += 5;
break;
case 4: // Did a tetris !
this.levelProgression += 8;
break;
}
this.score += this.getLinesRemovedPoints(lines.length);
2020-03-16 14:58:13 +01:00
}
getLinesToClear(coord: Object) {
let rows = [];
for (let i = 0; i < coord.length; i++) {
let isLineFull = true;
for (let col = 0; col < this.getWidth(); col++) {
if (this.currentGrid[coord[i].y][col].isEmpty) {
isLineFull = false;
break;
}
}
if (isLineFull && rows.indexOf(coord[i].y) === -1)
rows.push(coord[i].y);
}
return rows;
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;
this.gameTick = GameLogic.levelTicks[level];
clearInterval(this.gameTickInterval);
this.gameTickInterval = setInterval(this.onTick, this.gameTick);
}
2020-03-23 11:32:50 +01:00
onFreeze() {
this.freezeTetromino();
this.createTetromino();
}
2020-03-15 18:44:32 +01:00
onTick(callback: Function) {
2020-03-23 11:32:50 +01:00
this.currentObject.tryMove(0, 1,
this.currentGrid, this.getWidth(), this.getHeight(),
() => this.onFreeze());
2020-03-16 20:10:54 +01:00
callback(this.score, this.level, this.getFinalGrid());
2020-03-17 11:00:45 +01:00
if (this.canLevelUp()) {
2020-03-16 20:10:54 +01:00
this.level++;
this.setNewGameTick(this.level);
}
2020-03-16 19:40:52 +01:00
}
onClock(callback: Function) {
this.gameTime++;
callback(this.gameTime);
2020-03-15 18:44:32 +01:00
}
2020-03-16 19:10:32 +01:00
canUseInput() {
return this.gameRunning && !this.gamePaused
}
2020-03-15 18:44:32 +01:00
rightPressed(callback: Function) {
2020-03-17 00:24:57 +01:00
this.isPressedIn = true;
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) {
this.isPressedIn = true;
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) {
this.isPressedIn = true;
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) {
if (!this.canUseInput() || !this.isPressedIn)
2020-03-16 19:26:42 +01:00
return;
2020-03-23 11:32:50 +01:00
const moved = this.currentObject.tryMove(x, y,
this.currentGrid, this.getWidth(), this.getHeight(),
() => this.onFreeze());
if (moved) {
2020-03-17 00:24:57 +01:00
if (y === 1) {
this.score++;
callback(this.getFinalGrid(), this.score);
} else
callback(this.getFinalGrid());
2020-03-16 19:26:42 +01:00
}
2020-03-17 00:24:57 +01:00
this.pressInInterval = setTimeout(() => this.movePressedRepeat(false, callback, x, y), isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay);
}
pressedOut() {
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-23 11:32:50 +01:00
if (this.currentObject.tryRotate(this.currentGrid, this.getWidth(), this.getHeight()))
2020-03-16 19:26:42 +01:00
callback(this.getFinalGrid());
2020-03-15 19:28:41 +01:00
}
2020-03-17 14:22:49 +01:00
recoverNextPiece() {
this.currentObject = this.nextPieces.shift();
this.generateNextPieces();
}
generateNextPieces() {
while (this.nextPieces.length < this.nextPiecesCount) {
2020-03-23 11:32:50 +01:00
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-23 11:32:50 +01:00
if (!this.currentObject.isPositionValid(this.currentGrid, 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() {
if (!this.gameRunning)
return;
this.gamePaused = !this.gamePaused;
if (this.gamePaused) {
clearInterval(this.gameTickInterval);
2020-03-16 19:40:52 +01:00
clearInterval(this.gameTimeInterval);
2020-03-16 19:10:32 +01:00
} else {
this.gameTickInterval = setInterval(this.onTick, this.gameTick);
2020-03-16 19:40:52 +01:00
this.gameTimeInterval = setInterval(this.onClock, 1000);
2020-03-16 19:10:32 +01:00
}
}
endGame(isRestart: boolean) {
2020-03-15 19:28:41 +01:00
this.gameRunning = false;
2020-03-16 19:10:32 +01:00
this.gamePaused = false;
2020-03-15 18:44:32 +01:00
clearInterval(this.gameTickInterval);
2020-03-16 19:40:52 +01:00
clearInterval(this.gameTimeInterval);
2020-03-16 19:10:32 +01:00
this.endCallback(this.gameTime, this.score, 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-15 18:44:32 +01:00
if (this.gameRunning)
2020-03-16 19:10:32 +01:00
this.endGame(true);
2020-03-15 18:44:32 +01:00
this.gameRunning = true;
2020-03-16 19:10:32 +01:00
this.gamePaused = false;
2020-03-15 18:44:32 +01:00
this.gameTime = 0;
this.score = 0;
2020-03-17 11:00:45 +01:00
this.level = 0;
this.levelProgression = 0;
2020-03-16 20:10:54 +01:00
this.gameTick = GameLogic.levelTicks[this.level];
2020-03-17 14:22:49 +01:00
this.currentGrid = this.getEmptyGrid(this.getHeight(), this.getWidth());
this.nextPieces = [];
this.generateNextPieces();
2020-03-15 18:44:32 +01:00
this.createTetromino();
2020-03-16 20:10:54 +01:00
tickCallback(this.score, this.level, this.getFinalGrid());
2020-03-16 19:40:52 +01:00
clockCallback(this.gameTime);
2020-03-15 19:28:41 +01:00
this.onTick = this.onTick.bind(this, tickCallback);
2020-03-16 19:40:52 +01:00
this.onClock = this.onClock.bind(this, clockCallback);
2020-03-15 18:44:32 +01:00
this.gameTickInterval = setInterval(this.onTick, this.gameTick);
2020-03-16 19:40:52 +01:00
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
}
}