51
README.md
@ -13,34 +13,13 @@ As for now, qdarkstyle doesn't support PySide2 so PyQt5 is recommanded
|
|||||||
|
|
||||||
#### Self-extracting archive for Windows (x64)
|
#### Self-extracting archive for Windows (x64)
|
||||||
|
|
||||||
[Download](https://github.com/adrienmalin/Tetris2000/raw/master/Tetris2000.exe) (24 Mo)
|
[Download](https://github.com/adrienmalin/Tetris2000/raw/master/dist/Tetris2000.exe) (24 Mo)
|
||||||
|
|
||||||
#### From [PyPI](https://pypi.org/)
|
|
||||||
|
|
||||||
Open a terminal and type:
|
|
||||||
|
|
||||||
pip3 install --user Tetris2000-PyQt5
|
|
||||||
|
|
||||||
Or:
|
|
||||||
|
|
||||||
pip3 install --user Tetris2000-PySide2
|
|
||||||
|
|
||||||
If Python's Scripts directory is in path, type:
|
|
||||||
|
|
||||||
Tetris2000
|
|
||||||
|
|
||||||
Else on Windows:
|
|
||||||
|
|
||||||
python -m Tetris2000
|
|
||||||
|
|
||||||
Else on Linux:
|
|
||||||
|
|
||||||
python3 -m Tetris2000
|
|
||||||
|
|
||||||
|
|
||||||
#### From [GitHub](https://github.com)
|
#### From [GitHub](https://github.com)
|
||||||
|
|
||||||
From a terminal, install PyQt5:
|
* Install [Python 3.x](https://www.python.org/downloads/)
|
||||||
|
|
||||||
|
* From a terminal, install PyQt5:
|
||||||
|
|
||||||
pip3 install --user PyQt5
|
pip3 install --user PyQt5
|
||||||
|
|
||||||
@ -48,11 +27,29 @@ or PySide2:
|
|||||||
|
|
||||||
pip3 install --user PySide2
|
pip3 install --user PySide2
|
||||||
|
|
||||||
Install qdarkstyle:
|
* Install qdarkstyle:
|
||||||
|
|
||||||
pip3 install --user qdarkstyle
|
pip3 install --user qdarkstyle
|
||||||
|
|
||||||
* Download the archive from [GitHub](https://github.com/adrienmalin/Tetris2000)
|
* Download the archive from [GitHub](https://github.com/adrienmalin/Tetris2000)
|
||||||
* Unzip the archive
|
* Unzip the archive
|
||||||
* Open Tetris2000 folder
|
|
||||||
* Launch Tetris2000.py
|
* Launch Tetris2000.py
|
||||||
|
|
||||||
|
### Credits
|
||||||
|
|
||||||
|
* [Tetris](https://tetris.com) Game Design by Alekseï Pajitnov
|
||||||
|
* Graphism inspired by [Tetris Effect](https://www.tetriseffect.game)
|
||||||
|
* Window style sheet: [qdarkstyle by Colin Duquesnoy](https://github.com/ColinDuquesnoy/QDarkStyleSheet)
|
||||||
|
* Fonts by [Markus Koellmann](http://markus-designs.com), [Peter Wiegel](http://www.peter-wiegel.de)
|
||||||
|
* Images from:<br>
|
||||||
|
[OpenGameArt.org](https://opengameart.org) by beren77, Duion<br>
|
||||||
|
[Pexels](https://www.pexels.com) by Min An, Jaymantri, Felix Mittermeier<br>
|
||||||
|
[Pixabay](https://pixabay.com) by LoganArt<br>
|
||||||
|
[PIXNIO](https://pixnio.com) by Adrian Pelletier<br>
|
||||||
|
[Unsplash](https://unsplash.com) by Aron, Patrick Fore, Ilnur Kalimullin, Gabriel Garcia Marengo, Adnanta Raharja<br>
|
||||||
|
[StockSnap.io](https://stocksnap.io) by Nathan Anderson, José Ignacio Pompé
|
||||||
|
* Musics from [Overclocked Remix](https://ocremix.org/game/510/tetris-gb) by:<br>
|
||||||
|
CheDDer Nardz, djpretzel, MkVaff, Sir_NutS, R3FORGED, Sir_NutS
|
||||||
|
* Sound effects made with [voc-one by Simple-Media](http://www.simple-media.co.uk/vsti.htm)
|
||||||
|
|
||||||
|
Thanks to my pythonista friends [krakozaure](https://github.com/krakozaure) and ABR
|
19
Tetris2000.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from qt5 import QtWidgets
|
||||||
|
from game_gui import Window
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def play():
|
||||||
|
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
|
||||||
|
win = Window()
|
||||||
|
win.show()
|
||||||
|
win.frames.new_game()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
play()
|
7
__version__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
__author__ = "Adrien Malingrey"
|
||||||
|
__title__ = "Tetris 2000"
|
||||||
|
__version__ = "0.3"
|
BIN
backgrounds/00-spacefield_a-000.png
Normal file
After Width: | Height: | Size: 444 KiB |
BIN
backgrounds/01-ez_space_lite_01.png
Normal file
After Width: | Height: | Size: 291 KiB |
BIN
backgrounds/03-PIXNIO-1761261-1200x800.jpg
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
backgrounds/04-milky-way-starry-sky-night-sky-star-956981.jpeg
Normal file
After Width: | Height: | Size: 467 KiB |
BIN
backgrounds/05-patrick-fore-562330-unsplash.jpg
Normal file
After Width: | Height: | Size: 945 KiB |
BIN
backgrounds/06-pexels-photo-176851.jpeg
Normal file
After Width: | Height: | Size: 456 KiB |
BIN
backgrounds/07-ilnur-kalimullin-153166-unsplash.jpg
Normal file
After Width: | Height: | Size: 549 KiB |
BIN
backgrounds/08-sky-828648_1920.jpg
Normal file
After Width: | Height: | Size: 754 KiB |
BIN
backgrounds/09-jose-ignacio-pompe-744075-unsplash.jpg
Normal file
After Width: | Height: | Size: 642 KiB |
BIN
backgrounds/10-nature-night-sky-stars-974471.jpeg
Normal file
After Width: | Height: | Size: 377 KiB |
BIN
backgrounds/11-pexels-photo-755726.jpeg
Normal file
After Width: | Height: | Size: 526 KiB |
BIN
backgrounds/12-PIXNIO-1727359-1920x1280.jpg
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
backgrounds/13-cosmos-1845140_1920.jpg
Normal file
After Width: | Height: | Size: 597 KiB |
BIN
backgrounds/14-StockSnap_CDFFE6C72E.jpg
Normal file
After Width: | Height: | Size: 800 KiB |
BIN
backgrounds/15-nebula-668783_1920.jpg
Normal file
After Width: | Height: | Size: 416 KiB |
106
consts.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
from qt5 import QtGui
|
||||||
|
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ICON_PATH = os.path.join(PATH, "icons", "icon.ico")
|
||||||
|
BG_IMAGE_DIR = os.path.join(PATH, "backgrounds")
|
||||||
|
START_BG_IMAGE_NAME = "00-spacefield_a-000.png"
|
||||||
|
MUSICS_DIR = os.path.join(PATH, "musics")
|
||||||
|
SFX_DIR = os.path.join(PATH, "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 = os.path.join(PATH, "locale")
|
||||||
|
FONTS_DIR = os.path.join(PATH, "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 = os.path.join(PATH, "icons", "splash_screen.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 = 100
|
||||||
|
INITIAL_SPEED = 1000
|
||||||
|
ENTRY_DELAY = 80
|
||||||
|
LINE_CLEAR_DELAY = 80
|
||||||
|
LOCK_DELAY = 500
|
||||||
|
TEMPORARY_TEXT_DURATION = 1000
|
||||||
|
|
||||||
|
# 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
|
55
fonts/Creative Commons Lizenz.txt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
Lizenzvertrag
|
||||||
|
|
||||||
|
DAS URHEBERRECHTLICH GESCHÜTZTE WERK ODER DER SONSTIGE SCHUTZGEGENSTAND (WIE UNTEN BESCHRIEBEN) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE („CCPL“ ODER „LIZENZVERTRAG“) ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER EINSCHLÄGIGE GESETZE GESCHÜTZT.
|
||||||
|
|
||||||
|
DURCH DIE AUSÜBUNG EINES DURCH DIESEN LIZENZVERTRAG GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄ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ÄREN.
|
||||||
|
|
||||||
|
1. Definitionen
|
||||||
|
|
||||||
|
1. Unter einer „Bearbeitung“ wird eine Übersetzung oder andere Bearbeitung des Werkes verstanden, die Ihre persönliche geistige Schöpfung ist. Eine freie Benutzung des Werkes wird nicht als Bearbeitung angesehen.
|
||||||
|
2. Unter den „Lizenzelementen“ werden die folgenden Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewählt und in der Bezeichnung der Lizenz genannt werden: „Namensnennung“, „Nicht-kommerziell“, „Weitergabe unter gleichen Bedingungen“.
|
||||||
|
3. Unter dem „Lizenzgeber“ wird die natürliche 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 unabhängigen Elementen verstanden, die aufgrund der Auswahl oder Anordnung der Elemente eine persönliche geistige Schöpfung ist. Darunter fallen auch solche Sammelwerke, deren Elemente systematisch oder methodisch angeordnet und einzeln mit Hilfe elektronischer Mittel oder auf andere Weise zugänglich sind (Datenbankwerke). Ein Sammelwerk wird im Zusammenhang mit dieser Lizenz nicht als Bearbeitung (wie oben beschrieben) angesehen.
|
||||||
|
5. Mit „SIE“ und „Ihnen“ ist die natürliche oder juristische Person gemeint, die die durch diese Lizenz gewährten Nutzungsrechte ausübt und die zuvor die Bedingungen dieser Lizenz im Hinblick auf das Werk nicht verletzt hat, oder die die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz einer vorherigen Verletzung auszuüben.
|
||||||
|
6. Unter dem „Schutzgegenstand“wird 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 natürliche 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 Tonträger, einer Funksendung, einem Laufbild oder einer Darbietung eines ausübenden Künstlers.
|
||||||
|
9. Unter dem „Werk“ wird eine persönliche geistige Schöpfung verstanden, die Ihnen unter den Bedingungen dieser Lizenz angeboten wird.
|
||||||
|
|
||||||
|
2. Schranken des Urheberrechts. Diese Lizenz lässt sämtliche Befugnisse unberührt, die sich aus den Schranken des Urheberrechts,aus dem Erschöpfungsgrundsatz oder anderen Beschränkungen der Ausschließlichkeitsrechte des Rechtsinhabers ergeben.
|
||||||
|
|
||||||
|
3. Lizenzierung. Unter den Bedingungen dieses Lizenzvertrages räumt Ihnen der Lizenzgeber ein lizenzgebührenfreies, räumlich und zeitlich (für die Dauer des Urheberrechts oder verwandten Schutzrechts) unbeschrä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ältigen, zu verbreiten und auszustellen;
|
||||||
|
2. den Schutzgegenstand in unkörperlicher Form öffentlich wiederzugeben, insbesondere vorzutragen, aufzuführen und vorzuführen, öffentlich zugänglich zu machen, zu senden, durch Bild- und Tonträger wiederzugeben sowie Funksendungen und öffentliche Zugänglichmachungen wiederzugeben;
|
||||||
|
3. den Schutzgegenstand auf Bild- oder Tonträ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öffentlichen und in dem in a. bis c. genannten Umfang zu verwerten;
|
||||||
|
|
||||||
|
Die genannten Nutzungsrechte können für alle bekannten Nutzungsarten ausgeübt werden. Die genannten Nutzungsrechte beinhalten das Recht, solche Verä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änkungen. Die Einräumung der Nutzungsrechte gemäß Ziffer 3 erfolgt ausdrücklich nur unter den folgenden Bedingungen:
|
||||||
|
|
||||||
|
1. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz vervielfältigen, verbreiten oder öffentlich wiedergeben, und Sie müssen stets eine Kopie oder die vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz beifügen, wenn Sie den Schutzgegenstandvervielfältigen, verbreiten oder öffentlich wiedergeben. Sie dürfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewährten Rechte ändern oder beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Sie müssen alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie dürfen den Schutzgegenstand mit keinen technischen Schutzmaß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ä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ßlich unter den Bedingungen dieser Lizenz, einer späteren Version dieser Lizenz mit denselben Lizenzelementen wie diese Lizenz oder einer Creative Commons iCommons Lizenz, die dieselben Lizenzelemente wie diese Lizenz enthält (z.B. Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 2.0 Japan), vervielfältigen, verbreiten oder ö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ügen, wenn Sie die Bearbeitung vervielfältigen, verbreiten oder öffentlich wiedergeben. Sie dürfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewährten Rechte ändern oder beschränken, und Sie müssen alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie dürfen eine Bearbeitung nicht mit technischen Schutzmaß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ä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ältigen, verbreiten oder öffentlich wiedergeben, müssen Sie alle Urhebervermerke für den Schutzgegenstand unverä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ünftigerweise durchfü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ühren, in welcher Form der Schutzgegenstand in die Bearbeitung eingegangen ist (z.B. „Französische Ü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 auffälliger Weise zu erfolgen hat wie vergleichbare Hinweise auf andere Rechtsinhaber.
|
||||||
|
4. Obwohl die gemäss Ziffer 3 gewährten Nutzungsrechte in umfassender Weise
|
||||||
|
ausgeübt werden dürfen, findet diese Erlaubnis ihre gesetzliche Grenze in
|
||||||
|
den Persönlichkeitsrechten der Urheber und ausübenden Künstler, deren berechtigte geistige und persönliche Interessen bzw. deren Ansehen oder Ruf nicht dadurch gefährdet werden dürfen, dass ein Schutzgegenstand über das gesetzlich zulässige Maß hinaus beeinträchtigt wird.
|
||||||
|
|
||||||
|
5. Gewährleistung. Sofern dies von den Vertragsparteien nicht anderweitig schriftlich vereinbart,, bietet der Lizenzgeber keine Gewährleistung für die erteilten Rechte, außer für den Fall, dass Mängel arglistig verschwiegen wurden. Für Mängel anderer Art, insbesondere bei der mangelhaften Lieferung von Verkörperungen des Schutzgegenstandes, richtet sich die Gewährleistung nach der Regelung, die die Person, die Ihnen den Schutzgegenstand zur Verfügung stellt, mit Ihnen außerhalb dieser Lizenz vereinbart, oder - wenn eine solche Regelung nicht getroffen wurde - nach den gesetzlichen Vorschriften.
|
||||||
|
|
||||||
|
6. Haftung. Über die in Ziffer 5 genannte Gewährleistung hinaus haftet Ihnen der Lizenzgeber nur für Vorsatz und grobe Fahrlässigkeit.
|
||||||
|
|
||||||
|
7. Vertragsende
|
||||||
|
|
||||||
|
1. Dieser Lizenzvertrag und die durch ihn eingeräumten Nutzungsrechte enden automatisch bei jeder Verletzung der Vertragsbedingungen durch Sie. Für natü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ürlichen oder juristischen Personen erfü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ä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ältigen, verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz für den Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingeräumt hat.
|
||||||
|
2. Jedes Mal, wenn Sie eine Bearbeitung vervielfältigen, verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz für den ursprünglichen Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingeräumt hat.
|
||||||
|
3. Sollte eine Bestimmung dieses Lizenzvertrages unwirksam sein, so wird die Wirksamkeit der übrigen Lizenzbestimmungen dadurch nicht berü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ändige Vereinbarung zwischen den Vertragsparteien hinsichtlich des Schutzgegenstandes dar. Es gibt keine weiteren ergänzenden Vereinbarungen oder mündlichen Abreden im Hinblick auf den Schutzgegenstand. Der Lizenzgeber ist an keine zusätzlichen Abreden gebunden, die aus irgendeiner Absprache mit Ihnen entstehen könnten. Der Lizenzvertrag kann nicht ohne eine übereinstimmende schriftliche Vereinbarung zwischen dem Lizenzgeber und Ihnen abgeändert werden.
|
||||||
|
6. Auf diesen Lizenzvertrag findet das Recht der Bundesrepublik Deutschland Anwendung.
|
18
fonts/Maass readme.txt
Normal 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
|
BIN
fonts/PixelCaps!.otf
Normal file
BIN
fonts/Preview.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
fonts/ReadMe.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
fonts/maass slicer Italic.ttf
Normal file
2
fonts/markus-designs.com.url
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[InternetShortcut]
|
||||||
|
URL=http://markus-designs.com/
|
2
fonts/markuskoellmann.com.url
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[InternetShortcut]
|
||||||
|
URL=http://markuskoellmann.com/
|
1472
game_gui.py
Normal file
BIN
icons/16.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
icons/32.png
Normal file
After Width: | Height: | Size: 802 B |
BIN
icons/48.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/icon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
icons/splash_screen.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
323
locale/Tetris2000.ts
Normal 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="../game_gui.py" line="930"/>
|
||||||
|
<source>New game</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="917"/>
|
||||||
|
<source>A game is in progress.
|
||||||
|
Do you want to abord it?</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="930"/>
|
||||||
|
<source>Start level:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1048"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1037"/>
|
||||||
|
<source>Game over</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1040"/>
|
||||||
|
<source>Congratulations!
|
||||||
|
You have the high score: {}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1050"/>
|
||||||
|
<source>Score: {}
|
||||||
|
High score: {}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Matrix</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="135"/>
|
||||||
|
<source>Level
|
||||||
|
</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="418"/>
|
||||||
|
<source>PAUSE
|
||||||
|
|
||||||
|
Press %s
|
||||||
|
to resume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="426"/>
|
||||||
|
<source>GAME
|
||||||
|
OVER</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingStrings</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1177"/>
|
||||||
|
<source>Keyboard settings</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1178"/>
|
||||||
|
<source>Move left</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1179"/>
|
||||||
|
<source>Move right</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1180"/>
|
||||||
|
<source>Rotate clockwise</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1181"/>
|
||||||
|
<source>Rotate counterclockwise</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1182"/>
|
||||||
|
<source>Soft drop</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1183"/>
|
||||||
|
<source>Hard drop</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1184"/>
|
||||||
|
<source>Hold</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1185"/>
|
||||||
|
<source>Pause</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1186"/>
|
||||||
|
<source>Other settings</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1188"/>
|
||||||
|
<source>Delays</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1189"/>
|
||||||
|
<source>Auto-shift delay</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1190"/>
|
||||||
|
<source>Auto-repeat rate</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1192"/>
|
||||||
|
<source>Sound</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1193"/>
|
||||||
|
<source>Music volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1194"/>
|
||||||
|
<source>Effects volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1196"/>
|
||||||
|
<source>Show ghost piece</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1197"/>
|
||||||
|
<source>Show next queue</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1198"/>
|
||||||
|
<source>Hold enabled</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1113"/>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Stats</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="533"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="616"/>
|
||||||
|
<source>COMBO x{:n}
|
||||||
|
{:n}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="648"/>
|
||||||
|
<source>BACK TO BACK
|
||||||
|
{:n}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Score: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>High score: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Time: {}
|
||||||
|
</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Level: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Goal: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Lines: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Mini T-Spins: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>T-Spins: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Back-to-back: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Max combo: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Combos: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Lines per minute: {:.1f}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Tetrominos locked down: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Tetrominos per minute: {:.1f}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="733"/>
|
||||||
|
<source>: </source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Window</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1411"/>
|
||||||
|
<source>&New game</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1415"/>
|
||||||
|
<source>&Settings</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1419"/>
|
||||||
|
<source>&About</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1457"/>
|
||||||
|
<source>Quit game?</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1457"/>
|
||||||
|
<source>A game is in progress.
|
||||||
|
Do you want to abord it?</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1472"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1430"/>
|
||||||
|
<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>
|
BIN
locale/fr.qm
Normal file
352
locale/fr.ts
Normal 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="../game_gui.py" line="930"/>
|
||||||
|
<source>New game</source>
|
||||||
|
<translation>Nouvelle partie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="917"/>
|
||||||
|
<source>A game is in progress.
|
||||||
|
Do you want to abord it?</source>
|
||||||
|
<translation>Une partie est en cours.
|
||||||
|
Voulez-vous l'abandonner ?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="930"/>
|
||||||
|
<source>Start level:</source>
|
||||||
|
<translation>Commencer au niveau :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1048"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation>Meilleur score</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1037"/>
|
||||||
|
<source>Game over</source>
|
||||||
|
<translation>Partie terminée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1040"/>
|
||||||
|
<source>Congratulations!
|
||||||
|
You have the high score: {}</source>
|
||||||
|
<translation>Bravo !
|
||||||
|
Vous avez atteint le meilleur score : {}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1050"/>
|
||||||
|
<source>Score: {}
|
||||||
|
High score: {}</source>
|
||||||
|
<translation>Score : {}
|
||||||
|
Meilleur score : {}</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Matrix</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="135"/>
|
||||||
|
<source>Level
|
||||||
|
</source>
|
||||||
|
<translation>Niveau
|
||||||
|
</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="418"/>
|
||||||
|
<source>PAUSE
|
||||||
|
|
||||||
|
Press %s
|
||||||
|
to resume</source>
|
||||||
|
<translation>PAUSE
|
||||||
|
|
||||||
|
Appuyez sur
|
||||||
|
%s
|
||||||
|
pour reprendre</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="426"/>
|
||||||
|
<source>GAME
|
||||||
|
OVER</source>
|
||||||
|
<translation>PARTIE
|
||||||
|
TERMINÉE</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingStrings</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1177"/>
|
||||||
|
<source>Keyboard settings</source>
|
||||||
|
<translation>Configuration du clavier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1178"/>
|
||||||
|
<source>Move left</source>
|
||||||
|
<translation>Déplacer à gauche</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1179"/>
|
||||||
|
<source>Move right</source>
|
||||||
|
<translation>Déplacer à droite</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1180"/>
|
||||||
|
<source>Rotate clockwise</source>
|
||||||
|
<translation>Tourner dans le sens horaire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1181"/>
|
||||||
|
<source>Rotate counterclockwise</source>
|
||||||
|
<translation>Tourner dans le sens anti-horaire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1182"/>
|
||||||
|
<source>Soft drop</source>
|
||||||
|
<translation>Chute lente</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1183"/>
|
||||||
|
<source>Hard drop</source>
|
||||||
|
<translation>Chute rapide</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1184"/>
|
||||||
|
<source>Hold</source>
|
||||||
|
<translation>Réserve</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1185"/>
|
||||||
|
<source>Pause</source>
|
||||||
|
<translation>Pause</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1186"/>
|
||||||
|
<source>Other settings</source>
|
||||||
|
<translation>Autres paramètres</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1188"/>
|
||||||
|
<source>Delays</source>
|
||||||
|
<translation>Temporisation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1189"/>
|
||||||
|
<source>Auto-shift delay</source>
|
||||||
|
<translation>Délai avant répétition</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1190"/>
|
||||||
|
<source>Auto-repeat rate</source>
|
||||||
|
<translation>Vitesse de répétition</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1192"/>
|
||||||
|
<source>Sound</source>
|
||||||
|
<translation>Son</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1193"/>
|
||||||
|
<source>Music volume</source>
|
||||||
|
<translation>Volume de la musique</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1194"/>
|
||||||
|
<source>Effects volume</source>
|
||||||
|
<translation>Volume des effets sonores</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1196"/>
|
||||||
|
<source>Show ghost piece</source>
|
||||||
|
<translation>Afficher la pièce fantôme</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1197"/>
|
||||||
|
<source>Show next queue</source>
|
||||||
|
<translation>Afficher les 6 prochaines pièces</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1198"/>
|
||||||
|
<source>Hold enabled</source>
|
||||||
|
<translation>Activer la réserve</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1113"/>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Préférences</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Stats</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="533"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation>Meilleur score</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="616"/>
|
||||||
|
<source>COMBO x{:n}
|
||||||
|
{:n}</source>
|
||||||
|
<translation>COMBO x{:n}
|
||||||
|
{:n}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="648"/>
|
||||||
|
<source>BACK TO BACK
|
||||||
|
{:n}</source>
|
||||||
|
<translation>BACK TO BACK
|
||||||
|
{:n}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Time: {}
|
||||||
|
</source>
|
||||||
|
<translation>Temps : {}
|
||||||
|
</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Lines per minute: {:.1f}</source>
|
||||||
|
<translation>Lignes par minute : {:.1f}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Tetrominos per minute: {:.1f}</source>
|
||||||
|
<translation>Tétrominos par minute : {:.1f}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Score: </source>
|
||||||
|
<translation>Score : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>High score: </source>
|
||||||
|
<translation>Meilleur score : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Level: </source>
|
||||||
|
<translation>Niveau : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Goal: </source>
|
||||||
|
<translation>Objectif : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Lines: </source>
|
||||||
|
<translation>Lignes : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Mini T-Spins: </source>
|
||||||
|
<translation>Mini T-Spins : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>T-Spins: </source>
|
||||||
|
<translation>T-Spins : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Back-to-back: </source>
|
||||||
|
<translation>Back-to-back : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Max combo: </source>
|
||||||
|
<translation>Combo max : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="679"/>
|
||||||
|
<source>Combos: </source>
|
||||||
|
<translation>Combos : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="718"/>
|
||||||
|
<source>Tetrominos locked down: </source>
|
||||||
|
<translation>Tétrominos bloqués : </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="733"/>
|
||||||
|
<source>: </source>
|
||||||
|
<translation> : </translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Window</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1472"/>
|
||||||
|
<source>High score</source>
|
||||||
|
<translation>Meilleur score</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1411"/>
|
||||||
|
<source>&New game</source>
|
||||||
|
<translation>&Nouvelle partie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1415"/>
|
||||||
|
<source>&Settings</source>
|
||||||
|
<translation>&Préférences</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1419"/>
|
||||||
|
<source>&About</source>
|
||||||
|
<translation>&À propos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1457"/>
|
||||||
|
<source>A game is in progress.
|
||||||
|
Do you want to abord it?</source>
|
||||||
|
<translation>Une partie est en cours.
|
||||||
|
Voulez-vous l'abandonner ?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1457"/>
|
||||||
|
<source>Quit game?</source>
|
||||||
|
<translation>Quitter la partie ?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../game_gui.py" line="1430"/>
|
||||||
|
<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>
|
2
locale/update_ts.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
for /F %%n in ('dir /B *.ts') do pylupdate5 ..\game_gui.py -ts %%n
|
||||||
|
pause
|
2
locale/update_ts_noobsolete.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
for /F %%n in ('dir /B *.ts') do pylupdate5 ..\game_gui.py -ts -noobsolete %%n
|
||||||
|
pause
|
BIN
musics/01_Tetris_McVaffeQuasi_Ultimix_OC_ReMix.mp3
Normal file
BIN
musics/Tetris_CheDDer_OC_ReMix.mp3
Normal file
BIN
musics/Tetris_Crimea_River_OC_ReMix.mp3
Normal file
BIN
musics/Tetris_Slavic_Roots_OC_ReMix.mp3
Normal file
BIN
musics/Tetris_Thirty-Plus_Mix_OC_ReMix.mp3
Normal file
43
pyinstaller.spec
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['Tetris2000.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[
|
||||||
|
("backgrounds/*", "backgrounds"),
|
||||||
|
("fonts/*.ttf", "fonts"),
|
||||||
|
("fonts/*.otf", "fonts"),
|
||||||
|
("icons/*.ico", "icons"),
|
||||||
|
("icons/splash_screen.png", "icons"),
|
||||||
|
("locale/*.qm", "locale"),
|
||||||
|
("musics/*.mp3", "musics"),
|
||||||
|
("sfx/*.wav", "sfx")
|
||||||
|
],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=["PyQt4", "PySide", "PySide2"],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='Tetris2000',
|
||||||
|
debug=False,
|
||||||
|
strip=False,
|
||||||
|
upx=False,
|
||||||
|
console=False,
|
||||||
|
icon='icons\icon.ico')
|
||||||
|
coll = COLLECT(exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=False,
|
||||||
|
name='Tetris2000')
|
27
qt5.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PyQt5 import QtWidgets, QtCore, QtGui, QtMultimedia
|
||||||
|
except ImportError as pyqt5_error:
|
||||||
|
try:
|
||||||
|
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
|
BIN
sfx/hard_drop.wav
Normal file
BIN
sfx/line_clear.wav
Normal file
BIN
sfx/rotate.wav
Normal file
BIN
sfx/tetris.wav
Normal file
BIN
sfx/tetris.xt
Normal file
BIN
sfx/wall.wav
Normal file
465
tetromino.py
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
import consts
|
||||||
|
from consts import L, R, U, D, CLOCKWISE, COUNTERCLOCKWISE
|
||||||
|
from qt5 import QtCore, QtGui
|
||||||
|
|
||||||
|
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, 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
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Point({}, {})".format(self.x(), self.y())
|
||||||
|
|
||||||
|
__str__ = __repr__
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
return self.move(0, D, trail=1)
|
||||||
|
|
||||||
|
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=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):
|
||||||
|
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 """
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|