diff --git a/android/app/build.gradle b/android/app/build.gradle index 789da53f79d4f06086315ff99a2bb67df1e0c778..26a3890d2622f80fd5ad86f3c5b9d1eab8a03ce5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 33 + compileSdkVersion 34 namespace "org.benoitharrault.puzzlegame" defaultConfig { diff --git a/android/gradle.properties b/android/gradle.properties index bc12805202e72c604bb3162b9bf117c69fcc8db2..9b96205e6609eb50043f1f335634fdf02ccc3108 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.61 -app.versionCode=61 +app.versionName=0.1.0 +app.versionCode=62 diff --git a/assets/files/images.json b/assets/files/images.json deleted file mode 100644 index c0c454503034f01b0792b3875f552a85c3d8335b..0000000000000000000000000000000000000000 --- a/assets/files/images.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "images": [ - "00_default_08b2107e3c30da02a6c18613a1e90857", - "00_default_3bcda1b5fb7adcdac1824dde11060462", - "00_default_3c245519d89fefd792d05b72631ef8af", - "00_default_494b2e821e8e7130088ec2929bf49be8", - "animals_0824b924323459fb8e8a90054443c77b", - "animals_0a86f77291858c10ca6dcc631ba14f96", - "animals_36c401aebe4293803710b05a08d6a248", - "animals_5cb4af89a40d60e8839a43e915d15a2f", - "animals_60f53fdf0217001c7a89f48f243f2aa9", - "animals_677d788f482bffdd7f79c525aae93832", - "animals_76baeb891edc3f20814fcda6de541c8b", - "animals_815079fbba4a96c699b25fca11cf303e", - "animals_83f2c647801c57722f5582687a95071b", - "animals_8786f3eced6b176537608b50398b464d", - "animals_88c3071723a3d93b9d581313e2b25c65", - "animals_8cb1144c6afa8ccd37871a1c237e7c3a", - "animals_9b0b293a1f2393d46e2d8f7ca841e652", - "animals_9bd7055c69b6471477bc67e508a0ec7a", - "animals_a42fb9cddd68b019a1e6216f01d94742", - "animals_d3f924d97c3d6bb735a27c0e4edfcd7c", - "animals_e937f221caaa87337c1dc98002e13c56", - "animals_f05f46e9ea9f18d47fbd7733a608536b", - "animals_fc0a31d2c32467fdda711ffd521e79d9", - "anime_aa293fb70ed59c7b610f0e6e218a0917", - "anime_f48648cbbff8370f18bdc592d98bb563", - "dinosaurs_61c5d5727722d957ffa39b7cc5519bf6", - "dinosaurs_d2fafe9651b48459a72aa15cfeac3b61", - "ghibli_2c6fd10faee15c612ebfe71628069d36", - "ghibli_914deb9acd1f1ab2a50ec9f7924eb799", - "ghibli_b2c74d93a2c32f2ccbda73c4bee03b00", - "ghibli_f2798b83ce4d01142bdcd5d8803f5412", - "harrypotter_439fe7c78ebbf945de2eb18f2f78f7a8", - "harrypotter_d3664e7e2920eb8cee5b69982f710644", - "nature_38a414b6051a9b528fedc47878663c03", - "nature_98574287a17f734d52dc64dfc0f45fc8", - "nature_c0e131e9a2a813c337965ddfb413baf8", - "personal_0694cfbc9bb292f9da8c7c65d793a378", - "personal_0a0b014b37ddb90d9af1fcdedccef37d", - "personal_0d973affb8ffe7d0abe9ddd89957f580", - "personal_0ea16afc573d649222e512306872106f", - "personal_12381e1ed7bed4ec56c5d871a8b21a5d", - "personal_183bcd7c31fdece731a373ed375fd227", - "personal_1b98afe55b410ed03ea8b3f18fa5cc27", - "personal_22169df124ce293fbd519eb6d987a9b7", - "personal_259dd16880502cbc8fbbff87cee60a24", - "personal_29244801ee5399bc5a445f9c9f1e5487", - "personal_2f9d874fbd41462a6c115022e088f12e", - "personal_31320d5c42d2b0e81a3aa365af7bbd9d", - "personal_3b2274b4265177568057601aef1a4e87", - "personal_3cdb7de70fb4052ac849a6a5fa990eee", - "personal_3debd2d0b3b968d5a895d776bc8d7e03", - "personal_43f57a142340066e3137434a43b6cbbb", - "personal_4936d509c25ceaf585c94067f4f9061b", - "personal_4afcce1786d0265f45ca083b07bd407b", - "personal_4ee0e7589f440418d41e7e8283c6bcef", - "personal_4fe95f154947c4efdf9a35045b47e137", - "personal_548f73139e968e67d9737393e9cba794", - "personal_58e4fa6bdbfea985511936074d40a5e1", - "personal_5a2ec8b6ee98b400ae87207fcace71e3", - "personal_5b6e5f629039ff65178bac0b9d979185", - "personal_626e4843a78dbcc43e49bce849bc82cc", - "personal_642d854a9d4252d78faeb392b09ff04d", - "personal_6761903f2186e75e74fcbde92a4c6865", - "personal_67def669a778cd84299235188df223f9", - "personal_6baedd08c35004321ac26effade8221e", - "personal_6f28c333f1100e7d2cb316ba839a59b9", - "personal_70d72351d07f93710539592d8dd38d8a", - "personal_7d7e4fb5727fc23ab3fa7b40f8769fc1", - "personal_7ea40aebbbde03cbbcc19e5f92609b77", - "personal_84f358d1b64d7947617d85b21ab2375a", - "personal_90deb4cb0578338c442f268d6a24d5dc", - "personal_96b82ec73f9d83066297fa30a5b4384f", - "personal_9e44f127b4ec68efad8ce87da1d3fc51", - "personal_a6819fc32906ac2ca4a0f47503772307", - "personal_b27ef14b2c835df1ae1aeae938823193", - "personal_b50323b0dc6ce5fb9796adb80b814cd8", - "personal_b78c59c08302100769e3469fccff36c1", - "personal_b87fcb24c5d44e1328cddd66b75bc119", - "personal_b89b9d4366696fe1623b5b5d4f10f0fc", - "personal_b8d2facf43700d6f4e6c230c548e771b", - "personal_b961b8d3386fa525ac754eea04b32003", - "personal_bf4520a9582da41ecc280365afebb30f", - "personal_c0eb4eb4d39d58eecebc3218a56f6148", - "personal_c132137e54f68136af709edd6a8f602d", - "personal_c72571b7f7ac673b63754699e1b8c31c", - "personal_d0d1e8e150782b694d663b6cc838cf61", - "personal_d5d163c09ae4da219a7ae54b5a98b830", - "personal_d5f3483ee2a8598123a0d0a9a965aea0", - "personal_daf7dab4c768da753dede24093cef6c3", - "personal_e124afc5e5b3770e53f058431d37bf31", - "personal_e9da5393d39bebb0a5d393b4c55366a5", - "personal_efc2a3bf7597c3991afa800c4515a515", - "personal_f3991aee0215111c5e4c13994477e85b", - "personal_f4059dfc787363cc3437be52a47779a0", - "personal_fb47095d9556e3e7f00711afe128d5b2", - "sea_f2c615363f892ba270dae2fceb3a06e9", - "sea_f64def749dd7133248814a902ea140ab", - "space_438d8689bcc7e7cafd89a97d336d7981", - "" - ] -} diff --git a/assets/fonts/Nunito-Bold.ttf b/assets/fonts/Nunito-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6519feb781449ebe0015cbc74dfd9e13110fbba9 Binary files /dev/null and b/assets/fonts/Nunito-Bold.ttf differ diff --git a/assets/fonts/Nunito-Light.ttf b/assets/fonts/Nunito-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8a0736c41cd6c2a1225d356bf274de1d0afc3497 Binary files /dev/null and b/assets/fonts/Nunito-Light.ttf differ diff --git a/assets/fonts/Nunito-Medium.ttf b/assets/fonts/Nunito-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..88fccdc0638b6f5d6ac49d9d269dc3d518618ad1 Binary files /dev/null and b/assets/fonts/Nunito-Medium.ttf differ diff --git a/assets/fonts/Nunito-Regular.ttf b/assets/fonts/Nunito-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e7b8375a896ef0cd8e06730a78c84532b377e784 Binary files /dev/null and b/assets/fonts/Nunito-Regular.ttf differ diff --git a/assets/icons/difficulty_3x3.png b/assets/icons/difficulty_3x3.png deleted file mode 100644 index 9893a1c3d32dc5e9818d0b4da793268833e740ee..0000000000000000000000000000000000000000 Binary files a/assets/icons/difficulty_3x3.png and /dev/null differ diff --git a/assets/icons/difficulty_4x4.png b/assets/icons/difficulty_4x4.png deleted file mode 100644 index 57c99a774f4910ade3d46af33d7ce204b36562ac..0000000000000000000000000000000000000000 Binary files a/assets/icons/difficulty_4x4.png and /dev/null differ diff --git a/assets/icons/difficulty_5x5.png b/assets/icons/difficulty_5x5.png deleted file mode 100644 index 11e087ec80f029539736637ddacbcd4ccb24ee72..0000000000000000000000000000000000000000 Binary files a/assets/icons/difficulty_5x5.png and /dev/null differ diff --git a/assets/images/placeholder.png b/assets/images/placeholder.png deleted file mode 100644 index 725353a203906d2188d8c31cd4ad86bfd280c252..0000000000000000000000000000000000000000 Binary files a/assets/images/placeholder.png and /dev/null differ diff --git a/assets/translations/en.json b/assets/translations/en.json new file mode 100644 index 0000000000000000000000000000000000000000..7c987554a0784ef90fd559894b614c4f8855a791 --- /dev/null +++ b/assets/translations/en.json @@ -0,0 +1,12 @@ +{ + "app_name": "Jigsaw puzzle", + + "settings_title": "Settings", + "settings_label_theme": "Theme mode", + + "about_title": "Informations", + "about_content": "Jigsaw puzzle", + "about_version": "Version: {version}", + + "": "" +} diff --git a/assets/translations/fr.json b/assets/translations/fr.json new file mode 100644 index 0000000000000000000000000000000000000000..70581d6a87ccf7a95f62d1abfefe721f0f7b52ca --- /dev/null +++ b/assets/translations/fr.json @@ -0,0 +1,12 @@ +{ + "app_name": "Puzzle", + + "settings_title": "Réglages", + "settings_label_theme": "Thème de couleurs", + + "about_title": "Informations", + "about_content": "Puzzle.", + "about_version": "Version : {version}", + + "": "" +} diff --git a/assets/icons/button_back.png b/assets/ui/button_back.png similarity index 100% rename from assets/icons/button_back.png rename to assets/ui/button_back.png diff --git a/assets/ui/button_delete_saved_game.png b/assets/ui/button_delete_saved_game.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4f217689b11e444b7163557d7e5d68f3bbfe7d Binary files /dev/null and b/assets/ui/button_delete_saved_game.png differ diff --git a/assets/icons/button_random_pick.png b/assets/ui/button_random_pick.png similarity index 100% rename from assets/icons/button_random_pick.png rename to assets/ui/button_random_pick.png diff --git a/assets/ui/button_resume_game.png b/assets/ui/button_resume_game.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ea0a02d05e42377eb551a4b51428b511a32f5d Binary files /dev/null and b/assets/ui/button_resume_game.png differ diff --git a/assets/icons/button_shuffle.png b/assets/ui/button_shuffle.png similarity index 100% rename from assets/icons/button_shuffle.png rename to assets/ui/button_shuffle.png diff --git a/assets/ui/button_start.png b/assets/ui/button_start.png new file mode 100644 index 0000000000000000000000000000000000000000..6845e2f5c21598ab61f1684d2075aeec0334bf23 Binary files /dev/null and b/assets/ui/button_start.png differ diff --git a/assets/icons/game_win.png b/assets/ui/game_win.png similarity index 100% rename from assets/icons/game_win.png rename to assets/ui/game_win.png diff --git a/assets/icons/placeholder.png b/assets/ui/placeholder.png similarity index 100% rename from assets/icons/placeholder.png rename to assets/ui/placeholder.png diff --git a/assets/icons/tip_hidden.png b/assets/ui/tip_hidden.png similarity index 100% rename from assets/icons/tip_hidden.png rename to assets/ui/tip_hidden.png diff --git a/fastlane/metadata/android/en-US/changelogs/62.txt b/fastlane/metadata/android/en-US/changelogs/62.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4afd512e55b3fd8ffbfd795adb9b00832e5aaef --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/62.txt @@ -0,0 +1 @@ +Improve/normalize game architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/62.txt b/fastlane/metadata/android/fr-FR/changelogs/62.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a9871a5eb8eb3c6e9106520f1cbf1f39f9e5ef7 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/62.txt @@ -0,0 +1 @@ +Amélioration/normalisation de l'architecture du jeu. diff --git a/icons/difficulty_3x3.svg b/icons/difficulty_3x3.svg deleted file mode 100644 index 9806d6450e14e7ea2c3c01e5842c13e820816e3f..0000000000000000000000000000000000000000 --- a/icons/difficulty_3x3.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="#009d21"/><g transform="matrix(1.1542 0 0 1.1542 -7.5435 -7.7088)" fill="#f9f9f9" stroke="#005c0c" stroke-width="2" aria-label="3x3"><path d="m19.443 43.965-7.5195-1.3477q0.9375-3.5938 3.5938-5.5078 2.6758-1.9141 7.5586-1.9141 5.6055 0 8.1055 2.0898t2.5 5.2539q0 1.8555-1.0156 3.3594t-3.0664 2.6367q1.6602 0.41016 2.5391 0.95703 1.4258 0.87891 2.207 2.3242 0.80078 1.4258 0.80078 3.418 0 2.5-1.3086 4.8047-1.3086 2.2852-3.7695 3.5352-2.4609 1.2305-6.4648 1.2305-3.9062 0-6.1719-0.91797-2.2461-0.91797-3.7109-2.6758-1.4453-1.7773-2.2266-4.4531l7.9492-1.0547q0.46875 2.4023 1.4453 3.3398 0.99609 0.91797 2.5195 0.91797 1.6016 0 2.6562-1.1719 1.0742-1.1719 1.0742-3.125 0-1.9922-1.0352-3.0859-1.0156-1.0938-2.7734-1.0938-0.9375 0-2.5781 0.46875l0.41016-5.6836q0.66406 0.09766 1.0352 0.09766 1.5625 0 2.5977-0.99609 1.0547-0.99609 1.0547-2.3633 0-1.3086-0.78125-2.0898t-2.1484-0.78125q-1.4062 0-2.2852 0.85938-0.87891 0.83984-1.1914 2.9688z"/><path d="m37.373 43.574h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m72.803 43.965-7.5195-1.3477q0.9375-3.5938 3.5938-5.5078 2.6758-1.9141 7.5586-1.9141 5.6055 0 8.1055 2.0898t2.5 5.2539q0 1.8555-1.0156 3.3594t-3.0664 2.6367q1.6602 0.41016 2.5391 0.95703 1.4258 0.87891 2.207 2.3242 0.80078 1.4258 0.80078 3.418 0 2.5-1.3086 4.8047-1.3086 2.2852-3.7695 3.5352-2.4609 1.2305-6.4648 1.2305-3.9062 0-6.1719-0.91797-2.2461-0.91797-3.7109-2.6758-1.4453-1.7773-2.2266-4.4531l7.9492-1.0547q0.46875 2.4023 1.4453 3.3398 0.99609 0.91797 2.5195 0.91797 1.6016 0 2.6562-1.1719 1.0742-1.1719 1.0742-3.125 0-1.9922-1.0352-3.0859-1.0156-1.0938-2.7734-1.0938-0.9375 0-2.5781 0.46875l0.41016-5.6836q0.66406 0.09766 1.0352 0.09766 1.5625 0 2.5977-0.99609 1.0547-0.99609 1.0547-2.3633 0-1.3086-0.78125-2.0898t-2.1484-0.78125q-1.4062 0-2.2852 0.85938-0.87891 0.83984-1.1914 2.9688z"/></g></svg> diff --git a/icons/difficulty_4x4.svg b/icons/difficulty_4x4.svg deleted file mode 100644 index 9d30392df74c6b32a119ce67fa14528a8c440a7a..0000000000000000000000000000000000000000 --- a/icons/difficulty_4x4.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="#eeb517"/><g transform="matrix(1.1547 0 0 1.1547 -7.7373 -7.7368)" fill="#fff" stroke="#969400" stroke-width="2" aria-label="4x4"><path d="m25.303 59.209h-14.492v-6.543l14.492-17.227h6.9336v17.598h3.5938v6.1719h-3.5938v5.3516h-6.9336zm0-6.1719v-9.0039l-7.6562 9.0039z"/><path d="m37.275 43.818h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m78.662 59.209h-14.492v-6.543l14.492-17.227h6.9336v17.598h3.5938v6.1719h-3.5938v5.3516h-6.9336zm0-6.1719v-9.0039l-7.6562 9.0039z"/></g></svg> diff --git a/icons/difficulty_5x5.svg b/icons/difficulty_5x5.svg deleted file mode 100644 index 93595012b5e8c6c41aff690d4b44d9542338e0e9..0000000000000000000000000000000000000000 --- a/icons/difficulty_5x5.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="#d31158"/><g transform="matrix(1.1387 0 0 1.1387 -6.7678 -6.9338)" fill="#fff" stroke="#700934" stroke-width="2" aria-label="5x5"><path d="m14.971 35.439h18.887v6.3477h-12.793l-0.68359 4.2969q1.3281-0.625 2.6172-0.9375 1.3086-0.3125 2.5781-0.3125 4.2969 0 6.9727 2.5977t2.6758 6.543q0 2.7734-1.3867 5.332-1.3672 2.5586-3.9062 3.9062-2.5195 1.3477-6.4648 1.3477-2.832 0-4.8633-0.52734-2.0117-0.54688-3.4375-1.6016-1.4062-1.0742-2.2852-2.4219-0.87891-1.3477-1.4648-3.3594l8.0469-0.87891q0.29297 1.9336 1.3672 2.9492 1.0742 0.99609 2.5586 0.99609 1.6602 0 2.7344-1.25 1.0938-1.2695 1.0938-3.7695 0-2.5586-1.0938-3.75t-2.9102-1.1914q-1.1523 0-2.2266 0.56641-0.80078 0.41016-1.7578 1.4844l-6.7773-0.97656z"/><path d="m37.393 43.33h9.4336l3.3008 5.7812 3.8281-5.7812h8.7695l-7.0703 9.8828 7.5781 10.859h-9.2773l-3.8281-6.6797-4.5117 6.6797h-8.6133l7.5391-10.859z"/><path d="m68.33 35.439h18.887v6.3477h-12.793l-0.68359 4.2969q1.3281-0.625 2.6172-0.9375 1.3086-0.3125 2.5781-0.3125 4.2969 0 6.9727 2.5977t2.6758 6.543q0 2.7734-1.3867 5.332-1.3672 2.5586-3.9062 3.9062-2.5195 1.3477-6.4648 1.3477-2.832 0-4.8633-0.52734-2.0117-0.54688-3.4375-1.6016-1.4062-1.0742-2.2852-2.4219-0.87891-1.3477-1.4648-3.3594l8.0469-0.87891q0.29297 1.9336 1.3672 2.9492 1.0742 0.99609 2.5586 0.99609 1.6602 0 2.7344-1.25 1.0938-1.2695 1.0938-3.7695 0-2.5586-1.0938-3.75t-2.9102-1.1914q-1.1523 0-2.2266 0.56641-0.80078 0.41016-1.7578 1.4844l-6.7773-0.97656z"/></g></svg> diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart new file mode 100644 index 0000000000000000000000000000000000000000..b18afd1aaeeb28be070a1b76f7ce7d184565bb1f --- /dev/null +++ b/lib/config/default_game_settings.dart @@ -0,0 +1,49 @@ +import 'package:puzzlegame/utils/tools.dart'; + +class DefaultGameSettings { + // available game parameters codes + static const String parameterCodeTilesetSize = 'tilesetSize'; + static const String parameterCodeImageName = 'imageName'; + static const List<String> availableParameters = [ + parameterCodeTilesetSize, + parameterCodeImageName, + ]; + + // tileset size: available values + static const String tilesetSizeValueSmall = '3x3'; + static const String tilesetSizeValueMedium = '4x4'; + static const String tilesetSizeValueLarge = '5x5'; + static const List<String> allowedTilesetSizeValues = [ + tilesetSizeValueSmall, + tilesetSizeValueMedium, + tilesetSizeValueLarge, + ]; + // tileset size: default value + static const String defaultTilesetSizeValue = tilesetSizeValueMedium; + + // image name: available values + static const String imageNameRandomlyPicked = 'random'; + static const List<String> allowedImageNameS = [ + imageNameRandomlyPicked, + ]; + // image name: default value + static const String defaultImageName = imageNameRandomlyPicked; + + // available values from parameter code + static List<String> getAvailableValues(String parameterCode) { + switch (parameterCode) { + case parameterCodeTilesetSize: + return DefaultGameSettings.allowedTilesetSizeValues; + case parameterCodeImageName: + return DefaultGameSettings.allowedImageNameS; + } + + printlog('Did not find any available value for game parameter "$parameterCode".'); + return []; + } + + // parameters displayed with assets (instead of painter) + static List<String> displayedWithAssets = [ + // + ]; +} diff --git a/lib/config/default_global_settings.dart b/lib/config/default_global_settings.dart new file mode 100644 index 0000000000000000000000000000000000000000..dac4541466fd2fafb7eaaf3c016a8bc92b20d589 --- /dev/null +++ b/lib/config/default_global_settings.dart @@ -0,0 +1,33 @@ +import 'package:puzzlegame/utils/tools.dart'; + +class DefaultGlobalSettings { + // available global parameters codes + static const String parameterCodeSkin = 'skin'; + static const List<String> availableParameters = [ + parameterCodeSkin, + ]; + + // skin: available values + static const String skinValueDefault = 'default'; + static const List<String> allowedSkinValues = [ + skinValueDefault, + ]; + // skin: default value + static const String defaultSkinValue = skinValueDefault; + + // available values from parameter code + static List<String> getAvailableValues(String parameterCode) { + switch (parameterCode) { + case parameterCodeSkin: + return DefaultGlobalSettings.allowedSkinValues; + } + + printlog('Did not find any available value for global parameter "$parameterCode".'); + return []; + } + + // parameters displayed with assets (instead of painter) + static List<String> displayedWithAssets = [ + // + ]; +} diff --git a/lib/config/menu.dart b/lib/config/menu.dart new file mode 100644 index 0000000000000000000000000000000000000000..b68e1deabe3b96fb41da4c28cc9b98f95228e576 --- /dev/null +++ b/lib/config/menu.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:unicons/unicons.dart'; + +import 'package:puzzlegame/ui/screens/page_about.dart'; +import 'package:puzzlegame/ui/screens/page_game.dart'; +import 'package:puzzlegame/ui/screens/page_settings.dart'; + +class MenuItem { + final Icon icon; + final Widget page; + + const MenuItem({ + required this.icon, + required this.page, + }); +} + +class Menu { + static const indexGame = 0; + static const menuItemGame = MenuItem( + icon: Icon(UniconsLine.home), + page: PageGame(), + ); + + static const indexSettings = 1; + static const menuItemSettings = MenuItem( + icon: Icon(UniconsLine.setting), + page: PageSettings(), + ); + + static const indexAbout = 2; + static const menuItemAbout = MenuItem( + icon: Icon(UniconsLine.info_circle), + page: PageAbout(), + ); + + static Map<int, MenuItem> items = { + indexGame: menuItemGame, + indexSettings: menuItemSettings, + indexAbout: menuItemAbout, + }; + + static bool isIndexAllowed(int pageIndex) { + return items.keys.contains(pageIndex); + } + + static Widget getPageWidget(int pageIndex) { + return items[pageIndex]?.page ?? menuItemGame.page; + } + + static int itemsCount = Menu.items.length; +} diff --git a/lib/config/theme.dart b/lib/config/theme.dart new file mode 100644 index 0000000000000000000000000000000000000000..74f532fd5abf693979118609564d29167e902009 --- /dev/null +++ b/lib/config/theme.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; + +/// Colors from Tailwind CSS (v3.0) - June 2022 +/// +/// https://tailwindcss.com/docs/customizing-colors + +const int _primaryColor = 0xFF6366F1; +const MaterialColor primarySwatch = MaterialColor(_primaryColor, <int, Color>{ + 50: Color(0xFFEEF2FF), // indigo-50 + 100: Color(0xFFE0E7FF), // indigo-100 + 200: Color(0xFFC7D2FE), // indigo-200 + 300: Color(0xFFA5B4FC), // indigo-300 + 400: Color(0xFF818CF8), // indigo-400 + 500: Color(_primaryColor), // indigo-500 + 600: Color(0xFF4F46E5), // indigo-600 + 700: Color(0xFF4338CA), // indigo-700 + 800: Color(0xFF3730A3), // indigo-800 + 900: Color(0xFF312E81), // indigo-900 +}); + +const int _textColor = 0xFF64748B; +const MaterialColor textSwatch = MaterialColor(_textColor, <int, Color>{ + 50: Color(0xFFF8FAFC), // slate-50 + 100: Color(0xFFF1F5F9), // slate-100 + 200: Color(0xFFE2E8F0), // slate-200 + 300: Color(0xFFCBD5E1), // slate-300 + 400: Color(0xFF94A3B8), // slate-400 + 500: Color(_textColor), // slate-500 + 600: Color(0xFF475569), // slate-600 + 700: Color(0xFF334155), // slate-700 + 800: Color(0xFF1E293B), // slate-800 + 900: Color(0xFF0F172A), // slate-900 +}); + +const Color errorColor = Color(0xFFDC2626); // red-600 + +final ColorScheme lightColorScheme = ColorScheme.light( + primary: primarySwatch.shade500, + secondary: primarySwatch.shade500, + onSecondary: Colors.white, + error: errorColor, + onSurface: textSwatch.shade500, + surface: textSwatch.shade50, + surfaceContainerHighest: Colors.white, + shadow: textSwatch.shade900.withOpacity(.1), +); + +final ColorScheme darkColorScheme = ColorScheme.dark( + primary: primarySwatch.shade500, + secondary: primarySwatch.shade500, + onSecondary: Colors.white, + error: errorColor, + onSurface: textSwatch.shade300, + surface: const Color(0xFF262630), + surfaceContainerHighest: const Color(0xFF282832), + shadow: textSwatch.shade900.withOpacity(.2), +); + +final ThemeData lightTheme = ThemeData( + colorScheme: lightColorScheme, + fontFamily: 'Nunito', + textTheme: TextTheme( + displayLarge: TextStyle( + color: textSwatch.shade700, + fontFamily: 'Nunito', + ), + displayMedium: TextStyle( + color: textSwatch.shade600, + fontFamily: 'Nunito', + ), + displaySmall: TextStyle( + color: textSwatch.shade500, + fontFamily: 'Nunito', + ), + headlineLarge: TextStyle( + color: textSwatch.shade700, + fontFamily: 'Nunito', + ), + headlineMedium: TextStyle( + color: textSwatch.shade600, + fontFamily: 'Nunito', + ), + headlineSmall: TextStyle( + color: textSwatch.shade500, + fontFamily: 'Nunito', + ), + titleLarge: TextStyle( + color: textSwatch.shade700, + fontFamily: 'Nunito', + ), + titleMedium: TextStyle( + color: textSwatch.shade600, + fontFamily: 'Nunito', + ), + titleSmall: TextStyle( + color: textSwatch.shade500, + fontFamily: 'Nunito', + ), + bodyLarge: TextStyle( + color: textSwatch.shade700, + fontFamily: 'Nunito', + ), + bodyMedium: TextStyle( + color: textSwatch.shade600, + fontFamily: 'Nunito', + ), + bodySmall: TextStyle( + color: textSwatch.shade500, + fontFamily: 'Nunito', + ), + labelLarge: TextStyle( + color: textSwatch.shade700, + fontFamily: 'Nunito', + ), + labelMedium: TextStyle( + color: textSwatch.shade600, + fontFamily: 'Nunito', + ), + labelSmall: TextStyle( + color: textSwatch.shade500, + fontFamily: 'Nunito', + ), + ), +); + +final ThemeData darkTheme = lightTheme.copyWith( + colorScheme: darkColorScheme, + textTheme: TextTheme( + displayLarge: TextStyle( + color: textSwatch.shade200, + fontFamily: 'Nunito', + ), + displayMedium: TextStyle( + color: textSwatch.shade300, + fontFamily: 'Nunito', + ), + displaySmall: TextStyle( + color: textSwatch.shade400, + fontFamily: 'Nunito', + ), + headlineLarge: TextStyle( + color: textSwatch.shade200, + fontFamily: 'Nunito', + ), + headlineMedium: TextStyle( + color: textSwatch.shade300, + fontFamily: 'Nunito', + ), + headlineSmall: TextStyle( + color: textSwatch.shade400, + fontFamily: 'Nunito', + ), + titleLarge: TextStyle( + color: textSwatch.shade200, + fontFamily: 'Nunito', + ), + titleMedium: TextStyle( + color: textSwatch.shade300, + fontFamily: 'Nunito', + ), + titleSmall: TextStyle( + color: textSwatch.shade400, + fontFamily: 'Nunito', + ), + bodyLarge: TextStyle( + color: textSwatch.shade200, + fontFamily: 'Nunito', + ), + bodyMedium: TextStyle( + color: textSwatch.shade300, + fontFamily: 'Nunito', + ), + bodySmall: TextStyle( + color: textSwatch.shade400, + fontFamily: 'Nunito', + ), + labelLarge: TextStyle( + color: textSwatch.shade200, + fontFamily: 'Nunito', + ), + labelMedium: TextStyle( + color: textSwatch.shade300, + fontFamily: 'Nunito', + ), + labelSmall: TextStyle( + color: textSwatch.shade400, + fontFamily: 'Nunito', + ), + ), +); diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..41f8b274a7c22fc3f23e6066740afce820629c4e --- /dev/null +++ b/lib/cubit/game_cubit.dart @@ -0,0 +1,244 @@ +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:image/image.dart' as imglib; + +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/models/game/moving_tile.dart'; +import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/models/settings/settings_global.dart'; + +part 'game_state.dart'; + +class GameCubit extends HydratedCubit<GameState> { + GameCubit() + : super(GameState( + currentGame: Game.createEmpty(), + )); + + void updateState(Game game) { + emit(GameState( + currentGame: game, + )); + } + + void refresh() { + final Game game = Game( + // Settings + gameSettings: state.currentGame.gameSettings, + globalSettings: state.currentGame.globalSettings, + // State + isRunning: state.currentGame.isRunning, + isStarted: state.currentGame.isStarted, + isFinished: state.currentGame.isFinished, + animationInProgress: state.currentGame.animationInProgress, + shufflingInProgress: state.currentGame.shufflingInProgress, + // Base data + image: state.currentGame.image, + tiles: state.currentGame.tiles, + // Game data + movesCount: state.currentGame.movesCount, + displayTip: state.currentGame.displayTip, + tileSize: state.currentGame.tileSize, + ); + // game.dump(); + + updateState(game); + } + + void startNewGame({ + required GameSettings gameSettings, + required GlobalSettings globalSettings, + }) { + final Game newGame = Game.createNew( + // Settings + gameSettings: gameSettings, + globalSettings: globalSettings, + ); + + newGame.dump(); + + updateState(newGame); + refresh(); + + state.currentGame.isRunning = true; + state.currentGame.shufflingInProgress = true; + refresh(); + + Timer(const Duration(seconds: 1), () { + splitImageInTiles(); + }); + } + + void quitGame() { + state.currentGame.isRunning = false; + refresh(); + } + + void resumeSavedGame() { + state.currentGame.isRunning = true; + refresh(); + } + + void deleteSavedGame() { + state.currentGame.isRunning = false; + state.currentGame.isFinished = true; + refresh(); + } + + void updateTileSize(double tileSize) { + if (tileSize != state.currentGame.tileSize) { + state.currentGame.tileSize = tileSize; + for (var i = 0; i < state.currentGame.tiles.length; i++) { + state.currentGame.tiles[i].size = tileSize; + } + refresh(); + } + } + + void toggleDisplayTipImage() { + state.currentGame.displayTip = !state.currentGame.displayTip; + refresh(); + } + + void incrementMovesCount() { + state.currentGame.movesCount++; + refresh(); + } + + bool checkTilesetIsCleared() { + for (MovingTile tile in state.currentGame.tiles) { + if (!tile.isCorrect()) { + return false; + } + } + return true; + } + + void swapTiles(List<int> tile1, List<int> tile2) { + state.currentGame.isStarted = true; + + final int indexTile1 = state.currentGame.tiles.indexWhere( + (tile) => ((tile.currentCol == tile1[0]) && (tile.currentRow == tile1[1]))); + final int indexTile2 = state.currentGame.tiles.indexWhere( + (tile) => ((tile.currentCol == tile2[0]) && (tile.currentRow == tile2[1]))); + + final MovingTile swap = state.currentGame.tiles[indexTile1]; + state.currentGame.tiles[indexTile1] = state.currentGame.tiles[indexTile2]; + state.currentGame.tiles[indexTile2] = swap; + + final int swapCol = state.currentGame.tiles[indexTile1].currentCol; + state.currentGame.tiles[indexTile1].currentCol = + state.currentGame.tiles[indexTile2].currentCol; + state.currentGame.tiles[indexTile2].currentCol = swapCol; + + final int swapRow = state.currentGame.tiles[indexTile1].currentRow; + state.currentGame.tiles[indexTile1].currentRow = + state.currentGame.tiles[indexTile2].currentRow; + state.currentGame.tiles[indexTile2].currentRow = swapRow; + + incrementMovesCount(); + if (checkTilesetIsCleared()) { + state.currentGame.isFinished = true; + } + + refresh(); + } + + Future<void> splitImageInTiles() async { + final String imageAsset = 'assets/images/${state.currentGame.image}.png'; + final Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); + + final int tilesCount = state.currentGame.gameSettings.tilesetSizeValue; + + final imglib.Image image = imglib.decodeImage(imageData) ?? + imglib.Image.fromBytes( + height: 1, + width: 1, + bytes: Uint8List.fromList([]).buffer, + ); + + int x = 0, y = 0; + final int width = (image.width / tilesCount).round(); + final int height = (image.height / tilesCount).round(); + + final List<MovingTile> tiles = []; + for (int i = 0; i < tilesCount; i++) { + for (int j = 0; j < tilesCount; j++) { + final Uint8List tileData = Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( + image, + x: x, + y: y, + width: width, + height: height, + ))); + + tiles.add(MovingTile( + currentCol: j, + currentRow: i, + image: Image.memory(tileData), + size: state.currentGame.tileSize, + originalCol: j, + originalRow: i, + )); + + x += width; + } + x = 0; + y += height; + } + + state.currentGame.tiles = tiles; + shuffleTiles(); + + state.currentGame.shufflingInProgress = false; + refresh(); + } + + void shuffleTiles() { + final Random random = Random(); + + final List<MovingTile> tiles = state.currentGame.tiles; + final int tilesCount = tiles.length; + + for (int i = 0; i < (10 * tilesCount); i++) { + final int indexTile1 = random.nextInt(tilesCount); + final int indexTile2 = random.nextInt(tilesCount); + + final MovingTile swap = tiles[indexTile1]; + tiles[indexTile1] = tiles[indexTile2]; + tiles[indexTile2] = swap; + + final int swapCol = tiles[indexTile1].currentCol; + tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; + tiles[indexTile2].currentCol = swapCol; + + final int swapRow = tiles[indexTile1].currentRow; + tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; + tiles[indexTile2].currentRow = swapRow; + } + + state.currentGame.tiles = tiles; + } + + @override + GameState? fromJson(Map<String, dynamic> json) { + final Game currentGame = json['currentGame'] as Game; + + return GameState( + currentGame: currentGame, + ); + } + + @override + Map<String, dynamic>? toJson(GameState state) { + return <String, dynamic>{ + 'currentGame': state.currentGame.toJson(), + }; + } +} diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..00e211668c3269255926939324355792abd61c41 --- /dev/null +++ b/lib/cubit/game_state.dart @@ -0,0 +1,15 @@ +part of 'game_cubit.dart'; + +@immutable +class GameState extends Equatable { + const GameState({ + required this.currentGame, + }); + + final Game currentGame; + + @override + List<dynamic> get props => <dynamic>[ + currentGame, + ]; +} diff --git a/lib/cubit/nav_cubit.dart b/lib/cubit/nav_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..6f2839c50daad5736723a285304ad879ad995105 --- /dev/null +++ b/lib/cubit/nav_cubit.dart @@ -0,0 +1,37 @@ +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import 'package:puzzlegame/config/menu.dart'; + +class NavCubit extends HydratedCubit<int> { + NavCubit() : super(0); + + void updateIndex(int index) { + if (Menu.isIndexAllowed(index)) { + emit(index); + } else { + goToGamePage(); + } + } + + void goToGamePage() { + emit(Menu.indexGame); + } + + void goToSettingsPage() { + emit(Menu.indexSettings); + } + + void goToAboutPage() { + emit(Menu.indexAbout); + } + + @override + int fromJson(Map<String, dynamic> json) { + return Menu.indexGame; + } + + @override + Map<String, dynamic>? toJson(int state) { + return <String, int>{'pageIndex': state}; + } +} diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..0cc90f3d61d60474f837324cb83d1a0038175f71 --- /dev/null +++ b/lib/cubit/settings_game_cubit.dart @@ -0,0 +1,72 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import 'package:puzzlegame/config/default_game_settings.dart'; +import 'package:puzzlegame/models/settings/settings_game.dart'; + +part 'settings_game_state.dart'; + +class GameSettingsCubit extends HydratedCubit<GameSettingsState> { + GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); + + void setValues({ + String? tilesetSize, + String? imageName, + }) { + emit( + GameSettingsState( + settings: GameSettings( + tilesetSize: tilesetSize ?? state.settings.tilesetSize, + imageName: imageName ?? state.settings.imageName, + ), + ), + ); + } + + String getParameterValue(String code) { + switch (code) { + case DefaultGameSettings.parameterCodeTilesetSize: + return GameSettings.getTilesetSizeValueFromUnsafe(state.settings.tilesetSize); + case DefaultGameSettings.parameterCodeImageName: + return GameSettings.getImageNameFromUnsafe(state.settings.imageName); + } + + return ''; + } + + void setParameterValue(String code, String value) { + final String tilesetSize = code == DefaultGameSettings.parameterCodeTilesetSize + ? value + : getParameterValue(DefaultGameSettings.parameterCodeTilesetSize); + final String imageName = code == DefaultGameSettings.parameterCodeImageName + ? value + : getParameterValue(DefaultGameSettings.parameterCodeImageName); + + setValues( + tilesetSize: tilesetSize, + imageName: imageName, + ); + } + + @override + GameSettingsState? fromJson(Map<String, dynamic> json) { + final String tilesetSize = json[DefaultGameSettings.parameterCodeTilesetSize] as String; + final String imageName = json[DefaultGameSettings.parameterCodeImageName] as String; + + return GameSettingsState( + settings: GameSettings( + tilesetSize: tilesetSize, + imageName: imageName, + ), + ); + } + + @override + Map<String, dynamic>? toJson(GameSettingsState state) { + return <String, dynamic>{ + DefaultGameSettings.parameterCodeTilesetSize: state.settings.tilesetSize, + DefaultGameSettings.parameterCodeImageName: state.settings.imageName, + }; + } +} diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..5acd85b44ba541e1c5e9c26af1c4be26a385b9ed --- /dev/null +++ b/lib/cubit/settings_game_state.dart @@ -0,0 +1,15 @@ +part of 'settings_game_cubit.dart'; + +@immutable +class GameSettingsState extends Equatable { + const GameSettingsState({ + required this.settings, + }); + + final GameSettings settings; + + @override + List<dynamic> get props => <dynamic>[ + settings, + ]; +} diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings_global_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..aab5e8a44addc47c2426d229c866b62e35c6f47a --- /dev/null +++ b/lib/cubit/settings_global_cubit.dart @@ -0,0 +1,60 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import 'package:puzzlegame/config/default_global_settings.dart'; +import 'package:puzzlegame/models/settings/settings_global.dart'; + +part 'settings_global_state.dart'; + +class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { + GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault())); + + void setValues({ + String? skin, + }) { + emit( + GlobalSettingsState( + settings: GlobalSettings( + skin: skin ?? state.settings.skin, + ), + ), + ); + } + + String getParameterValue(String code) { + switch (code) { + case DefaultGlobalSettings.parameterCodeSkin: + return GlobalSettings.getSkinValueFromUnsafe(state.settings.skin); + } + return ''; + } + + void setParameterValue(String code, String value) { + final String skin = (code == DefaultGlobalSettings.parameterCodeSkin) + ? value + : getParameterValue(DefaultGlobalSettings.parameterCodeSkin); + + setValues( + skin: skin, + ); + } + + @override + GlobalSettingsState? fromJson(Map<String, dynamic> json) { + final String skin = json[DefaultGlobalSettings.parameterCodeSkin] as String; + + return GlobalSettingsState( + settings: GlobalSettings( + skin: skin, + ), + ); + } + + @override + Map<String, dynamic>? toJson(GlobalSettingsState state) { + return <String, dynamic>{ + DefaultGlobalSettings.parameterCodeSkin: state.settings.skin, + }; + } +} diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings_global_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..ebcddd700f252257223ca8e16c85202b04f3ff24 --- /dev/null +++ b/lib/cubit/settings_global_state.dart @@ -0,0 +1,15 @@ +part of 'settings_global_cubit.dart'; + +@immutable +class GlobalSettingsState extends Equatable { + const GlobalSettingsState({ + required this.settings, + }); + + final GlobalSettings settings; + + @override + List<dynamic> get props => <dynamic>[ + settings, + ]; +} diff --git a/lib/cubit/theme_cubit.dart b/lib/cubit/theme_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..b793e895dbb0c672d451cd403e0036c3d9ac9b42 --- /dev/null +++ b/lib/cubit/theme_cubit.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +part 'theme_state.dart'; + +class ThemeCubit extends HydratedCubit<ThemeModeState> { + ThemeCubit() : super(const ThemeModeState()); + + void getTheme(ThemeModeState state) { + emit(state); + } + + @override + ThemeModeState? fromJson(Map<String, dynamic> json) { + switch (json['themeMode']) { + case 'ThemeMode.dark': + return const ThemeModeState(themeMode: ThemeMode.dark); + case 'ThemeMode.light': + return const ThemeModeState(themeMode: ThemeMode.light); + case 'ThemeMode.system': + default: + return const ThemeModeState(themeMode: ThemeMode.system); + } + } + + @override + Map<String, String>? toJson(ThemeModeState state) { + return <String, String>{'themeMode': state.themeMode.toString()}; + } +} diff --git a/lib/cubit/theme_state.dart b/lib/cubit/theme_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..e479a50f12fe72a35a1fd1722ff72afbb692a136 --- /dev/null +++ b/lib/cubit/theme_state.dart @@ -0,0 +1,15 @@ +part of 'theme_cubit.dart'; + +@immutable +class ThemeModeState extends Equatable { + const ThemeModeState({ + this.themeMode, + }); + + final ThemeMode? themeMode; + + @override + List<Object?> get props => <Object?>[ + themeMode, + ]; +} diff --git a/lib/data/fetch_data_helper.dart b/lib/data/fetch_data_helper.dart new file mode 100644 index 0000000000000000000000000000000000000000..33c50c0d6834b245fdd110f8bb0dee0b230f2f4a --- /dev/null +++ b/lib/data/fetch_data_helper.dart @@ -0,0 +1,39 @@ +import 'package:puzzlegame/data/game_data.dart'; +import 'package:puzzlegame/utils/tools.dart'; + +class FetchDataHelper { + FetchDataHelper(); + + final List<String> _images = []; + List<String> get images => _images; + + void init() { + try { + final List<String> rawImages = GameData.data['images'] as List<String>; + for (var imageCode in rawImages) { + _images.add(imageCode.toString()); + } + } catch (e) { + printlog("$e"); + } + } + + List<String> getRandomItems(int count) { + if (_images.isEmpty) { + init(); + } + + List<String> shuffleableList = []; + for (var i = 0; i < _images.length; i++) { + shuffleableList.add(_images[i]); + } + + shuffleableList.shuffle(); + + return shuffleableList.sublist(0, count); + } + + String getRandomItem() { + return getRandomItems(1).first; + } +} diff --git a/lib/data/game_data.dart b/lib/data/game_data.dart new file mode 100644 index 0000000000000000000000000000000000000000..d6979485be40240ad41c92df21b180f29f3cebb8 --- /dev/null +++ b/lib/data/game_data.dart @@ -0,0 +1,104 @@ +class GameData { + static const Map<String, dynamic> data = { + "images": [ + "00_default_08b2107e3c30da02a6c18613a1e90857", + "00_default_3bcda1b5fb7adcdac1824dde11060462", + "00_default_3c245519d89fefd792d05b72631ef8af", + "00_default_494b2e821e8e7130088ec2929bf49be8", + "animals_0824b924323459fb8e8a90054443c77b", + "animals_0a86f77291858c10ca6dcc631ba14f96", + "animals_36c401aebe4293803710b05a08d6a248", + "animals_5cb4af89a40d60e8839a43e915d15a2f", + "animals_60f53fdf0217001c7a89f48f243f2aa9", + "animals_677d788f482bffdd7f79c525aae93832", + "animals_76baeb891edc3f20814fcda6de541c8b", + "animals_815079fbba4a96c699b25fca11cf303e", + "animals_83f2c647801c57722f5582687a95071b", + "animals_8786f3eced6b176537608b50398b464d", + "animals_88c3071723a3d93b9d581313e2b25c65", + "animals_8cb1144c6afa8ccd37871a1c237e7c3a", + "animals_9b0b293a1f2393d46e2d8f7ca841e652", + "animals_9bd7055c69b6471477bc67e508a0ec7a", + "animals_a42fb9cddd68b019a1e6216f01d94742", + "animals_d3f924d97c3d6bb735a27c0e4edfcd7c", + "animals_e937f221caaa87337c1dc98002e13c56", + "animals_f05f46e9ea9f18d47fbd7733a608536b", + "animals_fc0a31d2c32467fdda711ffd521e79d9", + "anime_aa293fb70ed59c7b610f0e6e218a0917", + "anime_f48648cbbff8370f18bdc592d98bb563", + "dinosaurs_61c5d5727722d957ffa39b7cc5519bf6", + "dinosaurs_d2fafe9651b48459a72aa15cfeac3b61", + "ghibli_2c6fd10faee15c612ebfe71628069d36", + "ghibli_914deb9acd1f1ab2a50ec9f7924eb799", + "ghibli_b2c74d93a2c32f2ccbda73c4bee03b00", + "ghibli_f2798b83ce4d01142bdcd5d8803f5412", + "harrypotter_439fe7c78ebbf945de2eb18f2f78f7a8", + "harrypotter_d3664e7e2920eb8cee5b69982f710644", + "nature_38a414b6051a9b528fedc47878663c03", + "nature_98574287a17f734d52dc64dfc0f45fc8", + "nature_c0e131e9a2a813c337965ddfb413baf8", + "personal_0694cfbc9bb292f9da8c7c65d793a378", + "personal_0a0b014b37ddb90d9af1fcdedccef37d", + "personal_0d973affb8ffe7d0abe9ddd89957f580", + "personal_0ea16afc573d649222e512306872106f", + "personal_12381e1ed7bed4ec56c5d871a8b21a5d", + "personal_183bcd7c31fdece731a373ed375fd227", + "personal_1b98afe55b410ed03ea8b3f18fa5cc27", + "personal_22169df124ce293fbd519eb6d987a9b7", + "personal_259dd16880502cbc8fbbff87cee60a24", + "personal_29244801ee5399bc5a445f9c9f1e5487", + "personal_2f9d874fbd41462a6c115022e088f12e", + "personal_31320d5c42d2b0e81a3aa365af7bbd9d", + "personal_3b2274b4265177568057601aef1a4e87", + "personal_3cdb7de70fb4052ac849a6a5fa990eee", + "personal_3debd2d0b3b968d5a895d776bc8d7e03", + "personal_43f57a142340066e3137434a43b6cbbb", + "personal_4936d509c25ceaf585c94067f4f9061b", + "personal_4afcce1786d0265f45ca083b07bd407b", + "personal_4ee0e7589f440418d41e7e8283c6bcef", + "personal_4fe95f154947c4efdf9a35045b47e137", + "personal_548f73139e968e67d9737393e9cba794", + "personal_58e4fa6bdbfea985511936074d40a5e1", + "personal_5a2ec8b6ee98b400ae87207fcace71e3", + "personal_5b6e5f629039ff65178bac0b9d979185", + "personal_626e4843a78dbcc43e49bce849bc82cc", + "personal_642d854a9d4252d78faeb392b09ff04d", + "personal_6761903f2186e75e74fcbde92a4c6865", + "personal_67def669a778cd84299235188df223f9", + "personal_6baedd08c35004321ac26effade8221e", + "personal_6f28c333f1100e7d2cb316ba839a59b9", + "personal_70d72351d07f93710539592d8dd38d8a", + "personal_7d7e4fb5727fc23ab3fa7b40f8769fc1", + "personal_7ea40aebbbde03cbbcc19e5f92609b77", + "personal_84f358d1b64d7947617d85b21ab2375a", + "personal_90deb4cb0578338c442f268d6a24d5dc", + "personal_96b82ec73f9d83066297fa30a5b4384f", + "personal_9e44f127b4ec68efad8ce87da1d3fc51", + "personal_a6819fc32906ac2ca4a0f47503772307", + "personal_b27ef14b2c835df1ae1aeae938823193", + "personal_b50323b0dc6ce5fb9796adb80b814cd8", + "personal_b78c59c08302100769e3469fccff36c1", + "personal_b87fcb24c5d44e1328cddd66b75bc119", + "personal_b89b9d4366696fe1623b5b5d4f10f0fc", + "personal_b8d2facf43700d6f4e6c230c548e771b", + "personal_b961b8d3386fa525ac754eea04b32003", + "personal_bf4520a9582da41ecc280365afebb30f", + "personal_c0eb4eb4d39d58eecebc3218a56f6148", + "personal_c132137e54f68136af709edd6a8f602d", + "personal_c72571b7f7ac673b63754699e1b8c31c", + "personal_d0d1e8e150782b694d663b6cc838cf61", + "personal_d5d163c09ae4da219a7ae54b5a98b830", + "personal_d5f3483ee2a8598123a0d0a9a965aea0", + "personal_daf7dab4c768da753dede24093cef6c3", + "personal_e124afc5e5b3770e53f058431d37bf31", + "personal_e9da5393d39bebb0a5d393b4c55366a5", + "personal_efc2a3bf7597c3991afa800c4515a515", + "personal_f3991aee0215111c5e4c13994477e85b", + "personal_f4059dfc787363cc3437be52a47779a0", + "personal_fb47095d9556e3e7f00711afe128d5b2", + "sea_f2c615363f892ba270dae2fceb3a06e9", + "sea_f64def749dd7133248814a902ea140ab", + "space_438d8689bcc7e7cafd89a97d336d7981", + ] + }; +} diff --git a/lib/main.dart b/lib/main.dart index 33c9734800800bafa2feb43450a630b9571e2a2d..034c064ec7dcb7cac09f7d761ed54267f5174537 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,14 +1,42 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:path_provider/path_provider.dart'; -import 'package:puzzlegame/provider/data.dart'; -import 'package:puzzlegame/screens/home.dart'; +import 'package:puzzlegame/config/theme.dart'; +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/cubit/nav_cubit.dart'; +import 'package:puzzlegame/cubit/settings_game_cubit.dart'; +import 'package:puzzlegame/cubit/settings_global_cubit.dart'; +import 'package:puzzlegame/cubit/theme_cubit.dart'; +import 'package:puzzlegame/ui/skeleton.dart'; -void main() { +void main() async { + // Initialize packages WidgetsFlutterBinding.ensureInitialized(); + await EasyLocalization.ensureInitialized(); + final Directory tmpDir = await getTemporaryDirectory(); + Hive.init(tmpDir.toString()); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: tmpDir, + ); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) - .then((value) => runApp(const MyApp())); + .then((value) => runApp(EasyLocalization( + path: 'assets/translations', + supportedLocales: const <Locale>[ + Locale('en'), + Locale('fr'), + ], + fallbackLocale: const Locale('en'), + useFallbackTranslations: true, + child: const MyApp(), + ))); } class MyApp extends StatelessWidget { @@ -16,23 +44,60 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (BuildContext context) => Data(), - child: Consumer<Data>( - builder: (context, data, child) { + final List<String> assets = getImagesAssets(); + for (String asset in assets) { + precacheImage(AssetImage(asset), context); + } + + return MultiBlocProvider( + providers: [ + BlocProvider<NavCubit>(create: (context) => NavCubit()), + BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()), + BlocProvider<GameCubit>(create: (context) => GameCubit()), + BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()), + BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()), + ], + child: BlocBuilder<ThemeCubit, ThemeModeState>( + builder: (BuildContext context, ThemeModeState state) { return MaterialApp( + title: 'Template app', + home: const SkeletonScreen(), + + // Theme stuff + theme: lightTheme, + darkTheme: darkTheme, + themeMode: state.themeMode, + + // Localization stuff + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, debugShowCheckedModeBanner: false, - theme: ThemeData( - primaryColor: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: const Home(), - routes: { - Home.id: (context) => const Home(), - }, ); }, ), ); } + + List<String> getImagesAssets() { + final List<String> assets = []; + + const List<String> gameImages = [ + 'button_back', + 'button_delete_saved_game', + 'button_random_pick', + 'button_resume_game', + 'button_shuffle', + 'button_start', + 'game_win', + 'placeholder', + 'tip_hidden', + ]; + + for (String image in gameImages) { + assets.add('assets/ui/$image.png'); + } + + return assets; + } } diff --git a/lib/models/game/game.dart b/lib/models/game/game.dart new file mode 100644 index 0000000000000000000000000000000000000000..1d0dccef293089b1662b7f01aba54bf1203d8aa7 --- /dev/null +++ b/lib/models/game/game.dart @@ -0,0 +1,127 @@ +import 'package:puzzlegame/data/fetch_data_helper.dart'; +import 'package:puzzlegame/models/game/moving_tile.dart'; +import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/models/settings/settings_global.dart'; +import 'package:puzzlegame/utils/tools.dart'; + +class Game { + Game({ + // Settings + required this.gameSettings, + required this.globalSettings, + + // State + this.isRunning = false, + this.isStarted = false, + this.isFinished = false, + this.animationInProgress = false, + this.shufflingInProgress = false, + + // Base data + required this.image, + this.tiles = const [], + + // Game data + this.movesCount = 0, + this.displayTip = false, + this.tileSize = 0.0, + }); + + // Settings + final GameSettings gameSettings; + final GlobalSettings globalSettings; + + // State + bool isRunning; + bool isStarted; + bool isFinished; + bool animationInProgress; + bool shufflingInProgress; + + // Base data + String image; + List<MovingTile> tiles; + + // Game data + int movesCount; + bool displayTip; + double tileSize; + + factory Game.createEmpty() { + return Game( + gameSettings: GameSettings.createDefault(), + globalSettings: GlobalSettings.createDefault(), + image: '', + tiles: [], + ); + } + + factory Game.createNew({ + GameSettings? gameSettings, + GlobalSettings? globalSettings, + }) { + final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); + final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); + + final String image = FetchDataHelper().getRandomItem(); + + return Game( + // Settings + gameSettings: newGameSettings, + globalSettings: newGlobalSettings, + // Base data + image: image, + ); + } + + bool get canBeResumed => isStarted && !isFinished; + + void dump() { + printlog(''); + printlog('## Current game dump:'); + printlog(''); + printlog('$Game:'); + printlog(' Settings'); + gameSettings.dump(); + globalSettings.dump(); + printlog(' State'); + printlog(' isRunning: $isRunning'); + printlog(' isStarted: $isStarted'); + printlog(' isFinished: $isFinished'); + printlog(' animationInProgress: $animationInProgress'); + printlog(' shufflingInProgress: $shufflingInProgress'); + printlog(' Base data'); + printlog(' image: $image'); + printlog(' Game data'); + printlog(' movesCount: $movesCount'); + printlog(' displayTip: $displayTip'); + printlog(' tileSize: $tileSize'); + printlog(''); + } + + @override + String toString() { + return '$Game(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + // Settings + 'gameSettings': gameSettings.toJson(), + 'globalSettings': globalSettings.toJson(), + // State + 'isRunning': isRunning, + 'isStarted': isStarted, + 'isFinished': isFinished, + 'animationInProgress': animationInProgress, + 'shufflingInProgress': shufflingInProgress, + // Base data + 'image': image, + 'tiles': tiles, + // Game data + 'movesCount': movesCount, + 'displayTip': displayTip, + 'tileSize': tileSize, + }; + } +} diff --git a/lib/models/game/moving_tile.dart b/lib/models/game/moving_tile.dart new file mode 100644 index 0000000000000000000000000000000000000000..3ef918c9a8e29e04746bb95f6b976546e4fd0d0a --- /dev/null +++ b/lib/models/game/moving_tile.dart @@ -0,0 +1,35 @@ +import 'package:puzzlegame/models/game/tile.dart'; + +class MovingTile extends Tile { + int currentCol; + int currentRow; + + MovingTile({ + required super.image, + required super.size, + required super.originalCol, + required super.originalRow, + required this.currentCol, + required this.currentRow, + }); + + bool isCorrect() { + return ((currentRow == originalRow) && (currentCol == originalCol)); + } + + @override + String toString() { + return '$Tile(${toJson()})'; + } + + @override + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + 'size': size, + 'originalCol': originalCol, + 'originalRow': originalRow, + 'currentRow': currentRow, + 'currentCol': currentCol, + }; + } +} diff --git a/lib/entities/tile.dart b/lib/models/game/tile.dart similarity index 52% rename from lib/entities/tile.dart rename to lib/models/game/tile.dart index 76e58df62b27e29bb8d2e5f6b915ef42bd288c4f..77a369bb32b2d98be11c5faa85bc0e127b6d82e4 100644 --- a/lib/entities/tile.dart +++ b/lib/models/game/tile.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:puzzlegame/provider/data.dart'; - class Tile { final Image image; - final double size; + double size; final int originalCol; final int originalRow; @@ -15,11 +13,16 @@ class Tile { required this.originalRow, }); - Widget widget(Data myProvider) { - return Container(); + @override + String toString() { + return '$Tile(${toJson()})'; } - bool isCorrect() { - return false; + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + 'size': size, + 'originalCol': originalCol, + 'originalRow': originalRow, + }; } } diff --git a/lib/models/settings/settings_game.dart b/lib/models/settings/settings_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..4ed699fb3f5119a6af2aabcdef7ad2b5836ff55f --- /dev/null +++ b/lib/models/settings/settings_game.dart @@ -0,0 +1,56 @@ +import 'package:puzzlegame/config/default_game_settings.dart'; +import 'package:puzzlegame/utils/tools.dart'; + +class GameSettings { + final String tilesetSize; + final String imageName; + + GameSettings({ + required this.tilesetSize, + required this.imageName, + }); + + static String getTilesetSizeValueFromUnsafe(String tilesetSize) { + if (DefaultGameSettings.allowedTilesetSizeValues.contains(tilesetSize)) { + return tilesetSize; + } + + return DefaultGameSettings.defaultTilesetSizeValue; + } + + static String getImageNameFromUnsafe(String imageName) { + if (DefaultGameSettings.allowedImageNameS.contains(imageName)) { + return imageName; + } + + return DefaultGameSettings.defaultImageName; + } + + factory GameSettings.createDefault() { + return GameSettings( + tilesetSize: DefaultGameSettings.defaultTilesetSizeValue, + imageName: DefaultGameSettings.defaultImageName, + ); + } + + int get tilesetSizeValue => int.parse(tilesetSize.split('x')[0]); + + void dump() { + printlog('$GameSettings:'); + printlog(' ${DefaultGameSettings.parameterCodeTilesetSize}: $tilesetSize'); + printlog(' ${DefaultGameSettings.parameterCodeImageName}: $imageName'); + printlog(''); + } + + @override + String toString() { + return '$GameSettings(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + DefaultGameSettings.parameterCodeTilesetSize: tilesetSize, + DefaultGameSettings.parameterCodeImageName: imageName, + }; + } +} diff --git a/lib/models/settings/settings_global.dart b/lib/models/settings/settings_global.dart new file mode 100644 index 0000000000000000000000000000000000000000..4c9879f16449d0786ae4b6396009c629cf1efbc9 --- /dev/null +++ b/lib/models/settings/settings_global.dart @@ -0,0 +1,41 @@ +import 'package:puzzlegame/config/default_global_settings.dart'; +import 'package:puzzlegame/utils/tools.dart'; + +class GlobalSettings { + String skin; + + GlobalSettings({ + required this.skin, + }); + + static String getSkinValueFromUnsafe(String skin) { + if (DefaultGlobalSettings.allowedSkinValues.contains(skin)) { + return skin; + } + + return DefaultGlobalSettings.defaultSkinValue; + } + + factory GlobalSettings.createDefault() { + return GlobalSettings( + skin: DefaultGlobalSettings.defaultSkinValue, + ); + } + + void dump() { + printlog('$GlobalSettings:'); + printlog(' ${DefaultGlobalSettings.parameterCodeSkin}: $skin'); + printlog(''); + } + + @override + String toString() { + return '$GlobalSettings(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + DefaultGlobalSettings.parameterCodeSkin: skin, + }; + } +} diff --git a/lib/provider/data.dart b/lib/provider/data.dart deleted file mode 100644 index dd85da9091f51786a56c70984313fdbb2e1d253c..0000000000000000000000000000000000000000 --- a/lib/provider/data.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/foundation.dart'; - -import 'package:puzzlegame/entities/moving_tile.dart'; - -class Data extends ChangeNotifier { - // application configuration - int _tilesCount = 4; - - // Game data - List<String> _availableImages = []; - String _selectedImage = ''; - List<MovingTile> _tiles = []; - double _tileImageSize = 1.0; - int _movesCount = 0; - - // application state - bool _isShufflingBoard = false; - bool _isTipImageDisplayed = false; - - String get selectedImage => _selectedImage; - - void updateSelectedImage(String value) { - _selectedImage = value; - _movesCount = 0; - notifyListeners(); - } - - void resetSelectedImage() { - updateSelectedImage(''); - } - - int get tilesCount => _tilesCount; - - void updateTilesCount(int tilesCountPerSide) { - _tilesCount = tilesCountPerSide; - notifyListeners(); - } - - List<String> get availableImages => _availableImages; - - void updateAvailableImages(List<String> availableImages) { - _availableImages = availableImages; - notifyListeners(); - } - - List<MovingTile> get tiles => _tiles; - - void updateTiles(List<MovingTile> tiles) { - _tiles = tiles; - notifyListeners(); - } - - int get movesCount => _movesCount; - void updateMovesCount(int movesCount) { - _movesCount = movesCount; - notifyListeners(); - } - - void incrementMovesCount() { - updateMovesCount(movesCount + 1); - } - - bool get isShufflingBoard => _isShufflingBoard; - - void updateIsShufflingBoard(bool isShuffling) { - _isShufflingBoard = isShuffling; - notifyListeners(); - } - - bool get displayTipImage => _isTipImageDisplayed; - - void updateIsTipImageDisplayed(bool isDisplayed) { - _isTipImageDisplayed = isDisplayed; - notifyListeners(); - } - - double get tileImageSize => _tileImageSize; - void updateTileImageSize(double tileImageSize) { - _tileImageSize = tileImageSize; - } - - void swapTiles(List<int> tile1, List<int> tile2) { - final int indexTile1 = _tiles.indexWhere( - (tile) => ((tile.currentCol == tile1[0]) && (tile.currentRow == tile1[1]))); - final int indexTile2 = _tiles.indexWhere( - (tile) => ((tile.currentCol == tile2[0]) && (tile.currentRow == tile2[1]))); - - final MovingTile swap = _tiles[indexTile1]; - _tiles[indexTile1] = _tiles[indexTile2]; - _tiles[indexTile2] = swap; - - final int swapCol = _tiles[indexTile1].currentCol; - _tiles[indexTile1].currentCol = _tiles[indexTile2].currentCol; - _tiles[indexTile2].currentCol = swapCol; - - final int swapRow = _tiles[indexTile1].currentRow; - _tiles[indexTile1].currentRow = _tiles[indexTile2].currentRow; - _tiles[indexTile2].currentRow = swapRow; - - incrementMovesCount(); - notifyListeners(); - } -} diff --git a/lib/screens/game.dart b/lib/screens/game.dart deleted file mode 100644 index 073e39b41a9ceb382c966bbf326198a105177fba..0000000000000000000000000000000000000000 --- a/lib/screens/game.dart +++ /dev/null @@ -1,225 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:puzzlegame/entities/moving_tile.dart'; -import 'package:puzzlegame/provider/data.dart'; -import 'package:puzzlegame/utils/game_utils.dart'; - -class Game { - static void toggleDisplayTipImage(Data myProvider) { - myProvider.updateIsTipImageDisplayed(!myProvider.displayTipImage); - } - - static Widget buildTilesetWidget(Data myProvider) { - final List<MovingTile> tiles = myProvider.tiles; - - final Color borderColor = - GameUtils.checkTilesetIsCleared(tiles) ? Colors.green : Colors.orange; - int tileIndex = 0; - - final Table tileset = Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - border: TableBorder.all( - color: Colors.black, - style: BorderStyle.solid, - width: 2, - ), - children: [ - for (int row = 0; row < myProvider.tilesCount; row++) - TableRow( - children: [ - for (int col = 0; col < myProvider.tilesCount; col++) - Column(children: [tiles[tileIndex++].widget(myProvider)]), - ], - ), - ], - ); - - return Container( - margin: const EdgeInsets.all(8), - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: borderColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: borderColor, - width: 8, - ), - ), - child: tileset, - ); - } - - static Widget buildTipWidget(Data myProvider) { - return Container( - margin: const EdgeInsets.all(2), - padding: const EdgeInsets.all(2), - child: Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - const Column( - children: [ - Image( - image: AssetImage('assets/images/placeholder.png'), - fit: BoxFit.fill, - ), - ], - ), - Column( - children: [ - TextButton( - child: Container( - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Colors.blue, - width: 4, - ), - ), - child: Image( - image: AssetImage( - myProvider.displayTipImage - ? GameUtils.getImageAssetName(myProvider.selectedImage) - : 'assets/icons/tip_hidden.png', - ), - fit: BoxFit.contain, - ), - ), - onPressed: () => Game.toggleDisplayTipImage(myProvider), - ), - ], - ), - const Column( - children: [ - Image( - image: AssetImage('assets/images/placeholder.png'), - fit: BoxFit.fill, - ), - ], - ), - ], - ), - ], - ), - ); - } - - static Widget buildGameWidget(Data myProvider) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 8), - buildTopIndicatorWidget(myProvider), - const SizedBox(height: 2), - Expanded( - child: buildGameBoard(myProvider), - ), - const SizedBox(height: 2), - Container( - child: GameUtils.checkTilesetIsCleared(myProvider.tiles) - ? Game.buildWinMessage(myProvider) - : Game.buildTipWidget(myProvider), - ), - ], - ); - } - - static Widget buildGameBoard(Data myProvider) { - return Container( - margin: const EdgeInsets.all(4), - padding: const EdgeInsets.all(4), - child: Column( - children: [ - buildTilesetWidget(myProvider), - ], - ), - ); - } - - static Widget buildTopIndicatorWidget(Data myProvider) { - return Table( - children: [ - TableRow( - children: [ - Column(children: [ - Text( - myProvider.movesCount.toString(), - style: const TextStyle( - fontSize: 40, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ]), - const Column(children: []), - ], - ), - ], - ); - } - - static Widget buildWinMessage(Data myProvider) { - return Container( - margin: const EdgeInsets.all(2), - padding: const EdgeInsets.all(2), - child: Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - const Column( - children: [ - Image( - image: AssetImage('assets/icons/game_win.png'), - fit: BoxFit.fill, - ), - ], - ), - Column( - children: [ - TextButton( - child: const Image( - image: AssetImage('assets/icons/button_back.png'), - fit: BoxFit.fill, - ), - onPressed: () => GameUtils.resetGame(myProvider), - ), - ], - ), - const Column( - children: [ - Image( - image: AssetImage('assets/icons/game_win.png'), - fit: BoxFit.fill, - ), - ], - ), - ], - ), - ], - ), - ); - } - - static Widget buildShufflingIndicatorWidget() { - return const Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'â³', - style: TextStyle( - fontSize: 60, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - SizedBox(height: 20), - CircularProgressIndicator(), - ], - ); - } -} diff --git a/lib/screens/home.dart b/lib/screens/home.dart deleted file mode 100644 index 1cee132b7767715e18274e26a7f9e3ce97bbde13..0000000000000000000000000000000000000000 --- a/lib/screens/home.dart +++ /dev/null @@ -1,212 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'package:puzzlegame/provider/data.dart'; -import 'package:puzzlegame/screens/game.dart'; -import 'package:puzzlegame/utils/game_utils.dart'; -import 'package:puzzlegame/utils/get_images_list.dart'; - -class Home extends StatelessWidget { - const Home({super.key}); - - static const String id = 'home'; - - final Color themePrimaryColor = Colors.blue; - final int _selectImageColumnsCount = 3; - - Future<void> getImagesList(Data myProvider) async { - GetImagesList getImagesList = GetImagesList(); - await getImagesList.init(); - if (getImagesList.availableImages.isNotEmpty) { - myProvider.updateAvailableImages(getImagesList.availableImages); - shuffleAvailableImages(myProvider); - } - } - - Future<void> selectImage(Data myProvider, String imageCode) async { - GameUtils.startGame(myProvider, imageCode); - } - - void shuffleAvailableImages(Data myProvider) { - final List<String> images = myProvider.availableImages; - images.shuffle(); - myProvider.updateAvailableImages(images); - } - - Widget _buildImageSelectorItem(Data myProvider, String image) { - return TextButton( - style: TextButton.styleFrom( - padding: const EdgeInsets.all(2), - ), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Colors.blue.shade200, - width: 4, - ), - ), - child: Image( - image: AssetImage(GameUtils.getImageAssetName(image)), - fit: BoxFit.fill, - ), - ), - onPressed: () { - selectImage(myProvider, image); - }, - ); - } - - Widget _buildImageSelector(Data myProvider) { - if (myProvider.availableImages.isEmpty) { - getImagesList(myProvider); - } - final List<String> images = myProvider.availableImages; - - return Container( - padding: const EdgeInsets.all(2), - child: ListView( - children: [ - Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - for (int imgIndex = 0; - imgIndex < images.length; - imgIndex += _selectImageColumnsCount) - TableRow( - children: [ - for (int colIndex = 0; colIndex < _selectImageColumnsCount; colIndex++) - Column( - children: [ - if (imgIndex + colIndex < images.length) - _buildImageSelectorItem(myProvider, images[imgIndex + colIndex]) - ], - ), - ], - ), - ], - ), - ], - ), - ); - } - - TextButton _buildTilesetSizeSelectorItem(Data myProvider, int value) { - String assetName = 'assets/icons/difficulty_${value}x$value.png'; - - Color borderColor = themePrimaryColor; - - if (myProvider.tilesCount == value) { - borderColor = Colors.white; - } - - return TextButton( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: borderColor, - width: 4, - ), - ), - child: Image( - image: AssetImage(assetName), - fit: BoxFit.fill, - ), - ), - onPressed: () { - myProvider.updateTilesCount(value); - }, - ); - } - - @override - Widget build(BuildContext context) { - final Data myProvider = Provider.of<Data>(context); - - Widget content; - - myProvider - .updateTileImageSize((MediaQuery.of(context).size.width - 70) / myProvider.tilesCount); - - if (myProvider.isShufflingBoard) { - content = Game.buildShufflingIndicatorWidget(); - precacheImage(const AssetImage('assets/icons/game_win.png'), context); - } else { - if (myProvider.selectedImage == '') { - content = _buildImageSelector(myProvider); - } else { - content = Game.buildGameWidget(myProvider); - } - } - - final List<Widget> menuActions = [ - _buildTilesetSizeSelectorItem(myProvider, 3), - _buildTilesetSizeSelectorItem(myProvider, 4), - _buildTilesetSizeSelectorItem(myProvider, 5), - TextButton( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: themePrimaryColor, - width: 4, - ), - ), - child: const Image( - image: AssetImage('assets/icons/button_shuffle.png'), - fit: BoxFit.fill, - ), - ), - onPressed: () => shuffleAvailableImages(myProvider), - ), - TextButton( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: themePrimaryColor, - width: 4, - ), - ), - child: const Image( - image: AssetImage('assets/icons/button_random_pick.png'), - fit: BoxFit.fill, - ), - ), - onPressed: () => GameUtils.startRandomGame(myProvider), - ), - ]; - - final List<Widget> gameActions = [ - TextButton( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Colors.blue, - width: 4, - ), - ), - child: const Image( - image: AssetImage('assets/icons/button_back.png'), - fit: BoxFit.fill, - ), - ), - onPressed: () => GameUtils.resetGame(myProvider), - ), - ]; - - return Scaffold( - appBar: AppBar( - backgroundColor: themePrimaryColor, - actions: myProvider.selectedImage == '' ? menuActions : gameActions, - ), - body: SafeArea( - child: Center(child: content), - ), - ); - } -} diff --git a/lib/ui/game/game_bottom.dart b/lib/ui/game/game_bottom.dart new file mode 100644 index 0000000000000000000000000000000000000000..3fb6d305abb6c129d9725fa7baa16662ef8e3708 --- /dev/null +++ b/lib/ui/game/game_bottom.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/widgets/game/game_tip.dart'; + +class GameBottomWidget extends StatelessWidget { + const GameBottomWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return (!currentGame.isFinished && !currentGame.shufflingInProgress) + ? const GameTipWidget() + : const SizedBox.shrink(); + }, + ); + } +} diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart new file mode 100644 index 0000000000000000000000000000000000000000..ab071e10e070f4a9414c4173e414c7aca1f856fe --- /dev/null +++ b/lib/ui/game/game_end.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_game_quit.dart'; + +class GameEndWidget extends StatelessWidget { + const GameEndWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + const Image decorationImage = Image( + image: AssetImage('assets/ui/game_win.png'), + fit: BoxFit.fill, + ); + + return Container( + margin: const EdgeInsets.all(2), + padding: const EdgeInsets.all(2), + child: Table( + defaultColumnWidth: const IntrinsicColumnWidth(), + children: [ + TableRow( + children: [ + const Column( + children: [decorationImage], + ), + Column( + children: [ + currentGame.animationInProgress == true + ? decorationImage + : const QuitGameButton() + ], + ), + const Column( + children: [decorationImage], + ), + ], + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/game/game_top.dart b/lib/ui/game/game_top.dart new file mode 100644 index 0000000000000000000000000000000000000000..504e0a6164ac3c317c5c68d03b0e593f302f8f26 --- /dev/null +++ b/lib/ui/game/game_top.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; + +class GameTopWidget extends StatelessWidget { + const GameTopWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return currentGame.shufflingInProgress + ? const SizedBox.shrink() + : Text( + '${currentGame.movesCount}', + style: const TextStyle( + fontSize: 45, + fontWeight: FontWeight.w600, + ), + ); + }, + ); + } +} diff --git a/lib/ui/helpers/app_titles.dart b/lib/ui/helpers/app_titles.dart new file mode 100644 index 0000000000000000000000000000000000000000..c3ddf3cce4b952ee0674e8553f6caa0bf4794236 --- /dev/null +++ b/lib/ui/helpers/app_titles.dart @@ -0,0 +1,33 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; + +class AppHeader extends StatelessWidget { + const AppHeader({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) { + return Text( + tr(text), + textAlign: TextAlign.start, + style: + Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2), + ); + } +} + +class AppTitle extends StatelessWidget { + const AppTitle({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) { + return Text( + tr(text), + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2), + ); + } +} diff --git a/lib/ui/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart new file mode 100644 index 0000000000000000000000000000000000000000..54ff6f748a4157ec8789a2039062901481d91861 --- /dev/null +++ b/lib/ui/helpers/outlined_text_widget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import 'package:puzzlegame/utils/color_extensions.dart'; + +class OutlinedText extends StatelessWidget { + const OutlinedText({ + super.key, + required this.text, + required this.fontSize, + required this.textColor, + this.outlineColor, + }); + + final String text; + final double fontSize; + final Color textColor; + final Color? outlineColor; + + @override + Widget build(BuildContext context) { + final double delta = fontSize / 30; + + return Text( + text, + style: TextStyle( + inherit: true, + fontSize: fontSize, + fontWeight: FontWeight.w600, + color: textColor, + shadows: [ + Shadow( + offset: Offset(-delta, -delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(delta, -delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(delta, delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(-delta, delta), + color: outlineColor ?? textColor.darken(), + ), + ], + ), + ); + } +} diff --git a/lib/ui/layouts/game_layout.dart b/lib/ui/layouts/game_layout.dart new file mode 100644 index 0000000000000000000000000000000000000000..ed8f35329b799731dc7953379179e8db97cb7204 --- /dev/null +++ b/lib/ui/layouts/game_layout.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/game/game_bottom.dart'; +import 'package:puzzlegame/ui/game/game_end.dart'; +import 'package:puzzlegame/ui/game/game_top.dart'; +import 'package:puzzlegame/ui/widgets/game/game_board.dart'; + +class GameLayout extends StatelessWidget { + const GameLayout({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return Container( + alignment: AlignmentDirectional.topCenter, + padding: const EdgeInsets.all(4), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const GameTopWidget(), + const SizedBox(height: 8), + const GameBoardWidget(), + const SizedBox(height: 8), + const GameBottomWidget(), + const Expanded(child: SizedBox.shrink()), + currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/layouts/parameters_layout.dart b/lib/ui/layouts/parameters_layout.dart new file mode 100644 index 0000000000000000000000000000000000000000..15aae8142dd31bcdb453ce2d96450750406592b5 --- /dev/null +++ b/lib/ui/layouts/parameters_layout.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/config/default_game_settings.dart'; +import 'package:puzzlegame/config/default_global_settings.dart'; +import 'package:puzzlegame/cubit/settings_game_cubit.dart'; +import 'package:puzzlegame/cubit/settings_global_cubit.dart'; +import 'package:puzzlegame/ui/parameters/parameter_image.dart'; +import 'package:puzzlegame/ui/parameters/parameter_painter.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_game_start_new.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_resume_saved_game.dart'; + +class ParametersLayout extends StatelessWidget { + const ParametersLayout({super.key, required this.canResume}); + + final bool canResume; + + final double separatorHeight = 8.0; + + @override + Widget build(BuildContext context) { + final List<Widget> lines = []; + + // Game settings + for (String code in DefaultGameSettings.availableParameters) { + lines.add(Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: buildParametersLine( + code: code, + isGlobal: false, + ), + )); + + lines.add(SizedBox(height: separatorHeight)); + } + + lines.add(SizedBox(height: separatorHeight)); + + if (canResume == false) { + // Start new game + lines.add(const Expanded( + child: StartNewGameButton(), + )); + } else { + // Resume game + lines.add(const Expanded( + child: ResumeSavedGameButton(), + )); + // Delete saved game + lines.add(SizedBox.square( + dimension: MediaQuery.of(context).size.width / 4, + child: const DeleteSavedGameButton(), + )); + } + + lines.add(SizedBox(height: separatorHeight)); + + // Global settings + for (String code in DefaultGlobalSettings.availableParameters) { + lines.add(Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: buildParametersLine( + code: code, + isGlobal: true, + ), + )); + + lines.add(SizedBox(height: separatorHeight)); + } + + return Column( + children: lines, + ); + } + + List<Widget> buildParametersLine({ + required String code, + required bool isGlobal, + }) { + final List<Widget> parameterButtons = []; + + final List<String> availableValues = isGlobal + ? DefaultGlobalSettings.getAvailableValues(code) + : DefaultGameSettings.getAvailableValues(code); + + if (availableValues.length <= 1) { + return []; + } + + for (String value in availableValues) { + final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>( + builder: (BuildContext context, GameSettingsState gameSettingsState) { + return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( + builder: (BuildContext context, GlobalSettingsState globalSettingsState) { + final GameSettingsCubit gameSettingsCubit = + BlocProvider.of<GameSettingsCubit>(context); + final GlobalSettingsCubit globalSettingsCubit = + BlocProvider.of<GlobalSettingsCubit>(context); + + final String currentValue = isGlobal + ? globalSettingsCubit.getParameterValue(code) + : gameSettingsCubit.getParameterValue(code); + + final bool isActive = (value == currentValue); + + final double displayWidth = MediaQuery.of(context).size.width; + final double itemWidth = displayWidth / availableValues.length - 26; + + final bool displayedWithAssets = + DefaultGlobalSettings.displayedWithAssets.contains(code) || + DefaultGameSettings.displayedWithAssets.contains(code); + + return TextButton( + child: Container( + child: displayedWithAssets + ? SizedBox.square( + dimension: itemWidth, + child: ParameterImage( + code: code, + value: value, + isSelected: isActive, + ), + ) + : CustomPaint( + size: Size(itemWidth, itemWidth), + willChange: false, + painter: ParameterPainter( + code: code, + value: value, + isSelected: isActive, + gameSettings: gameSettingsState.settings, + globalSettings: globalSettingsState.settings, + ), + isComplex: true, + ), + ), + onPressed: () { + isGlobal + ? globalSettingsCubit.setParameterValue(code, value) + : gameSettingsCubit.setParameterValue(code, value); + }, + ); + }, + ); + }, + ); + + parameterButtons.add(parameterButton); + } + + return parameterButtons; + } +} diff --git a/lib/ui/parameters/parameter_image.dart b/lib/ui/parameters/parameter_image.dart new file mode 100644 index 0000000000000000000000000000000000000000..136518953f1b73f314a9bfedd7d5cd4a53c1fd00 --- /dev/null +++ b/lib/ui/parameters/parameter_image.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class ParameterImage extends StatelessWidget { + const ParameterImage({ + super.key, + required this.code, + required this.value, + required this.isSelected, + }); + + final String code; + final String value; + final bool isSelected; + + static const Color buttonBackgroundColor = Colors.white; + static const Color buttonBorderColorActive = Colors.blue; + static const Color buttonBorderColorInactive = Colors.white; + static const double buttonBorderWidth = 8.0; + static const double buttonBorderRadius = 8.0; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: buttonBackgroundColor, + borderRadius: BorderRadius.circular(buttonBorderRadius), + border: Border.all( + color: + isSelected ? buttonBorderColorActive : buttonBorderColorInactive, + width: buttonBorderWidth, + ), + ), + child: Image( + image: AssetImage('assets/ui/${code}_$value.png'), + fit: BoxFit.fill, + ), + ); + } +} diff --git a/lib/ui/parameters/parameter_painter.dart b/lib/ui/parameters/parameter_painter.dart new file mode 100644 index 0000000000000000000000000000000000000000..9ac8eea99859cb7121633865d50e810885ab4199 --- /dev/null +++ b/lib/ui/parameters/parameter_painter.dart @@ -0,0 +1,151 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import 'package:puzzlegame/config/default_game_settings.dart'; +import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/models/settings/settings_global.dart'; +import 'package:puzzlegame/utils/tools.dart'; + +class ParameterPainter extends CustomPainter { + const ParameterPainter({ + required this.code, + required this.value, + required this.isSelected, + required this.gameSettings, + required this.globalSettings, + }); + + final String code; + final String value; + final bool isSelected; + final GameSettings gameSettings; + final GlobalSettings globalSettings; + + @override + void paint(Canvas canvas, Size size) { + // force square + final double canvasSize = min(size.width, size.height); + + const Color borderColorEnabled = Colors.blue; + const Color borderColorDisabled = Colors.white; + + // "enabled/disabled" border + final paint = Paint(); + paint.style = PaintingStyle.stroke; + paint.color = isSelected ? borderColorEnabled : borderColorDisabled; + paint.strokeJoin = StrokeJoin.round; + paint.strokeWidth = 10; + canvas.drawRect( + Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint); + + // content + switch (code) { + case DefaultGameSettings.parameterCodeTilesetSize: + paintTilesetSizeParameterItem(value, canvas, canvasSize); + break; + default: + printlog('Unknown parameter: $code/$value'); + paintUnknownParameterItem(value, canvas, canvasSize); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } + + // "unknown" parameter -> simple block with text + void paintUnknownParameterItem( + final String value, + final Canvas canvas, + final double size, + ) { + final paint = Paint(); + paint.strokeJoin = StrokeJoin.round; + paint.strokeWidth = 3; + + paint.color = Colors.grey; + paint.style = PaintingStyle.fill; + canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint); + + final textSpan = TextSpan( + text: '?\n$value', + style: const TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ); + final textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + textAlign: TextAlign.center, + ); + textPainter.layout(); + textPainter.paint( + canvas, + Offset( + (size - textPainter.width) * 0.5, + (size - textPainter.height) * 0.5, + ), + ); + } + + void paintTilesetSizeParameterItem( + final String value, + final Canvas canvas, + final double size, + ) { + Color backgroundColor = Colors.grey; + final int gridSize = int.parse(value.split('x')[0]); + + switch (value) { + case DefaultGameSettings.tilesetSizeValueSmall: + backgroundColor = Colors.green; + break; + case DefaultGameSettings.tilesetSizeValueMedium: + backgroundColor = Colors.orange; + break; + case DefaultGameSettings.tilesetSizeValueLarge: + backgroundColor = Colors.red; + break; + default: + printlog('Wrong value for size parameter value: $value'); + } + + final paint = Paint(); + paint.strokeJoin = StrokeJoin.round; + paint.strokeWidth = 3; + + // Colored background + paint.color = backgroundColor; + paint.style = PaintingStyle.fill; + canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint); + + // Mini grid + // TODO: add image + final borderColor = Colors.grey.shade800; + + final drawSize = size * 0.7; + + final double cellSize = drawSize / gridSize; + final double originX = (size - gridSize * cellSize) / 2; + final double originY = (size - gridSize * cellSize) / 2; + + for (int row = 0; row < gridSize; row++) { + for (int col = 0; col < gridSize; col++) { + final Offset topLeft = Offset(originX + col * cellSize, originY + row * cellSize); + final Offset bottomRight = topLeft + Offset(cellSize, cellSize); + + paint.color = Colors.grey.shade200; + paint.style = PaintingStyle.fill; + canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint); + + paint.color = borderColor; + paint.style = PaintingStyle.stroke; + canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint); + } + } + } +} diff --git a/lib/ui/screens/page_about.dart b/lib/ui/screens/page_about.dart new file mode 100644 index 0000000000000000000000000000000000000000..6716b349eb595de1c62c24cc6713b44e379d0f7f --- /dev/null +++ b/lib/ui/screens/page_about.dart @@ -0,0 +1,41 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +import 'package:puzzlegame/ui/helpers/app_titles.dart'; + +class PageAbout extends StatelessWidget { + const PageAbout({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + const SizedBox(height: 8), + const AppTitle(text: 'about_title'), + const Text('about_content').tr(), + FutureBuilder<PackageInfo>( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.done: + return const Text('about_version').tr( + namedArgs: { + 'version': snapshot.data!.version, + }, + ); + default: + return const SizedBox(); + } + }, + ), + ], + ), + ); + } +} diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..d4661980dc22b9528ba990c0bed6552f12cf7864 --- /dev/null +++ b/lib/ui/screens/page_game.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/layouts/game_layout.dart'; +import 'package:puzzlegame/ui/layouts/parameters_layout.dart'; + +class PageGame extends StatelessWidget { + const PageGame({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return currentGame.isRunning + ? const GameLayout() + : ParametersLayout(canResume: currentGame.canBeResumed); + }, + ); + } +} diff --git a/lib/ui/screens/page_settings.dart b/lib/ui/screens/page_settings.dart new file mode 100644 index 0000000000000000000000000000000000000000..abc6288e3d125c1db006de636b804b81eb9ee5ca --- /dev/null +++ b/lib/ui/screens/page_settings.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +import 'package:puzzlegame/ui/helpers/app_titles.dart'; +import 'package:puzzlegame/ui/settings/settings_form.dart'; + +class PageSettings extends StatelessWidget { + const PageSettings({super.key}); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + SizedBox(height: 8), + AppTitle(text: 'settings_title'), + SizedBox(height: 8), + SettingsForm(), + ], + ), + ); + } +} diff --git a/lib/ui/settings/settings_form.dart b/lib/ui/settings/settings_form.dart new file mode 100644 index 0000000000000000000000000000000000000000..e4e513f0e67174394667df6b1384190f2941bdde --- /dev/null +++ b/lib/ui/settings/settings_form.dart @@ -0,0 +1,63 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:unicons/unicons.dart'; + +import 'package:puzzlegame/ui/settings/theme_card.dart'; + +class SettingsForm extends StatefulWidget { + const SettingsForm({super.key}); + + @override + State<SettingsForm> createState() => _SettingsFormState(); +} + +class _SettingsFormState extends State<SettingsForm> { + @override + void dispose() { + super.dispose(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + // Light/dark theme + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + const Text('settings_label_theme').tr(), + const Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ThemeCard( + mode: ThemeMode.system, + icon: UniconsLine.cog, + ), + ThemeCard( + mode: ThemeMode.light, + icon: UniconsLine.sun, + ), + ThemeCard( + mode: ThemeMode.dark, + icon: UniconsLine.moon, + ) + ], + ), + ], + ), + + const SizedBox(height: 16), + ], + ); + } +} diff --git a/lib/ui/settings/theme_card.dart b/lib/ui/settings/theme_card.dart new file mode 100644 index 0000000000000000000000000000000000000000..1f4402957890c05e9ba81736ecfdf5fc1b7b90e3 --- /dev/null +++ b/lib/ui/settings/theme_card.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/theme_cubit.dart'; + +class ThemeCard extends StatelessWidget { + const ThemeCard({ + super.key, + required this.mode, + required this.icon, + }); + + final IconData icon; + final ThemeMode mode; + + @override + Widget build(BuildContext context) { + return BlocBuilder<ThemeCubit, ThemeModeState>( + builder: (BuildContext context, ThemeModeState state) { + return Card( + elevation: 2, + shadowColor: Theme.of(context).colorScheme.shadow, + color: state.themeMode == mode + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surface, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + margin: const EdgeInsets.all(5), + child: InkWell( + onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme( + ThemeModeState(themeMode: mode), + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Icon( + icon, + size: 32, + color: state.themeMode != mode + ? Theme.of(context).colorScheme.primary + : Colors.white, + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart new file mode 100644 index 0000000000000000000000000000000000000000..257ed0b335c25d4ccbdf786f2dab82a2f8f2fcec --- /dev/null +++ b/lib/ui/skeleton.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/config/menu.dart'; +import 'package:puzzlegame/cubit/nav_cubit.dart'; +import 'package:puzzlegame/ui/widgets/global_app_bar.dart'; + +class SkeletonScreen extends StatelessWidget { + const SkeletonScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const GlobalAppBar(), + extendBodyBehindAppBar: false, + body: Material( + color: Theme.of(context).colorScheme.surface, + child: BlocBuilder<NavCubit, int>( + builder: (BuildContext context, int pageIndex) { + return Padding( + padding: const EdgeInsets.only( + top: 8, + left: 2, + right: 2, + ), + child: Menu.getPageWidget(pageIndex), + ); + }, + ), + ), + backgroundColor: Theme.of(context).colorScheme.surface, + ); + } +} diff --git a/lib/ui/widgets/actions/button_delete_saved_game.dart b/lib/ui/widgets/actions/button_delete_saved_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..564f0c2529be890997be8fdf5fdfbee996c51cb7 --- /dev/null +++ b/lib/ui/widgets/actions/button_delete_saved_game.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; + +class DeleteSavedGameButton extends StatelessWidget { + const DeleteSavedGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_delete_saved_game.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).deleteSavedGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/actions/button_game_quit.dart b/lib/ui/widgets/actions/button_game_quit.dart new file mode 100644 index 0000000000000000000000000000000000000000..5a11378f5a6fbcbec84a0878ba29e091c24cdc25 --- /dev/null +++ b/lib/ui/widgets/actions/button_game_quit.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; + +class QuitGameButton extends StatelessWidget { + const QuitGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_back.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).quitGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/actions/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart new file mode 100644 index 0000000000000000000000000000000000000000..ce0170528901aa65ab6c658c533cdb7365a95398 --- /dev/null +++ b/lib/ui/widgets/actions/button_game_start_new.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/cubit/settings_game_cubit.dart'; +import 'package:puzzlegame/cubit/settings_global_cubit.dart'; + +class StartNewGameButton extends StatelessWidget { + const StartNewGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameSettingsCubit, GameSettingsState>( + builder: (BuildContext context, GameSettingsState gameSettingsState) { + return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( + builder: (BuildContext context, GlobalSettingsState globalSettingsState) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_start.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).startNewGame( + gameSettings: gameSettingsState.settings, + globalSettings: globalSettingsState.settings, + ); + }, + ); + }, + ); + }, + ); + } +} diff --git a/lib/ui/widgets/actions/button_resume_saved_game.dart b/lib/ui/widgets/actions/button_resume_saved_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..b90f57b157dea14e3b9d6a21ed62a9d3f126b538 --- /dev/null +++ b/lib/ui/widgets/actions/button_resume_saved_game.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; + +class ResumeSavedGameButton extends StatelessWidget { + const ResumeSavedGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_resume_game.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).resumeSavedGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/game/game.dart b/lib/ui/widgets/game/game.dart new file mode 100644 index 0000000000000000000000000000000000000000..5f280104f5aaf54b1bd5f4802dee7a452883d5e1 --- /dev/null +++ b/lib/ui/widgets/game/game.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +import 'package:puzzlegame/ui/game/game_top.dart'; +import 'package:puzzlegame/ui/widgets/game/game_tileset.dart'; + +class GameWidget extends StatelessWidget { + const GameWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 8), + const GameTopWidget(), + const SizedBox(height: 2), + Expanded( + child: Container( + margin: const EdgeInsets.all(4), + padding: const EdgeInsets.all(4), + child: const Column( + children: [ + GameTilesetwidget(), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart new file mode 100644 index 0000000000000000000000000000000000000000..ea15c33e1f93f3a78325dbab33648ad993fe5820 --- /dev/null +++ b/lib/ui/widgets/game/game_board.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/widgets/game/game_shuffling.dart'; +import 'package:puzzlegame/ui/widgets/game/game_tileset.dart'; + +class GameBoardWidget extends StatelessWidget { + const GameBoardWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Center( + child: BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + BlocProvider.of<GameCubit>(context).updateTileSize( + (MediaQuery.of(context).size.width - 60) / + currentGame.gameSettings.tilesetSizeValue); + + if (currentGame.shufflingInProgress) { + return const GameShufflingWidget(); + } else { + return const GameTilesetwidget(); + } + }, + ), + ); + } +} diff --git a/lib/ui/widgets/game/game_shuffling.dart b/lib/ui/widgets/game/game_shuffling.dart new file mode 100644 index 0000000000000000000000000000000000000000..e02d78cf18ef71e45001c13555352a144621822c --- /dev/null +++ b/lib/ui/widgets/game/game_shuffling.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class GameShufflingWidget extends StatelessWidget { + const GameShufflingWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'â³', + style: TextStyle( + fontSize: 60, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + SizedBox(height: 20), + CircularProgressIndicator(), + ], + ); + } +} diff --git a/lib/entities/moving_tile.dart b/lib/ui/widgets/game/game_tile.dart similarity index 52% rename from lib/entities/moving_tile.dart rename to lib/ui/widgets/game/game_tile.dart index 99fa0a94999fb974ccaeab42f76576e9823cd36b..c3bf98c5ca8f271676b03e537d7b843d597ecbf7 100644 --- a/lib/entities/moving_tile.dart +++ b/lib/ui/widgets/game/game_tile.dart @@ -1,41 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:puzzlegame/entities/tile.dart'; -import 'package:puzzlegame/provider/data.dart'; +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/moving_tile.dart'; -class MovingTile extends Tile { - int currentCol; - int currentRow; +class GameTileWidget extends StatelessWidget { + const GameTileWidget({super.key, required this.tile}); - MovingTile({ - required this.currentCol, - required this.currentRow, - required super.image, - required super.size, - required super.originalCol, - required super.originalRow, - }); - - Container _tileWidget() { - return Container( - decoration: BoxDecoration( - color: Colors.black, - border: Border.all( - color: Colors.black, - width: 1, - ), - ), - child: Image( - image: image.image, - width: size, - height: size, - fit: BoxFit.fill, - ), - ); - } + final MovingTile tile; @override - Widget widget(Data myProvider) { + Widget build(BuildContext context) { return DragTarget<List<int>>( builder: ( BuildContext context, @@ -43,38 +18,52 @@ class MovingTile extends Tile { List<dynamic> rejected, ) { return Container( - height: size, - width: size, + height: tile.size, + width: tile.size, color: Colors.cyan, child: Draggable<List<int>>( data: [ - currentCol, - currentRow, + tile.currentCol, + tile.currentRow, ], // Widget when draggable is being dragged - feedback: _tileWidget(), + feedback: staticTile(), // Widget to display on original place when being dragged childWhenDragging: Container( - height: size, - width: size, + height: tile.size, + width: tile.size, color: Colors.pinkAccent, ), // Widget when draggable is stationary - child: _tileWidget(), + child: staticTile(), ), ); }, onAcceptWithDetails: (DragTargetDetails<List<int>> source) { - myProvider.swapTiles([currentCol, currentRow], source.data); + BlocProvider.of<GameCubit>(context) + .swapTiles([tile.currentCol, tile.currentRow], source.data); }, ); } - @override - bool isCorrect() { - return ((currentRow == originalRow) && (currentCol == originalCol)); + Container staticTile() { + return Container( + decoration: BoxDecoration( + color: Colors.black, + border: Border.all( + color: Colors.black, + width: 1, + ), + ), + child: Image( + image: tile.image.image, + width: tile.size, + height: tile.size, + fit: BoxFit.fill, + ), + ); } } diff --git a/lib/ui/widgets/game/game_tileset.dart b/lib/ui/widgets/game/game_tileset.dart new file mode 100644 index 0000000000000000000000000000000000000000..fdeeb9fb1c968135eee81aa03525830c34814d4b --- /dev/null +++ b/lib/ui/widgets/game/game_tileset.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/models/game/moving_tile.dart'; +import 'package:puzzlegame/ui/widgets/game/game_tile.dart'; + +class GameTilesetwidget extends StatelessWidget { + const GameTilesetwidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + final Color borderColor = currentGame.isFinished ? Colors.green : Colors.orange; + final int tilesCount = currentGame.gameSettings.tilesetSizeValue; + + final List<MovingTile> tiles = currentGame.tiles; + int tileIndex = 0; + + return Container( + margin: const EdgeInsets.all(8), + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: borderColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: borderColor, + width: 8, + ), + ), + child: Table( + defaultColumnWidth: const IntrinsicColumnWidth(), + border: TableBorder.all( + color: Colors.black, + style: BorderStyle.solid, + width: 2, + ), + children: [ + for (int row = 0; row < tilesCount; row++) + TableRow( + children: [ + for (int col = 0; col < tilesCount; col++) + Column( + children: [ + GameTileWidget( + tile: tiles[tileIndex++], + ) + ], + ), + ], + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/widgets/game/game_tip.dart b/lib/ui/widgets/game/game_tip.dart new file mode 100644 index 0000000000000000000000000000000000000000..dc5a3d181931fc03a5f2ec8b0ecbb92cda4d5d5d --- /dev/null +++ b/lib/ui/widgets/game/game_tip.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; + +class GameTipWidget extends StatelessWidget { + const GameTipWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + final String asset = 'assets/images/${currentGame.image}.png'; + precacheImage(AssetImage(asset), context); + + return TextButton( + child: Container( + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: Colors.blue, + width: 4, + ), + ), + child: Image( + height: currentGame.tileSize, + image: AssetImage( + currentGame.displayTip ? asset : 'assets/ui/tip_hidden.png', + ), + fit: BoxFit.fill, + ), + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).toggleDisplayTipImage(); + }, + ); + }, + ); + } +} diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart new file mode 100644 index 0000000000000000000000000000000000000000..e2cfcb4e372486b78b942841b6b42013eaec095a --- /dev/null +++ b/lib/ui/widgets/global_app_bar.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/config/menu.dart'; +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/cubit/nav_cubit.dart'; +import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/ui/helpers/app_titles.dart'; + +class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { + const GlobalAppBar({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + return BlocBuilder<NavCubit, int>( + builder: (BuildContext context, int pageIndex) { + final Game currentGame = gameState.currentGame; + + final List<Widget> menuActions = []; + + if (currentGame.isRunning && !currentGame.isFinished) { + menuActions.add(TextButton( + child: const Image( + image: AssetImage('assets/ui/button_back.png'), + fit: BoxFit.fill, + ), + onPressed: () {}, + onLongPress: () { + BlocProvider.of<GameCubit>(context).quitGame(); + }, + )); + } else { + if (pageIndex == Menu.indexGame) { + // go to Settings page + menuActions.add(ElevatedButton( + onPressed: () { + context.read<NavCubit>().goToSettingsPage(); + }, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + ), + child: Menu.menuItemSettings.icon, + )); + + // go to About page + menuActions.add(ElevatedButton( + onPressed: () { + context.read<NavCubit>().goToAboutPage(); + }, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + ), + child: Menu.menuItemAbout.icon, + )); + } else { + // back to Home page + menuActions.add(ElevatedButton( + onPressed: () { + context.read<NavCubit>().goToGamePage(); + }, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + ), + child: Menu.menuItemGame.icon, + )); + } + } + + return AppBar( + title: const AppHeader(text: 'app_name'), + actions: menuActions, + ); + }, + ); + }, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(50); +} diff --git a/lib/ui/widgets/indicators/indicator_score.dart b/lib/ui/widgets/indicators/indicator_score.dart new file mode 100644 index 0000000000000000000000000000000000000000..349f715b1e0990beeb38b224b1e1d3679a9cc7cc --- /dev/null +++ b/lib/ui/widgets/indicators/indicator_score.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/ui/helpers/outlined_text_widget.dart'; +import 'package:puzzlegame/utils/color_extensions.dart'; + +class ScoreIndicator extends StatelessWidget { + const ScoreIndicator({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + const Color baseColor = Color.fromARGB(255, 218, 218, 218); + final Color outlineColor = baseColor.darken(); + + return OutlinedText( + text: gameState.currentGame.movesCount.toString(), + fontSize: 70, + textColor: baseColor, + outlineColor: outlineColor, + ); + }, + ); + } +} diff --git a/lib/utils/color_extensions.dart b/lib/utils/color_extensions.dart new file mode 100644 index 0000000000000000000000000000000000000000..4e55e338f0d3ed98b233d1ef887b7b3e17e29d97 --- /dev/null +++ b/lib/utils/color_extensions.dart @@ -0,0 +1,33 @@ +import 'dart:ui'; + +extension ColorExtension on Color { + Color darken([int percent = 40]) { + assert(1 <= percent && percent <= 100); + final value = 1 - percent / 100; + return Color.fromARGB( + alpha, + (red * value).round(), + (green * value).round(), + (blue * value).round(), + ); + } + + Color lighten([int percent = 40]) { + assert(1 <= percent && percent <= 100); + final value = percent / 100; + return Color.fromARGB( + alpha, + (red + ((255 - red) * value)).round(), + (green + ((255 - green) * value)).round(), + (blue + ((255 - blue) * value)).round(), + ); + } + + Color avg(Color other) { + final red = (this.red + other.red) ~/ 2; + final green = (this.green + other.green) ~/ 2; + final blue = (this.blue + other.blue) ~/ 2; + final alpha = (this.alpha + other.alpha) ~/ 2; + return Color.fromARGB(alpha, red, green, blue); + } +} diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart deleted file mode 100644 index db7d57ae59782558acb602f567a1ec80345668fe..0000000000000000000000000000000000000000 --- a/lib/utils/game_utils.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:async'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show rootBundle; -import 'package:image/image.dart' as imglib; - -import 'package:puzzlegame/entities/moving_tile.dart'; -import 'package:puzzlegame/entities/tile.dart'; -import 'package:puzzlegame/provider/data.dart'; - -class GameUtils { - static String getImageAssetName(String imageCode) { - return 'assets/images/$imageCode.png'; - } - - static startGame(Data myProvider, String imageCode) { - myProvider.updateIsShufflingBoard(true); - myProvider.updateSelectedImage(imageCode); - - Timer(const Duration(seconds: 1), () { - GameUtils.splitImageInTiles(myProvider); - }); - } - - static startRandomGame(Data myProvider) { - final List<int> sizes = [3, 4, 5]; - sizes.shuffle(); - myProvider.updateTilesCount(sizes[0]); - - final List<String> images = myProvider.availableImages; - images.shuffle(); - - startGame(myProvider, images[0]); - } - - static Future<void> resetGame(Data myProvider) async { - myProvider.resetSelectedImage(); - myProvider.updateIsTipImageDisplayed(false); - } - - static bool checkTilesetIsCleared(List<MovingTile> tiles) { - for (Tile tile in tiles) { - if (!tile.isCorrect()) { - return false; - } - } - return true; - } - - static List<MovingTile> shuffleMovingTiles(List<MovingTile> tiles) { - final Random random = Random(); - final int tilesCount = tiles.length; - - for (int i = 0; i < (10 * tilesCount); i++) { - final int indexTile1 = random.nextInt(tilesCount); - final int indexTile2 = random.nextInt(tilesCount); - - final MovingTile swap = tiles[indexTile1]; - tiles[indexTile1] = tiles[indexTile2]; - tiles[indexTile2] = swap; - - final int swapCol = tiles[indexTile1].currentCol; - tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; - tiles[indexTile2].currentCol = swapCol; - - final int swapRow = tiles[indexTile1].currentRow; - tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; - tiles[indexTile2].currentRow = swapRow; - } - - return tiles; - } - - static Future<void> splitImageInTiles(Data myProvider) async { - final String imageAsset = getImageAssetName(myProvider.selectedImage); - final Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); - - final imglib.Image image = imglib.decodeImage(imageData) ?? - imglib.Image.fromBytes( - height: 1, - width: 1, - bytes: Uint8List.fromList([]).buffer, - ); - - int x = 0, y = 0; - final int width = (image.width / myProvider.tilesCount).round(); - final int height = (image.height / myProvider.tilesCount).round(); - - final List<MovingTile> tiles = []; - for (int i = 0; i < myProvider.tilesCount; i++) { - for (int j = 0; j < myProvider.tilesCount; j++) { - final Uint8List tileData = Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( - image, - x: x, - y: y, - width: width, - height: height, - ))); - - tiles.add(MovingTile( - currentCol: j, - currentRow: i, - image: Image.memory(tileData), - size: myProvider.tileImageSize, - originalCol: j, - originalRow: i, - )); - - x += width; - } - x = 0; - y += height; - } - - myProvider.updateTiles(shuffleMovingTiles(tiles)); - myProvider.updateIsShufflingBoard(false); - } -} diff --git a/lib/utils/get_images_list.dart b/lib/utils/get_images_list.dart deleted file mode 100644 index ea5210d3c7e8910130aa5955b76387eef2689808..0000000000000000000000000000000000000000 --- a/lib/utils/get_images_list.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter/services.dart'; - -class GetImagesList { - GetImagesList(); - - List<String> _availableImages = []; - - init() async { - await imagesFromLocalFile(); - } - - Future<void> imagesFromLocalFile() async { - String jsonString; - try { - jsonString = await rootBundle.loadString('assets/files/images.json'); - final jsonResponse = await json.decode(jsonString); - - List imagesList = jsonResponse['images'] as List; - - for (var image in imagesList) { - _availableImages.add(image.toString()); - } - } catch (e) { - _availableImages = []; - } - - // Remove empty images - _availableImages.removeWhere((value) => (value == '')); - } - - List<String> get availableImages => _availableImages; -} diff --git a/pubspec.lock b/pubspec.lock index fdebf8b0e3e7878829c3a11867fe985dbaf28d88..b504a800ac956512116ad914106027b4bb08ecbe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,34 @@ packages: dependency: transitive description: name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "3.4.10" + version: "3.6.1" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" + source: hosted + version: "8.1.4" characters: dependency: transitive description: @@ -17,22 +41,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - collection: + clock: dependency: transitive description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.18.0" - convert: + version: "1.1.1" + collection: dependency: transitive description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "1.18.0" crypto: dependency: transitive description: @@ -41,43 +65,133 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201 + url: "https://pub.dev" + source: hosted + version: "3.0.7" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.dev" + source: hosted + version: "8.1.6" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + hydrated_bloc: + dependency: "direct main" + description: + name: hydrated_bloc + sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "9.1.5" image: dependency: "direct main" description: name: image - sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" url: "https://pub.dev" source: hosted - version: "4.1.7" - js: + version: "4.2.0" + intl: dependency: transitive description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.19.0" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" material_color_utilities: dependency: transitive description: @@ -90,10 +204,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" nested: dependency: transitive description: @@ -102,6 +216,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 + url: "https://pub.dev" + source: hosted + version: "8.0.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e + url: "https://pub.dev" + source: hosted + version: "3.0.0" path: dependency: transitive description: @@ -110,6 +240,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" + url: "https://pub.dev" + source: hosted + version: "2.2.5" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" petitparser: dependency: transitive description: @@ -118,27 +296,123 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" - pointycastle: + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + plugin_platform_interface: dependency: transitive description: - name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "2.1.8" provider: - dependency: "direct main" + dependency: transitive description: name: provider sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted version: "6.1.2" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" typed_data: dependency: transitive description: @@ -147,6 +421,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + unicons: + dependency: "direct main" + description: + name: unicons + sha256: dbfcf93ff4d4ea19b324113857e358e4882115ab85db04417a4ba1c72b17a670 + url: "https://pub.dev" + source: hosted + version: "2.1.1" vector_math: dependency: transitive description: @@ -155,6 +437,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" xml: dependency: transitive description: @@ -164,5 +470,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=1.16.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 1d3230d7d901f2e4838b47d50eb1374e06cf88c0..5b25af4eacf71594829cf25c052efafef7f3759a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,23 +1,49 @@ name: puzzlegame description: A puzzle game application. -publish_to: 'none' -version: 0.0.61+61 + +publish_to: "none" + +version: 0.1.0+62 environment: - sdk: '^3.0.0' + sdk: "^3.0.0" dependencies: flutter: sdk: flutter + + # base + easy_localization: ^3.0.1 + equatable: ^2.0.5 + flutter_bloc: ^8.1.1 + hive: ^2.2.3 + hydrated_bloc: ^9.0.0 + package_info_plus: ^8.0.0 + path_provider: ^2.0.11 + unicons: ^2.1.1 + + # specific image: ^4.1.3 - provider: ^6.0.5 dev_dependencies: - flutter_lints: ^3.0.1 + flutter_lints: ^4.0.0 flutter: uses-material-design: true assets: - - assets/files/ - - assets/icons/ - assets/images/ + - assets/ui/ + - assets/translations/ + + fonts: + - family: Nunito + fonts: + - asset: assets/fonts/Nunito-Bold.ttf + weight: 700 + - asset: assets/fonts/Nunito-Medium.ttf + weight: 500 + - asset: assets/fonts/Nunito-Regular.ttf + weight: 400 + - asset: assets/fonts/Nunito-Light.ttf + weight: 300 + diff --git a/icons/build_application_icons.sh b/resources/app/build_application_resources.sh similarity index 98% rename from icons/build_application_icons.sh rename to resources/app/build_application_resources.sh index 27dbe2647fe4e6d562fbd99451716d1b7d448570..6d67b8f4f9eca701d1aed7331ef41dfb0bd44f20 100755 --- a/icons/build_application_icons.sh +++ b/resources/app/build_application_resources.sh @@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; } CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -BASE_DIR="$(dirname "${CURRENT_DIR}")" +BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")" SOURCE_ICON="${CURRENT_DIR}/icon.svg" SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg" diff --git a/icons/featureGraphic.svg b/resources/app/featureGraphic.svg similarity index 100% rename from icons/featureGraphic.svg rename to resources/app/featureGraphic.svg diff --git a/icons/icon.svg b/resources/app/icon.svg similarity index 100% rename from icons/icon.svg rename to resources/app/icon.svg diff --git a/resources/build_resources.sh b/resources/build_resources.sh new file mode 100755 index 0000000000000000000000000000000000000000..41cf77292b5e860ceb070e06117af2b3a2d8da6b --- /dev/null +++ b/resources/build_resources.sh @@ -0,0 +1,8 @@ +#! /bin/bash + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +${CURRENT_DIR}/app/build_application_resources.sh +${CURRENT_DIR}/ui/build_ui_resources.sh +${CURRENT_DIR}/data/build_images_assets_list.sh + diff --git a/scripts/.gitignore b/resources/data/.gitignore similarity index 100% rename from scripts/.gitignore rename to resources/data/.gitignore diff --git a/scripts/01_optimize_images.sh b/resources/data/00_optimize_images.sh similarity index 100% rename from scripts/01_optimize_images.sh rename to resources/data/00_optimize_images.sh diff --git a/scripts/02_build_images_assets_list.sh b/resources/data/build_images_assets_list.sh similarity index 63% rename from scripts/02_build_images_assets_list.sh rename to resources/data/build_images_assets_list.sh index 3e9e3db237cf7289b932894354247a9694fa98a4..ddafcb22efa49ac47b2b5bec00e1e157e493cf5a 100755 --- a/scripts/02_build_images_assets_list.sh +++ b/resources/data/build_images_assets_list.sh @@ -3,12 +3,10 @@ command -v jq >/dev/null 2>&1 || { echo >&2 "I require jq (json parser) but it's not installed. Aborting."; exit 1; } CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -BASE_DIR="$(dirname "${CURRENT_DIR}")" +BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")" ASSETS_BASE_FOLDER="${BASE_DIR}/assets" -OUTPUT_ASSETS_FILE="${ASSETS_BASE_FOLDER}/files/images.json" IMAGES_ASSETS_FOLDER="${ASSETS_BASE_FOLDER}/images" -touch "${OUTPUT_ASSETS_FILE}" IMAGES_CACHE_FOLDER="${CURRENT_DIR}/cache" IMAGES_OPTIMIZED_FOLDER="${IMAGES_CACHE_FOLDER}/02_optimized_images" @@ -24,22 +22,25 @@ echo "Building assets json file..." FILES="$(find "${IMAGES_ASSETS_FOLDER}" -type f -name "*.png" | sed "s|^${IMAGES_OPTIMIZED_FOLDER}/||g" | sort)" -OUTPUT_ASSETS_FILE_TMP="${OUTPUT_ASSETS_FILE}.tmp" -echo "{" > "${OUTPUT_ASSETS_FILE_TMP}" -echo " \"images\": [" >> "${OUTPUT_ASSETS_FILE_TMP}" +echo "Injecting json file in game_data.dart..." + +GAME_DATA_DART_FILE="${BASE_DIR}/lib/data/game_data.dart" +echo "class GameData {" >"${GAME_DATA_DART_FILE}" +echo " static const Map<String, dynamic> data = {" >>"${GAME_DATA_DART_FILE}" +echo " \"images\": [" >>"${GAME_DATA_DART_FILE}" while read -r FILE; do FILE_CODE="$(basename "${FILE%.*}")" if [[ -n "${FILE}" ]]; then echo "- ${FILE_CODE}" - echo " \"${FILE_CODE}\"," >> "${OUTPUT_ASSETS_FILE_TMP}" + echo " \"${FILE_CODE}\"," >>"${GAME_DATA_DART_FILE}" fi done < <(echo "${FILES}") -echo " \"\"" >> "${OUTPUT_ASSETS_FILE_TMP}" -echo " ]" >> "${OUTPUT_ASSETS_FILE_TMP}" -echo "}" >> "${OUTPUT_ASSETS_FILE_TMP}" +echo " ]" >>"${GAME_DATA_DART_FILE}" +echo " };" >>"${GAME_DATA_DART_FILE}" + +echo "}" >>"${GAME_DATA_DART_FILE}" -# Format json -cat "${OUTPUT_ASSETS_FILE_TMP}" | jq > "${OUTPUT_ASSETS_FILE}" -rm "${OUTPUT_ASSETS_FILE_TMP}" +echo "Formatting dart source code file..." +dart format "${GAME_DATA_DART_FILE}" echo "done." diff --git a/scripts/cache/01_raw_images/.gitkeep b/resources/data/cache/01_raw_images/.gitkeep similarity index 100% rename from scripts/cache/01_raw_images/.gitkeep rename to resources/data/cache/01_raw_images/.gitkeep diff --git a/scripts/cache/01_raw_images/00_default/19-93194.jpg b/resources/data/cache/01_raw_images/00_default/19-93194.jpg similarity index 100% rename from scripts/cache/01_raw_images/00_default/19-93194.jpg rename to resources/data/cache/01_raw_images/00_default/19-93194.jpg diff --git a/scripts/cache/01_raw_images/00_default/1902316.jpg b/resources/data/cache/01_raw_images/00_default/1902316.jpg similarity index 100% rename from scripts/cache/01_raw_images/00_default/1902316.jpg rename to resources/data/cache/01_raw_images/00_default/1902316.jpg diff --git a/scripts/cache/01_raw_images/00_default/667852_poster_l.jpg b/resources/data/cache/01_raw_images/00_default/667852_poster_l.jpg similarity index 100% rename from scripts/cache/01_raw_images/00_default/667852_poster_l.jpg rename to resources/data/cache/01_raw_images/00_default/667852_poster_l.jpg diff --git a/scripts/cache/01_raw_images/00_default/goodmorningforsta.jpg b/resources/data/cache/01_raw_images/00_default/goodmorningforsta.jpg similarity index 100% rename from scripts/cache/01_raw_images/00_default/goodmorningforsta.jpg rename to resources/data/cache/01_raw_images/00_default/goodmorningforsta.jpg diff --git a/scripts/cache/01_raw_images/animals/1-34816.jpg b/resources/data/cache/01_raw_images/animals/1-34816.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1-34816.jpg rename to resources/data/cache/01_raw_images/animals/1-34816.jpg diff --git a/scripts/cache/01_raw_images/animals/1869714.jpg b/resources/data/cache/01_raw_images/animals/1869714.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1869714.jpg rename to resources/data/cache/01_raw_images/animals/1869714.jpg diff --git a/scripts/cache/01_raw_images/animals/1872282.jpg b/resources/data/cache/01_raw_images/animals/1872282.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1872282.jpg rename to resources/data/cache/01_raw_images/animals/1872282.jpg diff --git a/scripts/cache/01_raw_images/animals/1880455.jpg b/resources/data/cache/01_raw_images/animals/1880455.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1880455.jpg rename to resources/data/cache/01_raw_images/animals/1880455.jpg diff --git a/scripts/cache/01_raw_images/animals/1888275.jpg b/resources/data/cache/01_raw_images/animals/1888275.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1888275.jpg rename to resources/data/cache/01_raw_images/animals/1888275.jpg diff --git a/scripts/cache/01_raw_images/animals/1889220.jpg b/resources/data/cache/01_raw_images/animals/1889220.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1889220.jpg rename to resources/data/cache/01_raw_images/animals/1889220.jpg diff --git a/scripts/cache/01_raw_images/animals/1889565.jpg b/resources/data/cache/01_raw_images/animals/1889565.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/1889565.jpg rename to resources/data/cache/01_raw_images/animals/1889565.jpg diff --git a/scripts/cache/01_raw_images/animals/2-13953.jpg b/resources/data/cache/01_raw_images/animals/2-13953.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/2-13953.jpg rename to resources/data/cache/01_raw_images/animals/2-13953.jpg diff --git a/scripts/cache/01_raw_images/animals/3-54091.jpg b/resources/data/cache/01_raw_images/animals/3-54091.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/3-54091.jpg rename to resources/data/cache/01_raw_images/animals/3-54091.jpg diff --git a/scripts/cache/01_raw_images/animals/681366_poster_l.jpg b/resources/data/cache/01_raw_images/animals/681366_poster_l.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/681366_poster_l.jpg rename to resources/data/cache/01_raw_images/animals/681366_poster_l.jpg diff --git a/scripts/cache/01_raw_images/animals/683592_poster_l.jpg b/resources/data/cache/01_raw_images/animals/683592_poster_l.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/683592_poster_l.jpg rename to resources/data/cache/01_raw_images/animals/683592_poster_l.jpg diff --git a/scripts/cache/01_raw_images/animals/7-24566.jpg b/resources/data/cache/01_raw_images/animals/7-24566.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/7-24566.jpg rename to resources/data/cache/01_raw_images/animals/7-24566.jpg diff --git a/scripts/cache/01_raw_images/animals/Alpaca-In-The-Mountains-Karin-Bijlsma-Affiche.webp.png b/resources/data/cache/01_raw_images/animals/Alpaca-In-The-Mountains-Karin-Bijlsma-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/animals/Alpaca-In-The-Mountains-Karin-Bijlsma-Affiche.webp.png rename to resources/data/cache/01_raw_images/animals/Alpaca-In-The-Mountains-Karin-Bijlsma-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/animals/Being-Lazy-Andy-Westface-Affiche.webp.png b/resources/data/cache/01_raw_images/animals/Being-Lazy-Andy-Westface-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/animals/Being-Lazy-Andy-Westface-Affiche.webp.png rename to resources/data/cache/01_raw_images/animals/Being-Lazy-Andy-Westface-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/animals/Fly-High-Together-Andy-Westface-Affiche.webp.png b/resources/data/cache/01_raw_images/animals/Fly-High-Together-Andy-Westface-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/animals/Fly-High-Together-Andy-Westface-Affiche.webp.png rename to resources/data/cache/01_raw_images/animals/Fly-High-Together-Andy-Westface-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/animals/Me-Time-Andy-Westface-Affiche.webp.png b/resources/data/cache/01_raw_images/animals/Me-Time-Andy-Westface-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/animals/Me-Time-Andy-Westface-Affiche.webp.png rename to resources/data/cache/01_raw_images/animals/Me-Time-Andy-Westface-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/animals/Zebra-Balloon-Paul-Fuentes-Affiche.webp.png b/resources/data/cache/01_raw_images/animals/Zebra-Balloon-Paul-Fuentes-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/animals/Zebra-Balloon-Paul-Fuentes-Affiche.webp.png rename to resources/data/cache/01_raw_images/animals/Zebra-Balloon-Paul-Fuentes-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/animals/panda.jpg b/resources/data/cache/01_raw_images/animals/panda.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/panda.jpg rename to resources/data/cache/01_raw_images/animals/panda.jpg diff --git a/scripts/cache/01_raw_images/animals/rainmaker.jpg b/resources/data/cache/01_raw_images/animals/rainmaker.jpg similarity index 100% rename from scripts/cache/01_raw_images/animals/rainmaker.jpg rename to resources/data/cache/01_raw_images/animals/rainmaker.jpg diff --git a/scripts/cache/01_raw_images/anime/1893736.jpg b/resources/data/cache/01_raw_images/anime/1893736.jpg similarity index 100% rename from scripts/cache/01_raw_images/anime/1893736.jpg rename to resources/data/cache/01_raw_images/anime/1893736.jpg diff --git a/scripts/cache/01_raw_images/anime/1896891.jpg b/resources/data/cache/01_raw_images/anime/1896891.jpg similarity index 100% rename from scripts/cache/01_raw_images/anime/1896891.jpg rename to resources/data/cache/01_raw_images/anime/1896891.jpg diff --git a/scripts/cache/01_raw_images/dinosaurs/1879022.jpg b/resources/data/cache/01_raw_images/dinosaurs/1879022.jpg similarity index 100% rename from scripts/cache/01_raw_images/dinosaurs/1879022.jpg rename to resources/data/cache/01_raw_images/dinosaurs/1879022.jpg diff --git a/scripts/cache/01_raw_images/dinosaurs/Diplodocus-Dieter-Braun-Affiche.webp.png b/resources/data/cache/01_raw_images/dinosaurs/Diplodocus-Dieter-Braun-Affiche.webp.png similarity index 100% rename from scripts/cache/01_raw_images/dinosaurs/Diplodocus-Dieter-Braun-Affiche.webp.png rename to resources/data/cache/01_raw_images/dinosaurs/Diplodocus-Dieter-Braun-Affiche.webp.png diff --git a/scripts/cache/01_raw_images/ghibli/1882483.jpg b/resources/data/cache/01_raw_images/ghibli/1882483.jpg similarity index 100% rename from scripts/cache/01_raw_images/ghibli/1882483.jpg rename to resources/data/cache/01_raw_images/ghibli/1882483.jpg diff --git a/scripts/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8-1.jpg b/resources/data/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8-1.jpg similarity index 100% rename from scripts/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8-1.jpg rename to resources/data/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8-1.jpg diff --git a/scripts/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8.jpg b/resources/data/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8.jpg similarity index 100% rename from scripts/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8.jpg rename to resources/data/cache/01_raw_images/ghibli/flat,750x,075,f-pad,750x1000,f8f8f8.jpg diff --git a/scripts/cache/01_raw_images/ghibli/totoro.png b/resources/data/cache/01_raw_images/ghibli/totoro.png similarity index 100% rename from scripts/cache/01_raw_images/ghibli/totoro.png rename to resources/data/cache/01_raw_images/ghibli/totoro.png diff --git a/scripts/cache/01_raw_images/harrypotter/1893827.jpg b/resources/data/cache/01_raw_images/harrypotter/1893827.jpg similarity index 100% rename from scripts/cache/01_raw_images/harrypotter/1893827.jpg rename to resources/data/cache/01_raw_images/harrypotter/1893827.jpg diff --git a/scripts/cache/01_raw_images/harrypotter/1893838.jpg b/resources/data/cache/01_raw_images/harrypotter/1893838.jpg similarity index 100% rename from scripts/cache/01_raw_images/harrypotter/1893838.jpg rename to resources/data/cache/01_raw_images/harrypotter/1893838.jpg diff --git a/scripts/cache/01_raw_images/nature/11-74513.jpg b/resources/data/cache/01_raw_images/nature/11-74513.jpg similarity index 100% rename from scripts/cache/01_raw_images/nature/11-74513.jpg rename to resources/data/cache/01_raw_images/nature/11-74513.jpg diff --git a/scripts/cache/01_raw_images/nature/5-42180.jpg b/resources/data/cache/01_raw_images/nature/5-42180.jpg similarity index 100% rename from scripts/cache/01_raw_images/nature/5-42180.jpg rename to resources/data/cache/01_raw_images/nature/5-42180.jpg diff --git a/scripts/cache/01_raw_images/nature/8-43315.jpg b/resources/data/cache/01_raw_images/nature/8-43315.jpg similarity index 100% rename from scripts/cache/01_raw_images/nature/8-43315.jpg rename to resources/data/cache/01_raw_images/nature/8-43315.jpg diff --git a/scripts/cache/01_raw_images/personal/00b6f4b4509f4dd42ba672d7d98e8b12.jpg b/resources/data/cache/01_raw_images/personal/00b6f4b4509f4dd42ba672d7d98e8b12.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/00b6f4b4509f4dd42ba672d7d98e8b12.jpg rename to resources/data/cache/01_raw_images/personal/00b6f4b4509f4dd42ba672d7d98e8b12.jpg diff --git a/scripts/cache/01_raw_images/personal/02db820b7bb1747485d006a6c31f3b26.jpg b/resources/data/cache/01_raw_images/personal/02db820b7bb1747485d006a6c31f3b26.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/02db820b7bb1747485d006a6c31f3b26.jpg rename to resources/data/cache/01_raw_images/personal/02db820b7bb1747485d006a6c31f3b26.jpg diff --git a/scripts/cache/01_raw_images/personal/03a5166c2c85336e307c6f7cc19aaa53.jpg b/resources/data/cache/01_raw_images/personal/03a5166c2c85336e307c6f7cc19aaa53.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/03a5166c2c85336e307c6f7cc19aaa53.jpg rename to resources/data/cache/01_raw_images/personal/03a5166c2c85336e307c6f7cc19aaa53.jpg diff --git a/scripts/cache/01_raw_images/personal/0e6b73b5932cd51d9e73b4ee38a15d39.jpg b/resources/data/cache/01_raw_images/personal/0e6b73b5932cd51d9e73b4ee38a15d39.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/0e6b73b5932cd51d9e73b4ee38a15d39.jpg rename to resources/data/cache/01_raw_images/personal/0e6b73b5932cd51d9e73b4ee38a15d39.jpg diff --git a/scripts/cache/01_raw_images/personal/1a586eb5b865bc350345057318a5728f.jpg b/resources/data/cache/01_raw_images/personal/1a586eb5b865bc350345057318a5728f.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/1a586eb5b865bc350345057318a5728f.jpg rename to resources/data/cache/01_raw_images/personal/1a586eb5b865bc350345057318a5728f.jpg diff --git a/scripts/cache/01_raw_images/personal/20190519_125238.jpg b/resources/data/cache/01_raw_images/personal/20190519_125238.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20190519_125238.jpg rename to resources/data/cache/01_raw_images/personal/20190519_125238.jpg diff --git a/scripts/cache/01_raw_images/personal/20190722_140748.jpg b/resources/data/cache/01_raw_images/personal/20190722_140748.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20190722_140748.jpg rename to resources/data/cache/01_raw_images/personal/20190722_140748.jpg diff --git a/scripts/cache/01_raw_images/personal/20200606_095643.jpg b/resources/data/cache/01_raw_images/personal/20200606_095643.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200606_095643.jpg rename to resources/data/cache/01_raw_images/personal/20200606_095643.jpg diff --git a/scripts/cache/01_raw_images/personal/20200606_102141.jpg b/resources/data/cache/01_raw_images/personal/20200606_102141.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200606_102141.jpg rename to resources/data/cache/01_raw_images/personal/20200606_102141.jpg diff --git a/scripts/cache/01_raw_images/personal/20200618_120810.jpg b/resources/data/cache/01_raw_images/personal/20200618_120810.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200618_120810.jpg rename to resources/data/cache/01_raw_images/personal/20200618_120810.jpg diff --git a/scripts/cache/01_raw_images/personal/20200618_123346.jpg b/resources/data/cache/01_raw_images/personal/20200618_123346.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200618_123346.jpg rename to resources/data/cache/01_raw_images/personal/20200618_123346.jpg diff --git a/scripts/cache/01_raw_images/personal/20200619_142852.jpg b/resources/data/cache/01_raw_images/personal/20200619_142852.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200619_142852.jpg rename to resources/data/cache/01_raw_images/personal/20200619_142852.jpg diff --git a/scripts/cache/01_raw_images/personal/20200619_213922_1.jpg b/resources/data/cache/01_raw_images/personal/20200619_213922_1.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200619_213922_1.jpg rename to resources/data/cache/01_raw_images/personal/20200619_213922_1.jpg diff --git a/scripts/cache/01_raw_images/personal/20200623_100706.jpg b/resources/data/cache/01_raw_images/personal/20200623_100706.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200623_100706.jpg rename to resources/data/cache/01_raw_images/personal/20200623_100706.jpg diff --git a/scripts/cache/01_raw_images/personal/20200625_124835.jpg b/resources/data/cache/01_raw_images/personal/20200625_124835.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200625_124835.jpg rename to resources/data/cache/01_raw_images/personal/20200625_124835.jpg diff --git a/scripts/cache/01_raw_images/personal/20200626_124209.jpg b/resources/data/cache/01_raw_images/personal/20200626_124209.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200626_124209.jpg rename to resources/data/cache/01_raw_images/personal/20200626_124209.jpg diff --git a/scripts/cache/01_raw_images/personal/20200630_101026.jpg b/resources/data/cache/01_raw_images/personal/20200630_101026.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200630_101026.jpg rename to resources/data/cache/01_raw_images/personal/20200630_101026.jpg diff --git a/scripts/cache/01_raw_images/personal/20200630_101557.jpg b/resources/data/cache/01_raw_images/personal/20200630_101557.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200630_101557.jpg rename to resources/data/cache/01_raw_images/personal/20200630_101557.jpg diff --git a/scripts/cache/01_raw_images/personal/20200630_101758.jpg b/resources/data/cache/01_raw_images/personal/20200630_101758.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200630_101758.jpg rename to resources/data/cache/01_raw_images/personal/20200630_101758.jpg diff --git a/scripts/cache/01_raw_images/personal/20200701_144641.jpg b/resources/data/cache/01_raw_images/personal/20200701_144641.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200701_144641.jpg rename to resources/data/cache/01_raw_images/personal/20200701_144641.jpg diff --git a/scripts/cache/01_raw_images/personal/20200705_093806.jpg b/resources/data/cache/01_raw_images/personal/20200705_093806.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200705_093806.jpg rename to resources/data/cache/01_raw_images/personal/20200705_093806.jpg diff --git a/scripts/cache/01_raw_images/personal/20200717_161441.jpg b/resources/data/cache/01_raw_images/personal/20200717_161441.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200717_161441.jpg rename to resources/data/cache/01_raw_images/personal/20200717_161441.jpg diff --git a/scripts/cache/01_raw_images/personal/20200722_005935.jpg b/resources/data/cache/01_raw_images/personal/20200722_005935.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200722_005935.jpg rename to resources/data/cache/01_raw_images/personal/20200722_005935.jpg diff --git a/scripts/cache/01_raw_images/personal/20200724_163236.jpg b/resources/data/cache/01_raw_images/personal/20200724_163236.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200724_163236.jpg rename to resources/data/cache/01_raw_images/personal/20200724_163236.jpg diff --git a/scripts/cache/01_raw_images/personal/20200724_224026.jpg b/resources/data/cache/01_raw_images/personal/20200724_224026.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200724_224026.jpg rename to resources/data/cache/01_raw_images/personal/20200724_224026.jpg diff --git a/scripts/cache/01_raw_images/personal/20200727_094000.jpg b/resources/data/cache/01_raw_images/personal/20200727_094000.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200727_094000.jpg rename to resources/data/cache/01_raw_images/personal/20200727_094000.jpg diff --git a/scripts/cache/01_raw_images/personal/20200728_111054.jpg b/resources/data/cache/01_raw_images/personal/20200728_111054.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200728_111054.jpg rename to resources/data/cache/01_raw_images/personal/20200728_111054.jpg diff --git a/scripts/cache/01_raw_images/personal/20200728_140354.jpg b/resources/data/cache/01_raw_images/personal/20200728_140354.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200728_140354.jpg rename to resources/data/cache/01_raw_images/personal/20200728_140354.jpg diff --git a/scripts/cache/01_raw_images/personal/20200731_204947.jpg b/resources/data/cache/01_raw_images/personal/20200731_204947.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200731_204947.jpg rename to resources/data/cache/01_raw_images/personal/20200731_204947.jpg diff --git a/scripts/cache/01_raw_images/personal/20200801_185650.jpg b/resources/data/cache/01_raw_images/personal/20200801_185650.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200801_185650.jpg rename to resources/data/cache/01_raw_images/personal/20200801_185650.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_163415.jpg b/resources/data/cache/01_raw_images/personal/20200802_163415.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_163415.jpg rename to resources/data/cache/01_raw_images/personal/20200802_163415.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_163639.jpg b/resources/data/cache/01_raw_images/personal/20200802_163639.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_163639.jpg rename to resources/data/cache/01_raw_images/personal/20200802_163639.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_163829.jpg b/resources/data/cache/01_raw_images/personal/20200802_163829.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_163829.jpg rename to resources/data/cache/01_raw_images/personal/20200802_163829.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_164012.jpg b/resources/data/cache/01_raw_images/personal/20200802_164012.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_164012.jpg rename to resources/data/cache/01_raw_images/personal/20200802_164012.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_172820.jpg b/resources/data/cache/01_raw_images/personal/20200802_172820.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_172820.jpg rename to resources/data/cache/01_raw_images/personal/20200802_172820.jpg diff --git a/scripts/cache/01_raw_images/personal/20200802_173007.jpg b/resources/data/cache/01_raw_images/personal/20200802_173007.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/20200802_173007.jpg rename to resources/data/cache/01_raw_images/personal/20200802_173007.jpg diff --git a/scripts/cache/01_raw_images/personal/2a16dbaeeeef30a6b5479a8ba00aab9b.jpg b/resources/data/cache/01_raw_images/personal/2a16dbaeeeef30a6b5479a8ba00aab9b.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/2a16dbaeeeef30a6b5479a8ba00aab9b.jpg rename to resources/data/cache/01_raw_images/personal/2a16dbaeeeef30a6b5479a8ba00aab9b.jpg diff --git a/scripts/cache/01_raw_images/personal/31f577dd1d3c6158f20bd2e3668a6d74.jpg b/resources/data/cache/01_raw_images/personal/31f577dd1d3c6158f20bd2e3668a6d74.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/31f577dd1d3c6158f20bd2e3668a6d74.jpg rename to resources/data/cache/01_raw_images/personal/31f577dd1d3c6158f20bd2e3668a6d74.jpg diff --git a/scripts/cache/01_raw_images/personal/75347476e42316830b3bd4276abbac29.jpg b/resources/data/cache/01_raw_images/personal/75347476e42316830b3bd4276abbac29.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/75347476e42316830b3bd4276abbac29.jpg rename to resources/data/cache/01_raw_images/personal/75347476e42316830b3bd4276abbac29.jpg diff --git a/scripts/cache/01_raw_images/personal/81c29851faf31e5adbe748fa0e450aa6.jpg b/resources/data/cache/01_raw_images/personal/81c29851faf31e5adbe748fa0e450aa6.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/81c29851faf31e5adbe748fa0e450aa6.jpg rename to resources/data/cache/01_raw_images/personal/81c29851faf31e5adbe748fa0e450aa6.jpg diff --git a/scripts/cache/01_raw_images/personal/85a0304dbc66bba40def5200ab3a07c6.jpg b/resources/data/cache/01_raw_images/personal/85a0304dbc66bba40def5200ab3a07c6.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/85a0304dbc66bba40def5200ab3a07c6.jpg rename to resources/data/cache/01_raw_images/personal/85a0304dbc66bba40def5200ab3a07c6.jpg diff --git a/scripts/cache/01_raw_images/personal/861e579dd0c244ec7fae3eaf3eff16b2.jpg b/resources/data/cache/01_raw_images/personal/861e579dd0c244ec7fae3eaf3eff16b2.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/861e579dd0c244ec7fae3eaf3eff16b2.jpg rename to resources/data/cache/01_raw_images/personal/861e579dd0c244ec7fae3eaf3eff16b2.jpg diff --git a/scripts/cache/01_raw_images/personal/a2a679f3f3ae4e3764fbfede13d28099.jpg b/resources/data/cache/01_raw_images/personal/a2a679f3f3ae4e3764fbfede13d28099.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/a2a679f3f3ae4e3764fbfede13d28099.jpg rename to resources/data/cache/01_raw_images/personal/a2a679f3f3ae4e3764fbfede13d28099.jpg diff --git a/scripts/cache/01_raw_images/personal/bab9d1cdac549407377e8309c287b7fa.jpg b/resources/data/cache/01_raw_images/personal/bab9d1cdac549407377e8309c287b7fa.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/bab9d1cdac549407377e8309c287b7fa.jpg rename to resources/data/cache/01_raw_images/personal/bab9d1cdac549407377e8309c287b7fa.jpg diff --git a/scripts/cache/01_raw_images/personal/bc428f04fadcf6e8c760e7dc7152f42b.jpg b/resources/data/cache/01_raw_images/personal/bc428f04fadcf6e8c760e7dc7152f42b.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/bc428f04fadcf6e8c760e7dc7152f42b.jpg rename to resources/data/cache/01_raw_images/personal/bc428f04fadcf6e8c760e7dc7152f42b.jpg diff --git a/scripts/cache/01_raw_images/personal/e9f405ebcc2304a120872bc75732c476.jpg b/resources/data/cache/01_raw_images/personal/e9f405ebcc2304a120872bc75732c476.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/e9f405ebcc2304a120872bc75732c476.jpg rename to resources/data/cache/01_raw_images/personal/e9f405ebcc2304a120872bc75732c476.jpg diff --git a/scripts/cache/01_raw_images/personal/ed8fb705383eb8aac33197110212124e.jpg b/resources/data/cache/01_raw_images/personal/ed8fb705383eb8aac33197110212124e.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/ed8fb705383eb8aac33197110212124e.jpg rename to resources/data/cache/01_raw_images/personal/ed8fb705383eb8aac33197110212124e.jpg diff --git a/scripts/cache/01_raw_images/personal/efaaa9e5d4a4f1698715507178ca4e3d.jpg b/resources/data/cache/01_raw_images/personal/efaaa9e5d4a4f1698715507178ca4e3d.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/efaaa9e5d4a4f1698715507178ca4e3d.jpg rename to resources/data/cache/01_raw_images/personal/efaaa9e5d4a4f1698715507178ca4e3d.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-1dc365a57b9eea70d0d21fb6d1644b72.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-1dc365a57b9eea70d0d21fb6d1644b72.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-1dc365a57b9eea70d0d21fb6d1644b72.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-1dc365a57b9eea70d0d21fb6d1644b72.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-3aaafbe17a165937a260c2f38d30b648.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-3aaafbe17a165937a260c2f38d30b648.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-3aaafbe17a165937a260c2f38d30b648.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-3aaafbe17a165937a260c2f38d30b648.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-512be9e273586e00e10e2906d7446098.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-512be9e273586e00e10e2906d7446098.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-512be9e273586e00e10e2906d7446098.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-512be9e273586e00e10e2906d7446098.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-6bd2d51e9d3b639f679ccb388eb54146.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-6bd2d51e9d3b639f679ccb388eb54146.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-6bd2d51e9d3b639f679ccb388eb54146.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-6bd2d51e9d3b639f679ccb388eb54146.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-95a0a96a0220064f99f031fb9cfcaf75.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-95a0a96a0220064f99f031fb9cfcaf75.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-95a0a96a0220064f99f031fb9cfcaf75.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-95a0a96a0220064f99f031fb9cfcaf75.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-bc466b6c01a8be0039aedc535e4c2ebb.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-bc466b6c01a8be0039aedc535e4c2ebb.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-bc466b6c01a8be0039aedc535e4c2ebb.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-bc466b6c01a8be0039aedc535e4c2ebb.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-ca7ef660c832cbca8e82d92f67432175.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-ca7ef660c832cbca8e82d92f67432175.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-ca7ef660c832cbca8e82d92f67432175.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-ca7ef660c832cbca8e82d92f67432175.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-daeb8c1a86ad6406bad079886d376cb6.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-daeb8c1a86ad6406bad079886d376cb6.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-daeb8c1a86ad6406bad079886d376cb6.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-daeb8c1a86ad6406bad079886d376cb6.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-ee371f46003d800ad2db17558c04464e.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-ee371f46003d800ad2db17558c04464e.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-ee371f46003d800ad2db17558c04464e.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-ee371f46003d800ad2db17558c04464e.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-fa7601fd804ab472e8cb91e5a638e2ef.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-fa7601fd804ab472e8cb91e5a638e2ef.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-fa7601fd804ab472e8cb91e5a638e2ef.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-fa7601fd804ab472e8cb91e5a638e2ef.jpg diff --git a/scripts/cache/01_raw_images/personal/perso-source-640-ff4c9c993ff0a303c924aa35d0386e15.jpg b/resources/data/cache/01_raw_images/personal/perso-source-640-ff4c9c993ff0a303c924aa35d0386e15.jpg similarity index 100% rename from scripts/cache/01_raw_images/personal/perso-source-640-ff4c9c993ff0a303c924aa35d0386e15.jpg rename to resources/data/cache/01_raw_images/personal/perso-source-640-ff4c9c993ff0a303c924aa35d0386e15.jpg diff --git a/scripts/cache/01_raw_images/sea/1886961.jpg b/resources/data/cache/01_raw_images/sea/1886961.jpg similarity index 100% rename from scripts/cache/01_raw_images/sea/1886961.jpg rename to resources/data/cache/01_raw_images/sea/1886961.jpg diff --git a/scripts/cache/01_raw_images/sea/IADX6-086.jpg b/resources/data/cache/01_raw_images/sea/IADX6-086.jpg similarity index 100% rename from scripts/cache/01_raw_images/sea/IADX6-086.jpg rename to resources/data/cache/01_raw_images/sea/IADX6-086.jpg diff --git a/scripts/cache/01_raw_images/space/11916-large.jpg b/resources/data/cache/01_raw_images/space/11916-large.jpg similarity index 100% rename from scripts/cache/01_raw_images/space/11916-large.jpg rename to resources/data/cache/01_raw_images/space/11916-large.jpg diff --git a/scripts/cache/02_optimized_images/.gitkeep b/resources/data/cache/02_optimized_images/.gitkeep similarity index 100% rename from scripts/cache/02_optimized_images/.gitkeep rename to resources/data/cache/02_optimized_images/.gitkeep diff --git a/scripts/03_check_sliced_images.sh b/resources/data/check_sliced_images.sh similarity index 100% rename from scripts/03_check_sliced_images.sh rename to resources/data/check_sliced_images.sh diff --git a/icons/build_game_icons.sh b/resources/ui/build_ui_resources.sh similarity index 50% rename from icons/build_game_icons.sh rename to resources/ui/build_ui_resources.sh index a4c7b7573d1c165b3b3798d07458921e6aed7ad2..4f365ede7d83140ce6309a3083580f2662b30990 100755 --- a/icons/build_game_icons.sh +++ b/resources/ui/build_ui_resources.sh @@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; } CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -BASE_DIR="$(dirname "${CURRENT_DIR}")" +BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")" ASSETS_DIR="${BASE_DIR}/assets" OPTIPNG_OPTIONS="-preserve -quiet -o7" @@ -14,20 +14,23 @@ ICON_SIZE=192 ####################################################### -# Game images -AVAILABLE_GAME_IMAGES=" - button_back - button_shuffle - button_random_pick - game_win - placeholder - tip_hidden -" - -# Settings images -AVAILABLES_GAME_SETTINGS=" - difficulty:3x3,4x4,5x5 -" +# Game images (svg files found in `images` folder) +AVAILABLE_GAME_IMAGES="" +if [ -d "${CURRENT_DIR}/images" ]; then + AVAILABLE_GAME_IMAGES="$(find "${CURRENT_DIR}/images" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort)" +fi + +# Skins (subfolders found in `skins` folder) +AVAILABLE_SKINS="" +if [ -d "${CURRENT_DIR}/skins" ]; then + AVAILABLE_SKINS="$(find "${CURRENT_DIR}/skins" -mindepth 1 -type d | awk -F/ '{print $NF}')" +fi + +# Images per skin (svg files found recursively in `skins` folder and subfolders) +SKIN_IMAGES="" +if [ -d "${CURRENT_DIR}/skins" ]; then + SKIN_IMAGES="$(find "${CURRENT_DIR}/skins" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort | uniq)" +fi ####################################################### @@ -49,7 +52,7 @@ function optimize_svg() { } # build icons -function build_icon() { +function build_image() { SOURCE="$1" TARGET="$2" @@ -62,44 +65,46 @@ function build_icon() { optimize_svg "${SOURCE}" + mkdir -p "$(dirname "${TARGET}")" + inkscape \ --export-width=${ICON_SIZE} \ --export-height=${ICON_SIZE} \ --export-filename=${TARGET} \ - ${SOURCE} + "${SOURCE}" - optipng ${OPTIPNG_OPTIONS} ${TARGET} + optipng ${OPTIPNG_OPTIONS} "${TARGET}" } -function build_settings_icons() { - INPUT_STRING="$1" +function build_image_for_skin() { + SKIN_CODE="$1" - SETTING_NAME="$(echo "${INPUT_STRING}" | cut -d":" -f1)" - SETTING_VALUES="$(echo "${INPUT_STRING}" | cut -d":" -f2 | tr "," " ")" - - for SETTING_VALUE in ${SETTING_VALUES} + # skin images + for SKIN_IMAGE in ${SKIN_IMAGES} do - SETTING_CODE="${SETTING_NAME}_${SETTING_VALUE}" - build_icon ${CURRENT_DIR}/${SETTING_CODE}.svg ${ASSETS_DIR}/icons/${SETTING_CODE}.png + build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png done } ####################################################### -# Create output folders -mkdir -p ${ASSETS_DIR}/icons - # Delete existing generated images -find ${ASSETS_DIR}/icons -type f -name "*.png" -delete +if [ -d "${ASSETS_DIR}/ui" ]; then + find ${ASSETS_DIR}/ui -type f -name "*.png" -delete +fi +if [ -d "${ASSETS_DIR}/skins" ]; then + find ${ASSETS_DIR}/skins -type f -name "*.png" -delete +fi # build game images for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES} do - build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/icons/${GAME_IMAGE}.png + build_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png done -# build settings images -for GAME_SETTING in ${AVAILABLES_GAME_SETTINGS} +# build skins images +for SKIN in ${AVAILABLE_SKINS} do - build_settings_icons "${GAME_SETTING}" + build_image_for_skin "${SKIN}" done + diff --git a/icons/button_back.svg b/resources/ui/images/button_back.svg similarity index 100% rename from icons/button_back.svg rename to resources/ui/images/button_back.svg diff --git a/resources/ui/images/button_delete_saved_game.svg b/resources/ui/images/button_delete_saved_game.svg new file mode 100644 index 0000000000000000000000000000000000000000..ac7eefef476f761903fe781b8c86d0c94323550a --- /dev/null +++ b/resources/ui/images/button_delete_saved_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#ee7d49" stroke="#fff" stroke-width=".238"/><path d="m61.07 35.601-1.7399 27.837c-0.13442 2.1535-1.9205 3.8312-4.0781 3.8312h-16.84c-2.1576 0-3.9437-1.6777-4.0781-3.8312l-1.7399-27.837h-2.6176c-0.84621 0-1.5323-0.68613-1.5323-1.5323 0-0.84655 0.68613-1.5323 1.5323-1.5323h33.711c0.84621 0 1.5323 0.68578 1.5323 1.5323 0 0.84621-0.68613 1.5323-1.5323 1.5323zm-3.2617 0h-21.953l1.4715 26.674c0.05985 1.0829 0.95531 1.9305 2.0403 1.9305h14.929c1.085 0 1.9804-0.84757 2.0403-1.9305zm-10.977 3.0647c0.78977 0 1.4301 0.6403 1.4301 1.4301v19.614c0 0.78977-0.6403 1.4301-1.4301 1.4301s-1.4301-0.6403-1.4301-1.4301v-19.614c0-0.78977 0.6403-1.4301 1.4301-1.4301zm-6.1293 0c0.80004 0 1.4588 0.62935 1.495 1.4286l0.89647 19.719c0.03182 0.70016-0.50998 1.2933-1.2101 1.3255-0.01915 7.02e-4 -0.03831 1e-3 -0.05781 1e-3 -0.74462 0-1.3596-0.58215-1.4003-1.3261l-1.0757-19.719c-0.0407-0.74701 0.53188-1.3852 1.2786-1.4259 0.02462-0.0014 0.04926-2e-3 0.07388-2e-3zm12.259 0c0.74804 0 1.3541 0.60609 1.3541 1.3541 0 0.02462-3.28e-4 0.04926-0.0017 0.07388l-1.0703 19.618c-0.04379 0.80106-0.70597 1.4281-1.5081 1.4281-0.74804 0-1.3541-0.60609-1.3541-1.3541 0-0.02462 3.49e-4 -0.04925 0.0017-0.07388l1.0703-19.618c0.04379-0.80106 0.70597-1.4281 1.5081-1.4281zm-10.216-12.259h8.1728c2.2567 0 4.086 1.8293 4.086 4.086v2.0433h-16.344v-2.0433c0-2.2567 1.8293-4.086 4.086-4.086zm0.20453 3.0647c-0.67725 0-1.2259 0.54863-1.2259 1.2259v1.8388h10.215v-1.8388c0-0.67725-0.54863-1.2259-1.2259-1.2259z" fill="#fff" fill-rule="evenodd" stroke="#bd4812" stroke-width=".75383"/></svg> diff --git a/icons/button_random_pick.svg b/resources/ui/images/button_random_pick.svg similarity index 100% rename from icons/button_random_pick.svg rename to resources/ui/images/button_random_pick.svg diff --git a/resources/ui/images/button_resume_game.svg b/resources/ui/images/button_resume_game.svg new file mode 100644 index 0000000000000000000000000000000000000000..6ad8b64202d0e70f898c16c520e756fe8a934add --- /dev/null +++ b/resources/ui/images/button_resume_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m39.211 31.236c-0.84086-0.84489-2.9911-0.84489-2.9911 0v34.329c0 0.84594 2.1554 0.84594 2.9993 0l28.178-15.637c0.84392-0.84086 0.85812-2.2091 0.01623-3.053z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="6.1726"/><path d="m40.355 33.714c-0.71948-0.72294-2.5594-0.72294-2.5594 0v29.373c0 0.72383 1.8442 0.72383 2.5663 0l24.11-13.38c0.7221-0.71948 0.73426-1.8902 0.01389-2.6124z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.225"/><path d="m28.369 66.919v-37.591" fill="#105ca2" stroke="#105ca2" stroke-linecap="round" stroke-width="4.0337"/></svg> diff --git a/icons/button_shuffle.svg b/resources/ui/images/button_shuffle.svg similarity index 100% rename from icons/button_shuffle.svg rename to resources/ui/images/button_shuffle.svg diff --git a/resources/ui/images/button_start.svg b/resources/ui/images/button_start.svg new file mode 100644 index 0000000000000000000000000000000000000000..e9d49d2172b9a0305db82779971e3c1e12f34a70 --- /dev/null +++ b/resources/ui/images/button_start.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m34.852 25.44c-1.1248-1.1302-4.0012-1.1302-4.0012 0v45.921c0 1.1316 2.8832 1.1316 4.0121 0l37.693-20.918c1.1289-1.1248 1.1479-2.9551 0.02171-4.084z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="8.257"/><path d="m36.382 28.754c-0.96243-0.96706-3.4236-0.96706-3.4236 0v39.292c0 0.96825 2.467 0.96825 3.4329 0l32.252-17.898c0.96594-0.96243 0.9822-2.5285 0.01858-3.4945z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.314"/></svg> diff --git a/icons/game_win.svg b/resources/ui/images/game_win.svg similarity index 100% rename from icons/game_win.svg rename to resources/ui/images/game_win.svg diff --git a/icons/placeholder.svg b/resources/ui/images/placeholder.svg similarity index 100% rename from icons/placeholder.svg rename to resources/ui/images/placeholder.svg diff --git a/icons/tip_hidden.svg b/resources/ui/images/tip_hidden.svg similarity index 100% rename from icons/tip_hidden.svg rename to resources/ui/images/tip_hidden.svg