diff --git a/config.yaml b/config.yaml index e9f35aa..5b1193f 100644 --- a/config.yaml +++ b/config.yaml @@ -55,10 +55,12 @@ engine: fps: 60 tile-size: 20 cursor-blink-interval: 150 - piece-gravity-time: 600 - piece-set-time: 600 - piece-gravity-increase: 56 lines-per-level: 5 + # piece + piece-drop-delay: 600 + piece-lock-delay: 1000 + piece-drop-delay-decrease: 56 + # piece end points-table: - 0 # 0 line - 40 # 1 line @@ -78,11 +80,11 @@ color: well-border-2: "#000000" piece-1-player-1: "#1F37EC" piece-2-player-1: "#5BDB57" - piece-3-player-1: "#FFFFFF" + piece-3-player-1: "#F5F5F5" piece-inner-border-1-player-1: "#1F37EC" piece-1-player-2: "#F83801" piece-2-player-2: "#7F7F7F" - piece-3-player-2: "#FFFFFF" + piece-3-player-2: "#F5F5F5" piece-inner-border-1-player-2: "#F83801" piece-outer-border-1: "#000000" piece-ghost: "#9BFCF0" diff --git a/tetri5/entity.py b/tetri5/entity.py index 88c6bc1..e6e9463 100644 --- a/tetri5/entity.py +++ b/tetri5/entity.py @@ -25,7 +25,9 @@ class Entity: def draw(self, surface: pygame.Surface) -> None: for square in self._points: if self._color is not None: - pygame.draw.polygon(surface, pygame.Color(self._color), square, 0) + square_color = pygame.Color(self._color) + square_color.a = 255 + pygame.draw.polygon(surface, square_color, square, 0) if self._border_color is not None: pygame.draw.polygon(surface, pygame.Color(self._border_color), square, max(self._tile_size // 6, 1)) @@ -34,7 +36,7 @@ class Entity: for square_two in entity._points: for i in range(4): # 4 vertices if square_one[i][0] == square_two[i][0] and square_one[i][1] == square_two[i][1]: - return True + return True return False def _collide(self, points: List) -> bool: @@ -90,36 +92,22 @@ class Piece(Entity): self._previous_points = None self._previous_center = None - # Gravity - self._gravity_time = ConfigurationManager.get("engine", "piece-gravity-time") - self._current_gravity_time = 0 - self._applying_gravity = True + # Drop delay + self._drop_delay = ConfigurationManager.get("engine", "piece-drop-delay") # in ms + self._drop_delay_acc = 0 # acumulated drop delay, in ms + self._applying_drop_delay = True - # Set - self._set_time = ConfigurationManager.get("engine", "piece-set-time") - self._current_set_time = 0 - self._applying_set = False + # Lock delay + self._lock_delay = ConfigurationManager.get("engine", "piece-lock-delay") # in ms + self._lock_delay_acc = 0 # acumulated lock delay, in ms + self._applying_lock_delay = False def update(self, elapsed_time: int, well: Well, stack: "Stack", level: int, clear_current_piece: FunctionType) -> None: super().update(elapsed_time) - if self._applying_gravity: - self._applying_gravity = self._apply_gravity(elapsed_time, well, stack) - self._applying_set = not self._applying_gravity - - """ - For more information on the piece set logic go here: - https://strategywiki.org/wiki/Tetris/Features#Lock_delay - """ - if self._applying_set: - self._applying_set = self._apply_set(elapsed_time, well, stack, clear_current_piece) - self._applying_gravity = not self._applying_set # handle rotation, left and right movement if Controller.key_down(pygame.K_UP): - self.rotate() - if well and self.collide(well) or stack and self.collide(stack): - self.revert() - else: + if self.rotate_with_wall_kick(well, stack): SoundManager.play_piece_rotate_sfx() if Controller.key_down(pygame.K_LEFT): self.move((-self._tile_size, 0)) @@ -131,28 +119,41 @@ class Piece(Entity): self.revert() # handle soft drop movement and gravity based on level - gravity_time = ConfigurationManager.get("engine", "piece-gravity-time") - set_time = ConfigurationManager.get("engine", "piece-set-time") - gravity_increase = ConfigurationManager.get("engine", "piece-gravity-increase") + start_drop_delay = ConfigurationManager.get("engine", "piece-drop-delay") + start_lock_delay = ConfigurationManager.get("engine", "piece-lock-delay") + drop_delay_decrease = ConfigurationManager.get("engine", "piece-drop-delay-decrease") if Controller.key_pressed(pygame.K_DOWN): - self._gravity_time = max(10, (gravity_time - (level * gravity_increase)) // 10) - self._set_time = max(10, set_time // 10) + self._drop_delay = max(10, (start_drop_delay - (level * drop_delay_decrease)) // 10) + self._set_time = max(10, start_lock_delay // 10) if not Controller.key_pressed(pygame.K_DOWN): - self._gravity_time = gravity_time - (level * gravity_increase) - self._set_time = set_time + self._drop_delay = start_drop_delay - (level * drop_delay_decrease) + self._set_time = start_lock_delay # handle hard drop if Controller.key_down(pygame.K_SPACE): self._points = self._get_ghost_piece_points(well, stack) self._add_to_stack(stack, clear_current_piece) + if self._applying_drop_delay: + self._applying_drop_delay = self._apply_drop(elapsed_time, well, stack) + self._applying_lock_delay = not self._applying_drop_delay + + """ + For more information on the piece set logic go here: + https://strategywiki.org/wiki/Tetris/Features#Lock_delay + """ + if self._applying_lock_delay: + self._applying_lock_delay = self._apply_lock(elapsed_time, well, stack, clear_current_piece) + self._applying_drop_delay = not self._applying_lock_delay + def draw(self, surface: pygame.Surface, well: Well = None, stack: "Stack" = None) -> None: # ghost piece if well and stack: for square in self._get_ghost_piece_points(well, stack): pygame.draw.polygon(surface, pygame.Color(self._ghost_piece_color), square, max(self._tile_size // 6, 1)) # TODO add white to the yaml + super().draw(surface) # inner border piece @@ -164,6 +165,8 @@ class Piece(Entity): vertex_four = (square[3][0] + (self._tile_size // 10), square[3][1] - (self._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(self._tile_size // 6, 1)) + + # draw glimmer surface.set_at((square[0][0]+3, square[0][1]+3), pygame.Color(255, 255, 255)) surface.set_at((square[0][0]+4, square[0][1]+4), pygame.Color(255, 255, 255)) surface.set_at((square[0][0]+4, square[0][1]+5), pygame.Color(255, 255, 255)) @@ -181,13 +184,47 @@ class Piece(Entity): vertex[0] += vector[0] vertex[1] += vector[1] + def rotate_with_wall_kick(self, well: Well, stack: "Stack") -> bool: + self._rotate() + if well and self.collide(well) or stack and self.collide(stack): + self.revert() + + # trying kick to the left + self.move((-self._tile_size, 0)) + self._rotate(True) + if well and self.collide(well) or stack and self.collide(stack): + self.revert() + else: + # successful kick to the left + return True + + # trying kick to the right + self.move((self._tile_size, 0)) + self._rotate(True) + if well and self.collide(well) or stack and self.collide(stack): + self.revert() + else: + # successful kick to the right + return True + else: + return True + + # failed rotation + return False + + def revert(self) -> None: + if self._previous_points and self._previous_center: + self._points = self._previous_points + self._center = self._previous_center + ''' 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) + def _rotate(self, no_revert: bool = False) -> None: + if not no_revert: + self._previous_points = copy.deepcopy(self._points) + self._previous_center = copy.deepcopy(self._center) new_points = [] for square in self._points: @@ -200,11 +237,6 @@ class Piece(Entity): 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 - @classmethod def _get_points(self, shape: Tuple, position: Tuple) -> List: tile_size = ConfigurationManager.get("engine", "tile-size") @@ -221,25 +253,26 @@ class Piece(Entity): center = shape[-1] return [int(center[0] * self._tile_size + position[0]), int(center[1] * self._tile_size + position[1])] - def _apply_gravity(self, elapsed_time: int, well: Well, stack: "Stack") -> bool: - self._current_gravity_time += elapsed_time - if self._current_gravity_time >= self._gravity_time: - self._current_gravity_time = 0 + def _apply_drop(self, elapsed_time: int, well: Well, stack: "Stack") -> bool: + self._drop_delay_acc += elapsed_time + + if self._drop_delay_acc >= self._drop_delay: + self._drop_delay_acc = 0 if not self._entity_is_below(well) and not self._entity_is_below(stack): self.move((0, self._tile_size)) else: return False + return True - def _apply_set(self, elapsed_time: int, well: Well, stack: "Stack", clear_current_piece: FunctionType) -> bool: - self._current_set_time += elapsed_time - if self._current_set_time >= self._set_time: - self._current_set_time = 0 - if self._entity_is_below(well) or self._entity_is_below(stack): - self._add_to_stack(stack, clear_current_piece) - else: - return False - return True + def _apply_lock(self, elapsed_time: int, well: Well, stack: "Stack", clear_current_piece: FunctionType) -> bool: + self._lock_delay_acc += elapsed_time + + if self._lock_delay_acc >= self._lock_delay: + self._add_to_stack(stack, clear_current_piece) + return False + + return self._entity_is_below(well) or self._entity_is_below(stack) def _add_to_stack(self, stack: "Stack", clear_current_piece: FunctionType) -> None: SoundManager.play_piece_set_sfx()