Files
tetri5/tetri5/scene.py

405 lines
19 KiB
Python

import sys
import pygame
from types import FunctionType
from tetri5.util import ConfigurationManager
from tetri5.util import TextGenerator
from tetri5.util import Controller
from tetri5.util import SoundManager
from tetri5.entity import Well
from tetri5.entity import Stack
from tetri5.entity import PieceGenerator
from tetri5.online import MultiplayerService
"""
TODO
"""
class Scene:
pass
"""
TODO
"""
class TitleScene(Scene):
# Title screen options
ONE_PLAYER = "1 PLAYER"
TWO_PLAYER = "2 PLAYER"
def __init__(self, change_scene: FunctionType) -> None:
self._tile_size = ConfigurationManager.get("engine", "tile-size")
self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg"))
self._logo_image = pygame.image.load(ConfigurationManager.get("image", "title-screen"))
self._cursor_pos_one = ConfigurationManager.get("position", "cursor-option-one")
self._cursor_pos_two = ConfigurationManager.get("position", "cursor-option-two")
self._cursor_pos = self._cursor_pos_one
self._cursor_blink_interval = ConfigurationManager.get("engine", "cursor-blink-interval")
self._cursor_blink_time = 0
self._cursor_color = pygame.Color(ConfigurationManager.get("color", "cursor"))
self._cursor_off = False
self._logo_pos = ConfigurationManager.get("position", "title-logo")
self._option_one_pos = ConfigurationManager.get("position", "option-one")
self._option_two_pos = ConfigurationManager.get("position", "option-two")
self._is_multiplayer = False
self._change_scence = change_scene
def draw(self, surface: pygame.Surface) -> None:
surface.fill(self._background_color)
surface.blit(self._logo_image, self._logo_pos)
TextGenerator.draw(TitleScene.ONE_PLAYER, self._option_one_pos, surface)
TextGenerator.draw(TitleScene.TWO_PLAYER, self._option_two_pos, surface)
if self._cursor_off:
pygame.draw.circle(surface, self._cursor_color, self._cursor_pos, self._tile_size // 3)
def update(self, elapsed_time: int) -> None:
option_change = False
if Controller.key_pressed(pygame.K_UP) and self._is_multiplayer:
self._cursor_pos = self._cursor_pos_one
self._is_multiplayer = False
option_change = True
if Controller.key_pressed(pygame.K_DOWN) and not self._is_multiplayer:
self._cursor_pos = self._cursor_pos_two
self._is_multiplayer = True
option_change = True
if option_change:
SoundManager.play_option_change_sfx() # TODO add cool down
self._cursor_blink_time += elapsed_time
if self._cursor_blink_time >= self._cursor_blink_interval:
self._cursor_blink_time = 0
self._cursor_off = not self._cursor_off
if Controller.key_pressed(pygame.K_RETURN):
self._change_scence(SinglePlayerScene(self._change_scence) if not self._is_multiplayer else ConnectionScene(self._change_scence))
"""
TODO
"""
class SinglePlayerScene(Scene):
def __init__(self, change_scene: FunctionType) -> None:
self._top_label_pos = ConfigurationManager.get("position", "top-label")
self._top_value_pos = ConfigurationManager.get("position", "top-value")
self._score_label_pos = ConfigurationManager.get("position", "score-label")
self._score_value_pos = ConfigurationManager.get("position", "score-value")
self._lines_label_pos = ConfigurationManager.get("position", "lines-label")
self._next_label_pos = ConfigurationManager.get("position", "next-label")
self._level_label_pos = ConfigurationManager.get("position", "level-label")
self._next_piece_pos = ConfigurationManager.get("position", "next-piece")
self._spawn_piece_shift = ConfigurationManager.get("position", "spawn-piece")
self._tile_size = ConfigurationManager.get("engine", "tile-size")
self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg"))
self._points_table = ConfigurationManager.get("engine", "points-table")
self._lines_per_level = ConfigurationManager.get("engine", "lines-per-level")
self._score = 0
self._previous_level = 0
self._current_piece = None
self._next_piece = PieceGenerator.get_piece(self._next_piece_pos)
self._well = Well(ConfigurationManager.get("position", "well"),\
ConfigurationManager.get("color", "well-1"),\
ConfigurationManager.get("color", "well-border-1"))
self._stack = Stack()
self._change_scence = change_scene
SoundManager.play_theme_music()
def draw(self, surface: pygame.Surface) -> None:
surface.fill(self._background_color)
if self._stack:
self._stack.draw(surface)
if self._current_piece:
self._current_piece.draw(surface, self._well, self._stack)
if self._next_piece:
self._next_piece.draw(surface)
if self._well:
self._well.draw(surface)
score = str(self._score).zfill(6)
lines = str(self._stack.total_lines).zfill(4)
level = str(self._get_level()).zfill(2)
TextGenerator.draw("Top", self._top_label_pos, surface)
TextGenerator.draw("000000", self._top_value_pos, surface)
TextGenerator.draw("Score", self._score_label_pos, surface)
TextGenerator.draw(score, self._score_value_pos, surface)
TextGenerator.draw("Lines " + lines, self._lines_label_pos, surface)
TextGenerator.draw("Next", self._next_label_pos, surface)
TextGenerator.draw("LVL " + level, self._level_label_pos, surface)
def update(self, elapsed_time: int) -> None:
if self._current_piece:
self._current_piece.update(elapsed_time,\
self._well,\
self._stack,\
self._get_level(),\
self._clear_current_piece)
else:
self._current_piece = self._next_piece
self._current_piece.move(self._spawn_piece_shift)
self._next_piece = PieceGenerator.get_piece(self._next_piece_pos)
# TODO create game over scene
if self._stack and self._current_piece.collide(self._stack):
pygame.quit()
sys.exit()
if self._stack:
self._stack.update(elapsed_time)
self._score += self._points_table[self._stack.lines_completed_last] * (self._get_level() + 1)
if self._previous_level != self._get_level():
self._previous_level = self._get_level()
SoundManager.play_level_up_sfx()
def _get_level(self) -> int:
return 0 if self._stack is None else self._stack.total_lines // self._lines_per_level
def _clear_current_piece(self) -> None:
self._current_piece = None
class ConnectionScene(Scene):
MAX_GAME_ID_LEN = 5
MAX_PERIOD_COUNT = 3
def __init__(self, change_scene: FunctionType) -> None:
self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg"))
self._is_connecting = False
self._waiting_for_opponent = False
self._game_id_label_pos = ConfigurationManager.get("position", "game-id-label")
self._game_id_label = "Enter Game Id: "
self._game_id = "_"
self._cursor_blink_interval = ConfigurationManager.get("engine", "cursor-blink-interval")
self._cursor_blink_acc = 0 # blink accumulator
self._connecting_label_pos = ConfigurationManager.get("position", "connecting-label")
self._connecting_label = "Connecting"
self._waiting_for_opponent_label_pos = ConfigurationManager.get("position", "waiting-for-opponent-label")
self._waiting_for_opponent_label = "Waiting for opponent"
self._period_blink_interval = ConfigurationManager.get("engine", "period-blink-interval")
self._period_blink_acc = 0 # period accumulator
self._ping_interval = ConfigurationManager.get("engine", "ping-interval")
self._ping_acc = 0 # ping accumulator
self._change_scene = change_scene
def draw(self, surface: pygame.Surface) -> None:
surface.fill(self._background_color)
if not self._is_connecting and not self._waiting_for_opponent:
TextGenerator.draw(self._game_id_label + self._game_id, self._game_id_label_pos, surface)
elif self._is_connecting and not self._waiting_for_opponent:
TextGenerator.draw(self._connecting_label, self._connecting_label_pos, surface)
else:
TextGenerator.draw(self._waiting_for_opponent_label, self._waiting_for_opponent_label_pos, surface)
def update(self, elapsed_time: int) -> None:
# cursor blink logic
self._cursor_blink_acc += elapsed_time
if self._cursor_blink_acc >= self._cursor_blink_interval:
self._cursor_blink_acc = 0
if self._game_id.find("_") != -1:
self._game_id = self._game_id.replace("_", "")
else:
self._game_id += "_"
# period ellipsis logic for connecting
if self._is_connecting and not self._waiting_for_opponent:
self._period_blink_acc += elapsed_time
if self._period_blink_acc >= self._period_blink_interval:
self._period_blink_acc = 0
period_count = self._connecting_label.count(".")
if period_count < self.MAX_PERIOD_COUNT:
self._connecting_label += "."
else:
self._connecting_label = self._connecting_label.replace(".", "")
# period ellipsis logic for waiting on opponent
if self._waiting_for_opponent:
self._period_blink_acc += elapsed_time
if self._period_blink_acc >= self._period_blink_interval:
self._period_blink_acc = 0
period_count = self._waiting_for_opponent_label.count(".")
if period_count < self.MAX_PERIOD_COUNT:
self._waiting_for_opponent_label += "."
else:
self._waiting_for_opponent_label = self._waiting_for_opponent_label.replace(".", "")
# keyboard input
for event in pygame.event.get(pygame.KEYDOWN):
# user input logic
if not self._is_connecting:
self._game_id = self._game_id.replace("_", "")
if event.key == pygame.K_BACKSPACE:
self._game_id = self._game_id[:-1]
elif (event.unicode.isalpha() or event.unicode.isdigit()) and\
len(self._game_id) <= self.MAX_GAME_ID_LEN:
self._game_id += event.unicode.upper()
# connection logic
if event.key == pygame.K_RETURN and len(self._game_id) > 0 and not self._is_connecting:
self._is_connecting = True
MultiplayerService.init()
MultiplayerService.enter_game(self._game_id)
# server messaging logic
self._ping_acc += elapsed_time
message = MultiplayerService.try_receive_message();
if message: # TODO remove later, only for testing
print(message)
if message == MultiplayerService.WAIT_FOR_OPPONENT:
self._waiting_for_opponent = True
if message == MultiplayerService.START_GAME:
self._change_scene(MultiplayerScene(self._change_scene))
if message is None and self._ping_acc >= self._ping_interval:
self._ping_acc = 0
MultiplayerService.send_message("ping")
"""
TODO
"""
class MultiplayerScene(Scene):
def __init__(self, change_scene: FunctionType) -> None:
self._tile_size = ConfigurationManager.get("engine", "tile-size")
self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg"))
self._lines_per_level = ConfigurationManager.get("engine", "lines-per-level")
# wells init
self._well_player_one = Well(ConfigurationManager.get("position", "well-player-1"),\
ConfigurationManager.get("color", "well-1"),\
ConfigurationManager.get("color", "well-border-1"))
self._well_player_two = Well(ConfigurationManager.get("position", "well-player-2"),\
ConfigurationManager.get("color", "well-1"),\
ConfigurationManager.get("color", "well-border-1"))
# stacks init
self._stack_player_one = Stack()
self._stack_player_two = Stack()
# score positions
self._score_label_player_one_pos = ConfigurationManager.get("position", "score-label-player-1")
self._score_label_player_two_pos = ConfigurationManager.get("position", "score-label-player-2")
self._score_value_player_one_pos = ConfigurationManager.get("position", "score-value-player-1")
self._score_value_player_two_pos = ConfigurationManager.get("position", "score-value-player-2")
# lines positions
self._lines_label_player_one_pos = ConfigurationManager.get("position", "lines-label-player-1")
self._lines_label_player_two_pos = ConfigurationManager.get("position", "lines-label-player-2")
# next positions
self._next_label_player_one_pos = ConfigurationManager.get("position", "next-label-player-1")
self._next_label_player_two_pos = ConfigurationManager.get("position", "next-label-player-2")
# piece positions
self._next_piece_player_one_pos = ConfigurationManager.get("position", "next-piece-player-1")
self._next_piece_player_two_pos = ConfigurationManager.get("position", "next-piece-player-2")
self._spawn_piece_shift_player_one = ConfigurationManager.get("position", "spawn-piece-player-1")
self._spawn_piece_shift_player_two = ConfigurationManager.get("position", "spawn-piece-player-2")
# entities
self._next_piece_player_one = PieceGenerator.get_piece(self._next_piece_player_one_pos)
self._next_piece_player_two = PieceGenerator.get_piece(self._next_piece_player_two_pos, True)
self._current_piece_player_one = None
self._current_piece_player_two = None
self._change_scence = change_scene
MultiplayerService.init()
def draw(self, surface: pygame.Surface) -> None:
surface.fill(self._background_color)
# stacks
if self._stack_player_one is not None:
self._stack_player_one.draw(surface)
if self._stack_player_two is not None:
self._stack_player_two.draw(surface)
# pieces
if self._current_piece_player_one is not None:
self._current_piece_player_one.draw(surface, self._well_player_one, self._stack_player_one)
if self._current_piece_player_two is not None:
self._current_piece_player_two.draw(surface, self._well_player_two, self._stack_player_two)
if self._next_piece_player_one is not None:
self._next_piece_player_one.draw(surface)
if self._next_piece_player_two is not None:
self._next_piece_player_two.draw(surface)
# wells
if self._well_player_one is not None:
self._well_player_one.draw(surface)
if self._well_player_two is not None:
self._well_player_two.draw(surface)
# scores
TextGenerator.draw("Score", self._score_label_player_one_pos, surface)
TextGenerator.draw("000000", self._score_value_player_one_pos, surface)
TextGenerator.draw("Score", self._score_label_player_two_pos, surface)
TextGenerator.draw("000000", self._score_value_player_two_pos, surface)
# lines
TextGenerator.draw("Lines 0000", self._lines_label_player_one_pos, surface)
TextGenerator.draw("Lines 0000", self._lines_label_player_two_pos, surface)
# next
TextGenerator.draw("P1 NXT", self._next_label_player_one_pos, surface)
TextGenerator.draw("P2 NXT", self._next_label_player_two_pos, surface)
def update(self, elapsed_time: int) -> None:
self._update_piece_player_one(elapsed_time)
self._update_piece_player_two(elapsed_time)
if self._stack_player_one:
self._stack_player_one.update(elapsed_time)
def _update_piece_player_one(self, elapsed_time: int) -> None:
if self._current_piece_player_one is not None:
self._current_piece_player_one.update(elapsed_time,\
self._well_player_one,\
self._stack_player_one,\
self._get_level_player_one(),\
self._clear_current_piece_player_one)
else:
self._current_piece_player_one = self._next_piece_player_one
self._current_piece_player_one.move(self._spawn_piece_shift_player_one)
self._next_piece_player_one = PieceGenerator.get_piece(self._next_piece_player_one_pos)
# TODO create game over scene
if self._stack_player_one and self._current_piece_player_one.collide(self._stack_player_one):
pygame.quit()
MultiplayerService.quit()
sys.exit()
def _update_piece_player_two(self, elapsed_time: int) -> None:
if self._current_piece_player_two is not None:
self._current_piece_player_two.update(elapsed_time,\
self._well_player_two,\
self._stack_player_two,\
self._get_level_player_two(),\
self._clear_current_piece_player_two)
else:
self._current_piece_player_two = self._next_piece_player_two
self._current_piece_player_two.move(self._spawn_piece_shift_player_two)
self._next_piece_player_two = PieceGenerator.get_piece(self._next_piece_player_two_pos, True)
# TODO create game over scene
if self._stack_player_two and self._current_piece_player_two.collide(self._stack_player_two):
pygame.quit()
MultiplayerService.quit()
sys.exit()
def _get_level_player_one(self) -> int:
return 0 if self._stack_player_one is None else self._stack_player_one.total_lines // self._lines_per_level
def _get_level_player_two(self) -> int:
return 0 if self._stack_player_two is None else self._stack_player_two.total_lines // self._lines_per_level
def _clear_current_piece_player_one(self) -> None:
self._current_piece_player_one = None
def _clear_current_piece_player_two(self) -> None:
self._current_piece_player_two = None