mirror of
https://github.com/kristoferssolo/Tetris.git
synced 2025-10-21 20:00:35 +00:00
feat(game): add phantoms
This commit is contained in:
parent
c58bf61603
commit
4bd4ff00ad
@ -22,8 +22,8 @@ class Main(BaseScreen, SceenElement, TextScreen):
|
|||||||
self._set_buttons()
|
self._set_buttons()
|
||||||
self._initialize_increment_height()
|
self._initialize_increment_height()
|
||||||
self.settings = read_settings()
|
self.settings = read_settings()
|
||||||
self.game_mode = mode
|
|
||||||
self.game: Optional[Game] = None
|
self.game: Optional[Game] = None
|
||||||
|
self.game_mode = mode
|
||||||
self.settings_screen: Optional[Settings] = None
|
self.settings_screen: Optional[Settings] = None
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
|
|||||||
@ -59,6 +59,7 @@ class Tetris(BaseScreen):
|
|||||||
self._initialize_grid_surface()
|
self._initialize_grid_surface()
|
||||||
self._initialize_field_and_tetromino()
|
self._initialize_field_and_tetromino()
|
||||||
self.tetromino: Tetromino
|
self.tetromino: Tetromino
|
||||||
|
self.phantom_tetromino: Tetromino
|
||||||
|
|
||||||
self._initialize_game_state()
|
self._initialize_game_state()
|
||||||
self._initialize_timers()
|
self._initialize_timers()
|
||||||
@ -76,12 +77,14 @@ class Tetris(BaseScreen):
|
|||||||
self.update()
|
self.update()
|
||||||
self._draw_background()
|
self._draw_background()
|
||||||
self.sprites.draw(self.surface)
|
self.sprites.draw(self.surface)
|
||||||
|
self.phantom_sprites.draw(self.surface)
|
||||||
self._draw_border()
|
self._draw_border()
|
||||||
self._draw_grid()
|
self._draw_grid()
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
self._update_display_surface()
|
self._update_display_surface()
|
||||||
self.sprites.update()
|
self.sprites.update()
|
||||||
|
self.phantom_sprites.update()
|
||||||
|
|
||||||
def handle_events(self) -> None:
|
def handle_events(self) -> None:
|
||||||
"""Handle player input events."""
|
"""Handle player input events."""
|
||||||
@ -108,7 +111,9 @@ class Tetris(BaseScreen):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the movement was successful, False otherwise.
|
True if the movement was successful, False otherwise.
|
||||||
"""
|
"""
|
||||||
return self.tetromino.move_horizontal(Direction.LEFT)
|
moved = self.tetromino.move_horizontal(Direction.LEFT)
|
||||||
|
self._update_phantom_position()
|
||||||
|
return moved
|
||||||
|
|
||||||
def move_right(self) -> bool:
|
def move_right(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -117,7 +122,10 @@ class Tetris(BaseScreen):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the movement was successful, False otherwise.
|
True if the movement was successful, False otherwise.
|
||||||
"""
|
"""
|
||||||
return self.tetromino.move_horizontal(Direction.RIGHT)
|
|
||||||
|
moved = self.tetromino.move_horizontal(Direction.RIGHT)
|
||||||
|
self._update_phantom_position()
|
||||||
|
return moved
|
||||||
|
|
||||||
def rotate(self) -> bool:
|
def rotate(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -126,7 +134,9 @@ class Tetris(BaseScreen):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the rotation was successful, False otherwise.
|
True if the rotation was successful, False otherwise.
|
||||||
"""
|
"""
|
||||||
return self.tetromino.rotate()
|
rotated = self.tetromino.rotate()
|
||||||
|
self._update_phantom_position()
|
||||||
|
return rotated
|
||||||
|
|
||||||
def rotate_reverse(self) -> bool:
|
def rotate_reverse(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -135,7 +145,9 @@ class Tetris(BaseScreen):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the rotation was successful, False otherwise.
|
True if the rotation was successful, False otherwise.
|
||||||
"""
|
"""
|
||||||
return self.tetromino.rotate(Rotation.COUNTER_CLOCKWISE)
|
rotated = self.tetromino.rotate(Rotation.COUNTER_CLOCKWISE)
|
||||||
|
self._update_phantom_position()
|
||||||
|
return rotated
|
||||||
|
|
||||||
def drop(self) -> bool:
|
def drop(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -150,6 +162,7 @@ class Tetris(BaseScreen):
|
|||||||
"""Create a new tetromino and perform necessary actions."""
|
"""Create a new tetromino and perform necessary actions."""
|
||||||
self._play_landing_sound()
|
self._play_landing_sound()
|
||||||
self._check_finished_rows()
|
self._check_finished_rows()
|
||||||
|
self.phantom_tetromino.kill()
|
||||||
|
|
||||||
self.game_over: bool = self._check_game_over()
|
self.game_over: bool = self._check_game_over()
|
||||||
if self.game_over:
|
if self.game_over:
|
||||||
@ -162,6 +175,15 @@ class Tetris(BaseScreen):
|
|||||||
shape or self.get_next_figure(),
|
shape or self.get_next_figure(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.phantom_tetromino = Tetromino(
|
||||||
|
self.phantom_sprites,
|
||||||
|
None,
|
||||||
|
self.field,
|
||||||
|
self.tetromino.figure,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
self.phantom_tetromino.drop()
|
||||||
|
|
||||||
return self.tetromino
|
return self.tetromino
|
||||||
|
|
||||||
def _check_game_over(self) -> bool:
|
def _check_game_over(self) -> bool:
|
||||||
@ -281,6 +303,7 @@ class Tetris(BaseScreen):
|
|||||||
def _draw_components(self) -> None:
|
def _draw_components(self) -> None:
|
||||||
"""Draw additional components like borders and grid."""
|
"""Draw additional components like borders and grid."""
|
||||||
self.sprites.draw(self.surface)
|
self.sprites.draw(self.surface)
|
||||||
|
self.phantom_sprites.draw(self.surface)
|
||||||
self._draw_border()
|
self._draw_border()
|
||||||
self._draw_grid()
|
self._draw_grid()
|
||||||
|
|
||||||
@ -296,6 +319,7 @@ class Tetris(BaseScreen):
|
|||||||
def _initialize_sprites(self) -> None:
|
def _initialize_sprites(self) -> None:
|
||||||
"""Initialize the game sprites."""
|
"""Initialize the game sprites."""
|
||||||
self.sprites: pygame.sprite.Group[Block] = pygame.sprite.Group()
|
self.sprites: pygame.sprite.Group[Block] = pygame.sprite.Group()
|
||||||
|
self.phantom_sprites: pygame.sprite.Group[Block] = pygame.sprite.Group()
|
||||||
|
|
||||||
def _initialize_grid_surface(self) -> None:
|
def _initialize_grid_surface(self) -> None:
|
||||||
"""Initialize the grid surface."""
|
"""Initialize the grid surface."""
|
||||||
@ -314,6 +338,15 @@ class Tetris(BaseScreen):
|
|||||||
self.field,
|
self.field,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.phantom_tetromino = Tetromino(
|
||||||
|
self.phantom_sprites,
|
||||||
|
None,
|
||||||
|
self.field,
|
||||||
|
self.tetromino.figure,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
self.phantom_tetromino.drop()
|
||||||
|
|
||||||
def _initialize_timers(self) -> None:
|
def _initialize_timers(self) -> None:
|
||||||
"""Initialize game timers."""
|
"""Initialize game timers."""
|
||||||
self.timers = Timers(
|
self.timers = Timers(
|
||||||
@ -356,6 +389,11 @@ class Tetris(BaseScreen):
|
|||||||
"""Update the display surface."""
|
"""Update the display surface."""
|
||||||
self.dispaly_surface.blit(self.surface, CONFIG.game.pos)
|
self.dispaly_surface.blit(self.surface, CONFIG.game.pos)
|
||||||
|
|
||||||
|
def _update_phantom_position(self) -> None:
|
||||||
|
new_positions = [block.pos.copy() for block in self.tetromino.blocks]
|
||||||
|
self.phantom_tetromino.update_block_positions(new_positions)
|
||||||
|
self.phantom_tetromino.drop()
|
||||||
|
|
||||||
def _draw_background(self) -> None:
|
def _draw_background(self) -> None:
|
||||||
"""Fill the game surface with background color."""
|
"""Fill the game surface with background color."""
|
||||||
self.surface.fill(CONFIG.colors.bg_float)
|
self.surface.fill(CONFIG.colors.bg_float)
|
||||||
@ -445,6 +483,7 @@ class Tetris(BaseScreen):
|
|||||||
def _reset_game_state(self) -> None:
|
def _reset_game_state(self) -> None:
|
||||||
"""Reset the game state."""
|
"""Reset the game state."""
|
||||||
self.sprites.empty()
|
self.sprites.empty()
|
||||||
|
self.phantom_sprites.empty()
|
||||||
self._initialize_field_and_tetromino()
|
self._initialize_field_and_tetromino()
|
||||||
self._initialize_game_state()
|
self._initialize_game_state()
|
||||||
self.update_score(self.lines, self.score, self.level)
|
self.update_score(self.lines, self.score, self.level)
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import numpy as np
|
|||||||
import pygame
|
import pygame
|
||||||
from utils import CONFIG, Rotation, Size
|
from utils import CONFIG, Rotation, Size
|
||||||
|
|
||||||
|
from game.log import log
|
||||||
|
|
||||||
|
|
||||||
class Block(pygame.sprite.Sprite):
|
class Block(pygame.sprite.Sprite):
|
||||||
"""
|
"""
|
||||||
@ -27,8 +29,10 @@ class Block(pygame.sprite.Sprite):
|
|||||||
group: pygame.sprite.Group,
|
group: pygame.sprite.Group,
|
||||||
pos: pygame.Vector2,
|
pos: pygame.Vector2,
|
||||||
color: str,
|
color: str,
|
||||||
|
phantom: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(group)
|
super().__init__(group)
|
||||||
|
self.phantom = phantom
|
||||||
self._initialize_image(color)
|
self._initialize_image(color)
|
||||||
self._initialize_positions(pos)
|
self._initialize_positions(pos)
|
||||||
|
|
||||||
@ -91,6 +95,8 @@ class Block(pygame.sprite.Sprite):
|
|||||||
color: Color of the block.
|
color: Color of the block.
|
||||||
"""
|
"""
|
||||||
self.image = pygame.Surface(CONFIG.game.cell)
|
self.image = pygame.Surface(CONFIG.game.cell)
|
||||||
|
if self.phantom:
|
||||||
|
self.image.set_alpha(75)
|
||||||
self.image.fill(color)
|
self.image.fill(color)
|
||||||
|
|
||||||
def _initialize_positions(self, pos: pygame.Vector2) -> None:
|
def _initialize_positions(self, pos: pygame.Vector2) -> None:
|
||||||
|
|||||||
@ -31,15 +31,17 @@ class Tetromino:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
group: pygame.sprite.Group,
|
group: pygame.sprite.Group,
|
||||||
create_new: Callable[[Optional[Figure]], "Tetromino"],
|
create_new: Optional[Callable[[Optional[Figure]], "Tetromino"]],
|
||||||
field: np.ndarray[Optional[Block], Any],
|
field: np.ndarray[Optional[Block], Any],
|
||||||
shape: Optional[Figure] = None,
|
shape: Optional[Figure] = None,
|
||||||
|
phantom: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.figure: Figure = self._generate_figure(shape)
|
self.figure: Figure = self._generate_figure(shape)
|
||||||
self.block_positions: list[pygame.Vector2] = self.figure.value.shape
|
self.block_positions: list[pygame.Vector2] = self.figure.value.shape
|
||||||
self.color: str = self.figure.value.color
|
self.color: str = self.figure.value.color
|
||||||
self.create_new = create_new
|
self.create_new = create_new
|
||||||
self.field = field
|
self.field = field
|
||||||
|
self.phantom = phantom
|
||||||
self.blocks = self._initialize_blocks(group)
|
self.blocks = self._initialize_blocks(group)
|
||||||
|
|
||||||
def move_down(self) -> bool:
|
def move_down(self) -> bool:
|
||||||
@ -59,6 +61,7 @@ class Tetromino:
|
|||||||
for block in self.blocks:
|
for block in self.blocks:
|
||||||
self.field[int(block.pos.y), int(block.pos.x)] = block
|
self.field[int(block.pos.y), int(block.pos.x)] = block
|
||||||
|
|
||||||
|
if self.create_new:
|
||||||
self.create_new(None)
|
self.create_new(None)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -102,7 +105,7 @@ class Tetromino:
|
|||||||
]
|
]
|
||||||
|
|
||||||
if self._are_new_positions_valid(new_positions):
|
if self._are_new_positions_valid(new_positions):
|
||||||
self._update_block_positions(new_positions)
|
self.update_block_positions(new_positions)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if any(pos.x < 0 for pos in new_positions):
|
if any(pos.x < 0 for pos in new_positions):
|
||||||
@ -112,10 +115,6 @@ class Tetromino:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def next_rotation(self) -> "Tetromino":
|
|
||||||
self.rotate()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def drop(self) -> bool:
|
def drop(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Drops the Tetromino to the bottom of the game field.
|
Drops the Tetromino to the bottom of the game field.
|
||||||
@ -130,6 +129,10 @@ class Tetromino:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def kill(self) -> None:
|
||||||
|
for block in self.blocks:
|
||||||
|
block.kill()
|
||||||
|
|
||||||
def check_collision(self, direction: Direction) -> bool:
|
def check_collision(self, direction: Direction) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if there is a collision in the given direction.
|
Checks if there is a collision in the given direction.
|
||||||
@ -181,7 +184,7 @@ class Tetromino:
|
|||||||
for block in self.blocks
|
for block in self.blocks
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_block_positions(self, new_positions: list[pygame.Vector2]) -> None:
|
def update_block_positions(self, new_positions: list[pygame.Vector2]) -> None:
|
||||||
"""
|
"""
|
||||||
Updates the positions of Tetromino blocks.
|
Updates the positions of Tetromino blocks.
|
||||||
|
|
||||||
@ -219,7 +222,7 @@ class Tetromino:
|
|||||||
List of initialized blocks.
|
List of initialized blocks.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
Block(group=group, pos=pos, color=self.color)
|
Block(group=group, pos=pos, color=self.color, phantom=self.phantom)
|
||||||
for pos in self.block_positions
|
for pos in self.block_positions
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user