feat: add single player scene

This commit is contained in:
2021-07-08 01:20:33 -04:00
parent f3479e2be5
commit e607002062
5 changed files with 231 additions and 253 deletions

View File

@@ -28,7 +28,7 @@ engine:
piece-set-time: 600
piece-gravity-increase: 56
lines-per-level: 5
points-per-lines-completed:
points-table:
- 0 # 0 line
- 40 # 1 line
- 100 # 2 lines

View File

@@ -9,13 +9,11 @@ from tetri5.util import ConfigurationManager
def main() -> None:
ConfigurationManager.init()
game = Game()
game.init()
Game.init()
while True:
game.update()
game.draw()
Game.update()
Game.draw()
if __name__ == "__main__":
main()

View File

@@ -1,47 +1,43 @@
import copy
import random
import pygame
from typing import List, Tuple
from pygame import mixer
from typing import List, Tuple
from types import FunctionType
from tetri5.util import ConfigurationManager
from tetri5.util import Controller
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from tetri5.game import Game
"""
TODO description
"""
class Entity:
def __init__(self, points: Tuple, color: str, border_color: str = None):
self.points = points
self.color = color
self.border_color = border_color
self.elapsed_time = 0
self._tile_size = ConfigurationManager.get("engine", "tile-size")
self._points = points
self._color = color
self._border_color = border_color
self._elapsed_time = 0
def update(self, elapsed_time: int) -> None:
self.elapsed_time += elapsed_time
self._elapsed_time += elapsed_time
def draw(self, surface: pygame.Surface) -> None:
tile_size = ConfigurationManager.get("engine", "tile-size")
for square in self.points:
pygame.draw.polygon(surface, pygame.Color(self.color), square, 0)
if self.border_color:
pygame.draw.polygon(surface, pygame.Color(self.border_color), square, max(tile_size // 6, 1))
for square in self._points:
pygame.draw.polygon(surface, pygame.Color(self._color), square, 0)
if self._border_color:
pygame.draw.polygon(surface, pygame.Color(self._border_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:
for square_two in entity.points:
for square_one in self._points:
for square_two in entity._points:
for i in range(4): # 4 vertices
if square_one[i][0] == square_two[i][0] and square_one[i][1] == square_two[i][1]:
return True
return False
def _collide(self, points: List) -> bool:
for square_one in self.points:
for square_one in self._points:
for square_two in points:
for i in range(4): # 4 vertices
if square_one[i][0] == square_two[i][0] and square_one[i][1] == square_two[i][1]:
@@ -57,17 +53,17 @@ class Well(Entity):
HEIGHT = 20 # standard tetris well height, should not be changed
def __init__(self, position: Tuple, color: str, border_color: str):
super().__init__(self._get_points(position), color, border_color)
super().__init__(Well._get_points(position), color, border_color)
def _get_points(self, position: Tuple) -> List:
@classmethod
def _get_points(cls, position: Tuple) -> List:
tile_size = ConfigurationManager.get("engine", "tile-size")
shape = []
for i in range(self.WIDTH + 2):
for j in range(self.HEIGHT + 2):
if i in [0, self.WIDTH + 1]:
for i in range(cls.WIDTH + 2):
for j in range(cls.HEIGHT + 2):
if i in [0, cls.WIDTH + 1]:
shape.append(((i, j), (i + 1, j), (i + 1, j + 1), (i, j + 1)))
elif j in [0, self.HEIGHT + 1]:
elif j in [0, cls.HEIGHT + 1]:
shape.append(((i, j), (i + 1, j), (i + 1, j + 1), (i, j + 1)))
points = []
@@ -77,7 +73,6 @@ class Well(Entity):
point = [vertex[0] * tile_size + position[0], vertex[1] * tile_size + position[1]]
sub_points.append(point)
points.append(sub_points)
return points
'''
@@ -87,52 +82,49 @@ class Well(Entity):
class Piece(Entity):
def __init__(self, shape: Tuple, position: Tuple, color: str, inner_border_color: str, border_color: str):
super().__init__(self._get_points(shape, position), color, border_color)
self.inner_border_color = inner_border_color
self.center = self._get_center(shape, position)
self.piece_set_sound = mixer.Channel(2)
self.previous_points = None
self.previous_center = None
super().__init__(Piece._get_points(shape, position), color, border_color)
self._inner_border_color = inner_border_color
self._center = self._get_center(shape, position)
self._piece_set_sound = mixer.Channel(2)
self._previous_points = None
self._previous_center = None
# Gravity
self.gravity_time = ConfigurationManager.get("engine", "piece-gravity-time")
self.current_gravity_time = 0
self.applying_gravity = True
self._gravity_time = ConfigurationManager.get("engine", "piece-gravity-time")
self._current_gravity_time = 0
self._applying_gravity = True
# Set
self.set_time = ConfigurationManager.get("engine", "piece-set-time")
self.current_set_time = 0
self.applying_set = False
self._set_time = ConfigurationManager.get("engine", "piece-set-time")
self._current_set_time = 0
self._applying_set = False
def update(self, elapsed_time: int, game: "Game") -> None:
def update(self, elapsed_time: int, well: Well, stack: "Stack", level: int, clear_current_piece: FunctionType) -> None:
super().update(elapsed_time)
tile_size = ConfigurationManager.get("engine", "tile-size")
if self.applying_gravity:
self.applying_gravity = self._apply_gravity(elapsed_time, game.well, game.stack)
self.applying_set = not self.applying_gravity
if self._applying_gravity:
self._applying_gravity = self._apply_gravity(elapsed_time, well, stack)
self._applying_set = not self._applying_gravity
"""
For more information on the piece set logic go here:
https://strategywiki.org/wiki/Tetris/Features#Lock_delay
"""
if self.applying_set:
self.applying_set = self._apply_set(elapsed_time, game)
self.applying_gravity = not self.applying_set
if self._applying_set:
self._applying_set = self._apply_set(elapsed_time, well, stack, clear_current_piece)
self._applying_gravity = not self._applying_set
# handle rotation, left and right movement
if Controller.key_down(pygame.K_SPACE):
self.rotate()
if game.well and self.collide(game.well) or game.stack and self.collide(game.stack):
if well and self.collide(well) or stack and self.collide(stack):
self.revert()
if Controller.key_down(pygame.K_LEFT):
self.move((-tile_size, 0))
if game.well and self.collide(game.well) or game.stack and self.collide(game.stack):
self.move((-self._tile_size, 0))
if well and self.collide(well) or stack and self.collide(stack):
self.revert()
if Controller.key_down(pygame.K_RIGHT):
self.move((tile_size, 0))
if game.well and self.collide(game.well) or game.stack and self.collide(game.stack):
self.move((self._tile_size, 0))
if well and self.collide(well) or stack and self.collide(stack):
self.revert()
# handle soft drop movement and gravity based on level
@@ -141,41 +133,37 @@ class Piece(Entity):
gravity_increase = ConfigurationManager.get("engine", "piece-gravity-increase")
if Controller.key_pressed(pygame.K_DOWN):
self.gravity_time = max(10, (gravity_time - (game.get_level() * gravity_increase)) // 10)
self.set_time = max(10, set_time // 10)
self._gravity_time = max(10, (gravity_time - (level * gravity_increase)) // 10)
self._set_time = max(10, set_time // 10)
if not Controller.key_pressed(pygame.K_DOWN):
self.gravity_time = gravity_time - (game.get_level() * gravity_increase)
self.set_time = set_time
self._gravity_time = gravity_time - (level * gravity_increase)
self._set_time = set_time
def draw(self, surface: pygame.Surface, well: Well = None, stack: "Stack" = None) -> None:
tile_size = ConfigurationManager.get("engine", "tile-size")
# ghost piece
if well and stack:
for square in self._get_ghost_piece_points(well, stack):
pygame.draw.polygon(surface, pygame.Color("#FFFFFF"), square, max(tile_size // 6, 1)) # TODO add white to the yaml
pygame.draw.polygon(surface, pygame.Color("#FFFFFF"), 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:
vertex_one = (square[0][0] + (tile_size // 10), square[0][1] + (tile_size // 10))
vertex_two = (square[1][0] - (tile_size // 10), square[1][1] + (tile_size // 10))
vertex_three = (square[2][0] - (tile_size // 10), square[2][1] - (tile_size // 10))
vertex_four = (square[3][0] + (tile_size // 10), square[3][1] - (tile_size // 10))
for square in self._points:
if self._inner_border_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(tile_size // 6, 1))
pygame.draw.polygon(surface, pygame.Color(self._inner_border_color), new_square, max(self._tile_size // 6, 1))
def move(self, vector: Tuple) -> None:
self.previous_points = copy.deepcopy(self.points)
self.previous_center = copy.deepcopy(self.center)
self._previous_points = copy.deepcopy(self._points)
self._previous_center = copy.deepcopy(self._center)
self.center[0] += vector[0]
self.center[1] += vector[1]
self._center[0] += vector[0]
self._center[1] += vector[1]
for square in self.points:
for square in self._points:
for vertex in square:
vertex[0] += vector[0]
vertex[1] += vector[1]
@@ -185,28 +173,28 @@ class Piece(Entity):
https://gamedev.stackexchange.com/questions/17974/how-to-rotate-blocks-in-tetris
'''
def rotate(self) -> None:
self.previous_points = copy.deepcopy(self.points)
self.previous_center = copy.deepcopy(self.center)
self._previous_points = copy.deepcopy(self._points)
self._previous_center = copy.deepcopy(self._center)
new_points = []
for square in self.points:
for square in self._points:
for vertex in square:
h = vertex[0] - self.center[0]
k = vertex[1] - self.center[1]
h = vertex[0] - self._center[0]
k = vertex[1] - self._center[1]
vertex[0] = (k * -1) + self.center[0]
vertex[1] = h + self.center[1]
vertex[0] = (k * -1) + self._center[0]
vertex[1] = h + self._center[1]
new_points.append([square[-1]] + square[0:-1])
self.points = new_points
self._points = new_points
def revert(self) -> None:
if self.previous_points and self.previous_center:
self.points = self.previous_points
self.center = self.previous_center
if self._previous_points and self._previous_center:
self._points = self._previous_points
self._center = self._previous_center
@classmethod
def _get_points(self, shape: Tuple, position: Tuple) -> List:
tile_size = ConfigurationManager.get("engine", "tile-size")
tile_size = ConfigurationManager.get("engine", "tile-size")
points = []
for square in shape[:-1]:
sub_points = []
@@ -214,45 +202,36 @@ class Piece(Entity):
point = [vertex[0] * tile_size + position[0], vertex[1] * tile_size + position[1]]
sub_points.append(point)
points.append(sub_points)
return points
def _get_center(self, shape: Tuple, position: Tuple) -> List:
tile_size = ConfigurationManager.get("engine", "tile-size")
center = shape[-1]
# cast to int and avoid exception from pygame (center can be a floating point)
return [int(center[0] * tile_size + position[0]), int(center[1] * tile_size + position[1])]
return [int(center[0] * self._tile_size + position[0]), int(center[1] * self._tile_size + position[1])]
def _apply_gravity(self, elapsed_time: int, well: Well, stack: "Stack") -> bool:
tile_size = ConfigurationManager.get("engine", "tile-size")
self.current_gravity_time += elapsed_time
if self.current_gravity_time >= self.gravity_time:
self.current_gravity_time = 0
self._current_gravity_time += elapsed_time
if self._current_gravity_time >= self._gravity_time:
self._current_gravity_time = 0
if not self._entity_is_below(well) and not self._entity_is_below(stack):
self.move((0, tile_size))
self.move((0, self._tile_size))
else:
return False
return True
def _apply_set(self, elapsed_time: int, game: "Game") -> bool:
self.current_set_time += elapsed_time
if self.current_set_time >= self.set_time:
self.current_set_time = 0
if self._entity_is_below(game.well) or self._entity_is_below(game.stack):
def _apply_set(self, elapsed_time: int, well: Well, stack: "Stack", clear_current_piece: FunctionType) -> bool:
self._current_set_time += elapsed_time
if self._current_set_time >= self._set_time:
self._current_set_time = 0
if self._entity_is_below(well) or self._entity_is_below(stack):
self._play_piece_set_sound()
game.stack.add_piece(self) # TODO do on tetris object level?
game.current_piece = None # TODO turn into a method
stack.add_piece(self)
clear_current_piece()
else:
return False
return True
def _mimic_move(self, vector: Tuple) -> List:
mimic_points = copy.deepcopy(self.points)
mimic_points = copy.deepcopy(self._points)
for square in mimic_points:
for vertex in square:
@@ -263,24 +242,19 @@ class Piece(Entity):
def _play_piece_set_sound(self) -> None:
piece_set_sound_file = ConfigurationManager.get("sound", "piece-set")
self.piece_set_sound.play(mixer.Sound(piece_set_sound_file))
self._piece_set_sound.play(mixer.Sound(piece_set_sound_file))
def _entity_is_below(self, entity: Entity) -> bool:
tile_size = ConfigurationManager.get("engine", "tile-size")
mimic_points = self._mimic_move((0, tile_size))
mimic_points = self._mimic_move((0, self._tile_size))
return entity and entity._collide(mimic_points)
def _get_ghost_piece_points(self, well: Well, stack: "Stack") -> List:
tile_size = ConfigurationManager.get("engine", "tile-size")
prior_points = []
current_points = copy.deepcopy(self.points)
current_points = copy.deepcopy(self._points)
while not well._collide(current_points) and not stack._collide(current_points):
prior_points = copy.deepcopy(current_points)
for square in current_points:
for vertex in square:
vertex[1] += 1 * tile_size
vertex[1] += 1 * self._tile_size
return prior_points
# shape attributes
@@ -299,26 +273,22 @@ class Stack(Entity):
def __init__(self, color: str, border_color: str):
super().__init__([], color, border_color)
self.lines_completed_count = 0
self.total_lines = 0
self.lines_completed_last = 0
self.line_completed_sound = mixer.Channel(1)
def update(self, elapsed_time: int, game: "Game") -> None:
def update(self, elapsed_time: int) -> None: # TODO remove scene argument
super().update(elapsed_time)
lines_completed = self._complete_rows()
current_level = game.get_level()
points_per_lines_completed = ConfigurationManager.get("engine", "points-per-lines-completed")
game.score += points_per_lines_completed[lines_completed] * (current_level + 1)
self.lines_completed_count += lines_completed
self.lines_completed_last = self._complete_rows()
self.total_lines += self.lines_completed_last
def add_piece(self, piece: Piece) -> None:
self.points += piece.points
self._points += piece._points
# TODO refactor into multiple functions
def _complete_rows(self) -> int:
squares_by_row = {}
for square in self.points:
for square in self._points:
top_left_vertex = square[0]
if top_left_vertex[1] not in squares_by_row:
squares_by_row[top_left_vertex[1]] = []
@@ -327,28 +297,27 @@ class Stack(Entity):
squares_to_exclude = []
rows_completed = []
for key in squares_by_row:
for key, value in squares_by_row.items():
if len(squares_by_row[key]) == Well.WIDTH:
squares_to_exclude += squares_by_row[key]
squares_to_exclude += value
rows_completed.append(key)
if len(squares_to_exclude) == 0:
if not squares_to_exclude:
return 0
self._play_line_completed_sound()
tile_size = ConfigurationManager.get("engine", "tile-size")
new_points = []
for square in self.points:
for square in self._points:
if square not in squares_to_exclude:
for vertex in square:
distance_to_move = 0
for row_completed in rows_completed:
if vertex[1] <= row_completed:
distance_to_move += 1
vertex[1] += tile_size * distance_to_move
distance_to_move = sum(
vertex[1] <= row_completed
for row_completed in rows_completed
)
vertex[1] += self._tile_size * distance_to_move
new_points.append(square)
self.points = new_points
self._points = new_points
return len(rows_completed)
@@ -398,9 +367,7 @@ class PieceGenerator:
def _get_piece_color() -> Tuple:
random_number = random.randint(1, 3)
base_color = ConfigurationManager.get("color", "piece-" + str(random_number))
inner_border_color = None if random_number != 3 else ConfigurationManager.get("color", "piece-inner-border-1")
outer_border_color = ConfigurationManager.get("color", "piece-outer-border-1")
return (base_color, inner_border_color, outer_border_color)

View File

@@ -1,126 +1,60 @@
import sys
import pygame
from pygame import mixer
from tetri5.util import ConfigurationManager
from tetri5.util import TextGenerator
from tetri5.entity import PieceGenerator
from tetri5.entity import Well
from tetri5.entity import Stack
from tetri5.online import MultiplayerService
from tetri5.scene import TitleScene
from tetri5.scene import Scene, TitleScene
# TODO improve game assets https://www.spriters-resource.com/nes/tetris/
# TODO should be a singleton and refactor the whole file?
class Game:
def __init__(self):
self.fps = -1
self.tile_size = -1
self.screen = None
self.clock = 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
self.score = -1
_current_scene = None
def init(self) -> None:
@classmethod
def change_scene(cls, scene: Scene) -> None:
cls._current_scene = scene
@classmethod
def init(cls) -> None:
pygame.init()
TextGenerator.init(ConfigurationManager.get("image", "font"), (20, 20))
cls._current_scene = TitleScene(Game.change_scene)
win_width = ConfigurationManager.get("window", "width")
win_height = ConfigurationManager.get("window", "height")
win_title = ConfigurationManager.get("window", "title")
win_icon = ConfigurationManager.get("image", "window-icon")
self.fps = ConfigurationManager.get("engine", "fps")
self.tile_size = ConfigurationManager.get("engine", "tile-size")
self.screen = pygame.display.set_mode((win_width, win_height))
self.clock = pygame.time.Clock()
cls.fps = ConfigurationManager.get("engine", "fps")
cls.tile_size = ConfigurationManager.get("engine", "tile-size")
cls.screen = pygame.display.set_mode((win_width, win_height))
cls.clock = pygame.time.Clock()
pygame.display.set_caption(win_title)
pygame.display.set_icon(pygame.image.load(win_icon))
self.main_music = mixer.Channel(0)
self.well = Well((280, 80), ConfigurationManager.get("color", "well-1"), ConfigurationManager.get("color", "well-border-1")) # TODO calculate position later and redo color config for well
self.stack = Stack(ConfigurationManager.get("color", "stack-1"), ConfigurationManager.get("color", "stack-border-1"))
self.score = 0
self.main_music.set_volume(0.7) # TODO add volume to the config
self.main_music.play(mixer.Sound(ConfigurationManager.get("sound", "main-music")), -1)
# gets called from the games main loop
def update(self) -> None:
@classmethod
def update(cls) -> None:
# TODO write not initialized exception
elapsed_time = self.clock.tick(self.fps)
if self.current_scene:
self.current_scene.update(elapsed_time)
else:
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)
elapsed_time = cls.clock.tick(cls.fps)
if cls._current_scene:
cls._current_scene.update(elapsed_time)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
MultiplayerService.quit()
sys.exit()
def draw(self) -> None:
@classmethod
def draw(cls) -> None:
# TODO write not initialized exception
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)
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)
if cls._current_scene:
cls._current_scene.draw(cls.screen)
# update display
pygame.display.update()
def get_level(self) -> int:
lines_per_level = ConfigurationManager.get("engine", "lines-per-level")
return 0 if not self.stack else self.stack.lines_completed_count // lines_per_level

View File

@@ -1,19 +1,32 @@
import sys
import pygame
from pygame import mixer
from types import FunctionType
from tetri5.util import ConfigurationManager
from tetri5.util import TextGenerator
from tetri5.util import Controller
from tetri5.entity import Well
from tetri5.entity import Stack
from tetri5.entity import PieceGenerator
"""
TODO
"""
class TitleScene:
class Scene:
pass
"""
TODO
"""
class TitleScene(Scene):
# Title screen options
ONE_PLAYER = "1 PLAYER"
TWO_PLAYER = "2 PLAYER"
def __init__(self) -> None:
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_position_one = ConfigurationManager.get("position", "cursor-option-one")
self._cursor_position_two = ConfigurationManager.get("position", "cursor-option-two")
@@ -22,12 +35,13 @@ class TitleScene:
self._cursor_blink_time = 0
self._cursor_color = pygame.Color(ConfigurationManager.get("color", "cursor"))
self._cursor_off = False
self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg"))
self._logo_position = ConfigurationManager.get("position", "title-logo")
self._option_one_position = ConfigurationManager.get("position", "option-one")
self._option_two_position = 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_position)
@@ -38,7 +52,7 @@ class TitleScene:
if self._cursor_off:
pygame.draw.circle(surface, self._cursor_color, self._cursor_position, self._tile_size // 3)
def update(self, elapsed_time) -> None:
def update(self, elapsed_time: int) -> None:
if Controller.key_pressed(pygame.K_UP):
self._cursor_position = self._cursor_position_one
self._is_multiplayer = False
@@ -51,18 +65,83 @@ class TitleScene:
self._cursor_blink_time = 0
self._cursor_off = not self._cursor_off
"""
TODO
"""
class SinglePlayerScene:
pass
if Controller.key_pressed(pygame.K_RETURN):
self._change_scence(SinglePlayerScene(self._change_scence))
"""
TODO
"""
class MultiPlayerScene:
pass
class SinglePlayerScene(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._score = 0
self._level = 0
self._well = Well((280, 80), ConfigurationManager.get("color", "well-1"), ConfigurationManager.get("color", "well-border-1")) # TODO calculate position later and redo color config for well
self._stack = Stack(ConfigurationManager.get("color", "stack-1"), ConfigurationManager.get("color", "stack-border-1"))
self._current_piece = None
self._next_piece = PieceGenerator.get_piece((620, 160))
self._main_music = mixer.Channel(0)
self._main_music.set_volume(0.7) # TODO add volume to the config
self._main_music.play(mixer.Sound(ConfigurationManager.get("sound", "main-music")), -1)
self._points_table = ConfigurationManager.get("engine", "points-table")
self._change_scence = change_scene
def draw(self, surface: pygame.Surface) -> None:
surface.fill(self._background_color)
if self._next_piece:
self._next_piece.draw(surface)
if self._well:
self._well.draw(surface)
if self._stack:
self._stack.draw(surface)
if self._current_piece:
self._current_piece.draw(surface, self._well, self._stack)
score = str(self._score).zfill(6)
lines = str(self._stack.total_lines).zfill(4)
level = str(self._get_level()).zfill(2)
TextGenerator.draw("Top", (80, 120), surface)
TextGenerator.draw("000000", (80, 140), surface)
TextGenerator.draw("Score", (80, 180), surface)
TextGenerator.draw(score, (80, 200), surface)
TextGenerator.draw("Lines " + lines, (300, 40), surface)
TextGenerator.draw("Next", (600, 120), surface)
TextGenerator.draw("LVL " + level, (600, 220), 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((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()
sys.exit()
if self._stack:
self._stack.update(elapsed_time)
self._score += self._points_table[self._stack.lines_completed_last] * (self._get_level() + 1)
def _get_level(self) -> int:
lines_per_level = ConfigurationManager.get("engine", "lines-per-level")
return 0 if not self._stack else self._stack.total_lines // lines_per_level
def _clear_current_piece(self) -> None:
self._current_piece = None
"""
TODO
"""
class MultiPlayerScene(Scene):
pass