feat: improve piece function and add wall kick

This commit is contained in:
2021-07-14 13:54:39 -04:00
parent 668ec4a2aa
commit 7618260e1e
2 changed files with 93 additions and 58 deletions

View File

@@ -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"

View File

@@ -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()