diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c120690 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.vscode/* \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..0ff02b0 --- /dev/null +++ b/Pipfile @@ -0,0 +1,14 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pygame = "*" +pygame-gui = "*" + +[dev-packages] +autopep8 = "*" + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..88e7a1d --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,124 @@ +{ + "_meta": { + "hash": { + "sha256": "b42d0b2a166458fe2b6937a50a7d074afceca99686d2373818f2873d9d756f53" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.10" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "pygame": { + "hashes": [ + "sha256:0427c103f741234336e5606d2fad86f5403c1a3d1dc55c309fbff3c984f0c9ae", + "sha256:07ca9f683075aea9bd977af9f09a720ebf747343d3ea8103e4f1735283b02330", + "sha256:0e06ae8e1c830f1b9c36a2bc6bb11de840232e95b78e2c349c6ed803a303be19", + "sha256:0e97d38308c441942577fea7fcd1326308bc56d6be6c024218e94d075d322e0f", + "sha256:119dee20c372c85dc47b717119534d15a60c64ceab8b0eb09278866d10486afe", + "sha256:1219a963941bd53aa754e8449364c142004fe706c33a9c22ff2a76521a82d078", + "sha256:1fddec8829e96424800c806582d73a5173b7d48946cccf7d035839ca09850db8", + "sha256:20676da24e3e3e6b9fc4eecc7ba09d77ef46c3a83a028763ba1d222476c2e3fb", + "sha256:2405414d8c572668e04739875661e030a0c588e197fa95463fe301c3d0a0510b", + "sha256:24254c4244f0d9bdc904f5d3f38e86757ca4c6aa0e44a6d55ef5e016bc7274d6", + "sha256:24b4f7f30fa2b3d092b60be6fcc725fb91d569fc87a9bcc91614ee8b0c005726", + "sha256:3bb0674aa789848ddc264bfc60c54965bf3bb659c141de4f600e379acc9b944c", + "sha256:3c8d6637ff75351e581327efefa9d04eeb0f257b533392b6cc6b15ceca4f7c5e", + "sha256:40e4d8d65985bb467d9c5a1305fb53fd6820c61d764979600becab973339676f", + "sha256:4aa3ae32320cc704d63e185864e44f6265c2a6e52c9384afe152cc3d51b3a2ef", + "sha256:50d9a21edd551669862c27c9272747401b20b1939abaacb842c08ea1cdd1c04d", + "sha256:5c7600bf307de1ca1dca0cc7840e34604d5b0b0a5a5dad345c3fa62b054b886d", + "sha256:5d0c14152d0ca8ef5fbcc5ed9981462bdf59a9ae85a291e62d8a8d0b7e5cbe43", + "sha256:5e88b0d4338b94960686f59396f23f7f684fed4859fcc3b9f40286d72c1c61af", + "sha256:5ebbefb8b576572c8fc97a3321d37dc2b4afea6b6e3877a67f7158d8c2c4cefe", + "sha256:636f51f56615d67459b11918206bb4da30cd7d7042027bf997c218ccd6c77902", + "sha256:660c80c0b2e80f1f801715583b759fb4c7bc0c11eb3b534e89c9fc4bfbc38acd", + "sha256:6ecda8dd4583982bb65f9c682f244a5e94524dcf628379766227e9ed97201a49", + "sha256:754c2906f2ef47173a14493e1de116b2a56a2c8e1764f1202ba844d080248a5b", + "sha256:7889dce887ec83c9a0bef8d9eb3669d8863fdaf37c45bacec707d8ad90b24a38", + "sha256:7fdb93b4282962c9a2ebf1af994ee698be823dd913218ed97a5f2fb372b10b66", + "sha256:8e87716114e97322fb177e223d889a4be369a0f73212f4f8507fe0cd43253b23", + "sha256:93c4cbfc942dd00410eaa9e84252129f9f9993f37f683006d7b49ab245342254", + "sha256:9649419254d3282dae41f23837de4108b17bc62187c3acd8af2ae3801b765cbd", + "sha256:97a74ba186deee68318a52637012ef6abf5be6282c659e1d1ba6ad08cf35ec85", + "sha256:9d6452419e01a0f848aed0597f69fd10a4c2a7750c15d1b0607f86090a39dcf3", + "sha256:9d7b021b8dde5d528363e474bc18bd6f79a9666eef89fb4859bcb8f0a536c9de", + "sha256:a0ccf8e3dce7ca67d523a6020b7e3dbf4b26797a9a8db5cc4c7b5ef20fb64701", + "sha256:a56a811d8821f7b9a594e3d0e0dd8bd39b25e3eea8963d5963263b90fd2ea5c2", + "sha256:c5ea87da5fe4b6164c3854f3b0c9146811dbad0dd7fa74297683dfacc485ae1c", + "sha256:c99b95e62cdda29c2e60235d7763447c168a6a877403e6f9ca5b2e2bb297c2ce", + "sha256:c9ce7f3d8af14d7e04eb7eb41c5e5313c43508c252bb2b9eb53e51fc87ada9fd", + "sha256:ca5ef1315fa67c241a657ab077be44f230c05740c95f0b46409457dceefdc7e5", + "sha256:d2d3c50ee9847b743db6cd7b1bb17a94c2c2abc16679d70f5e745cabdf19e655", + "sha256:d6d0eca28f886f0477cd0721ac688189155a587f2bb8eae740e52ca56c3ad23c", + "sha256:dad6bf3fdd3752d7519422f3732be779b98fe7c87d32c3efe2fdffdcbeebb6ca", + "sha256:db2f40d5a75fd9cdda473c58b0d8b294da6e0179f00bb3b1fc2f7f29cac09bea", + "sha256:dc4444d61d48c5546df5137cdf81554887ddb6e2ef1be7f51eb77ea3b6bdd56f", + "sha256:dcc285ee1f1d0e2672cc52f880fd3f564b1505de710e817f692fbf64a72ca657", + "sha256:dd528dbb91eca16f7522c975d0f9e94b95f6b5024c82c3247dc0383d242d33c6", + "sha256:e09044e9e1aa8512d6a9c7ce5f94b881824bcfc401105f3c24f546dfc3bb4aa5", + "sha256:e18c9466131378421d00fc40b637425229238d506a073d9c537b230b355a25d6", + "sha256:e1bb25986db77a48f632469c6bc61baf7508ce945aa6161c02180d4ee5ac5b8d", + "sha256:e4b4cd440d50a9f8551b8989e856aab175593af07eb825cad22fd2f8f6f2ffce", + "sha256:e627300a66a90651fb39e41601d447b1fdbbfffca3f08ef0278d6cc0436b2160", + "sha256:e7a8e18677e0064b7a422f6653a622652d932826a27e50f279d55a8b122a1a83", + "sha256:e8632f6b2ddb90f6f3950744bd65d5ef15af615e3034057fa30ff836f48a7179", + "sha256:ea36f4f93524554a35cac2359df63b50af6556ed866830aa1f07f0d8580280ea", + "sha256:f149e182d0eeef15d8a9b4c9dad1b87dc6eba3a99bd3c44a777a3a2b053a3dca", + "sha256:fc2e5db54491e8f27785fc5204c96f540d3557dcf5b0a9a857b6594d6b32561b", + "sha256:fc30e834f65b893d1b4c230070183bf98e6b70c41c1511687e8436a33d5ce49d", + "sha256:fcc9586e17875c0cdf8764597955f9daa979098fd4f80be07ed68276ac225480", + "sha256:ff961c3280d6ee5f4163f4772f963d7a4dbe42e36c6dd54b79ad436c1f046e5d" + ], + "index": "pypi", + "version": "==2.1.2" + }, + "pygame-gui": { + "hashes": [ + "sha256:6c765dde8bfa2d0314c5f281fe006d2519b3ed6aebbc43e0aa8b0a5d91ba9992" + ], + "index": "pypi", + "version": "==0.6.4" + }, + "python-i18n": { + "hashes": [ + "sha256:bda5b8d889ebd51973e22e53746417bd32783c9bd6780fd27cadbb733915651d", + "sha256:df97f3d2364bf3a7ebfbd6cbefe8e45483468e52a9e30b909c6078f5f471e4e8" + ], + "version": "==0.3.9" + } + }, + "develop": { + "autopep8": { + "hashes": [ + "sha256:8b1659c7f003e693199f52caffdc06585bb0716900bbc6a7442fd931d658c077", + "sha256:ad924b42c2e27a1ac58e432166cc4588f5b80747de02d0d35b1ecbd3e7d57207" + ], + "index": "pypi", + "version": "==2.0.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785", + "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b" + ], + "markers": "python_version >= '3.6'", + "version": "==2.9.1" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.1" + } + } +} diff --git a/Project.py b/Project.py deleted file mode 100644 index c58cdaf..0000000 --- a/Project.py +++ /dev/null @@ -1,57 +0,0 @@ -import pgzrun -from random import randint, choice -import string - -WIDTH = 800 -HEIGHT = 500 -BLACK = (0, 0, 0) -WHITE = (255, 255, 255) -# LETTER = {"letter": "", "x": 0, "y": 0} -ON_SCREEN_LETTERS = [] -VELOCITY = 1 -SCORE = {"RIGHT": 0, "WRONG": 0} - - -def draw(): # Pygame Zero draw function - screen.clear() - screen.fill(BLACK) - for LETTER in ON_SCREEN_LETTERS: - screen.draw.text(LETTER["letter"], (LETTER["x"], LETTER["y"]), fontsize=50, color=WHITE) - screen.draw.text("RIGHT: " + str(SCORE["RIGHT"]), (WIDTH - 130, 10), fontsize=30, color=WHITE) - screen.draw.text("WRONG: " + str(SCORE["WRONG"]), (WIDTH - 130, 40), fontsize=30, color=WHITE) - - -def update(): - for i, LETTER in enumerate(ON_SCREEN_LETTERS): - LETTER["y"] += VELOCITY - if LETTER["y"] == HEIGHT - 5: - SCORE["WRONG"] += 1 - delete_letter(i) - while len(ON_SCREEN_LETTERS) < 4: - add_letter() - - -def on_key_down(key, mod, unicode): - if unicode: - for i, LETTER in enumerate(ON_SCREEN_LETTERS): - if LETTER["letter"] == unicode: - SCORE["RIGHT"] += 1 - delete_letter(i) - return - else: - SCORE["WRONG"] += 1 - - -def add_letter(): - letter = choice(string.ascii_letters).lower() - x = randint(10, WIDTH - 20) - y = 1 - ON_SCREEN_LETTERS.append({"letter": letter, "x": x, "y": y}) - - -def delete_letter(i): - del ON_SCREEN_LETTERS[i] - add_letter() - - -pgzrun.go() diff --git a/README.md b/README.md index 6663774..6ec3dcf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,60 @@ -[](https://classroom.github.com/online_ide?assignment_repo_id=8765555&assignment_repo_type=AssignmentRepo) -# PythonProjectTmp -ProjectTemplate +
+ +## ✏️ Who we are? + +We are students of King Mongkut's University of Technology North Bangkok working together on a group project on a computer programming class. + +## 📣 What is the project about? + +Typing test project, which requires python to create, which the instructor suggested using pygame for this project. + +## 📦 What are the libraries in the project? + +- pygame (latest) -> no specific version +- pygame_gui (latest) -> no specific version + +These libraries are managed with **pipenv** to make them easier to run in different environments and our project is using python version **3.9** + +## ⚗️ How to open the program? + +First of all, you need to contact pipenv to manage the environment, where you can see how to install it [here](https://pypi.org/project/pipenv/). + +After installation you need to go into the source code folder first after typing the command. + +``` +pipenv shell +pipenv install +``` + +After typing the above two commands, you can run the game with commands via script. + +**If you're running macOS or Linux** + +``` +chmod +x ./run.sh +./run.sh +``` + +**If you're running Windows** + +``` +You can click on **run.bat** to open the program. +or +.\run.bat +``` + +## 🔨 Authors + +- Yongyuth Chuankhuntod **(Python Developer & Lead Team)** +- Sukonlawat Choolert **(Graphic Designer)** +- Pornthawan Khumphai **(Graphic Designer)** +- Pawat Narkjuathong **(Composer)** +- Chanakan Toursupap **(Composer)** +- Jirapong Chanthaleka **(Composer)** diff --git a/__tests__/accuracy_and_wpm.py b/__tests__/accuracy_and_wpm.py new file mode 100644 index 0000000..67af67e --- /dev/null +++ b/__tests__/accuracy_and_wpm.py @@ -0,0 +1,12 @@ +import time + +timer = time.time() + +print(timer) +time.sleep(7.64) + +total_timer = time.time() - timer +print(total_timer) + +words_per_minute = (len("Every beginning has an end") * 60) / (5 * total_timer) +print(words_per_minute) \ No newline at end of file diff --git a/packages/sunshine_typing.png b/packages/sunshine_typing.png new file mode 100644 index 0000000..772f680 Binary files /dev/null and b/packages/sunshine_typing.png differ diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..221e7b0 --- /dev/null +++ b/run.bat @@ -0,0 +1 @@ +python source/__main__.py \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..0813c6d --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python3 source/__main__.py \ No newline at end of file diff --git a/sentences.txt b/sentences.txt new file mode 100644 index 0000000..c94e869 --- /dev/null +++ b/sentences.txt @@ -0,0 +1,10 @@ +A farmer was farming and he found a valuable treasure box that changed his life. +There was an epidemic in our cattle farm and now we need a lot of veterinarians. +Legend has it that there was a monster from another dimension with a very formidable. +The seeds found in the Sacred Grove are usually high-yielding seeds that never die. +Ender Dragons are powerful creatures and possess many treasures which is our goal. +Such powerful weapons always had a cruel and divine history that made people want. +Honest pets are usually dogs or cats, but you said taming a dragon was impossible. +A valuable treasure known as the Ender's Box is extremely rare in the magical world. +You can become a conqueror if you train enough to fight dragons which is difficult. +Magic is powerful, but if used improperly, it can overwhelm the user with dark energy. \ No newline at end of file diff --git a/source/__main__.py b/source/__main__.py new file mode 100644 index 0000000..7fe8b46 --- /dev/null +++ b/source/__main__.py @@ -0,0 +1,21 @@ +from app.configurations import configure +from app import program +from pygame.locals import * + +import os + + +configure.WINDOW_NAME = 'Sunshine Typing By Component Team' +configure.WINDOW_RECT = Rect(0, 0, 1280, 720) +configure.GAME_THEME_PATH = os.getcwd() + '/source/app/data/themes/default_theme.json' +configure.SENTENCES_PATH = os.getcwd() + '/sentences.txt' +configure.ENABLED_SCROLLING_BACKGROUND = False + + +if __name__ == '__main__': + bootstrap = program.Typing( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ).run() \ No newline at end of file diff --git a/.github/.keep b/source/app/__init__.py similarity index 100% rename from .github/.keep rename to source/app/__init__.py diff --git a/source/app/__pycache__/__init__.cpython-310.pyc b/source/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2ad0125 Binary files /dev/null and b/source/app/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/__pycache__/engine.cpython-310.pyc b/source/app/__pycache__/engine.cpython-310.pyc new file mode 100644 index 0000000..0934136 Binary files /dev/null and b/source/app/__pycache__/engine.cpython-310.pyc differ diff --git a/source/app/__pycache__/program.cpython-310.pyc b/source/app/__pycache__/program.cpython-310.pyc new file mode 100644 index 0000000..eef0582 Binary files /dev/null and b/source/app/__pycache__/program.cpython-310.pyc differ diff --git a/source/app/assets/__init__.py b/source/app/assets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/assets/audio/effect/click.ogg b/source/app/assets/audio/effect/click.ogg new file mode 100644 index 0000000..92b0d10 Binary files /dev/null and b/source/app/assets/audio/effect/click.ogg differ diff --git a/source/app/assets/audio/giga_chad.ogg b/source/app/assets/audio/giga_chad.ogg new file mode 100644 index 0000000..ebb1d89 Binary files /dev/null and b/source/app/assets/audio/giga_chad.ogg differ diff --git a/source/app/assets/image/__init__.py b/source/app/assets/image/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/assets/image/back.png b/source/app/assets/image/back.png new file mode 100644 index 0000000..0c7a15e Binary files /dev/null and b/source/app/assets/image/back.png differ diff --git a/source/app/assets/image/back_hovered.png b/source/app/assets/image/back_hovered.png new file mode 100644 index 0000000..c143bf1 Binary files /dev/null and b/source/app/assets/image/back_hovered.png differ diff --git a/source/app/assets/image/background.jpg b/source/app/assets/image/background.jpg new file mode 100644 index 0000000..760787c Binary files /dev/null and b/source/app/assets/image/background.jpg differ diff --git a/source/app/assets/image/core_background.png b/source/app/assets/image/core_background.png new file mode 100644 index 0000000..32198ca Binary files /dev/null and b/source/app/assets/image/core_background.png differ diff --git a/source/app/assets/image/entry_name_background.png b/source/app/assets/image/entry_name_background.png new file mode 100644 index 0000000..676e3b9 Binary files /dev/null and b/source/app/assets/image/entry_name_background.png differ diff --git a/source/app/assets/image/exit.png b/source/app/assets/image/exit.png new file mode 100644 index 0000000..f285412 Binary files /dev/null and b/source/app/assets/image/exit.png differ diff --git a/source/app/assets/image/exit_hovered.png b/source/app/assets/image/exit_hovered.png new file mode 100644 index 0000000..8454228 Binary files /dev/null and b/source/app/assets/image/exit_hovered.png differ diff --git a/source/app/assets/image/menu.png b/source/app/assets/image/menu.png new file mode 100644 index 0000000..1b82054 Binary files /dev/null and b/source/app/assets/image/menu.png differ diff --git a/source/app/assets/image/menu_hovered.png b/source/app/assets/image/menu_hovered.png new file mode 100644 index 0000000..e7847ba Binary files /dev/null and b/source/app/assets/image/menu_hovered.png differ diff --git a/source/app/assets/image/mini_sunshine_typing_logo.png b/source/app/assets/image/mini_sunshine_typing_logo.png new file mode 100644 index 0000000..8de7c27 Binary files /dev/null and b/source/app/assets/image/mini_sunshine_typing_logo.png differ diff --git a/source/app/assets/image/name_label.png b/source/app/assets/image/name_label.png new file mode 100644 index 0000000..a3c0d57 Binary files /dev/null and b/source/app/assets/image/name_label.png differ diff --git a/source/app/assets/image/play.png b/source/app/assets/image/play.png new file mode 100644 index 0000000..14b7a5f Binary files /dev/null and b/source/app/assets/image/play.png differ diff --git a/source/app/assets/image/play_hovered.png b/source/app/assets/image/play_hovered.png new file mode 100644 index 0000000..71cde59 Binary files /dev/null and b/source/app/assets/image/play_hovered.png differ diff --git a/source/app/assets/image/prepare_background.png b/source/app/assets/image/prepare_background.png new file mode 100644 index 0000000..559a84b Binary files /dev/null and b/source/app/assets/image/prepare_background.png differ diff --git a/source/app/assets/image/restart.png b/source/app/assets/image/restart.png new file mode 100644 index 0000000..1cc88e9 Binary files /dev/null and b/source/app/assets/image/restart.png differ diff --git a/source/app/assets/image/restart_hovered.png b/source/app/assets/image/restart_hovered.png new file mode 100644 index 0000000..3c542eb Binary files /dev/null and b/source/app/assets/image/restart_hovered.png differ diff --git a/source/app/assets/image/scoreboard_sign.png b/source/app/assets/image/scoreboard_sign.png new file mode 100644 index 0000000..cb2877c Binary files /dev/null and b/source/app/assets/image/scoreboard_sign.png differ diff --git a/source/app/assets/image/scoreboard_sign_hovered.png b/source/app/assets/image/scoreboard_sign_hovered.png new file mode 100644 index 0000000..f953426 Binary files /dev/null and b/source/app/assets/image/scoreboard_sign_hovered.png differ diff --git a/source/app/assets/image/sound_close.png b/source/app/assets/image/sound_close.png new file mode 100644 index 0000000..a66557f Binary files /dev/null and b/source/app/assets/image/sound_close.png differ diff --git a/source/app/assets/image/sound_close_hovered.png b/source/app/assets/image/sound_close_hovered.png new file mode 100644 index 0000000..a3fc843 Binary files /dev/null and b/source/app/assets/image/sound_close_hovered.png differ diff --git a/source/app/assets/image/sound_open.png b/source/app/assets/image/sound_open.png new file mode 100644 index 0000000..9382b57 Binary files /dev/null and b/source/app/assets/image/sound_open.png differ diff --git a/source/app/assets/image/sound_open_hovered.png b/source/app/assets/image/sound_open_hovered.png new file mode 100644 index 0000000..6ec235a Binary files /dev/null and b/source/app/assets/image/sound_open_hovered.png differ diff --git a/source/app/assets/image/start.png b/source/app/assets/image/start.png new file mode 100644 index 0000000..fffc36c Binary files /dev/null and b/source/app/assets/image/start.png differ diff --git a/source/app/assets/image/start_hovered.png b/source/app/assets/image/start_hovered.png new file mode 100644 index 0000000..3a027da Binary files /dev/null and b/source/app/assets/image/start_hovered.png differ diff --git a/source/app/assets/image/sunshine_typing_logo.png b/source/app/assets/image/sunshine_typing_logo.png new file mode 100644 index 0000000..772f680 Binary files /dev/null and b/source/app/assets/image/sunshine_typing_logo.png differ diff --git a/source/app/assets/image/window_icon.png b/source/app/assets/image/window_icon.png new file mode 100644 index 0000000..4fd2059 Binary files /dev/null and b/source/app/assets/image/window_icon.png differ diff --git a/source/app/classes/__init__.py b/source/app/classes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/classes/__pycache__/__init__.cpython-310.pyc b/source/app/classes/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..852a87c Binary files /dev/null and b/source/app/classes/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/classes/__pycache__/pages.cpython-310.pyc b/source/app/classes/__pycache__/pages.cpython-310.pyc new file mode 100644 index 0000000..48c13db Binary files /dev/null and b/source/app/classes/__pycache__/pages.cpython-310.pyc differ diff --git a/source/app/classes/__pycache__/typing_without_music.cpython-310.pyc b/source/app/classes/__pycache__/typing_without_music.cpython-310.pyc new file mode 100644 index 0000000..381c355 Binary files /dev/null and b/source/app/classes/__pycache__/typing_without_music.cpython-310.pyc differ diff --git a/source/app/classes/pages.py b/source/app/classes/pages.py new file mode 100644 index 0000000..1b549f4 --- /dev/null +++ b/source/app/classes/pages.py @@ -0,0 +1,430 @@ +from ..configurations import configure +from . import typing_without_music +from ..contexts import store +from ..core import game +from .. import program + +from pygame_gui.core.interfaces import IUIManagerInterface +from pygame_gui.core import ObjectID +from pygame.locals import * + +import pygame_gui +import sqlite3 +import pygame +import os + + +database = sqlite3.connect("sunshine_typing.db") +database_cursor = database.cursor() + + +class Play: + + + def __init__(self, rect: Rect, theme: str, music: bool, scrolling_background: bool = False): + self.rect = rect + self.width = rect.size[0] + self.height = rect.size[1] + + self.theme = theme + + self.music = music + + self.scrolling_background = scrolling_background + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/prepare_background.png').convert() + + self.mini_sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/mini_sunshine_typing_logo.png') + self.entry_name_background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/entry_name_background.png') + self.name_label = pygame.image.load(os.getcwd() + '/source/app/assets/image/name_label.png') + + self.ui_manager = pygame_gui.UIManager((self.width, self.height)) + self.ui_manager.get_theme().load_theme(theme) + + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(scroll) > self.background_image.get_width(): + scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + + def username_verify(self, payload: str): + if len(payload) > 1: + return True + + return False + + + def initialize(self): + clock = pygame.time.Clock() + is_running: bool = True + + mini_sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + (-50, 10), + (350, 150) + ), + image_surface=self.mini_sunshine_typing_logo, + manager=self.ui_manager, + ) + + entry_name_background = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) + 340, 375), + (265, 265) + ), + image_surface=self.entry_name_background_image, + manager=self.ui_manager, + ) + + name_label = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) + 340, 365), + (200, 100) + ), + image_surface=self.name_label, + manager=self.ui_manager, + ) + + username_condition_label = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 270, 650), + (650, 60) + ), + text='Name must be more than two characters', + manager=self.ui_manager, + object_id=ObjectID(object_id='@username_condition_label') + ) + + name_entry = pygame_gui.elements.UITextEntryLine( + relative_rect=pygame.Rect( + ((self.width / 2) + 373, 475), + (200, 75) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id='@entry_name') + ) + + start_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + ((self.width / 2) + 320, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@start_button') + ) + + scoreboard_sign_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 170, -10), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@scoreboard_sign_button') + ) + + back_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (10, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@back_button') + ) + + start_game = game.Game( + rect=self.rect, + theme=self.theme, + music=self.music + ) + + scoreboard_page = Scoreboard( + rect=self.rect, + theme=self.theme, + music=True, + ) + + typing_with_music_menu = program.Typing( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + typing_without_music_menu = typing_without_music.TypingWithoutMusic( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + start_game_state: bool = False + username_state: bool = False + back_previous_page_state: bool = False + + set_username: str = "" + + while is_running: + time_delta = clock.tick(60) / 1000.0 + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_TEXT_ENTRY_CHANGED): + username_state = self.username_verify(event.text) + + if username_state: + set_username = event.text + + if event.type.__eq__(pygame_gui.UI_BUTTON_PRESSED): + if event.ui_element == start_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + if username_state: + store.username = set_username + start_game_state = True + + if event.ui_element == back_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + back_previous_page_state = True + + if event.ui_element == scoreboard_sign_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + scoreboard_page.initialize() + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + + self.scroll_background(enabled=self.scrolling_background) + + if start_game_state: + start_game.start() + + if back_previous_page_state and self.music: + typing_with_music_menu.run() + + if back_previous_page_state and not self.music: + typing_without_music_menu.run() + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() + + +class Scoreboard: + + + def __init__(self, rect: Rect, theme: str, music: bool, scrolling_background: bool = False): + self.rect = rect + self.width = rect.size[0] + self.height = rect.size[1] + + self.theme = theme + + self.music = music + + self.scrolling_background = scrolling_background + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/prepare_background.png').convert() + + self.mini_sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/mini_sunshine_typing_logo.png') + + self.ui_manager = pygame_gui.UIManager((self.width, self.height)) + self.ui_manager.get_theme().load_theme(theme) + + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(scroll) > self.background_image.get_width(): + scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + + def initialize(self) -> None: + clock = pygame.time.Clock() + is_running: bool = True + + fetch_desc_rows = database_cursor.execute( + "SELECT username, wpm FROM typing ORDER BY wpm DESC LIMIT 5;" + ).fetchall() + database.commit() + + mini_sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + (-50, 10), + (350, 150) + ), + image_surface=self.mini_sunshine_typing_logo, + manager=self.ui_manager, + ) + + top_rankings_title = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 30), + (650, 100) + ), + text='Top 5 from lastest game', + manager=self.ui_manager, + object_id=ObjectID(object_id='@top_rankings_title') + ) + + first_ranking = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 150), + (650, 100) + ), + text="{} {}".format( + fetch_desc_rows[0][0], + int(fetch_desc_rows[0][1]) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@top_ranking") + ) + + second_ranking = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 200), + (650, 100) + ), + text="{} {}".format( + fetch_desc_rows[1][0], + int(fetch_desc_rows[1][1]) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@top_ranking") + ) + + third_ranking = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 250), + (650, 100) + ), + text="{} {}".format( + fetch_desc_rows[2][0], + int(fetch_desc_rows[2][1]) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@top_ranking") + ) + + fourth_ranking = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 300), + (650, 100) + ), + text="{} {}".format( + fetch_desc_rows[3][0], + int(fetch_desc_rows[3][1]) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@top_ranking") + ) + + five_ranking = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect( + ((self.width / 2) - 320, 350), + (650, 100) + ), + text="{} {}".format( + fetch_desc_rows[4][0], + int(fetch_desc_rows[4][1]) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@top_ranking") + ) + + back_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (10, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@back_button') + ) + + typing_with_music_menu = program.Typing( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + typing_without_music_menu = typing_without_music.TypingWithoutMusic( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + back_previous_page_state = False + + while is_running: + time_delta = clock.tick(60) / 1000.0 + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_BUTTON_PRESSED): + if event.ui_element == back_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + back_previous_page_state = True + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + + self.scroll_background(enabled=self.scrolling_background) + + if back_previous_page_state and self.music: + typing_with_music_menu.run() + + if back_previous_page_state and not self.music: + typing_without_music_menu.run() + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() + \ No newline at end of file diff --git a/source/app/classes/typing_without_music.py b/source/app/classes/typing_without_music.py new file mode 100644 index 0000000..8cd8422 --- /dev/null +++ b/source/app/classes/typing_without_music.py @@ -0,0 +1,184 @@ +from .. import engine, program +from . import pages + +from pygame.locals import * +from pygame_gui.core import ObjectID + +import webbrowser +import pygame_gui +import pygame +import random +import math +import time +import sys +import os + +class TypingWithoutMusic(engine.Engine): + + + def __init__(self, display_title: str, rect: Rect, theme: str = None, scrolling_background: bool = False): + pygame.display.set_caption(display_title) + + self.display_title = display_title + + self.theme = theme + + self.scrolling_background: bool = scrolling_background + + self.rect = rect + self.width: int = rect.size[0] + self.height: int = rect.size[1] + + self.music_mixer_channel = pygame.mixer.Channel(1) + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/background.jpg').convert() + + self.sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/sunshine_typing_logo.png').convert() + + self.scroll: int = 0 + self.tiles: int = math.ceil(self.width / self.background_image.get_width()) + 1 + + self.ui_manager = pygame_gui.UIManager(rect.size, theme) + + self.ui_menu = pages + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(scroll) > self.background_image.get_width(): + scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + + def run(self) -> None: + clock = pygame.time.Clock() + is_running: bool = True + + sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) - 240, 80), + (500, 300) + ), + image_surface=self.sunshine_typing_logo, + manager=self.ui_manager + ) + + play_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (10, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@play_button') + ) + + exit_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + ((self.width / 2) + 320, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@exit_button') + ) + + scoreboard_sign_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 170, -10), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@scoreboard_sign_button') + ) + + music_opened_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 330, -15), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@music_closed_button'), + ) + + enabled_audio = program.Typing( + self.display_title, + self.rect, + self.theme, + ) + + scoreboard_page = pages.Scoreboard( + rect=self.rect, + theme=self.theme, + music=False, + ) + + play_game_state: bool = False + enabled_audio_state: bool = False + + while is_running: + time_delta = clock.tick(60) / 1000.0 + + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_BUTTON_PRESSED): + if event.ui_element == play_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + play_game_state = True + + if event.ui_element == music_opened_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + enabled_audio_state = True + + if event.ui_element == scoreboard_sign_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + scoreboard_page.initialize() + + if event.ui_element == exit_button: + pygame.quit() + exit() + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + self.scroll_background(enabled=self.scrolling_background) + + if play_game_state and not enabled_audio_state: + self.ui_menu.Play( + rect=self.rect, + theme=self.theme, + music=False, + scrolling_background=self.scrolling_background + ).initialize() + + if enabled_audio_state: + self.music_mixer_channel.unpause() + enabled_audio.run() + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() diff --git a/source/app/configurations/__init__.py b/source/app/configurations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/configurations/__pycache__/__init__.cpython-310.pyc b/source/app/configurations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..be7fd85 Binary files /dev/null and b/source/app/configurations/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/configurations/__pycache__/configure.cpython-310.pyc b/source/app/configurations/__pycache__/configure.cpython-310.pyc new file mode 100644 index 0000000..83b0369 Binary files /dev/null and b/source/app/configurations/__pycache__/configure.cpython-310.pyc differ diff --git a/source/app/configurations/configure.py b/source/app/configurations/configure.py new file mode 100644 index 0000000..f67ab53 --- /dev/null +++ b/source/app/configurations/configure.py @@ -0,0 +1,7 @@ +from pygame.locals import * + +WINDOW_NAME: str = None +WINDOW_RECT: Rect = None +GAME_THEME_PATH: str = None +SENTENCES_PATH: str = None +ENABLED_SCROLLING_BACKGROUND: bool = None \ No newline at end of file diff --git a/source/app/contexts/__init__.py b/source/app/contexts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/contexts/__pycache__/__init__.cpython-310.pyc b/source/app/contexts/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..63f44f3 Binary files /dev/null and b/source/app/contexts/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/contexts/__pycache__/store.cpython-310.pyc b/source/app/contexts/__pycache__/store.cpython-310.pyc new file mode 100644 index 0000000..e4a4375 Binary files /dev/null and b/source/app/contexts/__pycache__/store.cpython-310.pyc differ diff --git a/source/app/contexts/store.py b/source/app/contexts/store.py new file mode 100644 index 0000000..934f1f3 --- /dev/null +++ b/source/app/contexts/store.py @@ -0,0 +1,3 @@ +# Global State + +username: str = "" \ No newline at end of file diff --git a/source/app/core/__init__.py b/source/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/core/__pycache__/__init__.cpython-310.pyc b/source/app/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..378fde7 Binary files /dev/null and b/source/app/core/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/core/__pycache__/finished.cpython-310.pyc b/source/app/core/__pycache__/finished.cpython-310.pyc new file mode 100644 index 0000000..2d0a74d Binary files /dev/null and b/source/app/core/__pycache__/finished.cpython-310.pyc differ diff --git a/source/app/core/__pycache__/game.cpython-310.pyc b/source/app/core/__pycache__/game.cpython-310.pyc new file mode 100644 index 0000000..e336d12 Binary files /dev/null and b/source/app/core/__pycache__/game.cpython-310.pyc differ diff --git a/source/app/core/game.py b/source/app/core/game.py new file mode 100644 index 0000000..a6e24b8 --- /dev/null +++ b/source/app/core/game.py @@ -0,0 +1,443 @@ +from ..classes import typing_without_music, pages +from ..configurations import configure +from ..contexts import store +from .. import program +from .. import engine + +from pygame_gui.core import ObjectID +from pygame.locals import * + +import pygame_gui +import sqlite3 +import pygame +import random +import time +import sys +import os + + +database = sqlite3.connect("sunshine_typing.db") +database_cursor = database.cursor() + +class Game(engine.Engine): + + + def __init__(self, rect: Rect, theme: str, music: bool): + self.rect = rect + self.width = rect.size[0] + self.height = rect.size[1] + + self.theme = theme + + self.music = music + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/core_background.png').convert() + + self.mini_sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/mini_sunshine_typing_logo.png') + + self.ui_manager = pygame_gui.UIManager((self.width, self.height)) + self.ui_manager.get_theme().load_theme(theme) + + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(scroll) > self.background_image.get_width(): + scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + + def random_sentence(self) -> str: + file = open(os.getcwd() + "/sentences.txt").read() + sentences = file.split('\n') + receive_sentence = random.choice(sentences) + + return sentences + + def start(self) -> None: + clock = pygame.time.Clock() + is_running: bool = True + + random_challenge: str = self.random_sentence() + sentence_challenge = random_challenge[random.randint(0, 2)] + + counter: int = 0 + sentence: str = "" + + mini_sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + (-50, 10), + (350, 150) + ), + image_surface=self.mini_sunshine_typing_logo, + manager=self.ui_manager, + ) + + sentence_label = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 630, 260), + (self.width, 120) + ), + text=sentence_challenge, + manager=self.ui_manager, + object_id=ObjectID(object_id="@sentence_label") + ) + + sentence_text_entry = pygame_gui.elements.UITextEntryLine( + relative_rect=Rect( + ((self.width / 2) - 490, 350), + (1000, 80) + ), + manager=self.ui_manager, + object_id=ObjectID(object_id="@sentence_text_entry") + ) + + start_timer = time.time() + + while is_running: + time_delta = clock.tick(60) / 1000.0 + + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_TEXT_ENTRY_CHANGED): + + if len(event.text) == len(sentence_challenge): + sentence = event.text + Finished( + rect=self.rect, + theme=self.theme, + music=self.music, + text_entry=event.text, + sentence=sentence_challenge, + timer=start_timer + ).start() + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + self.scroll_background(enabled=configure.ENABLED_SCROLLING_BACKGROUND) + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() + + +class Finished(engine.Engine): + + + def __init__(self, rect: Rect, theme: str, music: bool, text_entry: str, sentence: str, timer: float): + self.rect = rect + self.width = rect.size[0] + self.height = rect.size[1] + + self.theme = theme + + self.music = music + + self.text_entry = text_entry + self.sentence = sentence + self.timer = timer + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/core_background.png').convert() + + self.mini_sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/mini_sunshine_typing_logo.png') + self.result_background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/entry_name_background.png') + + self.ui_manager = pygame_gui.UIManager((self.width, self.height)) + self.ui_manager.get_theme().load_theme(theme) + + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(scroll) > self.background_image.get_width(): + scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + def start(self) -> None: + clock = pygame.time.Clock() + is_running: bool = True + + total_time = time.time() - self.timer + counter: int = 0 + for index, alpha in enumerate(self.sentence): + try: + if self.text_entry[index] == alpha: + counter += 1 + except: + pass + + accuracy = (counter * 100) / len(self.sentence) + words_per_minute = (len(self.text_entry) * 60) / (5 * total_time) + + fetch_rows = database_cursor.execute( + "SELECT username FROM typing WHERE username = ?", + (store.username.lower(),) + ).fetchall() + + if len(fetch_rows).__eq__(0): + database_cursor.execute( + "INSERT INTO typing (username, wpm) VALUES (?, ?)", + (store.username.lower(), words_per_minute) + ) + database.commit() + + elif len(fetch_rows).__eq__(1): + database_cursor.execute( + "UPDATE typing SET wpm = ? WHERE username = ?", + (words_per_minute, store.username.lower()) + ) + database.commit() + + fetch_desc_rows = database_cursor.execute( + "SELECT username, wpm FROM typing ORDER BY wpm DESC LIMIT 5;" + ).fetchall() + database.commit() + + mini_sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + (-50, 10), + (350, 150) + ), + image_surface=self.mini_sunshine_typing_logo, + manager=self.ui_manager, + ) + + scoreboard_sign_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 170, -10), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@scoreboard_sign_button') + ) + + finished_label = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 640, 100), + (self.width, 300) + ), + text="You have challenged the sentences", + manager=self.ui_manager, + object_id=ObjectID(object_id="@finished_label") + ) + + words_per_minute_background = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) + 230, 220), + (265, 265) + ), + image_surface=self.result_background_image, + manager=self.ui_manager, + ) + + accuracy_background = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) - 130, 220), + (265, 265) + ), + image_surface=self.result_background_image, + manager=self.ui_manager, + ) + + total_time_background = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) - 490, 220), + (265, 265) + ), + image_surface=self.result_background_image, + manager=self.ui_manager, + ) + + words_per_minute_label = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) + 230, 185), + (265, 265) + ), + text="WPM", + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_title") + ) + + accuracy_label = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 130, 185), + (265, 265) + ), + text="Accuracy", + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_title") + ) + + total_time_label = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 490, 185), + (265, 265) + ), + text="Total time", + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_title") + ) + + result_of_words_per_minute = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) + 230, 225), + (265, 265) + ), + text="%d" % (words_per_minute), + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_content") + ) + + result_of_accuracy = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 130, 225), + (265, 265) + ), + text="%d" % (accuracy), + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_content") + ) + + if total_time > 9: + result_of_total_time = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 678, 225), + (640, 265) + ), + text="{:.0f} Secs".format(total_time), + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_content") + ) + + else: + result_of_total_time = pygame_gui.elements.UILabel( + relative_rect=Rect( + ((self.width / 2) - 675, 225), + (640, 265) + ), + text="{:.0f} Secs".format(total_time), + manager=self.ui_manager, + object_id=ObjectID(object_id="@result_content") + ) + + restart_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (10, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@restart_button') + ) + + menu_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + ((self.width / 2) + 320, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@menu_button') + ) + + game_starter = Game( + rect=self.rect, + theme=self.theme, + music=self.music + ) + + typing_with_music_menu = program.Typing( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + typing_without_music_menu = typing_without_music.TypingWithoutMusic( + display_title=configure.WINDOW_NAME, + rect=configure.WINDOW_RECT, + theme=configure.GAME_THEME_PATH, + scrolling_background=configure.ENABLED_SCROLLING_BACKGROUND + ) + + scoreboard_page = pages.Scoreboard( + rect=self.rect, + theme=self.theme, + music=True, + ) + + restart_state: bool = False + back_previous_page_state: bool = False + + while is_running: + time_delta = clock.tick(60) / 1000.0 + + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_BUTTON_PRESSED): + if event.ui_element == restart_button: + restart_state = True + + if event.ui_element == menu_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + back_previous_page_state = True + + if event.ui_element == scoreboard_sign_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + scoreboard_page.initialize() + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + self.scroll_background(enabled=configure.ENABLED_SCROLLING_BACKGROUND) + + if restart_state: + game_starter.start() + + if back_previous_page_state and self.music: + typing_with_music_menu.run() + + if back_previous_page_state and not self.music: + typing_without_music_menu.run() + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() \ No newline at end of file diff --git a/source/app/data/__init__.py b/source/app/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/data/themes/__init__.py b/source/app/data/themes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/data/themes/default_theme.json b/source/app/data/themes/default_theme.json new file mode 100644 index 0000000..8edbf81 --- /dev/null +++ b/source/app/data/themes/default_theme.json @@ -0,0 +1,452 @@ +{ + "@play_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/play.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/play_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@exit_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/exit.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/exit_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@scoreboard_sign_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/scoreboard_sign.png", + "sub_surface_rect": "0,0,150,150" + }, + "hovered_image": { + "path": "./source/app/assets/image/scoreboard_sign_hovered.png", + "sub_surface_rect": "0,0,150,150" + } + } + }, + "@music_opened_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/sound_open.png", + "sub_surface_rect": "0,0,128,128" + }, + "hovered_image": { + "path": "./source/app/assets/image/sound_open_hovered.png", + "sub_surface_rect": "0,0,128,128" + } + } + }, + "@music_closed_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/sound_close.png", + "sub_surface_rect": "0,0,128,128" + }, + "hovered_image": { + "path": "./source/app/assets/image/sound_close_hovered.png", + "sub_surface_rect": "0,0,128,128" + } + } + }, + "@back_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/back.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/back_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@start_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/start.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/start_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@restart_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/restart.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/restart_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@menu_button": { + "misc": { + "border_width": "0", + "shadow_width": "0" + }, + "colours": { + "normal_bg": "#00000000", + "hovered_bg": "#00000000", + "selected_bg": "#00000000", + "active_bg": "#00000000", + "normal_border": "#00000000", + "hovered_border": "#00000000", + "disabled_border": "#00000000", + "selected_border": "#00000000", + "active_border": "#00000000" + }, + "images": { + "normal_image": { + "path": "./source/app/assets/image/menu.png", + "sub_surface_rect": "0,0,300,120" + }, + "hovered_image": { + "path": "./source/app/assets/image/menu_hovered.png", + "sub_surface_rect": "0,0,300,120" + } + } + }, + "@entry_name": { + "colours": { + "dark_bg": "#00000000", + "selected_bg": "#00000000", + "normal_text": "#ECBFA6", + "selected_text": "#ffffff", + "normal_border": "#00000000" + }, + "font": { + "name": "8bit_arcade", + "size": "64", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "border_width": "0", + "shadow_width": "0" + } + }, + "@username_condition_label": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffaa00", + "text_shadow": "#5a330b" + }, + "font": { + "name": "8bit_arcade", + "size": "32", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@top_rankings_title": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffaa00", + "text_shadow": "#5a330b" + }, + "font": { + "name": "8bit_arcade", + "size": "48", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@sentence_label": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffffff", + "text_shadow": "#000000" + }, + "font": { + "name": "inter", + "size": "26", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "inter-regular.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@finished_label": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffaa00", + "text_shadow": "#000000" + }, + "font": { + "name": "8bit_arcade", + "size": "64", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@result_content": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffffff", + "text_shadow": "#000000" + }, + "font": { + "name": "8bit_arcade", + "size": "48", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@result_title": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffaa00", + "text_shadow": "#5a330b" + }, + "font": { + "name": "8bit_arcade", + "size": "36", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@top_ranking": { + "colours": { + "dark_bg": "#00000000", + "normal_text": "#ffffff", + "text_shadow": "#000000" + }, + "font": { + "name": "8bit_arcade", + "size": "40", + "bold": "0", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "8bit_arcade.ttf" + } + }, + "misc": { + "text_shadow": "1", + "text_shadow_size": "3", + "text_shadow_offset": "0,0" + } + }, + "@sentence_text_entry": { + "colours": { + "dark_bg": "#55340f", + "selected_bg": "#55595e", + "normal_text": "#ffaa00", + "selected_text": "#FFFFFF", + "normal_border": "#000000" + }, + "font": { + "name": "inter", + "size": "26", + "bold": "1", + "italic": "0", + "regular_resource": { + "package": "app.fonts", + "resource": "inter-regular.ttf" + }, + "bold_resource": { + "package": "app.fonts", + "resource": "inter-bold.ttf" + } + }, + "misc": { + "shape": "rounded_rectangle", + "shape_corner_radius": 5, + "border_width": "2", + "shadow_width": "2", + "padding": "6,4" + } + } +} \ No newline at end of file diff --git a/source/app/engine.py b/source/app/engine.py new file mode 100644 index 0000000..a4fdafb --- /dev/null +++ b/source/app/engine.py @@ -0,0 +1,9 @@ +import pygame +import os + +class Engine: + pygame.init() + pygame.mixer.init() + + giga_chad = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/giga_chad.ogg') + pygame.mixer.Channel(1).play(giga_chad, loops=-1, fade_ms=10) \ No newline at end of file diff --git a/source/app/fonts/8bit_arcade.ttf b/source/app/fonts/8bit_arcade.ttf new file mode 100644 index 0000000..c080713 Binary files /dev/null and b/source/app/fonts/8bit_arcade.ttf differ diff --git a/source/app/fonts/__init__.py b/source/app/fonts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/app/fonts/__pycache__/__init__.cpython-310.pyc b/source/app/fonts/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..827c48a Binary files /dev/null and b/source/app/fonts/__pycache__/__init__.cpython-310.pyc differ diff --git a/source/app/fonts/inter-bold.ttf b/source/app/fonts/inter-bold.ttf new file mode 100644 index 0000000..8e82c70 Binary files /dev/null and b/source/app/fonts/inter-bold.ttf differ diff --git a/source/app/fonts/inter-regular.ttf b/source/app/fonts/inter-regular.ttf new file mode 100644 index 0000000..8d4eebf Binary files /dev/null and b/source/app/fonts/inter-regular.ttf differ diff --git a/source/app/program.py b/source/app/program.py new file mode 100644 index 0000000..910864b --- /dev/null +++ b/source/app/program.py @@ -0,0 +1,204 @@ +from . import engine +from .classes import pages, typing_without_music + +from pygame.locals import * +from pygame_gui.core import ObjectID + +import webbrowser +import pygame_gui +import pygame +import random +import math +import time +import sys +import os + +class Typing(engine.Engine): + """ + About typing test game using pygame to create game which is a project at King Mongkut's University of Technology North Bangkok. + By students of information of network engineering, there are 6 project groups, you can see it in our config file. + + Instructions: + You must include a variable which is a Class constructor, + which contains display_title, width, and height, to be used as part of the program. + + Example as a Code: + from pygame.locals import * + + rect: Rect = Rect(0, 0, 1280, 720) + + typing_game: Typing = Typing('untitled', rect) + typing_game.run() + """ + + + def __init__(self, display_title: str, rect: Rect, theme: str = None, scrolling_background: bool = False): + pygame.display.set_caption(display_title) + + icon = pygame.image.load(os.getcwd() + '/source/app/assets/image/window_icon.png') + pygame.display.set_icon(icon) + + self.display_title = display_title + + self.theme = theme + + self.scrolling_background = scrolling_background + + self.rect = rect + self.width: int = rect.size[0] + self.height: int = rect.size[1] + + self.music_mixer_channel = pygame.mixer.Channel(1) + + self.window_surface = pygame.display.set_mode(rect.size) + self.background = pygame.Surface(rect.size) + self.background_image = pygame.image.load(os.getcwd() + '/source/app/assets/image/background.jpg').convert() + + self.sunshine_typing_logo = pygame.image.load(os.getcwd() + '/source/app/assets/image/sunshine_typing_logo.png') + + self.scroll: int = 0 + self.tiles: int = math.ceil(self.width / self.background_image.get_width()) + 1 + + self.ui_manager = pygame_gui.UIManager(rect.size, theme) + + self.ui_menu = pages + + + def scroll_background(self, enabled: bool = False): + if enabled: + counter: int = 0 + while counter < self.tiles: + self.window_surface.blit( + self.background_image, + (self.background_image.get_width() * counter + self.scroll, 0) + ) + counter += 1 + + self.scroll -= 6 + + if abs(self.scroll) > self.background_image.get_width(): + self.scroll = 0 + + else: + self.window_surface.blit(self.background_image, (0, 0)) + + + def run(self) -> None: + clock = pygame.time.Clock() + is_running: bool = True + + sunshine_typing_logo = pygame_gui.elements.UIImage( + relative_rect=pygame.Rect( + ((self.width / 2) - 240, 80), + (500, 300) + ), + image_surface=self.sunshine_typing_logo, + manager=self.ui_manager, + ) + + play_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (10, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@play_button') + ) + + exit_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + ((self.width / 2) + 320, 580), + (300, 120) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@exit_button') + ) + + scoreboard_sign_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 170, -10), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@scoreboard_sign_button') + ) + + music_closed_button = pygame_gui.elements.UIButton( + relative_rect=pygame.Rect( + (self.width - 330, -15), + (150, 150) + ), + text='', + manager=self.ui_manager, + object_id=ObjectID(object_id='@music_opened_button'), + ) + + disabled_audio = typing_without_music.TypingWithoutMusic( + self.display_title, + self.rect, + self.theme + ) + + scoreboard_page = pages.Scoreboard( + rect=self.rect, + theme=self.theme, + music=True, + ) + + play_game_state: bool = False + disabled_audio_state: bool = False + + while is_running: + time_delta = clock.tick(60) / 1000.0 + + for event in pygame.event.get(): + if event.type.__eq__(pygame.QUIT): + pygame.quit() + exit() + + if event.type.__eq__(pygame_gui.UI_BUTTON_PRESSED): + if event.ui_element == play_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + play_game_state = True + + if event.ui_element == music_closed_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + disabled_audio_state = True + + if event.ui_element == scoreboard_sign_button: + click_effect = pygame.mixer.Sound(os.getcwd() + '/source/app/assets/audio/effect/click.ogg') + click_effect.play() + + scoreboard_page.initialize() + + if event.ui_element == exit_button: + pygame.quit() + exit() + + self.ui_manager.process_events(event) + + self.ui_manager.update(time_delta) + self.scroll_background(enabled=self.scrolling_background) + + if play_game_state and not disabled_audio_state: + self.ui_menu.Play( + rect=self.rect, + theme=self.theme, + music=True, + scrolling_background=self.scrolling_background + ).initialize() + + if disabled_audio_state: + self.music_mixer_channel.pause() + disabled_audio.run() + + self.ui_manager.draw_ui(self.window_surface) + + pygame.display.update() diff --git a/sunshine_typing.db b/sunshine_typing.db new file mode 100644 index 0000000..baace2e Binary files /dev/null and b/sunshine_typing.db differ