From c23f7fb7d1d58f8ffdac0ac1dbbbbcb30c4c791e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Wed, 15 May 2024 16:49:55 +0200 Subject: [PATCH] Normalize game architecture --- android/gradle.properties | 4 +- assets/translations/en.json | 6 +- assets/translations/fr.json | 6 +- assets/{icons => ui}/button_back.png | Bin assets/ui/button_delete_saved_game.png | Bin 0 -> 5813 bytes assets/ui/button_resume_game.png | Bin 0 -> 3659 bytes assets/{icons => ui}/button_start.png | Bin assets/ui/game_end.png | Bin 0 -> 7937 bytes assets/{icons => ui}/placeholder.png | Bin .../metadata/android/en-US/changelogs/40.txt | 1 + .../metadata/android/fr-FR/changelogs/40.txt | 1 + icons/build_game_icons.sh | 78 ----------- lib/config/default_game_settings.dart | 58 +++++--- lib/config/default_global_settings.dart | 27 +++- lib/config/menu.dart | 5 - lib/config/theme.dart | 10 +- lib/cubit/game_cubit.dart | 44 ++++-- lib/cubit/game_state.dart | 2 +- lib/cubit/settings_game_cubit.dart | 38 +++--- lib/cubit/settings_game_state.dart | 4 - lib/cubit/settings_global_cubit.dart | 42 ++++-- lib/cubit/settings_global_state.dart | 4 - lib/data/fetch_data_helper.dart | 2 +- lib/main.dart | 48 +++++-- lib/models/game.dart | 100 -------------- lib/models/game/game.dart | 125 ++++++++++++++++++ lib/models/{data => game}/game_item.dart | 0 lib/models/{ => settings}/settings_game.dart | 22 +-- lib/models/settings/settings_global.dart | 41 ++++++ lib/models/settings_global.dart | 23 ---- lib/ui/game/game_end.dart | 57 ++++++++ lib/ui/game/game_top.dart | 23 ++++ lib/ui/helpers/app_titles.dart | 32 +++++ .../helpers/outlined_text_widget.dart | 0 lib/ui/layouts/game_layout.dart | 38 ++++++ .../parameters_layout.dart} | 83 +++++++++--- lib/ui/parameters/parameter_image.dart | 38 ++++++ .../parameter_painter.dart | 28 ++-- lib/ui/screens/page_about.dart | 52 ++++---- lib/ui/screens/page_game.dart | 16 ++- lib/ui/screens/page_settings.dart | 26 ++-- .../{widgets => }/settings/settings_form.dart | 2 +- lib/ui/{widgets => }/settings/theme_card.dart | 0 lib/ui/skeleton.dart | 4 +- .../actions/button_delete_saved_game.dart | 21 +++ lib/ui/widgets/actions/button_game_quit.dart | 21 +++ .../{ => actions}/button_game_start_new.dart | 14 +- .../actions/button_resume_saved_game.dart | 21 +++ lib/ui/widgets/game/game.dart | 37 ------ lib/ui/widgets/game/game_board.dart | 22 +++ lib/ui/widgets/game/game_countdown.dart | 4 +- .../widgets/game/game_position_indicator.dart | 6 +- lib/ui/widgets/game/widget_category.dart | 5 +- lib/ui/widgets/game/widget_letter.dart | 7 +- lib/ui/widgets/global_app_bar.dart | 13 +- lib/ui/widgets/helpers/app_header.dart | 24 ---- lib/ui/widgets/helpers/app_title.dart | 17 --- pubspec.lock | 32 ++--- pubspec.yaml | 9 +- .../app/build_application_resources.sh | 2 +- {icons => resources/app}/featureGraphic.svg | 0 {icons => resources/app}/icon.svg | 0 resources/build_resources.sh | 8 ++ .../data}/build_categories_list.sh | 2 +- {scripts => resources/data}/categories.csv | 0 resources/ui/build_ui_resources.sh | 110 +++++++++++++++ .../ui/images}/button_back.svg | 0 .../ui/images/button_delete_saved_game.svg | 2 + resources/ui/images/button_resume_game.svg | 2 + .../ui/images}/button_start.svg | 0 resources/ui/images/game_end.svg | 2 + .../ui/images}/placeholder.svg | 0 72 files changed, 947 insertions(+), 524 deletions(-) rename assets/{icons => ui}/button_back.png (100%) create mode 100644 assets/ui/button_delete_saved_game.png create mode 100644 assets/ui/button_resume_game.png rename assets/{icons => ui}/button_start.png (100%) create mode 100644 assets/ui/game_end.png rename assets/{icons => ui}/placeholder.png (100%) create mode 100644 fastlane/metadata/android/en-US/changelogs/40.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/40.txt delete mode 100755 icons/build_game_icons.sh delete mode 100644 lib/models/game.dart create mode 100644 lib/models/game/game.dart rename lib/models/{data => game}/game_item.dart (100%) rename lib/models/{ => settings}/settings_game.dart (57%) create mode 100644 lib/models/settings/settings_global.dart delete mode 100644 lib/models/settings_global.dart create mode 100644 lib/ui/game/game_end.dart create mode 100644 lib/ui/game/game_top.dart create mode 100644 lib/ui/helpers/app_titles.dart rename lib/ui/{widgets => }/helpers/outlined_text_widget.dart (100%) create mode 100644 lib/ui/layouts/game_layout.dart rename lib/ui/{widgets/parameters.dart => layouts/parameters_layout.dart} (52%) create mode 100644 lib/ui/parameters/parameter_image.dart rename lib/ui/{painters => parameters}/parameter_painter.dart (87%) rename lib/ui/{widgets => }/settings/settings_form.dart (96%) rename lib/ui/{widgets => }/settings/theme_card.dart (100%) create mode 100644 lib/ui/widgets/actions/button_delete_saved_game.dart create mode 100644 lib/ui/widgets/actions/button_game_quit.dart rename lib/ui/widgets/{ => actions}/button_game_start_new.dart (72%) create mode 100644 lib/ui/widgets/actions/button_resume_saved_game.dart delete mode 100644 lib/ui/widgets/game/game.dart create mode 100644 lib/ui/widgets/game/game_board.dart delete mode 100644 lib/ui/widgets/helpers/app_header.dart delete mode 100644 lib/ui/widgets/helpers/app_title.dart rename icons/build_application_icons.sh => resources/app/build_application_resources.sh (98%) rename {icons => resources/app}/featureGraphic.svg (100%) rename {icons => resources/app}/icon.svg (100%) create mode 100755 resources/build_resources.sh rename {scripts => resources/data}/build_categories_list.sh (98%) rename {scripts => resources/data}/categories.csv (100%) create mode 100755 resources/ui/build_ui_resources.sh rename {icons => resources/ui/images}/button_back.svg (100%) create mode 100644 resources/ui/images/button_delete_saved_game.svg create mode 100644 resources/ui/images/button_resume_game.svg rename {icons => resources/ui/images}/button_start.svg (100%) create mode 100644 resources/ui/images/game_end.svg rename {icons => resources/ui/images}/placeholder.svg (100%) diff --git a/android/gradle.properties b/android/gradle.properties index 665a788..e3b4d8a 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=1.2.33 -app.versionCode=39 +app.versionName=1.3.0 +app.versionCode=40 diff --git a/assets/translations/en.json b/assets/translations/en.json index d7aedb9..393e6ef 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,12 +1,8 @@ { "app_name": "Categories", - "bottom_nav_home": "Game", - "bottom_nav_settings": "Settings", - "bottom_nav_about": "About", - "settings_title": "Settings", - "settings_label_theme":"Theme mode", + "settings_label_theme": "Theme mode", "about_title": "Informations", "about_content": "Categories", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index d40e253..3088da5 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,12 +1,8 @@ { "app_name": "Petit Bac", - "bottom_nav_home": "Jeu", - "bottom_nav_settings": "Réglages", - "bottom_nav_about": "Infos", - "settings_title": "Réglages", - "settings_label_theme":"Thème de couleurs", + "settings_label_theme": "Thème de couleurs", "about_title": "Informations", "about_content": "Petit Bac.", 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 GIT binary patch literal 5813 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE69Bd2>3_*8t*cliYI14-?iy0W?oEaG8oERJS zYjH3zFi4iTMwA5Sr<If^7Ns(jmzV2h=4BTrCl;jY<rk&TerF@az#!)2>EaktaqDet zW=TlsF5mref9v*MJul|s5FjP6Mc%7f;YPzYGh@!rIxBci+3+hhYfSFY-IioOQ`(iK zrOwRBSR+M_b7t_&=MF*~OM*N-R%vlQpZh*UwdnOc=~Y3jDxtsEZJi#mvikkX;QRYZ z_a)XJ?>2nK$}q3%cj3kVPb#KOy+13U+O|5bH1S&7E1A1t3<VN*%1*SMn6|^$HhJq> zHTQ=2>o3;#8$MT&Gd%vgRkGPHWorHYy9qzC1hwy4Hox5NpY|(PnBhQS!EHrprD!et zjfWk!rFMPhRgO;nHGA=iZ*p>sTH3c~7MHO1PJAwOntzSa)Et?EUS>SZ4Zr#GvY)bt zOZSHBsm%IZ!oJgXTkQmvjL^xm3)XzPWd8G)Zt}-e27b|Bx{o8CE<OCy)UwlkXYRH= z^I3j`mIhvnN`A55DB*`guu7BRn~<tp$7i#CJ2u?E`sC%gX>a3#I#06AGCK85$1p7Y z$d97gwMR@@45a3ny>1fjNSu+dAo7n?j_mm}o{AsOB)oVd!g%H=>)xKg=a;?As*;l$ z)1GNm8z*0BHOV`yZ_>vw$9CC`?v<+V`H!bCofW=Q)*F6qxnXjqx;y8G`)dD$o7C+u zy!qJ3Z>-*av}pFfwRc%ooVz;rQoRZ1WcD5H{u^?cR-f~<NET<z;x?K-{cUVx2LFS& ziwkSDwX?f7O~2F4u;18ij_1)9#&;aaPj-sj{;<O6)#TYF9d&vJkqy;)t!kTQ?ODPc z#gY*yT42L1%(}yPRg+)Z-B_bQ|AtwAuEZA9FmC@Z`oQd5?q6<EMxTq~iwztb)DMWf zm-pS8yI)|znjD9t6MaQz$Q)2sa&kAHzWLOq>905gicM3u*L{}o;d>x?dV%lanU?xL zPggO`5N`fsVA!DEczaL2Eo0Ba<&zCH8{D6=D4kQeD)`{z?ey;=i9rnhPEUXSWfkF5 zW%{D|yK1iFWTpzCQ~PfwJlb~ucK2N~9{t;!MJ6*<gsEh_FFK~B)mE`_Qd4GIY%$2J z#+-RM_g6o2`=@jG`TK+*hVx1c+bcKK3NSD*IxsM>99Y7jz`)SJz`(%7z`(&kDd$1j zj*s^orrE7G_pW$dA8}@jo=#R)`f`Ws3$ibozPvRfSzv+ZqKvQEI?ul9ohgstF3Wte z_>Iv2{0mF;biVYy^J`pJc(TfOSDB8w%f;RG2j?_1__FWx{&(chUitKW%26Aq#A-*b z)N8uCa%I{&s~1sAcNNsV6@4W4&u(qxyGQnk?;IP{68?DqHoX5(H}b}sxHn~SQoHZF zyT)m8&;Hr8BmLc_e?Ryf8rs`mXR~c&_-Dd5_hR!*-t9M<t<JQ~*7>ql?$5p42jUG3 z{QMvDZBAc*sv8;UCR2UEJu+&|j`BmZ>kpJOGTBtzi87z^vvPY<=j}94-Yv^snQ3?O zrd&T)dv@Ji+bb3yieE~Z9`kx>E41xgLfswp9Xsnd_6Z9-IH;8V(Ob!=SozESOUsK- ze94=ZF@ama=&xniv~~Zc?i8$v`XkF{$noL8M62b?4fd^i{qt0ZZ!T|nQ1zw*fw_^_ z4()sM>eKYOOtH!ZA<_?j+}jzvdPjI@;5rHCSJt*4=Qc3#i|v{@O}%FA$rW9fF1&tO z9A9vA@1-AGU(DJTxGXAoYDKL1XUiIOg#*p5k9fEl--XN;bz9lbFSb5i|JCbF)v5ZM zbhdxtRP<sBnZ1VPMcaE876wKJfdhIX>t)Xvp3n^VF|p_T68*HRPk&X3w9gk`pMI|X z;>+qQip6#Lg=c3Aii><VntDfI{qfHmE$ZhVRA`89*S*f4x-0TL_wy^E7bVM?&+?uX zJS)oaAZ>w?(XUC#<~~U`-R)B?@BioB!EK_x@wIx?vy7^6fdg|3a*t|HJ8@>;cgeoC z{x5r9AB_?HV9CsQhvj8y;*r&o8$MLaZ2x>YR_+)pqnyzuu{|35LbQU9UHq`npGUZO z>edbX91qO2)|xWA?yY+=$x$h**|{ztM6>8){EYqQALqrVzAE%9Q2Q_XLHeJ>XX%d? z-7{{_kB$4XifiZpKfAwlYE<VJGxXm5cEa}O(l2{W)J@Dy+)cs@n(khdjQiPj;kujO zs}p+uZx%4re)O~!U&%Q^KxrDw!sOd4&t<M!u<?{odWp*07!AACZ!JN3S+)Q7*fgjs zRrpwo?f=PJnwsu5$6#5{&%LwHrk~uPcJ7q%<Iwr){|;yBv<S1zOb~Er-0;zV(~ENV zl|0KfDR@f+-@5ZGxNv6L+K{z6XToady|`-kXmb;TLIYEh=7tBNTmNj>5+M=%>Gs{( zXVX3B-Hq*;xn%M-_al24-+WqfznMXyK`4A#S=z$<KY^(RUs=<QXZatDtX*j)zx%DA z(cj98`5Y_^j3ON~uT5p``@h%3=Ed{NvySfnx#rnU^Xt5Vuis56ui{ub!*;95lblHt zS`+#odv0<UI)34@ZMLJl;>jylv_5aVG$Ymhzz#mSz*h`R793p-Klti<Hd>d5xCxtV z+?-Lh^s0b%ichs`VC0J(>PKJm>ISY@>v{M0%lo{#8@XP}B~SgnpgQYr^D(dFFms7r z$%2<YpXXv~VBk_YTNNh}_VumN)Vl%PlUAIZyX3j+r|W;+w<jN6nC{wH6nNnO$N%+B zzePTkmfgElx9GC8>l}|u5s@>Lm5jec&2yVS{{yFN1cUjH*N)3wev4jG{N10W{Z6;q zzIW=eoa&Hpi6iMtz&4d-*1S}B{=>-J>E@=Ec`IK2Hq*H0KXq!!+SilB+OBvVtxt(| zUC#bW;o<YkZnqvSDY%*~_VD*f{ZQ9zk@M3_TNZ3QCG%_5>&goP%?wf-KUZG6Q5m+# zro`f%XJ%*X8H;?^pPkoF$_H59{_|4CZ1?t8a*4*(o%>h5nj!Xe>ovpH+bJdGk-LJ{ zNUjjPB)f8#du5u_=F^!C4E!87!<;9l`aiataq9Y=w@c2g*O0N^6Z^5wxoAn6W8{-3 zeL6MYy0e%Hf=+Nu$Wu*oEt~Y{@67u-!t3u(`ok2kNc-L4>!IH@Iyav%Ef(9$!qmX9 zE&KDoLl#|!&#mNf-=3_;EpyYYN=~=&(zi9icA1+?r%ifPxp%G_gKl_=_vS5k-*Y~X zG*io3yy$nzyj}eQ-(_D!{XBgkScGN570!u;B5^j$jOI+y4>#N|*zb9MvESK~^L4}0 z`gbO4U%ORYW#!vY^?c8wkkHG*3mm;Z@do`b-}*|9)xI`eaCe2rUVe@T3@e`RtFZc) z-G8dU=-<YZyY2)Wzt`+$uNWnAYqo9K6oy5=k8BWI^l;juW$CA9FRJrgRx7sXkMr!% z?<amg)b1|c$M&1Mfnn9xKMALI{XM}q_2Cnl-)9~b>@)W$jeBw9n@-tZmelxw<fUnc zwcI%c+yj;UjBYEZy^WZdA7J^crYC7`{U772mQPl9yc2qT!CQpoLd5ytii_V){4d;Z zAQSmWv*u6mn^T__zG@MUZ#&|BXjY$)!mqD-Iu)mkSACznL)}SemTt`7RT8hSUj7~W ze97Cm)i2E(7-ooUTsAF}iA!Hj+^y7CNo2XzuMd{`E6*)_67l9s`W&@}up1_S-r7Bi zdd0?|A6+xk`}ozU27mF4U%UzoYOcY;4v}$2T9M1ls$N~zNr^Hq{dwfcx<@bH>M*|0 z^fz+7tz7X+{>Rt(VeXr!eckM``OtYsZU&|uZo&aV(n}=mmoKk5aAWxuHrvJO<}dpE zgd2nk4Bz(#if`@BJpV{#zroj^JwFmM7#$d{XE3d}Umv^MIa5&fyKGMP3#Dg!b=E!C zVNBWYbww?9^@_FYdKcwfm&#ytXmHD9(prCY<)JHki%PCp)cU5jKAN*a^zhQB!hJ#x zKU8bpuG7fc9lI>@O4xcf?GsI63@im3Y9E#CRBMP@IU#WAp1<y%%eJ-unEf$r;UgKY z-XPA+n^^=r*RH>&aW7kEUz#NU{_V@8)`Z;s*E%iB=(r3E1EbUxmJ4bUQH6iM?A*I# zc6h)dt&9@u<s1%E=Ow-T`ZesSn#@kkYwvRRUfJ()ZQI+eX^ZckRuf=g*|A)$q3YR* zQ&T<Xx;~c(S~NxR`2KU^w`W*6Gi3Rx&Uydde!AH0RQ(sqy4$~f&eOffG$m@9@8;c) zce63dTxVhNd!b_Gb9<h%^p#Gd&Ww7`zX9IMIRfTiu`0f>?9Q$5;$j=A<OeKUrPkEh zZ?wF?)WFbtfn`FmjH&BhxAXC42ZGcDYoa3UOCB(1{+*!AvLnkkOZ4ZwfIZWvBsNK$ zW?ykNA^1s_!7D))T?UQ|8NLl+5tFsFU0a{}-`9Md*Y{?F%95P2xWX0&Cp*naQO~D_ z&K2Lc^-l5C`EU82I&m{FSuEx3h<d5(tCSf!F=&2Zw@y*QV$KDwC)!q7xf<nf<=?`f z(2#A)SSY=GLxQIyqwT|1=U;c@Mc?oL^}ar0-PTuE-Gm)hA5FZwo;!5Isw)Y}w>Vj) zL3+GR8F%$R@37qx^7XrP+a~YBa-!LFu8ecvf4^4xOS@N%VQcb~trkn>=GC-s6Zjss z@}S!`K`t{;8k(NP6jZi0rS9I^CtN!Js!pvFEfc+9>BJy(<wTT>TKztsFCV7&r5;IE zVq(>0;CK+^>u{`*|LEknUss;)=joK+znCY~?YFnJvxQT`uO-ubyAL1fPvcgdHBCzA z@dGuG1=q8fURC|QFmKl@J)XC%{R(e+5+|=IYGF`XshU~nVI6zwl+LTG3CHc4K}l|{ zn!^7pao#@v?p|M_dRjy^Xo_}<x+lnV2CuA>Q8GabWi>CR<Vv;je|}sCO4i4+m~MHS zGP2ssZ(VUHY*lDtNRm$_Q)W8HY`vU50SE1sqK4b&Z&|Z5&pIM#12~t32ww=0i4AjG zdsXs~AX7%K>*_U@`7=%X56TvRoI5>>$t#K3^tNo5(JRimIa(g8mxwL_o8+o`dz0An zDNmPHPio?gVh}j6bXLQrEfU_Dn^M?aR|zf63aqwrws2y&_$Xk~l~1x4y>1mYiDWzR zP7~t>g<O;`!&{^ETbr$HpSte2loIl?EVuWZAX7laRkM|$*NZhg7x^k2o#inn_B1&2 ztyMb^QWP63>bCyk{T%a$(@NV4S{Um7c;?T(l6A&n0V@OJoNFv5&rV941>coC6vuRB zdc+Nttx})8K`HZrkT1j9y~0va)yuW54P)l5zZY>hjw#@fugFc=yD#VHGl3kW<;!4s ze9O#vX<t<Je$1Pz_(qjM^^VJpuUG7TFAj9#y~Y{EAn@Q{29t|Mp;!9u^Ak(kHcV%T zT6&ZBsj|x>AE867pa|<!Q%H)dfBCHZt7OFNx;dQD_ku*%-t=y$$qwr73BS4g%ZxI& zswDPMHz`nvrOs+tF;#iVQ<m0iQZl#u&TKj-@J*;;n#HswVGDeHFWJ4FrDC^jMW`Jp zUw(HN7Wn<|N!x0J+^SE@|HXoRIbm|PXV%dhAxqy~S>vU8H0sW=l~A=4cKprVspXR# zF1Ao{!%Ln-ea1^`7lv|GYptscsNOC$(c*Un-xdai1Ai~FEUDQz=Vx`c#zvLjMXB;X zU9N(|PkpP`y4!o>XS&YZCIoUT>#PRf%b{&k_SGjJUbUe=P21Xe&W}U8zR8t@FIi^H zJ@eZft3xJhrMqYS+Oq5T@`uwlt()KPy^8Jf1-0v$j48i68yOTD+SDBOT{J1JoAC9Q zVCa%ytBngh=g!=@#^s{?lv9!-R`Et}5_Lt^inLbs>@O>SS+dP^dqH`yj?nSTb%}<P zdd=%R=hS9>ce}5^z_DO0=Zy914lh43L6vE7-?D(|W>M*r8&p4E5Zf5Ib*ap**{1?t zUT@kKuxM}H%Fo7sXUFZny>Y3jU2c>6B#)?TOWxl;z@%Ox;%$&MbHz$F>#f({EsIlN z;CL`W?LpkfFL##yZMhh9dQ!v0wXG{+pRJk1=>F-e-P)%o%NNv(dzn4hy)kV;gxJMf z^X|D@azy@#FIlnqnUcrOWqGe}#Lhav%;><Nx#EuFGR_@>MaO0Z%3gTzbNe;^iN82E zy_ndeu<7gHz4w++wGd9d`Pj;X)dXVgf`wLd+Ie+NB|xRuX%oiK>z`-k#Ma&o^W9S2 z6U3cx;f^0C3j?FfC6+6p6;U!5ug^MDadP<rL)Z16!rpTysIJW|=MrFGxuDm%?cJ}S zRov6_X1E7R7Zr7Cgiqi9C%~~y_QB<-6Z*N;9hVmRb8MYbrt_xzy%@^|{!-ijx!E7z z=T(XBys@b^FxcVAB*!&M>XBC0_Q;oc?+$5ZP;fYWGvVKq{hLY*F5B#A_Te&k#Z>$D z&BAu3UE4F}<~?3>zxIOOXT=jt%?y2u)`>W(^<?U?=iXkTx`3V0p`q>cqU-#L2b1Rs zJmuZwyL|Sq!rxhH+;eV3+WdXF`GGYnqtsHq#dd!V2G5^rb;S3pf^G4IDeIJsSVSc_ z9*AgM5;EDf)<Z3D>+`i5_E*)e_&!_^rm>|q>(I8Di#GIWxn)lGtZ7$hxU6t=`qHN! zr+=N`etXp0)XbOBWZJI6z_S9WwV7vrymMff-gIg~=3(wtAC`!zT*=v{*>KD$`Bc;m z_o!98DB}Z64WN+%CI$rt4hB%Ghk=EGL4bk5fq{X6k%44R#w?ZuRo2TqyPqY&2PgE) zZX4{$IUnT1^&n8=_U<_wySfg>^=z~gy`N(M9uE2WkXJ45_ufv1wOZ%jY9Cp`Ft6nl zH#^skx7!t%DkKlr`y2xat?DrLO!?i!@a#xJybsp{PlMM@`hT4o_B^@!>uj=tVS~HF z{z{$IC;jh+`ZI)uUEBZX#j+wFz6XMQA1>-XU32NecX6hI8H@DY5{nr88#M3PuR8bc zHD|!w`JZ<FTkJVQ;lSSM_IHk*{i#_WEZFe);;M$nkuy{dtX*uc`2Wn4ZTEuQ7}n3) z^Q8G#uCQTX!)?32*`FqTyPx}+i>ctxwThoSMNAFV>;5j*Qz@3NU!urVaaAl;U-SO{ zS7vHF1*>n(Z(gl^#x=4#=WVPj!|5Q6m+Tft8gA><Zu3pJy0de6>NY)92Hn;(ClfcS z%Ku1UYJA3<clg<q3le#k|3Bqm+%qHY{}jz{&!0^C-kvPf_>41e_pue9GuLd{vUu9u zY~cnkl`ifosm1kPx;J0+o@AMIck+FSzr|X+3m3aPo841jWZ`K$w`|FTioe%B&Inm> zbIs46LU!vGs_*~Hvf;tRS96YRep)>}G^wdkZQU26u=JyI)$Omz-QG5xVVXc@;<2pc zA1h1#NHO@nTX{bBl0>0Z@9h^}?m4#<nIbM;nlt%F?9%(&D}U)eHu!nK+5C^ze&xz% zF?Y9zoO`^&*1V0OG$Z-k#kb~hmh%JZKEx`^q&{eDydCv(;nvV&E9MKWS<1UsI{WxJ zfdzc~*J)~gT6glQtl80@tzoQlPpq=oxp<0|@053Q4t}}4oMSl)gACht*-eU@mUc~^ z|6+BMU)s#af1Q6ce=q1=Si!P=@Be~pb+L{N(SO<&XZ!v<yK(E$oyDuTH=Bh#f3c0J z;qZfP&Soy#j^wWDzPc?YYyJ0T28IhcAO4>{z?&XC@$dx(1_lOCS3j3^P6<r_Ijo&w literal 0 HcmV?d00001 diff --git a/assets/ui/button_resume_game.png b/assets/ui/button_resume_game.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ea0a02d05e42377eb551a4b51428b511a32f5d GIT binary patch literal 3659 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE69Bd2>3_*8t*cliYI14-?iy0W?oEaG8oERJS zYjH3zFi4iTMwA5Sr<If^7Ns(jmzV2h=4BTrCl;jY<rk&TerF@az`%RM)5S5Q;?~=_ zl@Xz-a>r-BocS|*o}0kdqnkW^Pp>%{w0TQOkm2T;ne!{<41Iz(osm7ec8!jDw2!VP ztEh{@bcdUc3W0{<B6`;1tf@QSe2;r@hqb==p7w_wjo)n-%z66z_;Y)G{(Uv}&-dN` zJn!>9J(nj;4PiS)?Z2E?;t!8}(R=9DvhQC)w~6jDx}C*Pk$5vvGAUBX*mQ$s^d{j4 zeMO7^PVFhm4w!Ur)-LVX3+pX2R?U_TbPxW1?NYr{_KV&Gh93|uzqF2q9N#o`#r7qe z)`VWWyg%aGE}sUrMfY|Yt*$RwH({g3t&~d51+(Jr@Eus&o_bdOn97l^o0lcm{7aI1 zzjH3r7a#r2?JRThH0nyzdH*Ra@UHYdldxdMQJ!Yoh%?K6uN8T~|10bEr42rc@(0<A zL=<g;PiU{mi7L`&DYz>6Ca_qkhgTv;A^n+)RO{uX+guDc-?egWuv+x5{aobb_`{*{ z8ZFn4GS%)lQMJu)L7lA7f+g2}h4{>yV9m)YuE8_yW`yDHq_+7}R2Vj^FFY{GSg@H> zgzI#k&$<cG-dCoFH|&?Q%eU;-whyq<XAcX`nrP~K=F40^?giT0Tlb4LS=tA%=LG8R zJtOvT3Il7X?`LtBhk5(~;@n4l#IKp0-8qwCo#0I;J~o~e6JN+E{=Rj4aX9CM%nf<T z@!}FX3*7FBEdM%_ZFhV$QwhKA?G?uz7)>`vUth8R)5Oj&#v-k^(g8J%w-P1K8{W7c zukPTq@2LtKM@Hilr_-9>a-vr=9CMTUnz`s^&q2nh2b)zEpS<Cq9L0KK-KO9Bgj!zm zUts$$ws~IahP)h6g{N~&IhmQaw9NQ<B#iM}I(MXm$O2WRqLwpaj8%V2*6ibHX4-OO znlP{Gn=%dIgddw&7K?&Jx+*5Sp7|H>?Z)RLJ14sFa9t4jytpSt?8M=vM|>vlJ^b20 z!y$OXnayu@i(J38{47T^gF`fk>&(EwsKC&`AR)rQ$k4#R;K0D3z`!8Dz`()4z{0@5 z#6XsC!?`D|^Yw!6w{l*&o2|ank8{ayyN(6v`%XXoXPLZx%B9Ki%Pua~?!VN~U$@wO zn*5^U;d2+HUw=CB_%$yUE~XQPe^jp|M`@JKmMIWjH<>x}#x0-ab;n-FcdtC(KJm$` zlXkoRw(an}>Buam-u-_Y$H69sPYd_^Un+f=zwr4Mfms{Y{?-28kzafw_Rq5n?H|`4 zS*bR#)z9&g`}f1Gfx)Pub&oEig!1**xBtAEaCP~uy|1>rpMSakLU8uE3oUO$Iiy%# zSjE_uZ_z%`k-zEooNpUe@0{`>{^Z{Gt)8!>8JJ2|GVtqrzbyE%{z>CbLz7?ZUzj-- z2ryQi=XxK)81?I5gX+mIht8*R$g@mX&yaKPot^29%d8oDlb>vDW~(onVzVtwp+&%< zj%ma1zvozft}8FQ@FO(v%dcB3Qu{1=s;XFvIuGQX=ll7{#P>$qA|6H&6$b8K%Wn7> zncjLK6sR5cTh(e>`hu0xb&rJ}=DoLl^nPNigA>C~?gNMFRhIu*_27QTi{PUsB^TGn zC~Y{p?Ya3pX9LBC8rF)}vzg6#|4+To@vqbJ%Z1+y&xxn{Z`9EDTeQ)By3zuWgWmX@ zERSjsJJZy=@6POJFL+{HZ*E-FvxtW=MWtcgj<)Cqu`cbiCdI*AI}6w7ZC>=}iFkUz z6vc*5tQikW5=Hm^an*?roVT;Rt#+w%21t0vCJp}2oR6ae=Lx%>|KG5Phw+KV0o6Lr zH;>wz_(eNhe~Lfv-t?pTd#r%_{||dR1e_UWau#^b;#i|mzuSIpm4Erai`n1!4lQ`v zp1Ef}V`3YFk+8!e4)N8mGuOpyl|HW!+Irmj;Fs^`{Vueu)i@>KkjK<v;whl`+DUis z&##Q@>I%fWj94ZtW(eBPE72*b`sI!LZ;S9Li(l;QZRN0FnJ|-Kx>eW|_L>JZf$yH} z{96?G?<G?cA7hHzfwwxR^4ZPApBAymF`e*X=v12Gp>gWXUpA3c4h4Tkm97c38?SvV zkh|aYDGwwR%xD!b#a;LIc~z(P-@jj6@s(A8f$4-#1E0v#z5C}cK3H7-Sv>VVH&~FN zQ|U^yp7_(>_nt-u@-1nUUz)K=!<k_xw?beG*J1~6rv8b)Z`Zi~+!%V?Wb1iNmVhn> zA=eXv-J7oeO4+x_#@X6cl1amtq0`mUQ|HuqPmNQ``_H#C2sorMm2A+kH!J5a50rZx z&BLhBaE9eY(;^$cR98u+6^;$-G)~>tQeXi2I;gwIb8gF`HGOCIC^Sg1PKcN?xpe85 zKSlc%*<?Hw<X~V-(P(fHym8ukjfTD5`Jao<d^2xl5OA2s<RP-~VAAsShtFGWojHBB z#j#|ODl;jT3m_N%@SP*}wc_`)-ErTe1^5{i8k!zVXs8vo*c^ADZ!t)Bsn7=Vi|^(O zt~sU0z_>++p`GJ@Ns{*Qe|DV$3`{G6TT&g5&%L!xkja4|)N6y;n`gEU{@oM=Nfk>y z*{S#PS$UJ#F=qyj1?QCBoLN1sziti}qe8>4yyjy|V+*oeE3Q=8vNSNfisD#p7M*dT zX|LZ~?^EZ(+ZY5KOdsm6GP%6Nf6<&>zxd-<ANDd}32^^#^XGA!_FuLZ`qSd>w6#A} zXgJiY|1{!y^`82rAOD@QUBJxrpmoNzyHDM8IG<OEFL?jQLTdX`p7+cS45|BsPFgHo zzr=I3rT>~QVaIPL2srpM6|ntxy82XhS>-vMcb2+WJ|37o>zqy-gO~UNG4VU6(|Jtf zk3C<%@9+<Wh9K4lJo}E`I}%%PvvAE{aoGhMXM1ln6k`c^cC90bJ8wR}vT31$IZMFH zXFGZ(CmpPP`B~x|v&px61wIN4EHA7WEt=zFGTjcgGH3}nBprNfI(7epsvn^lT0aCF z&M|rD?D(6$@PQKpuk57jK?U>UdYZTyr|2}aJeaSs;(^|d@8`G{r|&!M`a!Xwhh;)Q z!QNW_&j0$)o9`FzGUL``nXs8*&cXF7w_i|KRNu+{Zn1X%r1pB%Lwt-WN(bI*><DJc zIPfT?>fv*v78#}!E)1NX|GWr3>bJMAqV}L|e}OZ@Ob&&O5B#3!?AV*$dU^BffC9ya zIV=-C@U45&v|E-{oauxs1E;lqfq0C*NDPO9IirPOj_(%3DEIo$e2xcO8Ey$Kcq*|- z{n=;HJM7PoUwR?mEf~YGAcnE;dH99y`L5m?J1RMJSYA{y@}*7l;;M7pd9hGL)BoVx zukZ9tN_=-EeZSMlpe1rZ{Xuyq>veCZgLk`{xIx+Zz|Gw&4rcAo{2-lMc1vLCV)isP z>-Yj&=FA6|noY`#x%5~jtYqlfDDNKRSz5cB-OoGnpzL4eumZ&fCYAyjc9poB>G^g( zj_jW`KcxufcAZ^P`ER93(Mvm#7>)%wjK^{{pL1;#sq($=didY;#wKpYC+ZB`OQqd2 zpB}uMt#148W#OkAVT}w<3^TbD4sHAM?#zSTzMM<ir!Ui}Gmhj~V6dw@{LVL)jO>55 zpMKXIfBAf_VBNOvPcwWY4z@DHI?U%^e2ZuQ>~w+N&)WH)H~$rreG%}XdjJ0Oib73n zjJtTm4s2WI*c>L4`F*q8KhZ1YjxzNJYs<xXT@SW06g_;oe)EdsTQ00#_osQ9eBnFE zD#Ihq;c}%}R#)z7cgRJ~dnCaAu!&()lWmwm(cXK8&(pVCR@&Pi+de}sy7Y|U+Lwm9 zM?OXvasRn%T-F`DltpTjRqwIik^)*3H4dOHjD|k0fTU$7?>!V?$l#t<IjwfORioOu zUX!GNhGjG4Q&l!QJdW+i_|a9dnH8yhqvmk_>~U!?Lk$P-A9FJ_1UKKG%lTlFe)Ne$ z3m9|-C*GZLa?^W(2Nx`}mDpG_I-Q@dWshP#v1-GweL^A4jf}G%gc`oKocfJ-Et7<F z=9A@~YQk*n8I9*B`!3tXR<$dX!42GSW6uCJ+)hrrvz==~?uOd`VI0dG9qw-ENta&p zdY!Pt5#292i{+YGuAGsJO>Ue~`dymy!KBm;zxQ|MGcI_vx_;TgeNRuFiC1knF;~zs zLYU!|irM)|s%d+otC@C)Tr)WvDU!VEcmt#9L*Z<b*V=10uRr^{l*z#8^xOT51emve za4X<VopSTreT~=VA`QN+LEURI_1Tj?G;B(gv@5z-6?NuId<9Fv%FHEpA6ciZ>132} zS>U7Ab>s=>&aF(gH^R={7HJ3!%9a(HuKvWQiN&EY{NIln#p40a{#&MhEMrZ$!g=NW zhsx78uQy-hY$$s6=aI-ipX%_s<*XUYqTP1tX2mU8$;ix;@^9Jnf@N344p*%)zF8K= z!2a{9uie8)C%JvR4R;b|-kGoZ<XO0>?vs-y(;3e0NS_j2{>~#?qbgV3g&|h{&jRCK z*Ih-g`Mz23=g(WpapBNitL9Q~pLG-VSzJ?DpwWDr_4KVv7Qx9e`zGA`lW{mUsQ8W` zW7LktYwp>3hUfA7T#D{{<CTA<zVt%O>9Scfc5R>bPde(chAP8{Lz@m89oe+S>}U+A z_3f{>MNVw}w?@&g-=-gV^LOLopKo~>*FE)DV%Cy=vwq(C+*vEvZ(4g{>di3b1Kg+i uP79o#G-GA^tj$+fR_+yLVpt&YkKfy1c8Nh;O*R7q1B0ilpUXO@geCyfY-%<D literal 0 HcmV?d00001 diff --git a/assets/icons/button_start.png b/assets/ui/button_start.png similarity index 100% rename from assets/icons/button_start.png rename to assets/ui/button_start.png diff --git a/assets/ui/game_end.png b/assets/ui/game_end.png new file mode 100644 index 0000000000000000000000000000000000000000..876334279c1711b349a62131a33607eecf924eb6 GIT binary patch literal 7937 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 P3=9mOu6{1-oD!M<Ur^e& literal 0 HcmV?d00001 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/fastlane/metadata/android/en-US/changelogs/40.txt b/fastlane/metadata/android/en-US/changelogs/40.txt new file mode 100644 index 0000000..d4afd51 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40.txt @@ -0,0 +1 @@ +Improve/normalize game architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/40.txt b/fastlane/metadata/android/fr-FR/changelogs/40.txt new file mode 100644 index 0000000..6a9871a --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/40.txt @@ -0,0 +1 @@ +Amélioration/normalisation de l'architecture du jeu. diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh deleted file mode 100755 index 7368dc4..0000000 --- a/icons/build_game_icons.sh +++ /dev/null @@ -1,78 +0,0 @@ -#! /bin/bash - -# Check dependencies -command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; } -command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; } -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}")" -ASSETS_DIR="${BASE_DIR}/assets" - -OPTIPNG_OPTIONS="-preserve -quiet -o7" -ICON_SIZE=192 - -####################################################### - -# Game images -AVAILABLE_GAME_IMAGES=" - button_back - button_start - placeholder -" - -####################################################### - -# optimize svg -function optimize_svg() { - SOURCE="$1" - - cp ${SOURCE} ${SOURCE}.tmp - scour \ - --remove-descriptive-elements \ - --enable-id-stripping \ - --enable-viewboxing \ - --enable-comment-stripping \ - --nindent=4 \ - --quiet \ - -i ${SOURCE}.tmp \ - -o ${SOURCE} - rm ${SOURCE}.tmp -} - -# build icons -function build_icon() { - SOURCE="$1" - TARGET="$2" - - echo "Building ${TARGET}" - - if [ ! -f "${SOURCE}" ]; then - echo "Missing file: ${SOURCE}" - exit 1 - fi - - optimize_svg "${SOURCE}" - - inkscape \ - --export-width=${ICON_SIZE} \ - --export-height=${ICON_SIZE} \ - --export-filename=${TARGET} \ - ${SOURCE} - - optipng ${OPTIPNG_OPTIONS} ${TARGET} -} - -####################################################### - -# Create output folders -mkdir -p ${ASSETS_DIR}/icons - -# Delete existing generated images -find ${ASSETS_DIR}/icons -type f -name "*.png" -delete - -# build game images -for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES} -do - build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/icons/${GAME_IMAGE}.png -done diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart index a1556ef..68fbb4e 100644 --- a/lib/config/default_game_settings.dart +++ b/lib/config/default_game_settings.dart @@ -10,42 +10,70 @@ class DefaultGameSettings { ]; // items count: available values - static const int itemsCountValueNoLimit = 0; - static const int itemsCountValueShort = 5; - static const int itemsCountValueMedium = 10; - static const int itemsCountValueLong = 20; - static const List<int> allowedItemsCountValues = [ + static const String itemsCountValueNoLimit = 'nolimit'; + static const String itemsCountValueShort = 'short'; + static const String itemsCountValueMedium = 'medium'; + static const String itemsCountValueLong = 'long'; + static const List<String> allowedItemsCountValues = [ itemsCountValueNoLimit, itemsCountValueShort, itemsCountValueMedium, itemsCountValueLong, ]; // items count: default value - static const int defaultItemsCountValue = itemsCountValueMedium; + static const String defaultItemsCountValue = itemsCountValueMedium; // timer value: available values - static const int timerValueNoTimer = 0; - static const int timerValueLow = 10; - static const int timerValueMedium = 30; - static const int timerValueHigh = 60; - static const List<int> allowedTimerValues = [ + static const String timerValueNoTimer = 'nolimit'; + static const String timerValueLow = 'low'; + static const String timerValueMedium = 'medium'; + static const String timerValueHigh = 'high'; + static const List<String> allowedTimerValues = [ timerValueNoTimer, timerValueLow, timerValueMedium, timerValueHigh, ]; // timer value: default value - static const int defaultTimerValue = timerValueMedium; + static const String defaultTimerValue = timerValueMedium; - static List<int> getAvailableValues(String parameterCode) { + // available values from parameter code + static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { - case 'itemsCount': + case parameterCodeItemsCount: return DefaultGameSettings.allowedItemsCountValues; - case 'timerValue': + case parameterCodeTimerValue: return DefaultGameSettings.allowedTimerValues; } printlog('Did not find any available value for game parameter "$parameterCode".'); return []; } + + static int getItemsCountValueFromCode(String itemsCountParameterValue) { + const Map<String, int> values = { + DefaultGameSettings.itemsCountValueNoLimit: 0, + DefaultGameSettings.itemsCountValueShort: 5, + DefaultGameSettings.itemsCountValueMedium: 10, + DefaultGameSettings.itemsCountValueLong: 20, + }; + return values[itemsCountParameterValue] ?? + getItemsCountValueFromCode(DefaultGameSettings.defaultItemsCountValue); + } + + static int getTimerValueFromCode(String timerValueParameterValue) { + const Map<String, int> values = { + timerValueNoTimer: 0, + timerValueLow: 5, + timerValueMedium: 30, + timerValueHigh: 90, + }; + return values[timerValueParameterValue] ?? + getTimerValueFromCode(DefaultGameSettings.defaultTimerValue); + } + + // 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 index 363c2a1..5201cc6 100644 --- a/lib/config/default_global_settings.dart +++ b/lib/config/default_global_settings.dart @@ -1,10 +1,33 @@ import 'package:petitbac/utils/tools.dart'; class DefaultGlobalSettings { - static const List<String> availableParameters = []; + // 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; + } - static List<int> getAvailableValues(String parameterCode) { 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 index d875b6b..437003f 100644 --- a/lib/config/menu.dart +++ b/lib/config/menu.dart @@ -6,12 +6,10 @@ import 'package:petitbac/ui/screens/page_game.dart'; import 'package:petitbac/ui/screens/page_settings.dart'; class MenuItem { - final String code; final Icon icon; final Widget page; const MenuItem({ - required this.code, required this.icon, required this.page, }); @@ -20,21 +18,18 @@ class MenuItem { class Menu { static const indexGame = 0; static const menuItemGame = MenuItem( - code: 'bottom_nav_game', icon: Icon(UniconsLine.home), page: PageGame(), ); static const indexSettings = 1; static const menuItemSettings = MenuItem( - code: 'bottom_nav_settings', icon: Icon(UniconsLine.setting), page: PageSettings(), ); static const indexAbout = 2; static const menuItemAbout = MenuItem( - code: 'bottom_nav_about', icon: Icon(UniconsLine.info_circle), page: PageAbout(), ); diff --git a/lib/config/theme.dart b/lib/config/theme.dart index be39034..74f532f 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light( secondary: primarySwatch.shade500, onSecondary: Colors.white, error: errorColor, - background: textSwatch.shade200, - onBackground: textSwatch.shade500, onSurface: textSwatch.shade500, surface: textSwatch.shade50, - surfaceVariant: Colors.white, + surfaceContainerHighest: Colors.white, shadow: textSwatch.shade900.withOpacity(.1), ); @@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark( secondary: primarySwatch.shade500, onSecondary: Colors.white, error: errorColor, - background: const Color(0xFF171724), - onBackground: textSwatch.shade400, onSurface: textSwatch.shade300, surface: const Color(0xFF262630), - surfaceVariant: const Color(0xFF282832), + surfaceContainerHighest: const Color(0xFF282832), shadow: textSwatch.shade900.withOpacity(.2), ); @@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith( ), ), ); - -final ThemeData appTheme = darkTheme; diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart index d310d36..13f7460 100644 --- a/lib/cubit/game_cubit.dart +++ b/lib/cubit/game_cubit.dart @@ -6,17 +6,17 @@ import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:petitbac/data/fetch_data_helper.dart'; -import 'package:petitbac/models/data/game_item.dart'; -import 'package:petitbac/models/game.dart'; -import 'package:petitbac/models/settings_game.dart'; -import 'package:petitbac/models/settings_global.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/models/game/game_item.dart'; +import 'package:petitbac/models/settings/settings_game.dart'; +import 'package:petitbac/models/settings/settings_global.dart'; part 'game_state.dart'; class GameCubit extends HydratedCubit<GameState> { GameCubit() : super(GameState( - currentGame: Game.createNull(), + currentGame: Game.createEmpty(), )); void updateState(Game game) { @@ -27,13 +27,19 @@ class GameCubit extends HydratedCubit<GameState> { void refresh() { final Game game = Game( + // Settings gameSettings: state.currentGame.gameSettings, globalSettings: state.currentGame.globalSettings, - items: state.currentGame.items, + // State isRunning: state.currentGame.isRunning, + isStarted: state.currentGame.isStarted, isFinished: state.currentGame.isFinished, - countdown: state.currentGame.countdown, + animationInProgress: state.currentGame.animationInProgress, + // Base data + items: state.currentGame.items, + // Game data position: state.currentGame.position, + countdown: state.currentGame.countdown, ); // game.dump(); @@ -44,7 +50,8 @@ class GameCubit extends HydratedCubit<GameState> { required GameSettings gameSettings, required GlobalSettings globalSettings, }) { - Game newGame = Game.createNew( + final Game newGame = Game.createNew( + // Settings gameSettings: gameSettings, globalSettings: globalSettings, ); @@ -60,8 +67,19 @@ class GameCubit extends HydratedCubit<GameState> { refresh(); } + void resumeSavedGame() { + state.currentGame.isRunning = true; + refresh(); + } + + void deleteSavedGame() { + state.currentGame.isRunning = false; + state.currentGame.isFinished = true; + refresh(); + } + void next() { - if (state.currentGame.position < (state.currentGame.gameSettings.itemsCount - 1)) { + if (state.currentGame.position < (state.currentGame.gameSettings.itemsCountValue - 1)) { state.currentGame.position++; startTimer(); } else { @@ -71,7 +89,7 @@ class GameCubit extends HydratedCubit<GameState> { } void startTimer() { - int timerValue = state.currentGame.gameSettings.timerValue; + int timerValue = state.currentGame.gameSettings.timerCountValue; if (timerValue != 0) { state.currentGame.countdown = timerValue; @@ -88,7 +106,7 @@ class GameCubit extends HydratedCubit<GameState> { if (state.currentGame.countdown == 0) { timer.cancel(); if (state.currentGame.position == - (state.currentGame.gameSettings.itemsCount - 1)) { + (state.currentGame.gameSettings.itemsCountValue - 1)) { state.currentGame.isFinished = true; } } @@ -97,7 +115,7 @@ class GameCubit extends HydratedCubit<GameState> { }, ); } else { - if (state.currentGame.position == (state.currentGame.gameSettings.itemsCount - 1)) { + if (state.currentGame.position == (state.currentGame.gameSettings.itemsCountValue - 1)) { state.currentGame.isFinished = true; } } @@ -132,7 +150,7 @@ class GameCubit extends HydratedCubit<GameState> { @override GameState? fromJson(Map<String, dynamic> json) { - Game currentGame = json['currentGame'] as Game; + final Game currentGame = json['currentGame'] as Game; return GameState( currentGame: currentGame, diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart index 8581d72..00e2116 100644 --- a/lib/cubit/game_state.dart +++ b/lib/cubit/game_state.dart @@ -9,7 +9,7 @@ class GameState extends Equatable { final Game currentGame; @override - List<Object?> get props => <Object?>[ + List<dynamic> get props => <dynamic>[ currentGame, ]; } diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart index b31cae3..677c18c 100644 --- a/lib/cubit/settings_game_cubit.dart +++ b/lib/cubit/settings_game_cubit.dart @@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:petitbac/models/settings_game.dart'; -import 'package:petitbac/utils/tools.dart'; +import 'package:petitbac/config/default_game_settings.dart'; +import 'package:petitbac/models/settings/settings_game.dart'; part 'settings_game_state.dart'; @@ -11,8 +11,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); void setValues({ - int? itemsCount, - int? timerValue, + String? itemsCount, + String? timerValue, }) { emit( GameSettingsState( @@ -24,22 +24,24 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { ); } - int getParameterValue(String code) { + String getParameterValue(String code) { switch (code) { - case 'itemsCount': + case DefaultGameSettings.parameterCodeItemsCount: return GameSettings.getItemsCountValueFromUnsafe(state.settings.itemsCount); - case 'timerValue': + case DefaultGameSettings.parameterCodeTimerValue: return GameSettings.getTimerValueFromUnsafe(state.settings.timerValue); } - return 0; - } - void setParameterValue(String code, int value) { - printlog('GameSettingsCubit.setParameterValue'); - printlog('code: $code / value: $value'); + return ''; + } - int itemsCount = code == 'itemsCount' ? value : getParameterValue('itemsCount'); - int timerValue = code == 'timerValue' ? value : getParameterValue('timerValue'); + void setParameterValue(String code, String value) { + final String itemsCount = code == DefaultGameSettings.parameterCodeItemsCount + ? value + : getParameterValue(DefaultGameSettings.parameterCodeItemsCount); + final String timerValue = code == DefaultGameSettings.parameterCodeTimerValue + ? value + : getParameterValue(DefaultGameSettings.parameterCodeTimerValue); setValues( itemsCount: itemsCount, @@ -49,8 +51,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { @override GameSettingsState? fromJson(Map<String, dynamic> json) { - int itemsCount = json['itemsCount'] as int; - int timerValue = json['timerValue'] as int; + final String itemsCount = json[DefaultGameSettings.parameterCodeItemsCount] as String; + final String timerValue = json[DefaultGameSettings.parameterCodeTimerValue] as String; return GameSettingsState( settings: GameSettings( @@ -63,8 +65,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { @override Map<String, dynamic>? toJson(GameSettingsState state) { return <String, dynamic>{ - 'itemsCount': state.settings.itemsCount, - 'timerValue': state.settings.timerValue, + DefaultGameSettings.parameterCodeItemsCount: state.settings.itemsCount, + DefaultGameSettings.parameterCodeTimerValue: state.settings.timerValue, }; } } diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart index b773dc6..5acd85b 100644 --- a/lib/cubit/settings_game_state.dart +++ b/lib/cubit/settings_game_state.dart @@ -12,8 +12,4 @@ class GameSettingsState extends Equatable { List<dynamic> get props => <dynamic>[ settings, ]; - - Map<String, dynamic> get values => <String, dynamic>{ - 'settings': settings, - }; } diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings_global_cubit.dart index 43f64fe..c4bfba2 100644 --- a/lib/cubit/settings_global_cubit.dart +++ b/lib/cubit/settings_global_cubit.dart @@ -2,43 +2,59 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:petitbac/models/settings_global.dart'; -import 'package:petitbac/utils/tools.dart'; +import 'package:petitbac/config/default_global_settings.dart'; +import 'package:petitbac/models/settings/settings_global.dart'; part 'settings_global_state.dart'; class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault())); - void setValues() { + void setValues({ + String? skin, + }) { emit( GlobalSettingsState( - settings: GlobalSettings(), + settings: GlobalSettings( + skin: skin ?? state.settings.skin, + ), ), ); } - int getParameterValue(String code) { - switch (code) {} - return 0; + String getParameterValue(String code) { + switch (code) { + case DefaultGlobalSettings.parameterCodeSkin: + return GlobalSettings.getSkinValueFromUnsafe(state.settings.skin); + } + return ''; } - void setParameterValue(String code, int value) { - printlog('GlobalSettingsCubit.setParameterValue'); - printlog('code: $code / value: $value'); + void setParameterValue(String code, String value) { + final String skin = (code == DefaultGlobalSettings.parameterCodeSkin) + ? value + : getParameterValue(DefaultGlobalSettings.parameterCodeSkin); - setValues(); + setValues( + skin: skin, + ); } @override GlobalSettingsState? fromJson(Map<String, dynamic> json) { + final String skin = json[DefaultGlobalSettings.parameterCodeSkin] as String; + return GlobalSettingsState( - settings: GlobalSettings(), + settings: GlobalSettings( + skin: skin, + ), ); } @override Map<String, dynamic>? toJson(GlobalSettingsState state) { - return <String, dynamic>{}; + return <String, dynamic>{ + DefaultGlobalSettings.parameterCodeSkin: state.settings.skin, + }; } } diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings_global_state.dart index 4e4fbdf..ebcddd7 100644 --- a/lib/cubit/settings_global_state.dart +++ b/lib/cubit/settings_global_state.dart @@ -12,8 +12,4 @@ class GlobalSettingsState extends Equatable { List<dynamic> get props => <dynamic>[ settings, ]; - - Map<String, dynamic> get values => <String, dynamic>{ - 'settings': settings, - }; } diff --git a/lib/data/fetch_data_helper.dart b/lib/data/fetch_data_helper.dart index dc79648..f93d583 100644 --- a/lib/data/fetch_data_helper.dart +++ b/lib/data/fetch_data_helper.dart @@ -2,8 +2,8 @@ import 'dart:math'; import 'package:petitbac/data/game_data.dart'; import 'package:petitbac/models/data/category.dart'; -import 'package:petitbac/models/data/game_item.dart'; import 'package:petitbac/models/data/letter.dart'; +import 'package:petitbac/models/game/game_item.dart'; import 'package:petitbac/utils/tools.dart'; class FetchDataHelper { diff --git a/lib/main.dart b/lib/main.dart index 4c088e7..0519f0f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive/hive.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; @@ -25,18 +26,17 @@ void main() async { storageDirectory: tmpDir, ); - runApp( - EasyLocalization( - path: 'assets/translations', - supportedLocales: const <Locale>[ - Locale('en'), - Locale('fr'), - ], - fallbackLocale: const Locale('en'), - useFallbackTranslations: true, - child: const MyApp(), - ), - ); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .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 { @@ -44,6 +44,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + final List<String> assets = getImagesAssets(); + for (String asset in assets) { + precacheImage(AssetImage(asset), context); + } + return MultiBlocProvider( providers: [ BlocProvider<NavCubit>(create: (context) => NavCubit()), @@ -73,4 +78,23 @@ class MyApp extends StatelessWidget { ), ); } + + List<String> getImagesAssets() { + final List<String> assets = []; + + const List<String> gameImages = [ + 'button_back', + 'button_delete_saved_game', + 'button_resume_game', + 'button_start', + 'game_end', + 'placeholder', + ]; + + for (String image in gameImages) { + assets.add('assets/ui/$image.png'); + } + + return assets; + } } diff --git a/lib/models/game.dart b/lib/models/game.dart deleted file mode 100644 index 9cdb175..0000000 --- a/lib/models/game.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:petitbac/data/fetch_data_helper.dart'; -import 'package:petitbac/models/data/category.dart'; -import 'package:petitbac/models/data/game_item.dart'; -import 'package:petitbac/models/data/letter.dart'; -import 'package:petitbac/models/settings_game.dart'; -import 'package:petitbac/models/settings_global.dart'; -import 'package:petitbac/utils/tools.dart'; - -class Game { - final List<GameItem> items; - final GameSettings gameSettings; - final GlobalSettings globalSettings; - int countdown = 0; - int position = 0; - bool isRunning = false; - bool isFinished = false; - - Game({ - required this.items, - required this.gameSettings, - required this.globalSettings, - required this.countdown, - required this.position, - this.isRunning = false, - this.isFinished = false, - }); - - factory Game.createNull() { - return Game( - items: [], - gameSettings: GameSettings.createDefault(), - globalSettings: GlobalSettings.createDefault(), - countdown: 0, - position: 0, - ); - } - - factory Game.createNew({ - GameSettings? gameSettings, - GlobalSettings? globalSettings, - }) { - GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); - GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); - - List<GameItem> items = FetchDataHelper().getRandomItems(newGameSettings.itemsCount); - - return Game( - items: items, - gameSettings: newGameSettings, - globalSettings: newGlobalSettings, - countdown: 0, - position: 0, - isRunning: true, - isFinished: false, - ); - } - - void stop() { - isRunning = false; - } - - GameItem get item => items[position]; - Category get category => item.category; - Letter get letter => item.letter; - - void dump() { - printlog(''); - printlog('## Current game dump:'); - printlog(''); - gameSettings.dump(); - globalSettings.dump(); - printlog(''); - printlog('items:'); - printlog(items.toString()); - printlog(''); - printlog('Game: '); - printlog(' isRunning: $isRunning'); - printlog(' isFinished: $isFinished'); - printlog(' position: $position'); - printlog(' countdown: $countdown'); - printlog(''); - } - - @override - String toString() { - return '$Game(${toJson()})'; - } - - Map<String, dynamic>? toJson() { - return <String, dynamic>{ - 'items': items, - 'gameSettings': gameSettings, - 'globalSettings': globalSettings, - 'countdown': countdown, - 'position': position, - 'isRunning': isRunning, - 'isFinished': isFinished, - }; - } -} diff --git a/lib/models/game/game.dart b/lib/models/game/game.dart new file mode 100644 index 0000000..d9c4f87 --- /dev/null +++ b/lib/models/game/game.dart @@ -0,0 +1,125 @@ +import 'package:petitbac/data/fetch_data_helper.dart'; +import 'package:petitbac/models/data/category.dart'; +import 'package:petitbac/models/data/letter.dart'; +import 'package:petitbac/models/game/game_item.dart'; +import 'package:petitbac/models/settings/settings_game.dart'; +import 'package:petitbac/models/settings/settings_global.dart'; +import 'package:petitbac/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, + + // Base data + required this.items, + + // Game data + this.position = 0, + this.countdown = 0, + }); + + // Settings + final GameSettings gameSettings; + final GlobalSettings globalSettings; + + // State + bool isRunning; + bool isStarted; + bool isFinished; + bool animationInProgress; + + // Base data + final List<GameItem> items; + + // Game data + int position; + int countdown; + + factory Game.createEmpty() { + return Game( + gameSettings: GameSettings.createDefault(), + globalSettings: GlobalSettings.createDefault(), + items: [], + ); + } + + factory Game.createNew({ + GameSettings? gameSettings, + GlobalSettings? globalSettings, + }) { + final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); + final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); + + final List<GameItem> items = + FetchDataHelper().getRandomItems(newGameSettings.itemsCountValue); + + return Game( + // Settings + gameSettings: newGameSettings, + globalSettings: newGlobalSettings, + // State + isRunning: true, + isStarted: true, + // Base data + items: items, + ); + } + + bool get canBeResumed => isStarted && !isFinished; + + GameItem get item => items[position]; + Category get category => item.category; + Letter get letter => item.letter; + + 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(' Base data'); + printlog(' items: $items'); + printlog(' Game data'); + printlog(' position: $position'); + printlog(' countdown: $countdown'); + 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, + // Base data + 'items': items, + // Game data + 'position': position, + 'countdown': countdown, + }; + } +} diff --git a/lib/models/data/game_item.dart b/lib/models/game/game_item.dart similarity index 100% rename from lib/models/data/game_item.dart rename to lib/models/game/game_item.dart diff --git a/lib/models/settings_game.dart b/lib/models/settings/settings_game.dart similarity index 57% rename from lib/models/settings_game.dart rename to lib/models/settings/settings_game.dart index 5262546..fe3d610 100644 --- a/lib/models/settings_game.dart +++ b/lib/models/settings/settings_game.dart @@ -2,15 +2,18 @@ import 'package:petitbac/config/default_game_settings.dart'; import 'package:petitbac/utils/tools.dart'; class GameSettings { - final int itemsCount; - final int timerValue; + final String itemsCount; + final String timerValue; GameSettings({ required this.itemsCount, required this.timerValue, }); - static int getItemsCountValueFromUnsafe(int itemsCount) { + int get itemsCountValue => DefaultGameSettings.getItemsCountValueFromCode(itemsCount); + int get timerCountValue => DefaultGameSettings.getTimerValueFromCode(timerValue); + + static String getItemsCountValueFromUnsafe(String itemsCount) { if (DefaultGameSettings.allowedItemsCountValues.contains(itemsCount)) { return itemsCount; } @@ -18,7 +21,7 @@ class GameSettings { return DefaultGameSettings.defaultItemsCountValue; } - static int getTimerValueFromUnsafe(int timerValue) { + static String getTimerValueFromUnsafe(String timerValue) { if (DefaultGameSettings.allowedTimerValues.contains(timerValue)) { return timerValue; } @@ -34,9 +37,10 @@ class GameSettings { } void dump() { - printlog('Game settings: '); - printlog(' itemsCount: $itemsCount'); - printlog(' timerValue: $timerValue'); + printlog('$GameSettings:'); + printlog(' ${DefaultGameSettings.parameterCodeItemsCount}: $itemsCount'); + printlog(' ${DefaultGameSettings.parameterCodeTimerValue}: $timerValue'); + printlog(''); } @override @@ -46,8 +50,8 @@ class GameSettings { Map<String, dynamic>? toJson() { return <String, dynamic>{ - 'itemsCount': itemsCount, - 'timerValue': timerValue, + DefaultGameSettings.parameterCodeItemsCount: itemsCount, + DefaultGameSettings.parameterCodeTimerValue: timerValue, }; } } diff --git a/lib/models/settings/settings_global.dart b/lib/models/settings/settings_global.dart new file mode 100644 index 0000000..6ac348a --- /dev/null +++ b/lib/models/settings/settings_global.dart @@ -0,0 +1,41 @@ +import 'package:petitbac/config/default_global_settings.dart'; +import 'package:petitbac/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/models/settings_global.dart b/lib/models/settings_global.dart deleted file mode 100644 index 2448620..0000000 --- a/lib/models/settings_global.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:petitbac/utils/tools.dart'; - -class GlobalSettings { - GlobalSettings(); - - factory GlobalSettings.createDefault() { - return GlobalSettings(); - } - - void dump() { - printlog('Global settings: '); - printlog(' (none)'); - } - - @override - String toString() { - return '$GlobalSettings(${toJson()})'; - } - - Map<String, dynamic>? toJson() { - return <String, dynamic>{}; - } -} diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart new file mode 100644 index 0000000..045c4d8 --- /dev/null +++ b/lib/ui/game/game_end.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:petitbac/cubit/game_cubit.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/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_end.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], + ), + const Column( + children: [decorationImage], + ), + Column( + children: [ + currentGame.animationInProgress == true + ? decorationImage + : const QuitGameButton() + ], + ), + const Column( + children: [decorationImage], + ), + 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 0000000..2f7e2c7 --- /dev/null +++ b/lib/ui/game/game_top.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:petitbac/cubit/game_cubit.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/widgets/game/game_position_indicator.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.gameSettings.itemsCountValue != 0 + ? const GamePositionIndicator() + : const SizedBox.shrink(); + }, + ); + } +} diff --git a/lib/ui/helpers/app_titles.dart b/lib/ui/helpers/app_titles.dart new file mode 100644 index 0000000..b98107b --- /dev/null +++ b/lib/ui/helpers/app_titles.dart @@ -0,0 +1,32 @@ +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/widgets/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart similarity index 100% rename from lib/ui/widgets/helpers/outlined_text_widget.dart rename to lib/ui/helpers/outlined_text_widget.dart diff --git a/lib/ui/layouts/game_layout.dart b/lib/ui/layouts/game_layout.dart new file mode 100644 index 0000000..27aaafc --- /dev/null +++ b/lib/ui/layouts/game_layout.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:petitbac/cubit/game_cubit.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/game/game_end.dart'; +import 'package:petitbac/ui/game/game_top.dart'; +import 'package:petitbac/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 Expanded(child: SizedBox.shrink()), + currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/widgets/parameters.dart b/lib/ui/layouts/parameters_layout.dart similarity index 52% rename from lib/ui/widgets/parameters.dart rename to lib/ui/layouts/parameters_layout.dart index c13ccba..0751974 100644 --- a/lib/ui/widgets/parameters.dart +++ b/lib/ui/layouts/parameters_layout.dart @@ -5,11 +5,16 @@ import 'package:petitbac/config/default_game_settings.dart'; import 'package:petitbac/config/default_global_settings.dart'; import 'package:petitbac/cubit/settings_game_cubit.dart'; import 'package:petitbac/cubit/settings_global_cubit.dart'; -import 'package:petitbac/ui/painters/parameter_painter.dart'; -import 'package:petitbac/ui/widgets/button_game_start_new.dart'; +import 'package:petitbac/ui/parameters/parameter_image.dart'; +import 'package:petitbac/ui/parameters/parameter_painter.dart'; +import 'package:petitbac/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:petitbac/ui/widgets/actions/button_game_start_new.dart'; +import 'package:petitbac/ui/widgets/actions/button_resume_saved_game.dart'; -class Parameters extends StatelessWidget { - const Parameters({super.key}); +class ParametersLayout extends StatelessWidget { + const ParametersLayout({super.key, required this.canResume}); + + final bool canResume; final double separatorHeight = 8.0; @@ -31,7 +36,24 @@ class Parameters extends StatelessWidget { } lines.add(SizedBox(height: separatorHeight)); - lines.add(const Expanded(child: StartNewGameButton())); + + 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 @@ -58,7 +80,7 @@ class Parameters extends StatelessWidget { }) { final List<Widget> parameterButtons = []; - final List<int> availableValues = isGlobal + final List<String> availableValues = isGlobal ? DefaultGlobalSettings.getAvailableValues(code) : DefaultGameSettings.getAvailableValues(code); @@ -66,7 +88,7 @@ class Parameters extends StatelessWidget { return []; } - for (int value in availableValues) { + for (String value in availableValues) { final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>( builder: (BuildContext context, GameSettingsState gameSettingsState) { return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( @@ -76,7 +98,7 @@ class Parameters extends StatelessWidget { final GlobalSettingsCubit globalSettingsCubit = BlocProvider.of<GlobalSettingsCubit>(context); - final int currentValue = isGlobal + final String currentValue = isGlobal ? globalSettingsCubit.getParameterValue(code) : gameSettingsCubit.getParameterValue(code); @@ -85,22 +107,39 @@ class Parameters extends StatelessWidget { 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: CustomPaint( - size: Size(itemWidth, itemWidth), - willChange: false, - painter: ParameterPainter( - code: code, - value: value, - isSelected: isActive, - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - ), - isComplex: true, + 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), + onPressed: () { + isGlobal + ? globalSettingsCubit.setParameterValue(code, value) + : gameSettingsCubit.setParameterValue(code, value); + }, ); }, ); diff --git a/lib/ui/parameters/parameter_image.dart b/lib/ui/parameters/parameter_image.dart new file mode 100644 index 0000000..fc4b576 --- /dev/null +++ b/lib/ui/parameters/parameter_image.dart @@ -0,0 +1,38 @@ +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/painters/parameter_painter.dart b/lib/ui/parameters/parameter_painter.dart similarity index 87% rename from lib/ui/painters/parameter_painter.dart rename to lib/ui/parameters/parameter_painter.dart index 2509c75..49e5190 100644 --- a/lib/ui/painters/parameter_painter.dart +++ b/lib/ui/parameters/parameter_painter.dart @@ -3,8 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:petitbac/config/default_game_settings.dart'; -import 'package:petitbac/models/settings_game.dart'; -import 'package:petitbac/models/settings_global.dart'; +import 'package:petitbac/models/settings/settings_game.dart'; +import 'package:petitbac/models/settings/settings_global.dart'; import 'package:petitbac/utils/tools.dart'; class ParameterPainter extends CustomPainter { @@ -17,7 +17,7 @@ class ParameterPainter extends CustomPainter { }); final String code; - final int value; + final String value; final bool isSelected; final GameSettings gameSettings; final GlobalSettings globalSettings; @@ -60,7 +60,7 @@ class ParameterPainter extends CustomPainter { // "unknown" parameter -> simple block with text void paintUnknownParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { @@ -96,14 +96,14 @@ class ParameterPainter extends CustomPainter { } void paintItemsCountParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { - const itemCountEmoji = '💬\n'; + const itemCountEmoji = '💬'; Color backgroundColor = Colors.grey; - String text = ''; + String text = '$itemCountEmoji\n${DefaultGameSettings.getItemsCountValueFromCode(value)}'; switch (value) { case DefaultGameSettings.itemsCountValueNoLimit: @@ -112,15 +112,12 @@ class ParameterPainter extends CustomPainter { break; case DefaultGameSettings.itemsCountValueShort: backgroundColor = Colors.green; - text = itemCountEmoji + DefaultGameSettings.itemsCountValueShort.toString(); break; case DefaultGameSettings.itemsCountValueMedium: backgroundColor = Colors.orange; - text = itemCountEmoji + DefaultGameSettings.itemsCountValueMedium.toString(); break; case DefaultGameSettings.itemsCountValueLong: backgroundColor = Colors.red; - text = itemCountEmoji + DefaultGameSettings.itemsCountValueLong.toString(); break; default: printlog('Wrong value for itemsCount parameter value: $value'); @@ -160,14 +157,14 @@ class ParameterPainter extends CustomPainter { } void paintTimerParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { - const timerEmoji = '⏲️\n'; + const timerEmoji = '⏲️'; Color backgroundColor = Colors.grey; - String text = ''; + String text = '$timerEmoji\n${DefaultGameSettings.getTimerValueFromCode(value)}"'; switch (value) { case DefaultGameSettings.timerValueNoTimer: @@ -176,15 +173,12 @@ class ParameterPainter extends CustomPainter { break; case DefaultGameSettings.timerValueLow: backgroundColor = Colors.green; - text = '$timerEmoji${DefaultGameSettings.timerValueLow}"'; break; case DefaultGameSettings.timerValueMedium: backgroundColor = Colors.orange; - text = '$timerEmoji${DefaultGameSettings.timerValueMedium}"'; break; case DefaultGameSettings.timerValueHigh: backgroundColor = Colors.red; - text = '$timerEmoji${DefaultGameSettings.timerValueHigh}"'; break; default: printlog('Wrong value for itemsCount parameter value: $value'); @@ -192,7 +186,7 @@ class ParameterPainter extends CustomPainter { final paint = Paint(); paint.strokeJoin = StrokeJoin.round; - paint.strokeWidth = 3 / 100 * size; + paint.strokeWidth = 3; // Colored background paint.color = backgroundColor; diff --git a/lib/ui/screens/page_about.dart b/lib/ui/screens/page_about.dart index 1720556..8f2a3d6 100644 --- a/lib/ui/screens/page_about.dart +++ b/lib/ui/screens/page_about.dart @@ -2,36 +2,40 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:petitbac/ui/widgets/helpers/app_header.dart'; +import 'package:petitbac/ui/helpers/app_titles.dart'; class PageAbout extends StatelessWidget { const PageAbout({super.key}); @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: <Widget>[ - const AppHeader(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(); - } - }, - ), - ], + 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 index 933478a..fd6bfec 100644 --- a/lib/ui/screens/page_game.dart +++ b/lib/ui/screens/page_game.dart @@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/cubit/game_cubit.dart'; -import 'package:petitbac/ui/widgets/game/game.dart'; -import 'package:petitbac/ui/widgets/parameters.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/layouts/game_layout.dart'; +import 'package:petitbac/ui/layouts/parameters_layout.dart'; class PageGame extends StatelessWidget { const PageGame({super.key}); @@ -11,8 +12,13 @@ class PageGame extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - return gameState.currentGame.isRunning ? const GameWidget() : const Parameters(); - }); + 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 index d8690b9..433f4dc 100644 --- a/lib/ui/screens/page_settings.dart +++ b/lib/ui/screens/page_settings.dart @@ -1,22 +1,26 @@ import 'package:flutter/material.dart'; -import 'package:petitbac/ui/widgets/helpers/app_header.dart'; -import 'package:petitbac/ui/widgets/settings/settings_form.dart'; +import 'package:petitbac/ui/helpers/app_titles.dart'; +import 'package:petitbac/ui/settings/settings_form.dart'; class PageSettings extends StatelessWidget { const PageSettings({super.key}); @override Widget build(BuildContext context) { - return const Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: <Widget>[ - AppHeader(text: 'settings_title'), - SizedBox(height: 8), - SettingsForm(), - ], + 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/widgets/settings/settings_form.dart b/lib/ui/settings/settings_form.dart similarity index 96% rename from lib/ui/widgets/settings/settings_form.dart rename to lib/ui/settings/settings_form.dart index eef343a..f51c716 100644 --- a/lib/ui/widgets/settings/settings_form.dart +++ b/lib/ui/settings/settings_form.dart @@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:unicons/unicons.dart'; -import 'package:petitbac/ui/widgets/settings/theme_card.dart'; +import 'package:petitbac/ui/settings/theme_card.dart'; class SettingsForm extends StatefulWidget { const SettingsForm({super.key}); diff --git a/lib/ui/widgets/settings/theme_card.dart b/lib/ui/settings/theme_card.dart similarity index 100% rename from lib/ui/widgets/settings/theme_card.dart rename to lib/ui/settings/theme_card.dart diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart index 789d18b..bd4614b 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -14,7 +14,7 @@ class SkeletonScreen extends StatelessWidget { appBar: const GlobalAppBar(), extendBodyBehindAppBar: false, body: Material( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: BlocBuilder<NavCubit, int>( builder: (BuildContext context, int pageIndex) { return Padding( @@ -28,7 +28,7 @@ class SkeletonScreen extends StatelessWidget { }, ), ), - backgroundColor: Theme.of(context).colorScheme.background, + 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 0000000..611b237 --- /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:petitbac/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 0000000..b4691ad --- /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:petitbac/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/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart similarity index 72% rename from lib/ui/widgets/button_game_start_new.dart rename to lib/ui/widgets/actions/button_game_start_new.dart index 2b0ca95..067a2d3 100644 --- a/lib/ui/widgets/button_game_start_new.dart +++ b/lib/ui/widgets/actions/button_game_start_new.dart @@ -14,17 +14,17 @@ class StartNewGameButton extends StatelessWidget { builder: (BuildContext context, GameSettingsState gameSettingsState) { return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( builder: (BuildContext context, GlobalSettingsState globalSettingsState) { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - return TextButton( child: const Image( - image: AssetImage('assets/icons/button_start.png'), + image: AssetImage('assets/ui/button_start.png'), fit: BoxFit.fill, ), - onPressed: () => gameCubit.startNewGame( - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - ), + 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 0000000..6119772 --- /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:petitbac/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 deleted file mode 100644 index b7384eb..0000000 --- a/lib/ui/widgets/game/game.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:petitbac/cubit/game_cubit.dart'; -import 'package:petitbac/models/game.dart'; -import 'package:petitbac/ui/widgets/game/game_countdown.dart'; -import 'package:petitbac/ui/widgets/game/game_position_indicator.dart'; -import 'package:petitbac/ui/widgets/game/widget_category.dart'; -import 'package:petitbac/ui/widgets/game/widget_letter.dart'; - -class GameWidget extends StatelessWidget { - const GameWidget({super.key}); - - @override - Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; - - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - currentGame.gameSettings.itemsCount != 0 - ? const GamePositionIndicator() - : const SizedBox.shrink(), - const WidgetLetter(), - const GameButtonNextWithCountdown(), - const WidgetCategory(), - ], - ), - ); - }, - ); - } -} diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart new file mode 100644 index 0000000..a3bc232 --- /dev/null +++ b/lib/ui/widgets/game/game_board.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +import 'package:petitbac/ui/widgets/game/game_countdown.dart'; +import 'package:petitbac/ui/widgets/game/widget_category.dart'; +import 'package:petitbac/ui/widgets/game/widget_letter.dart'; + +class GameBoardWidget extends StatelessWidget { + const GameBoardWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + WidgetLetter(), + GameButtonNextWithCountdown(), + WidgetCategory(), + ], + ); + } +} diff --git a/lib/ui/widgets/game/game_countdown.dart b/lib/ui/widgets/game/game_countdown.dart index fdbd0f4..8e4510a 100644 --- a/lib/ui/widgets/game/game_countdown.dart +++ b/lib/ui/widgets/game/game_countdown.dart @@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/cubit/game_cubit.dart'; import 'package:petitbac/cubit/settings_game_cubit.dart'; -import 'package:petitbac/models/game.dart'; +import 'package:petitbac/models/game/game.dart'; import 'package:petitbac/utils/color_extensions.dart'; class GameButtonNextWithCountdown extends StatelessWidget { @@ -48,7 +48,7 @@ class GameButtonNextWithCountdown extends StatelessWidget { if (currentGame.isFinished) { gameCubit.quitGame(); } else { - if (currentGame.gameSettings.itemsCount == 0) { + if (currentGame.gameSettings.itemsCountValue == 0) { gameCubit.pickNewItem(); gameCubit.startTimer(); } else { diff --git a/lib/ui/widgets/game/game_position_indicator.dart b/lib/ui/widgets/game/game_position_indicator.dart index 2d850ba..5298b45 100644 --- a/lib/ui/widgets/game/game_position_indicator.dart +++ b/lib/ui/widgets/game/game_position_indicator.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/cubit/game_cubit.dart'; -import 'package:petitbac/models/game.dart'; -import 'package:petitbac/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/helpers/outlined_text_widget.dart'; import 'package:petitbac/utils/color_extensions.dart'; class GamePositionIndicator extends StatelessWidget { @@ -16,7 +16,7 @@ class GamePositionIndicator extends StatelessWidget { final Game currentGame = gameState.currentGame; final int currentPosition = currentGame.position + 1; - final int maxPosition = currentGame.gameSettings.itemsCount; + final int maxPosition = currentGame.gameSettings.itemsCountValue; // Normalized [0..1] value final double barValue = currentPosition / maxPosition; diff --git a/lib/ui/widgets/game/widget_category.dart b/lib/ui/widgets/game/widget_category.dart index 61d41ea..c86fd3d 100644 --- a/lib/ui/widgets/game/widget_category.dart +++ b/lib/ui/widgets/game/widget_category.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/cubit/game_cubit.dart'; -import 'package:petitbac/models/game.dart'; +import 'package:petitbac/models/game/game.dart'; import 'package:petitbac/utils/color_extensions.dart'; class WidgetCategory extends StatelessWidget { @@ -35,8 +35,7 @@ class WidgetCategory extends StatelessWidget { height: 180, child: TextButton( onPressed: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.pickNewCategory(); + BlocProvider.of<GameCubit>(context).pickNewCategory(); }, child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/ui/widgets/game/widget_letter.dart b/lib/ui/widgets/game/widget_letter.dart index dbdea5f..df1db43 100644 --- a/lib/ui/widgets/game/widget_letter.dart +++ b/lib/ui/widgets/game/widget_letter.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/cubit/game_cubit.dart'; -import 'package:petitbac/models/game.dart'; -import 'package:petitbac/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/helpers/outlined_text_widget.dart'; import 'package:petitbac/utils/color_extensions.dart'; class WidgetLetter extends StatelessWidget { @@ -36,8 +36,7 @@ class WidgetLetter extends StatelessWidget { height: 100, child: TextButton( onPressed: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.pickNewLetter(); + BlocProvider.of<GameCubit>(context).pickNewLetter(); }, child: OutlinedText( text: currentGame.letter.text, diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart index 512717a..e64acbd 100644 --- a/lib/ui/widgets/global_app_bar.dart +++ b/lib/ui/widgets/global_app_bar.dart @@ -4,8 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:petitbac/config/menu.dart'; import 'package:petitbac/cubit/game_cubit.dart'; import 'package:petitbac/cubit/nav_cubit.dart'; -import 'package:petitbac/models/game.dart'; -import 'package:petitbac/ui/widgets/helpers/app_title.dart'; +import 'package:petitbac/models/game/game.dart'; +import 'package:petitbac/ui/helpers/app_titles.dart'; class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { const GlobalAppBar({super.key}); @@ -20,16 +20,15 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { final List<Widget> menuActions = []; - if (currentGame.isRunning) { + if (currentGame.isRunning && !currentGame.isFinished) { menuActions.add(TextButton( child: const Image( - image: AssetImage('assets/icons/button_back.png'), + image: AssetImage('assets/ui/button_back.png'), fit: BoxFit.fill, ), onPressed: () {}, onLongPress: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.quitGame(); + BlocProvider.of<GameCubit>(context).quitGame(); }, )); } else { @@ -70,7 +69,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { } return AppBar( - title: const AppTitle(text: 'app_name'), + title: const AppHeader(text: 'app_name'), actions: menuActions, ); }, diff --git a/lib/ui/widgets/helpers/app_header.dart b/lib/ui/widgets/helpers/app_header.dart deleted file mode 100644 index b5c5be0..0000000 --- a/lib/ui/widgets/helpers/app_header.dart +++ /dev/null @@ -1,24 +0,0 @@ -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 Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - tr(text), - textAlign: TextAlign.start, - style: Theme.of(context).textTheme.headlineSmall!.apply(fontWeightDelta: 2), - ), - const SizedBox(height: 8), - ], - ); - } -} diff --git a/lib/ui/widgets/helpers/app_title.dart b/lib/ui/widgets/helpers/app_title.dart deleted file mode 100644 index 7cbbb20..0000000 --- a/lib/ui/widgets/helpers/app_title.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; - -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.headlineLarge!.apply(fontWeightDelta: 2), - ); - } -} diff --git a/pubspec.lock b/pubspec.lock index bdba0ef..e0ab96e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -106,10 +106,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.dev" source: hosted - version: "8.1.5" + version: "8.1.6" flutter_lints: dependency: "direct dev" description: @@ -164,10 +164,10 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -188,10 +188,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: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.5" path_provider_foundation: dependency: transitive description: @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -308,10 +308,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_foundation: dependency: transitive description: @@ -425,10 +425,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.1" xdg_directories: dependency: transitive description: @@ -438,5 +438,5 @@ packages: source: hosted version: "1.0.4" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7a18da7..00f81f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A PetitBac game application. publish_to: "none" -version: 1.2.33+39 +version: 1.3.0+40 environment: sdk: "^3.0.0" @@ -12,6 +12,7 @@ dependencies: flutter: sdk: flutter + # base easy_localization: ^3.0.1 equatable: ^2.0.5 flutter_bloc: ^8.1.1 @@ -21,13 +22,16 @@ dependencies: path_provider: ^2.0.11 unicons: ^2.1.1 + # specific + # (none) + dev_dependencies: flutter_lints: ^4.0.0 flutter: uses-material-design: true assets: - - assets/icons/ + - assets/ui/ - assets/translations/ fonts: @@ -41,3 +45,4 @@ flutter: 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 27dbe26..6d67b8f 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 0000000..6b80eb9 --- /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_categories_list.sh diff --git a/scripts/build_categories_list.sh b/resources/data/build_categories_list.sh similarity index 98% rename from scripts/build_categories_list.sh rename to resources/data/build_categories_list.sh index 8453c9e..ff4459a 100755 --- a/scripts/build_categories_list.sh +++ b/resources/data/build_categories_list.sh @@ -3,7 +3,7 @@ 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}")")" # CSV source file for categories SOURCE_CATEGORIES_CSV_FILE="${CURRENT_DIR}/categories.csv" diff --git a/scripts/categories.csv b/resources/data/categories.csv similarity index 100% rename from scripts/categories.csv rename to resources/data/categories.csv diff --git a/resources/ui/build_ui_resources.sh b/resources/ui/build_ui_resources.sh new file mode 100755 index 0000000..4f365ed --- /dev/null +++ b/resources/ui/build_ui_resources.sh @@ -0,0 +1,110 @@ +#! /bin/bash + +# Check dependencies +command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; } +command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; } +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 "$(dirname "${CURRENT_DIR}")")" +ASSETS_DIR="${BASE_DIR}/assets" + +OPTIPNG_OPTIONS="-preserve -quiet -o7" +ICON_SIZE=192 + +####################################################### + +# 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 + +####################################################### + +# optimize svg +function optimize_svg() { + SOURCE="$1" + + cp ${SOURCE} ${SOURCE}.tmp + scour \ + --remove-descriptive-elements \ + --enable-id-stripping \ + --enable-viewboxing \ + --enable-comment-stripping \ + --nindent=4 \ + --quiet \ + -i ${SOURCE}.tmp \ + -o ${SOURCE} + rm ${SOURCE}.tmp +} + +# build icons +function build_image() { + SOURCE="$1" + TARGET="$2" + + echo "Building ${TARGET}" + + if [ ! -f "${SOURCE}" ]; then + echo "Missing file: ${SOURCE}" + exit 1 + fi + + optimize_svg "${SOURCE}" + + mkdir -p "$(dirname "${TARGET}")" + + inkscape \ + --export-width=${ICON_SIZE} \ + --export-height=${ICON_SIZE} \ + --export-filename=${TARGET} \ + "${SOURCE}" + + optipng ${OPTIPNG_OPTIONS} "${TARGET}" +} + +function build_image_for_skin() { + SKIN_CODE="$1" + + # skin images + for SKIN_IMAGE in ${SKIN_IMAGES} + do + build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png + done +} + +####################################################### + +# Delete existing generated images +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_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png +done + +# build skins images +for SKIN in ${AVAILABLE_SKINS} +do + 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 0000000..ac7eefe --- /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/resources/ui/images/button_resume_game.svg b/resources/ui/images/button_resume_game.svg new file mode 100644 index 0000000..6ad8b64 --- /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_start.svg b/resources/ui/images/button_start.svg similarity index 100% rename from icons/button_start.svg rename to resources/ui/images/button_start.svg diff --git a/resources/ui/images/game_end.svg b/resources/ui/images/game_end.svg new file mode 100644 index 0000000..fe20923 --- /dev/null +++ b/resources/ui/images/game_end.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"><g transform="matrix(.17604 0 0 .17604 7.9341 1.7716)"><path d="m101.92 496.35c-1.8555 0-3.7109-0.69532-5.1484-2.0898-2.9297-2.8438-3-7.5234-0.15234-10.453l9.1875-9.4648c2.8438-2.9297 7.5234-3 10.453-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4648c-1.4492 1.4961-3.375 2.2461-5.3047 2.2461z" fill="#ff4e61"/><path d="m201.65 133.26c-1.8516 0-3.7109-0.69531-5.1445-2.0898-2.9297-2.8438-3-7.5234-0.15625-10.449l9.1914-9.4688c2.8438-2.9297 7.5195-3 10.449-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#ff4e61"/><path d="m413.8 100.39c-1.8555 0-3.7109-0.69141-5.1484-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4688c2.8438-2.9258 7.5234-2.9961 10.453-0.15234 2.9297 2.8398 3 7.5234 0.15625 10.449l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#5c73bc"/><path d="m413.8 463.77c-1.8555 0-3.7109-0.69532-5.1484-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4688c2.8438-2.9258 7.5234-3 10.453-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m63.07 112.91c-1.8516 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4687c2.8438-2.9258 7.5234-2.9961 10.453-0.15234 2.9258 2.8438 2.9961 7.5234 0.15234 10.449l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m12.309 278.82c-1.8516 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1875-9.4688c2.8438-2.9297 7.5234-3 10.453-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4453 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#2dc471"/><path d="m216.29 278.49-23.996 12.996c-6.2226 3.3711-13.496-2.0742-12.309-9.2148l4.582-27.523c0.47266-2.8359-0.4375-5.7266-2.4375-7.7344l-19.414-19.496c-5.0352-5.0547-2.2578-13.863 4.7031-14.906l26.824-4.0156c2.7656-0.41407 5.1524-2.1992 6.3867-4.7812l12-25.043c3.1133-6.4922 12.102-6.4922 15.215 0l11.996 25.043c1.2383 2.582 3.625 4.3672 6.3867 4.7812l26.828 4.0156c6.957 1.043 9.7344 9.8516 4.6992 14.906l-19.41 19.496c-2 2.0078-2.9141 4.8984-2.4414 7.7344l4.582 27.523c1.1914 7.1406-6.082 12.586-12.305 9.2148l-23.996-12.996c-2.4727-1.3398-5.4258-1.3398-7.8945 0z" fill="#ffd02f"/><path d="m220.24 512c-4.082 0-7.3906-3.3086-7.3906-7.3906v-115.59c0-4.082 3.3086-7.3945 7.3906-7.3945s7.3906 3.3125 7.3906 7.3945v115.59c0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#5c73bc"/><path d="m220.3 357.42h-0.11328c-4.082 0-7.3945-3.3125-7.3945-7.3945s3.3086-7.3906 7.3945-7.3906h0.11328c4.082 0 7.3906 3.3086 7.3906 7.3906s-3.3086 7.3945-7.3906 7.3945z" fill="#5c73bc"/><path d="m220.3 332h-0.14838c-4.082-0.0156-7.375-3.3398-7.3594-7.4219 0.0195-4.0742 3.3242-7.3594 7.3906-7.3594h0.14848c4.082 0.0156 7.375 3.3398 7.3594 7.4219-0.0156 4.0703-3.3242 7.3594-7.3906 7.3594z" fill="#fa0"/><path d="m87.234 230.89c-1.9297 0-3.8555-0.75-5.3047-2.2422l-79.34-81.738c-2.8438-2.9297-2.7773-7.6094 0.15234-10.449 2.9297-2.8438 7.6094-2.7734 10.453 0.15235l79.344 81.738c2.8438 2.9258 2.7734 7.6094-0.15625 10.449-1.4375 1.3945-3.293 2.0898-5.1484 2.0898z" fill="#ff4e61"/><path d="m113.95 258.5c-1.8633 0-3.7266-0.69922-5.1641-2.1055-2.9219-2.8516-2.9766-7.5312-0.125-10.453l0.082-0.082c2.8516-2.918 7.5312-2.9766 10.453-0.12109 2.9219 2.8516 2.9766 7.5312 0.12109 10.453l-0.0781 0.082c-1.4492 1.4805-3.3672 2.2266-5.2891 2.2266z" fill="#fa0"/><path d="m131.4 276.48c-1.8555 0-3.7109-0.69531-5.1484-2.0898-2.9258-2.8438-2.9961-7.5234-0.15235-10.449l0.0781-0.0859c2.8476-2.9297 7.5273-2.9961 10.453-0.15235 2.9297 2.8438 3 7.5234 0.15625 10.453l-0.082 0.082c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#5c73bc"/><path d="m353.24 227.99c-1.8555 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l79.34-81.734c2.8438-2.9297 7.5234-3 10.453-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-79.344 81.734c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m326.52 255.6c-1.9141 0-3.8242-0.73828-5.2695-2.2109l-0.082-0.082c-2.8633-2.9141-2.8203-7.5938 0.0899-10.453 2.9141-2.8633 7.5938-2.8203 10.453 0.0898l0.082 0.082c2.8594 2.9141 2.8203 7.5938-0.0937 10.453-1.4375 1.4141-3.3086 2.1211-5.1797 2.1211z" fill="#ff4e61"/><path d="m309.07 273.58c-1.9297 0-3.8555-0.75-5.3047-2.2422l-0.082-0.082c-2.8398-2.9297-2.7734-7.6094 0.15625-10.453s7.6094-2.7734 10.453 0.15234l0.082 0.082c2.8398 2.9297 2.7734 7.6094-0.15625 10.453-1.4375 1.3945-3.293 2.0898-5.1484 2.0898z" fill="#fa0"/><path d="m300.65 116.69c-1.2422 0-2.5-0.3125-3.6523-0.97266-3.5469-2.0234-4.7812-6.5391-2.7578-10.082l56.863-99.652c2.0234-3.543 6.5352-4.7773 10.082-2.7539 3.5469 2.0234 4.7812 6.5391 2.7578 10.082l-56.863 99.652c-1.3633 2.3867-3.8594 3.7266-6.4297 3.7266z" fill="#62d38f"/><path d="m281.52 150.33c-1.293 0-2.5977-0.33593-3.7891-1.0469l-0.0976-0.0586c-3.5-2.0938-4.6445-6.6328-2.5469-10.137 2.0938-3.5078 6.6328-4.6445 10.137-2.5508l0.0977 0.0586c3.5039 2.0938 4.6445 6.6328 2.5508 10.137-1.3867 2.3164-3.8359 3.5976-6.3516 3.5976z" fill="#fa0"/><path d="m269.02 172.25c-1.3008 0-2.6172-0.34375-3.8086-1.0625l-0.0977-0.0586c-3.4961-2.1094-4.6211-6.6523-2.5156-10.148 2.1094-3.4961 6.6523-4.6172 10.148-2.5117l0.0976 0.0586c3.4961 2.1094 4.6211 6.6523 2.5117 10.148-1.3867 2.3008-3.832 3.5742-6.3359 3.5742z" fill="#2dc471"/><path d="m139.96 116.69c-2.5703 0-5.0664-1.3398-6.4297-3.7305l-56.863-99.648c-2.0234-3.5469-0.78906-8.0586 2.7539-10.082 3.5469-2.0234 8.0625-0.79297 10.086 2.7539l56.863 99.648c2.0234 3.5469 0.78906 8.0625-2.7539 10.086-1.1562 0.66016-2.4141 0.97266-3.6562 0.97266z" fill="#5c73bc"/><path d="m159.09 150.33c-2.5078 0-4.957-1.2773-6.3438-3.582-2.1016-3.5-0.96875-8.043 2.5273-10.145l0.10157-0.0586c3.5-2.1016 8.0391-0.97266 10.141 2.5273 2.1055 3.5 0.97266 8.0391-2.5273 10.145l-0.0977 0.0586c-1.1914 0.71484-2.5039 1.0547-3.8008 1.0547z" fill="#ff4e61"/><path d="m171.6 172.25c-2.5 0-4.9375-1.2656-6.3281-3.5625-2.1172-3.4922-1-8.0352 2.4883-10.152l0.0977-0.0586c3.4961-2.1133 8.0391-1 10.156 2.4922 2.1133 3.4922 1 8.0352-2.4922 10.152l-0.0977 0.0586c-1.1992 0.72656-2.5195 1.0703-3.8242 1.0703z" fill="#fa0"/><path d="m402.14 357.28-15.523 11.602c-4.0234 3.0117-9.6523-0.043-9.5234-5.1641l0.5039-19.75c0.0508-2.0352-0.87109-3.9648-2.4688-5.1641l-15.508-11.621c-4.0234-3.0156-2.9453-9.4726 1.8242-10.93l18.391-5.6094c1.8906-0.57812 3.3906-2.082 4-4.0156l5.9375-18.785c1.5391-4.875 7.8359-5.8125 10.652-1.5898l10.863 16.285c1.1211 1.6758 2.9688 2.6797 4.9414 2.6797l19.18 0.0117c4.9766 4e-3 7.7891 5.8828 4.7578 9.9492l-11.676 15.672c-1.2031 1.6172-1.5586 3.7383-0.94922 5.6719l5.918 18.797c1.5312 4.875-3.0273 9.4453-7.7148 7.7344l-18.078-6.5977c-1.8594-0.67969-3.9258-0.37109-5.5273 0.82422z" fill="#ffd02f"/><path d="m261.51 512c-4.082 0-7.3906-3.3086-7.3906-7.3906 0-57.23 22.832-95.922 41.984-118.3 20.828-24.332 41.613-35.023 42.488-35.469 3.6406-1.8477 8.0898-0.39063 9.9336 3.2539 1.8438 3.6367 0.39453 8.0781-3.2422 9.9297-0.3125 0.16016-19.5 10.164-38.367 32.395-25.227 29.719-38.016 66.121-38.016 108.2 0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#ff4e61"/><path d="m102.86 397.35 11.766 15.605c3.0547 4.0469 9.2852 2.7305 10.547-2.2266l4.8633-19.113c0.5-1.9648 1.9102-3.5547 3.7695-4.2461l18.039-6.707c4.6797-1.7383 5.3906-8.25 1.207-11.016l-16.141-10.672c-1.6602-1.1016-2.6914-2.9726-2.7578-5.0039l-0.61719-19.75c-0.15625-5.1211-5.9492-7.832-9.7969-4.5859l-14.84 12.516c-1.5312 1.2891-3.5781 1.7227-5.4726 1.1562l-18.422-5.5c-4.7773-1.4258-9.0703 3.4102-7.2617 8.1836l6.9688 18.41c0.71875 1.8945 0.48438 4.0352-0.625 5.7188l-10.77 16.348c-2.793 4.2422 0.34375 9.9375 5.3125 9.6445l19.145-1.1406c1.9727-0.11719 3.875 0.77343 5.0859 2.3789z" fill="#ffd02f"/><path d="m179.02 512c-4.082 0-7.3906-3.3086-7.3906-7.3906 0-30.059-6.6797-57.559-19.852-81.734-1.9531-3.5859-0.62891-8.0742 2.957-10.027 3.5859-1.9531 8.0742-0.62891 10.027 2.9531 14.363 26.375 21.648 56.254 21.648 88.809 0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#fa0"/><path d="m268.93 55.898c0-11.285-8.8828-20.434-19.836-20.434-10.957 0-19.836 9.1484-19.836 20.434 0 11.285 8.8789 20.438 19.836 20.438 10.953 0 19.836-9.1523 19.836-20.438z" fill="#ffd02f"/><path d="m373.08 446.81c0-11.285-8.8789-20.434-19.832-20.434-10.957 0-19.836 9.1484-19.836 20.434s8.8789 20.434 19.836 20.434c10.953 0 19.832-9.1484 19.832-20.434z" fill="#5c73bc"/><path d="m44.129 450.86c0-9.0508-7.1211-16.387-15.91-16.387-8.7852 0-15.906 7.3359-15.906 16.387 0 9.0547 7.1211 16.391 15.906 16.391 8.7891 0 15.91-7.3359 15.91-16.391z" fill="#62d38f"/><path d="m88.172 288.35c0-9.0508-7.1211-16.387-15.91-16.387-8.7852 0-15.906 7.3359-15.906 16.387s7.1211 16.391 15.906 16.391c8.7891 0 15.91-7.3398 15.91-16.391z" fill="#5c73bc"/><g fill="#ff4e61"><path d="m210.84 16.391c0-9.0547-7.1211-16.391-15.906-16.391-8.7891 0-15.91 7.3359-15.91 16.391 0 9.0508 7.1211 16.387 15.91 16.387 8.7852 0 15.906-7.3359 15.906-16.387z"/><path d="m365.23 152.88c0-9.0508-7.125-16.391-15.91-16.391-8.7852 0-15.91 7.3398-15.91 16.391s7.125 16.387 15.91 16.387c8.7852 0 15.91-7.3359 15.91-16.387z"/><path d="m139.96 32.746c-1.8555 0-3.7109-0.69141-5.1484-2.0898-2.9297-2.8438-3-7.5195-0.15625-10.449l9.1914-9.4688c2.8438-2.9297 7.5234-3 10.449-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-9.1875 9.4688c-1.4492 1.4922-3.3789 2.2422-5.3047 2.2422z"/></g></g></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 -- GitLab