diff --git a/src/game/screens/tetris.py b/src/game/screens/tetris.py index 88d0edb..461e375 100644 --- a/src/game/screens/tetris.py +++ b/src/game/screens/tetris.py @@ -97,6 +97,7 @@ class Tetris(BaseScreen): self._handle_rotation_keys(keys) self._handle_down_key(keys) self._handle_drop_key(keys) + self._handle_pause_key(keys) def move_down(self) -> bool: """ @@ -161,6 +162,27 @@ class Tetris(BaseScreen): """ return self.tetromino.drop() + def restart(self) -> None: + """Restart the game.""" + logger.info(f"Restarting the game. Score was {self.score}") + self._reset_game_state() + + def pause(self) -> None: + """Pause the game.""" + self.paused: bool + if self.paused: + logger.debug("Unpause") + self.paused = False + self.timers.unpause() + else: + logger.debug("Pause") + self.paused = True + self.timers.pause() + + def mute(self) -> None: + """Mute the game.""" + self.landing_sound.set_volume(0) + def create_new_tetromino(self, shape: Optional[Figure] = None) -> Optional[Tetromino]: """Create a new tetromino and perform necessary actions.""" self._play_landing_sound() @@ -204,15 +226,6 @@ class Tetris(BaseScreen): return True return False - def restart(self) -> None: - """Restart the game.""" - logger.info(f"Restarting the game. Score was {self.score}") - self._reset_game_state() - - def mute(self) -> None: - """Mute the game.""" - self.landing_sound.set_volume(0) - def _draw_grid(self) -> None: """Draw the grid on the game surface.""" for col in range(1, CONFIG.game.columns): @@ -372,6 +385,7 @@ class Tetris(BaseScreen): self.score: int = 0 self.lines: int = 0 self.game_over = False + self.paused = False def _initialize_sound(self) -> None: """Initialize game sounds.""" @@ -467,6 +481,19 @@ class Tetris(BaseScreen): self.drop() self.timers.drop.activate() + def _handle_pause_key(self, keys: pygame.key.ScancodeWrapper) -> None: + """ + Handle the pause key. + + See `settings.toml` for the default key bindings. + """ + pause_keys = [pygame.key.key_code(key) for key in self.settings["General"]["pause"]] + pause_key_pressed = any(keys[key] for key in pause_keys) + + if pause_key_pressed and not self.timers.horizontal.active: + self.pause() + self.timers.horizontal.activate() + def _reset_game_state(self) -> None: """Reset the game state.""" self.sprites.empty() diff --git a/src/game/timer.py b/src/game/timer.py index 1df5ce1..55fc401 100644 --- a/src/game/timer.py +++ b/src/game/timer.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, NamedTuple, Optional +from typing import Any, Callable, Iterator, Optional import pygame from attrs import define, field @@ -55,7 +55,8 @@ class Timer: self.activate() -class Timers(NamedTuple): +@define +class Timers: """ NamedTuple for grouping different timers. @@ -70,3 +71,17 @@ class Timers(NamedTuple): horizontal: Timer rotation: Timer drop: Timer + + def __iter__(self) -> Iterator[Timer]: + """Returns an iterator over the timers.""" + return iter((self.vertical, self.horizontal, self.rotation, self.drop)) + + def pause(self) -> None: + """Pauses all timers.""" + for timer in self: + timer.deactivate() + + def unpause(self) -> None: + """Unpauses all timers.""" + for timer in self: + timer.activate()