diff --git a/config.yaml b/config.yaml index df28b14..b41f887 100644 --- a/config.yaml +++ b/config.yaml @@ -10,6 +10,7 @@ sound: piece-set: "resource/sound/piece_set_3.wav" image: + title-screen: "resource/image/title_screen.png" window-icon: "resource/image/tetris_icon.png" font: "resource/image/press-start-2p-font.bmp" diff --git a/main.py b/main.py index 62bc0f2..391a183 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ from tetris.game import Game from tetris.util import ConfigurationManager def main() -> None: - ConfigurationManager.load() + ConfigurationManager. init() game = Game() game.initialize() diff --git a/resource/image/title_screen.png b/resource/image/title_screen.png new file mode 100644 index 0000000..c5f0a35 Binary files /dev/null and b/resource/image/title_screen.png differ diff --git a/tetris/game.py b/tetris/game.py index 64b7090..8086ccf 100644 --- a/tetris/game.py +++ b/tetris/game.py @@ -7,7 +7,9 @@ from tetris.entity import PieceGenerator from tetris.entity import Well from tetris.entity import Stack from tetris.online import MultiplayerService +from tetris.scene import TitleScene +# TODO improve game assets https://www.spriters-resource.com/nes/tetris/ # TODO should be a singleton and refactor the whole file? class Game: @@ -16,15 +18,19 @@ class Game: self.tile_size = -1 self.screen = None self.clock = None - self.main_music = None + + self.current_scene = TitleScene() + + # In Game # self.current_piece = None self.next_piece = None self.well = None self.stack = None + self.main_music = None def initialize(self) -> None: pygame.init() - TextGenerator.load(ConfigurationManager.get("image", "font"), (20, 20)) + TextGenerator.init(ConfigurationManager.get("image", "font"), (20, 20)) win_width = ConfigurationManager.get("window", "width") win_height = ConfigurationManager.get("window", "height") @@ -50,25 +56,29 @@ class Game: # gets called from the games main loop def update(self) -> None: - # TODO write not initialized exception - elapsed_time = self.clock.tick(self.fps) - if not self.next_piece: - self.next_piece = PieceGenerator.get_piece((620, 160)) - - if self.current_piece: - self.current_piece.update(elapsed_time, self) + if self.current_scene: + self.current_scene.update() else: - self.current_piece = self.next_piece - self.current_piece.move((360 - 620, 100 - 160)) # TODO calculate spawn position correctly - self.next_piece = PieceGenerator.get_piece((620, 160)) # (360, 100) - if self.stack and self.current_piece.collide(self.stack): # TODO game over redo - pygame.quit() - MultiplayerService.quit() - sys.exit() + # TODO write not initialized exception + elapsed_time = self.clock.tick(self.fps) - if self.stack: - self.stack.update(elapsed_time, self) + if not self.next_piece: + self.next_piece = PieceGenerator.get_piece((620, 160)) + + if self.current_piece: + self.current_piece.update(elapsed_time, self) + else: + self.current_piece = self.next_piece + self.current_piece.move((360 - 620, 100 - 160)) # TODO calculate spawn position correctly + self.next_piece = PieceGenerator.get_piece((620, 160)) # (360, 100) + if self.stack and self.current_piece.collide(self.stack): # TODO game over redo + pygame.quit() + MultiplayerService.quit() + sys.exit() + + if self.stack: + self.stack.update(elapsed_time, self) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -79,31 +89,34 @@ class Game: def draw(self) -> None: # TODO write not initialized exception - # draw window bg - bg_color = pygame.Color(ConfigurationManager.get("color", "window-bg")) - self.screen.fill(bg_color) + if self.current_scene: + self.current_scene.draw(self.screen) + else: + # draw window bg + bg_color = pygame.Color(ConfigurationManager.get("color", "window-bg")) + self.screen.fill(bg_color) - # draw all game objects - if self.next_piece: - self.next_piece.draw(self.screen) - if self.well: - self.well.draw(self.screen) - if self.stack: - self.stack.draw(self.screen) - if self.current_piece: - self.current_piece.draw(self.screen, self.well, self.stack) - - score = str(self.score).zfill(6) - lines = str(self.stack.lines_completed_count).zfill(4) - level = str(self.get_level()).zfill(2) + # draw all game objects + if self.next_piece: + self.next_piece.draw(self.screen) + if self.well: + self.well.draw(self.screen) + if self.stack: + self.stack.draw(self.screen) + if self.current_piece: + self.current_piece.draw(self.screen, self.well, self.stack) + + score = str(self.score).zfill(6) + lines = str(self.stack.lines_completed_count).zfill(4) + level = str(self.get_level()).zfill(2) - TextGenerator.draw("Top", (80, 120), self.screen) - TextGenerator.draw("000000", (80, 140), self.screen) - TextGenerator.draw("Score", (80, 180), self.screen) - TextGenerator.draw(score, (80, 200), self.screen) - TextGenerator.draw("Lines " + lines, (300, 40), self.screen) - TextGenerator.draw("Next", (600, 120), self.screen) - TextGenerator.draw("LVL " + level, (600, 220), self.screen) + TextGenerator.draw("Top", (80, 120), self.screen) + TextGenerator.draw("000000", (80, 140), self.screen) + TextGenerator.draw("Score", (80, 180), self.screen) + TextGenerator.draw(score, (80, 200), self.screen) + TextGenerator.draw("Lines " + lines, (300, 40), self.screen) + TextGenerator.draw("Next", (600, 120), self.screen) + TextGenerator.draw("LVL " + level, (600, 220), self.screen) # update display pygame.display.update() diff --git a/tetris/scene.py b/tetris/scene.py new file mode 100644 index 0000000..55478f3 --- /dev/null +++ b/tetris/scene.py @@ -0,0 +1,33 @@ +import pygame +from tetris.util import ConfigurationManager +from tetris.util import TextGenerator +from tetris.util import Controller + +""" + TODO +""" +class TitleScene: + + # Title screen options + ONE_PLAYER = "ONE_PLAYER" + TWO_PLAYER = "TWO_PLAYER" + + def __init__(self) -> None: + image_path = ConfigurationManager.get("image", "title-screen") + + self.splash_image = pygame.image.load(image_path) + self.cursor_position = None + self.cursor_flash_timer = 0 + + def draw(self, screen) -> None: + screen.fill(pygame.Color(ConfigurationManager.get("color", "window-bg"))) + screen.blit(self.splash_image, (300, 0)) + + TextGenerator.draw("1 PLAYER", (300, 400), screen) + TextGenerator.draw("2 PLAYER", (300, 450), screen) + + def update(self) -> None: + if Controller.key_pressed(pygame.K_DOWN): + self.current_option = TitleScene.ONE_PLAYER + if Controller.key_pressed(pygame.K_UP): + self.current_option = TitleScene.TWO_PLAYER \ No newline at end of file diff --git a/tetris/util.py b/tetris/util.py index 80ae5d8..ed5c943 100644 --- a/tetris/util.py +++ b/tetris/util.py @@ -8,96 +8,96 @@ from typing import KeysView, Tuple class ConfigurationManager: CONFIG_FILE_LOCATION = "config.yaml" - configuration = [] + _configuration = [] @classmethod - def load(cls) -> None: + def init(cls) -> None: with open(cls.CONFIG_FILE_LOCATION, "r") as yaml_file: - cls.configuration = yaml.safe_load(yaml_file) + cls._configuration = yaml.safe_load(yaml_file) @classmethod def get(cls, key: str, sub_key: str = None): if sub_key: - return cls.configuration[key][sub_key] + return cls._configuration[key][sub_key] else: - return cls.configuration[key] + return cls._configuration[key] """ TODO description """ class TextGenerator: - sheet = None - glyph_size = (-1, -1) - characters = { } + _sheet = None + _glyph_size = (-1, -1) + _characters = { } @classmethod - def load(cls, file: str, glyph_size: Tuple) -> None: - cls.sheet = pygame.image.load(file) - cls.glyph_size = glyph_size + def init(cls, file: str, glyph_size: Tuple) -> None: + cls._sheet = pygame.image.load(file) + cls._glyph_size = glyph_size # load character positions in bitmap into the characters dictionary # letters - cls.characters["A"] = (9 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["B"] = (10 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["C"] = (11 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["D"] = (0 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["E"] = (1 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["F"] = (2 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["G"] = (3 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["H"] = (4 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["I"] = (5 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["J"] = (6 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["K"] = (7 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["L"] = (8 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["M"] = (9 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["N"] = (10 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["O"] = (11 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["P"] = (0 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Q"] = (1 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["R"] = (2 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["S"] = (3 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["T"] = (4 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["U"] = (5 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["V"] = (6 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["W"] = (7 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["X"] = (8 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Y"] = (9 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Z"] = (10 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["A"] = (9 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["B"] = (10 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["C"] = (11 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["D"] = (0 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["E"] = (1 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["F"] = (2 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["G"] = (3 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["H"] = (4 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["I"] = (5 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["J"] = (6 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["K"] = (7 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["L"] = (8 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["M"] = (9 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["N"] = (10 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["O"] = (11 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["P"] = (0 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Q"] = (1 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["R"] = (2 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["S"] = (3 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["T"] = (4 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["U"] = (5 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["V"] = (6 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["W"] = (7 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["X"] = (8 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Y"] = (9 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Z"] = (10 * glyph_size[0], 4 * glyph_size[1]) # numbers - cls.characters["0"] = (4 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["1"] = (5 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["2"] = (6 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["3"] = (7 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["4"] = (8 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["5"] = (9 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["6"] = (10 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["7"] = (11 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["8"] = (0 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["9"] = (1 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["0"] = (4 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["1"] = (5 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["2"] = (6 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["3"] = (7 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["4"] = (8 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["5"] = (9 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["6"] = (10 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["7"] = (11 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["8"] = (0 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["9"] = (1 * glyph_size[0], 2 * glyph_size[1]) @classmethod def draw(cls, text: str, position: Tuple, surface: pygame.Surface) -> None: x_position = 0 for char_ in text: if not char_.isspace(): - surface.blit(cls.sheet, (position[0] + x_position, position[1]), pygame.Rect(cls.characters[char_.upper()], (cls.glyph_size[0], cls.glyph_size[1]))) - x_position += cls.glyph_size[0] + surface.blit(cls._sheet, (position[0] + x_position, position[1]), pygame.Rect(cls._characters[char_.upper()], (cls._glyph_size[0], cls._glyph_size[1]))) + x_position += cls._glyph_size[0] """ TODO description """ class Controller: - keys_pressed = {} + _keys_pressed = {} @classmethod def key_down(cls, key: int) -> bool: - prior_pressed_state = False if key not in cls.keys_pressed else cls.keys_pressed[key] - cls.keys_pressed[key] = pygame.key.get_pressed()[key] + prior_pressed_state = False if key not in cls._keys_pressed else cls._keys_pressed[key] + cls._keys_pressed[key] = pygame.key.get_pressed()[key] - return cls.keys_pressed[key] and not prior_pressed_state + return cls._keys_pressed[key] and not prior_pressed_state @classmethod def key_pressed(cls, key: int) -> bool: