forked from vergnet/application-amicale
Improved game flow typing
This commit is contained in:
parent
494b319f19
commit
fe26ec0cc4
14 changed files with 164 additions and 197 deletions
|
@ -5,17 +5,21 @@ import {Alert, View} from 'react-native';
|
|||
import {IconButton, Text, withTheme} from 'react-native-paper';
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import GameLogic from "./GameLogic";
|
||||
import Grid from "./components/Grid";
|
||||
import type {Grid} from "./components/GridComponent";
|
||||
import GridComponent from "./components/GridComponent";
|
||||
import Preview from "./components/Preview";
|
||||
import i18n from "i18n-js";
|
||||
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
|
||||
import {StackNavigationProp} from "@react-navigation/stack";
|
||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomTheme,
|
||||
}
|
||||
|
||||
type State = {
|
||||
grid: Array<Array<Object>>,
|
||||
grid: Grid,
|
||||
gameRunning: boolean,
|
||||
gameTime: number,
|
||||
gameScore: number,
|
||||
|
@ -24,19 +28,11 @@ type State = {
|
|||
|
||||
class GameScreen extends React.Component<Props, State> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
logic: GameLogic;
|
||||
onTick: Function;
|
||||
onClock: Function;
|
||||
onGameEnd: Function;
|
||||
updateGrid: Function;
|
||||
updateGridScore: Function;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
this.logic = new GameLogic(20, 10, this.colors);
|
||||
this.logic = new GameLogic(20, 10, this.props.theme.colors);
|
||||
this.state = {
|
||||
grid: this.logic.getCurrentGrid(),
|
||||
gameRunning: false,
|
||||
|
@ -44,38 +40,32 @@ class GameScreen extends React.Component<Props, State> {
|
|||
gameScore: 0,
|
||||
gameLevel: 0,
|
||||
};
|
||||
this.onTick = this.onTick.bind(this);
|
||||
this.onClock = this.onClock.bind(this);
|
||||
this.onGameEnd = this.onGameEnd.bind(this);
|
||||
this.updateGrid = this.updateGrid.bind(this);
|
||||
this.updateGridScore = this.updateGridScore.bind(this);
|
||||
this.props.navigation.addListener('blur', this.onScreenBlur.bind(this));
|
||||
this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
|
||||
this.props.navigation.addListener('blur', this.onScreenBlur);
|
||||
this.props.navigation.addListener('focus', this.onScreenFocus);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const rightButton = this.getRightButton.bind(this);
|
||||
this.props.navigation.setOptions({
|
||||
headerRight: rightButton,
|
||||
headerRight: this.getRightButton,
|
||||
});
|
||||
this.startGame();
|
||||
}
|
||||
|
||||
getRightButton() {
|
||||
getRightButton = () => {
|
||||
return <MaterialHeaderButtons>
|
||||
<Item title="pause" iconName="pause" onPress={() => this.togglePause()}/>
|
||||
<Item title="pause" iconName="pause" onPress={this.togglePause}/>
|
||||
</MaterialHeaderButtons>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any interval on un-focus
|
||||
*/
|
||||
onScreenBlur() {
|
||||
onScreenBlur = () => {
|
||||
if (!this.logic.isGamePaused())
|
||||
this.logic.togglePause();
|
||||
}
|
||||
|
||||
onScreenFocus() {
|
||||
onScreenFocus = () => {
|
||||
if (!this.logic.isGameRunning())
|
||||
this.startGame();
|
||||
else if (this.logic.isGamePaused())
|
||||
|
@ -97,7 +87,7 @@ class GameScreen extends React.Component<Props, State> {
|
|||
return format;
|
||||
}
|
||||
|
||||
onTick(score: number, level: number, newGrid: Array<Array<Object>>) {
|
||||
onTick = (score: number, level: number, newGrid: Grid) => {
|
||||
this.setState({
|
||||
gameScore: score,
|
||||
gameLevel: level,
|
||||
|
@ -105,50 +95,50 @@ class GameScreen extends React.Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
onClock(time: number) {
|
||||
onClock = (time: number) => {
|
||||
this.setState({
|
||||
gameTime: time,
|
||||
});
|
||||
}
|
||||
|
||||
updateGrid(newGrid: Array<Array<Object>>) {
|
||||
updateGrid = (newGrid: Grid) => {
|
||||
this.setState({
|
||||
grid: newGrid,
|
||||
});
|
||||
}
|
||||
|
||||
updateGridScore(newGrid: Array<Array<Object>>, score: number) {
|
||||
updateGridScore = (newGrid: Grid, score: number) => {
|
||||
this.setState({
|
||||
grid: newGrid,
|
||||
gameScore: score,
|
||||
});
|
||||
}
|
||||
|
||||
togglePause() {
|
||||
togglePause = () => {
|
||||
this.logic.togglePause();
|
||||
if (this.logic.isGamePaused())
|
||||
this.showPausePopup();
|
||||
}
|
||||
|
||||
showPausePopup() {
|
||||
showPausePopup = () => {
|
||||
Alert.alert(
|
||||
i18n.t("screens.game.pause"),
|
||||
i18n.t("screens.game.pauseMessage"),
|
||||
[
|
||||
{text: i18n.t("screens.game.restart.text"), onPress: () => this.showRestartConfirm()},
|
||||
{text: i18n.t("screens.game.resume"), onPress: () => this.togglePause()},
|
||||
{text: i18n.t("screens.game.restart.text"), onPress: this.showRestartConfirm},
|
||||
{text: i18n.t("screens.game.resume"), onPress: this.togglePause},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
|
||||
showRestartConfirm() {
|
||||
showRestartConfirm = () => {
|
||||
Alert.alert(
|
||||
i18n.t("screens.game.restart.confirm"),
|
||||
i18n.t("screens.game.restart.confirmMessage"),
|
||||
[
|
||||
{text: i18n.t("screens.game.restart.confirmNo"), onPress: () => this.showPausePopup()},
|
||||
{text: i18n.t("screens.game.restart.confirmYes"), onPress: () => this.startGame()},
|
||||
{text: i18n.t("screens.game.restart.confirmNo"), onPress: this.showPausePopup},
|
||||
{text: i18n.t("screens.game.restart.confirmYes"), onPress: this.startGame},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
|
@ -163,20 +153,20 @@ class GameScreen extends React.Component<Props, State> {
|
|||
message,
|
||||
[
|
||||
{text: i18n.t("screens.game.gameOver.exit"), onPress: () => this.props.navigation.goBack()},
|
||||
{text: i18n.t("screens.game.restart.text"), onPress: () => this.startGame()},
|
||||
{text: i18n.t("screens.game.restart.text"), onPress: this.startGame},
|
||||
],
|
||||
{cancelable: false},
|
||||
);
|
||||
}
|
||||
|
||||
startGame() {
|
||||
startGame = () => {
|
||||
this.logic.startGame(this.onTick, this.onClock, this.onGameEnd);
|
||||
this.setState({
|
||||
gameRunning: true,
|
||||
});
|
||||
}
|
||||
|
||||
onGameEnd(time: number, score: number, isRestart: boolean) {
|
||||
onGameEnd = (time: number, score: number, isRestart: boolean) => {
|
||||
this.setState({
|
||||
gameTime: time,
|
||||
gameScore: score,
|
||||
|
@ -187,6 +177,7 @@ class GameScreen extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const colors = this.props.theme.colors;
|
||||
return (
|
||||
<View style={{
|
||||
width: '100%',
|
||||
|
@ -200,11 +191,11 @@ class GameScreen extends React.Component<Props, State> {
|
|||
}}>
|
||||
<MaterialCommunityIcons
|
||||
name={'timer'}
|
||||
color={this.colors.subtitle}
|
||||
color={colors.subtitle}
|
||||
size={20}/>
|
||||
<Text style={{
|
||||
marginLeft: 5,
|
||||
color: this.colors.subtitle
|
||||
color: colors.subtitle
|
||||
}}>{this.getFormattedTime(this.state.gameTime)}</Text>
|
||||
</View>
|
||||
<View style={{
|
||||
|
@ -215,7 +206,7 @@ class GameScreen extends React.Component<Props, State> {
|
|||
}}>
|
||||
<MaterialCommunityIcons
|
||||
name={'gamepad'}
|
||||
color={this.colors.text}
|
||||
color={colors.text}
|
||||
size={20}/>
|
||||
<Text style={{
|
||||
marginLeft: 5
|
||||
|
@ -228,20 +219,20 @@ class GameScreen extends React.Component<Props, State> {
|
|||
}}>
|
||||
<MaterialCommunityIcons
|
||||
name={'star'}
|
||||
color={this.colors.tetrisScore}
|
||||
color={colors.tetrisScore}
|
||||
size={30}/>
|
||||
<Text style={{
|
||||
marginLeft: 5,
|
||||
fontSize: 22,
|
||||
}}>{this.state.gameScore}</Text>
|
||||
</View>
|
||||
<Grid
|
||||
<GridComponent
|
||||
width={this.logic.getWidth()}
|
||||
height={this.logic.getHeight()}
|
||||
containerMaxHeight={'80%'}
|
||||
containerMaxWidth={'60%'}
|
||||
grid={this.state.grid}
|
||||
backgroundColor={this.colors.tetrisBackground}
|
||||
backgroundColor={colors.tetrisBackground}
|
||||
/>
|
||||
<View style={{
|
||||
position: 'absolute',
|
||||
|
@ -249,7 +240,7 @@ class GameScreen extends React.Component<Props, State> {
|
|||
right: 5,
|
||||
}}>
|
||||
<Preview
|
||||
next={this.logic.getNextPiecesPreviews()}
|
||||
items={this.logic.getNextPiecesPreviews()}
|
||||
/>
|
||||
</View>
|
||||
<View style={{
|
||||
|
@ -287,7 +278,7 @@ class GameScreen extends React.Component<Props, State> {
|
|||
onPressIn={() => this.logic.downPressedIn(this.updateGridScore)}
|
||||
onPress={() => this.logic.pressedOut()}
|
||||
style={{marginLeft: 'auto'}}
|
||||
color={this.colors.tetrisScore}
|
||||
color={colors.tetrisScore}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -2,39 +2,37 @@
|
|||
|
||||
import Piece from "./Piece";
|
||||
import ScoreManager from "./ScoreManager";
|
||||
import type {coordinates} from './Shapes/BaseShape';
|
||||
|
||||
|
||||
export type cell = {color: string, isEmpty: boolean, key: string};
|
||||
export type grid = Array<Array<cell>>;
|
||||
import type {Coordinates} from './Shapes/BaseShape';
|
||||
import type {Grid} from "./components/GridComponent";
|
||||
import type {Cell} from "./components/CellComponent";
|
||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||
|
||||
/**
|
||||
* Class used to manage the game grid
|
||||
*
|
||||
*/
|
||||
export default class GridManager {
|
||||
|
||||
#currentGrid: grid;
|
||||
#colors: Object;
|
||||
#currentGrid: Grid;
|
||||
#theme: CustomTheme;
|
||||
|
||||
/**
|
||||
* Initializes a grid of the given size
|
||||
*
|
||||
* @param width The grid width
|
||||
* @param height The grid height
|
||||
* @param colors Object containing current theme colors
|
||||
* @param theme Object containing current theme
|
||||
*/
|
||||
constructor(width: number, height: number, colors: Object) {
|
||||
this.#colors = colors;
|
||||
constructor(width: number, height: number, theme: CustomTheme) {
|
||||
this.#theme = theme;
|
||||
this.#currentGrid = this.getEmptyGrid(height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current grid
|
||||
*
|
||||
* @return {grid} The current grid
|
||||
* @return {Grid} The current grid
|
||||
*/
|
||||
getCurrentGrid(): grid {
|
||||
getCurrentGrid(): Grid {
|
||||
return this.#currentGrid;
|
||||
}
|
||||
|
||||
|
@ -42,13 +40,13 @@ export default class GridManager {
|
|||
* Get a new empty grid line of the given size
|
||||
*
|
||||
* @param width The line size
|
||||
* @return {Array<cell>}
|
||||
* @return {Array<Cell>}
|
||||
*/
|
||||
getEmptyLine(width: number): Array<cell> {
|
||||
getEmptyLine(width: number): Array<Cell> {
|
||||
let line = [];
|
||||
for (let col = 0; col < width; col++) {
|
||||
line.push({
|
||||
color: this.#colors.tetrisBackground,
|
||||
color: this.#theme.colors.tetrisBackground,
|
||||
isEmpty: true,
|
||||
key: col.toString(),
|
||||
});
|
||||
|
@ -61,9 +59,9 @@ export default class GridManager {
|
|||
*
|
||||
* @param width The grid width
|
||||
* @param height The grid height
|
||||
* @return {grid} A new empty grid
|
||||
* @return {Grid} A new empty grid
|
||||
*/
|
||||
getEmptyGrid(height: number, width: number): grid {
|
||||
getEmptyGrid(height: number, width: number): Grid {
|
||||
let grid = [];
|
||||
for (let row = 0; row < height; row++) {
|
||||
grid.push(this.getEmptyLine(width));
|
||||
|
@ -91,21 +89,21 @@ export default class GridManager {
|
|||
* Gets the lines to clear around the given piece's coordinates.
|
||||
* The piece's coordinates are used for optimization and to prevent checking the whole grid.
|
||||
*
|
||||
* @param coord The piece's coordinates to check lines at
|
||||
* @param pos The piece's coordinates to check lines at
|
||||
* @return {Array<number>} An array containing the line numbers to clear
|
||||
*/
|
||||
getLinesToClear(coord: Array<coordinates>): Array<number> {
|
||||
getLinesToClear(pos: Array<Coordinates>): Array<number> {
|
||||
let rows = [];
|
||||
for (let i = 0; i < coord.length; i++) {
|
||||
for (let i = 0; i < pos.length; i++) {
|
||||
let isLineFull = true;
|
||||
for (let col = 0; col < this.#currentGrid[coord[i].y].length; col++) {
|
||||
if (this.#currentGrid[coord[i].y][col].isEmpty) {
|
||||
for (let col = 0; col < this.#currentGrid[pos[i].y].length; col++) {
|
||||
if (this.#currentGrid[pos[i].y][col].isEmpty) {
|
||||
isLineFull = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isLineFull && rows.indexOf(coord[i].y) === -1)
|
||||
rows.push(coord[i].y);
|
||||
if (isLineFull && rows.indexOf(pos[i].y) === -1)
|
||||
rows.push(pos[i].y);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ import ShapeO from "./Shapes/ShapeO";
|
|||
import ShapeS from "./Shapes/ShapeS";
|
||||
import ShapeT from "./Shapes/ShapeT";
|
||||
import ShapeZ from "./Shapes/ShapeZ";
|
||||
import type {coordinates} from './Shapes/BaseShape';
|
||||
import type {grid} from './GridManager';
|
||||
import type {Coordinates} from './Shapes/BaseShape';
|
||||
import BaseShape from "./Shapes/BaseShape";
|
||||
import type {Grid} from "./components/GridComponent";
|
||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||
|
||||
/**
|
||||
* Class used as an abstraction layer for shapes.
|
||||
|
@ -24,26 +26,26 @@ export default class Piece {
|
|||
ShapeT,
|
||||
ShapeZ,
|
||||
];
|
||||
#currentShape: Object;
|
||||
#colors: Object;
|
||||
#currentShape: BaseShape;
|
||||
#theme: CustomTheme;
|
||||
|
||||
/**
|
||||
* Initializes this piece's color and shape
|
||||
*
|
||||
* @param colors Object containing current theme colors
|
||||
* @param theme Object containing current theme
|
||||
*/
|
||||
constructor(colors: Object) {
|
||||
this.#currentShape = this.getRandomShape(colors);
|
||||
this.#colors = colors;
|
||||
constructor(theme: CustomTheme) {
|
||||
this.#currentShape = this.getRandomShape(theme);
|
||||
this.#theme = theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random shape object
|
||||
*
|
||||
* @param colors Object containing current theme colors
|
||||
* @param theme Object containing current theme
|
||||
*/
|
||||
getRandomShape(colors: Object) {
|
||||
return new this.#shapes[Math.floor(Math.random() * 7)](colors);
|
||||
getRandomShape(theme: CustomTheme) {
|
||||
return new this.#shapes[Math.floor(Math.random() * 7)](theme);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,13 +53,13 @@ export default class Piece {
|
|||
*
|
||||
* @param grid The grid to remove the piece from
|
||||
*/
|
||||
removeFromGrid(grid: grid) {
|
||||
const coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(true);
|
||||
for (let i = 0; i < coord.length; i++) {
|
||||
grid[coord[i].y][coord[i].x] = {
|
||||
color: this.#colors.tetrisBackground,
|
||||
removeFromGrid(grid: Grid) {
|
||||
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
|
||||
for (let i = 0; i < pos.length; i++) {
|
||||
grid[pos[i].y][pos[i].x] = {
|
||||
color: this.#theme.colors.tetrisBackground,
|
||||
isEmpty: true,
|
||||
key: grid[coord[i].y][coord[i].x].key
|
||||
key: grid[pos[i].y][pos[i].x].key
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -68,13 +70,13 @@ export default class Piece {
|
|||
* @param grid The grid to add the piece to
|
||||
* @param isPreview Should we use this piece's current position to determine the cells?
|
||||
*/
|
||||
toGrid(grid: grid, isPreview: boolean) {
|
||||
const coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(!isPreview);
|
||||
for (let i = 0; i < coord.length; i++) {
|
||||
grid[coord[i].y][coord[i].x] = {
|
||||
toGrid(grid: Grid, isPreview: boolean) {
|
||||
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(!isPreview);
|
||||
for (let i = 0; i < pos.length; i++) {
|
||||
grid[pos[i].y][pos[i].x] = {
|
||||
color: this.#currentShape.getColor(),
|
||||
isEmpty: false,
|
||||
key: grid[coord[i].y][coord[i].x].key
|
||||
key: grid[pos[i].y][pos[i].x].key
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -87,15 +89,15 @@ export default class Piece {
|
|||
* @param height The grid's height
|
||||
* @return {boolean} If the position is valid
|
||||
*/
|
||||
isPositionValid(grid: grid, width: number, height: number) {
|
||||
isPositionValid(grid: Grid, width: number, height: number) {
|
||||
let isValid = true;
|
||||
const coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(true);
|
||||
for (let i = 0; i < coord.length; i++) {
|
||||
if (coord[i].x >= width
|
||||
|| coord[i].x < 0
|
||||
|| coord[i].y >= height
|
||||
|| coord[i].y < 0
|
||||
|| !grid[coord[i].y][coord[i].x].isEmpty) {
|
||||
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
|
||||
for (let i = 0; i < pos.length; i++) {
|
||||
if (pos[i].x >= width
|
||||
|| pos[i].x < 0
|
||||
|| pos[i].y >= height
|
||||
|| pos[i].y < 0
|
||||
|| !grid[pos[i].y][pos[i].x].isEmpty) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
|
@ -114,7 +116,7 @@ export default class Piece {
|
|||
* @param freezeCallback Callback to use if the piece should freeze itself
|
||||
* @return {boolean} True if the move was valid, false otherwise
|
||||
*/
|
||||
tryMove(x: number, y: number, grid: grid, width: number, height: number, freezeCallback: Function) {
|
||||
tryMove(x: number, y: number, grid: Grid, width: number, height: number, freezeCallback: () => void) {
|
||||
if (x > 1) x = 1; // Prevent moving from more than one tile
|
||||
if (x < -1) x = -1;
|
||||
if (y > 1) y = 1;
|
||||
|
@ -143,7 +145,7 @@ export default class Piece {
|
|||
* @param height The grid's height
|
||||
* @return {boolean} True if the rotation was valid, false otherwise
|
||||
*/
|
||||
tryRotate(grid: grid, width: number, height: number) {
|
||||
tryRotate(grid: Grid, width: number, height: number) {
|
||||
this.removeFromGrid(grid);
|
||||
this.#currentShape.rotate(true);
|
||||
if (!this.isPositionValid(grid, width, height)) {
|
||||
|
@ -158,9 +160,9 @@ export default class Piece {
|
|||
/**
|
||||
* Gets this piece used cells coordinates
|
||||
*
|
||||
* @return {Array<coordinates>} An array of coordinates
|
||||
* @return {Array<Coordinates>} An array of coordinates
|
||||
*/
|
||||
getCoordinates(): Array<coordinates> {
|
||||
getCoordinates(): Array<Coordinates> {
|
||||
return this.#currentShape.getCellsCoordinates(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// @flow
|
||||
|
||||
export type coordinates = {
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export type Coordinates = {
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
||||
|
||||
type Shape = Array<Array<number>>;
|
||||
|
||||
/**
|
||||
* Abstract class used to represent a BaseShape.
|
||||
* Abstract classes do not exist by default in Javascript: we force it by throwing errors in the constructor
|
||||
|
@ -12,16 +16,18 @@ export type coordinates = {
|
|||
*/
|
||||
export default class BaseShape {
|
||||
|
||||
#currentShape: Array<Array<number>>;
|
||||
#currentShape: Shape;
|
||||
#rotation: number;
|
||||
position: coordinates;
|
||||
position: Coordinates;
|
||||
theme: CustomTheme;
|
||||
|
||||
/**
|
||||
* Prevent instantiation if classname is BaseShape to force class to be abstract
|
||||
*/
|
||||
constructor() {
|
||||
constructor(theme: CustomTheme) {
|
||||
if (this.constructor === BaseShape)
|
||||
throw new Error("Abstract class can't be instantiated");
|
||||
this.theme = theme;
|
||||
this.#rotation = 0;
|
||||
this.position = {x: 0, y: 0};
|
||||
this.#currentShape = this.getShapes()[this.#rotation];
|
||||
|
@ -41,7 +47,7 @@ export default class BaseShape {
|
|||
*
|
||||
* Used by tests to read private fields
|
||||
*/
|
||||
getShapes(): Array<Array<Array<number>>> {
|
||||
getShapes(): Array<Shape> {
|
||||
throw new Error("Method 'getShapes()' must be implemented");
|
||||
}
|
||||
|
||||
|
@ -50,7 +56,7 @@ export default class BaseShape {
|
|||
*
|
||||
* Used by tests to read private fields
|
||||
*/
|
||||
getCurrentShape(): Array<Array<number>> {
|
||||
getCurrentShape(): Shape {
|
||||
return this.#currentShape;
|
||||
}
|
||||
|
||||
|
@ -59,9 +65,9 @@ export default class BaseShape {
|
|||
* This will return an array of coordinates representing the positions of the cells used by this object.
|
||||
*
|
||||
* @param isAbsolute Should we take into account the current position of the object?
|
||||
* @return {Array<coordinates>} This object cells coordinates
|
||||
* @return {Array<Coordinates>} This object cells coordinates
|
||||
*/
|
||||
getCellsCoordinates(isAbsolute: boolean): Array<coordinates> {
|
||||
getCellsCoordinates(isAbsolute: boolean): Array<Coordinates> {
|
||||
let coordinates = [];
|
||||
for (let row = 0; row < this.#currentShape.length; row++) {
|
||||
for (let col = 0; col < this.#currentShape[row].length; col++) {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeI extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisI;
|
||||
return this.theme.colors.tetrisI;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeJ extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisJ;
|
||||
return this.theme.colors.tetrisJ;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeL extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisL;
|
||||
return this.theme.colors.tetrisL;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeO extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 4;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisO;
|
||||
return this.theme.colors.tetrisO;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeS extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisS;
|
||||
return this.theme.colors.tetrisS;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeT extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisT;
|
||||
return this.theme.colors.tetrisT;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import BaseShape from "./BaseShape";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export default class ShapeZ extends BaseShape {
|
||||
|
||||
#colors: Object;
|
||||
|
||||
constructor(colors: Object) {
|
||||
super();
|
||||
constructor(theme: CustomTheme) {
|
||||
super(theme);
|
||||
this.position.x = 3;
|
||||
this.#colors = colors;
|
||||
}
|
||||
|
||||
getColor(): string {
|
||||
return this.#colors.tetrisZ;
|
||||
return this.theme.colors.tetrisZ;
|
||||
}
|
||||
|
||||
getShapes() {
|
||||
|
|
|
@ -3,28 +3,25 @@
|
|||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
|
||||
export type Cell = {color: string, isEmpty: boolean, key: string};
|
||||
|
||||
type Props = {
|
||||
item: Object
|
||||
cell: Cell,
|
||||
theme: CustomTheme,
|
||||
}
|
||||
|
||||
class Cell extends React.PureComponent<Props> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
}
|
||||
class CellComponent extends React.PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
const item = this.props.item;
|
||||
const item = this.props.cell;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: item.isEmpty ? 'transparent' : item.color,
|
||||
borderColor: item.isEmpty ? 'transparent' : this.colors.tetrisBorder,
|
||||
borderColor: item.isEmpty ? 'transparent' : this.props.theme.colors.tetrisBorder,
|
||||
borderStyle: 'solid',
|
||||
borderRadius: 2,
|
||||
borderWidth: 1,
|
||||
|
@ -38,4 +35,4 @@ class Cell extends React.PureComponent<Props> {
|
|||
|
||||
}
|
||||
|
||||
export default withTheme(Cell);
|
||||
export default withTheme(CellComponent);
|
|
@ -3,10 +3,12 @@
|
|||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import Cell from "./Cell";
|
||||
import type {Cell} from "./CellComponent";
|
||||
import CellComponent from "./CellComponent";
|
||||
|
||||
export type Grid = Array<Array<CellComponent>>;
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
grid: Array<Array<Object>>,
|
||||
backgroundColor: string,
|
||||
height: number,
|
||||
|
@ -15,14 +17,7 @@ type Props = {
|
|||
containerMaxWidth: number | string,
|
||||
}
|
||||
|
||||
class Grid extends React.Component<Props> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
}
|
||||
class GridComponent extends React.Component<Props> {
|
||||
|
||||
getRow(rowNumber: number) {
|
||||
let cells = this.props.grid[rowNumber].map(this.getCellRender);
|
||||
|
@ -39,8 +34,8 @@ class Grid extends React.Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
getCellRender = (item: Object) => {
|
||||
return <Cell item={item} key={item.key}/>;
|
||||
getCellRender = (item: Cell) => {
|
||||
return <CellComponent cell={item}/>;
|
||||
};
|
||||
|
||||
getGrid() {
|
||||
|
@ -67,4 +62,4 @@ class Grid extends React.Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withTheme(Grid);
|
||||
export default withTheme(GridComponent);
|
|
@ -3,33 +3,25 @@
|
|||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import Grid from "./Grid";
|
||||
import type {Grid} from "./GridComponent";
|
||||
import GridComponent from "./GridComponent";
|
||||
|
||||
type Props = {
|
||||
next: Object,
|
||||
items: Array<Grid>,
|
||||
}
|
||||
|
||||
class Preview extends React.PureComponent<Props> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
}
|
||||
|
||||
getGrids() {
|
||||
let grids = [];
|
||||
for (let i = 0; i < this.props.next.length; i++) {
|
||||
grids.push(
|
||||
this.getGridRender(this.props.next[i], i)
|
||||
);
|
||||
for (let i = 0; i < this.props.items.length; i++) {
|
||||
grids.push(this.getGridRender(this.props.items[i], i));
|
||||
}
|
||||
return grids;
|
||||
}
|
||||
|
||||
getGridRender(item: Object, index: number) {
|
||||
return <Grid
|
||||
getGridRender(item: Grid, index: number) {
|
||||
return <GridComponent
|
||||
width={item[0].length}
|
||||
height={item.length}
|
||||
grid={item}
|
||||
|
@ -41,7 +33,7 @@ class Preview extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
render() {
|
||||
if (this.props.next.length > 0) {
|
||||
if (this.props.items.length > 0) {
|
||||
return (
|
||||
<View>
|
||||
{this.getGrids()}
|
||||
|
|
Loading…
Reference in a new issue