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
|
||||
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"
|
||||
|
||||
139
tetri5/entity.py
139
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()
|
||||
|
||||
Reference in New Issue
Block a user