import contextlib from functools import partial import sys from typing import Callable, NoReturn import pygame from ..color import Color from ..controller import Input, KeyboardController, JoystickController from .base_screen import Screen from ..player import Player, WouldCollide from ..misc import Pause def handle_input_pressed(instance, players: list[Player], event: pygame.event.Event) -> None: for player in players: if isinstance(player.controller, instance): player.handle_input_pressed(event) def handle_input_released(instance, players: list[Player], event: pygame.event.Event) -> None: for player in players: if isinstance(player.controller, instance): player.handle_input_released(event) # Number of frames frames_per_gridcell = [48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1] right_pane_canvas = pygame.Surface((300, 1000)) right_pane_canvas.fill((255, 255, 255)) class InGame(Screen): def __init__(self, players: list[Player], screen: pygame.surface.Surface): self.players: list[Player] = players self.screen: pygame.surface.Surface = screen self.event_handler: dict[int, Callable[[pygame.event.Event], None]] = {} def exit(_) -> NoReturn: sys.exit() self.event_handler[pygame.QUIT] = exit self.event_handler[pygame.KEYDOWN] = partial(handle_input_pressed, KeyboardController, self.players) self.event_handler[pygame.KEYUP] = partial(handle_input_released, KeyboardController, self.players) self.event_handler[pygame.JOYBUTTONDOWN] = partial(handle_input_pressed, JoystickController, self.players) self.event_handler[pygame.JOYBUTTONUP] = partial(handle_input_released, JoystickController, self.players) def refresh_right_pane_canvas(self) -> None: for player in self.players: player.level_canvas.fill(Color.BLACK.value) player.score_canvas.fill(Color.BLACK.value) if pygame.font: score_font = pygame.font.Font(None, 56) player.score_canvas.blit(score_font.render(f"{player.score:08d}", True, Color.WHITE.value), (0, 0)) player.level_canvas.blit(score_font.render(f"{player.level:08d}", True, Color.WHITE.value), (0, 0)) right_pane_canvas.blit(player.score_canvas, (2, 10)) right_pane_canvas.blit(player.level_canvas, (2, 70)) right_pane_canvas.blit(player.piece_preview_canvas, (50, 200)) def refresh(self) -> None: for player in self.players: player.refresh_grid_canvas() self.screen.blit(player.grid_canvas, (0, 0)) self.refresh_right_pane_canvas() self.screen.blit(right_pane_canvas, (501, 0)) def oneframe(self) -> None: for player in self.players: player.piece_drop_frames += 1 try: for event in pygame.event.get(): with contextlib.suppress(KeyError): self.event_handler[event.type](event) except Pause: pygame.event.clear() raise for player in self.players: player.das += 1 if player.das == 16: with contextlib.suppress(WouldCollide): if player.controller.is_pressed(Input.MOVE_RIGHT): player.move_piece_right() if player.controller.is_pressed(Input.MOVE_LEFT): player.move_piece_left() player.das = 10 if player.pressing_down_countdown == 0: try: player.move_piece_down() except WouldCollide: player.lock_piece() player.pressing_down_countdown = 2 elif player.pressing_down_countdown is not None: player.pressing_down_countdown -= 1 if player.piece_drop_frames >= frames_per_gridcell[player.level - 1]: player.piece_drop_frames = 0 try: player.move_piece_down() except WouldCollide: player.lock_piece()