Application Android et IOS pour l'amicale des élèves
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Piece.js 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import ShapeL from "../Shapes/ShapeL";
  2. import ShapeI from "../Shapes/ShapeI";
  3. import ShapeJ from "../Shapes/ShapeJ";
  4. import ShapeO from "../Shapes/ShapeO";
  5. import ShapeS from "../Shapes/ShapeS";
  6. import ShapeT from "../Shapes/ShapeT";
  7. import ShapeZ from "../Shapes/ShapeZ";
  8. import type {Coordinates} from '../Shapes/BaseShape';
  9. import BaseShape from "../Shapes/BaseShape";
  10. import type {Grid} from "../components/GridComponent";
  11. import type {CustomTheme} from "../../../managers/ThemeManager";
  12. /**
  13. * Class used as an abstraction layer for shapes.
  14. * Use this class to manipulate pieces rather than Shapes directly
  15. *
  16. */
  17. export default class Piece {
  18. #shapes = [
  19. ShapeL,
  20. ShapeI,
  21. ShapeJ,
  22. ShapeO,
  23. ShapeS,
  24. ShapeT,
  25. ShapeZ,
  26. ];
  27. #currentShape: BaseShape;
  28. #theme: CustomTheme;
  29. /**
  30. * Initializes this piece's color and shape
  31. *
  32. * @param theme Object containing current theme
  33. */
  34. constructor(theme: CustomTheme) {
  35. this.#currentShape = this.getRandomShape(theme);
  36. this.#theme = theme;
  37. }
  38. /**
  39. * Gets a random shape object
  40. *
  41. * @param theme Object containing current theme
  42. */
  43. getRandomShape(theme: CustomTheme) {
  44. return new this.#shapes[Math.floor(Math.random() * 7)](theme);
  45. }
  46. /**
  47. * Removes the piece from the given grid
  48. *
  49. * @param grid The grid to remove the piece from
  50. */
  51. removeFromGrid(grid: Grid) {
  52. const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
  53. for (let i = 0; i < pos.length; i++) {
  54. grid[pos[i].y][pos[i].x] = {
  55. color: this.#theme.colors.tetrisBackground,
  56. isEmpty: true,
  57. key: grid[pos[i].y][pos[i].x].key
  58. };
  59. }
  60. }
  61. /**
  62. * Adds this piece to the given grid
  63. *
  64. * @param grid The grid to add the piece to
  65. * @param isPreview Should we use this piece's current position to determine the cells?
  66. */
  67. toGrid(grid: Grid, isPreview: boolean) {
  68. const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(!isPreview);
  69. for (let i = 0; i < pos.length; i++) {
  70. grid[pos[i].y][pos[i].x] = {
  71. color: this.#currentShape.getColor(),
  72. isEmpty: false,
  73. key: grid[pos[i].y][pos[i].x].key
  74. };
  75. }
  76. }
  77. /**
  78. * Checks if the piece's current position is valid
  79. *
  80. * @param grid The current game grid
  81. * @param width The grid's width
  82. * @param height The grid's height
  83. * @return {boolean} If the position is valid
  84. */
  85. isPositionValid(grid: Grid, width: number, height: number) {
  86. let isValid = true;
  87. const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
  88. for (let i = 0; i < pos.length; i++) {
  89. if (pos[i].x >= width
  90. || pos[i].x < 0
  91. || pos[i].y >= height
  92. || pos[i].y < 0
  93. || !grid[pos[i].y][pos[i].x].isEmpty) {
  94. isValid = false;
  95. break;
  96. }
  97. }
  98. return isValid;
  99. }
  100. /**
  101. * Tries to move the piece by the given offset on the given grid
  102. *
  103. * @param x Position X offset
  104. * @param y Position Y offset
  105. * @param grid The grid to move the piece on
  106. * @param width The grid's width
  107. * @param height The grid's height
  108. * @param freezeCallback Callback to use if the piece should freeze itself
  109. * @return {boolean} True if the move was valid, false otherwise
  110. */
  111. tryMove(x: number, y: number, grid: Grid, width: number, height: number, freezeCallback: () => void) {
  112. if (x > 1) x = 1; // Prevent moving from more than one tile
  113. if (x < -1) x = -1;
  114. if (y > 1) y = 1;
  115. if (y < -1) y = -1;
  116. if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
  117. this.removeFromGrid(grid);
  118. this.#currentShape.move(x, y);
  119. let isValid = this.isPositionValid(grid, width, height);
  120. if (!isValid)
  121. this.#currentShape.move(-x, -y);
  122. let shouldFreeze = !isValid && y !== 0;
  123. this.toGrid(grid, false);
  124. if (shouldFreeze)
  125. freezeCallback();
  126. return isValid;
  127. }
  128. /**
  129. * Tries to rotate the piece
  130. *
  131. * @param grid The grid to rotate the piece on
  132. * @param width The grid's width
  133. * @param height The grid's height
  134. * @return {boolean} True if the rotation was valid, false otherwise
  135. */
  136. tryRotate(grid: Grid, width: number, height: number) {
  137. this.removeFromGrid(grid);
  138. this.#currentShape.rotate(true);
  139. if (!this.isPositionValid(grid, width, height)) {
  140. this.#currentShape.rotate(false);
  141. this.toGrid(grid, false);
  142. return false;
  143. }
  144. this.toGrid(grid, false);
  145. return true;
  146. }
  147. /**
  148. * Gets this piece used cells coordinates
  149. *
  150. * @return {Array<Coordinates>} An array of coordinates
  151. */
  152. getCoordinates(): Array<Coordinates> {
  153. return this.#currentShape.getCellsCoordinates(true);
  154. }
  155. }