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