from typing import List, Tuple from pygame import mixer import pygame import copy from entity.Entity import Entity from util.ConfigurationManager import ConfigurationManager ''' For information on the Tetris piece Tetromino go here: https://tetris.fandom.com/wiki/Tetromino ''' class Piece(Entity): def __init__(self, shape: Tuple, position: Tuple, color: str, inner_border_color: str, border_color: str): super().__init__(self.__get_points(shape, position), color, border_color) self.inner_border_color = inner_border_color self.center = self.__get_center(shape, position) self.piece_set_sound = mixer.Channel(2) self.previous_points = None self.previous_center = None # Gravity self.gravity_time = ConfigurationManager.configuration["engine"]["piece-gravity-time"] self.current_gravity_time = 0 self.applying_gravity = True # Set self.set_time = ConfigurationManager.configuration["engine"]["piece-set-time"] self.current_set_time = 0 self.applying_set = False def update(self, elapsed_time: int, tetris) -> None: super().update(elapsed_time) if self.applying_gravity: self.applying_gravity = self.__apply_gravity(elapsed_time, tetris.well, tetris.stack) self.applying_set = not self.applying_gravity if self.applying_set: self.applying_set = self.__apply_set(elapsed_time, tetris) self.applying_gravity = not self.applying_set def draw(self, surface: pygame.Surface) -> None: super().draw(surface) tile_size = ConfigurationManager.configuration["engine"]["tile-size"] for square in self.points: if self.inner_border_color: vertex_one = (square[0][0] + (tile_size // 10), square[0][1] + (tile_size // 10)) vertex_two = (square[1][0] - (tile_size // 10), square[1][1] + (tile_size // 10)) vertex_three = (square[2][0] - (tile_size // 10), square[2][1] - (tile_size // 10)) vertex_four = (square[3][0] + (tile_size // 10), square[3][1] - (tile_size // 10)) new_square = (vertex_one, vertex_two, vertex_three, vertex_four) pygame.draw.polygon(surface, pygame.Color(self.inner_border_color), new_square, max(tile_size // 6, 1)) def move(self, vector: Tuple) -> None: self.previous_points = copy.deepcopy(self.points) self.previous_center = copy.deepcopy(self.center) self.center[0] += vector[0] self.center[1] += vector[1] for square in self.points: for vertex in square: vertex[0] += vector[0] vertex[1] += vector[1] ''' For more information on a rotation of a piece go here: https://gamedev.stackexchange.com/questions/17974/how-to-rotate-blocks-in-tetris ''' def rotate(self) -> None: self.previous_points = copy.deepcopy(self.points) self.previous_center = copy.deepcopy(self.center) new_points = [] for square in self.points: for vertex in square: h = vertex[0] - self.center[0] k = vertex[1] - self.center[1] vertex[0] = (k * -1) + self.center[0] vertex[1] = h + self.center[1] new_points.append([square[-1]] + square[0:-1]) self.points = new_points def revert(self) -> None: if self.previous_points and self.previous_center: self.points = self.previous_points self.center = self.previous_center def __mimic_move(self, vector: Tuple): # TODO figure out how to annotate return type as Piece mimic_points = copy.deepcopy(self.points) for square in mimic_points: for vertex in square: vertex[0] += vector[0] vertex[1] += vector[1] return mimic_points def __play_piece_set_sound(self) -> None: piece_set_sound_file = ConfigurationManager.configuration["sound"]["piece-set"] self.piece_set_sound.play(mixer.Sound(piece_set_sound_file)) def __get_points(self, shape: Tuple, position: Tuple) -> List: tile_size = ConfigurationManager.configuration["engine"]["tile-size"] points = [] for square in shape[:-1]: sub_points = [] for vertex in square: point = [vertex[0] * tile_size + position[0], vertex[1] * tile_size + position[1]] sub_points.append(point) points.append(sub_points) return points def __get_center(self, shape: Tuple, position: Tuple) -> List: tile_size = ConfigurationManager.configuration["engine"]["tile-size"] center = shape[-1] # cast to int and avoid exception from pygame (center can be a floating point) return [int(center[0] * tile_size + position[0]), int(center[1] * tile_size + position[1])] def __apply_gravity(self, elapsed_time, well, stack) -> bool: # TODO define well and stack type tile_size = ConfigurationManager.configuration["engine"]["tile-size"] self.current_gravity_time += elapsed_time if self.current_gravity_time >= self.gravity_time: self.current_gravity_time = 0 if not self.__entity_is_below(well) and not self.__entity_is_below(stack): self.move((0, tile_size)) else: return False return True def __apply_set(self, elapsed_time, tetris) -> bool: # TODO define tetris type self.current_set_time += elapsed_time if self.current_set_time >= self.set_time: self.current_set_time = 0 if self.__entity_is_below(tetris.well) or self.__entity_is_below(tetris.stack): self.__play_piece_set_sound() tetris.stack.add_piece(self) # TODO do on tetris object level? tetris.current_piece = None # TODO turn into a method else: return False return True def __entity_is_below(self, entity: Entity) -> bool: # TODO tile_size = ConfigurationManager.configuration["engine"]["tile-size"] mimic_points = self.__mimic_move((0, tile_size)) return entity and entity.collide_points(mimic_points) # shape attributes I_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((3, 0), (4, 0), (4, 1), (3, 1)), (2, 0)) J_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((2, 1), (3, 1), (3, 2), (2, 2)), (1.5, 0.5)) L_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((0, 1), (1, 1), (1, 2), (0, 2)), (1.5, 0.5)) O_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((0, 1), (1, 1), (1, 2), (0, 2)), (1, 1)) S_SHAPE = (((0, 1), (1, 1), (1, 2), (0, 2)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 0), (3, 0), (3, 1), (2, 1)), (1.5, 0.5)) T_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 0), (3, 0), (3, 1), (2, 1)), (1.5, 0.5)) Z_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 1), (3, 1), (3, 2), (2, 2)), (1.5, 0.5))