/* * Copyright 2020 Arnaud Vergnet * * This file is part of CAMPUS. * * CAMPUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CAMPUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CAMPUS. If not, see . */ // @flow import type {CustomThemeType} from '../../../managers/ThemeManager'; export type CoordinatesType = { x: number, y: number, }; export type ShapeType = Array>; /** * 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 * and in methods to implement */ export default class BaseShape { #currentShape: ShapeType; #rotation: number; position: CoordinatesType; theme: CustomThemeType; /** * Prevent instantiation if classname is BaseShape to force class to be abstract */ constructor(theme: CustomThemeType) { 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]; } /** * Gets this shape's color. * Must be implemented by child class */ // eslint-disable-next-line class-methods-use-this getColor(): string { throw new Error("Method 'getColor()' must be implemented"); } /** * Gets this object's all possible shapes as an array. * Must be implemented by child class. * * Used by tests to read private fields */ // eslint-disable-next-line class-methods-use-this getShapes(): Array { throw new Error("Method 'getShapes()' must be implemented"); } /** * Gets this object's current shape. */ getCurrentShape(): ShapeType { return this.#currentShape; } /** * Gets this object's coordinates. * 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} This object cells coordinates */ getCellsCoordinates(isAbsolute: boolean): Array { const coordinates = []; for (let row = 0; row < this.#currentShape.length; row += 1) { for (let col = 0; col < this.#currentShape[row].length; col += 1) { if (this.#currentShape[row][col] === 1) { if (isAbsolute) { coordinates.push({ x: this.position.x + col, y: this.position.y + row, }); } else coordinates.push({x: col, y: row}); } } } return coordinates; } /** * Rotate this object * * @param isForward Should we rotate clockwise? */ rotate(isForward: boolean) { if (isForward) this.#rotation += 1; else this.#rotation -= 1; if (this.#rotation > 3) this.#rotation = 0; else if (this.#rotation < 0) this.#rotation = 3; this.#currentShape = this.getShapes()[this.#rotation]; } /** * Move this object * * @param x Position X offset to add * @param y Position Y offset to add */ move(x: number, y: number) { this.position.x += x; this.position.y += y; } }