feat: improve piece function and add wall kick
This commit is contained in:
12
config.yaml
12
config.yaml
@@ -55,10 +55,12 @@ engine:
|
|||||||
fps: 60
|
fps: 60
|
||||||
tile-size: 20
|
tile-size: 20
|
||||||
cursor-blink-interval: 150
|
cursor-blink-interval: 150
|
||||||
piece-gravity-time: 600
|
|
||||||
piece-set-time: 600
|
|
||||||
piece-gravity-increase: 56
|
|
||||||
lines-per-level: 5
|
lines-per-level: 5
|
||||||
|
# piece
|
||||||
|
piece-drop-delay: 600
|
||||||
|
piece-lock-delay: 1000
|
||||||
|
piece-drop-delay-decrease: 56
|
||||||
|
# piece end
|
||||||
points-table:
|
points-table:
|
||||||
- 0 # 0 line
|
- 0 # 0 line
|
||||||
- 40 # 1 line
|
- 40 # 1 line
|
||||||
@@ -78,11 +80,11 @@ color:
|
|||||||
well-border-2: "#000000"
|
well-border-2: "#000000"
|
||||||
piece-1-player-1: "#1F37EC"
|
piece-1-player-1: "#1F37EC"
|
||||||
piece-2-player-1: "#5BDB57"
|
piece-2-player-1: "#5BDB57"
|
||||||
piece-3-player-1: "#FFFFFF"
|
piece-3-player-1: "#F5F5F5"
|
||||||
piece-inner-border-1-player-1: "#1F37EC"
|
piece-inner-border-1-player-1: "#1F37EC"
|
||||||
piece-1-player-2: "#F83801"
|
piece-1-player-2: "#F83801"
|
||||||
piece-2-player-2: "#7F7F7F"
|
piece-2-player-2: "#7F7F7F"
|
||||||
piece-3-player-2: "#FFFFFF"
|
piece-3-player-2: "#F5F5F5"
|
||||||
piece-inner-border-1-player-2: "#F83801"
|
piece-inner-border-1-player-2: "#F83801"
|
||||||
piece-outer-border-1: "#000000"
|
piece-outer-border-1: "#000000"
|
||||||
piece-ghost: "#9BFCF0"
|
piece-ghost: "#9BFCF0"
|
||||||
|
|||||||
137
tetri5/entity.py
137
tetri5/entity.py
@@ -25,7 +25,9 @@ class Entity:
|
|||||||
def draw(self, surface: pygame.Surface) -> None:
|
def draw(self, surface: pygame.Surface) -> None:
|
||||||
for square in self._points:
|
for square in self._points:
|
||||||
if self._color is not None:
|
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:
|
if self._border_color is not None:
|
||||||
pygame.draw.polygon(surface, pygame.Color(self._border_color), square, max(self._tile_size // 6, 1))
|
pygame.draw.polygon(surface, pygame.Color(self._border_color), square, max(self._tile_size // 6, 1))
|
||||||
|
|
||||||
@@ -90,36 +92,22 @@ class Piece(Entity):
|
|||||||
self._previous_points = None
|
self._previous_points = None
|
||||||
self._previous_center = None
|
self._previous_center = None
|
||||||
|
|
||||||
# Gravity
|
# Drop delay
|
||||||
self._gravity_time = ConfigurationManager.get("engine", "piece-gravity-time")
|
self._drop_delay = ConfigurationManager.get("engine", "piece-drop-delay") # in ms
|
||||||
self._current_gravity_time = 0
|
self._drop_delay_acc = 0 # acumulated drop delay, in ms
|
||||||
self._applying_gravity = True
|
self._applying_drop_delay = True
|
||||||
|
|
||||||
# Set
|
# Lock delay
|
||||||
self._set_time = ConfigurationManager.get("engine", "piece-set-time")
|
self._lock_delay = ConfigurationManager.get("engine", "piece-lock-delay") # in ms
|
||||||
self._current_set_time = 0
|
self._lock_delay_acc = 0 # acumulated lock delay, in ms
|
||||||
self._applying_set = False
|
self._applying_lock_delay = False
|
||||||
|
|
||||||
def update(self, elapsed_time: int, well: Well, stack: "Stack", level: int, clear_current_piece: FunctionType) -> None:
|
def update(self, elapsed_time: int, well: Well, stack: "Stack", level: int, clear_current_piece: FunctionType) -> None:
|
||||||
super().update(elapsed_time)
|
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
|
# handle rotation, left and right movement
|
||||||
if Controller.key_down(pygame.K_UP):
|
if Controller.key_down(pygame.K_UP):
|
||||||
self.rotate()
|
if self.rotate_with_wall_kick(well, stack):
|
||||||
if well and self.collide(well) or stack and self.collide(stack):
|
|
||||||
self.revert()
|
|
||||||
else:
|
|
||||||
SoundManager.play_piece_rotate_sfx()
|
SoundManager.play_piece_rotate_sfx()
|
||||||
if Controller.key_down(pygame.K_LEFT):
|
if Controller.key_down(pygame.K_LEFT):
|
||||||
self.move((-self._tile_size, 0))
|
self.move((-self._tile_size, 0))
|
||||||
@@ -131,28 +119,41 @@ class Piece(Entity):
|
|||||||
self.revert()
|
self.revert()
|
||||||
|
|
||||||
# handle soft drop movement and gravity based on level
|
# handle soft drop movement and gravity based on level
|
||||||
gravity_time = ConfigurationManager.get("engine", "piece-gravity-time")
|
start_drop_delay = ConfigurationManager.get("engine", "piece-drop-delay")
|
||||||
set_time = ConfigurationManager.get("engine", "piece-set-time")
|
start_lock_delay = ConfigurationManager.get("engine", "piece-lock-delay")
|
||||||
gravity_increase = ConfigurationManager.get("engine", "piece-gravity-increase")
|
drop_delay_decrease = ConfigurationManager.get("engine", "piece-drop-delay-decrease")
|
||||||
|
|
||||||
if Controller.key_pressed(pygame.K_DOWN):
|
if Controller.key_pressed(pygame.K_DOWN):
|
||||||
self._gravity_time = max(10, (gravity_time - (level * gravity_increase)) // 10)
|
self._drop_delay = max(10, (start_drop_delay - (level * drop_delay_decrease)) // 10)
|
||||||
self._set_time = max(10, set_time // 10)
|
self._set_time = max(10, start_lock_delay // 10)
|
||||||
if not Controller.key_pressed(pygame.K_DOWN):
|
if not Controller.key_pressed(pygame.K_DOWN):
|
||||||
self._gravity_time = gravity_time - (level * gravity_increase)
|
self._drop_delay = start_drop_delay - (level * drop_delay_decrease)
|
||||||
self._set_time = set_time
|
self._set_time = start_lock_delay
|
||||||
|
|
||||||
# handle hard drop
|
# handle hard drop
|
||||||
if Controller.key_down(pygame.K_SPACE):
|
if Controller.key_down(pygame.K_SPACE):
|
||||||
self._points = self._get_ghost_piece_points(well, stack)
|
self._points = self._get_ghost_piece_points(well, stack)
|
||||||
self._add_to_stack(stack, clear_current_piece)
|
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:
|
def draw(self, surface: pygame.Surface, well: Well = None, stack: "Stack" = None) -> None:
|
||||||
# ghost piece
|
# ghost piece
|
||||||
if well and stack:
|
if well and stack:
|
||||||
for square in self._get_ghost_piece_points(well, 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
|
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)
|
super().draw(surface)
|
||||||
|
|
||||||
# inner border piece
|
# 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))
|
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)
|
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))
|
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]+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]+4), pygame.Color(255, 255, 255))
|
||||||
surface.set_at((square[0][0]+4, square[0][1]+5), 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[0] += vector[0]
|
||||||
vertex[1] += vector[1]
|
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:
|
For more information on a rotation of a piece go here:
|
||||||
https://gamedev.stackexchange.com/questions/17974/how-to-rotate-blocks-in-tetris
|
https://gamedev.stackexchange.com/questions/17974/how-to-rotate-blocks-in-tetris
|
||||||
'''
|
'''
|
||||||
def rotate(self) -> None:
|
def _rotate(self, no_revert: bool = False) -> None:
|
||||||
self._previous_points = copy.deepcopy(self._points)
|
if not no_revert:
|
||||||
self._previous_center = copy.deepcopy(self._center)
|
self._previous_points = copy.deepcopy(self._points)
|
||||||
|
self._previous_center = copy.deepcopy(self._center)
|
||||||
|
|
||||||
new_points = []
|
new_points = []
|
||||||
for square in self._points:
|
for square in self._points:
|
||||||
@@ -200,11 +237,6 @@ class Piece(Entity):
|
|||||||
new_points.append([square[-1]] + square[0:-1])
|
new_points.append([square[-1]] + square[0:-1])
|
||||||
self._points = new_points
|
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
|
@classmethod
|
||||||
def _get_points(self, shape: Tuple, position: Tuple) -> List:
|
def _get_points(self, shape: Tuple, position: Tuple) -> List:
|
||||||
tile_size = ConfigurationManager.get("engine", "tile-size")
|
tile_size = ConfigurationManager.get("engine", "tile-size")
|
||||||
@@ -221,25 +253,26 @@ class Piece(Entity):
|
|||||||
center = shape[-1]
|
center = shape[-1]
|
||||||
return [int(center[0] * self._tile_size + position[0]), int(center[1] * self._tile_size + position[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:
|
def _apply_drop(self, elapsed_time: int, well: Well, stack: "Stack") -> bool:
|
||||||
self._current_gravity_time += elapsed_time
|
self._drop_delay_acc += elapsed_time
|
||||||
if self._current_gravity_time >= self._gravity_time:
|
|
||||||
self._current_gravity_time = 0
|
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):
|
if not self._entity_is_below(well) and not self._entity_is_below(stack):
|
||||||
self.move((0, self._tile_size))
|
self.move((0, self._tile_size))
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _apply_set(self, elapsed_time: int, well: Well, stack: "Stack", clear_current_piece: FunctionType) -> bool:
|
def _apply_lock(self, elapsed_time: int, well: Well, stack: "Stack", clear_current_piece: FunctionType) -> bool:
|
||||||
self._current_set_time += elapsed_time
|
self._lock_delay_acc += elapsed_time
|
||||||
if self._current_set_time >= self._set_time:
|
|
||||||
self._current_set_time = 0
|
if self._lock_delay_acc >= self._lock_delay:
|
||||||
if self._entity_is_below(well) or self._entity_is_below(stack):
|
self._add_to_stack(stack, clear_current_piece)
|
||||||
self._add_to_stack(stack, clear_current_piece)
|
return False
|
||||||
else:
|
|
||||||
return False
|
return self._entity_is_below(well) or self._entity_is_below(stack)
|
||||||
return True
|
|
||||||
|
|
||||||
def _add_to_stack(self, stack: "Stack", clear_current_piece: FunctionType) -> None:
|
def _add_to_stack(self, stack: "Stack", clear_current_piece: FunctionType) -> None:
|
||||||
SoundManager.play_piece_set_sfx()
|
SoundManager.play_piece_set_sfx()
|
||||||
|
|||||||
Reference in New Issue
Block a user