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.gravity_time = ConfigurationManager.configuration["engine"]["piece-gravity-time"] self.current_gravity_time = 0 self.set_time = ConfigurationManager.configuration["engine"]["piece-set-time"] self.current_set_time = 0 self.piece_set_sound = mixer.Channel(2) self.previous_points = None self.previous_center = None def update(self, elapsed_time: int, tetris) -> None: super().update(elapsed_time) well = tetris.well stack = tetris.stack tile_size = ConfigurationManager.configuration["engine"]["tile-size"] if self.elapsed_time >= self.gravity_time and self.current_set_time == 0: self.elapsed_time = 0 self.move((0, tile_size)) if well and self.collide(well) or stack and self.collide(stack): self.revert() self.current_set_time += elapsed_time if self.current_set_time > 0: self.current_set_time += elapsed_time if self.current_set_time >= self.set_time: self.__play_piece_set_sound() self.current_set_time = 0 stack.add_piece(self) tetris.current_piece = None 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: # reset elapsed time if user moves down to stall gravity if vector[1] > 0: self.elapsed_time = 0 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 sub_points in self.points: for point in sub_points: point[0] += vector[0] point[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 __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 return [int(center[0] * tile_size + position[0]), int(center[1] * tile_size + position[1])] # 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))