diff --git a/tetri5/entity.py b/tetri5/entity.py index bb5a7ab..0a2a5e8 100644 --- a/tetri5/entity.py +++ b/tetri5/entity.py @@ -12,24 +12,23 @@ from tetri5.util import SoundManager """ class Entity: - def __init__(self, points: Tuple, color: str, border_color: str = None): + def __init__(self, points: Tuple, base_color: str, outer_color: str = None): self._tile_size = ConfigurationManager.get("engine", "tile-size") self._points = points - self._color = color - self._border_color = border_color - self._elapsed_time = 0 + self._base_color = base_color + self._outer_color = outer_color + self._elapsed_time = 0 # TODO does nothing right now def update(self, elapsed_time: int) -> None: self._elapsed_time += elapsed_time def draw(self, surface: pygame.Surface) -> None: for square in self._points: - if self._color is not None: - square_color = pygame.Color(self._color) - square_color.a = 255 + if self._base_color is not None: + square_color = pygame.Color(self._base_color) pygame.draw.polygon(surface, square_color, square, 0) - if self._border_color is not None: - pygame.draw.polygon(surface, pygame.Color(self._border_color), square, max(self._tile_size // 6, 1)) + if self._outer_color is not None: + pygame.draw.polygon(surface, pygame.Color(self._outer_color), square, max(self._tile_size // 6, 1)) def collide(self, entity: "Entity") -> bool: # TODO figure out how to do type hint for entity param of type Entity for square_one in self._points: @@ -55,8 +54,8 @@ class Well(Entity): WIDTH = 10 # standard tetris well width, should not be changed HEIGHT = 20 # standard tetris well height, should not be changed - def __init__(self, position: Tuple, color: str, border_color: str): - super().__init__(Well._get_points(position), color, border_color) + def __init__(self, position: Tuple, base_color: str, outer_color: str): + super().__init__(Well._get_points(position), base_color, outer_color) @classmethod def _get_points(cls, position: Tuple) -> List: @@ -84,10 +83,10 @@ class Well(Entity): ''' class Piece(Entity): - def __init__(self, shape: Tuple, position: Tuple, color: str, inner_border_color: str, border_color: str): - super().__init__(Piece._get_points(shape, position), color, border_color) - self._inner_border_color = inner_border_color - self._ghost_piece_color = ConfigurationManager.get("color", "piece-ghost") + def __init__(self, shape: Tuple, position: Tuple, base_color: str, inner_color: str, outer_color: str): + super().__init__(Piece._get_points(shape, position), base_color, outer_color) + self._inner_color = inner_color + self._ghost_piece_base_color = ConfigurationManager.get("color", "piece-ghost") self._center = self._get_center(shape, position) self._previous_points = None self._previous_center = None @@ -150,29 +149,35 @@ class Piece(Entity): def draw(self, surface: pygame.Surface, well: Well = None, stack: "Stack" = None, ghost_piece_off: bool = False) -> None: # ghost piece - if well and stack and not ghost_piece_off: + if well is not None and stack is not None and not ghost_piece_off: ghost_piece_points = self._get_ghost_piece_points(well, stack) if ghost_piece_points is not None: for square in ghost_piece_points: - pygame.draw.polygon(surface, pygame.Color(self._ghost_piece_color), square, max(self._tile_size // 6, 1)) # TODO add white to the yaml + pygame.draw.polygon(surface, pygame.Color(self._ghost_piece_base_color), square, max(self._tile_size // 6, 1)) # TODO add white to the yaml - super().draw(surface) - - # inner border piece for square in self._points: - if self._inner_border_color: + if self._base_color is not None: + square_color = pygame.Color(ConfigurationManager.get("color", self._base_color)) + pygame.draw.polygon(surface, square_color, square, 0) + if self._outer_color is not None: + pygame.draw.polygon(surface, pygame.Color(ConfigurationManager.get("color", self._outer_color)), square, max(self._tile_size // 6, 1)) + + for square in self._points: + # inner color border piece + if self._inner_color: vertex_one = (square[0][0] + (self._tile_size // 10), square[0][1] + (self._tile_size // 10)) vertex_two = (square[1][0] - (self._tile_size // 10), square[1][1] + (self._tile_size // 10)) vertex_three = (square[2][0] - (self._tile_size // 10), square[2][1] - (self._tile_size // 10)) vertex_four = (square[3][0] + (self._tile_size // 10), square[3][1] - (self._tile_size // 10)) new_square = (vertex_one, vertex_two, vertex_three, vertex_four) - pygame.draw.polygon(surface, pygame.Color(self._inner_border_color), new_square, max(self._tile_size // 6, 1)) + pygame.draw.polygon(surface, pygame.Color(ConfigurationManager.get("color", self._inner_color)), new_square, max(self._tile_size // 6, 1)) # draw glimmer - surface.set_at((square[0][0]+3, square[0][1]+3), pygame.Color(255, 255, 255)) - surface.set_at((square[0][0]+4, square[0][1]+4), pygame.Color(255, 255, 255)) - surface.set_at((square[0][0]+4, square[0][1]+5), pygame.Color(255, 255, 255)) - surface.set_at((square[0][0]+5, square[0][1]+4), pygame.Color(255, 255, 255)) + surface.set_at((square[0][0]+3, square[0][1]+3), "white") + surface.set_at((square[0][0]+4, square[0][1]+4), "white") + surface.set_at((square[0][0]+5, square[0][1]+5), "white") + surface.set_at((square[0][0]+4, square[0][1]+5), "white") + surface.set_at((square[0][0]+5, square[0][1]+4), "white") def move(self, vector: Tuple) -> None: self._previous_points = copy.deepcopy(self._points) @@ -339,7 +344,7 @@ class Stack(Entity): def __init__(self): super().__init__([], None, None) - self._square_designs = [] + self._square_colors = [] self.total_lines = 0 self.lines_completed_last = 0 @@ -351,28 +356,29 @@ class Stack(Entity): def draw(self, surface: pygame.Surface) -> None: for i in range(len(self._points)): square = self._points[i] - square_design = self._square_designs[i] + square_design = self._square_colors[i] if square_design.base_color is not None: - pygame.draw.polygon(surface, pygame.Color(square_design.base_color), square, 0) + pygame.draw.polygon(surface, pygame.Color(ConfigurationManager.get("color", square_design.base_color)), square, 0) if square_design.outer_color is not None: - pygame.draw.polygon(surface, pygame.Color(square_design.outer_color), square, max(self._tile_size // 6, 1)) + pygame.draw.polygon(surface, pygame.Color(ConfigurationManager.get("color", square_design.outer_color)), square, max(self._tile_size // 6, 1)) if square_design.inner_color is not None: vertex_one = (square[0][0] + (self._tile_size // 10), square[0][1] + (self._tile_size // 10)) vertex_two = (square[1][0] - (self._tile_size // 10), square[1][1] + (self._tile_size // 10)) vertex_three = (square[2][0] - (self._tile_size // 10), square[2][1] - (self._tile_size // 10)) vertex_four = (square[3][0] + (self._tile_size // 10), square[3][1] - (self._tile_size // 10)) new_square = (vertex_one, vertex_two, vertex_three, vertex_four) - pygame.draw.polygon(surface, pygame.Color(square_design.inner_color), new_square, max(self._tile_size // 6, 1)) + pygame.draw.polygon(surface, pygame.Color(ConfigurationManager.get("color", square_design.inner_color)), new_square, max(self._tile_size // 6, 1)) - # draw square glimmer + # draw glimmer surface.set_at((square[0][0]+3, square[0][1]+3), pygame.Color(255, 255, 255)) surface.set_at((square[0][0]+4, square[0][1]+4), pygame.Color(255, 255, 255)) + surface.set_at((square[0][0]+5, square[0][1]+5), pygame.Color(255, 255, 255)) surface.set_at((square[0][0]+4, square[0][1]+5), pygame.Color(255, 255, 255)) surface.set_at((square[0][0]+5, square[0][1]+4), pygame.Color(255, 255, 255)) def add_piece(self, piece: Piece) -> None: self._points += piece._points - self._square_designs += [SquareDesign(piece._color, piece._inner_border_color, piece._border_color) for _ in range(len(piece._points))] + self._square_colors += [SquareColor(piece._base_color, piece._inner_color, piece._outer_color) for _ in range(len(piece._points))] # TODO refactor into multiple functions def _complete_rows(self) -> int: @@ -400,7 +406,7 @@ class Stack(Entity): SoundManager.play_line_complete_sfx() new_points = [] - new_square_designs = [] + self.new_square_colors = [] for i in range(len(self._points)): square = self._points[i] if square not in squares_to_exclude: @@ -411,16 +417,16 @@ class Stack(Entity): ) vertex[1] += self._tile_size * distance_to_move new_points.append(square) - new_square_designs.append(self._square_designs[i]) + self.new_square_colors.append(self._square_colors[i]) self._points = new_points - self._square_designs = new_square_designs + self._square_colors = self.new_square_colors return len(rows_completed) """ TODO description """ -class SquareDesign: +class SquareColor: def __init__(self, base_color: str, inner_color: str, outer_color: str): self.base_color = base_color self.inner_color = inner_color @@ -438,13 +444,12 @@ class PieceGenerator: if len(cls._bucket) == 0: cls._generate_bucket(cls._bucket) - base_color, inner_border_color, outer_border_color = cls._get_piece_color() - return Piece(cls._get_piece_shape(cls._bucket.pop()), position, base_color, inner_border_color, outer_border_color) + base_color, inner_color, outer_color = cls._get_piece_color() + return Piece(cls._get_piece_shape(cls._bucket.pop()), position, base_color, inner_color, outer_color) @classmethod - def get_opponent_piece(cls) -> Piece: - base_color, inner_border_color, outer_border_color = cls._get_piece_color(True) - return Piece(Piece.Z_SHAPE, (-250, -250), base_color, inner_border_color, outer_border_color) + def get_opponent_piece(cls, base_color: str, inner_color: str, outer_color: str) -> Piece: + return Piece(Piece.Z_SHAPE, (-250, -250), base_color, inner_color, outer_color) @classmethod def _generate_bucket(cls, bucket: List) -> None: @@ -473,12 +478,11 @@ class PieceGenerator: return None @classmethod - def _get_piece_color(cls, is_player_two: bool = False) -> Tuple: + def _get_piece_color(cls) -> Tuple: random_number = random.randint(1, 3) - player_mod = "player-1" if not is_player_two else "player-2" - base_color = ConfigurationManager.get("color", "piece-" + str(random_number) + "-" + player_mod) - inner_border_color = None if random_number != 3 else ConfigurationManager.get("color", "piece-inner-border-1" + "-" + player_mod) - outer_border_color = ConfigurationManager.get("color", "piece-outer-border-1") + base_color = "piece-" + str(random_number) + "-player-1" + inner_color = None if random_number != 3 else "piece-inner-border-1-player-1" + outer_color = "piece-outer-border-1" - return (base_color, inner_border_color, outer_border_color) \ No newline at end of file + return (base_color, inner_color, outer_color) \ No newline at end of file diff --git a/tetri5/online.py b/tetri5/online.py index 8f65bc7..67b68ca 100644 --- a/tetri5/online.py +++ b/tetri5/online.py @@ -157,7 +157,7 @@ class _NetworkConnectionService(): async def _try_receive_message(cls) -> None: try: task = cls._pending_receive_task or asyncio.create_task(cls._websocket.recv()) - done, pending = await asyncio.wait({task}, timeout=4e-3) # TODO experiment with the timeout + done, pending = await asyncio.wait({task}, timeout=2e-3) # TODO experiment with the timeout if task in done: json_str = await task @@ -217,18 +217,22 @@ class _NetworkConnectionService(): # DTOs class PieceDto(): - def __init__(self, points: List, center: List) -> None: + def __init__(self, points: List, center: List, base_color: str, inner_color: str, outer_color: str) -> None: self.points = points self.center = center + self.base_color = base_color + self.inner_color = inner_color + self.outer_color = outer_color @staticmethod def create(data: Dict) -> "PieceDto": - return PieceDto(data["points"], data["center"]) + return PieceDto(data["points"], data["center"], data["base_color"], data["inner_color"], data["outer_color"]) class StackDto(): - def __init__(self, points: List) -> None: + def __init__(self, points: List, square_colors: List[Dict]) -> None: self.points = points + self.square_colors = square_colors @staticmethod def create(data: Dict) -> "StackDto": - return StackDto(data["points"]) \ No newline at end of file + return StackDto(data["points"], data["square_colors"]) \ No newline at end of file diff --git a/tetri5/scene.py b/tetri5/scene.py index 3c373a8..0149ac4 100644 --- a/tetri5/scene.py +++ b/tetri5/scene.py @@ -5,7 +5,7 @@ from tetri5.util import ConfigurationManager from tetri5.util import TextGenerator from tetri5.util import Controller from tetri5.util import SoundManager -from tetri5.entity import SquareDesign, Well +from tetri5.entity import SquareColor, Well from tetri5.entity import Stack from tetri5.entity import PieceGenerator from tetri5.online import * @@ -347,28 +347,24 @@ class MultiplayerScene(Scene): if self._stack_player_one is not None: self._stack_player_one.update(elapsed_time) - - if self._stack_player_two is not None: - # get opponent stack from server and modify for current client - opponent_stack = MultiplayerService.try_receive_stack() - if opponent_stack is not None: - for square in opponent_stack.points: - for vertex in square: - vertex[0] += 400 - - if len(opponent_stack.points) > len(self._stack_player_two._points): - for _ in range(len(opponent_stack.points) - len(self._stack_player_two._points)): - self._stack_player_two._square_designs.append(self._last_square_design) - - # load opponent stack into game - self._stack_player_two._points = opponent_stack.points - - print(opponent_stack) + self._update_stack_player_two() if self._current_piece_player_one is not None: - MultiplayerService.send_piece(PieceDto(self._current_piece_player_one._points, self._current_piece_player_one._center)) + MultiplayerService.send_piece(PieceDto(self._current_piece_player_one._points,\ + self._current_piece_player_one._center,\ + self._current_piece_player_one._base_color,\ + self._current_piece_player_one._inner_color,\ + self._current_piece_player_one._outer_color)) if self._stack_player_one is not None: - MultiplayerService.send_stack(StackDto(self._stack_player_one._points)) + MultiplayerService.send_stack( + StackDto( + self._stack_player_one._points, + [ + x.__dict__ + for x in self._stack_player_one._square_colors + ], + ) + ) def _update_piece_player_one(self, elapsed_time: int) -> None: if self._current_piece_player_one is not None: @@ -398,20 +394,35 @@ class MultiplayerScene(Scene): vertex[0] += 400 opponent_piece.center[0] += 400 - # if it was added to stack then update colors - if opponent_piece.center[1] < self._current_piece_player_two._center[1]: - base_color = self._current_piece_player_two._color - inner_color = self._current_piece_player_two._inner_border_color - outer_color = self._current_piece_player_two._border_color - - self._last_square_design = SquareDesign(base_color, inner_color, outer_color) - self._current_piece_player_two = PieceGenerator.get_opponent_piece() - # load opponent piece into game self._current_piece_player_two._points = opponent_piece.points self._current_piece_player_two._center = opponent_piece.center + self._current_piece_player_two._base_color = opponent_piece.base_color.replace("player-1", "player-2") + if opponent_piece.inner_color is not None: + self._current_piece_player_two._inner_color = opponent_piece.inner_color.replace("player-1", "player-2") + else: + self._current_piece_player_two._inner_color = None + self._current_piece_player_two._outer_color = opponent_piece.outer_color.replace("player-1", "player-2") else: - self._current_piece_player_two = PieceGenerator.get_opponent_piece() + self._current_piece_player_two = PieceGenerator.get_opponent_piece(None, None, None) + + def _update_stack_player_two(self) -> None: + if self._stack_player_two is not None: + # get opponent stack from server and modify for current client + opponent_stack = MultiplayerService.try_receive_stack() + if opponent_stack is not None: + for square in opponent_stack.points: + for vertex in square: + vertex[0] += 400 + + # load opponent stack into game + self._stack_player_two._points = opponent_stack.points + self._stack_player_two._square_colors = [ + SquareColor(x["base_color"].replace("player-1", "player-2"),\ + x["inner_color"].replace("player-1", "player-2") if x["inner_color"] else None,\ + x["outer_color"].replace("player-1", "player-2")) + for x in opponent_stack.square_colors + ] 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