Project reorganization to use with fbs

This commit is contained in:
adrienmalin
2018-09-28 15:11:21 +02:00
parent 1a308f456a
commit 67b48eeb0b
63 changed files with 316 additions and 213 deletions

101
src/main/NSIS/Installer.nsi Normal file
View File

@ -0,0 +1,101 @@
!include MUI2.nsh
!include FileFunc.nsh
;--------------------------------
;Perform Machine-level install, if possible
!define MULTIUSER_EXECUTIONLEVEL Highest
;Add support for command-line args that let uninstaller know whether to
;uninstall machine- or user installation:
!define MULTIUSER_INSTALLMODE_COMMANDLINE
!include MultiUser.nsh
!include LogicLib.nsh
Function .onInit
!insertmacro MULTIUSER_INIT
;Do not use InstallDir at all so we can detect empty $InstDir!
${If} $InstDir == "" ; /D not used
${If} $MultiUser.InstallMode == "AllUsers"
StrCpy $InstDir "$PROGRAMFILES\%{app_name}"
${Else}
StrCpy $InstDir "$LOCALAPPDATA\%{app_name}"
${EndIf}
${EndIf}
FunctionEnd
Function un.onInit
!insertmacro MULTIUSER_UNINIT
FunctionEnd
;--------------------------------
;General
Name "%{app_name}"
OutFile "..\%{app_name}Setup.exe"
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
;--------------------------------
;Pages
!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of %{app_name}.$\r$\n$\r$\n$\r$\nClick Next to continue."
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_CHECKED
!define MUI_FINISHPAGE_RUN_TEXT "Run %{app_name}"
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
!define UNINST_KEY \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\%{app_name}"
Section
SetOutPath "$InstDir"
File /r "..\%{app_name}\*"
WriteRegStr SHCTX "Software\%{app_name}" "" $InstDir
WriteUninstaller "$InstDir\uninstall.exe"
CreateShortCut "$SMPROGRAMS\%{app_name}.lnk" "$InstDir\%{app_name}.exe"
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "%{app_name}"
WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" \
"$\"$InstDir\uninstall.exe$\" /$MultiUser.InstallMode"
WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" \
"$\"$InstDir\uninstall.exe$\" /$MultiUser.InstallMode /S"
WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "%{author}"
${GetSize} "$InstDir" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" "$0"
SectionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
RMDir /r "$InstDir"
Delete "$SMPROGRAMS\%{app_name}.lnk"
DeleteRegKey /ifempty SHCTX "Software\%{app_name}"
DeleteRegKey SHCTX "${UNINST_KEY}"
SectionEnd
Function LaunchLink
!addplugindir "."
ShellExecAsUser::ShellExecAsUser "open" "$SMPROGRAMS\%{app_name}.lnk"
FunctionEnd

Binary file not shown.

BIN
src/main/icons/Icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

11
src/main/icons/README.md Normal file
View File

