diff --git a/Tetris2000/Tetris2000.py b/Tetris2000/Tetris2000.py deleted file mode 100644 index 1dc0f45..0000000 --- a/Tetris2000/Tetris2000.py +++ /dev/null @@ -1,1937 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Another TETRIS® clone -Tetris Game Design by Alexey Pajitnov. -Parts of comments issued from 2009 Tetris Design Guideline -""" - -__author__ = "Adrien Malingrey" -__title__ = "Tetris 2000" -__version__ = "0.2" - - -import sys -import os -import random -import locale -import time -import itertools -import ctypes -import collections - -try: # PyQt5 - from PyQt5 import QtWidgets, QtCore, QtGui, QtMultimedia -except ImportError as pyqt5_error: - try: # PySide2 - from PySide2 import QtWidgets, QtCore, QtGui, QtMultimedia - except ImportError as pyside2_error: - sys.exit( - "This program require a Qt5 library.\n" - "You can install PyQt5 (recommended) :\n" - " pip3 install --user PyQt5\n" - " pip3 install --user qdarkstyle\n" - "or PySide2 :\n" - " pip3 install --user PySide2\n" - + pyqt5_error.msg + "\n" - + pyside2_error.msg - ) - else: - os.environ["QT_API"] = "pyside2" -else: - os.environ["QT_API"] = "pyqt5" - QtCore.Signal = QtCore.pyqtSignal - - -# Consts -# Paths -PATH = os.path.dirname(os.path.abspath(__file__)) -ICON_PATH = os.path.join(PATH, "data", "icons", "icon.ico") -BG_IMAGE_DIR = os.path.join(PATH, "data", "backgrounds") -MUSIC_PATH = os.path.join(PATH, "data", "sounds", "Tetris - Song A.mp3") -SOUNDS_DIR = os.path.join(PATH, "data", "sounds") -LOCALE_PATH = os.path.join(PATH, "data", "locale") -FONTS_DIR = os.path.join(PATH, "data", "fonts") -# Coordinates and direction -L, R, U, D = -1, 1, -1, 1 # Left, Right, Up, Down -CLOCKWISE, COUNTERCLOCKWISE = 1, -1 -# Delay -ANIMATION_DELAY = 100 # milliseconds - - -class Point(QtCore.QPoint): - """ - Point of coordinates (x, y) - """ - - def __init__(self, x, y): - super().__init__(x, y) - - def __add__(self, o): - return Point(self.x() + o.x(), self.y() + o.y()) - - def __sub__(self, o): - return Point(self.x() - o.x(), self.y() - o.y()) - - def __mul__(self, k): - return Point(k * self.x(), k * self.y()) - - def __truediv__(self, k): - return Point(self.x() / k, k * self.y() / k) - - __radd__ = __add__ - __rsub__ = __sub__ - __rmul__ = __mul__ - __rtruediv__ = __truediv__ - - def rotate(self, center, direction=CLOCKWISE): - """ Returns the Point image of the rotation of self - through 90° CLOKWISE or COUNTERCLOCKWISE around center""" - if self == center: - return self - - p = self - center - p = Point(-direction * p.y(), direction * p.x()) - p += center - return p - - -class Block: - """ - Mino or block - Mino : A single square-shaped building block of a shape called a Tetrimino. - Four Minos arranged into any of their various connected patterns is known as a Tetrimino - Block : A single block locked in a cell in the Grid - """ - - # Colors - BORDER_COLOR = QtGui.QColor(0, 159, 218, 255) - FILL_COLOR = QtGui.QColor(0, 159, 218, 25) - GLOWING_BORDER_COLOR = None - GLOWING_FILL_COLOR = QtGui.QColor(186, 211, 255, 70) - LIGHT_COLOR = QtGui.QColor(242, 255, 255, 40) - TRANSPARENT = QtGui.QColor(255, 255, 255, 0) - GLOWING = 0 - - side = 20 - - def __init__(self, coord, trail=0): - self.coord = coord - self.trail = trail - self.border_color = self.BORDER_COLOR - self.fill_color = self.FILL_COLOR - self.glowing = self.GLOWING - - def paint(self, painter, top_left_corner, spotlight): - p = top_left_corner + self.coord * Block.side - self.center = p + QtCore.QPoint(Block.side / 2, Block.side / 2) - self.glint = 0.15 * Block.side * spotlight + 0.85 * self.center - - if self.trail: - start = ( - top_left_corner + (self.coord + Point(0, self.trail * U)) * Block.side - ) - stop = top_left_corner + (self.coord + Point(0, 2 * D)) * Block.side - fill = QtGui.QLinearGradient(start, stop) - fill.setColorAt(0, self.LIGHT_COLOR) - fill.setColorAt(1, self.GLOWING_FILL_COLOR) - painter.setBrush(fill) - painter.setPen(QtCore.Qt.NoPen) - painter.drawRoundedRect( - start.x(), - start.y(), - Block.side, - Block.side * (1 + self.trail), - 20, - 20, - QtCore.Qt.RelativeSize, - ) - - if self.glowing: - fill = QtGui.QRadialGradient(self.center, self.glowing * Block.side) - fill.setColorAt(0, self.TRANSPARENT) - fill.setColorAt(0.5 / self.glowing, self.LIGHT_COLOR) - fill.setColorAt(1, self.TRANSPARENT) - painter.setBrush(QtGui.QBrush(fill)) - painter.setPen(QtCore.Qt.NoPen) - painter.drawEllipse( - self.center.x() - self.glowing * Block.side, - self.center.y() - self.glowing * Block.side, - 2 * self.glowing * Block.side, - 2 * self.glowing * Block.side, - ) - - painter.setBrush(self.brush()) - painter.setPen(self.pen()) - painter.drawRoundedRect( - p.x() + 1, - p.y() + 1, - Block.side - 2, - Block.side - 2, - 20, - 20, - QtCore.Qt.RelativeSize, - ) - - def brush(self): - if self.fill_color is None: - return QtCore.Qt.NoBrush - - fill = QtGui.QRadialGradient(self.glint, 1.5 * Block.side) - fill.setColorAt(0, self.fill_color.lighter()) - fill.setColorAt(1, self.fill_color) - return QtGui.QBrush(fill) - - def pen(self): - if self.border_color is None: - return QtCore.Qt.NoPen - - border = QtGui.QRadialGradient(self.glint, Block.side) - border.setColorAt(0, self.border_color.lighter()) - border.setColorAt(1, self.border_color.darker()) - return QtGui.QPen(QtGui.QBrush(border), 1, join=QtCore.Qt.RoundJoin) - - def shine(self, glowing=2, delay=None): - self.border_color = Block.GLOWING_BORDER_COLOR - self.fill_color = Block.GLOWING_FILL_COLOR - self.glowing = glowing - if delay: - QtCore.QTimer.singleShot(delay, self.fade) - - def fade(self): - self.border_color = Block.BORDER_COLOR - self.fill_color = Block.FILL_COLOR - self.glowing = 0 - self.trail = 0 - - -class GhostBlock(Block): - """ - Mino of the ghost piece - """ - - BORDER_COLOR = QtGui.QColor(135, 213, 255, 255) - FILL_COLOR = None - GLOWING_FILL_COLOR = QtGui.QColor(201, 149, 205, 255) - GLOWING = 1 - - -class MetaTetro(type): - """ - Save the different shapes of Tetrominoes - """ - - def __init__(cls, name, bases, dico): - type.__init__(cls, name, bases, dico) - Tetromino.classes.append(cls) - Tetromino.nb_classes += 1 - - -class Tetromino: - """ - Geometric Tetris® shape formed by four Minos connected along their sides. - A total of seven possible Tetriminos can be made using four Minos. - """ - - COORDS = NotImplemented - SUPER_ROTATION_SYSTEM = ( - { - COUNTERCLOCKWISE: ((0, 0), (R, 0), (R, U), (0, 2 * D), (R, 2 * D)), - CLOCKWISE: ((0, 0), (L, 0), (L, U), (0, 2 * D), (L, 2 * D)), - }, - { - COUNTERCLOCKWISE: ((0, 0), (R, 0), (R, D), (0, 2 * U), (R, 2 * U)), - CLOCKWISE: ((0, 0), (R, 0), (R, D), (0, 2 * U), (R, 2 * U)), - }, - { - COUNTERCLOCKWISE: ((0, 0), (L, 0), (L, U), (0, 2 * D), (L, 2 * D)), - CLOCKWISE: ((0, 0), (R, 0), (R, U), (0, 2 * D), (R, 2 * D)), - }, - { - COUNTERCLOCKWISE: ((0, 0), (L, 0), (L, D), (0, 2 * U), (L, 2 * U)), - CLOCKWISE: ((0, 0), (L, 0), (L, D), (0, 2 * D), (L, 2 * U)), - }, - ) - - classes = [] - nb_classes = 0 - random_bag = [] - - def __new__(cls): - """ - Return a Tetromino using the 7-bag Random Generator - Tetris uses a “bag” system to determine the sequence of Tetriminos - that appear during game play. - This system allows for equal distribution among the seven Tetriminos. - The seven different Tetriminos are placed into a virtual bag, - then shuffled into a random order. - This order is the sequence that the bag “feeds” the Next Queue. - Every time a new Tetrimino is generated and starts its fall within the Matrix, - the Tetrimino at the front of the line in the bag is placed at the end of the Next Queue, - pushing all Tetriminos in the Next Queue forward by one. - The bag is refilled and reshuffled once it is empty. - """ - if not cls.random_bag: - cls.random_bag = random.sample(cls.classes, cls.nb_classes) - return super().__new__(cls.random_bag.pop()) - - def __init__(self): - self.orientation = 0 - self.t_spin = "" - - def insert_into(self, matrix, position): - self.matrix = matrix - self.minoes = tuple(Block(Point(*coord) + position) for coord in self.COORDS) - - def _try_movement(self, next_coords_generator, trail=False): - """ - Test if self can fit in the Grid with new coordinates, - i.e. all cells are empty. - If it can, change self's coordinates and return True. - Else, make no changes and return False - Update the Grid if there is no drop trail - """ - futures_coords = [] - for p in next_coords_generator: - if not self.matrix.is_empty_cell(p): - return False - futures_coords.append(p) - - for block, future_coord in zip(self.minoes, futures_coords): - block.coord = future_coord - if not trail: - self.matrix.update() - return True - - def move(self, horizontally, vertically, trail=False): - """ - Try to translate self horizontally or vertically - The Tetrimino in play falls from just above the Skyline one cell at a time, - and moves left and right one cell at a time. - Each Mino of a Tetrimino “snaps” to the appropriate cell position at the completion of a move, - although intermediate Tetrimino movement appears smooth. - Only right, left, and downward movement are allowed. - Movement into occupied cells and Matrix walls and floors is not allowed - Update the Grid if there is no drop trail - """ - return self._try_movement( - (block.coord + Point(horizontally, vertically) for block in self.minoes), - trail, - ) - - def rotate(self, direction=CLOCKWISE): - """ - Try to rotate self through 90° CLOCKWISE or COUNTERCLOCKWISE around its center - Tetriminos can rotate clockwise and counterclockwise using the Super Rotation System. - This system allows Tetrimino rotation in situations that - the original Classic Rotation System did not allow, - such as rotating against walls. - Each time a rotation button is pressed, - the Tetrimino in play rotates 90 degrees in the clockwise or counterclockwise direction. - Rotation can be performed while the Tetrimino is Auto-Repeating left or right. - There is no Auto-Repeat for rotation itself. - """ - rotated_coords = tuple( - block.coord.rotate(self.minoes[0].coord, direction) for block in self.minoes - ) - - for movement in self.SUPER_ROTATION_SYSTEM[self.orientation][direction]: - if self._try_movement(coord + Point(*movement) for coord in rotated_coords): - self.orientation = (self.orientation + direction) % 4 - return True - return False - - def soft_drop(self): - """ - Causes the Tetrimino to drop at an accelerated rate (s.AUTO_REPEAT_RATE) - from its current location - """ - dropped = self.move(0, D, trail=True) - if dropped: - for block in self.minoes: - block.trail = 1 - self.matrix.update() - return dropped - - def hard_drop(self): - """ - Causes the Tetrimino in play to drop straight down instantly from its - current location and Lock Down on the first Surface it lands on. - It does not allow for further player manipulation of the Tetrimino in play. - """ - trail = 0 - while self.move(0, D, trail=True): - trail += 1 - for block in self.minoes: - block.trail = trail - self.matrix.update() - return trail - - -class TetroI(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital I - four minoes in a straight line - """ - - COORDS = (L, 0), (2 * L, 0), (0, 0), (R, 0) - SUPER_ROTATION_SYSTEM = ( - { - COUNTERCLOCKWISE: ((0, D), (L, D), (2 * R, D), (L, U), (2 * R, 2 * D)), - CLOCKWISE: ((R, 0), (L, 0), (2 * R, 0), (L, D), (2 * R, 2 * U)), - }, - { - COUNTERCLOCKWISE: ((L, 0), (R, 0), (2 * L, 0), (R, U), (2 * L, 2 * D)), - CLOCKWISE: ((0, D), (L, D), (2 * R, D), (L, U), (2 * R, 2 * D)), - }, - { - COUNTERCLOCKWISE: ((0, U), (R, U), (2 * L, U), (R, D), (2 * L, 2 * U)), - CLOCKWISE: ((L, 0), (R, 0), (2 * L, 0), (R, U), (2 * L, 2 * D)), - }, - { - COUNTERCLOCKWISE: ((R, 0), (L, 0), (2 * R, 0), (L, D), (2 * R, 2 * U)), - CLOCKWISE: ((0, U), (R, U), (2 * L, U), (R, D), (2 * L, 2 * U)), - }, - ) - - -class TetroT(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital T - a row of three minoes with one added above the center - Can perform a T-Spin - """ - - COORDS = (0, 0), (L, 0), (0, U), (R, 0) - T_SLOT_A = ((L, U), (R, U), (R, D), (L, D)) - T_SLOT_B = ((R, U), (R, D), (L, D), (L, U)) - T_SLOT_C = ((L, D), (L, U), (R, U), (R, D)) - T_SLOT_D = ((R, D), (L, D), (L, U), (R, U)) - - def __init__(self): - super().__init__() - - def rotate(self, direction=CLOCKWISE): - """ - Detects T-Spins: - this action can be achieved by first landing a T-Tetrimino, - and before it Locks Down, rotating it in a T-Slot - (any Block formation such that when the T-Tetrimino is spun into it, - any three of the four cells diagonally adjacent to the center of self - are occupied by existing Blocks.) - """ - rotated = super().rotate(direction) - if rotated: - center = self.minoes[0].coord - pa = center + Point(*self.T_SLOT_A[self.orientation]) - pb = center + Point(*self.T_SLOT_B[self.orientation]) - pc = center + Point(*self.T_SLOT_C[self.orientation]) - pd = center + Point(*self.T_SLOT_D[self.orientation]) - - a = not self.matrix.is_empty_cell(pa) - b = not self.matrix.is_empty_cell(pb) - c = not self.matrix.is_empty_cell(pc) - d = not self.matrix.is_empty_cell(pd) - - if (a and b) and (c or d): - if c: - pe = (pa + pc) / 2 - elif d: - pe = (pb + pd) / 2 - if not self.matrix.is_empty_cell(pe): - self.t_spin = "T-Spin" - elif (a or b) and (c and d): - self.t_spin = "Mini T-Spin" - return rotated - - -class TetroZ(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital Z - two stacked horizontal dominoes with the top one offset to the left - """ - - COORDS = (0, 0), (L, U), (0, U), (R, 0) - - -class TetroS(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital S - two stacked horizontal dominoes with the top one offset to the right - """ - - COORDS = (0, 0), (0, U), (L, 0), (R, U) - - -class TetroL(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital L - a row of three minoes with one added above the right side - """ - - COORDS = (0, 0), (L, 0), (R, 0), (R, U) - - -class TetroJ(Tetromino, metaclass=MetaTetro): - """ - Tetromino shaped like a capital J - a row of three minoes with one added above the left side - """ - - COORDS = (0, 0), (L, U), (L, 0), (R, 0) - - -class TetroO(Tetromino, metaclass=MetaTetro): - """ - Square shape - four minoes in a 2×2 square. - """ - - COORDS = (0, 0), (L, 0), (0, U), (L, U) - - def rotate(self, direction=1): - """ irrelevant """ - pass - - -class Ghost(Tetromino): - """ - A graphical representation of where the Tetrimino in play will come to rest - if it is dropped from its current position. - """ - - def __new__(cls, piece): - return object.__new__(cls) - - def __init__(self, piece): - self.matrix = piece.matrix - self.minoes = tuple( - GhostBlock(Point(mino.coord.x(), mino.coord.y())) for mino in piece.minoes - ) - self.hard_drop() - - def hard_drop(self): - while self.move(0, D): - pass - - -class Grid(QtWidgets.QWidget): - """ - Mother class of Hold queue, Next piece Queue, Matrix, and Next queue - """ - - ROWS = 6 - COLUMNS = 6 - STARTING_POSITION = Point(3, 4) - GRIDLINE_COLOR = QtGui.QColor(255, 255, 255, 60) - HARD_DROP_MOVEMENT = 0.2 - SPOTLIGHT = Point(0, 0) - - def __init__(self, frames): - super().__init__(frames) - self.setStyleSheet("background-color: transparent") - self.frames = frames - self.spotlight = self.SPOTLIGHT - self.piece = None - - def insert(self, piece, position=None): - """ - Add a Tetromino to self - Update its coordinates - """ - piece.insert_into(self, position or self.STARTING_POSITION) - self.piece = piece - self.update() - - def resizeEvent(self, event): - self.bottom = Block.side * self.ROWS - self.grid_top = 2 * Block.side - width = Block.side * self.COLUMNS - self.left = (self.width() - width) // 2 - self.right = width + self.left - self.top_left_corner = Point(self.left, 0) - - def paintEvent(self, event=None): - painter = QtGui.QPainter(self) - painter.setRenderHint(QtGui.QPainter.Antialiasing) - - self.paint_grid(painter) - - if (not self.frames.paused or not self.frames.playing) and self.piece: - self.paint_piece(painter, self.piece) - - def paint_grid(self, painter): - painter.setPen(self.GRIDLINE_COLOR) - for x in (self.left + i * Block.side for i in range(self.COLUMNS + 1)): - painter.drawLine(x, self.grid_top, x, self.bottom) - for y in (j * Block.side for j in range(2, self.ROWS + 1)): - painter.drawLine(self.left, y, self.right, y) - - def paint_piece(self, painter, piece): - for mino in piece.minoes: - mino.paint(painter, self.top_left_corner, self.spotlight) - - -class Matrix(Grid): - """ - The rectangular arrangement of cells creating the active game area. - Tetriminos fall from the top-middle just above the Skyline (off-screen) to the bottom. - """ - - ROWS = 22 - COLUMNS = 10 - STARTING_POSITION = Point(COLUMNS // 2, 1) - TEXT_COLOR = QtGui.QColor(204, 255, 255, 128) - TEMPORARY_TEXT_DURATION = 1000 # milliseconds - - # Delays - INITIAL_SPEED = 1000 # row per milliseconds - ENTRY_DELAY = 80 # millisecondes - LINE_CLEAR_DELAY = 80 # millisecondes - LOCK_DELAY = 500 # millisecondes - - drop_signal = QtCore.Signal(int) - lock_signal = QtCore.Signal(int, str) - - def __init__(self, frames): - super().__init__(frames) - - self.game_over = False - self.text = "" - self.temporary_texts = [] - - self.load_sounds() - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - self.auto_repeat_delay = 0 - self.auto_repeat_timer = QtCore.QTimer() - self.auto_repeat_timer.timeout.connect(self.auto_repeat) - self.fall_timer = QtCore.QTimer() - self.fall_timer.timeout.connect(self.fall) - - self.cells = [] - self.apply_settings() - - def load_sounds(self): - self.hard_drop_sound = QtMultimedia.QSoundEffect(self) - self.hard_drop_sound.setSource( - QtCore.QUrl.fromLocalFile( - os.path.join(SOUNDS_DIR, "hard_drop.wav") - ) - ) - self.rotate_sound = QtMultimedia.QSoundEffect(self) - self.rotate_sound.setSource( - QtCore.QUrl.fromLocalFile( - os.path.join(SOUNDS_DIR,"rotate.wav") - ) - ) - - def apply_settings(self): - self.keys = { - getattr(QtCore.Qt, "Key_" + name): action - for action, name in settings[s.KEYBOARD].items() - } - self.auto_repeat_timer.start(settings[s.DELAYS][s.AUTO_REPEAT_RATE]) - self.spotlight = self.SPOTLIGHT - for sound in self.hard_drop_sound, self.rotate_sound: - sound.setVolume(settings[s.SOUND][s.EFFECTS_VOLUME]) - - def new_game(self): - self.game_over = False - self.lock_delay = self.LOCK_DELAY - self.cells = [self.empty_row() for y in range(self.ROWS)] - self.setFocus() - self.actions_to_repeat = [] - - def new_level(self, level): - self.show_temporary_text(self.tr("Level\n") + str(level)) - self.speed = self.INITIAL_SPEED * (0.8 - ((level - 1) * 0.007)) ** (level - 1) - self.fall_timer.start(self.speed) - if level > 15: - self.lock_delay *= 0.9 - - def empty_row(self): - return [None for x in range(self.COLUMNS)] - - def is_empty_cell(self, coord): - x, y = coord.x(), coord.y() - return 0 <= x < self.COLUMNS and y < self.ROWS and not self.cells[y][x] - - def keyPressEvent(self, event): - if event.isAutoRepeat(): - return - - if not self.frames.playing: - return - - try: - action = self.keys[event.key()] - except KeyError: - return - - self.do(action) - if action in (s.MOVE_LEFT, s.MOVE_RIGHT, s.SOFT_DROP): - if action not in self.actions_to_repeat: - self.actions_to_repeat.append(action) - self.auto_repeat_wait() - - def keyReleaseEvent(self, event): - if event.isAutoRepeat(): - return - - if not self.frames.playing: - return - - try: - self.actions_to_repeat.remove(self.keys[event.key()]) - except (KeyError, ValueError): - pass - else: - self.auto_repeat_wait() - - if not self.actions_to_repeat: - for mino in self.piece.minoes: - mino.fade() - self.update() - - def auto_repeat_wait(self): - self.auto_repeat_delay = ( - time.time() + settings[s.DELAYS][s.AUTO_SHIFT_DELAY] / 1000 - ) - - def auto_repeat(self): - """ - Tapping the move button allows a single cell movement of the Tetrimino - in the direction pressed. - Holding down the move button triggers an Auto-Repeat movement - that allows the player to move a Tetrimino from one side of the Matrix - to the other in about 0.5 seconds. - This is essential on higher levels when the Fall Speed of a Tetrimino is very fast. - There is a slight delay between the time the move button is pressed - and the time when Auto-Repeat kicks in : s.AUTO_SHIFT_DELAY. - This delay prevents unwanted extra movement of a Tetrimino. - Auto-Repeat only affects Left/Right movement. - Auto-Repeat continues to the Next Tetrimino (after Lock Down) - as long as the move button remains pressed. - In addition, when Auto-Repeat begins, - and the player then holds the opposite direction button, - the Tetrimino then begins moving the opposite direction with the initial delay. - When any single button is then released, - the Tetrimino should again move in the direction still held, - with the Auto-Shift delay applied once more. - """ - if ( - not self.frames.playing - or self.frames.paused - or time.time() < self.auto_repeat_delay - ): - return - - if self.actions_to_repeat: - self.do(self.actions_to_repeat[-1]) - - def do(self, action): - """The player can move, rotate, Soft Drop, Hard Drop, - and Hold the falling Tetrimino (i.e., the Tetrimino in play). - """ - if action == s.PAUSE: - self.frames.pause(not self.frames.paused) - - if not self.frames.playing or self.frames.paused or not self.piece: - return - - for mino in self.piece.minoes: - mino.shine(0) - - if action == s.MOVE_LEFT: - if self.piece.move(L, 0): - self.lock_wait() - - elif action == s.MOVE_RIGHT: - if self.piece.move(R, 0): - self.lock_wait() - - elif action == s.ROTATE_CLOCKWISE: - if self.piece.rotate(direction=CLOCKWISE): - self.rotate_sound.play() - self.lock_wait() - - elif action == s.ROTATE_COUNTERCLOCKWISE: - if self.piece.rotate(direction=COUNTERCLOCKWISE): - self.rotate_sound.play() - self.lock_wait() - - elif action == s.SOFT_DROP: - if self.piece.soft_drop(): - self.drop_signal.emit(1) - - elif action == s.HARD_DROP: - trail = self.piece.hard_drop() - self.hard_drop_sound.play() - self.top_left_corner += Point(0, self.HARD_DROP_MOVEMENT * Block.side) - self.drop_signal.emit(2 * trail) - QtCore.QTimer.singleShot(ANIMATION_DELAY, self.after_hard_drop) - self.lock_phase() - - elif action == s.HOLD: - self.frames.hold() - - def after_hard_drop(self): - """ Reset the animation movement of the Matrix on a hard drop """ - self.top_left_corner -= Point(0, self.HARD_DROP_MOVEMENT * Block.side) - - def lock_wait(self): - self.fall_delay = time.time() + (self.speed + self.lock_delay) / 1000 - - def fall(self): - """ - Once a Tetrimino is generated, - it immediately drops one row (if no existing Block is in its path). - From here, it begins its descent to the bottom of the Matrix. - The Tetrimino will fall at its normal Fall Speed - whether or not it is being manipulated by the player. - """ - if self.piece: - if self.piece.move(0, 1): - self.lock_wait() - else: - if time.time() >= self.fall_delay: - self.lock_phase() - - def lock_phase(self): - """ - The player can perform the same actions on a Tetrimino in this phase - as he/she can in the Falling Phase, - as long as the Tetrimino is not yet Locked Down. - A Tetrimino that is Hard Dropped Locks Down immediately. - However, if a Tetrimino naturally falls or Soft Drops onto a landing Surface, - it is given a delay (self.fall_delay) on a Lock Down Timer - before it actually Locks Down. - """ - # Enter minoes into the matrix - for mino in self.piece.minoes: - if mino.coord.y() >= 0: - self.cells[mino.coord.y()][mino.coord.x()] = mino - mino.shine(glowing=2, delay=ANIMATION_DELAY) - self.update() - - if all(mino.coord.y() <= 1 for mino in self.piece.minoes): - self.frames.game_over() - return - - """ - In this phase, - the engine looks for patterns made from Locked Down Blocks in the Matrix. - Once a pattern has been matched, - it can trigger any number of Tetris variant-related effects. - The classic pattern is the Line Clear pattern. - This pattern is matched when one or more rows of 10 horizontally aligned - Matrix cells are occupied by Blocks. - The matching Blocks are then marked for removal on a hit list. - Blocks on the hit list are cleared from the Matrix at a later time - in the Eliminate Phase. - """ - # Dectect complete lines - self.complete_lines = [] - for y, row in enumerate(self.cells): - if all(cell for cell in row): - self.complete_lines.append(y) - for block in row: - block.shine() - self.spotlight = row[self.COLUMNS // 2].coord - self.auto_repeat_timer.stop() - self.lock_signal.emit(len(self.complete_lines), self.piece.t_spin) - - if not self.complete_lines: - self.frames.new_piece() - return - - self.fall_timer.stop() - QtCore.QTimer.singleShot(self.LINE_CLEAR_DELAY, self.eliminate_phase) - - def eliminate_phase(self): - """ - Any Minos marked for removal, i.e., on the hit list, - are cleared from the Matrix in this phase. - If this results in one or more complete 10-cell rows in the Matrix - becoming unoccupied by Minos, - then all Minos above that row(s) collapse, - or fall by the number of complete rows cleared from the Matrix. - """ - for y in self.complete_lines: - del self.cells[y] - self.cells.insert(0, self.empty_row()) - - for y, row in enumerate(self.cells): - for x, block in enumerate(row): - if block: - block.coord.setX(x) - block.coord.setY(y) - - self.update() - self.auto_repeat_wait() - self.auto_repeat_timer.start(settings[s.DELAYS][s.AUTO_REPEAT_RATE]) - - self.frames.new_piece() - - def paintEvent(self, event): - """ - Draws grid, actual piece, blocks in the Matrix and show texts - """ - painter = QtGui.QPainter(self) - painter.setRenderHint(QtGui.QPainter.Antialiasing) - - self.paint_grid(painter) - - if not self.frames.paused or self.game_over: - if self.piece: - if settings[s.OTHER][s.GHOST]: - self.ghost = Ghost(self.piece) - self.spotlight = self.ghost.minoes[0].coord - self.paint_piece(painter, self.ghost) - self.paint_piece(painter, self.piece) - - # Blocks in matrix - for row in self.cells: - for block in row: - if block: - block.paint(painter, self.top_left_corner, self.spotlight) - - if self.frames.playing and self.frames.paused: - painter.setFont(QtGui.QFont("Maassslicer", 0.75 * Block.side)) - painter.setPen(self.TEXT_COLOR) - painter.drawText( - self.rect(), - QtCore.Qt.AlignCenter | QtCore.Qt.TextWordWrap, - self.tr("PAUSE\n\nPress %s\nto resume") % settings[s.KEYBOARD][s.PAUSE], - ) - if self.game_over: - painter.setFont(QtGui.QFont("Maassslicer", Block.side)) - painter.setPen(self.TEXT_COLOR) - painter.drawText( - self.rect(), - QtCore.Qt.AlignCenter | QtCore.Qt.TextWordWrap, - self.tr("GAME\nOVER"), - ) - if self.temporary_texts: - painter.setFont(self.temporary_text_font) - painter.setPen(self.TEXT_COLOR) - painter.drawText( - self.rect(), - QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap, - "\n\n".join(self.temporary_texts), - ) - - def resizeEvent(self, event): - super().resizeEvent(event) - self.temporary_text_font = QtGui.QFont("Maassslicer", Block.side) - - def show_temporary_text(self, text): - self.temporary_texts.append(text.upper()) - self.font = self.temporary_text_font - self.update() - QtCore.QTimer.singleShot(self.TEMPORARY_TEXT_DURATION, self.delete_text) - - def delete_text(self): - del self.temporary_texts[0] - self.update() - - def focusOutEvent(self, event): - if self.frames.playing: - self.frames.pause(True) - - -class HoldQueue(Grid): - """ - The Hold Queue allows the player to “hold” a falling Tetrimino for as long as they wish. - Holding a Tetrimino releases the Tetrimino already in the Hold Queue (if one exists). - """ - - def paintEvent(self, event): - if not settings[s.OTHER][s.HOLD_ENABLED]: - return - - super().paintEvent(event) - - -class NextQueue(Grid): - """ - The Next Queue allows the player to see the Next Tetrimino that will be generated - and put into play. - """ - - ROWS = 16 - COLUMNS = 6 - - def __init__(self, parent): - super().__init__(parent) - self.pieces = [] - - def new_piece(self): - self.pieces = self.pieces[1:] + [Tetromino()] - self.insert_pieces() - - def insert_pieces(self): - for y, piece in enumerate(self.pieces): - piece.insert_into(self, Point(3, 3 * y + 1)) - - def paintEvent(self, event=None): - if not settings[s.OTHER][s.SHOW_NEXT_QUEUE]: - return - - painter = QtGui.QPainter(self) - painter.setRenderHint(QtGui.QPainter.Antialiasing) - - if not self.frames.paused: - for piece in self.pieces: - self.paint_piece(painter, piece) - - -class Stats(QtWidgets.QWidget): - """ - Show informations relevant to the game being played is displayed on-screen. - Looks for patterns made from Locked Down Blocks in the Matrix and calculate score. - """ - - ROWS = 15 - COLUMNS = 6 - TEXT_COLOR = QtGui.QColor(0, 159, 218, 128) - - temporary_text = QtCore.Signal(str) - - def __init__(self, frames): - super().__init__(frames) - self.frames = frames - self.setStyleSheet("background-color: transparent") - - self.SCORES = ( - {"name": "", "": 0, "Mini T-Spin": 1, "T-Spin": 4}, - {"name": self.tr("Single"), "": 1, "Mini T-Spin": 2, "T-Spin": 8}, - {"name": self.tr("Double"), "": 3, "T-Spin": 12}, - {"name": self.tr("Triple"), "": 5, "T-Spin": 16}, - {"name": self.tr("Tetris"), "": 8}, - ) - - self.load_sounds() - - self.setSizePolicy( - QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding - ) - self.text_options = QtGui.QTextOption(QtCore.Qt.AlignRight) - self.text_options.setWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) - - self.clock = QtCore.QTimer() - self.clock.timeout.connect(self.tick) - - self.high_score = int(qsettings.value(self.tr("High score"), 0)) - - def load_sounds(self): - self.line_clear_sound = QtMultimedia.QSoundEffect(self) - self.line_clear_sound.setSource( - QtCore.QUrl.fromLocalFile( - os.path.join(SOUNDS_DIR, "line_clear.wav") - ) - ) - self.tetris_sound = QtMultimedia.QSoundEffect(self) - self.tetris_sound.setSource( - QtCore.QUrl.fromLocalFile( - os.path.join(SOUNDS_DIR, "tetris.wav") - ) - ) - for sound in self.line_clear_sound, self.tetris_sound: - sound.setVolume(settings[s.SOUND][s.EFFECTS_VOLUME]) - - def new_game(self): - self.level -= 1 - self.goal = 0 - self.complete_lines_total = 0 - self.score_total = 1 - self.t_spin_total = 0 - self.mini_t_spin_total = 0 - self.nb_back_to_back = 0 - self.back_to_back_scores = None - self.combo = -1 - self.combos_total = 0 - self.max_combo = 0 - self.chronometer = 0 - self.nb_tetro = 0 - self.clock.start(1000) - self.lines_stats = [0, 0, 0, 0, 0] - - def new_level(self): - self.level += 1 - self.goal += 5 * self.level - return self.level - - def update_score(self, nb_complete_lines, t_spin): - """ - The player scores points by performing Single, Double, Triple, - and Tetris Line Clears, as well as T-Spins and Mini T-Spins. - Soft and Hard Drops also award points. - There is a special bonus for Back-to-Backs, - which is when two actions such as a Tetris and T-Spin Double take place - without a Single, Double, or Triple Line Clear occurring between them. - Scoring for Line Clears, T-Spins, and Mini T-Spins are level dependent, - while Hard and Soft Drop point values remain constant. - """ - self.nb_tetro += 1 - if nb_complete_lines: - self.complete_lines_total += nb_complete_lines - self.lines_stats[nb_complete_lines] += 1 - if t_spin == "T-Spin": - self.t_spin_total += 1 - elif t_spin == "Mini T-Spin": - self.mini_t_spin_total += 1 - - score = self.SCORES[nb_complete_lines][t_spin] - - if score: - text = " ".join((t_spin, self.SCORES[nb_complete_lines]["name"])) - if (t_spin and nb_complete_lines) or nb_complete_lines == 4: - self.tetris_sound.play() - if 1 <= nb_complete_lines <= 3: - self.line_clear_sound.play() - - self.goal -= score - score = 100 * self.level * score - self.score_total += score - - self.temporary_text.emit(text + "\n{:n}".format(score)) - -# ============================================================================== -# Combo -# Bonus for complete lines on each consecutive lock downs -# if nb_complete_lines: -# ============================================================================== - if nb_complete_lines: - self.combo += 1 - if self.combo > 0: - if nb_complete_lines == 1: - combo_score = 20 * self.combo * self.level - else: - combo_score = 50 * self.combo * self.level - self.score_total += combo_score - self.max_combo = max(self.max_combo, self.combo) - self.combos_total += 1 - self.temporary_text.emit( - self.tr("COMBO x{:n}\n{:n}").format(self.combo, combo_score) - ) - else: - self.combo = -1 - -# ============================================================================== -# Back-to_back sequence -# Two major bonus actions, such as two Tetrises, performed without -# a Single, Double, or Triple Line Clear occurring between them. -# Bonus for Tetrises, T-Spin Line Clears, and Mini T-Spin Line Clears -# performed consecutively in a B2B sequence. -# ============================================================================== - if (t_spin and nb_complete_lines) or nb_complete_lines == 4: - if self.back_to_back_scores is not None: - self.back_to_back_scores.append(score // 2) - else: - # The first Line Clear in the Back-to-Back sequence - # does not receive the Back-to-Back Bonus. - self.back_to_back_scores = [] - elif nb_complete_lines and not t_spin: - # A Back-to-Back sequence is only broken by a Single, Double, or Triple Line Clear. - # Locking down a Tetrimino without clearing a line - # or holding a Tetrimino does not break the Back-to-Back sequence. - # T-Spins and Mini T-Spins that do not clear any lines - # do not receive the Back-to-Back Bonus; instead they are scored as normal. - # They also cannot start a Back-to-Back sequence, however, - # they do not break an existing Back-to-Back sequence. - if self.back_to_back_scores: - b2b_score = sum(self.back_to_back_scores) - self.score_total += b2b_score - self.nb_back_to_back += 1 - self.temporary_text.emit( - self.tr("BACK TO BACK\n{:n}").format(b2b_score) - ) - self.back_to_back_scores = None - - self.high_score = max(self.score_total, self.high_score) - self.update() - - def update_drop_score(self, n): - """ Tetrimino is Soft Dropped for n lines or Hard Dropped for (n/2) lines""" - self.score_total += n - self.high_score = max(self.score_total, self.high_score) - self.update() - - def tick(self): - self.chronometer += 1 - self.update() - - def paintEvent(self, event): - if not self.frames.playing and not self.frames.board.game_over: - return - - painter = QtGui.QPainter(self) - painter.setFont(self.font) - painter.setPen(self.TEXT_COLOR) - - painter.drawText( - QtCore.QRectF(self.rect()), self.text(sep="\n\n"), self.text_options - ) - - def text(self, full_stats=False, sep="\n"): - text = ( - self.tr("Score: ") - + locale.format("%i", self.score_total, grouping=True, monetary=True) - + sep - + self.tr("High score: ") - + locale.format("%i", self.high_score, grouping=True, monetary=True) - + sep - + self.tr("Time: {}\n").format( - time.strftime("%H:%M:%S", time.gmtime(self.chronometer)) - ) - + sep - + self.tr("Level: ") - + locale.format("%i", self.level, grouping=True, monetary=True) - + sep - + self.tr("Goal: ") - + locale.format("%i", self.goal, grouping=True, monetary=True) - + sep - + self.tr("Lines: ") - + locale.format( - "%i", self.complete_lines_total, grouping=True, monetary=True - ) - + sep - + self.tr("Mini T-Spins: ") - + locale.format("%i", self.mini_t_spin_total, grouping=True, monetary=True) - + sep - + self.tr("T-Spins: ") - + locale.format("%i", self.t_spin_total, grouping=True, monetary=True) - + sep - + self.tr("Back-to-back: ") - + locale.format("%i", self.nb_back_to_back, grouping=True, monetary=True) - + sep - + self.tr("Max combo: ") - + locale.format("%i", self.max_combo, grouping=True, monetary=True) - + sep - + self.tr("Combos: ") - + locale.format("%i", self.combos_total, grouping=True, monetary=True) - ) - if full_stats: - minutes = self.chronometer / 60 - text += ( - "\n" - + sep - + self.tr("Lines per minute: {:.1f}").format( - self.complete_lines_total / minutes - ) - + sep - + self.tr("Tetrominos locked down: ") - + locale.format("%i", self.nb_tetro, grouping=True, monetary=True) - + sep - + self.tr("Tetrominos per minute: {:.1f}").format( - self.nb_tetro / minutes - ) - + sep - ) - text += sep.join( - score_type["name"] - + ": " - + locale.format("%i", nb, grouping=True, monetary=True) - for score_type, nb in tuple(zip(self.SCORES, self.lines_stats))[1:] - ) - return text - - def resizeEvent(self, event): - self.font = QtGui.QFont("PixelCaps!", Block.side / 3.5) - - -class AspectRatioWidget(QtWidgets.QWidget): - """ - Keeps aspect ratio of child widget on resize - https://stackoverflow.com/questions/48043469/how-to-lock-aspect-ratio-while-resizing-the-window - """ - - def __init__(self, widget, parent): - super().__init__(parent) - self.aspect_ratio = widget.size().width() / widget.size().height() - self.setLayout(QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight, self)) - # add spacer, then widget, then spacer - self.layout().addItem(QtWidgets.QSpacerItem(0, 0)) - self.layout().addWidget(widget) - self.layout().addItem(QtWidgets.QSpacerItem(0, 0)) - - def resizeEvent(self, e): - w = e.size().width() - h = e.size().height() - - if w / h > self.aspect_ratio: # too wide - self.layout().setDirection(QtWidgets.QBoxLayout.LeftToRight) - widget_stretch = h * self.aspect_ratio - outer_stretch = (w - widget_stretch) / 2 + 0.5 - else: # too tall - self.layout().setDirection(QtWidgets.QBoxLayout.TopToBottom) - widget_stretch = w / self.aspect_ratio - outer_stretch = (h - widget_stretch) / 2 + 0.5 - - self.layout().setStretch(0, outer_stretch) - self.layout().setStretch(1, widget_stretch) - self.layout().setStretch(2, outer_stretch) - - -class Frames(QtWidgets.QWidget): - """ - Display Hold queue, Matrix, Next piece, Next queue and Stats. - Manage interactions between them. - """ - - def __init__(self, parent): - super().__init__(parent) - self.setSizePolicy( - QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding - ) - - self.playing = False - self.paused = False - - self.backgrounds = tuple( - QtGui.QImage(os.path.join(BG_IMAGE_DIR, entry.name)) - for entry in os.scandir(BG_IMAGE_DIR) - if entry.is_file() and ".jpg" in entry.name - ) - self.reset_backgrounds() - - self.load_music() - - self.hold_queue = HoldQueue(self) - self.board = Matrix(self) - self.next_piece = Grid(self) - self.stats = Stats(self) - self.next_queue = NextQueue(self) - - self.matrices = (self.hold_queue, self.board, self.next_piece) - self.columns = sum(matrix.COLUMNS + 1 for matrix in self.matrices) + 1 - self.rows = self.board.ROWS + 3 - - w = QtWidgets.QWidget(self) - w.setStyleSheet("background-color: transparent") - grid = QtWidgets.QGridLayout() - for x in range(self.rows): - grid.setRowStretch(x, 1) - for y in range(self.columns): - grid.setColumnStretch(y, 1) - grid.setSpacing(0) - x, y = 1, 1 - grid.addWidget( - self.hold_queue, y, x, self.hold_queue.ROWS, self.hold_queue.COLUMNS + 1 - ) - x += self.hold_queue.COLUMNS + 1 - grid.addWidget(self.board, y, x, self.board.ROWS + 2, self.board.COLUMNS + 1) - x += self.board.COLUMNS + 1 - grid.addWidget( - self.next_piece, y, x, self.next_piece.ROWS, self.next_piece.COLUMNS + 1 - ) - x, y = 0, self.hold_queue.ROWS + 2 - grid.addWidget(self.stats, y, x, self.stats.ROWS, self.stats.COLUMNS + 1) - x += self.stats.COLUMNS + self.board.COLUMNS + 3 - grid.addWidget( - self.next_queue, y, x, self.next_queue.ROWS, self.next_queue.COLUMNS + 1 - ) - w.setLayout(grid) - w.resize(self.columns, self.rows) - asw = AspectRatioWidget(w, self) - layout = QtWidgets.QGridLayout() - layout.addWidget(asw) - self.setLayout(layout) - - self.stats.temporary_text.connect(self.board.show_temporary_text) - self.board.drop_signal.connect(self.stats.update_drop_score) - self.board.lock_signal.connect(self.stats.update_score) - - def load_music(self): - playlist = QtMultimedia.QMediaPlaylist(self) - for entry in os.scandir(SOUNDS_DIR): - if entry.is_file() and ".mp3" in entry.name: - music_path = QtMultimedia.QMediaContent( - QtCore.QUrl.fromLocalFile( - os.path.join(SOUNDS_DIR, entry.name) - ) - ) - playlist.addMedia(music_path) - playlist.setPlaybackMode(QtMultimedia.QMediaPlaylist.Loop) - self.music = QtMultimedia.QMediaPlayer(self) - self.music.setAudioRole(QtMultimedia.QAudio.GameRole) - self.music.setPlaylist(playlist) - self.music.setVolume(settings[s.SOUND][s.MUSIC_VOLUME]) - - def resizeEvent(self, event): - Block.side = 0.9 * min(self.width() // self.columns, self.height() // self.rows) - self.resize_bg_image() - - def resize_bg_image(self): - self.resized_bg_image = QtGui.QPixmap.fromImage(self.bg_image) - self.resized_bg_image = self.resized_bg_image.scaled( - self.size(), - QtCore.Qt.KeepAspectRatioByExpanding, - QtCore.Qt.SmoothTransformation, - ) - self.resized_bg_image = self.resized_bg_image.copy( - (self.resized_bg_image.width() - self.width()) // 2, - (self.resized_bg_image.height() - self.height()) // 2, - self.width(), - self.height(), - ) - - def reset_backgrounds(self): - self.backgrounds_cycle = itertools.cycle(self.backgrounds) - self.set_new_background() - - def set_new_background(self): - self.bg_image = QtGui.QImage(next(self.backgrounds_cycle)) - self.resize_bg_image() - - def new_game(self): - if self.playing: - answer = QtWidgets.QMessageBox.question( - self, - self.tr("New game"), - self.tr("A game is in progress.\n" "Do you want to abord it?"), - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, - QtWidgets.QMessageBox.Cancel, - ) - if answer == QtWidgets.QMessageBox.Cancel: - self.pause(False) - return - self.music.stop() - - self.reset_backgrounds() - self.stats.level, ok = QtWidgets.QInputDialog.getInt( - self, - self.tr("New game"), - self.tr("Start level:"), - 1, - 1, - 15, - flags=QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint, - ) - if not ok: - return - - self.playing = True - self.music.play() - self.hold_queue.piece = None - self.stats.new_game() - self.board.new_game() - self.next_queue.pieces = [Tetromino() for _ in range(5)] - self.next_queue.insert_pieces() - self.next_piece.insert(Tetromino()) - self.pause(False) - self.new_level() - - def new_level(self): - self.set_new_background() - level = self.stats.new_level() - self.board.new_level(level) - self.new_piece() - - def new_piece(self): - if self.stats.goal <= 0: - self.new_level() - self.board.insert(self.next_piece.piece) - self.board.lock_wait() - self.next_piece.insert(self.next_queue.pieces[0]) - self.next_queue.new_piece() - self.hold_enabled = settings[s.OTHER][s.HOLD_ENABLED] - self.update() - - if not self.board.piece.move(0, 0): - self.game_over() - return - - self.board.fall_timer.start(self.board.speed) - - def pause(self, paused): - if not self.playing: - return - - if paused: - self.paused = True - self.update() - self.board.fall_timer.stop() - self.stats.clock.stop() - self.board.auto_repeat_timer.stop() - self.music.pause() - else: - self.board.text = "" - self.update() - QtCore.QTimer.singleShot(1000, self.resume) - - def resume(self): - self.paused = False - self.update() - self.board.fall_timer.start(self.board.speed) - self.stats.clock.start(1000) - self.board.auto_repeat_timer.start(settings[s.DELAYS][s.AUTO_REPEAT_RATE]) - self.music.play() - - def hold(self): - """ - Using the Hold command places the Tetrimino in play into the Hold Queue. - The previously held Tetrimino (if one exists) will then start falling - from the top of the Matrix, - beginning from its generation position and North Facing orientation. - Only one Tetrimino may be held at a time. - A Lock Down must take place between Holds. - For example, at the beginning, the first Tetrimino is generated and begins to fall. - The player decides to hold this Tetrimino. - Immediately the Next Tetrimino is generated from the Next Queue and begins to fall. - The player must first Lock Down this Tetrimino before holding another Tetrimino. - In other words, you may not Hold the same Tetrimino more than once. - """ - - if not self.hold_enabled: - return - - piece = self.hold_queue.piece - if piece: - self.hold_queue.insert(self.board.piece) - self.board.insert(piece) - self.update() - else: - self.hold_queue.insert(self.board.piece) - self.new_piece() - self.hold_enabled = False - - def game_over(self): - self.board.fall_timer.stop() - self.stats.clock.stop() - self.board.auto_repeat_timer.stop() - self.music.stop() - self.playing = False - self.board.game_over = True - if self.stats.score_total == self.stats.high_score: - text = self.tr("Congratulations!\nYou have the high score!") + "\n\n" - else: - text = "" - text += self.stats.text(full_stats=True) - qsettings.setValue(self.tr("High score"), self.stats.high_score) - QtWidgets.QMessageBox.information(self, self.tr("Game over"), text) - - def paintEvent(self, event): - painter = QtGui.QPainter(self) - painter.drawPixmap(self.rect(), self.resized_bg_image) - - -class KeyButton(QtWidgets.QPushButton): - """ Button widget capturing key name on focus """ - - names = { - value: name.replace("Key_", "") - for name, value in QtCore.Qt.__dict__.items() - if "Key_" in name - } - - def __init__(self, *args): - super().__init__(*args) - - def keyPressEvent(self, event): - key = event.key() - self.setText(self.names[key]) - - -class SettingsGroup(QtWidgets.QGroupBox): - """ Group box of a type of settings """ - - def __init__(self, group, parent, cls): - super().__init__(group, parent) - layout = QtWidgets.QFormLayout(self) - self.widgets = {} - for setting, value in settings[group].items(): - if cls == KeyButton: - widget = KeyButton(value) - elif cls == QtWidgets.QCheckBox: - widget = QtWidgets.QCheckBox(setting) - widget.setChecked(value) - elif cls == QtWidgets.QSpinBox: - widget = QtWidgets.QSpinBox() - widget.setRange(0, 1000) - widget.setValue(value) - widget.setSuffix(" ms") - elif cls == QtWidgets.QSlider: - widget = QtWidgets.QSlider(QtCore.Qt.Horizontal) - widget.setValue(value) - if cls == QtWidgets.QCheckBox: - layout.addRow(widget) - else: - layout.addRow(setting, widget) - self.widgets[setting] = widget - self.setLayout(layout) - - -class SettingsDialog(QtWidgets.QDialog): - """ Show settings dialog """ - - def __init__(self, parent): - super().__init__(parent) - self.setWindowTitle(self.tr("Settings")) - self.setModal(True) - - vlayout = QtWidgets.QVBoxLayout() - - self.groups = {} - self.groups[s.KEYBOARD] = SettingsGroup(s.KEYBOARD, self, KeyButton) - self.groups[s.DELAYS] = SettingsGroup(s.DELAYS, self, QtWidgets.QSpinBox) - self.groups[s.SOUND] = SettingsGroup(s.SOUND, self, QtWidgets.QSlider) - self.groups[s.OTHER] = SettingsGroup(s.OTHER, self, QtWidgets.QCheckBox) - - for group in self.groups.values(): - vlayout.addWidget(group) - - buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel - ) - buttons.accepted.connect(self.ok) - buttons.rejected.connect(self.close) - vlayout.addWidget(buttons) - - self.groups[s.SOUND].widgets[s.MUSIC_VOLUME].valueChanged.connect( - parent.frames.music.setVolume - ) - - self.setLayout(vlayout) - self.show() - - def ok(self): - """ Save settings """ - - for group, elements in self.groups.items(): - for setting, widget in elements.widgets.items(): - if isinstance(widget, KeyButton): - value = widget.text() - elif isinstance(widget, QtWidgets.QCheckBox): - value = widget.isChecked() - elif isinstance(widget, QtWidgets.QSpinBox): - value = widget.value() - elif isinstance(widget, QtWidgets.QSlider): - value = widget.value() - settings[group][setting] = value - qsettings.setValue(group + "/" + setting, value) - self.close() - - -class SettingStrings(QtCore.QObject): - """ - Setting string for translation - """ - - def __init__(self): - super().__init__() - - self.KEYBOARD = self.tr("Keyboard settings") - self.MOVE_LEFT = self.tr("Move left") - self.MOVE_RIGHT = self.tr("Move right") - self.ROTATE_CLOCKWISE = self.tr("Rotate clockwise") - self.ROTATE_COUNTERCLOCKWISE = self.tr("Rotate counterclockwise") - self.SOFT_DROP = self.tr("Soft drop") - self.HARD_DROP = self.tr("Hard drop") - self.HOLD = self.tr("Hold") - self.PAUSE = self.tr("Pause") - self.OTHER = self.tr("Other settings") - - self.DELAYS = self.tr("Delays") - self.AUTO_SHIFT_DELAY = self.tr("Auto-shift delay") - self.AUTO_REPEAT_RATE = self.tr("Auto-repeat rate") - - self.SOUND = self.tr("Sound") - self.MUSIC_VOLUME = self.tr("Music volume") - self.EFFECTS_VOLUME = self.tr("Effects volume") - - self.GHOST = self.tr("Show ghost piece") - self.SHOW_NEXT_QUEUE = self.tr("Show next queue") - self.HOLD_ENABLED = self.tr("Hold enabled") - - -class Window(QtWidgets.QMainWindow): - """ Main window """ - - def __init__(self): - self.set_locale() - - super().__init__() - self.setWindowTitle(__title__.upper()) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - - self.setWindowIcon(QtGui.QIcon(ICON_PATH)) - # Windows' taskbar icon - try: - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( - ".".join((__author__, __title__, __version__)) - ) - except AttributeError: - pass - - self.load_settings() - - # Stylesheet - try: - import qdarkstyle - except ImportError: - pass - else: - self.setStyleSheet(qdarkstyle.load_stylesheet_from_environment()) - - for font_name in "maass slicer Italic.ttf", "PixelCaps!.otf": - QtGui.QFontDatabase.addApplicationFont( - os.path.join(FONTS_DIR, font_name) - ) - - self.frames = Frames(self) - self.setCentralWidget(self.frames) - self.hold_queue = self.frames.hold_queue - self.board = self.frames.board - self.stats = self.frames.stats - - self.menu = self.menuBar() - - geometry = qsettings.value("WindowGeometry") - if geometry: - self.restoreGeometry(geometry) - else: - self.resize(839, 807) - self.setWindowState( - QtCore.Qt.WindowStates( - int(qsettings.value("WindowState", QtCore.Qt.WindowActive)) - ) - ) - - self.show() - self.frames.new_game() - - def set_locale(self): - app = QtWidgets.QApplication.instance() - - # Set appropriate thounsand separator characters - locale.setlocale(locale.LC_ALL, "") - # Qt - language = QtCore.QLocale.system().name()[:2] - - qt_translator = QtCore.QTranslator(app) - qt_translation_path = QtCore.QLibraryInfo.location( - QtCore.QLibraryInfo.TranslationsPath - ) - if qt_translator.load("qt_" + language, qt_translation_path): - app.installTranslator(qt_translator) - - tetris2000_translator = QtCore.QTranslator(app) - if tetris2000_translator.load(language, LOCALE_PATH): - app.installTranslator(tetris2000_translator) - - def load_settings(self): - global qsettings - qsettings = QtCore.QSettings(__author__, __title__) - global s - s = SettingStrings() - global settings - settings = collections.OrderedDict( - [ - ( - s.KEYBOARD, - collections.OrderedDict( - [ - ( - s.MOVE_LEFT, - qsettings.value(s.KEYBOARD + "/" + s.MOVE_LEFT, "Left"), - ), - ( - s.MOVE_RIGHT, - qsettings.value( - s.KEYBOARD + "/" + s.MOVE_RIGHT, "Right" - ), - ), - ( - s.ROTATE_CLOCKWISE, - qsettings.value( - s.KEYBOARD + "/" + s.ROTATE_CLOCKWISE, "Up" - ), - ), - ( - s.ROTATE_COUNTERCLOCKWISE, - qsettings.value( - s.KEYBOARD + "/" + s.ROTATE_COUNTERCLOCKWISE, - "Control", - ), - ), - ( - s.SOFT_DROP, - qsettings.value(s.KEYBOARD + "/" + s.SOFT_DROP, "Down"), - ), - ( - s.HARD_DROP, - qsettings.value( - s.KEYBOARD + "/" + s.HARD_DROP, "Space" - ), - ), - ( - s.HOLD, - qsettings.value(s.KEYBOARD + "/" + s.HOLD, "Shift"), - ), - ( - s.PAUSE, - qsettings.value(s.KEYBOARD + "/" + s.PAUSE, "Escape"), - ), - ] - ), - ), - ( - s.DELAYS, - collections.OrderedDict( - [ - ( - s.AUTO_SHIFT_DELAY, - int( - qsettings.value( - s.DELAYS + "/" + s.AUTO_SHIFT_DELAY, 170 - ) - ), - ), - ( - s.AUTO_REPEAT_RATE, - int( - qsettings.value( - s.DELAYS + "/" + s.AUTO_REPEAT_RATE, 20 - ) - ), - ), - ] - ), - ), - ( - s.SOUND, - collections.OrderedDict( - [ - ( - s.MUSIC_VOLUME, - int( - qsettings.value(s.SOUND + "/" + s.MUSIC_VOLUME, 25) - ), - ), - ( - s.EFFECTS_VOLUME, - int( - qsettings.value( - s.SOUND + "/" + s.EFFECTS_VOLUME, 50 - ) - ), - ), - ] - ), - ), - ( - s.OTHER, - collections.OrderedDict( - [ - ( - s.GHOST, - bool(qsettings.value(s.OTHER + "/" + s.GHOST, True)), - ), - ( - s.SHOW_NEXT_QUEUE, - bool( - qsettings.value( - s.OTHER + "/" + s.SHOW_NEXT_QUEUE, True - ) - ), - ), - ( - s.HOLD_ENABLED, - bool( - qsettings.value( - s.OTHER + "/" + s.HOLD_ENABLED, True - ) - ), - ), - ] - ), - ), - ] - ) - - def menuBar(self): - menu = super().menuBar() - - new_game_action = QtWidgets.QAction(self.tr("&New game"), self) - new_game_action.triggered.connect(self.frames.new_game) - menu.addAction(new_game_action) - - settings_action = QtWidgets.QAction(self.tr("&Settings"), self) - settings_action.triggered.connect(self.show_settings_dialog) - menu.addAction(settings_action) - - about_action = QtWidgets.QAction(self.tr("&About"), self) - about_action.triggered.connect(self.about) - menu.addAction(about_action) - return menu - - def show_settings_dialog(self): - SettingsDialog(self).exec_() - if self.frames.music.volume() and self.frames.playing: - self.frames.music.play() - else: - self.frames.music.pause() - self.frames.board.apply_settings() - self.frames.stats.line_clear_sound.setVolume( - settings[s.SOUND][s.EFFECTS_VOLUME] - ) - self.frames.stats.tetris_sound.setVolume(settings[s.SOUND][s.EFFECTS_VOLUME]) - if self.frames.playing: - self.frames.hold_enabled = settings[s.OTHER][s.HOLD_ENABLED] - self.frames.pause(False) - - def about(self): - QtWidgets.QMessageBox.about( - self, - __title__, - self.tr( - "Tetris® clone\n" - "by Adrien Malingrey\n\n" - "Tetris Game Design by Alekseï Pajitnov\n" - "Graphism inspired by Tetris Effect\n" - "Window style sheet by Colin Duquesnoy\n" - "PixelCaps! font by Markus Koellmann\n" - "Maass slicer font by Peter Wiegel\n" - "Traditional song Korobeiniki arranged by Kobashik\n" - "Sound effects made with voc-one by Simple-Media\n" - "Background images found on xshyfc.com" - ), - ) - if self.playing: - self.pause(False) - - def closeEvent(self, event): - if self.frames.playing: - answer = QtWidgets.QMessageBox.question( - self, - self.tr("Quit game?"), - self.tr("A game is in progress.\nDo you want to abord it?"), - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, - QtWidgets.QMessageBox.Cancel, - ) - if answer == QtWidgets.QMessageBox.Cancel: - event.ignore() - self.frames.pause(False) - return - - self.frames.music.stop() - - # Save settings - qsettings.setValue(self.tr("High score"), self.stats.high_score) - qsettings.setValue("WindowGeometry", self.saveGeometry()) - qsettings.setValue("WindowState", int(self.windowState())) - - -def main(args=[]): - app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(args) - win = Window() - return app.exec_() - - -if __name__ == "__main__": - return_code = main(sys.argv) - sys.exit(return_code) diff --git a/Tetris2000/__init__.py b/Tetris2000/__init__.py deleted file mode 100644 index b5a6c7b..0000000 --- a/Tetris2000/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import Tetris2000 diff --git a/Tetris2000/__main__.py b/Tetris2000/__main__.py deleted file mode 100644 index 8fa6f89..0000000 --- a/Tetris2000/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -import sys -from . import Tetris2000 - - -return_code = Tetris2000.main(sys.argv) -sys.exit(return_code) diff --git a/Tetris2000/data/backgrounds/00.jpg b/Tetris2000/data/backgrounds/00.jpg deleted file mode 100644 index a763934..0000000 Binary files a/Tetris2000/data/backgrounds/00.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/01.jpg b/Tetris2000/data/backgrounds/01.jpg deleted file mode 100644 index 37de1f5..0000000 Binary files a/Tetris2000/data/backgrounds/01.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/02.jpg b/Tetris2000/data/backgrounds/02.jpg deleted file mode 100644 index ccdfda5..0000000 Binary files a/Tetris2000/data/backgrounds/02.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/03.jpg b/Tetris2000/data/backgrounds/03.jpg deleted file mode 100644 index f30685e..0000000 Binary files a/Tetris2000/data/backgrounds/03.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/04.jpg b/Tetris2000/data/backgrounds/04.jpg deleted file mode 100644 index 04d18b9..0000000 Binary files a/Tetris2000/data/backgrounds/04.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/05.jpg b/Tetris2000/data/backgrounds/05.jpg deleted file mode 100644 index d80d6d0..0000000 Binary files a/Tetris2000/data/backgrounds/05.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/06.jpg b/Tetris2000/data/backgrounds/06.jpg deleted file mode 100644 index 26038c5..0000000 Binary files a/Tetris2000/data/backgrounds/06.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/07.jpg b/Tetris2000/data/backgrounds/07.jpg deleted file mode 100644 index a24ed96..0000000 Binary files a/Tetris2000/data/backgrounds/07.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/08.jpg b/Tetris2000/data/backgrounds/08.jpg deleted file mode 100644 index de03801..0000000 Binary files a/Tetris2000/data/backgrounds/08.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/09.jpg b/Tetris2000/data/backgrounds/09.jpg deleted file mode 100644 index 868053f..0000000 Binary files a/Tetris2000/data/backgrounds/09.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/10.jpg b/Tetris2000/data/backgrounds/10.jpg deleted file mode 100644 index 90f92c0..0000000 Binary files a/Tetris2000/data/backgrounds/10.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/11.jpg b/Tetris2000/data/backgrounds/11.jpg deleted file mode 100644 index df6a211..0000000 Binary files a/Tetris2000/data/backgrounds/11.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/12.jpg b/Tetris2000/data/backgrounds/12.jpg deleted file mode 100644 index 334a6b1..0000000 Binary files a/Tetris2000/data/backgrounds/12.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/13.jpg b/Tetris2000/data/backgrounds/13.jpg deleted file mode 100644 index dfea4f9..0000000 Binary files a/Tetris2000/data/backgrounds/13.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/14.jpg b/Tetris2000/data/backgrounds/14.jpg deleted file mode 100644 index e1adb14..0000000 Binary files a/Tetris2000/data/backgrounds/14.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/15.jpg b/Tetris2000/data/backgrounds/15.jpg deleted file mode 100644 index d1d8d92..0000000 Binary files a/Tetris2000/data/backgrounds/15.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/16.jpg b/Tetris2000/data/backgrounds/16.jpg deleted file mode 100644 index eac4c16..0000000 Binary files a/Tetris2000/data/backgrounds/16.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/17.jpg b/Tetris2000/data/backgrounds/17.jpg deleted file mode 100644 index 5512219..0000000 Binary files a/Tetris2000/data/backgrounds/17.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/18.jpg b/Tetris2000/data/backgrounds/18.jpg deleted file mode 100644 index a937504..0000000 Binary files a/Tetris2000/data/backgrounds/18.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/19.jpg b/Tetris2000/data/backgrounds/19.jpg deleted file mode 100644 index 904cb40..0000000 Binary files a/Tetris2000/data/backgrounds/19.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/20.jpg b/Tetris2000/data/backgrounds/20.jpg deleted file mode 100644 index b394175..0000000 Binary files a/Tetris2000/data/backgrounds/20.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/21.jpg b/Tetris2000/data/backgrounds/21.jpg deleted file mode 100644 index a0d0b8d..0000000 Binary files a/Tetris2000/data/backgrounds/21.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/22.jpg b/Tetris2000/data/backgrounds/22.jpg deleted file mode 100644 index 6144f1b..0000000 Binary files a/Tetris2000/data/backgrounds/22.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/23.jpg b/Tetris2000/data/backgrounds/23.jpg deleted file mode 100644 index 2f51c89..0000000 Binary files a/Tetris2000/data/backgrounds/23.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/24.jpg b/Tetris2000/data/backgrounds/24.jpg deleted file mode 100644 index 1ed33a4..0000000 Binary files a/Tetris2000/data/backgrounds/24.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/25.jpg b/Tetris2000/data/backgrounds/25.jpg deleted file mode 100644 index 55d6dfb..0000000 Binary files a/Tetris2000/data/backgrounds/25.jpg and /dev/null differ diff --git a/Tetris2000/data/backgrounds/deep-space-wallpaper-background.html.url b/Tetris2000/data/backgrounds/deep-space-wallpaper-background.html.url deleted file mode 100644 index f3ff059..0000000 --- a/Tetris2000/data/backgrounds/deep-space-wallpaper-background.html.url +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://xshyfc.com/deep-space-wallpaper-background.html?lang=fr diff --git a/Tetris2000/data/fonts/Creative Commons Lizenz.txt b/Tetris2000/data/fonts/Creative Commons Lizenz.txt deleted file mode 100644 index 3f5e028..0000000 --- a/Tetris2000/data/fonts/Creative Commons Lizenz.txt +++ /dev/null @@ -1,55 +0,0 @@ -Lizenzvertrag - -DAS URHEBERRECHTLICH GESCHTZTE WERK ODER DER SONSTIGE SCHUTZGEGENSTAND (WIE UNTEN BESCHRIEBEN) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE (CCPL ODER LIZENZVERTRAG) ZUR VERFGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER EINSCHLGIGE GESETZE GESCHTZT. - -DURCH DIE AUSBUNG EINES DURCH DIESEN LIZENZVERTRAG GEWHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. DER LIZENZGEBER RUMT IHNEN DIE HIER BESCHRIEBENEN RECHTE UNTER DER VORAUSSETZUNGEIN, DASS SIE SICH MIT DIESEN VERTRAGSBEDINGUNGEN EINVERSTANDEN ERKLREN. - -1. Definitionen - - 1. Unter einer Bearbeitung wird eine bersetzung oder andere Bearbeitung des Werkes verstanden, die Ihre persnliche geistige Schpfung ist. Eine freie Benutzung des Werkes wird nicht als Bearbeitung angesehen. - 2. Unter den Lizenzelementen werden die folgenden Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewhlt und in der Bezeichnung der Lizenz genannt werden: Namensnennung, Nicht-kommerziell, Weitergabe unter gleichen Bedingungen. - 3. Unter dem Lizenzgeber wird die natrliche oder juristische Person verstanden, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet. - 4. Unter einem Sammelwerk wird eine Sammlung von Werken, Daten oder anderen unabhngigen Elementen verstanden, die aufgrund der Auswahl oder Anordnung der Elemente eine persnliche geistige Schpfung ist. Darunter fallen auch solche Sammelwerke, deren Elemente systematisch oder methodisch angeordnet und einzeln mit Hilfe elektronischer Mittel oder auf andere Weise zugnglich sind (Datenbankwerke). Ein Sammelwerk wird im Zusammenhang mit dieser Lizenz nicht als Bearbeitung (wie oben beschrieben) angesehen. - 5. Mit SIE und Ihnen ist die natrliche oder juristische Person gemeint, die die durch diese Lizenz gewhrten Nutzungsrechte ausbt und die zuvor die Bedingungen dieser Lizenz im Hinblick auf das Werk nicht verletzt hat, oder die die ausdrckliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewhrten Nutzungsrechte trotz einer vorherigen Verletzung auszuben. - 6. Unter dem Schutzgegenstandwird das Werk oder Sammelwerk oder das Schutzobjekt eines verwandten Schutzrechts, das Ihnen unter den Bedingungen dieser Lizenz angeboten wird, verstanden - 7. Unter dem Urheber wird die natrliche Person verstanden, die das Werk geschaffen hat. - 8. Unter einem verwandten Schutzrecht wird das Recht an einem anderen urheberrechtlichen Schutzgegenstand als einem Werk verstanden, zum Beispiel einer wissenschaftlichen Ausgabe, einem nachgelassenen Werk, einem Lichtbild, einer Datenbank, einem Tontrger, einer Funksendung, einem Laufbild oder einer Darbietung eines ausbenden Knstlers. - 9. Unter dem Werk wird eine persnliche geistige Schpfung verstanden, die Ihnen unter den Bedingungen dieser Lizenz angeboten wird. - -2. Schranken des Urheberrechts. Diese Lizenz lsst smtliche Befugnisse unberhrt, die sich aus den Schranken des Urheberrechts,aus dem Erschpfungsgrundsatz oder anderen Beschrnkungen der Ausschlielichkeitsrechte des Rechtsinhabers ergeben. - -3. Lizenzierung. Unter den Bedingungen dieses Lizenzvertrages rumt Ihnen der Lizenzgeber ein lizenzgebhrenfreies, rumlich und zeitlich (fr die Dauer des Urheberrechts oder verwandten Schutzrechts) unbeschrnktes einfaches Nutzungsrecht ein, den Schutzgegenstand in der folgenden Art und Weise zu nutzen: - - 1. den Schutzgegenstand in krperlicher Form zu verwerten, insbesondere zu vervielfltigen, zu verbreiten und auszustellen; - 2. den Schutzgegenstand in unkrperlicher Form ffentlich wiederzugeben, insbesondere vorzutragen, aufzufhren und vorzufhren, ffentlich zugnglich zu machen, zu senden, durch Bild- und Tontrger wiederzugeben sowie Funksendungen und ffentliche Zugnglichmachungen wiederzugeben; - 3. den Schutzgegenstand auf Bild- oder Tontrger aufzunehmen, Lichtbilder davon herzustellen, weiterzusenden und in dem in a. und b. genannten Umfang zu verwerten; - 4. den Schutzgegenstand zu bearbeiten oder in anderer Weise umzugestalten und die Bearbeitungen zu verffentlichen und in dem in a. bis c. genannten Umfang zu verwerten; - -Die genannten Nutzungsrechte knnen fr alle bekannten Nutzungsarten ausgebt werden. Die genannten Nutzungsrechte beinhalten das Recht, solche Vernderungen an dem Werk vorzunehmen, die technisch erforderlich sind, um die Nutzungsrechte fr alle Nutzungsarten wahrzunehmen. Insbesondere sind davon die Anpassung an andere Medien und auf andere Dateiformate umfasst. - -4. Beschrnkungen. Die Einrumung der Nutzungsrechte gem Ziffer 3 erfolgt ausdrcklich nur unter den folgenden Bedingungen: - - 1. Sie drfen den Schutzgegenstand ausschlielich unter den Bedingungen dieser Lizenz vervielfltigen, verbreiten oder ffentlich wiedergeben, und Sie mssen stets eine Kopie oder die vollstndige Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz beifgen, wenn Sie den Schutzgegenstandvervielfltigen, verbreiten oder ffentlich wiedergeben. Sie drfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewhrten Rechte ndern oder beschrnken. Sie drfen den Schutzgegenstand nicht unterlizenzieren. Sie mssen alle Hinweise unverndert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie drfen den Schutzgegenstand mit keinen technischen Schutzmanahmen versehen, die den Zugang oder den Gebrauch des Schutzgegenstandes in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschrnkungen gelten auch fr den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet; sie verlangen aber nicht, dass das Sammelwerk insgesamt zum Gegenstand dieser Lizenz gemacht wird. Wenn Sie ein Sammelwerk erstellen, mssen Sie - soweit dies praktikabel ist - auf die Mitteilung eines Lizenzgebers oder Urhebers hin aus dem Sammelwerk jeglichen Hinweis auf diesen Lizenzgeber oder diesen Urheber entfernen. Wenn Sie den Schutzgegenstand bearbeiten, mssen Sie - soweit dies praktikabel ist- auf die Aufforderung eines Rechtsinhabers hin von der Bearbeitung jeglichen Hinweis auf diesen Rechtsinhaber entfernen. - 2. Sie drfen eine Bearbeitung ausschlielich unter den Bedingungen dieser Lizenz, einer spteren Version dieser Lizenz mit denselben Lizenzelementen wie diese Lizenz oder einer Creative Commons iCommons Lizenz, die dieselben Lizenzelemente wie diese Lizenz enthlt (z.B. Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 2.0 Japan), vervielfltigen, verbreiten oder ffentlich wiedergeben. Sie mssen stets eine Kopie oder die Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz oder einer anderen Lizenz der im vorhergehenden Satz beschriebenen Art beifgen, wenn Sie die Bearbeitung vervielfltigen, verbreiten oder ffentlich wiedergeben. Sie drfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewhrten Rechte ndern oder beschrnken, und Sie mssen alle Hinweise unverndert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie drfen eine Bearbeitung nicht mit technischen Schutzmanahmen versehen, die den Zugang oder den Gebrauch der Bearbeitung in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschrnkungen gelten auch fr eine Bearbeitung als Bestandteil eines Sammelwerkes; sie erfordern aber nicht, dass das Sammelwerk insgesamt zum Gegenstand dieser Lizenz gemacht wird. - 3. Wenn Sie den Schutzgegenstand oder eine Bearbeitung oder ein Sammelwerk vervielfltigen, verbreiten oder ffentlich wiedergeben, mssen Sie alle Urhebervermerke fr den Schutzgegenstand unverndert lassen und die Urheberschaft oder Rechtsinhaberschaft in einer der von Ihnen vorgenommenen Nutzung angemessenen Form anerkennen, indem Sie den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Urhebers oder Rechteinhabers nennen, wenn dieser angegeben ist. Dies gilt auch fr den Titel des Schutzgegenstandes, wenn dieser angeben ist, sowie - in einem vernnftigerweise durchfhrbaren Umfang - fr die mit dem Schutzgegenstand zu verbindende Internetadresse in Form des Uniform-Resource-Identifier (URI), wie sie der Lizenzgeber angegeben hat, sofern dies geschehen ist, es sei denn, diese Internetadresse verweist nicht auf den Urhebervermerk oder die Lizenzinformationen zu dem Schutzgegenstand. Bei einer Bearbeitung ist ein Hinweis darauf aufzufhren, in welcher Form der Schutzgegenstand in die Bearbeitung eingegangen ist (z.B. Franzsische bersetzung des ... (Werk) durch ... (Urheber) oder Das Drehbuch beruht auf dem Werk des ... (Urheber)). Ein solcher Hinweis kann in jeder angemessenen Weise erfolgen, wobei jedoch bei einer Bearbeitung, einer Datenbank oder einem Sammelwerk der Hinweis zumindest an gleicher Stelle und in ebenso aufflliger Weise zu erfolgen hat wie vergleichbare Hinweise auf andere Rechtsinhaber. - 4. Obwohl die gemss Ziffer 3 gewhrten Nutzungsrechte in umfassender Weise - ausgebt werden drfen, findet diese Erlaubnis ihre gesetzliche Grenze in - den Persnlichkeitsrechten der Urheber und ausbenden Knstler, deren berechtigte geistige und persnliche Interessen bzw. deren Ansehen oder Ruf nicht dadurch gefhrdet werden drfen, dass ein Schutzgegenstand ber das gesetzlich zulssige Ma hinaus beeintrchtigt wird. - -5. Gewhrleistung. Sofern dies von den Vertragsparteien nicht anderweitig schriftlich vereinbart,, bietet der Lizenzgeber keine Gewhrleistung fr die erteilten Rechte, auer fr den Fall, dass Mngel arglistig verschwiegen wurden. Fr Mngel anderer Art, insbesondere bei der mangelhaften Lieferung von Verkrperungen des Schutzgegenstandes, richtet sich die Gewhrleistung nach der Regelung, die die Person, die Ihnen den Schutzgegenstand zur Verfgung stellt, mit Ihnen auerhalb dieser Lizenz vereinbart, oder - wenn eine solche Regelung nicht getroffen wurde - nach den gesetzlichen Vorschriften. - -6. Haftung. ber die in Ziffer 5 genannte Gewhrleistung hinaus haftet Ihnen der Lizenzgeber nur fr Vorsatz und grobe Fahrlssigkeit. - -7. Vertragsende - - 1. Dieser Lizenzvertrag und die durch ihn eingerumten Nutzungsrechte enden automatisch bei jeder Verletzung der Vertragsbedingungen durch Sie. Fr natrliche und juristische Personen, die von Ihnen eine Bearbeitung, eine Datenbank oder ein Sammelwerk unter diesen Lizenzbedingungen erhalten haben, gilt die Lizenz jedoch weiter, vorausgesetzt, diese natrlichen oder juristischen Personen erfllen smtliche Vertragsbedingungen. Die Ziffern 1, 2, 5, 6, 7 und 8 gelten bei einer Vertragsbeendigung fort. - 2. Unter den oben genannten Bedingungen erfolgt die Lizenz auf unbegrenzte Zeit (fr die Dauer des Schutzrechts). Dennoch behlt sich der Lizenzgeber das Recht vor, den Schutzgegenstand unter anderen Lizenzbedingungen zu nutzen oder die eigene Weitergabe des Schutzgegenstandes jederzeit zu beenden, vorausgesetzt, dass solche Handlungen nicht dem Widerruf dieser Lizenz dienen (oder jeder anderen Lizenzierung, die auf Grundlage dieser Lizenz erfolgt ist oder erfolgen muss) und diese Lizenz wirksam bleibt, bis Sie unter den oben genannten Voraussetzungen endet. - -8. Schlussbestimmungen - - 1. Jedes Mal, wenn Sie den Schutzgegenstand vervielfltigen, verbreiten oder ffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz fr den Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingerumt hat. - 2. Jedes Mal, wenn Sie eine Bearbeitung vervielfltigen, verbreiten oder ffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz fr den ursprnglichen Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingerumt hat. - 3. Sollte eine Bestimmung dieses Lizenzvertrages unwirksam sein, so wird die Wirksamkeit der brigen Lizenzbestimmungen dadurch nicht berhrt, und an die Stelle der unwirksamen Bestimmung tritt eine Ersatzregelung, die dem mit der unwirksamen Bestimmung angestrebten Zweck am nchsten kommt. - 4. Nichts soll dahingehend ausgelegt werden, dass auf eine Bestimmung dieses Lizenzvertrages verzichtet oder einer Vertragsverletzung zugestimmt wird, so lange ein solcher Verzicht oder eine solche Zustimmung nicht schriftlich vorliegen und von der verzichtenden oder zustimmenden Vertragspartei unterschrieben sind - 5. Dieser Lizenzvertrag stellt die vollstndige Vereinbarung zwischen den Vertragsparteien hinsichtlich des Schutzgegenstandes dar. Es gibt keine weiteren ergnzenden Vereinbarungen oder mndlichen Abreden im Hinblick auf den Schutzgegenstand. Der Lizenzgeber ist an keine zustzlichen Abreden gebunden, die aus irgendeiner Absprache mit Ihnen entstehen knnten. Der Lizenzvertrag kann nicht ohne eine bereinstimmende schriftliche Vereinbarung zwischen dem Lizenzgeber und Ihnen abgendert werden. - 6. Auf diesen Lizenzvertrag findet das Recht der Bundesrepublik Deutschland Anwendung. \ No newline at end of file diff --git a/Tetris2000/data/fonts/Maass readme.txt b/Tetris2000/data/fonts/Maass readme.txt deleted file mode 100644 index 79830f4..0000000 --- a/Tetris2000/data/fonts/Maass readme.txt +++ /dev/null @@ -1,18 +0,0 @@ -This Font I created for the world leader in salmon slicing machines: Maass Salmon Slicers. It was inspired by their logo. - -If you want to chop tons of salmon, visit http://www.maass-slicers.de/ - -If you like to use this font, please drop me a note at: wiegel@aepnet.de - -This Font is freeware. However it is Not Public Domain. -You are allowed to share this font (including this readme-file) without asking for any fee -and to use it however and howoften you want (but please not for something like slicing salmons or other fish :-) -if you are not Mass. ). -If you want to add this to your Font-Download-Page, please drop me a note. -And please ask me, if you want to put it on a commercial Font-CD ore some other commercial software product. ----- -Update Notice: Jan.2007: I have removed the Maass-Logos, cause the company asked me to do this. And as I had to edit the font, I have added a lot of more glyphs: -I have addes lowercase letters, the complete set of european "Latin Extended A" glyphs fpr all the west and east european languages, -greek glyphs as well as kyrillic letters, including special letters for other countries than russia using kyrillic writing. - -(c) P. Wiegel CAT-Design Wolgast diff --git a/Tetris2000/data/fonts/PixelCaps!.otf b/Tetris2000/data/fonts/PixelCaps!.otf deleted file mode 100644 index d3a6de6..0000000 Binary files a/Tetris2000/data/fonts/PixelCaps!.otf and /dev/null differ diff --git a/Tetris2000/data/fonts/Preview.png b/Tetris2000/data/fonts/Preview.png deleted file mode 100644 index a3eace7..0000000 Binary files a/Tetris2000/data/fonts/Preview.png and /dev/null differ diff --git a/Tetris2000/data/fonts/ReadMe.png b/Tetris2000/data/fonts/ReadMe.png deleted file mode 100644 index ca8cefe..0000000 Binary files a/Tetris2000/data/fonts/ReadMe.png and /dev/null differ diff --git a/Tetris2000/data/fonts/maass slicer Italic.ttf b/Tetris2000/data/fonts/maass slicer Italic.ttf deleted file mode 100644 index 7c4fe59..0000000 Binary files a/Tetris2000/data/fonts/maass slicer Italic.ttf and /dev/null differ diff --git a/Tetris2000/data/fonts/markus-designs.com.url b/Tetris2000/data/fonts/markus-designs.com.url deleted file mode 100644 index f1d6b3e..0000000 --- a/Tetris2000/data/fonts/markus-designs.com.url +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=http://markus-designs.com/ diff --git a/Tetris2000/data/fonts/markuskoellmann.com.url b/Tetris2000/data/fonts/markuskoellmann.com.url deleted file mode 100644 index 082fda2..0000000 --- a/Tetris2000/data/fonts/markuskoellmann.com.url +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=http://markuskoellmann.com/ diff --git a/Tetris2000/data/icons/16.png b/Tetris2000/data/icons/16.png deleted file mode 100644 index fef8a1c..0000000 Binary files a/Tetris2000/data/icons/16.png and /dev/null differ diff --git a/Tetris2000/data/icons/32.png b/Tetris2000/data/icons/32.png deleted file mode 100644 index 34bd9ff..0000000 Binary files a/Tetris2000/data/icons/32.png and /dev/null differ diff --git a/Tetris2000/data/icons/48.png b/Tetris2000/data/icons/48.png deleted file mode 100644 index f9bad54..0000000 Binary files a/Tetris2000/data/icons/48.png and /dev/null differ diff --git a/Tetris2000/data/icons/icon.ico b/Tetris2000/data/icons/icon.ico deleted file mode 100644 index c7dd5f8..0000000 Binary files a/Tetris2000/data/icons/icon.ico and /dev/null differ diff --git a/Tetris2000/data/locale/PyTetris.ts b/Tetris2000/data/locale/PyTetris.ts deleted file mode 100644 index 37b4a97..0000000 --- a/Tetris2000/data/locale/PyTetris.ts +++ /dev/null @@ -1,327 +0,0 @@ - - - - Frames - - - New game - - - - - A game is in progress. -Do you want to abord it? - - - - - Start level: - - - - - High score - - - - - Game over - - - - - Congratulations! -You have the high score! - - - - - Matrix - - - Level - - - - - - PAUSE - -Press %s -to resume - - - - - GAME -OVER - - - - - SettingStrings - - - Keyboard settings - - - - - Move left - - - - - Move right - - - - - Rotate clockwise - - - - - Rotate counterclockwise - - - - - Soft drop - - - - - Hard drop - - - - - Hold - - - - - Pause - - - - - Other settings - - - - - Delays - - - - - Auto-shift delay - - - - - Auto-repeat rate - - - - - Sound - - - - - Music volume - - - - - Effects volume - - - - - Show ghost piece - - - - - Show next queue - - - - - Hold enabled - - - - - SettingsDialog - - - Settings - - - - - Stats - - - High score - - - - - Single - - - - - Double - - - - - Triple - - - - - Tetris - - - - - COMBO x{:n} -{:n} - - - - - BACK TO BACK -{:n} - - - - - Time: {} - - - - - - Lines per minute: {:.1f} - - - - - Tetrominos per minute: {:.1f} - - - - - Score: - - - - - High score: - - - - - Level: - - - - - Goal: - - - - - Lines: - - - - - Mini T-Spins: - - - - - T-Spins: - - - - - Back-to-back: - - - - - Max combo: - - - - - Combos: - - - - - Tetrominos locked down: - - - - - Window - - - High score - - - - - &New game - - - - - &Settings - - - - - &About - - - - - A game is in progress. -Do you want to abord it? - - - - - Quit game? - - - - - Tetris® clone -by Adrien Malingrey - -Tetris Game Design by Alekseï Pajitnov -Graphism inspired by Tetris Effect -Window style sheet by Colin Duquesnoy -PixelCaps! font by Markus Koellmann -Maass slicer font by Peter Wiegel -Traditional song Korobeiniki arranged by Kobashik -Sound effects made with voc-one by Simple-Media -Background images found on xshyfc.com - - - - diff --git a/Tetris2000/data/locale/fr.qm b/Tetris2000/data/locale/fr.qm deleted file mode 100644 index 7339b26..0000000 Binary files a/Tetris2000/data/locale/fr.qm and /dev/null differ diff --git a/Tetris2000/data/locale/fr.ts b/Tetris2000/data/locale/fr.ts deleted file mode 100644 index e17bf48..0000000 --- a/Tetris2000/data/locale/fr.ts +++ /dev/null @@ -1,349 +0,0 @@ - - - - Frames - - - New game - Nouvelle partie - - - - A game is in progress. -Do you want to abord it? - Une partie est en cours. -Voulez-vous l'abandonner ? - - - - Start level: - Commencer au niveau : - - - - High score - Meilleur score - - - - Game over - Partie terminée - - - - Congratulations! -You have the high score! - Bravo ! -Vous avez atteint le meilleur score ! - - - - Matrix - - - Level - - Niveau - - - - - PAUSE - -Press %s -to resume - PAUSE - -Appuyez sur -%s -pour reprendre - - - - GAME -OVER - PARTIE -TERMINÉE - - - - SettingStrings - - - Keyboard settings - Configuration du clavier - - - - Move left - Déplacer à gauche - - - - Move right - Déplacer à droite - - - - Rotate clockwise - Tourner dans le sens horaire - - - - Rotate counterclockwise - Tourner dans le sens anti-horaire - - - - Soft drop - Chute lente - - - - Hard drop - Chute rapide - - - - Hold - Réserve - - - - Pause - Pause - - - - Other settings - Autres paramètres - - - - Delays - Temporisation - - - - Auto-shift delay - Délai avant répétition - - - - Auto-repeat rate - Vitesse de répétition - - - - Sound - Son - - - - Music volume - Volume de la musique - - - - Effects volume - Volume des effets sonores - - - - Show ghost piece - Afficher l'ombre - - - - Show next queue - Afficher les 6 prochaines pièces - - - - Hold enabled - Activer la réserve - - - - SettingsDialog - - - Settings - Préférences - - - - Stats - - - High score - Meilleur score - - - - Single - Single - - - - Double - Double - - - - Triple - Triple - - - - Tetris - Tetris - - - - COMBO x{:n} -{:n} - COMBO x{:n} -{:n} - - - - BACK TO BACK -{:n} - BACK TO BACK -{:n} - - - - Time: {} - - Temps : {} - - - - - Lines per minute: {:.1f} - Lignes par minute : {:.1f} - - - - Tetrominos per minute: {:.1f} - Tétrominos par minute : {:.1f} - - - - Score: - Score : - - - - High score: - Meilleur score : - - - - Level: - Niveau : - - - - Goal: - Objectif : - - - - Lines: - Lignes : - - - - Mini T-Spins: - Mini T-Spins : - - - - T-Spins: - T-Spins : - - - - Back-to-back: - Back-to-back : - - - - Max combo: - Combo max : - - - - Combos: - Combos : - - - - Tetrominos locked down: - Tétrominos bloqués : - - - - Window - - - High score - Meilleur score - - - - &New game - &Nouvelle partie - - - - &Settings - &Préférences - - - - &About - &À propos - - - - A game is in progress. -Do you want to abord it? - Une partie est en cours. -Voulez-vous l'abandonner ? - - - - Quit game? - Quitter la partie ? - - - - Tetris® clone -by Adrien Malingrey - -Tetris Game Design by Alekseï Pajitnov -Graphism inspired by Tetris Effect -Window style sheet by Colin Duquesnoy -PixelCaps! font by Markus Koellmann -Maass slicer font by Peter Wiegel -Traditional song Korobeiniki arranged by Kobashik -Sound effects made with voc-one by Simple-Media -Background images found on xshyfc.com - Clone de Tetris® -Auteur : Adrien Malingrey - -Concepteur de Tetris : Alekseï Pajitnov -Graphisme inspiré de Tetris Effect -Style de fenêtre : Colin Duquesnoy -Police PixelCaps! : Markus Koellmann -Police Maass slice : Peter Wiegel -Korobeiniki : Chanson traditionnelle arrangée par Kobashik -Effets sonores réalisés avec voc-one de Simple-Media -Images de fond trouvées sur xshyfc.com - - - diff --git a/Tetris2000/data/sounds/01 - Song A.mp3 b/Tetris2000/data/sounds/01 - Song A.mp3 deleted file mode 100644 index ee27d32..0000000 Binary files a/Tetris2000/data/sounds/01 - Song A.mp3 and /dev/null differ diff --git a/Tetris2000/data/sounds/Tetris - Song A.mid b/Tetris2000/data/sounds/Tetris - Song A.mid deleted file mode 100644 index 3034c1a..0000000 Binary files a/Tetris2000/data/sounds/Tetris - Song A.mid and /dev/null differ diff --git a/Tetris2000/data/sounds/hard_drop.wav b/Tetris2000/data/sounds/hard_drop.wav deleted file mode 100644 index d1f88a2..0000000 Binary files a/Tetris2000/data/sounds/hard_drop.wav and /dev/null differ diff --git a/Tetris2000/data/sounds/line_clear.wav b/Tetris2000/data/sounds/line_clear.wav deleted file mode 100644 index e6507cd..0000000 Binary files a/Tetris2000/data/sounds/line_clear.wav and /dev/null differ diff --git a/Tetris2000/data/sounds/rotate.wav b/Tetris2000/data/sounds/rotate.wav deleted file mode 100644 index 9e93d12..0000000 Binary files a/Tetris2000/data/sounds/rotate.wav and /dev/null differ diff --git a/Tetris2000/data/sounds/rotate.xt b/Tetris2000/data/sounds/rotate.xt deleted file mode 100644 index 8ba10c1..0000000 Binary files a/Tetris2000/data/sounds/rotate.xt and /dev/null differ diff --git a/Tetris2000/data/sounds/tetris.wav b/Tetris2000/data/sounds/tetris.wav deleted file mode 100644 index f6cc51d..0000000 Binary files a/Tetris2000/data/sounds/tetris.wav and /dev/null differ diff --git a/Tetris2000/data/sounds/tetris.xt b/Tetris2000/data/sounds/tetris.xt deleted file mode 100644 index 22b1b37..0000000 Binary files a/Tetris2000/data/sounds/tetris.xt and /dev/null differ