feat: add single player scene
This commit is contained in:
@@ -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
|
||||
|
||||
8
main.py
8
main.py
@@ -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()
|
||||
257
tetri5/entity.py
257
tetri5/entity.py
@@ -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)
|
||||
112
tetri5/game.py
112
tetri5/game.py
@@ -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
|
||||
|
||||
|
||||
105
tetri5/scene.py
105
tetri5/scene.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user