@ -0,0 +1,11 @@
![Sample app icon](linux/128.png)
This directory contains the icons that are displayed for your app. Feel free to
change them.
The difference between the icons on Mac and the other platforms is that on Mac,
they contain a ~5% transparent margin. This is because otherwise they look too
big (eg. in the Dock or in the app switcher).
You can create Icon.ico from the .png files with
[an online tool](http://icoconvert.com/Multi_Image_to_one_icon/).

BIN
src/main/icons/base/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
src/main/icons/base/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

BIN
src/main/icons/base/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/main/icons/linux/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
src/main/icons/linux/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

BIN
src/main/icons/linux/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/main/icons/mac/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
src/main/icons/mac/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

BIN
src/main/icons/mac/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = "Adrien Malingrey"
__title__ = "Tetris 2000"
__version__ = "0.3"

105
src/main/python/consts.py Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
from PyQt5 import QtGui
# Paths
ICON_PATH = "icons/icon.ico"
BG_IMAGE_DIR = "backgrounds"
START_BG_IMAGE_PATH = os.path.join(BG_IMAGE_DIR, "01-spacefield_a-000.png")
MUSICS_DIR = "musics"
SFX_DIR = "sfx"
LINE_CLEAR_SFX_PATH = os.path.join(SFX_DIR, "line_clear.wav")
TETRIS_SFX_PATH = os.path.join(SFX_DIR, "tetris.wav")
ROTATE_SFX_PATH = os.path.join(SFX_DIR, "rotate.wav")
HARD_DROP_SFX_PATH = os.path.join(SFX_DIR, "hard_drop.wav")
WALL_SFX_PATH = os.path.join(SFX_DIR, "wall.wav")
LOCALE_PATH = "locale"
FONTS_DIR = "fonts"
STATS_FONT_PATH = os.path.join(FONTS_DIR, "PixelCaps!.otf")
STATS_FONT_NAME = "PixelCaps!"
MATRIX_FONT_PATH = os.path.join(FONTS_DIR, "maass slicer Italic.ttf")
MATRIX_FONT_NAME = "Maassslicer"
SPLASH_SCREEN_PATH = "splashscreen/splashscreen.png"
# Coordinates and direction
L, R, U, D = -1, 1, -1, 1 # Left, Right, Up, Down
CLOCKWISE, COUNTERCLOCKWISE = 1, -1
# Delays in milliseconds
ANIMATION_DELAY = 67
INITIAL_SPEED = 1000
ENTRY_DELAY = 80
LINE_CLEAR_DELAY = 80
LOCK_DELAY = 500
TEMPORARY_TEXT_DURATION = 1000
AFTER_LVL_15_ACCELERATION = 0.9
# Block Colors
BLOCK_BORDER_COLOR = QtGui.QColor(0, 159, 218, 255)
BLOCK_FILL_COLOR = QtGui.QColor(0, 159, 218, 25)
BLOCK_GLOWING_BORDER_COLOR = None
BLOCK_GLOWING_FILL_COLOR = QtGui.QColor(186, 211, 255, 70)
BLOCK_LIGHT_COLOR = QtGui.QColor(242, 255, 255, 40)
BLOCK_TRANSPARENT = QtGui.QColor(255, 255, 255, 0)
BLOCK_GLOWING = 0
BLOCK_INITIAL_SIDE = 20
GHOST_BLOCK_BORDER_COLOR = QtGui.QColor(135, 213, 255, 255)
GHOST_BLOCK_FILL_COLOR = None
GHOST_BLOCK_GLOWING_FILL_COLOR = QtGui.QColor(201, 149, 205, 255)
GHOST_BLOCK_GLOWING = 1
# Grid
GRID_INVISIBLE_ROWS = 3
GRID_DEFAULT_ROWS = 4
GRID_DEFAULT_COLUMNS = 6
GRID_GRIDLINE_COLOR = QtGui.QColor(255, 255, 255, 60)
GRID_HARD_DROP_MOVEMENT = 0.2
GRID_SPOTLIGHT = 0, 0
# Matrix
MATRIX_ROWS = 20
MATRIX_COLUMNS = 10
MATRIX_TEXT_COLOR = QtGui.QColor(204, 255, 255, 128)
# Next Queue
NEXT_QUEUE_ROWS = 16
NEXT_QUEUE_COLUMNS = 6
# Stats frame
STATS_ROWS = 15
STATS_COLUMNS = 6
STATS_TEXT_COLOR = QtGui.QColor(0, 159, 218, 128)
SCORES = (
{"name": "", "": 0, "Mini T-Spin": 1, "T-Spin": 4},
{"name": "Single", "": 1, "Mini T-Spin": 2, "T-Spin": 8},
{"name": "Double", "": 3, "T-Spin": 12},
{"name": "Triple", "": 5, "T-Spin": 16},
{"name": "Tetris", "": 8},
)
# Default settings
DEFAULT_WINDOW_SIZE = 839, 807
# Key mapping
DEFAULT_MOVE_LEFT_KEY = "Left"
DEFAULT_MOVE_RIGHT_KEY = "Right"
DEFAULT_ROTATE_CLOCKWISE_KEY = "Up"
DEFAULT_ROTATE_COUNTERCLOCKWISE_KEY = "Control"
DEFAULT_SOFT_DROP_KEY = "Down"
DEFAULT_HARD_DROP_KEY = "Space"
DEFAULT_HOLD_KEY = "Shift"
DEFAULT_PAUSE_KEY = "Escape"
# Delays in milliseconds
DEFAULT_AUTO_SHIFT_DELAY = 170
DEFAULT_AUTO_REPEAT_RATE = 20
# Volume in percent
DEFAUT_MUSIC_VOLUME = 25
DEFAULT_SFX_VOLUME = 50
# Other
DEFAULT_SHOW_GHOST = True
DEFAULT_SHOW_NEXT_QUEUE = True
DEFAULT_HOLD_ENABLED = True

1529
src/main/python/game_gui.py Normal file

File diff suppressed because it is too large Load Diff

25
src/main/python/main.py Normal file
View File

@ -0,0 +1,25 @@
#!/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
"""
from fbs_runtime.application_context import ApplicationContext
from game_gui import Window
import sys
class AppContext(ApplicationContext): # 1. Subclass ApplicationContext
def run(self): # 2. Implement run()
win = Window(self)
win.show()
return self.app.exec_() # 3. End run() with this line
if __name__ == '__main__':
appctxt = AppContext() # 4. Instantiate the subclass
exit_code = appctxt.run() # 5. Invoke run()
sys.exit(exit_code)

49
src/main/python/point.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5 import QtCore
from consts import CLOCKWISE
class Point(QtCore.QPoint):
"""
Point of coordinates (x, y)
"""
x = property(QtCore.QPoint.x, QtCore.QPoint.setX)
y = property(QtCore.QPoint.y, QtCore.QPoint.setY)
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
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, self.y / k)
__radd__ = __add__
__rsub__ = __sub__
__rmul__ = __mul__
__rtruediv__ = __truediv__
def __repr__(self):
return "Point({}, {})".format(self.x, self.y)
__str__ = __repr__

View File

@ -0,0 +1,417 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import random
from PyQt5 import QtCore, QtGui
import consts
from consts import L, R, U, D, CLOCKWISE, COUNTERCLOCKWISE
from point import Point
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 = consts.BLOCK_BORDER_COLOR
FILL_COLOR = consts.BLOCK_FILL_COLOR
GLOWING_BORDER_COLOR = consts.BLOCK_GLOWING_BORDER_COLOR
GLOWING_FILL_COLOR = consts.BLOCK_GLOWING_FILL_COLOR
LIGHT_COLOR = consts.BLOCK_LIGHT_COLOR
TRANSPARENT = consts.BLOCK_TRANSPARENT
GLOWING = consts.BLOCK_GLOWING
side = consts.BLOCK_INITIAL_SIDE
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
block_center = Point(Block.side / 2, Block.side / 2)
self.center = p + block_center
spotlight = top_left_corner + Block.side * spotlight + block_center
self.glint = 0.15 * 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 = consts.GHOST_BLOCK_BORDER_COLOR
FILL_COLOR = consts.GHOST_BLOCK_FILL_COLOR
GLOWING_FILL_COLOR = consts.GHOST_BLOCK_GLOWING_FILL_COLOR
GLOWING = consts.GHOST_BLOCK_GLOWING
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=0, update=True):
"""
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
block.trail = trail
if update:
self.matrix.update()
return True
def move(self, horizontally, vertically, trail=0, update=True):
"""
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,
update
)
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(
mino.coord.rotate(self.minoes[0].coord, direction) for mino 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
"""
return self.move(0, D, trail=1)
def hard_drop(self, show_trail=True, update=True):
"""
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=trail, update=update):
if show_trail:
trail += 1
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):
self.t_spin = "T-Spin"
elif c and d and (a or b):
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 """
return False
class GhostPiece(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(show_trail=False, update=False)

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

View File

@ -0,0 +1,55 @@
Lizenzvertrag
DAS URHEBERRECHTLICH GESCH<43>TZTE WERK ODER DER SONSTIGE SCHUTZGEGENSTAND (WIE UNTEN BESCHRIEBEN) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE (<28>CCPL<50> ODER <20>LIZENZVERTRAG<41>) ZUR VERF<52>GUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER EINSCHL<48>GIGE GESETZE GESCH<43>TZT.
DURCH DIE AUS<55>BUNG EINES DURCH DIESEN LIZENZVERTRAG GEW<45>HRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKL<4B>REN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. DER LIZENZGEBER R<>UMT IHNEN DIE HIER BESCHRIEBENEN RECHTE UNTER DER VORAUSSETZUNGEIN, DASS SIE SICH MIT DIESEN VERTRAGSBEDINGUNGEN EINVERSTANDEN ERKL<4B>REN.
1. Definitionen
1. Unter einer <20>Bearbeitung<6E> wird eine <20>bersetzung oder andere Bearbeitung des Werkes verstanden, die Ihre pers<72>nliche geistige Sch<63>pfung ist. Eine freie Benutzung des Werkes wird nicht als Bearbeitung angesehen.
2. Unter den <20>Lizenzelementen<65> werden die folgenden Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgew<65>hlt und in der Bezeichnung der Lizenz genannt werden: <20>Namensnennung<6E>, <20>Nicht-kommerziell<6C>, <20>Weitergabe unter gleichen Bedingungen<65>.
3. Unter dem <20>Lizenzgeber<65> wird die nat<61>rliche oder juristische Person verstanden, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet.
4. Unter einem <20>Sammelwerk<72> wird eine Sammlung von Werken, Daten oder anderen unabh<62>ngigen Elementen verstanden, die aufgrund der Auswahl oder Anordnung der Elemente eine pers<72>nliche geistige Sch<63>pfung ist. Darunter fallen auch solche Sammelwerke, deren Elemente systematisch oder methodisch angeordnet und einzeln mit Hilfe elektronischer Mittel oder auf andere Weise zug<75>nglich sind (Datenbankwerke). Ein Sammelwerk wird im Zusammenhang mit dieser Lizenz nicht als Bearbeitung (wie oben beschrieben) angesehen.
5. Mit <20>SIE<49> und <20>Ihnen<65> ist die nat<61>rliche oder juristische Person gemeint, die die durch diese Lizenz gew<65>hrten Nutzungsrechte aus<75>bt und die zuvor die Bedingungen dieser Lizenz im Hinblick auf das Werk nicht verletzt hat, oder die die ausdr<64>ckliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gew<65>hrten Nutzungsrechte trotz einer vorherigen Verletzung auszu<7A>ben.
6. Unter dem <20>Schutzgegenstand<6E>wird das Werk oder Sammelwerk oder das Schutzobjekt eines verwandten Schutzrechts, das Ihnen unter den Bedingungen dieser Lizenz angeboten wird, verstanden
7. Unter dem <20>Urheber<65> wird die nat<61>rliche Person verstanden, die das Werk geschaffen hat.
8. Unter einem <20>verwandten Schutzrecht<68> 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 Tontr<74>ger, einer Funksendung, einem Laufbild oder einer Darbietung eines aus<75>benden K<>nstlers.
9. Unter dem <20>Werk<72> wird eine pers<72>nliche geistige Sch<63>pfung verstanden, die Ihnen unter den Bedingungen dieser Lizenz angeboten wird.
2. Schranken des Urheberrechts. Diese Lizenz l<>sst s<>mtliche Befugnisse unber<65>hrt, die sich aus den Schranken des Urheberrechts,aus dem Ersch<63>pfungsgrundsatz oder anderen Beschr<68>nkungen der Ausschlie<69>lichkeitsrechte des Rechtsinhabers ergeben.
3. Lizenzierung. Unter den Bedingungen dieses Lizenzvertrages r<>umt Ihnen der Lizenzgeber ein lizenzgeb<65>hrenfreies, r<>umlich und zeitlich (f<>r die Dauer des Urheberrechts oder verwandten Schutzrechts) unbeschr<68>nktes einfaches Nutzungsrecht ein, den Schutzgegenstand in der folgenden Art und Weise zu nutzen:
1. den Schutzgegenstand in k<>rperlicher Form zu verwerten, insbesondere zu vervielf<6C>ltigen, zu verbreiten und auszustellen;
2. den Schutzgegenstand in unk<6E>rperlicher Form <20>ffentlich wiederzugeben, insbesondere vorzutragen, aufzuf<75>hren und vorzuf<75>hren, <20>ffentlich zug<75>nglich zu machen, zu senden, durch Bild- und Tontr<74>ger wiederzugeben sowie Funksendungen und <20>ffentliche Zug<75>nglichmachungen wiederzugeben;
3. den Schutzgegenstand auf Bild- oder Tontr<74>ger 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 ver<65>ffentlichen und in dem in a. bis c. genannten Umfang zu verwerten;
Die genannten Nutzungsrechte k<>nnen f<>r alle bekannten Nutzungsarten ausge<67>bt werden. Die genannten Nutzungsrechte beinhalten das Recht, solche Ver<65>nderungen an dem Werk vorzunehmen, die technisch erforderlich sind, um die Nutzungsrechte f<>r alle Nutzungsarten wahrzunehmen. Insbesondere sind davon die Anpassung an andere Medien und auf andere Dateiformate umfasst.
4. Beschr<68>nkungen. Die Einr<6E>umung der Nutzungsrechte gem<65><6D> Ziffer 3 erfolgt ausdr<64>cklich nur unter den folgenden Bedingungen:
1. Sie d<>rfen den Schutzgegenstand ausschlie<69>lich unter den Bedingungen dieser Lizenz vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben, und Sie m<>ssen stets eine Kopie oder die vollst<73>ndige Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz beif<69>gen, wenn Sie den Schutzgegenstandvervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben. Sie d<>rfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gew<65>hrten Rechte <20>ndern oder beschr<68>nken. Sie d<>rfen den Schutzgegenstand nicht unterlizenzieren. Sie m<>ssen alle Hinweise unver<65>ndert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie d<>rfen den Schutzgegenstand mit keinen technischen Schutzma<6D>nahmen versehen, die den Zugang oder den Gebrauch des Schutzgegenstandes in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschr<68>nkungen gelten auch f<>r 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, m<>ssen 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, m<>ssen Sie - soweit dies praktikabel ist- auf die Aufforderung eines Rechtsinhabers hin von der Bearbeitung jeglichen Hinweis auf diesen Rechtsinhaber entfernen.
2. Sie d<>rfen eine Bearbeitung ausschlie<69>lich unter den Bedingungen dieser Lizenz, einer sp<73>teren Version dieser Lizenz mit denselben Lizenzelementen wie diese Lizenz oder einer Creative Commons iCommons Lizenz, die dieselben Lizenzelemente wie diese Lizenz enth<74>lt (z.B. Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 2.0 Japan), vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben. Sie m<>ssen 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 beif<69>gen, wenn Sie die Bearbeitung vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben. Sie d<>rfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gew<65>hrten Rechte <20>ndern oder beschr<68>nken, und Sie m<>ssen alle Hinweise unver<65>ndert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie d<>rfen eine Bearbeitung nicht mit technischen Schutzma<6D>nahmen versehen, die den Zugang oder den Gebrauch der Bearbeitung in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschr<68>nkungen gelten auch f<>r 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 vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben, m<>ssen Sie alle Urhebervermerke f<>r den Schutzgegenstand unver<65>ndert 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 f<>r den Titel des Schutzgegenstandes, wenn dieser angeben ist, sowie - in einem vern<72>nftigerweise durchf<68>hrbaren Umfang - f<>r 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 aufzuf<75>hren, in welcher Form der Schutzgegenstand in die Bearbeitung eingegangen ist (z.B. <20>Franz<6E>sische <20>bersetzung des ... (Werk) durch ... (Urheber)<29> oder <20>Das Drehbuch beruht auf dem Werk des ... (Urheber)<29>). 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 auff<66>lliger Weise zu erfolgen hat wie vergleichbare Hinweise auf andere Rechtsinhaber.
4. Obwohl die gem<65>ss Ziffer 3 gew<65>hrten Nutzungsrechte in umfassender Weise
ausge<67>bt werden d<>rfen, findet diese Erlaubnis ihre gesetzliche Grenze in
den Pers<72>nlichkeitsrechten der Urheber und aus<75>benden K<>nstler, deren berechtigte geistige und pers<72>nliche Interessen bzw. deren Ansehen oder Ruf nicht dadurch gef<65>hrdet werden d<>rfen, dass ein Schutzgegenstand <20>ber das gesetzlich zul<75>ssige Ma<4D> hinaus beeintr<74>chtigt wird.
5. Gew<65>hrleistung. Sofern dies von den Vertragsparteien nicht anderweitig schriftlich vereinbart,, bietet der Lizenzgeber keine Gew<65>hrleistung f<>r die erteilten Rechte, au<61>er f<>r den Fall, dass M<>ngel arglistig verschwiegen wurden. F<>r M<>ngel anderer Art, insbesondere bei der mangelhaften Lieferung von Verk<72>rperungen des Schutzgegenstandes, richtet sich die Gew<65>hrleistung nach der Regelung, die die Person, die Ihnen den Schutzgegenstand zur Verf<72>gung stellt, mit Ihnen au<61>erhalb dieser Lizenz vereinbart, oder - wenn eine solche Regelung nicht getroffen wurde - nach den gesetzlichen Vorschriften.
6. Haftung. <20>ber die in Ziffer 5 genannte Gew<65>hrleistung hinaus haftet Ihnen der Lizenzgeber nur f<>r Vorsatz und grobe Fahrl<72>ssigkeit.
7. Vertragsende
1. Dieser Lizenzvertrag und die durch ihn einger<65>umten Nutzungsrechte enden automatisch bei jeder Verletzung der Vertragsbedingungen durch Sie. F<>r nat<61>rliche 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 nat<61>rlichen oder juristischen Personen erf<72>llen s<>mtliche 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 (f<>r die Dauer des Schutzrechts). Dennoch beh<65>lt 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 vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz f<>r den Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz einger<65>umt hat.
2. Jedes Mal, wenn Sie eine Bearbeitung vervielf<6C>ltigen, verbreiten oder <20>ffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz f<>r den urspr<70>nglichen Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz einger<65>umt hat.
3. Sollte eine Bestimmung dieses Lizenzvertrages unwirksam sein, so wird die Wirksamkeit der <20>brigen Lizenzbestimmungen dadurch nicht ber<65>hrt, und an die Stelle der unwirksamen Bestimmung tritt eine Ersatzregelung, die dem mit der unwirksamen Bestimmung angestrebten Zweck am n<>chsten 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 vollst<73>ndige Vereinbarung zwischen den Vertragsparteien hinsichtlich des Schutzgegenstandes dar. Es gibt keine weiteren erg<72>nzenden Vereinbarungen oder m<>ndlichen Abreden im Hinblick auf den Schutzgegenstand. Der Lizenzgeber ist an keine zus<75>tzlichen Abreden gebunden, die aus irgendeiner Absprache mit Ihnen entstehen k<>nnten. Der Lizenzvertrag kann nicht ohne eine <20>bereinstimmende schriftliche Vereinbarung zwischen dem Lizenzgeber und Ihnen abge<67>ndert werden.
6. Auf diesen Lizenzvertrag findet das Recht der Bundesrepublik Deutschland Anwendung.

View File

@ -0,0 +1,18 @@
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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

View File

@ -0,0 +1,2 @@
[InternetShortcut]
URL=http://markus-designs.com/

View File

@ -0,0 +1,2 @@
[InternetShortcut]
URL=http://markuskoellmann.com/

View File

@ -0,0 +1,323 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0">
<context>
<name>Frames</name>
<message>
<location filename="../frames.py" line="205"/>
<source>New game</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="192"/>
<source>A game is in progress.
Do you want to abord it?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="205"/>
<source>Start level:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="323"/>
<source>High score</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="312"/>
<source>Game over</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="315"/>
<source>Congratulations!
You have the high score: {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../frames.py" line="325"/>
<source>Score: {}
High score: {}</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Matrix</name>
<message>
<location filename="../matrix.py" line="73"/>
<source>Level
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../matrix.py" line="356"/>
<source>PAUSE
Press %s
to resume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../matrix.py" line="364"/>
<source>GAME
OVER</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingStrings</name>
<message>
<location filename="../settings.py" line="20"/>
<source>Keyboard settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="21"/>
<source>Move left</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="22"/>
<source>Move right</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="23"/>
<source>Rotate clockwise</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="24"/>
<source>Rotate counterclockwise</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="25"/>
<source>Soft drop</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="26"/>
<source>Hard drop</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="27"/>
<source>Hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="28"/>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="29"/>
<source>Other settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="31"/>
<source>Delays</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="32"/>
<source>Auto-shift delay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="33"/>
<source>Auto-repeat rate</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="35"/>
<source>Sound</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="36"/>
<source>Music volume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="37"/>
<source>Effects volume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="39"/>
<source>Show ghost piece</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="40"/>
<source>Show next queue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settings.py" line="41"/>
<source>Hold enabled</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings.py" line="95"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Stats</name>
<message>
<location filename="../stats.py" line="43"/>
<source>High score</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="126"/>
<source>COMBO x{:n}
{:n}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="158"/>
<source>BACK TO BACK
{:n}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Score: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>High score: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Time: {}
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Level: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Goal: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Lines: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Mini T-Spins: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>T-Spins: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Back-to-back: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Max combo: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Combos: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Lines per minute: {:.1f}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Tetrominos locked down: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Tetrominos per minute: {:.1f}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../stats.py" line="243"/>
<source>: </source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Window</name>
<message>
<location filename="../window.py" line="93"/>
<source>&amp;New game</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="97"/>
<source>&amp;Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="101"/>
<source>&amp;About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="139"/>
<source>Quit game?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="139"/>
<source>A game is in progress.
Do you want to abord it?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="154"/>
<source>High score</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../window.py" line="112"/>
<source>Tetris® clone by Adrien Malingrey
Tetris Game Design by Alekseï Pajitnov
Graphism inspired by Tetris Effect
Window style sheet: qdarkstyle by Colin Duquesnoy
Fonts by Markus Koellmann, Peter Wiegel
Images from:
OpenGameArt.org by beren77, Duion
Pexels.com by Min An, Jaymantri, Felix Mittermeier
Pixabay.com by LoganArt
Pixnio.com by Adrian Pelletier
Unsplash.com by Aron, Patrick Fore, Ilnur Kalimullin, Gabriel Garcia Marengo, Adnanta Raharja
StockSnap.io by Nathan Anderson, José Ignacio Pompé
Musics from ocremix.org by:
CheDDer Nardz, djpretzel, MkVaff, Sir_NutS, R3FORGED, Sir_NutS
Sound effects made with voc-one by Simple-Media</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@ -0,0 +1,352 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>Frames</name>
<message>
<location filename="../frames.py" line="205"/>
<source>New game</source>
<translation>Nouvelle partie</translation>
</message>
<message>
<location filename="../frames.py" line="192"/>
<source>A game is in progress.
Do you want to abord it?</source>
<translation>Une partie est en cours.
Voulez-vous l&apos;abandonner ?</translation>
</message>
<message>
<location filename="../frames.py" line="205"/>
<source>Start level:</source>
<translation>Commencer au niveau :</translation>
</message>
<message>
<location filename="../frames.py" line="323"/>
<source>High score</source>
<translation>Meilleur score</translation>
</message>
<message>
<location filename="../frames.py" line="312"/>
<source>Game over</source>
<translation>Partie terminée</translation>
</message>
<message>
<location filename="../frames.py" line="315"/>
<source>Congratulations!
You have the high score: {}</source>
<translation>Bravo !
Vous avez atteint le meilleur score : {}</translation>
</message>
<message>
<location filename="../frames.py" line="325"/>
<source>Score: {}
High score: {}</source>
<translation>Score : {}
Meilleur score : {}</translation>
</message>
</context>
<context>
<name>Matrix</name>
<message>
<location filename="../matrix.py" line="73"/>
<source>Level
</source>
<translation>Niveau
</translation>
</message>
<message>
<location filename="../matrix.py" line="356"/>
<source>PAUSE
Press %s
to resume</source>
<translation>PAUSE
Appuyez sur
%s
pour reprendre</translation>
</message>
<message>
<location filename="../matrix.py" line="364"/>
<source>GAME
OVER</source>
<translation>PARTIE
TERMINÉE</translation>
</message>
</context>
<context>
<name>SettingStrings</name>
<message>
<location filename="../settings.py" line="20"/>
<source>Keyboard settings</source>
<translation>Configuration du clavier</translation>
</message>
<message>
<location filename="../settings.py" line="21"/>
<source>Move left</source>
<translation>Déplacer à gauche</translation>
</message>
<message>
<location filename="../settings.py" line="22"/>
<source>Move right</source>
<translation>Déplacer à droite</translation>
</message>
<message>
<location filename="../settings.py" line="23"/>
<source>Rotate clockwise</source>
<translation>Tourner dans le sens horaire</translation>
</message>
<message>
<location filename="../settings.py" line="24"/>
<source>Rotate counterclockwise</source>
<translation>Tourner dans le sens anti-horaire</translation>
</message>
<message>
<location filename="../settings.py" line="25"/>
<source>Soft drop</source>
<translation>Chute lente</translation>
</message>
<message>
<location filename="../settings.py" line="26"/>
<source>Hard drop</source>
<translation>Chute rapide</translation>
</message>
<message>
<location filename="../settings.py" line="27"/>
<source>Hold</source>
<translation>Réserve</translation>
</message>
<message>
<location filename="../settings.py" line="28"/>
<source>Pause</source>
<translation>Pause</translation>
</message>
<message>
<location filename="../settings.py" line="29"/>
<source>Other settings</source>
<translation>Autres paramètres</translation>
</message>
<message>
<location filename="../settings.py" line="31"/>
<source>Delays</source>
<translation>Temporisation</translation>
</message>
<message>
<location filename="../settings.py" line="32"/>
<source>Auto-shift delay</source>
<translation>Délai avant répétition</translation>
</message>
<message>
<location filename="../settings.py" line="33"/>
<source>Auto-repeat rate</source>
<translation>Vitesse de répétition</translation>
</message>
<message>
<location filename="../settings.py" line="35"/>
<source>Sound</source>
<translation>Son</translation>
</message>
<message>
<location filename="../settings.py" line="36"/>
<source>Music volume</source>
<translation>Volume de la musique</translation>
</message>
<message>
<location filename="../settings.py" line="37"/>
<source>Effects volume</source>
<translation>Volume des effets sonores</translation>
</message>
<message>
<location filename="../settings.py" line="39"/>
<source>Show ghost piece</source>
<translation>Afficher la pièce fantôme</translation>
</message>
<message>
<location filename="../settings.py" line="40"/>
<source>Show next queue</source>
<translation>Afficher les 6 prochaines pièces</translation>
</message>
<message>
<location filename="../settings.py" line="41"/>
<source>Hold enabled</source>
<translation>Activer la réserve</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings.py" line="95"/>
<source>Settings</source>
<translation>Préférences</translation>
</message>
</context>
<context>
<name>Stats</name>
<message>
<location filename="../stats.py" line="43"/>
<source>High score</source>
<translation>Meilleur score</translation>
</message>
<message>
<location filename="../stats.py" line="126"/>
<source>COMBO x{:n}
{:n}</source>
<translation>COMBO x{:n}
{:n}</translation>
</message>
<message>
<location filename="../stats.py" line="158"/>
<source>BACK TO BACK
{:n}</source>
<translation>BACK TO BACK
{:n}</translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Time: {}
</source>
<translation>Temps : {}
</translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Lines per minute: {:.1f}</source>
<translation>Lignes par minute : {:.1f}</translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Tetrominos per minute: {:.1f}</source>
<translation>Tétrominos par minute : {:.1f}</translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Score: </source>
<translation>Score : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>High score: </source>
<translation>Meilleur score : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Level: </source>
<translation>Niveau : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Goal: </source>
<translation>Objectif : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Lines: </source>
<translation>Lignes : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Mini T-Spins: </source>
<translation>Mini T-Spins : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>T-Spins: </source>
<translation>T-Spins : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Back-to-back: </source>
<translation>Back-to-back : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Max combo: </source>
<translation>Combo max : </translation>
</message>
<message>
<location filename="../stats.py" line="189"/>
<source>Combos: </source>
<translation>Combos : </translation>
</message>
<message>
<location filename="../stats.py" line="228"/>
<source>Tetrominos locked down: </source>
<translation>Tétrominos bloqués : </translation>
</message>
<message>
<location filename="../stats.py" line="243"/>
<source>: </source>
<translation> : </translation>
</message>
</context>
<context>
<name>Window</name>
<message>
<location filename="../window.py" line="154"/>
<source>High score</source>
<translation>Meilleur score</translation>
</message>
<message>
<location filename="../window.py" line="93"/>
<source>&amp;New game</source>
<translation>&amp;Nouvelle partie</translation>
</message>
<message>
<location filename="../window.py" line="97"/>
<source>&amp;Settings</source>
<translation>&amp;Préférences</translation>
</message>
<message>
<location filename="../window.py" line="101"/>
<source>&amp;About</source>
<translation>&amp;À propos</translation>
</message>
<message>
<location filename="../window.py" line="139"/>
<source>A game is in progress.
Do you want to abord it?</source>
<translation>Une partie est en cours.
Voulez-vous l&apos;abandonner ?</translation>
</message>
<message>
<location filename="../window.py" line="139"/>
<source>Quit game?</source>
<translation>Quitter la partie ?</translation>
</message>
<message>
<location filename="../window.py" line="112"/>
<source>Tetris® clone by Adrien Malingrey
Tetris Game Design by Alekseï Pajitnov
Graphism inspired by Tetris Effect
Window style sheet: qdarkstyle by Colin Duquesnoy
Fonts by Markus Koellmann, Peter Wiegel
Images from:
OpenGameArt.org by beren77, Duion
Pexels.com by Min An, Jaymantri, Felix Mittermeier
Pixabay.com by LoganArt
Pixnio.com by Adrian Pelletier
Unsplash.com by Aron, Patrick Fore, Ilnur Kalimullin, Gabriel Garcia Marengo, Adnanta Raharja
StockSnap.io by Nathan Anderson, José Ignacio Pompé
Musics from ocremix.org by:
CheDDer Nardz, djpretzel, MkVaff, Sir_NutS, R3FORGED, Sir_NutS
Sound effects made with voc-one by Simple-Media</source>
<translation>Clone de Tetris® par Adrien Malingrey
Conception du jeu : Alekseï Pajitnov
Graphismes inspirés de Tetris Effect
Style de fenêtre : qdarkstyle par Colin Duquesnoy
Polices par Markus Koellmann, Peter Wiegel
Images issues de :
OpenGameArt.org par beren77, Duion
Pexels.com par Min An, Jaymantri, Felix Mittermeier
Pixabay.com par LoganArt
Pixnio.com par Adrian Pelletier
Unsplash.com par Aron, Patrick Fore, Ilnur Kalimullin, Gabriel Garcia Marengo, Adnanta Raharja
StockSnap.io par Nathan Anderson, José Ignacio Pompé
Musiques issues de ocremix.org par :
CheDDer Nardz, djpretzel, MkVaff, Sir_NutS, R3FORGED, Sir_NutS
Effets sonores réalisés avec voc-one de Simple-Media</translation>
</message>
</context>
</TS>

View File

@ -0,0 +1,3 @@
for /F %%n in ('dir /B *.ts') do pylupdate5 -verbose ..\window.py ..\settings.py ..\stats.py ..\matrix.py ..\frames.py -ts %%n
echo You may need to edit *.ts files with a text editor to correct special characters
pause

View File

@ -0,0 +1,3 @@
for /F %%n in ('dir /B *.ts') do pylupdate5 -verbose ..\window.py ..\settings.py ..\stats.py ..\matrix.py ..\frames.py -ts -noobsolete %%n
echo You may need to edit *.ts files with a text editor to correct special characters
pause

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>${app_name}</string>
<key>CFBundleDisplayName</key>
<string>${app_name}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIconFile</key>
<string>Icon.icns</string>
<key>CFBundleIdentifier</key>
<string>${mac_bundle_identifier}</string>
<key>LSBackgroundOnly</key>
<string>0</string>
<key>CFBundleShortVersionString</key>
<string>${version}</string>
<key>CFBundleVersion</key>
<string>${version}</string>
<key>CFBundleName</key>
<string>${app_name}</string>
<!-- Enable Retina support on OS X: -->
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB