From ca6933ac79d5327bf09d8ab8cac000fe98c5826b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 7 Nov 2025 23:45:04 +0100 Subject: [PATCH 001/162] Adds Field and Weapon to model --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt | 10 ++++++++++ .../hu/mokegyesulet/it/dunestrat/model/Weapon.kt | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt new file mode 100644 index 0000000..21f3383 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt @@ -0,0 +1,10 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class Field( + val id: String, + val water: Int, + val spice: Int, + val effectiveWeapon: Weapon, + val harvester: Boolean, + val neighbours: List, +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt new file mode 100644 index 0000000..bf5ed46 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt @@ -0,0 +1,8 @@ +package hu.mokegyesulet.it.dunestrat.model + +enum class Weapon { + PISTOL, + LASGUN, + CRYSKNIFE, + LEGION +} \ No newline at end of file From d267ae0c44106e2cd7c3ccf022da3bf0040cd2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 8 Nov 2025 12:42:10 +0100 Subject: [PATCH 002/162] Adds Player, Desert and image resources --- .../composeResources/drawable/crysknife.png | Bin 0 -> 1044 bytes .../composeResources/drawable/fremen_ikon.png | Bin 0 -> 2474 bytes .../composeResources/drawable/grid.png | Bin 0 -> 146633 bytes .../composeResources/drawable/hegy.png | Bin 0 -> 78973 bytes .../composeResources/drawable/lasgun.png | Bin 0 -> 2168 bytes .../composeResources/drawable/pistol.png | Bin 0 -> 2099 bytes .../composeResources/drawable/sivatag.png | Bin 0 -> 51899 bytes .../composeResources/drawable/ures.png | Bin 0 -> 13240 bytes .../mokegyesulet/it/dunestrat/model/Desert.kt | 3 +++ .../mokegyesulet/it/dunestrat/model/Player.kt | 11 +++++++++++ 10 files changed, 14 insertions(+) create mode 100644 composeApp/src/commonMain/composeResources/drawable/crysknife.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/fremen_ikon.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/grid.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/hegy.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/lasgun.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/pistol.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/sivatag.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/ures.png create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt diff --git a/composeApp/src/commonMain/composeResources/drawable/crysknife.png b/composeApp/src/commonMain/composeResources/drawable/crysknife.png new file mode 100644 index 0000000000000000000000000000000000000000..96d8b4ebc16e89f7456746222138e5752b7e2a0d GIT binary patch literal 1044 zcmV+v1nc{WP)xqS0(m?la3%oc;s5}~xG_BTfjh8@o)<4lXw!Rj|At`!)i+mf zATIl*-u7N8E|bX8{oMTQKBi}~K={VYZV~gWD%9;zg1Y$34SqlIw$#zD%w+d`Nn{HE zdg`sB9FsV~Gm+%HzR}aqC`QTf2%dh1j^S}U{fuHHj(KE@AHeWl|4+i#b8c|yC*ji7 z;4AxY)*tj*q1OjL=&LLNcWYF@Y;a>5%PO-w_DdzDY`qqq;g(iZ*j^6q@0m=x{0x}4 zjMlt25Lvh=F6NF#o&@TRjH$SSf1&7Wud3oV=+<)wx?20H9zf8@@ciidF2;@&WaB<> z)vk1J3Dq=f2f9AruAQ*m8)BV9(j8dn=5`L*#%7Czacxxx9l`ylc3C z;6akoj+58CwfELj08(CaF#4G44DN0xB^@`&FQk%Mr28jNX!YuGJ)Tq*%TYak6;H?T z6Y9~vkjiXukM@O9W>ZzW0*ef7$!M?ec(HvhHnXKNq9~m^KW|z@<0FwOuF&Jf@v)fn znMRYo%;R4bO5#*hBzhMmG5&pO^VFYG*znnRiq*L&P5;9Pq;`1?i_ZpB&WFYyP-Mh- z@%fpgzdME>$eL6Q&n5h) zHtL5my!Nq0kG*?AHv1rn{!4r8AZ~a~@r4{j z_{P5B@Ox16QhOXiBFqMYyy>1Mqif}j5QQkyhxRZjyTqYO$F>;Os08*E%vakXG4*e`V z)VM)(^D^yU55PXV9@1L(r4e(u488$e?DvR_W=z0d+m4vB{QwjAzJouYYYgWFH>KkM O0000^5H zgb0O%O~{>lj)tH2H@xrXdwjm%zu-%_ZfgPIm*fWk01%X=nZt2*{VQJZvDdiP79R(9 zm?_GM_ZZQ<;y>>wHkwaz9Ms;9{;HLkoS@c5v3m>Qt2B7nVcQH}LSc-0JAAHpwk2L3mGfWd z6ct01`ka&nngq~O)z4@fhf;Kw_>??3!aa>)oII$qBj}G@g3w|Y2c>vjoNW? zUpp^@YQdf|O?3{i1Nud8=z?t;!B=Sa4EYbwk@DdRKvhCp?x7!AY$-mXLr-Av$Q{gi zxJ4*YOpbSt*ZW82l=3qm=h@cBNt-y2lK*@udSIJ~m3G+f|ieJ`_{ z7q91`zNB6)_k>GPv$qIm;mPq;YdO|>MFN3}F`%bs-V4R5mcN^XEGxte=1WHahWK@3 zF=TkW7^I*~UuirD`nM`2a!iFo!M^rdD?~QFAspHQpGNI#2S%-KxdJCP_ zu~M1i$fvU+<)C90V58s>@#e6z786a~yjlg@g)QWgyVpggHXIG-Js9Z?Ur?^wyDA}m zDIcipzqZfKpuG(>;Ml7e7B_=%Kln#@rP;{i!-6K;P+r&ZfyXW@v?7gZHSFM5)nDzJ z?wKPNR41h?k*Ca*8q?NajazuUy)PUpGItB_k8@C5Lwi6B&b=B})vc_r-;Ym8&3!Zn z97^~tmsg_Lsx`>OKJIRlAF;m?Y9}b``R=ybwBbH)yP0Xg3VF zZNPmA?Kq-twb=O74YDRaE8o+I{}c)v67=AsjhpNx;V(Cj*`(Yw7T0%1mZ%2}i8_x5 zcB7sQpm$WXFh+al+9w5RD!0BNRO{OiJ)0vJHSP}#^rD}uh{}y?M78GQfqRI{Gdo=f1jMk&%6odH@)8^{-)dw=xy;) zOWc<~^WC0A)Ll06V7~7R&w&Lb^OZ(rXEV)zg0f-rOmv2f`O6pLvIm8m()h2Ex7?8X zK@#^;M-k?fR0;tunZmaTNA4J_nwVlD zc`FQfK&*ykFR4%OBeGB66%22PU3V$A_*gGp&luGvihus`_+yW=^h-V^| zbn1}mMx!&(nUF`03b$20d2>p!lt3Qd8 znTnOqn}ZJ5s9l16(pl_z@XlhO@9`{XwES zG#2vn!S0UC>g$!F&Kl`=&S$Q(IjMKUC@Yo>JW+)gzWFX&c}^%qe=T+cQL7e}8aM3r zg!D2>N!^~!^}!E4WKYlJ?A#~aG~kL(Uka1z{RC)qMh7nWEDxs@wb0?`Q~u_(-1h*)@|p4`rxwStIYTD_eExdDRHIa zcF%$*gKkFn&+q)v9f|lM*#aTw?GiQ~3#_<$ddDdGRn}$n^i*k4N{9cNnhTAY*$9^F z-9+DsOu!ZwPr-H0xO%B9SyjprJ#|fdZTSqjqhG8AjrGw$OP{nPmhBl@<@L+2w_i_W z4xCi%GVG8FZ-C&=Pv6-Qb6vHTKtzNkV6$|Z59=gZ$R8d_h@w)6wbE_t!~;LC>LA#< zXn0riX%fa;dHtS~UGG3v{{y!X*}dcj0iopYViR)~=v{+HoQQ*3Ff3g#xp7KqC_zQM|OSUwE>y(x*b? zsHp2thJ3|GT6y}09mbI@!pM1}`FnR;JLr05o+kJ783rk(#>K&)HNC-oVzn0>YsuM@ z?avJ|uE1a}uIaAZ<@hk%aozaw#SOb)$&!45 z^=dfd>M~=J(^oM*HLK~%OHEjIr=gkzgxKx6#`}K#Qi9WU$yQn=WXhfD%Wu*$r0M0l zfo6QP=Ne)h*#$fNdVL=G5~&f8H-46JnCM?Ek8l$ZzAY8|X!$M{RsP(R`n)ZRObv5W z>`bDIq1$F2-QSSF8N}dQ`ulnMKz-alMX&$T@G+i3vIOxf(#5JTJ#6UEGLH|!`M>m@ zuLU7WG>Wh=pSKJm-nujiZ)4;IU<3Zg_cYj@*Zq{=&54`;>5a$}EjT<7BrEUEcI5HF P0sv6vwq~`a-iiMQ{#d3F literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/drawable/grid.png b/composeApp/src/commonMain/composeResources/drawable/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..709618e61a1eb977df1f9c4fd7610ab1e4dde29b GIT binary patch literal 146633 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/drawable/hegy.png b/composeApp/src/commonMain/composeResources/drawable/hegy.png new file mode 100644 index 0000000000000000000000000000000000000000..c4bf4aa1076dfdf5cffed0b048bb5db8ae258ae0 GIT binary patch literal 78973 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/drawable/pistol.png b/composeApp/src/commonMain/composeResources/drawable/pistol.png new file mode 100644 index 0000000000000000000000000000000000000000..b77b278fffafb5d6af67a80357a422d4a56d238e GIT binary patch literal 2099 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
6fQTP{8ssTg0tQ*mCa(F+j=N=A1{ObE z!mJA&3x2yq)H&GrI`|3R@sF#e`|2K$kN2r3GyDd&G#M595_Fx%AP*|T$nmjfELhn+ z-DF5j?c&8^cS`TljXMZ_6_EjyR-%rQu&}>HPY4u{MQ_4xddk4=1}6$r-;$qPN{Tt# z!=tLTcrmC;b0J++&atFPD@Ra)s|3V{tGHqVS1N;$gxE7PUF*F_s2mp=QH?fTa0W`N zO~V9r>d65C*qig$|LQHUARF?Yy__=+X6(y<0{0jHp8c zakG>EHp#L&20o3hhga6a=};`|I8$cE>p%;#9+hZR1Aka~DqGJnlep3F=DO(K@P#5! zy&ir>c-B0D>G`V8S>SV8RHziBs=A{k%f zi`iB1&^FL*z*VjTNsOa=hY|H@715$Aadiexr#1I+m4li|AI6uyUO^^-v?a%5xQ!8F zG+kc%pNg9C5AI7t>p2|lM5`bo+1|`BX81tgv`g{h;=x|iETB&7>j&o^n220Z6>!{) zORC~Nhv9u-iB_ddNU(L!?EN$abSSVI1N2Q2J-l1^!+cSo$_tOI7;9X(d%We3k||R& zhT98x%pV zJxn-ROfvb(NrCbT@}3;Vs3x71a{AyyxgPAjRNC7?VK6j(@=Y+PW-NIRjPb=1m;VunT1TA$q%h zuif2a3^G@4Apt%RKU-Gr0=;w#XmkF-g?}_=CG%T~2c$pw(-!Xotl$Ie)3|p1?Qma1 z00R}Fa9gb~iOu3uT13{3X%Ckfo@bvjQjh|~lLp_b%{@TGx}Mw zZI7@{oSVzUM1$K%Gt^svi>Od}o@&NeDpyCNf+J*|FFZg~47Lgf%$!R!^I^oY03<}) zZYFeGJBwFf3CjLse{@qx)jgHW{B38%vf=kRA)MkvQFE-e+x}e>BYa_sLQ*a9_pzZm zeqMNwp+8~2SlH$JpLVWLzBhBmm z#dbQsch2MI{PcVjhs+8PKU$qlv;NkrGx z4Zs8&h#cgjJ%isL)eo3Iq>F8QxMEOs9;*i43G``6`ClJy;`ql`w-tS=^%NTKWv#vUsc|;BVWiPxq*zv!aRzfTz&#`fMOXjPoI`B z&Pwni)rHSelI#J`4(-kkJlKQx^!FKMBhblU@@lSb$ZqLTHtjWA?NzV&V@YRS$U#$_ zDEikGFZDU2%eZodHT}WIHjY@(J>g$Ho3}$YnV^-Qa%I>Mf{bBpN6tHA`%iH|H^F*C zM2Vg=Z0iDPw(aiV%qi>oJD944{niyR18Fr=z~~ILFYzw5=J{pFueNW!bZHUD-`-)I zI0gTYFutPg7=bYbbgoGzSTWBEFzBF)QQcqxKfr@@Ia=x-@VQc>KAjC*O) zbH)_G-VHqJiC7Bd*2}_p3WIf?S*5GK@c4BRYzD{{WcJ4|@Xxw+wi%9(7k#+O51`nL zwz@cCc2JX>U6=n=i6pW@J{dh`gneSdD2lC8+s8WnI`aUlgS%{qo5P+2_woA6-ZGxK*Hl(!40^A{i1%$Ih{lR68N*k_Hqle^_8 z^`A9hQ>cWsr>coW&w3=P)7@rqyEbT88LJJq8ekyMCXV*j-irK9*KllK>s~B%8--vk z-TY(=k*RAi_b)9K^;i%?kWVNfd0RI)wM1gc%>f8CGq2cfZ#Wt+Z&rY3G2=euC}t@- z4yHSqDh~dKl>mr&-?tdGA1yM_2Jh9&N}ip$h^=_dK01Emc@K9Cf0;8+?~|}q#SBVy z+f;rINDWrm%&#*?Vp)&?(s9D^)6ubhMB^h^ad>9&;jl8s9rUK~`6FUXKmrd1^6{HF z^4mJ<=vQQnyHuli8tYW+1A~sGe`O!5hhEf4#6DWj7}0z;N-jt)9`rycrEei8dL=kv zNhyut!A<;JiYgAqwKomH*&tYz=%myy>Rc7~=9ivid#sA6gXV>;fN(^PwC(EA2nXa= z&><_39!YZ78$CqSgYf)ZcVsZI4imwOFvk9 zL+%W}2xcISYCOAoCg^Iz`g`$-(HV8LOn&_V&r2>;qXGnSvN<11@aYxj8Xqk#m0>|9 z5Y0L-fg&AMSnw=uKZ{KFbLVS`NLSpIh>tIspmFB}98CynCbEfyadExSPo*i8PkTna zIJlk_fz5*kkHmxS6UU*6Oq#bNdW``h#wa~qNoFLz@zs>q z`_JZF*-5Z(y7g@2R^zU2O7VfRtvoS$1avYtSD`A-s5eMZS(YK7%y$Jqd`J0za( zd`J0|DB_A^t5tut7AqHBA{olSYz@8h*l@`CI=ze3A{G$HtJoG$IRsk!9?UF=4eV4F zk;7K1P*cvC_w0WwHIrWXRI0!lwo6*9;HGzcnE9(a zZ2m`&{4R6UJ`A4F7Ae^APhe7nDZMiRH6G~ zVl{(FXQ~t+Wc*u>qu!sT$&1ZP_T?6Yx*{&6lsO-l{inUI+@;VafYm{Rj{g)#3p>7+ z3o(+KJ=qg!bTi$y7{>RXa-s{V-jnfzi!cLN}X6T>{s^@;x zMSU!na@>8Dd+9&c-1^_l*}t!-zab&pnZy@!tBH!xESeC^;U@NuFc;AlBVQ^HWFZy2 zE9tQ(#%L`XzWug{Ydjwj4;@!p*S6nEWrfWy390?uZuFUNO*8w1v(bRNFok`Q!|hhZ z1X(CE*N!Q4eT)$8eJoCy@b}5FwPWPtn!yG&n?AB+Y(J7IkDJmmvWJ?%S%6|4T`Lh! za{rJRl}XNYgGP*UGHs&#;L*lJISZ|ftXyw9brTauWB&ouAn$%qi!!=QZzu;Z$w>WL zYD`R+Hz)YJ0R*w=Cnf+L^FGb9cf!UPK_^y3S*}^fOE3%gAOs7&`9`7b>5mDuOZxJZ zeq*T*!z5iUf@{fXS35CE9NQK>#$T%?VEBt+L_wBOYAx_QZyg~u(mE!47qdM@CDUhaY(G;k1I=WQ4?(CjNPhwS;W zN3ZDU{%G9!vFS3qXoQb94)$42Br%DF`WE!3RP79~m0wZ``Pk=2tftA*)xQuw7a%Lw zEz5WSMXq!J`APqI|?BhI9F$3pM5uV-2X@&Ri)dky;xBU-w2-!=)w zS+QvL#OZ~}ODJ;!pUIJgEfzmYU>*?s(V(MED8NPF>`IT%usG-rYF@^RxT+&Q67M!- z8pEm~Y55?sZlvo*zv|hPnZ01XH|O?k-}WeDGcVGKi&ev$d8XQI*BY`^4v9q;)A36H zXqH5`C$0DpWiv;!#N!PV^yUExJPofdzGyZgeHsGd%nv>^+&H_|Qm7@(t(? zVx27;i|7Sn8Fdiw&F)ZQTAW_WmgSZB6nmJ+O3WZ;FNJLP-0q_hrrO4D>6I7r^H-?`NSmlpk^Hx4`VAeSW25sgvkKudA_l=) z+jfZch?plrG!P4(HFkbAYK)S{eCT*4fk|UvF!`58AV9z|hLC&YXeUPF@(X!f{kNbQ z#huBvP>T??POpJ(g6 zvNI(1A8`DJ_OyUSXm2#xffwj?M-0rO7go>WHJxtVxgyb`_-`Xpzm zfu9M!mTr!W%qs4rRquKIkCkymTMd!*m}qJ@9>ki`MC!RJ7!%PWRf-t-#0MBFMvTh% z3kOr0|3wiVaum$_u3;`KOqf+lU)J6fUQiYI&_3FUWylsg&bxjz^}kbmkq!+Yl^_`k zd%bHPy)kkf>x|J{dgw4y0lG{~iSp)%9dnf@?F>hS^URqzyr^cf73xm0Vm)Dj?D`s~ zm#;AX2j1QmHnNYHU;F$&gGDk>IwKeRhK&CHGHd?P_p>qo&EtqMQX>oUeKAQn<*6E= z3`{;bX}c6yd#^igrAMD1lIlvm#64K^g6;wn!1?AI{4=6tDe5LIx}#bclA`QPdekE6 zcFa!?9n1+abej!wFSpMW#U)}w^S=g5%#U+oSO-m{xb?8L%w(R&Gvc;?W%(YnwY)s~ z-2w#T8%F&C_^^**tmLQIr#zOSMGQh%jG~|V4JdOEnf)=34jUf81b727MJ;gM%MiC6 zM`xZGrvSo_nfR%?;TkUCZ(H}={Q+F- zgA735wALJ(@1_s92>zm@1l<}q?Cfd2c}&!=UiD|m#ccv>&+CQeU$=%h(i7p6XVJzo z58Vbmv0*o1m&na=+zlN4`0K)qd(J>`_y-p9`LkqyHhQGTy&WYVAO+eqrlC?e>llZo z22HO)|25$P-s0uo*Hz^mA>k*+iBjJa6ilM&_`=*Tibed#$B@{uklAJmU>2ETIq-RB z%PAtv$|bT5bI@5hzm~;y@3hV&XCK%?wk1t39e;19OEsn)?XLYhhb1P_oprwL>ah7W zwW&+lUc_Svb|G2hF1xF>RaL*Pv3^N1|tyfZ)53dsz;sE0WLByBoQl!#}(x z(eH#|_Dzw2c~!>7c|Rri+~yxKLrK{%tPY$MwVHm3dl}n!y)Jk@P_bLY&jipqQge|( zL)?=V8&e4o*uUXqz%@8xQYP@t$=D}Rd ziXgIv!x2CzvIRd{g5+5`(V-opv7U&kXX8m zJc)a|3zxzUd1!1uXrc>HH4}Hf^1=ZkUOl#0MF{5(!G5b?2ccD&o!DxLYsKsYFB>#!O4RyqQsLSuq!N`HHKtlws=N02^aINz7 zfq#l_b^yeQ@~+L}!nQ|u zF~py+`8ZZsEQdu;@`?a*C)8nHe4Fz?GjBxiv6W4RBK}28IOD^yx%Wz51sQG&Q9EwY zv#F;H5$D-Ie0iLso~V+NOB>V^OZfw`CVbpb5Ebox@Nj)+nBJ{T049=N&OkhBC}6uAc9*gxac`t(L{ zER@ia{nby%;O~-FrAX(yM(Ae(3^TrM^^7Y`hR@;QADcF~fs)sFJu`F$u z6k#5dl%0M~+Wmm0VD32btOl8p`L_b&ZmR*kCgBC~VOPH7${27XTtPM4zuJ!+{_e(= z6A_rS6?syE9sI-8WARTD$AmYrEaGh`BCH7j^`N&Qw=9US5ut>MT$#S>W5%i{zh5pyJdvfgS2-bwob?K8?tE{=_Hna3m(d_oIgvOMk1j@# zJ)n@K%(o=4QiK@WG5^D>r$b$he0p-2l`o93LEyEbn`Fl3R309iqseTF@XvH2HW~DmJCt{ zITDb9n07J)`D(TF2HY~LR0Jr`q5RLqaaqQl6!fo!pmp_ zI3>NY(>_{G=PQkV{>z*twu72@;;k5Gm&O+|v>2K3{fe~hj5KJ~J%QU8Ee|P_JWzmE z)+rG+ee8^jxG9o|tZ{qX78PdaMNHEcfzz<{3;z=4p7ZQE0}^b?r1J-iorgv8;kBjQ zVu~Yy5jOO~YHedZwLc3%7J!=GA9zfZKI^l_48O3vwM z2|PFk@fKv)Ns3BG35KNKGo%S7s{x7hWeG8;H7~!ktgP(*g$)5Szm4M0<{O9ZV|p#( z4z8i0FZ%(7BQQ%6nPcToB0YkAuXPTD)CF?2nGnLWN0iWTvMl>^qbX7lNHFV>N2(V1 zD*wJ~rBsc1eCYS@KUs$UXrAMb=LZQNj7*eLjh~WK4WvhU<~E`H;n__=XeN~{hZ>|bw z6)r)!;HO%lV^!=i=@+-|2g|N-3hUol06K(9d!!jIcOULR5qJkmkF}4be}5KQ5F{7B zGNu`+e2i|78cUlDCqFkv_s6r_Qpiz1g~_Vg_#U^k(0;9(M4janlS_&{{(=)))^L$& zYVG_V1*8vd1%N6`S>@X$heCKs#>to!-Z~1!%Io$#EyzF|9 z?z=Y=?3X$CJ>sl`YySW1bqXbR=wvfhS?N*JYI54G#x}TUCT=4u=#m!=SbHr(^}bfy z3!ElB&RFz#!peKgLdI5-u}2!p!)FMrRF&x~TH06{PZ~Gk?_Arii#PIrb7!e8WXu<& z7*7PSB&x6Rx|ZLIo5l}{z$$bv)oZ!?%w7yZwkAhpK7mafcG<#Qwb&Zj5yvLhc}|dB zm2DcbpMCVmC6S@HADe1x@;surf#1$w=(8JOW2fP0mEo+hgYjEu7CJT0gb3V%x)j)S;XS!kLC4I+NP#cw~8PkZ7RcUbzpafqH z*S~^%n`vtM?tkJhqZKK<1WJQz=#kGuN}YXZxE(8JY@6bX6Sev)B+(I?I^(hI(S8&?n#}-{k9pa*7G-@ z%2LX4dHG`UmOV86EzVQz>~rcK1*L|!flPygqmYZ(UfBa@e!3HY-`=uoT;YBud(BDb z+mU+50_2+CRDa-8H<4Z2f7FBpTs2By@TTM~+cG-9?PlD*_w>5Q_mr!5w0^}LutTR% zlxm2A{w zMobphJkh-$ODJP4d2rw$s$8mnC_?3a|4a|S8yMG=#6>GNVxI_p<(Cflsl)^@4YA(g zD0$jaT=dJ#D07UVV9wxFImh--!0|h#^+)J|&mA-P^rCBTtL|-F({BE=)El^S?dmbw zS{qNC_1PohpH}^O!`N0=S>kmjZs!$+PumK#hvVk7d8N|6N)O_S%?IU#u628`zckW;BH7c0f zx}9-!E3dUQG)qT+NTT*#}-MRB{jqfkObOl+V zqJU!JEG>8+W^!RCuG8youD{de5%5zf#t|!DMy*v$8lZMD<&T}57(G$SPNdMwj1BfG z|2r3u=`DjV9tKYlU9IqAaC$8y%*Su6(1eth_e z9M!oRDco`|J70{XX$~5K3@1ZjYx`@J`OxNsSTRBXOs3V>=MG`8Mhqn$m>3I4)dqKn z{yUfPDvp~UW@_yF-4GiN#)xhl?LZXv`~kpNsKrO$I`d}~076`uOm4G`1yq~vN(>`_ zlfNyH@bWYR2aAk3N6WDgyVg>2j_Zs1J*T6Rgi&fV zEg?njAo8%_tN80EygLxfGNN33gr*xmvA@#Qx&2Lu8bSyHu+RdAm5K-mO<6nM;aif0 z{d2XZYPU0;2@aHu-eJGDH$tiIB$9#}Ga}&*_#mNcI}Ksd(gBz3{xx!siJ7{1hU7px zc>Z#(GP4^GCIW7`Dt%On9oh^hoSprJJbyxYKf`E%(*#;$lPu*C4wvu5%b(Z{mS)ZF zTGTWWNpB&I><(BpH6Q(MG8{R4b8OwVJyymA>D~Fci+^K>1-xMNpD-FM|JLb-Hp}1( zr4uV$>a^H%N*pf3`Cf{Bw1AzT436TET!EpNJhpCGMdn|0B;6-hPlwD4KWkR(a?YU2#774%R4w+l1Y9CtploBn4*a zgX}y>K^INcm^0sFA-*vQr4zC##$VWOj+c+qeSDm}1D(})_rN54!}*lv2cduOm>^E~ zSm2bOG7K1|Ezdp8+ICY(swIbp)3z1=W3(JQvX2!1Op7X!CHv5zc6p%Y)6;!9X#%Ta zN+-;Yd=Fxe%4fWIo|=PIE7oMhoH)+M@9D-(SgjYchdkO zFd1b|J0;V-I#v5m7ePF`N2U2AD z09CO-Dquzyvvj+Zv*?=KUVkl|>=wxFrWEDGglZF&T3Eaqm^Q~24g@8rGc>Ob<$$eQ z8qF-Q)nWn*;h;uMP~Ie8(oIx9bb@JyJ8X$4Sv|9(8I0x0Pd)wz?Up`y%iGF4*B|uJ z6MYh}MEf^+-QFsIi4i=(#C$;K;`c&=O>9yuN+|KtPqZL)k!MKg3q2T@}tv+{KnWMK(5R{(U zlvnNVv1Vh(b+F#)s2!-`r0&lDovSJ~9`ibI4z8eeRCzd9mFW&VBDttWLYkGi3_HoK zBx%mJ*l5Al4jm2F6l3%iYVQ!-(+4e&H1ylG#kd9*xxT_NKi8}EqP8%1yvPU0psOqO z$>7mPUxxH~$4w2kO?>(~L-DMEb?w6L5- zct$m<|C32@$4GiGBL8N zUh*St15?xa8LchMw9+ESPvoD!`tu~|_oi!a>aYg*5TkRd8R2B}os2IQp!nPZh9(j? zf{mT)GQ{Q$ncE8-`{ln+U8qO3*Vbch^mgB~I@?V22?d=+%-l@aVIR{;YTc#G@YDnj zW&ouxQI@6QMOtu-R&^s7?vHHldIoRix`^_B$WlP(rJ=RMMU z*so?u5-}4+bTIham+-pDt?b%PJCsxGMnJpMZ68j546`j?jLNV_{n>X#R0n9{4me)C z^5OD64?)>UhR+?KiRc@^)&LgEF>S;iR@WbOX6t*GkSpM~(9s9ld}T%`0x1Vb=6uS_ z*X)mOuYWiwOe$@3j45i~UUzyv8|T@Tep}B60E-(a0K&%!Gig3pafNsgI5BuaR9BV% z)6Bearz0ejAqA(0+Y3K!xD7qOBtYzvGXiaWK1;KBwhQuCMIZ(+Y(Eu9=$ zvsHh#GWICM*G+V5%%oZLl&8e(OBq@_Mzg^|QL8z5#>m=WlDEFd6% zwyVNw|AO_$qlbvc-4&onMfx(a}s;>MKWfm9{8yte{Um( z0cOU`I8I56QpTBib1Tqum}lmA9d;CqeVLt4N?&py0^v-mdZG(oqV6qKu`3}W9dy4Q zYSSV2?;ZR(FmbG@Z7j5j{D^jgI}kAcd<00Odp)4#pN>S>0jMW^(qxMab?n$ej4a9v z7{XsWhJmKJ9CPH=K`#rINBg`syqR^Ze>$Aat0Jqj&r7H-m3y0}TZiIJLN;_FbMrf&=66)fNHm z`84`WR7GIbo>JTn)>gbilCX>Xw+<|!65RSa3yWrqc`(5u|i1QOnh1;{zj-n8vLWt;BZ{?@mWlPm@ zho6l9?uuv_BaYhi+}$TULsO})Mm>{pE2f$QT_8ENA^~L=p-dPAo64y?5dX7g2`B;k zJAVcDBubJ{&kkg|iAL%TPWdHoT`YFwTwxt&x6El{Bj7?0(y4U8aeq?pQ|9E4CI>NJ z1%2mUs-nR9Yt$1%sc<}y2if+g>4t|F`|L*g&bWfWS^AX-5}-brG>7N_(pE4KNqD)A zH_p%Z;sBBm^uv$2-+LGSiQ_Nhe&CkTr5pqhL$=`?qD=&;0?SfS#m-TTUjq|dmi&$U zeCUyvS@^w#+1bHV&LXfxb@~7jI*}o%%?bxXfz-llo3!p{#}r}y_8oX5^h5GpW*Y{{ z=bc|2VGL<74FGCy#axh;m#SwfZ-4@wvxDH(J<%5Ue<#9D;%@C;6cTZDW|I<>qt1Mn z025Zj9t$+<^zT!4G+*&F(*n3Vfuxk6+`m<{=mRl9S>7c{(5@;;wcfYFfCT#_M8Ai8 zc&cjP@hQGdDXM^Zgy3h0*zUOBisQAvV}M2do5za@s%6j08RCa(9g5y+ZP_>L`jPqk z{+h`;B4Kq8&KRZ4TLp*?%iw+}WflP}z$jU5b5c}sYDJJ3TSzjyX8|>{Q z%;665T`?%lK z7%?s`yy3Lo=nt;l&pQeIyl~S{`WW}~BNN-FQbUXk!QK6d+jv%jTVjMyUX}o;Xah%P z!eub#jyI~#XfS08(ckj&vGU%^4_9DQV^!4Z%aUCX8?GUdC8!ZnyvnUR(HZ?M85iX2%FE(}bDc*Tb> z#9OfQm7w@v%pN?r#P3SSD70E%_#~Yq2c%#D=N>7r^$*)!KWrU-*d_uVAK~`vn3^|# z#i{FdyF=PRme%{eLo>j~l&`vz#$?^Wdky)c*EbX;2zhd-FFwhwc0@%$1JH}Und=4M z%|VPxebfx;))U~2ka@u?p8iTn^tL?9!T0iY_UFT08ZWSaQU@`)(sWtDYw>aKz@(Y| zgTmR7kxW77q`(O_8voMcRK(^ae`9k#9tLc(MP(-+uPV+AXD9*tk~qKPvmv ze^DE1-fE|1ebZI38wp@E_Xwj9B~4Ungd9%mwBp>$-w)RiUuM4C9#3P&J1l-w-03XVEb~>!}D_3L4a7cz|kN4o6(8)82w?#UZh*oAk|q+bvjO!b|8pfM(iT<)Z$89S zs5&bmJ%L`HrTy3j6AHX>FVWK90p2Y^B%0BhSGM1UHsZxjNyvz`61L^dfjQl#A4#f# zerh&dvgAK@=Cja`%q^Ly`;7jbj6bWpI>}Kwz9dzR&r@3%TL&S@_^A$8nkUMLU%B-Oczg1@_fN4aJ?eiK`!@nZ z-+z{*+g<610}8iIdkC;!gzg5{`SZv@-zA&1HtKG+n4Z~OFGWSd*S0}{PBcJyhBAoT z70;c`*ZO=tlh2n&Fb9MoaiNO>RawE)y$pO&DirhD4eYISKfx41+bX#aaG||6{rjO@ zHN%sg=+j!>nn}|1;55N_q=fg|oC7^RUVaVW{zK|?naoN#GVnIlr~9X0caeJaai=`Z z995WhFW_iN9p7C@B^j3TLtT;h7Q*M_886xDad0-Bh`q$7rW$F2*`^wJ0tfK>@_6v~N#|8+jqS0{qeZq+ z=?(g4)4+Hxby640BV%oLGbCkE?S8*-2m%rU!bjd!!r16yva{$5Me`LI8&yag{aFOL zcI-;Y{svfE&U;3|ML8MvB|T)UDUVd|dirU{Kb@7Wp=tQoTa@j{P9DV-mVtpOGau7K z6N?(Lr+A~I!;2(n4Jwb}onQr0X}(qWkC|#u{IFF@F!@khFr$l<;XnJ)39{Qt8q?vN zzw0!Qew5X=KJCaD7srNjF-Z<%pz@={C#eYs^+Y@=nkI(ACYr~~G-M<|K3AJw0{hzh z_z%z*G~OT6nLI+=cT3~l^NX8jcc2>n0nY(8r^LO2oEhKXJYCWIn@ap(Wm@o#;o)vw ztQ^V_D<^;kcAQ`=b8<*EdtgQ_eVAuw@IVwB^{_^I@bjcsekkTs-9t*tpE$V3Ka90c z&NI{gq^7R+ZS*wziz8y8*!aDm47NJ`b?%zx-*a0V(|Suz1jG3;NMNXAWTf&VU!70RR9+EbzHh1 z6fkR(zHw;-ATy}^CwGXq#PK6_=e*U`S0|@;4c0Wk@vD{(51!H?Zl%Fau-#ZVjYHh3 za*LW|ZG^(E3-WzWVa=NYU@yd(8+eg$TUuh)?kp)SxT; z%*0Q$UxDu!J-HIn9Q1FJmn=Sr@+luL!wxGU)F2fa7yV#Gb+wP54*{G)amH2&HeZYR zV(v)W4?C_})4@4DQh6LmUxpw}U{8&4MG+s0@2IzVZFeEaqzjraDKU>@G#TB(E^&w` zTprkdrHHr1sX%KSX2j^7&3=ZBhHy5YNsNAm8aX|kU00j1RPzH`6t`q!A8fs!{Z_5v z+i@N5Mu0%XqX+0x$RAbw*6@v=s8-@hzm+GiOwoP22-mV1n#Q-97v@D(>`43HsF_4R+2lS2@ezB*EIw2c3JHkL`k-s3=63roHroTW z)?<|m_>a7m9;PISm@ng09iMP)9l(~i_WMB=3hQZ76ZZPG^-B$miX1R;uedM4kb|ud zR904kdiC%?`jofYRrvO+qD$8i58f86E+K(6$5v)pfC`kD7rg?n%oC5mG^7M=XK_0{ z1+Rae?5Ii?Bjvy{TDnL}t#6kTPDd~OfDjb^{H#`pHe#&GY86HM`$WBW_@_#v!|C4u zF^~11#jf6c6IWpNt^{e64NgO#&0mkpm{C5)+|CeMg3yW(bg?`TM7&!cYYe1=-%l?+ zo;+4w(rC@Grfqz<8tTY%~SLYMeHd6;pq>QKpp{-gp5uAZss9+dvoe{ol@+eZbtnZ{9)*N!~l?0 zjYfO8S9ie+z*IFEw)_|?!sTKBT$mtQbU`{f71!V%{jhST?*a5Pv2{aQf=g1YJt(-! zni08;m8yr;h;lu4&WNnnq>V~x4p0(hQd0H2Uk~8HKkkg#?U+ZN#C&S`&x-mQ0nwYl zt9~OE%Kk&0Gc7Uiw3mr$1R2Qa`_rB21(buH!EfLU16+}e!fG=NNSlOK1JII#wByDp zW}w5G^c>nbe$%W{+u(-;p(N2Gn|%_35CL%c&sVBwC9{+xd4F`4bD!6K3iFG4*EzfYtx&BR|9LBYkRsZRo#pl4DEPwh{?nr8@gA@z4OJr8BenepHe58c!-Fg00l|a+( z#5l22jVUD(55)5(XC*{L8|++^{u5QucGSpqaVpeKf#k{kZh;QwmnuYfoIK0`h=kvc z%8_{2jG$nd<#QErp=^`D7{$PX^-%NH`wA#WoZp*#?0W;2{@D&#HAfyl@+>`48PF+Q zU2c*z`3ZJKij?2q_C*eheK|+i$qb%LonfC1P`^j^&s}}1 z2lDQiO|8HjmJ=G{-f`M&Bia%^KgD=+fL3RyfX`QJ|JClIu#BX)a+a)Xajm!lPFuOe znSQDhuE+@9>MA;pw062)9lxw7dJo3B%L(qMi(xdONrF}w`rHw65YZCp4d09H+_mg~ z)HSvJ(>Ea_=KJaw(@lUrff%G}JMb->r+-+z>{^53JEyS4BV%Ac1d?+t(ks`anj7 zN&jxwf9Pvoxn!A4^Gfzj{=r87l6B>0+~*6*TWFC7k{`oZ#~6L|y}{*L1^H|hD5$p9 zPlo!D2(vwDR7p zRsy$D^tsY1IqrbQVs}a>hPRcjj=X!ihY3EE}G0;xx!vnS=wBw7sM8#G`FA|?(U}2VHREjMS&mB8z}YlxtjY_ zK~4)$Ux*m;*`58Z(*u%c^iUW67#1)VrsJz(V*d_@N{Li8y#!&AO}iodr7BK(a1+ro zSs)j0W(mapmoNd4-*kSsAU(nH+v9CqOYGHh*^8=pOy`z>b-Mq9a6? zwj;MZnXHz2Lp*cg&!-cnsK5?=%$!$DK!<(_pY=8R!-L*jSAEgsTWm?|x2pG~YZz6* z-y@{W;qlQi)3W-dq8|2Y=NYNxMWHMz0pD)G^a^Ki>}**B(y8QTFH2ub+*`)(C>JBvC77>c6mzg?^hSr?S1=*VE(Yo|*{?m_ud& zj$;$_W$6eYp*Xsaz(3m>fZo*<~8vH@k}&|SZmYx?%E7te|uqGwM5t#<$M)xRX%k`F-yzUw>lXmXqgI1?F$q_Li!Vb5)VSmEC)cA}e)ieT-~2JFCgej5 zftv`Nfl2d7JKzd;^}AbPgpRMm;Qsi#jdGeUNI~BxrzPif0Yp^o1INhBD61`XLxADA z&`ukc$tY>uBk+$qe@YhRuSD5zck$65-^+j+bD63opl_wyjHw8Hk{zfow-s2`)Yt*! zKaeXXFPaSU;i@y5>Ib)&NdJ1!^icmkZz%Mm=WAj=)iM;ITLS?}DOjG6XM=SgBZXo# zezQOSydjT5b$m|+{4uokZwTX>T^14=MGTvSIco{pkg$DMu-^4tRs&i;PTR6NoEO*mQ#$tx$x}Sz zkw>H7#plW}EKJ62YXLJ0g3M-o{`ScUDc0tB8_f6PetbBjFrXeVB6Jd!8ymNBU)Ltg zyQVEAw{|EjZ*@ZXKXp39UOri4tVX*f@Z5`=CR3o}lHWIwaN#zLKAe}*iPNNixEUkL zF*-FA$3j#o>&#d;?qRSFPmqgvLkurH*^zkw|X6TkT9 zqd(HcSyB?T#-%Xl-6*Rw-d@Qe1rXa)QKKWQlj;*$ET~aHz@O+#fDZ%PVS#Oz;3W30 zxgnAh0twm^k-2N+-(e0Gh9R1a2b2VT*=5AAp%`v76z|?URe_ei3b#{RuhfCNXOO?j zR?I8uHobf^r_`eFJAQ?JS}j=|;RC7mXRMMM?%TzkO^ zWI>&}wWi23NM|;A=K-#RMN&tckItU)tCiQAXv!e=QX{J}SoeLzI-$3m&Q#2`@^CM|- zBng5zX!H__pgqGk)?gTV1^q{Z#(O{B{s#Lu=e#yH0R2}L#@DeHqpIRtoBD&wIEx!y zZoik=j?sB|{SUiYNk_Q($7l3$W6fb9oVNEpDWP4eiSjJyEC_;fg0aWkVej@7?6beDX=56*G7) zufL3Q31=x%ZjtGf*T=>u2G_CFnS$gzH$uGz^9z=nX#p3WWbVNo-@&JhO_7atu)M8| ziy#dURhK4iZ3rX&$3WY|3VzM3Y&;StGGwghp6~!SuctO5t~DiYZbP9*AKbEeqL`W{ z5$}HnyOlYA2?$`#=Tl8X=C=K+K{s5$ZTQeU1fy00P#``7+FTry@U3F06H^PJX)s(T zHqR%(Z{&k&9d~Yb{2fDUpbuQ?ryp- z+R_oXVdZQOz)I3rpI3+VAGuifieOk8-#9j1+gON#B9?L485iwwH!56#F8_4I53=hY z>3Bv3h+eJ6HZLF^I#;mJWQKxc8Sy~l{O)5U`yS*{Bw9(~#*TRe)VfA_Evpz%eK%ik zbI7LClFHO3H?v}xG zUw#J2i?i;LCtDCYjHB%9%EYIK1rCa$qkncjumNy_gFsTE5^d#)(Ia^Ljp-%oLjJ(O zfO{czpSm1hRpSHh5FnQ+OHYtWSAF@(ao|y?J;1B2YWt=XJ??A@B@k43Td|^ zZ>m7}_$L~SYbv7M+eIgCGcJqdo-I1=6Z4h7F$e%+-6kMt!PP@$2mf~6k|x;vp4%bY z-tgdMEm|A6V8Cx7Qs%33_;jFb`!T!`n5IJ=#t9tcXsTWv+B- zwr|Gi-ihZwbN-1jVMa&Srfx+9y;9 zgMbAo2aApX0Q?tGsyu7yt2L+@RTA)f^`sE7#-0G3EluZ*AW`3Zl0%h<79|WZqry~*9DUclEVMhJ2djNSGd1)Z)h{27o6Mtmf&fjdah zGl0}iS40D5%iPFU7g}Ru@KqP1yIH>{HiF(MQI$0^fhwc7T;1X~-?$|)jn6moPoK=x zK664jaEdf%XggA3?#kJIqO-3HE&YL_31VvleUp5M1{8@xE_YT6NXFemSQ#t9XEBsI zLpUYHSkA<#i6kKW*eEThoj(#QpBNnxyw7KY05zs7O=VR1;vnYlrwf6=c*fSR#J|No zlN7T4K{xM4{@aR%rIS5TH1Ezh9p8_8I!-c=9>49}(f0duM&ybqMH(ebg#l*n#5{db z_b?0Y&hGYv0KdxvoF)M)f-HQXL{riK(+Rx%aIP!Cuf1#)=i^Ppe82($ok$q=~jMGZxLMyBqI{$#*j^ zA9T?eD`#Wk{UaL;6xTimlyPSJ z_KKmyxJMZ{yqI8g1Fiva-%ZX_7mY?b9lJMcCB{k>AI*(%g$b#U@R$c+p$kpIsK4_H zuzg{F&DD?ltZF9MyeOMM9K_FmY|Xm%ulCllmTlo0de+bKN#~OqCr9w}7j#&cO3p`A zWJH|5o)r|cx9}z)n46*KStZ-xit+)5L2P-h;n6*FOdcr|0Ku~qT zG~1dYw#nA;;dUSdbb@KLg;}i4FJVNsZ%h@0bU*!B#qo{dEbCw=qpnWuIj-jcP)eii zf42+|%+8Wr>H8#qVjWzadxR#3sUV}i&h^+cm`N)6z}PmRb1i;b00`J8(j4chXAxTPfAo^X`}iO0TClyyl4(wlmimP$sqWQ z&y<@GyfSMmxfM-#BOFovr>r|qk<_S_Zd>;0_NlM4_*Esrxw^|1_cqoglDpWT`GMSu ziErGb%j+yZ%)dwoC&!)?Nk|Zq0#zm`Mfa7MSAvmH-Y(=#flc;Ocu5_pHy5*&(S=uS zDYh{_^*Ss>$ye^;F@SS2Dpm-v0ygWhb!_B}T7gZ{4G3r`OAXbgCMLD|0A48_hC=8EU_+B7@B4&++7EMD z1Rk7j`a*LccY96>mfdiT1nitedw^_;%e<9Tn6mzDie}#)>HpyF+&Fi#&G^+Xw`@K> zH*%Wr!N|*UsHY`}wl$|D<*$T;Wf2}paA&g-(%h`L-Ff4^Kl5s=j0&qEuq2PP#KIZ4DXUrve6r7w=0%uH;@wa3J^SGWq#F1%ZnI%2y|{@4m@>}~Jw8aV z72Mb!XHZS0VsP%9q=V_5&sYM=Troh5n$m3~S&z=P+gG%iU-DBz7_}MyM{5GzcZ}Hw z*BCZ91{aFbCOLgvE1JYww~_Lg1B8u#AS=L0v!wM2C62^BwB@QJV0l`;L@sIUG}}5w z+qX_;Xk1ze$i{yHTgNq)v5lywy@52He*-)AnJYh~Lxs9`+QLS2C^=_B@fpI~^T&OX z(*$P}nfrmm7Tj0#{JEPmPyn_G{EF56XUWD$q^=%db(F){(rdpQlRT}6YAU)V-aLpg zOvsIKzM={-;u1451&?3W?qRI9P95+H9Rj8faG_7%@W6a{W>V5$=3J&)ofU<8TbzQPRn+g?*{wyV2d#+*=mAnP=j=;4( zdj0b1vWp5dDk^I5skd22tVntZ_PxW(*<}}>1jQ&byd3K5-;2ndpp2G9?AqG+*$2!P$$-%?)w8#ymZc}DA7Z*P7UIY%TC8(w@>)gRN#I&v3!o^_K08^vit-wZcMS;o66lpAe;(S|=h}IQrt}r-J!E#loh{Ruz-%jHt6n z(DH@ffwIAW!8=~;*$vAoHr8m~poHU1X5H)Q< zxgra3YvJKr(uz8c5FTelDe_vc9=k>STalmfDdQ*)WlgNiLVZ+uu7^l^dd}FMX=eP+ zm{snLGj(B0pN2CEI}%ec+jZ}Q{Z!V3r8ru5 zI)C88zUIj0bkV*C+)ZC4jR}}Nd-0`7Tj%WvfwnYWZ0&2dm*bxvg}$Ba#9}CQG2Iws zd*w&3DGE2V&Oaf*m#D_`5Gb}uR6KZDFsX0NRy~UmdIP&}=t1;rZlwhE&jnvUx-Jp< z>-^`PZ%yJB?hf5fn>M)=t9a@AVhk7%FeOmMC<%*sC51nejIr&CV4ANWZ5!k4o$vFg zG$_{Z#qV{n21<>&94*}E-b{%SA609b?F`1jZx#shhAEyW1m8?>xt#X?hyhhaTu7QL zH$W-=TB!84XBOX^#cy!qyn@{$u(E*d z*hsL_TKjX8oe$1%Kw`$QptXbrUya#qZ1rNXJTS4}_YAIDg`T-@9Lo%_uw5dy35i>frs{{PluDw-1yOB%b1naa(QqXy? zzejML+FRCt1*;_<_B=%M2(h`u4Jc+`9$V6j+|j_}>K8Q`FnH9O&F-XYQZ$f=o3{E^ zrzIfc8zqO`WzOlsm#Wg`KpMJC1QQgdOhu`x8DK4eS>&q5`R&Tr;(x)x>Dw4SJuOYT zh=9E-U#6>@k+&vm&yI1MvTN8JaeO`4w{5nlT_P9p?A_a4NG$NOkA0!}?> z{B0WL3|QLi(xChR?Rh=ls8{@MWi^+8zsI$(w_MFWmgk<57FZuZpoHR|u919`(v4H2ZvL)7#x6McfYU`!DXJRvL}+b3aik ziaw+%6}&k;v&5jLg(i=&=JNbiP~KR%Yd&1uwb1R94t;A0M7|Vo6%C~ZxV$+9>_J=V zyuOsCcsf+s*{jVHQny*0!7_MiqQL`clrr8POyj5K#pmfgwR5Qp4M)0*G>>|bD6Kb( za?IUA6Mr9Q4&z<46K27-vCoe#_xuTz5`u|k63a^B0*#r$JE zP$VPdWt*e3nBA5|j7R=z9aE~yjo-)mpoBL9~fc zqApt|^OUzxv3Bp5z+bRh zWf}>ABo7BGrcFkF(P%k=jllWTP3j|})GfGWZ^a`*V%0QQ541)nk>!lhhRCGFWzuc7 z7}bVpFmyMgdq>=iJZkCbbuqkh^PWIlYo6XtK91G);K2jq{>CkBG*`1_4(~ASPS2yK?Tga049utyJ6Bld+Upu8sRqK6M7A2BKoCsc^Qo%PjWjjF^JHIzFp zNln>EZ`tbk==rMZ%jXFD4$_K(7N<>uPh!pO-WC2F`hP8em6D+CDzN77W?(BlcJgw` zy4HG(@bMrYV|SVh0>P0ziwk3N)z#hgsPiLb?0l~JTk2Mo-yTRI&i~q2Tf$m(`}%*V zL${@9RMd|TeFqfJjSbZmilJY)i%xjeO=|J+(z2(kf!k9TOENyWn~?MFUKi|6LG56? zLtq;S>|f6w|BPpscg?=SHfpb#zE^`o|OvLs5#twH8d`7$Ata8S(8nbdDd{hO+O`nGM9{jd7%_^;4m;Zoa&x8p4a<4#BA&YA4 zCLxXl#nDs0gok-6Qw7jMF4a=Wy&b!mPEA<6l<&Xb(=dH$hx0=#NK2KgS+Ww(l#fk1 zdq3mH7YC2IUln0LMbHhm_EVI(4xlu}*S}QJ@>8tiU7){I`90qhxDNzc9`qYueQKvW zL5k962H?u~Gr}UhvkwCk-6c)#G8Db_^yS7tOf_a*uQD_yElLp``hPTic_38Z|31TH z-^RXW9m3cuqbwC6dqpv}XXh$E*6>Fg7wJ+27-&JYv`T&$HCiE#z&WFD${K2>KvaAE_qoY0ku5)H=(F zmH@qaVI-lfiVlZZEulx{hs{z^t^Jx~S+8+r2nP!-R&!G^?oTIfk(; z)}at>xM3JsTDB%}UEwibygdh8J$Cq_|6QGbqXc5wzR=mlhvj~Lg?Jyj^s z=Y?xH>IVdS)Q1w@IN*zI@u7R@VA~fL|GvhK4%YJ!r{r@5ZFaBU#{Rq&7ePyoa`H8B9lCZls4c^9r+}%scF@z2$z?8dSh$#&v^zt!S^@&}3RH{-P$y9NndPeg z@wx0}GE|Hh-@)zU6^iIeNPQM5Nf|($I_2{kT7%a58VJ5Iz6d zv-YkJh@;y>c5n6gXvcxc(B_@NY7rn?lQF_Cc!{qvI!oCyVYbNnL;C`pg4-2<%iXI! z3uC+$;OxKN=K5T#T>{$Di!@d}smVjKD-Sj)5Sc-AQ3J10Vn>x}qCdV+U#nx!>?3_g z5ur}8GK(X_woih%^Y?&Q$RJpG9q}dA3+}Ue`vl-!;wzaS7|3J6edvOJTA_#Edo`!F z)R2_&>2`6z5549i!1_|GU?>QTFWGaV5V!n9k3%6`747R!W~Dx`JeY9W#>OEt^ zz8V$>`cyQoHe9t z4n7hLcR*oZU%%k#wiLC&` zMoo~5bM)5hplp^Dj@p7CnH|0OHe3QVN3-U=pDw zriutBOK!#XA=XfX`}2*UrAPp3^RNekbg8yRmWI)f!z@6~CY=k{S&r#BrPifUwJ`Yi zz*s+9aX)xii>0eVAt$xp#&k&ezG}csjYwbMEIFZsIN^%QphqfeeplJ}$jC_J+oLG+ z1?3Vj8PE?DlP2_0T6RP-^5ZTQHWb@3&%0Q%SZ2}$Yq+k4F)1r;{pnT&4(#w|-9v2M z&;bwZVSd`nZr}TYuZ3a3Jh4`vFFi&Boiq@jVxOly1zK2+~@{UhTw3JTRdD%y&Ua0@Yho`|V-p$VX{u33mL^1I#@m>9@)Vx$m zOF&^m#iTDSHlE4VwYvd@y`o7d{wbj4&AeAM;l@5O)XF`p3Uz*b25NyL8K-SHr{qHm z_hP?>I3>wUJtAmr-);Rvn~+|MeMB!WPY(*(4fG8<+4+3c{({c?8ejwc31Pta1sL1M1Hv4NqkvfLudUU)rmVdu6znE^q->Sl~tJ zWa#7AI1zVHXmHA^R|qv;CcQXh_6&8EeLpo-4xvYE%H--oI61eD=3#EJs){CzyP+(+ zLiCq5ofK=&K~@xSsXtlI_rkp*2lYoBj6hWfN~@W#pV}|+5t^lyV6eeom|VMlRn=(% zMu2WcsHxg3V?~F=#G{)6$K}fP~SxM?KoiA}9hTUv(v*(x;P)5UpKvCRL zBq3jgJ>A4qKqOA4$+oSo)`{Zsspfc8DR@(N@%1JtY32^}tWDt?j6GmQyU^OAu>`yS zsn&G~q4kUzsERc?S7WWn20+qHb;;7zd@^8E`+#4f2^cBabr04;F)?QVz`!EY%%W8V zH*H^S;2K~|&$yCX405Z7OPHUX`MmScYxRp^9#wvim?m{pnaafxNc*KHGUex3Bq5hrcOe!ekE5@i#KiIXUr}egCqn zG97R{$hTEN6a3t+PHrMQa<13_P&Z+=idW%CO>mb!m{A@rP&8qZ%R0}zu@0Q)WuGqE zod-Ya%*iZIScFbP_j!%}4V0Q-88ITqB%$gSCTU4Q%sitrTrqJtqTr3&3<*N zosTE>cV8vs>Nf3rsA)Bjm#SV(NSzbnfwx_18mai&~UAIk_3BcM$tx^w}+MaD@s!ZxNKt?N`rk)tuT^4%0C*fxtV>H~gLm z=LU4k)tjf6ST~eAmni$Y{SuBUsZ;lN2p(T>REiM51(4H8LBt@ki-LvytN>oJ!Sb^t zNh{U2`!?o&`l_VfekxY-AgMOfQaOvNt2YE7`xkgg7+3$!yG+7`n??9L%lFWmk%Yp- zW6g&jC_vccC}-4w+_Y3SAmn~T(?g|!JzPOQDNn_el9S$Z)7E%XF&lm(_q&W)Za8-d zr|$kvzWZYFcJ`NMw_GD6x%EMQtv6Cetn;M?f*e5+{B=c)hOT%IRlVOo;4+_8s~HEl z%&fz}IPb+A$)}mvTxpq4hCH3+v#|N3Z=@D;p*&mcq zIy%3Vsy*IcfG;%NmrSz#d*Qi;{^|1)&Vs;Eq92i1ZbY_y2LR!*H9-W0u_K~bZ@~1+ zYBaljOhiG?qP5*r56U%=rZs_4C%^omMF_ejq~;iqk%_ z*RQ9&(H<{46qvnR1~Y&JXetEMvaphpWz!P+I2YOcn#50vQfwA>;ts|IW#fJS+j%0( z;J5>aY}FcmYeJf*>yX02QYfY76gi~pz{P`P6C@g2AF7KN#`Ap^&S1Zh!VpmfR)6_p z5-wY_)NzJ)3j$EzL&ix8C zU6YvI4;KcVN-wFO)7SY~Zo&Qc@1g6rPP2-7q14)@#-tG0zZaUqH-dU8vU zErL>jJI;ZoPAE)DOd&S$kMH)q3CS%7_H5K^tsT>XsKoB?NEwv|6mA>Q(6hq(T8baI z%mu!Ykfo=xPv70YR`7M3Pr3qWX?Pmea0R;6`pDX68We9rbT;Y+q)rVXW|!z8XuIVh zV=+O;?Ef6h{@6D8KkB;MI;MzU5D<;oXAUo z!bDJY`@4##(?9Rfg|Oa8N4@Yq=|L-@4uExS^HWa`-*-ZK(XGEW;q8ezD+I zTxuKP>3LX}xX|>?TV6mG%TAI!yP_+H)=~ZH2eRJA4CH1~+7S%{KY@54_kc6ER{w3x z5kj%aukw@}R-5m;k!#m%fTWTXxj~;zeQzg6$6Y1NtZ?z>unY{Dli_YD`$`t#TH41< zA9tC104f*bFar2@;hh{!;AA6`pmaYkN7oZYcIM(KytRA+EEMG z&)j&0i3rdqC!tP4MrLQ?pllb^Zk9R38q*;+&3Wfh>WYaPjKO0Z?W9Bmm&pg;H8;U) z>@C+WC?V7xs@l~bM}E)6p$hCf`);CA%-K`?E=&$lm_};))cDwzRfR@tc|;oAxESGu z=n~(meo&!5ozFL>mFKC<^k$_R&y)odSH-u;v(wEDo4HyDAG6TjtJWZ|>MU0S0SVzo0i&JcmlIC8zOx5b6P0^<*@-09qbs>+`1sKV z21iToQ^(MU-Ue?$-{bGS&@w5tn+z_D3+J%{G{^Z9dh(?yBC0YeLGCG6H?I!=2|y$c zK_U3YWBr&0k@^SUQ$#%2`hEK@{3fSsqBrM8V%&K-O(4rH9S#iG1Ml&(dNx3dw&vR2 z<_|_xj05Mxey|8d)rM&MptWX9I!z42zrNN#U`~pdGC>PfT86CN@(H|HIIjisepJj1 zJ4zw>Ig(>BspSq(%Ln6E*YR+q%r0_+Atpq^$%UAWX<>Pnq+A6Lf9>{jCA;)u&b|Vz z3B$i5fyp$C2#M0K7@B+vH{pBxH%?33w#Jixv}HG+;IDCq_(v#{a&F0RQi}>0y`0;SV!I7Iqi{Ky2@vdj$_928~jJsxLn^@|YY0-s?6>5DxMK+vw zy~?k2AX%~`=#;zI!Hd70X%gY*Osko*)NYw87;Wf4N&nbz3A%TrWTHC{6Ds(PVo%i} zRfc4-&a>W9VM-AXpQLo}K0uDGgLO*lYpsPX``aMF7NW-VtN>KdMUjko@6lSbTJ}>5 z%+Uf%Cj`Es;Ggz(mTzy6pio66BX?6zvccus`>6<_p8gz=rJLS|D7(Lqs$6i9Z61)t zN*3cu)HzN&3Je}=mV68ogH9@QTIX#f{p6F?`75idGN8hs#|c0T^kgKBF3rluK#7zmVL0F_1YvfZ09h(x`?GOKzV zTgaQyV?@83uf1lW$hHsol~R}YmljO69K1hTUbX7rb|&=u0t8B)g><3eGdwuSE}n2b zjw?4lIC%PH{G@`SEnK-lb?64k1WDc92y-SjFCcvCRBn=yp`6$wv{Bwy>0L%AiTRl2 zZ_;X%ly-!kN7LsRX1BgkITDF9GldDN@!_Yx*#KSq zkP@lv!OF>pB6eXQ2y3JiRCI1vC;)L1jQKY!UFoE|~0j^n^l zJpQ&0dkTiul9>Y5x`R`cVOXcB`T<49K(*dYj?oj7=a&b1WV!=tSqPl=37mdfgo!jG zMiR0;OvEA{mD}E69}7rNf1E0$19oJ`Yz%&jIzeu_!?V`TkMRMLC?;+U1$E$Z{2{0V z(B#vDgk~2`kJ?1-H<^KM*y&eip>Pk$Q&h|??Fj%@@JbzP9{eG{Q+3uDzJ(Jugb^Nc zp|#Le;KC-+H|COOt0_=XlzF zcpm0YO0EEH+vX7+RLN_gVB9mmOh`L(>VLd==zy}Z=ShcQZr~ZN+)=BIC){J4E;_t< z@TpVuF@!#&Yl@F!71(#LVl+XYXKi1O4#>cvwoUDlk1UvBfCf2DBSuAxMrFrbR+0>_ z!>sOa>gq^7LXd5G?L>7}cR3Ew*B%WBBkC~eM28|*)cLB^DPnhM~ ztMk$>V&Y~>((JJ}gZ+-WVD}-pv*nYFxnV}c-zDpOOdK}qa)2<(j`3omkzj~ffQ?uw zU2Obm5)^#^E%6be&3iiQCwD%;mx)Ub1f0L$fftX8U@A~rvRQO!p|&-SkdBn*BS>c%oJN>`dTAb~q({3O7Q07Ck2sf!Xb5%R1C}pb8c+`s%w&zpIC1J4 zbv9c2rhJrpr64&#uIa#ftLdBM1I3`nsZ4vCp`H;r>B2(?S{^0z8?o^4}O^>@UM~(?YhuPR05s*2Kst>r)I zIpIJH4fyS8$@3MHu##~8p#W-RW-cl;^cv&i^wBM8X|9q;-WNP&vaKiiVZ`S-yg=DOwjurQP~7OS93!JM|k$hfXV5nUl+-8Q!+Bi2pV{ zO8r5*OV>*vwbLtYZe~WhwrX}HW?YrYcpn`HJF@mOpiIoxldTe<2%#4hI6VZb)r)~Z z1w;k;X@PB0mllZU;jg89gPmyArqoYtq?7NvZa+<8&bu3Rc~qGqcE@jRp{{U;`!BEk zh;O~ra&)h`uxHbTP)X3*h9!3`ui3!n+Q_SqC9Yuu%}G2>O--ExQ1{fSQYy>UoWNms zmU*g-4V3TPyY;hGXlP!v9T6TvQ~jrp4^ktiWSAW#6X$ior+4$mV>rE-(p$wWY>mBS zL-b@Dh2`r^p|Ca5c2Ak9uXPlv-ck!W!}ZJg&!+`Q3v@C}HO}yx@*CjgVMj$(`@cU? z_Wp`2{#d^4e!?S?;JYVGd@4vvC+4uQx@4q+o=<>$dXQ$nWT)As)+veh^pFM zAn{uC(`myUNUBNL&AzKde_WdMveM{f#!zRV4`k({vKJTAa(#fDj-9JT4F+wAf|~R@ zn1gFcqp?#!B!&I$0!3r(w=Km8{Ajb_;6%B?PJPjIM=2`o-tcIY<==7}LLQ`+r9ILg zV|mCk5A}Jf`W}mI>3#yz`KPvRsXF#|*um{~N3XCO0@Am;FV^TCQP?sWlaeblDUjvo z#ZcH?0`vv`H%AgFUV`kli)|I1hW$P)(JkmG#(7VPi!fe)N5KiyIll(JU2B2dYX)m= zLL(jfdOU@hV`a$xqnuh}up`kvRz{7zle7)gW>S@karZ9+a4#gjiLTveZw{>6^r^(r^1t_iwo6 zV{q&o}9hGYy6Zs`y`y0H{+PItG*VFdBOD&U~Mq^bqjYxnW-WEfu zO#`%&_QD^4XO31dv!vK%8K9ogDG$l5F57y8mwPuc=Gd^P=}c*+%w|81knKhC?6EPr ztzQ0erN&ta|L~wZFUS3t#yQJPPf5?G6-NRaN)HlRUx&i=BVA^0@cbD~=Ixk1vSoV|aTUACm9qd2%hg$VqjvbP{!Ve+shqgw%&@35O zCgpqijwk+VYPqKTHlwZq7g&R=F2@8Ev%Hgh#7=sux)2i%9*o@ogaweL%jzn>QlqaS z!d3N%NnDG9F?)VRMMSNu`J5n+?#t)#kk}e|D$!?iL=VgRwU(DOFrT~U@%nv^a>|Tl z{N?RLH4j2-dmfFGPaA4gk}x0H){N!?qp>pW*A*&R5T}jdm8_N8<7AXp%{$bxre?Y@ zz|%C@Y*Jx$CLEteVs!gFsSK%2M|#$f*yezuB2S`uQLfrK&UZ|JhU}I}_&!ta{`x1e z#c-Rz@f$?TUltqf=J@3>5}x}?>dx8UVd~*R51#pdv-N)dSzS(r9uqKBzrP|p@zX1X zz8BjKIMySw?YUrlpx}IsC{`H!aA*tVC};$p3OL7DMbaSMC8|*l@C!C9uaZ&Oh_Zpi?+aq|={^0T$#P#4H2pG)!(-d7 zdg|FwJx`KtPZDFl){6278LInDUWDfYWOzEVBn7XFQ#rz~y7t#XToO8#Ds3yLG*!2v zAEwOZh$X&1dzygxU;3UGx8`{lBf{xgOloyYn>DQZVism}Zj>6=Jlkknq~;CvuFYff z58mewj7UeCLS5j}(=)ekk4qhh6h{XsKW`!acqq9#*xWM^RVwSP3()<_To z2t0zQEvfKh$V!#72ST3>{qlNIoGDGgJz*I<>7v}x$!r2!J|Upy+mOPj5Izzo;T}CI zRz9k53(fVZ1k^D5j|-@2>o6DQ#^i_#+hg|?)jl9Z z)OLu8gN^_nlsT8WlJzz!;umhD_-ShrzE_EPVU5OmF+{&?!>R>u!0q|9Oe9AHyS!>5e$D8?&h`XJ=whu7bgjA8HYf zt_1%&5S7)>J{FPQbYkuxw)&d*mZX91#Ems*iO{@E>1?TWkul>Tq??AZhI{(C@!AanN6FVSwcG|ATJGKu6TWzDhN1@6&_ z-rY#j*|^OMSwGKB2VvDR!wqq3m!;^QHY(q4wM?W0WxYQGB=+CCr5gZT=05X^gjZLE zh?2-j3r^SV+IiT5*~}~LcP5?t4&Y*IY)Q4mS~0Amh&wI>QdFAdooKJ5Eo2^myQ=i0 zE5(>iqkD@yuMEN*I1W?t(dnpfmiKqteGAghqYnBD;(2NH`wqBqVZ{c+GnDxZH%fOW zq4>!ze zlwTi@FieC9hw$C9RVR}| zc*@8Sd*}PvXP=yi$S9a-}#9#LH2Vou-s+M5~Besl^GA1206xZFmaWepXli(iHPs>BUj0>j*vbWZlmNgX9Qn%@yx^rd@UDJ z0%_^*ZSapA z+Hzj{(Qj72(~{9JAJ+i=S-QA`={c;(c2@IlYy7b?9r~BWYkuV$F`Ay&B0!!^>8;IO z-+n#B^>syGh8=WjRnNP!n6H%0Kvv0}isHwOqt+#bn5jP-^qL==nDpDCViul%CHcJ# zrNQwu5LKn;>u+_e6Gj;bi+10dJ6!RYw_z`hc-QAv8Bts)>)YL4n!JdGeR=wmPmHUmpe1_Yp2hYL37AS-()jfDH9e7k1 z*vFOlq598HKB8OEmPWXXh%sIwT_VW?xMH-gl?ERU1WMUW*-)wNi=fPk~JUh0##LtO-c zeZI=VPtS<@1gTuB{2^>{&Wao|duTPgN}O2{dX(j{iIlNcecboa0d<*(%ZJ%;E;!Oc z1;xR~k|gC$ar2BA=(UV?jm$yrAi{Ky@IlVVk5IEBw#5I-ygv_MQqV0)5iey1wn%V& zv$OA27FgXcM6s^Gtgiyag}VwY={V1H1Gopx_%g*K%Au0Q$M{iiG0MPU`M?hk{-Jw` zcV@qlJ8GA412rcB?FMooL#V~@F<*;5u8CFgWj_N#kR?9Z2w!eBY9HI9GfQYDS}6i4 z-motqX0m7_Er^1_tnPLDw``$Q!|!*%Qg&l}F>JV>kvFCy4oq5X@89<&t@#td4k%P; z7|~)_SOq9oq-Zm8KFev}g5doUSJ@%I{^|ORN1hxVdhllmSwZ>G#;#>kFx(j@yUbS^ zzwhjkC$;wpG2ytrTt}!r5WL4W+o!ju^c)HVUrUqLA4t|KA41$}p$4<|i}ILhSZpUQD4hv+(1r6egACfZ9yB;9OxS{yN9w6+|-GB`yq)p1vYsrg_L!9V)mSjYz-4mK%(^WmXMo zWY9XDmmJ3?E5cAjI8OG%G--~y^-Tn44EEUr`hc>wp$h0+rdA0u^yPy0&c3Y)v^{*N zKB+?OIsW)Zv6}YdDfeC%8UF(;GOZ*-!xjR}2?wX!o#JBMsFPcdOpk_4B|ZTcBiosq zNqA=(H%*l>@&N#9;5o&TYkMGid^0upSm7je78~pHjE{CTEJ?6k`RIkq&VX948E1Ic zWAf=lH{U}t+~($TYz$B)PT!s0&ayj4JcBDMYge*uyMO1QBow&*1pj)aRTVt(pRpBT zeEbk%OIDUnF&{&d(tehfCJp1_fI_~?)8MV*qvVyH6bHc?)}h_boC7|%la1WPT75M2 z%cp02XP<6*#ne0rWS$t5*!&;0WP`dp+CD#9LxkG;Ov)69dMs~Iw5m%~3oz~w;TO0E z{QMbGA$#)Jp|0sc@kpe_aTpkSrTkE%H$iL_?la+Z;1e{_w|itRog4IFjQt3Ns%;E* zJEf%Ahu@pmjXnm4)NB>7OfDUnWJG0kFcF?c3L3NO1na%@^62M*DM(gH!_3TJs;Ano zhpubF|D-Qes>Jy4Lz}(7XXaM88|2noNuT-FTTi4T$0HQdS4HE`u{<1rPSy0J5nGxa z`tHCD!Pj$#rgmhna<-qPSPqRXEsWgq_O{vGKb|UfMkT{c@t=EI;@!c8Ld%@@Qk>Zp zo(9Z^p2&1ou*%#B;6mK`h9tk20P^+8E?NWxFFd=h!&+5O~c4(Iq(KX7nlo8!T@s{B6xJ#eq$Nvk_bzc-44)M@C%_NDZ zW%-Z)LTuF~v8a<#44G-L+i6G%$>`>PQJ{e(E136dqMdPix-9iPUf|W4*_lBBQ<21> z#5)ji|DwPIT5fsN_;sBuyAqXov*zM#Z35`R@vpr+FV%EFBF-n_&uT z3e5A^3Bw)dV`^kT8T#n}anRk8sx+JkCN7Ki5FM?y57#E9{VHt+RM?oi>uQ8*e%z|fQ#F<^N!z1N!hSrG>>|D%+K@l zfA&*KY_yX=M%w=2eZ%@}U=&9yEAZkUol0fX&xJv6+SU$^U~+ z0<~U%M~KJ5j&WO=018Q%+iqZOVv+}pFB6ZmrOP$;QfBc(sCGd7r|)+ zD!Smd_qS#C`Ncco&qT~Q{8mYDN)XMIonaJ699Iq2>2X`QgPmSqK_9Ma@VFtc9gVY< zY}LBPj!qZZ3$IQpQBAfyCK=96(GE2M1GM`}c=}Z$(j4(o5fSAf=5a6A0>@viOnPrP z#1SA)07B}Q%#sr-wjL2W7e$o)nH{XQ?ak$geR=YR$CSd4$HVhrE$My=cBm|Np4aaN ztvdsPi#FZ&8Z{Z(3ENIeb!)8UU+1;J#pL&1Tmg$<0W|Z9zAb4KR#qRyi;~}n> zmA>O@-d}-ab>~kHU^J^rbV4o^gFWskN}%rW(jZ%)vE#;>jHRI(riZZBF`s0zWt{5f z#Fy8rS!1maQ!HQoqgNl5D{m;KURk^zvo+q?TVMHtg0~fd%g9(&c+G0|uRAt5&>n(z zj6VF`)M)54D{d#8%4^i(DZBXPaj$^=BNpKoEn~QT?Q{P9H#vj_DAAENPx2?o)Zvftxnyc0wk1$6fU0 zpOinH{2!tZ3p&}%UA3R(gsDG3J}&S1hE#n-*F#8CwLNUS#X6bY&w{J;Jp#Ho+asSl zmRQ%gyTr{~RXrBHbJ9JH^(Bw_1O+L9mp)M!DxZflj_(|mpwpw4&Rml`QrZZu9KgR5 zL5$VGBj}mGl+HAuH9#7RvA}0|oi$`GB2B029EqoCF@|=bZ(@uQfy*~Y5#Kj~hQ9p= zR~lA0G&I>VAF@C~QGVK}au@Vg#JO)N>FVX{vW6WjY8dVlqAPO1h-8u&;6ncH%v}kl z;FaAN7Z5&bGOg@J^}y+&M$wwL$5FfOX8L-rZoWA1^&i{6=INqeW=$4M91MAWe+ezKb5?C_(>8w$(NyI1~(qV2=K3I3W}6`p%h7j-b64#iw05kvnOk z)o*ce^@pagH{2HC;m${_*qx)(Mea7%}NZ1s#XxA}<(k2(3@ z|6sretu0~@is%GrU>}knTm%JB;$0-v@W%**&#VSDY6_i?8)3N`k(Awsi)10`b_wBx zB#3QN5_^6-dCoqqwuD0aqw@P3a4>P$-ji z-n2#(=yZ<2hemzi!$2nP3*35{NM`cYouO=`<$C9Ri;C*=|0(QtVtdnt+}N(B^eCb} z)p7)yw?`c?Auk!Wc)Cgdn|uVtJRr8FyXsou6$hyKPVV0`+@Uy8Y2DIyalmvvAqJ87H>+s$=}+Dhl${I+3ZVrJd(wsA^O` znPmR453ulMv*7Lo>DS)NSTc)Ijp)kwXrMZ6@JIc&78=H=3flP-h*M@&*=X7MKySyg zeiB=9_99KKMxp~(<6zZlQ1nh{8{YtSYUQ&+J~tg*u<>I zNl|c(jn)GmD%$}YQAuac8FZZ7JM^frDqzeQl7!7!*>4^)E6#|qsyxBe#| zAt^FDjUzeoH1`UIk?Uhw$U(!MM9BJ4$6@{D^g~MRH8+2KIPiAiM&`DYaqoHAf`%fO zU5r;A?Ct}-Vf)q<8Z1#<4b2xmX;B9G+41<7#fFXEqz*UP@Vr#`ZT9)=ERVPIL#k;e z;JHUuVTidL(Wjw|Se9h6)fWC`nO6 zdjS&koH{!Z&YwHX&2##Ck{`6Rx4m3}ts61M%}_GgMno2IC2y`im&wb-gg0r)>~o4B z4tLx`blq>jKw0JcMOPblV3>#G@~!)@e)bVOG?8Zx3@yQiTldgNHC0^Uhz6Y#0r5b3 zAy^TU%f%=ue)y2c%`o1@usocmuQ;GA9#Kacb;qkO5P?utvlYEG-N#f?M7Ryld3T&_x7T}MQ?q)(8@J_epvBFug&u#gF+jXQKw|bl++xXt_|q`~ z0ZB=b#Dw=Tzt8`MSxXV*8+X~`$Cw$*2C3)zCxIu@Zh(ZdI{gki=HwG;6lBm_J^d^1 z^`i@2aMaxQZj%Ai&&NFvZ#FUAjz1s=#l2T#<9nb>eeCs~LJyuZN;}Pj=4(rmSo~Un zS@EIian`7mGp+vN`@R9w)^lMA=?+kVfMgY~sI0k^pk7Ik1qH{}tw<0+Nk<^LDAH>O z$Tg>s@i0kt`>!L#Y95Z9Kfc>b#Y-E9M|Yu*09R7pyW)G*ME*FJ)qt6e>W>XN**mnxAt#;p4*+u z?X%u(j;ito^1oxyNP^=%_krg%OqmWzP5so9tEbNOIbB2O!xak}p*AhP&S>00ptI?# zHiO{UXwMYD9}E|`t;smVV)YL-mT;kshIFB|@N9=$rd5+mD69Xis|=f=DV84y=u}C7 z*`6-Cv0Ub9EoL|uS~0zqc@{tEAx-Bmp>gbud*7Z?AFA(aFHhEdHJnO;U&)fz1mnkvEUP9e|+Llb~CnmUZBjKilIAY-kaiP~9 zpIw$<=5TJP1z_HsDs^r^*Mc5&9U?)|gE!96-*}im7kj;W{Efk*zg=AWl&T}_F;nLVtuH*zeCGQBg&lwXP zIDJU!fHN%@vp$O7VQRT_LC;vyff`px$=G^}0&^K`8vt4;-yf~>xpB?5m}N4jQd1)Q zQC7Q|T2R3Xs=NK0JK%i{PMCImxo~LgE&}@fgV@>U`{~7ZQ;=HdzGr$~{+}df{dnDd zS8tzrV{62Dk9ybM`0LVVb74$}Bz_FPWEO8`;Seb4L=-t3TMoIWR3tZeq5c+Q)YL0b z#Qg*5Ewr{9_FbzbFZPJCpUAv=pDz-q?dfqaMc+k$vt)$3o58k*CFYtBd3)WgS<)vl zih@&`^vvehq84>gFse3H1P}oNu+m^L3hYs=GDF&n;k)z0r4g8Q^*m^;??CWe1Wl*U ze7`K2;qh73PA4snBDc>w|UW);Gh3q#R?kiu1cVfQl}1;_)c2cWDKmP=bM1HJx%Gw7W7X% z(u})&{oS>H1oIUjOc-z||4;6h=ecbV_&pkzCe)F%=JwT^$H(j}>?mFfmu*QDV#|Db z<=qJNfl6oGD8mY#f>?4@?tcbEG`%nq$8%I)m}71dfWnmtUy3#Lq0CZWviyV2A)@btO;d#kvOOif<&y_n znE4!2mLLr4*8%r=;Yx#H{R2B+JlQM%IG+UXk^{2tulGLE&!bEht*jN*!?Z>Fov_pC z0x+mm3l^8Zk;iPhHoO;vt5FS72T$It5rp8%&2Oh^36og+H66SoZd~UBjHZr0gWDGL zk;A>IdDruA;$&_xY@VbQt^8jAz5zl0cAHqs63m&COsoL5d5jZS7qJdcUkXS=uIf=~-Z0!&Lix$BY=VA(8 zFfG%oBCWm-hr%WWu>r)&8v{45L#MEwN$(*5Rxg4n&czhIKBJ;7xTD#C9`w$`!*alm zjexE532aqWp1?LCVEL`a^Zh2aK)}Y)R`ChsAt-1m80dft@0Fs3JC(rlz}m!)#fE_Q zuoT$RKwz=Nh6CFY<_R(ZtEOd&k=9U@jJ9^@xPSvTttGI5t>3(PbE(Cv0jyJEm)bGG z5GZV+Bvw9~40AEXCy)zWFfFqSHNqqV8=$Zjv6DA%uD1o20-Nj!@TrTrIYR7c(>kJC37Y;0^+8XI=_x0lRtgJ7bc7os`KF1So7nR!fi(SP>5t=VFGY{zLxS z9{Pi)`tq%9+xGkZ{(sm%Wc8Z6dWWj|-W9*5#krWFsJ|OktRbpmq%{=vnIf)3RK+HO zp{aj4BWXGPRh)|%qWZ_b{ik9Tygv1*k<&!*H-Gat{~vb!xM1U~u@V3P002ovPDHLk FV1kYTOqu`y literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/drawable/ures.png b/composeApp/src/commonMain/composeResources/drawable/ures.png new file mode 100644 index 0000000000000000000000000000000000000000..b44be7a07ea6adab96141a7ac1b04d3134df2e5b GIT binary patch literal 13240 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, val players: List) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt new file mode 100644 index 0000000..bd8f0b6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -0,0 +1,11 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class Player( + val id: String, + val water: Int, + val spice: Int, + private val weapons: Map, + val ownedFields: List, +) { + fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 +} From bda4ff43d526b0bdf8ff3f7b64035d2169c74177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 8 Nov 2025 16:38:25 +0100 Subject: [PATCH 003/162] Introduces initial game state model Adds initial game state, player step. --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 3 --- .../mokegyesulet/it/dunestrat/model/Field.kt | 4 ++-- .../it/dunestrat/model/GameState.kt | 20 +++++++++++++++++++ .../mokegyesulet/it/dunestrat/model/Player.kt | 2 +- .../it/dunestrat/model/PlayerStep.kt | 9 +++++++++ 5 files changed, 32 insertions(+), 6 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt deleted file mode 100644 index fd957d9..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ /dev/null @@ -1,3 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.model - -data class Desert(val fields: List, val players: List) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt index 21f3383..2f68b8e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt @@ -6,5 +6,5 @@ data class Field( val spice: Int, val effectiveWeapon: Weapon, val harvester: Boolean, - val neighbours: List, -) \ No newline at end of file + val neighbours: Set, +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt new file mode 100644 index 0000000..7d5db97 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -0,0 +1,20 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class GameState( + val fields: Set, + val players: Set, +) { + + fun runTurn(playerSteps: Set): GameState { + val newFields = fields.toMutableSet() + val newPlayers = players.toMutableSet() + + TODO() + } + + companion object { + fun create12PlayerHexagon(): GameState { + TODO() + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index bd8f0b6..b6b1e5d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -5,7 +5,7 @@ data class Player( val water: Int, val spice: Int, private val weapons: Map, - val ownedFields: List, + val ownedFields: Set, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt new file mode 100644 index 0000000..66c2307 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -0,0 +1,9 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class PlayerStep( + val player: Player, + val leaveFields: Set, + val enterFields: Set, + val purchaseWeapons: Map, + val buildHarvester: Set, +) From 416b4869136a9248947c4dbdd12cd7d991831634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 8 Nov 2025 16:56:42 +0100 Subject: [PATCH 004/162] Fixes formatting --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt index bf5ed46..4459198 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt @@ -4,5 +4,5 @@ enum class Weapon { PISTOL, LASGUN, CRYSKNIFE, - LEGION -} \ No newline at end of file + LEGION, +} From 31433c2a3f7c8d2df34f5b4c08f5b9fa54acd71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 9 Nov 2025 16:12:36 +0100 Subject: [PATCH 005/162] Introduces distinct models for desert and game state. Separates the desert representation from the overall game state, allowing for independent management of the desert's fields and their properties. This enhances modularity and prepares the groundwork for implementing desert-specific features and logic separately from the game state management. --- .../hu/mokegyesulet/it/dunestrat/model/Desert.kt | 11 +++++++++++ .../hu/mokegyesulet/it/dunestrat/model/DesertField.kt | 10 ++++++++++ .../hu/mokegyesulet/it/dunestrat/model/GameState.kt | 8 +------- .../dunestrat/model/{Field.kt => GameStateField.kt} | 4 ++-- .../hu/mokegyesulet/it/dunestrat/model/Player.kt | 2 +- .../hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt | 6 +++--- 6 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt rename composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/{Field.kt => GameStateField.kt} (71%) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt new file mode 100644 index 0000000..16f1a1f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -0,0 +1,11 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class Desert( + val fields: Set, +) { + companion object { + fun create12PlayerHexagon(): Desert { + TODO() + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt new file mode 100644 index 0000000..bd8ca7f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt @@ -0,0 +1,10 @@ +package hu.mokegyesulet.it.dunestrat.model + +data class DesertField( + val id: String, + val water: Int, + val spice: Int, + val effectiveWeapon: Weapon, + val neighbours: Set, + val startingField: Boolean, +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 7d5db97..beb553f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -1,7 +1,7 @@ package hu.mokegyesulet.it.dunestrat.model data class GameState( - val fields: Set, + val fields: Set, val players: Set, ) { @@ -11,10 +11,4 @@ data class GameState( TODO() } - - companion object { - fun create12PlayerHexagon(): GameState { - TODO() - } - } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt similarity index 71% rename from composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt rename to composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index 2f68b8e..6df6adf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Field.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -1,10 +1,10 @@ package hu.mokegyesulet.it.dunestrat.model -data class Field( +data class GameStateField( val id: String, val water: Int, val spice: Int, val effectiveWeapon: Weapon, val harvester: Boolean, - val neighbours: Set, + val neighbours: Set, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index b6b1e5d..7370f8b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -5,7 +5,7 @@ data class Player( val water: Int, val spice: Int, private val weapons: Map, - val ownedFields: Set, + val ownedFields: Set, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index 66c2307..65af053 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -2,8 +2,8 @@ package hu.mokegyesulet.it.dunestrat.model data class PlayerStep( val player: Player, - val leaveFields: Set, - val enterFields: Set, + val leaveFields: Set, + val enterFields: Set, val purchaseWeapons: Map, - val buildHarvester: Set, + val buildHarvester: Set, ) From de71e067d3a225d31868759eadb6b9ba7b64433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 9 Nov 2025 17:12:10 +0100 Subject: [PATCH 006/162] Remove some breaking dependencies --- composeApp/build.gradle.kts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 5cb2d90..3a2568f 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -57,11 +57,6 @@ kotlin { implementation(compose.components.uiToolingPreview) implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) - implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.material.icons.extended) - implementation(project.dependencies.platform(libs.firebase.bom)) - implementation(libs.firebase.auth) - implementation(libs.firebase.firestore) } commonTest.dependencies { implementation(libs.kotlin.test) From 9dd7300e61ee1e26079357b65de153b8ba5c2f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Raisz?= Date: Thu, 13 Nov 2025 21:38:54 +0100 Subject: [PATCH 007/162] chore: removed unnecessary step --- composeApp/build.gradle.kts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 3a2568f..7b8fb96 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -1,7 +1,6 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jlleitschuh.gradle.ktlint.reporter.ReporterType plugins { alias(libs.plugins.kotlinMultiplatform) @@ -70,22 +69,6 @@ kotlin { tasks.getByPath("preBuild").dependsOn("ktlintFormat") -subprojects { - apply(plugin = "org.jlleitschuh.gradle.ktlint") - configure { - version.set("1.7.1") - debug.set(true) - verbose.set(true) - outputToConsole.set(true) - outputColorName.set("RED") - ignoreFailures.set(false) - reporters { - reporter(ReporterType.PLAIN) - reporter(ReporterType.CHECKSTYLE) - } - } -} - android { namespace = "hu.mokegyesulet.it.dunestrat" compileSdk = libs.versions.android.compileSdk.get().toInt() From 8961f7f48fc815b2b80a00c6184a5757d6095466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Thu, 13 Nov 2025 18:53:49 +0100 Subject: [PATCH 008/162] Adds Navigation and Icons dependencies --- composeApp/build.gradle.kts | 2 ++ gradle/libs.versions.toml | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 7b8fb96..6015bd9 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -56,6 +56,8 @@ kotlin { implementation(compose.components.uiToolingPreview) implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.navigation.compose) + implementation(libs.material.icons.extended) } commonTest.dependencies { implementation(libs.kotlin.test) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 50e5c7c..9e64013 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,15 +14,15 @@ composeMultiplatform = "1.9.2" junit = "4.13.2" kotlin = "2.2.21" kotlinx-coroutines = "1.10.2" -materialIconsExtended = "1.7.8" -navigationCompose = "2.9.6" +materialIconsExtended = "1.7.3" +navigationCompose = "2.9.0" google-services = "4.4.4" firebase = "34.5.0" ktlint-plugin = "13.1.0" [libraries] -androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } -androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } +material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } +navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } junit = { module = "junit:junit", version.ref = "junit" } @@ -34,9 +34,6 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } -firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase" } -firebase-auth = { module = "com.google.firebase:firebase-auth" } -firebase-firestore = { module = "com.google.firebase:firebase-firestore" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } From 7ae22d82c6aa87c6ba591b61cad34afb85587239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 16 Nov 2025 14:12:35 +0100 Subject: [PATCH 009/162] Adds navigation and basic menu - basic navigation and usage instructions in a dev.md - modifies a ktlint rule regarding function signatures --- composeApp/.editorconfig | 2 +- .../hu/mokegyesulet/it/dunestrat/App.kt | 24 ++----------- .../it/dunestrat/feature/MainMenu.kt | 34 +++++++++++++++++++ .../it/dunestrat/navigation/NavGraph.kt | 23 +++++++++++++ .../it/dunestrat/navigation/Screen.kt | 7 ++++ .../it/dunestrat/navigation/dev.md | 9 +++++ 6 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md diff --git a/composeApp/.editorconfig b/composeApp/.editorconfig index 4006c13..e2c1039 100644 --- a/composeApp/.editorconfig +++ b/composeApp/.editorconfig @@ -16,7 +16,7 @@ ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equa ktlint_code_style = android_studio ktlint_enum_entry_name_casing = upper_or_camel_cases ktlint_function_signature_body_expression_wrapping = default -ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset +ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 2 ktlint_ignore_back_ticked_identifier = false ktlint_property_naming_constant_naming = screaming_snake_case max_line_length = 100 diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index cd78057..d6904aa 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import dune_strat.composeapp.generated.resources.Res import dune_strat.composeapp.generated.resources.compose_multiplatform +import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.ui.tooling.preview.Preview @@ -22,27 +23,6 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Preview fun App() { MaterialTheme { - var showContent by remember { mutableStateOf(false) } - Column( - modifier = Modifier - .background(MaterialTheme.colorScheme.primaryContainer) - .safeContentPadding() - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button(onClick = { showContent = !showContent }) { - Text("Click me!") - } - AnimatedVisibility(showContent) { - val greeting = remember { Greeting().greet() } - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Image(painterResource(Res.drawable.compose_multiplatform), null) - Text("Compose: $greeting") - } - } - } + NavGraph() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt new file mode 100644 index 0000000..5aba143 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt @@ -0,0 +1,34 @@ +package hu.mokegyesulet.it.dunestrat.feature + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun MainMenu( + onPlaceholder1: () -> Unit, + onPlaceholder2: () -> Unit, +) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceEvenly, + ) { + Button( + onClick = onPlaceholder1, + ) { + Text(text = "Placeholder 1") + } + + Button( + onClick = onPlaceholder2, + ) { + Text(text = "Placeholder 2") + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt new file mode 100644 index 0000000..060c156 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -0,0 +1,23 @@ +package hu.mokegyesulet.it.dunestrat.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import hu.mokegyesulet.it.dunestrat.feature.MainMenu + +@Composable +fun NavGraph(navController: NavHostController = rememberNavController()) { + NavHost( + navController = navController, + startDestination = Screen.MainMenu.route, + ) { + composable(Screen.MainMenu.route) { + MainMenu( + onPlaceholder1 = {}, + onPlaceholder2 = {}, + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt new file mode 100644 index 0000000..7488c3a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -0,0 +1,7 @@ +package hu.mokegyesulet.it.dunestrat.navigation + +sealed class Screen( + val route: String, +) { + data object MainMenu : Screen(route = "main-menu") +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md new file mode 100644 index 0000000..8086e60 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md @@ -0,0 +1,9 @@ +To add a new composable to the navigation follow these steps: +- Create a data object/class for it in `Screen.kt` + - If you don't need additional information from navigation use a data object: `data object YourComponentsName : Screen("your_components_name")` + - If you need some additional info (e.g. id of an object) use a data class: `data class YourComponentsName(val objectId: Int, ... ): Screen("your_components_name")` +- Create a `composable(Screen.YourComponentsName.route)` entry in `NavGraph.kt` + - Add your composable to it +- Fill in the lambdas for navigation + - Use `navController.navigate(Screen.OtherComponentsName.route)` to navigate to other components + - Use `navController.popBackStack()` to navigate to the previously displayed component before your component \ No newline at end of file From 628366fac96fcef2ec28370728106cb8328be3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 16 Nov 2025 17:57:27 +0100 Subject: [PATCH 010/162] Track required plugins for project And some other files --- .gitignore | 7 ++++++- .idea/AndroidProjectSystem.xml | 6 ++++++ .idea/copilot.data.migration.agent.xml | 6 ++++++ .idea/copilot.data.migration.ask.xml | 6 ++++++ .idea/copilot.data.migration.ask2agent.xml | 6 ++++++ .idea/copilot.data.migration.edit.xml | 6 ++++++ .idea/externalDependencies.xml | 7 +++++++ .idea/gradle.xml | 11 +++++++++++ .idea/misc.xml | 4 ++++ .idea/vcs.xml | 6 ++++++ 10 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .idea/AndroidProjectSystem.xml create mode 100644 .idea/copilot.data.migration.agent.xml create mode 100644 .idea/copilot.data.migration.ask.xml create mode 100644 .idea/copilot.data.migration.ask2agent.xml create mode 100644 .idea/copilot.data.migration.edit.xml create mode 100644 .idea/externalDependencies.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index adfa9bf..e8b5977 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ xcuserdata !src/**/build/ local.properties -.idea .DS_Store captures .externalNativeBuild @@ -17,3 +16,9 @@ captures !*.xcworkspace/contents.xcworkspacedata **/xcshareddata/WorkspaceSettings.xcsettings node_modules/ + +# IntelliJ files +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf/ +.idea/*.iml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml new file mode 100644 index 0000000..4ea72a9 --- /dev/null +++ b/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml new file mode 100644 index 0000000..7ef04e2 --- /dev/null +++ b/.idea/copilot.data.migration.ask.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000..1f2ea11 --- /dev/null +++ b/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml new file mode 100644 index 0000000..8648f94 --- /dev/null +++ b/.idea/copilot.data.migration.edit.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml new file mode 100644 index 0000000..85b9f87 --- /dev/null +++ b/.idea/externalDependencies.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..1b387c7 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6ed36dd --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 7be71bcbd159ff2eb4499e29063cab292826fdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 23 Nov 2025 18:07:38 +0100 Subject: [PATCH 011/162] Added Compose Hot Reload And bumped some dependency versions --- .idea/artifacts/composeApp_js.xml | 8 ++++++++ .idea/artifacts/composeApp_jvm.xml | 8 ++++++++ .idea/artifacts/composeApp_wasm_js.xml | 8 ++++++++ .idea/gradle.xml | 7 +++++++ .idea/ktlint-plugin.xml | 7 +++++++ .idea/misc.xml | 4 ++++ .idea/modules.xml | 11 +++++++++++ .idea/runConfigurations.xml | 17 +++++++++++++++++ composeApp/build.gradle.kts | 6 ++++++ .../kotlin/hu/mokegyesulet/it/dunestrat/App.kt | 16 +--------------- .../kotlin/hu/mokegyesulet/it/dunestrat/main.kt | 1 + gradle/libs.versions.toml | 12 ++++++------ 12 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 .idea/artifacts/composeApp_js.xml create mode 100644 .idea/artifacts/composeApp_jvm.xml create mode 100644 .idea/artifacts/composeApp_wasm_js.xml create mode 100644 .idea/ktlint-plugin.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations.xml diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml new file mode 100644 index 0000000..00ef8a3 --- /dev/null +++ b/.idea/artifacts/composeApp_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml new file mode 100644 index 0000000..01d03af --- /dev/null +++ b/.idea/artifacts/composeApp_jvm.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml new file mode 100644 index 0000000..0bd6a39 --- /dev/null +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 1b387c7..1d8dc37 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,10 +1,17 @@ + diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml new file mode 100644 index 0000000..e8bd90c --- /dev/null +++ b/.idea/ktlint-plugin.xml @@ -0,0 +1,7 @@ + + + + DISTRACT_FREE + DEFAULT + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 6ed36dd..391d412 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f7f6104 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 6015bd9..d5b7a78 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -67,6 +67,12 @@ kotlin { implementation(libs.kotlinx.coroutinesSwing) } } + + jvmToolchain(21) +} + +tasks.withType().configureEach { + compilerOptions.jvmTarget.set(JvmTarget.JVM_21) } tasks.getByPath("preBuild").dependsOn("ktlintFormat") diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index d6904aa..c49f363 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -1,22 +1,8 @@ package hu.mokegyesulet.it.dunestrat -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.safeContentPadding -import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import dune_strat.composeapp.generated.resources.Res -import dune_strat.composeapp.generated.resources.compose_multiplatform +import androidx.compose.runtime.Composable import hu.mokegyesulet.it.dunestrat.navigation.NavGraph -import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.ui.tooling.preview.Preview @Composable diff --git a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt index bd9fbcf..1896c35 100644 --- a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt +++ b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt @@ -7,6 +7,7 @@ fun main() = application { Window( onCloseRequest = ::exitApplication, title = "dune-strat", + alwaysOnTop = true, ) { App() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9e64013..5aab40b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,22 +3,22 @@ agp = "8.12.3" android-compileSdk = "36" android-minSdk = "24" android-targetSdk = "36" -androidx-activity = "1.11.0" +androidx-activity = "1.12.0" androidx-appcompat = "1.7.1" androidx-core = "1.17.0" androidx-espresso = "3.7.0" -androidx-lifecycle = "2.9.5" +androidx-lifecycle = "2.9.6" androidx-testExt = "1.3.0" -composeHotReload = "1.0.0-rc02" -composeMultiplatform = "1.9.2" +composeHotReload = "1.0.0-rc04" +composeMultiplatform = "1.9.3" junit = "4.13.2" kotlin = "2.2.21" kotlinx-coroutines = "1.10.2" materialIconsExtended = "1.7.3" -navigationCompose = "2.9.0" +navigationCompose = "2.9.1" google-services = "4.4.4" firebase = "34.5.0" -ktlint-plugin = "13.1.0" +ktlint-plugin = "14.0.1" [libraries] material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } From 74763ac1e35cc220297d45929fd28011385a2c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Tue, 25 Nov 2025 15:30:50 +0100 Subject: [PATCH 012/162] Bump to 1.0.0, remove unnecessary config from window --- .idea/misc.xml | 1 - .../src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt | 1 - gradle/libs.versions.toml | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 391d412..026022e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt index 1896c35..bd9fbcf 100644 --- a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt +++ b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/main.kt @@ -7,7 +7,6 @@ fun main() = application { Window( onCloseRequest = ::exitApplication, title = "dune-strat", - alwaysOnTop = true, ) { App() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5aab40b..852d96c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ androidx-core = "1.17.0" androidx-espresso = "3.7.0" androidx-lifecycle = "2.9.6" androidx-testExt = "1.3.0" -composeHotReload = "1.0.0-rc04" +composeHotReload = "1.0.0" composeMultiplatform = "1.9.3" junit = "4.13.2" kotlin = "2.2.21" From fc87bfbcee55604fb7643eced75b9040966c773a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9they-Prikkel?= Date: Sun, 9 Nov 2025 20:17:18 +0100 Subject: [PATCH 013/162] Trying and hoping --- .../it/dunestrat/model/GameState.kt | 34 +++++++++++++++++-- .../mokegyesulet/it/dunestrat/model/Player.kt | 25 ++++++++++++++ .../it/dunestrat/model/PlayerStep.kt | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index beb553f..588f67e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -6,9 +6,39 @@ data class GameState( ) { fun runTurn(playerSteps: Set): GameState { - val newFields = fields.toMutableSet() + val newFields = mutableSetOf() val newPlayers = players.toMutableSet() + val playerSteps = playerSteps.toMutableSet() - TODO() + this.leaveFields(newPlayers, playerSteps) + this.waterConsumption(newPlayers, playerSteps) + + + + return this.copy(fields = newFields, players = newPlayers) + } + + + fun leaveFields(players: Set, playerSteps: Set) { + players.map { player -> + player.leaveFields(getPlayerStepById(player.id, playerSteps).leaveFields) + } + } + + fun waterConsumption(players: Set, playerSteps: Set): Set { + var newPlayerSteps = playerSteps.toMutableSet() + players.map { player -> + val res = player.waterConsumption() + if (res.second) { + newPlayerSteps = newPlayerSteps.filter { playerStep -> playerStep.playerId == player.id }.toMutableSet() + newPlayerSteps.add(PlayerStep(player.id, setOf(), setOf(), mapOf(), setOf())) + } + res.first + } + return newPlayerSteps + } + + fun getPlayerStepById(playerId: String, playerSteps: Set): PlayerStep { + return playerSteps.first() { playerStep -> playerStep.playerId == playerId } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 7370f8b..5b7cc94 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -8,4 +8,29 @@ data class Player( val ownedFields: Set, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 + + fun leaveFields(fields: Set): Player { + return this.copy(ownedFields = ownedFields - fields) + } + + fun waterConsumption(): Pair { + var waterChange = 0 + ownedFields.forEach { field -> waterChange += field.water } + val newWater = this.water + waterChange + if (newWater >= 0) { + return this.copy(water = newWater) to false + } + + return this.leaveFields(ownedFields.filter { field -> field.water < 0 }.toSet()).copy(water = 0) to true + } + + fun purchaseWeapons(purchaseWeapons: Map, purchasePrice: Int): Player { + val newWeapons = weapons.toMutableMap() + purchaseWeapons.forEach { weapon -> + newWeapons[weapon.key] = getWeaponCount(weapon.key) + weapon.value + } + + return this.copy(weapons = newWeapons, spice = spice - purchasePrice) + } + } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index 65af053..2eb9e05 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -1,7 +1,7 @@ package hu.mokegyesulet.it.dunestrat.model data class PlayerStep( - val player: Player, + val playerId: String, val leaveFields: Set, val enterFields: Set, val purchaseWeapons: Map, From fd8518001c2079192a3cce725216d6564c7eb76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Tue, 11 Nov 2025 15:58:51 +0100 Subject: [PATCH 014/162] Modifies model to use mutable objects --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 2 +- .../it/dunestrat/model/DesertField.kt | 14 +++---- .../it/dunestrat/model/GameState.kt | 37 +++++++------------ .../it/dunestrat/model/GameStateField.kt | 2 +- .../mokegyesulet/it/dunestrat/model/Player.kt | 34 ++++++++--------- .../it/dunestrat/model/PlayerStep.kt | 10 ++--- 6 files changed, 44 insertions(+), 55 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 16f1a1f..b83c16b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -1,6 +1,6 @@ package hu.mokegyesulet.it.dunestrat.model -data class Desert( +class Desert( val fields: Set, ) { companion object { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt index bd8ca7f..9326e47 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt @@ -1,10 +1,10 @@ package hu.mokegyesulet.it.dunestrat.model -data class DesertField( - val id: String, - val water: Int, - val spice: Int, - val effectiveWeapon: Weapon, - val neighbours: Set, - val startingField: Boolean, +class DesertField( + var id: String, + var water: Int, + var spice: Int, + var effectiveWeapon: Weapon, + val neighbours: MutableSet, + var startingField: Boolean, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 588f67e..d17ad13 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -1,44 +1,35 @@ package hu.mokegyesulet.it.dunestrat.model -data class GameState( +class GameState( val fields: Set, val players: Set, ) { - fun runTurn(playerSteps: Set): GameState { - val newFields = mutableSetOf() - val newPlayers = players.toMutableSet() + fun runTurn(playerSteps: MutableSet) { val playerSteps = playerSteps.toMutableSet() - this.leaveFields(newPlayers, playerSteps) - this.waterConsumption(newPlayers, playerSteps) - - - - return this.copy(fields = newFields, players = newPlayers) + this.leaveFields(playerSteps) + this.waterConsumption(playerSteps) } - - fun leaveFields(players: Set, playerSteps: Set) { + fun leaveFields(playerSteps: Set) { players.map { player -> player.leaveFields(getPlayerStepById(player.id, playerSteps).leaveFields) } } - fun waterConsumption(players: Set, playerSteps: Set): Set { - var newPlayerSteps = playerSteps.toMutableSet() - players.map { player -> + fun waterConsumption(playerSteps: MutableSet) { + players.forEach { player -> val res = player.waterConsumption() - if (res.second) { - newPlayerSteps = newPlayerSteps.filter { playerStep -> playerStep.playerId == player.id }.toMutableSet() - newPlayerSteps.add(PlayerStep(player.id, setOf(), setOf(), mapOf(), setOf())) + if (res) { + playerSteps.removeAll { it.playerId == player.id } + playerSteps.add(PlayerStep(player.id, setOf(), setOf(), mapOf(), setOf())) } - res.first } - return newPlayerSteps } - fun getPlayerStepById(playerId: String, playerSteps: Set): PlayerStep { - return playerSteps.first() { playerStep -> playerStep.playerId == playerId } - } + fun getPlayerStepById(playerId: String, playerSteps: Set): PlayerStep = + playerSteps.first { playerStep -> + playerStep.playerId == playerId + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index 6df6adf..b22f238 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -5,6 +5,6 @@ data class GameStateField( val water: Int, val spice: Int, val effectiveWeapon: Weapon, - val harvester: Boolean, + var harvester: Boolean, val neighbours: Set, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 5b7cc94..baeb7f5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -1,36 +1,34 @@ package hu.mokegyesulet.it.dunestrat.model -data class Player( +class Player( val id: String, - val water: Int, - val spice: Int, - private val weapons: Map, - val ownedFields: Set, + var water: Int, + var spice: Int, + private val weapons: MutableMap, + val ownedFields: MutableSet, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 - fun leaveFields(fields: Set): Player { - return this.copy(ownedFields = ownedFields - fields) + fun leaveFields(fields: Set) { + ownedFields.removeAll(fields) } - fun waterConsumption(): Pair { + fun waterConsumption(): Boolean { var waterChange = 0 ownedFields.forEach { field -> waterChange += field.water } - val newWater = this.water + waterChange - if (newWater >= 0) { - return this.copy(water = newWater) to false + water += waterChange + if (water >= 0) { + return false } - - return this.leaveFields(ownedFields.filter { field -> field.water < 0 }.toSet()).copy(water = 0) to true + leaveFields(ownedFields.filter { field -> field.water < 0 }.toSet()) + return true } - fun purchaseWeapons(purchaseWeapons: Map, purchasePrice: Int): Player { - val newWeapons = weapons.toMutableMap() + fun purchaseWeapons(purchaseWeapons: Map, purchasePrice: Int) { purchaseWeapons.forEach { weapon -> - newWeapons[weapon.key] = getWeaponCount(weapon.key) + weapon.value + weapons[weapon.key] = getWeaponCount(weapon.key) + weapon.value } - return this.copy(weapons = newWeapons, spice = spice - purchasePrice) + spice -= purchasePrice } - } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index 2eb9e05..0543082 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -1,9 +1,9 @@ package hu.mokegyesulet.it.dunestrat.model -data class PlayerStep( +class PlayerStep( val playerId: String, - val leaveFields: Set, - val enterFields: Set, - val purchaseWeapons: Map, - val buildHarvester: Set, + var leaveFields: Set, + var enterFields: Set, + var purchaseWeapons: Map, + var buildHarvester: Set, ) From 38127bc4a938473dcb27fcc09501ac012d3f808e Mon Sep 17 00:00:00 2001 From: domahet Date: Sun, 16 Nov 2025 18:04:45 +0100 Subject: [PATCH 015/162] implement some functions --- .../it/dunestrat/model/GameState.kt | 6 ++++ .../mokegyesulet/it/dunestrat/model/Player.kt | 36 +++++++++++++++++++ .../mokegyesulet/it/dunestrat/model/Weapon.kt | 10 +++--- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index d17ad13..937509a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -9,6 +9,7 @@ class GameState( val playerSteps = playerSteps.toMutableSet() this.leaveFields(playerSteps) + this.waterConsumption(playerSteps) } @@ -32,4 +33,9 @@ class GameState( playerSteps.first { playerStep -> playerStep.playerId == playerId } + + fun checkPurchases(playerSteps: MutableSet) { + players.forEach { player -> + } + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index baeb7f5..b9ed857 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -1,9 +1,13 @@ package hu.mokegyesulet.it.dunestrat.model +import kotlin.collections.forEach +import kotlin.math.pow + class Player( val id: String, var water: Int, var spice: Int, + var harvestersPurchased: Int, private val weapons: MutableMap, val ownedFields: MutableSet, ) { @@ -17,10 +21,13 @@ class Player( var waterChange = 0 ownedFields.forEach { field -> waterChange += field.water } water += waterChange + + // return whether there is an issue or not if (water >= 0) { return false } leaveFields(ownedFields.filter { field -> field.water < 0 }.toSet()) + water = 0 return true } @@ -31,4 +38,33 @@ class Player( spice -= purchasePrice } + + fun deliverWeapons(purchaseWeapons: Map) { + purchaseWeapons.keys.forEach { weapon -> + weapons[weapon] = getWeaponCount(weapon) + (purchaseWeapons[weapon] ?: 0) + } + } + + fun purchaseHarvester(purchaseField: GameStateField, purchasePrice: Int) { + purchaseField.harvester = true + harvestersPurchased++ + spice -= purchasePrice + } + + fun calculatePrices(purchases: PlayerStep): Map { + val prices = mutableMapOf() + var sum = 0 + purchases.purchaseWeapons.keys.forEach { weapon -> + sum += weapon.price + } + prices["weapons"] = sum + prices["harvester"] = 5 * 2.toDouble().pow(harvestersPurchased).toInt() + return prices + } + + fun validatePrices(purchasePrices: MutableMap): Boolean { + var sum = 0 + purchasePrices.values.forEach { value -> sum += value } + return sum >= spice + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt index 4459198..79bf13b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Weapon.kt @@ -1,8 +1,8 @@ package hu.mokegyesulet.it.dunestrat.model -enum class Weapon { - PISTOL, - LASGUN, - CRYSKNIFE, - LEGION, +enum class Weapon(val price: Int) { + PISTOL(1), + LASGUN(1), + CRYSKNIFE(1), + LEGION(2), } From 26b543b8044a42d8286b20d92d77bd9f18d6112e Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 16 Nov 2025 18:22:58 +0100 Subject: [PATCH 016/162] gamestate chekpurchases --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt | 2 +- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 937509a..d1b86de 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -35,7 +35,7 @@ class GameState( } fun checkPurchases(playerSteps: MutableSet) { - players.forEach { player -> + players.forEach { player -> player.validatePrices(player.calculatePrices(getPlayerStepById(player.id, playerSteps))) } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index b9ed857..575eb3b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -51,7 +51,7 @@ class Player( spice -= purchasePrice } - fun calculatePrices(purchases: PlayerStep): Map { + fun calculatePrices(purchases: PlayerStep): mutableMap { val prices = mutableMapOf() var sum = 0 purchases.purchaseWeapons.keys.forEach { weapon -> @@ -67,4 +67,5 @@ class Player( purchasePrices.values.forEach { value -> sum += value } return sum >= spice } + } From f15e4d96bbdf941a70ebd0956dfd4e491a4d235a Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 16 Nov 2025 18:55:29 +0100 Subject: [PATCH 017/162] purchases does not requer price to be specified, it will calculate it by itself --- .../hu/mokegyesulet/it/dunestrat/model/Player.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 575eb3b..26c38b5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -31,7 +31,12 @@ class Player( return true } - fun purchaseWeapons(purchaseWeapons: Map, purchasePrice: Int) { + fun purchaseWeapons(purchaseWeapons: Map) { + var purchasePrice = 0 + purchaseWeapons.keys.forEach { weapon -> + purchasePrice += weapon.price + } + purchaseWeapons.forEach { weapon -> weapons[weapon.key] = getWeaponCount(weapon.key) + weapon.value } @@ -45,10 +50,10 @@ class Player( } } - fun purchaseHarvester(purchaseField: GameStateField, purchasePrice: Int) { + fun purchaseHarvester(purchaseField: GameStateField) { purchaseField.harvester = true harvestersPurchased++ - spice -= purchasePrice + spice -= 5 * 2.toDouble().pow(harvestersPurchased).toInt() } fun calculatePrices(purchases: PlayerStep): mutableMap { From 8b99d873519fdaf9d31fd938d3a8b03880fce31f Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 16 Nov 2025 19:19:46 +0100 Subject: [PATCH 018/162] fixed calculatePrices to account for multiple harvesters --- .../hu/mokegyesulet/it/dunestrat/model/Player.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 26c38b5..acaa8be 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -56,15 +56,16 @@ class Player( spice -= 5 * 2.toDouble().pow(harvestersPurchased).toInt() } - fun calculatePrices(purchases: PlayerStep): mutableMap { - val prices = mutableMapOf() + fun calculatePrices(purchases: PlayerStep): Int { var sum = 0 purchases.purchaseWeapons.keys.forEach { weapon -> sum += weapon.price } - prices["weapons"] = sum - prices["harvester"] = 5 * 2.toDouble().pow(harvestersPurchased).toInt() - return prices + + for (i in 0.. purchases.buildHarvester.size) { + sum += 5 * 2.toDouble().pow(harvestersPurchased+i).toInt() + } + return sum } fun validatePrices(purchasePrices: MutableMap): Boolean { From 6f2b7276eeb20dc09c5bb368617617b4208f5141 Mon Sep 17 00:00:00 2001 From: domahet Date: Sun, 16 Nov 2025 19:39:24 +0100 Subject: [PATCH 019/162] implement harvester purchase --- .../it/dunestrat/model/GameState.kt | 18 ++++++++++++++++-- .../mokegyesulet/it/dunestrat/model/Player.kt | 13 ++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index d1b86de..899b225 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -9,8 +9,9 @@ class GameState( val playerSteps = playerSteps.toMutableSet() this.leaveFields(playerSteps) - this.waterConsumption(playerSteps) + this.checkPurchases(playerSteps) + // TODO: implement combat } fun leaveFields(playerSteps: Set) { @@ -35,7 +36,20 @@ class GameState( } fun checkPurchases(playerSteps: MutableSet) { - players.forEach { player -> player.validatePrices(player.calculatePrices(getPlayerStepById(player.id, playerSteps))) + players.forEach { player -> + player.inDebt = player.validatePrices( + player.calculatePrices(getPlayerStepById(player.id, playerSteps)), + ) + } + } + + fun purchaseHarvesers(playerSteps: MutableSet) { + players.forEach { player -> + if (!player.inDebt) { + getPlayerStepById(player.id, playerSteps).buildHarvester.forEach { field -> + player.purchaseHarvester(field) + } + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index acaa8be..7558272 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -10,6 +10,7 @@ class Player( var harvestersPurchased: Int, private val weapons: MutableMap, val ownedFields: MutableSet, + var inDebt: Boolean, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 @@ -56,16 +57,15 @@ class Player( spice -= 5 * 2.toDouble().pow(harvestersPurchased).toInt() } - fun calculatePrices(purchases: PlayerStep): Int { + fun calculatePrices(purchases: PlayerStep): MutableMap { + val prices = mutableMapOf() var sum = 0 purchases.purchaseWeapons.keys.forEach { weapon -> sum += weapon.price } - - for (i in 0.. purchases.buildHarvester.size) { - sum += 5 * 2.toDouble().pow(harvestersPurchased+i).toInt() - } - return sum + prices["weapons"] = sum + prices["harvester"] = 5 * 2.toDouble().pow(harvestersPurchased).toInt() + return prices } fun validatePrices(purchasePrices: MutableMap): Boolean { @@ -73,5 +73,4 @@ class Player( purchasePrices.values.forEach { value -> sum += value } return sum >= spice } - } From f405b0e5be0b6071b336fc92a185e1ecd071ecce Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 23 Nov 2025 19:38:10 +0100 Subject: [PATCH 020/162] In theory evrything --- .../it/dunestrat/model/GameState.kt | 113 ++++++++++++++++-- .../mokegyesulet/it/dunestrat/model/Player.kt | 40 ++++--- .../it/dunestrat/model/PlayerStep.kt | 2 +- 3 files changed, 131 insertions(+), 24 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 899b225..c6a9d05 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -11,7 +11,11 @@ class GameState( this.leaveFields(playerSteps) this.waterConsumption(playerSteps) this.checkPurchases(playerSteps) - // TODO: implement combat + this.buildHarvesters(playerSteps) + this.purchaseWeapons(playerSteps) + this.produce() + this.expansions(playerSteps) + this.deliverWeapons(playerSteps) } fun leaveFields(playerSteps: Set) { @@ -37,19 +41,112 @@ class GameState( fun checkPurchases(playerSteps: MutableSet) { players.forEach { player -> - player.inDebt = player.validatePrices( - player.calculatePrices(getPlayerStepById(player.id, playerSteps)), - ) + player.inDebt = + player.spice >= player.calculatePrices(getPlayerStepById(player.id, playerSteps)) } } - fun purchaseHarvesers(playerSteps: MutableSet) { - players.forEach { player -> - if (!player.inDebt) { - getPlayerStepById(player.id, playerSteps).buildHarvester.forEach { field -> + fun buildHarvesters(playerSteps: MutableSet) { + players.filter { !it.inDebt }.forEach { player -> + getPlayerStepById(player.id, playerSteps).buildHarvesters.forEach { field -> + if (field in player.ownedFields) { player.purchaseHarvester(field) } } } } + + fun purchaseWeapons(playerSteps: MutableSet) { + players.filter { !it.inDebt }.forEach { player -> + player.purchaseWeapons(getPlayerStepById(player.id, playerSteps).purchaseWeapons) + } + } + + fun produceResources() { + players.forEach { player -> + player.ownedFields.forEach { field -> + player.spice += field.spice + player.water += field.water + } + } + } + + fun deliverWeapons(playerSteps: MutableSet) { + players.filter { !it.inDebt }.forEach { player -> + player.deliverWeapons(getPlayerStepById(player.id, playerSteps).purchaseWeapons) + } + } + + fun expansions(playerSteps: MutableSet) { + val chosenfields = mutableMapOf>() + val losses = mutableMapOf() + + players.forEach { player -> + losses[player] = 0.0 + } + + fields.forEach { field -> + chosenfields[field] = mutableSetOf() + } + + players.filter { !it.inDebt }.forEach { player -> + getPlayerStepById(player.id, playerSteps).enterFields.forEach { field -> + if (player.isFieldReachable(field)) { + chosenfields[field]?.add(player) + } + } + } + + chosenfields.forEach { entry -> + val field = entry.key + val players = entry.value + + if (players.isNotEmpty()) { + var max = players.first().calculatePower(field) + var winingplayer: Player = players.first() + var draw = false + players.filter { player -> player != winingplayer }.forEach { player -> + val power = player.calculatePower(field) + if (max < power) { + max = power + winingplayer = player + draw = false + } else if (max == power) { + draw = true + } + if (fieldOcupiedBy(field) != null) { + if (fieldOcupiedBy(field)!!.calculatePower(field) >= max) { + draw = true + } else if (draw == true) { + player.leaveFields(setOf(field)) + } + } else if (!draw) { + winingplayer.ownedFields.add(field) + losses[winingplayer] = losses[winingplayer]!! + 0.2 + } else { + losses[winingplayer] = losses[winingplayer]!! + 0.1 + } + players.filter { player -> player != winingplayer }.forEach { player -> + losses[player] = losses[player]!! + 0.1 + } + } + } + } + + players.forEach { player -> + if (losses[player]!! > 1.0) { + losses[player] = 1.0 + } + player.loseWeaponPrecent(losses[player]!!) + } + } + + fun fieldOcupiedBy(field: GameStateField): Player? { + players.forEach { player -> + if (field in player.ownedFields) { + return player + } + } + return null + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 7558272..20e6d89 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -38,10 +38,6 @@ class Player( purchasePrice += weapon.price } - purchaseWeapons.forEach { weapon -> - weapons[weapon.key] = getWeaponCount(weapon.key) + weapon.value - } - spice -= purchasePrice } @@ -53,24 +49,38 @@ class Player( fun purchaseHarvester(purchaseField: GameStateField) { purchaseField.harvester = true - harvestersPurchased++ - spice -= 5 * 2.toDouble().pow(harvestersPurchased).toInt() + spice -= 5 * 2.toDouble().pow(harvestersPurchased++).toInt() } - fun calculatePrices(purchases: PlayerStep): MutableMap { - val prices = mutableMapOf() + fun calculatePrices(purchases: PlayerStep): Int { var sum = 0 purchases.purchaseWeapons.keys.forEach { weapon -> sum += weapon.price } - prices["weapons"] = sum - prices["harvester"] = 5 * 2.toDouble().pow(harvestersPurchased).toInt() - return prices + sum += 5 * 2.toDouble().pow(harvestersPurchased).toInt() + return sum } - fun validatePrices(purchasePrices: MutableMap): Boolean { - var sum = 0 - purchasePrices.values.forEach { value -> sum += value } - return sum >= spice + fun calculatePower(field: GameStateField): Int { + var sum = getWeaponCount(Weapon.LEGION) * 2 + getWeaponCount(field.effectiveWeapon) * 2 + weapons.keys.forEach { weapon -> + sum += getWeaponCount(weapon) + } + return sum + } + + fun isFieldReachable(fieldinqestion: GameStateField): Boolean { + ownedFields.filter { field -> field != fieldinqestion }.forEach { field -> + if (fieldinqestion in field.neighbours) { + return true + } + } + return false + } + + fun loseWeaponPrecent(precent: Double) { + weapons.keys.forEach { weapon -> + weapons[weapon] = ((weapons[weapon]!!.toDouble()) * (1 - precent)).toInt() + } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index 0543082..f9c8d35 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -5,5 +5,5 @@ class PlayerStep( var leaveFields: Set, var enterFields: Set, var purchaseWeapons: Map, - var buildHarvester: Set, + var buildHarvesters: Set, ) From 574ab16f0af6c625ee16e483363a90be1de41644 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 30 Nov 2025 17:01:12 +0100 Subject: [PATCH 021/162] Refractored GameState --- .../it/dunestrat/model/GameState.kt | 49 +++++++++---------- .../mokegyesulet/it/dunestrat/model/Player.kt | 6 ++- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index c6a9d05..15fdeaf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -13,7 +13,7 @@ class GameState( this.checkPurchases(playerSteps) this.buildHarvesters(playerSteps) this.purchaseWeapons(playerSteps) - this.produce() + this.produceResources() this.expansions(playerSteps) this.deliverWeapons(playerSteps) } @@ -39,14 +39,14 @@ class GameState( playerStep.playerId == playerId } - fun checkPurchases(playerSteps: MutableSet) { + fun checkPurchases(playerSteps: Set) { players.forEach { player -> player.inDebt = player.spice >= player.calculatePrices(getPlayerStepById(player.id, playerSteps)) } } - fun buildHarvesters(playerSteps: MutableSet) { + fun buildHarvesters(playerSteps: Set) { players.filter { !it.inDebt }.forEach { player -> getPlayerStepById(player.id, playerSteps).buildHarvesters.forEach { field -> if (field in player.ownedFields) { @@ -56,7 +56,7 @@ class GameState( } } - fun purchaseWeapons(playerSteps: MutableSet) { + fun purchaseWeapons(playerSteps: Set) { players.filter { !it.inDebt }.forEach { player -> player.purchaseWeapons(getPlayerStepById(player.id, playerSteps).purchaseWeapons) } @@ -71,13 +71,13 @@ class GameState( } } - fun deliverWeapons(playerSteps: MutableSet) { + fun deliverWeapons(playerSteps: Set) { players.filter { !it.inDebt }.forEach { player -> player.deliverWeapons(getPlayerStepById(player.id, playerSteps).purchaseWeapons) } } - fun expansions(playerSteps: MutableSet) { + fun expansions(playerSteps: Set) { val chosenfields = mutableMapOf>() val losses = mutableMapOf() @@ -89,27 +89,26 @@ class GameState( chosenfields[field] = mutableSetOf() } - players.filter { !it.inDebt }.forEach { player -> - getPlayerStepById(player.id, playerSteps).enterFields.forEach { field -> - if (player.isFieldReachable(field)) { - chosenfields[field]?.add(player) - } + players.filter { !it.inDebt } + .forEach { player -> + getPlayerStepById(player.id, playerSteps).enterFields + .filter { field -> player.isFieldReachable(field) } + .forEach { field -> chosenfields[field]?.add(player) } } - } - chosenfields.forEach { entry -> - val field = entry.key - val players = entry.value + chosenfields.filter { it.value.isNotEmpty() } + .forEach { entry -> + val field = entry.key + val players = entry.value - if (players.isNotEmpty()) { var max = players.first().calculatePower(field) - var winingplayer: Player = players.first() + var winningPlayer: Player = players.first() var draw = false - players.filter { player -> player != winingplayer }.forEach { player -> + players.filter { player -> player != winningPlayer }.forEach { player -> val power = player.calculatePower(field) if (max < power) { max = power - winingplayer = player + winningPlayer = player draw = false } else if (max == power) { draw = true @@ -121,22 +120,18 @@ class GameState( player.leaveFields(setOf(field)) } } else if (!draw) { - winingplayer.ownedFields.add(field) - losses[winingplayer] = losses[winingplayer]!! + 0.2 + winningPlayer.ownedFields.add(field) + losses[winningPlayer] = losses[winningPlayer]!! + 0.2 } else { - losses[winingplayer] = losses[winingplayer]!! + 0.1 + losses[winningPlayer] = losses[winningPlayer]!! + 0.1 } - players.filter { player -> player != winingplayer }.forEach { player -> + players.filter { player -> player != winningPlayer }.forEach { player -> losses[player] = losses[player]!! + 0.1 } } } - } players.forEach { player -> - if (losses[player]!! > 1.0) { - losses[player] = 1.0 - } player.loseWeaponPrecent(losses[player]!!) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 20e6d89..1c6bd11 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -78,9 +78,11 @@ class Player( return false } - fun loseWeaponPrecent(precent: Double) { + fun loseWeaponPrecent(percent: Double) { + val normalizedPercent = if (percent > 1.0) 1.0 else percent weapons.keys.forEach { weapon -> - weapons[weapon] = ((weapons[weapon]!!.toDouble()) * (1 - precent)).toInt() + weapons[weapon] = ((weapons[weapon]?.toDouble() ?: 0.0) * (1 - normalizedPercent)) + .toInt() } } } From 81e9ed41b0275d55984a6f1096025977942f4edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nimr=C3=B3d?= <106318293+user93482@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:37:13 +0100 Subject: [PATCH 022/162] Modify calculatePower to include defender logic --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 1c6bd11..4b97dd9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -61,9 +61,12 @@ class Player( return sum } - fun calculatePower(field: GameStateField): Int { - var sum = getWeaponCount(Weapon.LEGION) * 2 + getWeaponCount(field.effectiveWeapon) * 2 - weapons.keys.forEach { weapon -> + fun calculatePower(field: GameStateField,isdefender: Boolean): Int { + var sum = getWeaponCount(field.effectiveWeapon) * 2 + if (!isdefender){ + sum += getWeaponCount(Weapon.LEGION) * 3 + } + weapons.filter { weapon -> weapon != Weapon.LEGION}.keys.forEach { weapon -> sum += getWeaponCount(weapon) } return sum From 2556260d46e37416bf13c3c6af3e3979dd260aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nimr=C3=B3d?= <106318293+user93482@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:40:51 +0100 Subject: [PATCH 023/162] modified expansions, to account for legion not useable in defense --- .idea/misc.xml | 3 +- .idea/modules.xml | 3 - .../it/dunestrat/model/GameState.kt | 60 ++++++++++--------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 026022e..49145cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,8 @@ + - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index f7f6104..3e42bd6 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,9 +2,6 @@ - - - diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 15fdeaf..0e3673f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -34,10 +34,12 @@ class GameState( } } - fun getPlayerStepById(playerId: String, playerSteps: Set): PlayerStep = - playerSteps.first { playerStep -> - playerStep.playerId == playerId - } + fun getPlayerStepById( + playerId: String, + playerSteps: Set, + ): PlayerStep = playerSteps.first { playerStep -> + playerStep.playerId == playerId + } fun checkPurchases(playerSteps: Set) { players.forEach { player -> @@ -101,32 +103,34 @@ class GameState( val field = entry.key val players = entry.value - var max = players.first().calculatePower(field) - var winningPlayer: Player = players.first() - var draw = false - players.filter { player -> player != winningPlayer }.forEach { player -> - val power = player.calculatePower(field) - if (max < power) { - max = power - winningPlayer = player - draw = false - } else if (max == power) { - draw = true - } - if (fieldOcupiedBy(field) != null) { - if (fieldOcupiedBy(field)!!.calculatePower(field) >= max) { + if (players.isNotEmpty()) { + var max = players.first().calculatePower(field, false) + var winningPlayer: Player = players.first() + var draw = false + players.filter { player -> player != winningPlayer }.forEach { player -> + val power = player.calculatePower(field, false) + if (max < power) { + max = power + winningPlayer = player + draw = false + } else if (max == power) { draw = true - } else if (draw == true) { - player.leaveFields(setOf(field)) } - } else if (!draw) { - winningPlayer.ownedFields.add(field) - losses[winningPlayer] = losses[winningPlayer]!! + 0.2 - } else { - losses[winningPlayer] = losses[winningPlayer]!! + 0.1 - } - players.filter { player -> player != winningPlayer }.forEach { player -> - losses[player] = losses[player]!! + 0.1 + if (fieldOcupiedBy(field) != null) { + if (fieldOcupiedBy(field)!!.calculatePower(field, true) >= max) { + draw = true + } else if (draw == true) { + player.leaveFields(setOf(field)) + } + } else if (!draw) { + winningPlayer.ownedFields.add(field) + losses[winningPlayer] = losses[winningPlayer]!! + 0.2 + } else { + losses[winningPlayer] = losses[winningPlayer]!! + 0.1 + } + players.filter { player -> player != winningPlayer }.forEach { player -> + losses[player] = losses[player]!! + 0.1 + } } } } From dc187e6c26ea8f8b1733ea80909d1c421f9f4ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nimr=C3=B3d?= <106318293+user93482@users.noreply.github.com> Date: Tue, 25 Nov 2025 07:19:24 +0100 Subject: [PATCH 024/162] Refactor calculatePower function parameters --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 4b97dd9..b654948 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -61,9 +61,9 @@ class Player( return sum } - fun calculatePower(field: GameStateField,isdefender: Boolean): Int { + fun calculatePower(field: GameStateField): Int { var sum = getWeaponCount(field.effectiveWeapon) * 2 - if (!isdefender){ + if (field not in ownedFields){ sum += getWeaponCount(Weapon.LEGION) * 3 } weapons.filter { weapon -> weapon != Weapon.LEGION}.keys.forEach { weapon -> From 9d5532a8eae2a5f2ef5c8196b9e9c4636cf7f714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nimr=C3=B3d?= <106318293+user93482@users.noreply.github.com> Date: Tue, 25 Nov 2025 07:20:03 +0100 Subject: [PATCH 025/162] Remove unused parameter from calculatePower calls --- .../it/dunestrat/model/GameState.kt | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 0e3673f..9ac1ccc 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -104,11 +104,11 @@ class GameState( val players = entry.value if (players.isNotEmpty()) { - var max = players.first().calculatePower(field, false) + var max = players.first().calculatePower(field) var winningPlayer: Player = players.first() var draw = false players.filter { player -> player != winningPlayer }.forEach { player -> - val power = player.calculatePower(field, false) + val power = player.calculatePower(field) if (max < power) { max = power winningPlayer = player @@ -117,27 +117,32 @@ class GameState( draw = true } if (fieldOcupiedBy(field) != null) { - if (fieldOcupiedBy(field)!!.calculatePower(field, true) >= max) { + if (fieldOcupiedBy(field)!!.calculatePower(field) >= max) { draw = true - } else if (draw == true) { - player.leaveFields(setOf(field)) } - } else if (!draw) { - winningPlayer.ownedFields.add(field) - losses[winningPlayer] = losses[winningPlayer]!! + 0.2 - } else { - losses[winningPlayer] = losses[winningPlayer]!! + 0.1 - } - players.filter { player -> player != winningPlayer }.forEach { player -> - losses[player] = losses[player]!! + 0.1 + if (fieldOcupiedBy(field) != null) { + if (fieldOcupiedBy(field)!!.calculatePower(field, true) >= max) { + draw = true + } else if (draw == true) { + player.leaveFields(setOf(field)) + } + } else if (!draw) { + winningPlayer.ownedFields.add(field) + losses[winningPlayer] = losses[winningPlayer]!! + 0.2 + } else { + losses[winningPlayer] = losses[winningPlayer]!! + 0.1 + } + players.filter { player -> player != winningPlayer }.forEach { player -> + losses[player] = losses[player]!! + 0.1 + } } } } - } - players.forEach { player -> - player.loseWeaponPrecent(losses[player]!!) - } + players.forEach { player -> + player.loseWeaponPrecent(losses[player]!!) + } + } } fun fieldOcupiedBy(field: GameStateField): Player? { From bb0d8b19e79d1e23cd5ddf18c47f294a5eb30f21 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 30 Nov 2025 17:10:41 +0100 Subject: [PATCH 026/162] Fix typo --- .../hu/mokegyesulet/it/dunestrat/model/GameState.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 9ac1ccc..2d26dd5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -116,12 +116,12 @@ class GameState( } else if (max == power) { draw = true } - if (fieldOcupiedBy(field) != null) { - if (fieldOcupiedBy(field)!!.calculatePower(field) >= max) { + if (fieldOccupiedBy(field) != null) { + if (fieldOccupiedBy(field)!!.calculatePower(field) >= max) { draw = true } - if (fieldOcupiedBy(field) != null) { - if (fieldOcupiedBy(field)!!.calculatePower(field, true) >= max) { + if (fieldOccupiedBy(field) != null) { + if (fieldOccupiedBy(field)!!.calculatePower(field) >= max) { draw = true } else if (draw == true) { player.leaveFields(setOf(field)) @@ -145,7 +145,7 @@ class GameState( } } - fun fieldOcupiedBy(field: GameStateField): Player? { + fun fieldOccupiedBy(field: GameStateField): Player? { players.forEach { player -> if (field in player.ownedFields) { return player From 4c3548452c453175797729ca7445f33f785734b5 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 30 Nov 2025 17:47:08 +0100 Subject: [PATCH 027/162] Fixed typos --- .../hu/mokegyesulet/it/dunestrat/model/Player.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index b654948..4d6c840 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -63,18 +63,18 @@ class Player( fun calculatePower(field: GameStateField): Int { var sum = getWeaponCount(field.effectiveWeapon) * 2 - if (field not in ownedFields){ + if (field !in ownedFields) { sum += getWeaponCount(Weapon.LEGION) * 3 } - weapons.filter { weapon -> weapon != Weapon.LEGION}.keys.forEach { weapon -> + weapons.filter { weapon -> weapon.key != Weapon.LEGION }.keys.forEach { weapon -> sum += getWeaponCount(weapon) } return sum } - fun isFieldReachable(fieldinqestion: GameStateField): Boolean { - ownedFields.filter { field -> field != fieldinqestion }.forEach { field -> - if (fieldinqestion in field.neighbours) { + fun isFieldReachable(fieldInQuestion: GameStateField): Boolean { + ownedFields.filter { field -> field != fieldInQuestion }.forEach { field -> + if (fieldInQuestion in field.neighbours) { return true } } From 202175d3a520ab23e3ce68e80c139ac1c7036b40 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 30 Nov 2025 18:49:51 +0100 Subject: [PATCH 028/162] Fixed things requested by comments --- .../it/dunestrat/model/GameState.kt | 11 +++----- .../mokegyesulet/it/dunestrat/model/Player.kt | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 2d26dd5..ab94372 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -6,8 +6,6 @@ class GameState( ) { fun runTurn(playerSteps: MutableSet) { - val playerSteps = playerSteps.toMutableSet() - this.leaveFields(playerSteps) this.waterConsumption(playerSteps) this.checkPurchases(playerSteps) @@ -19,15 +17,14 @@ class GameState( } fun leaveFields(playerSteps: Set) { - players.map { player -> + players.forEach { player -> player.leaveFields(getPlayerStepById(player.id, playerSteps).leaveFields) } } fun waterConsumption(playerSteps: MutableSet) { players.forEach { player -> - val res = player.waterConsumption() - if (res) { + if (player.waterConsumption()) { playerSteps.removeAll { it.playerId == player.id } playerSteps.add(PlayerStep(player.id, setOf(), setOf(), mapOf(), setOf())) } @@ -37,9 +34,9 @@ class GameState( fun getPlayerStepById( playerId: String, playerSteps: Set, - ): PlayerStep = playerSteps.first { playerStep -> + ): PlayerStep = playerSteps.find { playerStep -> playerStep.playerId == playerId - } + } ?: PlayerStep(playerId, setOf(), setOf(), mapOf(), setOf()) fun checkPurchases(playerSteps: Set) { players.forEach { player -> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 4d6c840..1e82be8 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -33,12 +33,7 @@ class Player( } fun purchaseWeapons(purchaseWeapons: Map) { - var purchasePrice = 0 - purchaseWeapons.keys.forEach { weapon -> - purchasePrice += weapon.price - } - - spice -= purchasePrice + spice -= calculateWeaponPrices(purchaseWeapons) } fun deliverWeapons(purchaseWeapons: Map) { @@ -52,15 +47,20 @@ class Player( spice -= 5 * 2.toDouble().pow(harvestersPurchased++).toInt() } - fun calculatePrices(purchases: PlayerStep): Int { + fun calculateWeaponPrices(purchaseWeapons: Map): Int { var sum = 0 - purchases.purchaseWeapons.keys.forEach { weapon -> - sum += weapon.price + purchaseWeapons.forEach { entry -> + sum += entry.value * entry.key.price } - sum += 5 * 2.toDouble().pow(harvestersPurchased).toInt() return sum } + fun calculateHarvesterPrice(count: Int): Int = + (5 * 2.toDouble().pow(harvestersPurchased) * (2.toDouble().pow(count) - 1)).toInt() + fun calculatePrices(purchases: PlayerStep): Int = + calculateWeaponPrices(purchases.purchaseWeapons) + + calculateHarvesterPrice(purchases.buildHarvesters.size) + fun calculatePower(field: GameStateField): Int { var sum = getWeaponCount(field.effectiveWeapon) * 2 if (field !in ownedFields) { @@ -73,7 +73,10 @@ class Player( } fun isFieldReachable(fieldInQuestion: GameStateField): Boolean { - ownedFields.filter { field -> field != fieldInQuestion }.forEach { field -> + if (fieldInQuestion in ownedFields) { + return false + } + ownedFields.forEach { field -> if (fieldInQuestion in field.neighbours) { return true } From ff78cc57b7d37ffdc1b8de1711b93a567851476b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Tue, 13 Jan 2026 19:57:28 +0100 Subject: [PATCH 029/162] refactor expansion calculation --- .../it/dunestrat/model/GameState.kt | 87 ++++++++++--------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index ab94372..2d0d248 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -77,7 +77,7 @@ class GameState( } fun expansions(playerSteps: Set) { - val chosenfields = mutableMapOf>() + val chosenFields = mutableMapOf>() val losses = mutableMapOf() players.forEach { player -> @@ -85,64 +85,38 @@ class GameState( } fields.forEach { field -> - chosenfields[field] = mutableSetOf() + chosenFields[field] = mutableSetOf() } players.filter { !it.inDebt } .forEach { player -> getPlayerStepById(player.id, playerSteps).enterFields .filter { field -> player.isFieldReachable(field) } - .forEach { field -> chosenfields[field]?.add(player) } + .forEach { field -> chosenFields[field]?.add(player) } } - chosenfields.filter { it.value.isNotEmpty() } + chosenFields.filter { it.value.isNotEmpty() } .forEach { entry -> val field = entry.key val players = entry.value if (players.isNotEmpty()) { - var max = players.first().calculatePower(field) - var winningPlayer: Player = players.first() - var draw = false - players.filter { player -> player != winningPlayer }.forEach { player -> - val power = player.calculatePower(field) - if (max < power) { - max = power - winningPlayer = player - draw = false - } else if (max == power) { - draw = true - } - if (fieldOccupiedBy(field) != null) { - if (fieldOccupiedBy(field)!!.calculatePower(field) >= max) { - draw = true - } - if (fieldOccupiedBy(field) != null) { - if (fieldOccupiedBy(field)!!.calculatePower(field) >= max) { - draw = true - } else if (draw == true) { - player.leaveFields(setOf(field)) - } - } else if (!draw) { - winningPlayer.ownedFields.add(field) - losses[winningPlayer] = losses[winningPlayer]!! + 0.2 - } else { - losses[winningPlayer] = losses[winningPlayer]!! + 0.1 - } - players.filter { player -> player != winningPlayer }.forEach { player -> - losses[player] = losses[player]!! + 0.1 - } - } + val occupant = getOccupant(field) + val max = players.maxOfOrNull { it.calculatePower(field) } ?: 0 + val winningPlayers: List = players.filter { player -> + player.calculatePower(field) == max + } + val newOccupant = modifyFieldOwnershipsAfterBattle(field, winningPlayers) + players.forEach { player -> + losses[player] = + (losses[player] ?: 0.0) + + calculateLossOnField(player, occupant, newOccupant) } - } - - players.forEach { player -> - player.loseWeaponPrecent(losses[player]!!) } } } - fun fieldOccupiedBy(field: GameStateField): Player? { + fun getOccupant(field: GameStateField): Player? { players.forEach { player -> if (field in player.ownedFields) { return player @@ -150,4 +124,35 @@ class GameState( } return null } + + private fun modifyFieldOwnershipsAfterBattle( + field: GameStateField, + winningPlayers: List, + ): Player? { + val occupant = getOccupant(field) + if (occupant in winningPlayers) { + return occupant + } + occupant?.ownedFields?.remove(field) + if (winningPlayers.size >= 2) { + return null + } + val newOccupant = winningPlayers[0] + newOccupant.ownedFields.add(field) + return newOccupant + } + + private fun calculateLossOnField( + player: Player, + previousOccupant: Player?, + newOccupant: Player?, + ): Double { + if (previousOccupant == player) { + return 0.0 + } + if (newOccupant == player) { + return 0.2 + } + return 0.1 + } } From 0e500cb3a29a053ec9dddb90329d666a8ee12e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sat, 17 Jan 2026 14:53:54 +0100 Subject: [PATCH 030/162] Implement Supabase DB and auth, add team data --- .gitignore | 4 + .idea/artifacts/composeApp_js.xml | 2 +- .idea/artifacts/composeApp_jvm.xml | 2 +- .idea/artifacts/composeApp_wasm_js.xml | 2 +- .idea/compiler.xml | 6 + .idea/modules.xml | 3 + build.gradle.kts | 1 + composeApp/.idea/workspace.xml | 60 + composeApp/build.gradle.kts | 22 + .../mokegyesulet/it/dunestrat/backend/Auth.kt | 6 + .../it/dunestrat/backend/MemoryRepository.kt | 32 + .../it/dunestrat/backend/Repository.kt | 17 + .../dunestrat/backend/SubabaseRepository.kt | 51 + .../it/dunestrat/backend/SupabaseAuth.kt | 15 + .../backend/SupabaseClientProvider.kt | 17 + .../backend/entities/GameDatabaseEntity.kt | 26 + .../entities/GameStateDatabaseEntity.kt | 26 + .../entities/PlayerStepDatabaseEntity.kt | 49 + .../it/dunestrat/feature/MainMenu.kt | 34 - .../it/dunestrat/feature/mainmenu/MainMenu.kt | 59 + .../feature/mainmenu/MainMenuViewModel.kt | 28 + .../mokegyesulet/it/dunestrat/model/Game.kt | 21 + .../it/dunestrat/model/GameState.kt | 31 +- .../it/dunestrat/model/GameStateField.kt | 3 + .../mokegyesulet/it/dunestrat/model/Player.kt | 4 +- .../it/dunestrat/model/PlayerStep.kt | 10 +- .../it/dunestrat/model/Student.kt | 9 + .../mokegyesulet/it/dunestrat/model/Team.kt | 9 + .../it/dunestrat/navigation/NavGraph.kt | 9 +- .../it/dunestrat/navigation/Screen.kt | 1 + flake.lock | 64 + flake.nix | 22 + gradle/libs.versions.toml | 15 +- kotlin-js-store/wasm/yarn.lock | 5 + kotlin-js-store/yarn.lock | 3050 +++++++++++++++++ 35 files changed, 3670 insertions(+), 45 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 composeApp/.idea/workspace.xml create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 kotlin-js-store/yarn.lock diff --git a/.gitignore b/.gitignore index e8b5977..7f7e692 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ node_modules/ .idea/usage.statistics.xml .idea/shelf/ .idea/*.iml + +# Nix +.direnv +.envrc diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml index 00ef8a3..e52ad0e 100644 --- a/.idea/artifacts/composeApp_js.xml +++ b/.idea/artifacts/composeApp_js.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml index 01d03af..96d842a 100644 --- a/.idea/artifacts/composeApp_jvm.xml +++ b/.idea/artifacts/composeApp_jvm.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml index 0bd6a39..e3c9c59 100644 --- a/.idea/artifacts/composeApp_wasm_js.xml +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 3e42bd6..f7f6104 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,6 +2,9 @@ + + + diff --git a/build.gradle.kts b/build.gradle.kts index d3c5ec8..72a57bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,4 +11,5 @@ plugins { alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.googleServices) apply false alias(libs.plugins.ktlint) apply false + kotlin("plugin.serialization") version "2.3.0" apply false } diff --git a/composeApp/.idea/workspace.xml b/composeApp/.idea/workspace.xml new file mode 100644 index 0000000..008f2f9 --- /dev/null +++ b/composeApp/.idea/workspace.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1764949855968 + + + + \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index d5b7a78..b658c82 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -10,6 +10,7 @@ plugins { alias(libs.plugins.composeHotReload) alias(libs.plugins.googleServices) alias(libs.plugins.ktlint) + kotlin("plugin.serialization") version "2.3.0" } kotlin { @@ -46,6 +47,7 @@ kotlin { androidMain.dependencies { implementation(compose.preview) implementation(libs.androidx.activity.compose) + implementation(libs.ktor.client.cio) } commonMain.dependencies { implementation(compose.runtime) @@ -58,6 +60,15 @@ kotlin { implementation(libs.androidx.lifecycle.runtimeCompose) implementation(libs.navigation.compose) implementation(libs.material.icons.extended) + implementation( + project.dependencies.platform(libs.supabase.bom), + ) + implementation(libs.supabase.auth) + implementation(libs.supabase.auth.compose) + implementation(libs.supabase.storage) + implementation(libs.supabase.realtime) + implementation(libs.supabase.postgrest) + implementation(libs.kotlinx.serialization) } commonTest.dependencies { implementation(libs.kotlin.test) @@ -65,6 +76,17 @@ kotlin { jvmMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutinesSwing) + implementation(libs.ktor.client.cio) + } + jsMain { + dependencies { + implementation(libs.ktor.client.js) + } + } + iosMain { + dependencies { + implementation(libs.ktor.client.darwin) + } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt new file mode 100644 index 0000000..b54860a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt @@ -0,0 +1,6 @@ +package hu.mokegyesulet.it.dunestrat.backend + +interface Auth { + suspend fun logIn() + suspend fun logOut() +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt new file mode 100644 index 0000000..7458662 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt @@ -0,0 +1,32 @@ +package hu.mokegyesulet.it.dunestrat.backend + +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +object MemoryRepository : Repository { + private val game = MutableStateFlow>(emptyList()) + private val gameState = MutableStateFlow>(emptyList()) + private val playerStep = MutableStateFlow>(emptyList()) + + override fun getGames(): Flow> = game.asStateFlow() + + override suspend fun saveGame(game: Game) { + this.game.value += game + } + + override fun getGameStates(): Flow> = gameState.asStateFlow() + + override suspend fun saveGameState(gameState: GameState) { + this.gameState.value += gameState + } + + override fun getPlayerSteps(): Flow> = playerStep.asStateFlow() + + override suspend fun savePlayerStep(playerStep: PlayerStep) { + this.playerStep.value += playerStep + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt new file mode 100644 index 0000000..d4c1592 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -0,0 +1,17 @@ +package hu.mokegyesulet.it.dunestrat.backend + +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import kotlinx.coroutines.flow.Flow + +interface Repository { + suspend fun saveGame(game: Game) + fun getGames(): Flow> + + suspend fun saveGameState(gameState: GameState) + fun getGameStates(): Flow> + + suspend fun savePlayerStep(playerStep: PlayerStep) + fun getPlayerSteps(): Flow> +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt new file mode 100644 index 0000000..58db755 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt @@ -0,0 +1,51 @@ +package hu.mokegyesulet.it.dunestrat.backend + +import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntry +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.postgrest.from +import io.github.jan.supabase.realtime.selectAsFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +object SubabaseRepository : Repository { + val supabase = SupabaseClientProvider.client + const val GAME_TABLE = GameDatabaseEntry.TABLE_NAME + const val GAME_STATE_TABLE = GameDatabaseEntry.TABLE_NAME + const val STEP_TABLE = PlayerStepDatabaseEntry.TABLE_NAME + + @OptIn(SupabaseExperimental::class) + override fun getGames(): Flow> = + supabase.from(GAME_TABLE).selectAsFlow(GameDatabaseEntry::id).map { list -> + list.map { it.toGame() } + } + + override suspend fun saveGame(game: Game) { + supabase.from(GAME_TABLE).insert(game.toDatabaseEntry()) + } + + @OptIn(SupabaseExperimental::class) + override fun getGameStates(): Flow> = + supabase.from(GAME_STATE_TABLE).selectAsFlow(GameStateDatabaseEntry::id).map { list -> + list.map { it.toGameState() } + } + + override suspend fun saveGameState(gameState: GameState) { + supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) + } + + @OptIn(SupabaseExperimental::class) + override fun getPlayerSteps(): Flow> = + supabase.from(STEP_TABLE).selectAsFlow(PlayerStepDatabaseEntry::id).map { list -> + list.map { it.toPlayerStep() } + } + + override suspend fun savePlayerStep(playerStep: PlayerStep) { + supabase.from(STEP_TABLE).insert(playerStep.toDatabaseEntry()) + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt new file mode 100644 index 0000000..b1926c6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt @@ -0,0 +1,15 @@ +package hu.mokegyesulet.it.dunestrat.backend + +import io.github.jan.supabase.auth.auth +import io.github.jan.supabase.auth.providers.Google +import io.github.jan.supabase.auth.status.SessionStatus +import kotlinx.coroutines.flow.Flow + +object SupabaseAuth : Auth { + private val auth = SupabaseClientProvider.client.auth + + override suspend fun logIn() = auth.signInWith(Google) + val sessionStatus get(): Flow = auth.sessionStatus + + override suspend fun logOut() = auth.signOut() +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt new file mode 100644 index 0000000..5c233af --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt @@ -0,0 +1,17 @@ +package hu.mokegyesulet.it.dunestrat.backend + +import io.github.jan.supabase.createSupabaseClient +import io.github.jan.supabase.postgrest.Postgrest +import io.github.jan.supabase.realtime.Realtime + +object SupabaseClientProvider { + private val _client = createSupabaseClient( + supabaseUrl = "https://lnwvuwepwaexwybselsf.supabase.co", + supabaseKey = "sb_publishable_Y8MlS_tbyIRvvsvbHvb1_g_wbyg6EfI", + ) { + install(io.github.jan.supabase.auth.Auth) + install(Postgrest) + install(Realtime) + } + val client get() = _client +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt new file mode 100644 index 0000000..1d24f31 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt @@ -0,0 +1,26 @@ +package hu.mokegyesulet.it.dunestrat.backend.entities + +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameProgress +import kotlinx.serialization.json.Json + +class GameDatabaseEntry( + val id: Int?, + val progress: Int, + val json: String, +) { + fun toGame(): Game { + val game = Json.decodeFromString(json) + return game.copy(id = id, progress = GameProgress.entries[progress]) + } + + companion object { + const val TABLE_NAME = "game" + } +} + +fun Game.toDatabaseEntry(): GameDatabaseEntry = GameDatabaseEntry( + id = id, + this.progress.ordinal, + json = Json.encodeToString(this), +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt new file mode 100644 index 0000000..eb2034d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -0,0 +1,26 @@ +package hu.mokegyesulet.it.dunestrat.backend.entities + +import hu.mokegyesulet.it.dunestrat.model.GameState +import kotlinx.serialization.json.Json + +class GameStateDatabaseEntry( + val id: Int?, + val gameId: Int, + val index: Int, + val json: String, +) { + fun toGameState(): GameState { + val gameState = Json.decodeFromString(json) + return gameState.copy(id = id, gameId = gameId, index = index) + } + companion object { + const val TABLE_NAME = "game_state" + } +} + +fun GameState.toDatabaseEntry(): GameStateDatabaseEntry = GameStateDatabaseEntry( + id = id, + gameId = gameId, + index = index, + json = Json.encodeToString(this), +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt new file mode 100644 index 0000000..57eda9a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -0,0 +1,49 @@ +package hu.mokegyesulet.it.dunestrat.backend.entities + +import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import kotlinx.serialization.json.Json + +class PlayerStepDatabaseEntry( + val id: Int?, + val gameStateId: Int, + val json: String, +) { + + fun toPlayerStep(): PlayerStep { + val playerStep = Json.decodeFromString(json) + return playerStep.copy(id = id, gameStateId = gameStateId) + +// val data = Json.parseToJsonElement(json).jsonObject +// PlayerStep( +// id, +// gameStateId, +// data["playerId"]!!.toString().removeSurrounding("\""), +// data["leaveFields"]!!.jsonArray.map { +// Json.decodeFromString(it.toString()) +// }.toSet(), +// data ["enterFields"]!!.jsonArray.map { +// Json.decodeFromString(it.toString()) +// }.toSet(), +// data["purchaseWeapons"]!!.jsonObject.map { +// val weapon = Json.decodeFromString( +// it.key, +// ) +// val amount = it.value.toString().toInt() +// weapon to amount +// }.toMap(), +// data["buildHarvesters"]!!.jsonArray.map { +// Json.decodeFromString(it.toString()) +// }.toSet(), +// ) + } + + companion object { + const val TABLE_NAME = "player_step" + } +} + +fun PlayerStep.toDatabaseEntry(): PlayerStepDatabaseEntry = PlayerStepDatabaseEntry( + id = id, + gameStateId = gameStateId, + json = Json.encodeToString(this), +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt deleted file mode 100644 index 5aba143..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt +++ /dev/null @@ -1,34 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.feature - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier - -@Composable -fun MainMenu( - onPlaceholder1: () -> Unit, - onPlaceholder2: () -> Unit, -) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceEvenly, - ) { - Button( - onClick = onPlaceholder1, - ) { - Text(text = "Placeholder 1") - } - - Button( - onClick = onPlaceholder2, - ) { - Text(text = "Placeholder 2") - } - } -} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt new file mode 100644 index 0000000..51f3a2f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -0,0 +1,59 @@ +package hu.mokegyesulet.it.dunestrat.feature.mainmenu + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel + +@Composable +fun MainMenu( + onPlaceholder1: () -> Unit, + onPlaceholder2: () -> Unit, +) { + val viewModel: MainMenuViewModel = viewModel { MainMenuViewModel() } + val loggedIn by viewModel.isLoggedIn.collectAsState(initial = false) + if (loggedIn) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceEvenly, + ) { + Button( + onClick = onPlaceholder1, + ) { + Text(text = "Placeholder 1") + } + + Button( + onClick = onPlaceholder2, + ) { + Text(text = "Placeholder 2") + } + + Button( + onClick = viewModel::onLogout, + ) { + Text(text = "Log out") + } + } + } else { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Button( + onClick = viewModel::onLogin, + ) { + Text(text = "Log in") + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt new file mode 100644 index 0000000..c7111be --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt @@ -0,0 +1,28 @@ +package hu.mokegyesulet.it.dunestrat.feature.mainmenu + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import hu.mokegyesulet.it.dunestrat.backend.SupabaseAuth +import io.github.jan.supabase.auth.status.SessionStatus +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +class MainMenuViewModel : ViewModel() { + fun onLogin() { + viewModelScope.launch { + SupabaseAuth.logIn() + } + } + + fun onLogout() { + viewModelScope.launch { + SupabaseAuth.logOut() + } + } + + val isLoggedIn: Flow + get() = SupabaseAuth.sessionStatus.map { + it is SessionStatus.Authenticated + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt new file mode 100644 index 0000000..65fbf07 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt @@ -0,0 +1,21 @@ +package hu.mokegyesulet.it.dunestrat.model + +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class Game( + @Transient + val id: Int? = null, + val name: String, + @Transient + val progress: GameProgress = GameProgress.ERROR, + val teams: List, +) + +enum class GameProgress { + INITIALIZED, + ONGOING, + FINISHED, + ERROR, +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 2d0d248..24422af 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -1,6 +1,17 @@ package hu.mokegyesulet.it.dunestrat.model -class GameState( +import kotlin.collections.setOf +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class GameState( + @Transient + val id: Int? = null, + @Transient + val gameId: Int = -1, + @Transient + val index: Int = -1, val fields: Set, val players: Set, ) { @@ -26,7 +37,15 @@ class GameState( players.forEach { player -> if (player.waterConsumption()) { playerSteps.removeAll { it.playerId == player.id } - playerSteps.add(PlayerStep(player.id, setOf(), setOf(), mapOf(), setOf())) + playerSteps.add( + PlayerStep( + playerId = player.id, + leaveFields = setOf(), + enterFields = setOf(), + purchaseWeapons = mapOf(), + buildHarvesters = setOf(), + ), + ) } } } @@ -36,7 +55,13 @@ class GameState( playerSteps: Set, ): PlayerStep = playerSteps.find { playerStep -> playerStep.playerId == playerId - } ?: PlayerStep(playerId, setOf(), setOf(), mapOf(), setOf()) + } ?: PlayerStep( + playerId = playerId, + leaveFields = setOf(), + enterFields = setOf(), + purchaseWeapons = mapOf(), + buildHarvesters = setOf(), + ) fun checkPurchases(playerSteps: Set) { players.forEach { player -> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index b22f238..ad71f80 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -1,5 +1,8 @@ package hu.mokegyesulet.it.dunestrat.model +import kotlinx.serialization.Serializable + +@Serializable data class GameStateField( val id: String, val water: Int, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 1e82be8..5b39c0c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -2,8 +2,10 @@ package hu.mokegyesulet.it.dunestrat.model import kotlin.collections.forEach import kotlin.math.pow +import kotlinx.serialization.Serializable -class Player( +@Serializable +data class Player( val id: String, var water: Int, var spice: Int, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index f9c8d35..d6dfebf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -1,6 +1,14 @@ package hu.mokegyesulet.it.dunestrat.model -class PlayerStep( +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class PlayerStep( + @Transient + val id: Int? = null, + @Transient + val gameStateId: Int = -1, val playerId: String, var leaveFields: Set, var enterFields: Set, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt new file mode 100644 index 0000000..6df28b7 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt @@ -0,0 +1,9 @@ +package hu.mokegyesulet.it.dunestrat.model + +import kotlinx.serialization.Serializable + +@Serializable +class Student( + val name: String = "Ezekiel", + val batkabankId: String = "", +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt new file mode 100644 index 0000000..16b9269 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt @@ -0,0 +1,9 @@ +package hu.mokegyesulet.it.dunestrat.model + +import kotlinx.serialization.Serializable + +@Serializable +class Team( + val playerId: String, + val students: List, +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 060c156..e13131a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -5,7 +5,8 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import hu.mokegyesulet.it.dunestrat.feature.MainMenu +import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu +import hu.mokegyesulet.it.dunestrat.feature.supabase.SupabaseScreen @Composable fun NavGraph(navController: NavHostController = rememberNavController()) { @@ -15,9 +16,13 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable(Screen.MainMenu.route) { MainMenu( - onPlaceholder1 = {}, + onPlaceholder1 = { navController.navigate(Screen.Supabase.route) }, onPlaceholder2 = {}, ) } + + composable(Screen.Supabase.route) { + SupabaseScreen() + } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 7488c3a..1e449f6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -4,4 +4,5 @@ sealed class Screen( val route: String, ) { data object MainMenu : Screen(route = "main-menu") + data object Supabase : Screen(route = "supabase") } diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..35afeec --- /dev/null +++ b/flake.lock @@ -0,0 +1,64 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1768178648, + "narHash": "sha256-kz/F6mhESPvU1diB7tOM3nLcBfQe7GU7GQCymRlTi/s=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3fbab70c6e69c87ea2b6e48aa6629da2aa6a23b0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..837e24a --- /dev/null +++ b/flake.nix @@ -0,0 +1,22 @@ +{ + description = "A basic flake with a shell"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + inputs.systems.url = "github:nix-systems/default"; + inputs.flake-utils = { + url = "github:numtide/flake-utils"; + inputs.systems.follows = "systems"; + }; + + outputs = { + nixpkgs, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + devShells.default = pkgs.mkShell {packages = [pkgs.jdk21];}; + } + ); +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 852d96c..ff2dcc7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,8 +17,10 @@ kotlinx-coroutines = "1.10.2" materialIconsExtended = "1.7.3" navigationCompose = "2.9.1" google-services = "4.4.4" -firebase = "34.5.0" ktlint-plugin = "14.0.1" +kotlinx-serialization = "1.10.0-RC" +supabase = "3.3.0" +ktor = "3.2.2" [libraries] material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } @@ -34,6 +36,17 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } +supabase-auth = { module = "io.github.jan-tennert.supabase:auth-kt"} +supabase-auth-compose = { module = "io.github.jan-tennert.supabase:compose-auth"} +supabase-postgrest = { module = "io.github.jan-tennert.supabase:postgrest-kt"} +supabase-storage = { module = "io.github.jan-tennert.supabase:storage-kt"} +supabase-realtime = { module = "io.github.jan-tennert.supabase:realtime-kt"} +supabase-bom = { module = "io.github.jan-tennert.supabase:bom", version.ref = "supabase" } +kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json",version.ref="kotlinx-serialization" } +ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } + [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/kotlin-js-store/wasm/yarn.lock b/kotlin-js-store/wasm/yarn.lock index 5f4567d..d3b9b64 100644 --- a/kotlin-js-store/wasm/yarn.lock +++ b/kotlin-js-store/wasm/yarn.lock @@ -6,3 +6,8 @@ version "3.2.0" resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + +ws@8.18.3: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 0000000..c51ad34 --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,3050 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.6.1": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" + integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + +"@jsonjoy.com/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/buffers@^1.0.0", "@jsonjoy.com/buffers@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz#8d99c7f67eaf724d3428dfd9826c6455266a5c83" + integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== + +"@jsonjoy.com/codegen@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz#5c23f796c47675f166d23b948cdb889184b93207" + integrity sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g== + +"@jsonjoy.com/json-pack@^1.11.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz#93f8dd57fe3a3a92132b33d1eb182dcd9e7629fa" + integrity sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg== + dependencies: + "@jsonjoy.com/base64" "^1.1.2" + "@jsonjoy.com/buffers" "^1.2.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/json-pointer" "^1.0.2" + "@jsonjoy.com/util" "^1.9.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pointer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz#049cb530ac24e84cba08590c5e36b431c4843408" + integrity sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg== + dependencies: + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/util" "^1.9.0" + +"@jsonjoy.com/util@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.9.0.tgz#7ee95586aed0a766b746cd8d8363e336c3c47c46" + integrity sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ== + dependencies: + "@jsonjoy.com/buffers" "^1.0.0" + "@jsonjoy.com/codegen" "^1.0.0" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@types/body-parser@*": + version "1.19.6" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.12": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz#74f47555b3d804b54cb7030e6f9aa0c7485cfc5b" + integrity sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": + version "4.19.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz#f1d306dcc03b1aafbfb6b4fe684cce8a31cffc10" + integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.5.tgz#3ba069177caa34ab96585ca23b3984d752300cdc" + integrity sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "^1" + +"@types/express@^4.17.21": + version "4.17.25" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.25.tgz#070c8c73a6fee6936d65c195dbbfb7da5026649b" + integrity sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "^1" + +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + +"@types/http-proxy@^1.17.8": + version "1.17.17" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.17.tgz#d9e2c4571fe3507343cb210cd41790375e59a533" + integrity sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.14" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.14.tgz#006c2616ccd65550560c2757d8472eb6d3ecea0b" + integrity sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@>=10.0.0": + version "24.10.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.0.tgz#6b79086b0dfc54e775a34ba8114dcc4e0221f31f" + integrity sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A== + dependencies: + undici-types "~7.16.0" + +"@types/qs@*": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== + +"@types/send@*": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-1.2.1.tgz#6a784e45543c18c774c049bff6d3dbaf045c9c74" + integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== + dependencies: + "@types/node" "*" + +"@types/send@<1": + version "0.17.6" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.6.tgz#aeb5385be62ff58a52cd5459daa509ae91651d25" + integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@^1", "@types/serve-static@^1.15.5": + version "1.15.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.10.tgz#768169145a778f8f5dfcb6360aead414a3994fee" + integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "<1" + +"@types/sockjs@^0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.10": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-3.0.1.tgz#76ac285b9658fa642ce238c276264589aa2b6b57" + integrity sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA== + +"@webpack-cli/info@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-3.0.1.tgz#3cff37fabb7d4ecaab6a8a4757d3826cf5888c63" + integrity sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ== + +"@webpack-cli/serve@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-3.0.1.tgz#bd8b1f824d57e30faa19eb78e4c0951056f72f00" + integrity sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== + +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +baseline-browser-mapping@^2.8.19: + version "2.8.25" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz#947dc6f81778e0fa0424a2ab9ea09a3033e71109" + integrity sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@1.20.3, body-parser@^1.19.0: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.24.0: + version "4.27.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.27.0.tgz#755654744feae978fbb123718b2f139bc0fa6697" + integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw== + dependencies: + baseline-browser-mapping "^2.8.19" + caniuse-lite "^1.0.30001751" + electron-to-chromium "^1.5.238" + node-releases "^2.0.26" + update-browserslist-db "^1.1.4" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2, call-bound@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001751: + version "1.0.30001754" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz#7758299d9a72cce4e6b038788a15b12b44002759" + integrity sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^3.5.1, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +compressible@~2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.8.1" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" + integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.1.0" + safe-buffer "5.2.1" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + +cookie@~0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.0, debug@^4.3.4, debug@^4.3.5: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" + integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.238: + version "1.5.248" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.248.tgz#e69941494efbc69ebf2803517f4e85d016013d4b" + integrity sha512-zsur2yunphlyAO4gIubdJEXCK6KOVvtpiuDfCIqbM9FjcnMYiyn0ICa3hWfPr0nc41zcLWobgy1iL7VvoOyA2Q== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +engine.io-parser@~5.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +engine.io@~6.6.0: + version "6.6.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.4.tgz#0a89a3e6b6c1d4b0c2a2a637495e7c149ec8d8ee" + integrity sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g== + dependencies: + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.7.2" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + +enhanced-resolve@^5.17.3: + version "5.18.4" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828" + integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.2.tgz#22a5ed2fd7ce0cbcff1d1474cf4909a44bdb6e85" + integrity sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + punycode "^1.4.1" + safe-regex-test "^1.1.0" + +envinfo@^7.14.0: + version "7.20.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.20.0.tgz#3fd9de69fb6af3e777a017dfa033676368d67dd7" + integrity sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +express@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.12" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +follow-redirects@^1.0.0: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regex.js@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz#2b323728271d133830850e32311f40766c5f6413" + integrity sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ== + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^10.4.5: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.10.tgz#b3277bd6d7ed5588e20ea73bf724fcbe44609075" + integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== + +http-proxy-middleware@^2.0.9: + version "2.0.9" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" + integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-network-error@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.3.0.tgz#2ce62cbca444abd506f8a900f39d20b898d37512" + integrity sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== + dependencies: + glob "^7.1.3" + minimatch "^9.0.3" + webpack-merge "^4.1.5" + +"karma@github:Kotlin/karma#6.4.5": + version "6.4.4" + resolved "https://codeload.github.com/Kotlin/karma/tar.gz/239a8fc984584f0d96b1dd750e7a5e2c79da93a6" + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.7.2" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kotlin-web-helpers@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-3.0.0.tgz#3ed6b48f694f74bb60a737a9d7e2c0e3b29abdb9" + integrity sha512-kdQO4AJQkUPvpLh9aglkXDRyN+CfXO7pKq+GESEnxooBFkQpytLrqZis3ABvmFN1cGw/ZQ/K38u5sRGW+NfBnw== + dependencies: + format-util "^1.0.5" + +launch-editor@^2.6.1: + version "2.12.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.12.0.tgz#cc740f4e0263a6b62ead2485f9896e545321f817" + integrity sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg== + dependencies: + picocolors "^1.1.1" + shell-quote "^1.8.3" + +loader-runner@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" + integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^4.43.1: + version "4.50.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.50.0.tgz#1832177d5592ec1e6a816fb4fe01012ada2856e7" + integrity sha512-N0LUYQMUA1yS5tJKmMtU9yprPm6ZIg24yr/OVv/7t6q0kKDIho4cBbXRi1XKttUmNYDYgF/q45qrKE/UhGO0CA== + dependencies: + "@jsonjoy.com/json-pack" "^1.11.0" + "@jsonjoy.com/util" "^1.9.0" + glob-to-regex.js "^1.0.1" + thingies "^2.5.0" + tree-dump "^1.0.3" + tslib "^2.0.0" + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== + dependencies: + mime-db "^1.54.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@11.7.2: + version "11.7.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.2.tgz#3c0079fe5cc2f8ea86d99124debcc42bb1ab22b5" + integrity sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ== + dependencies: + browser-stdout "^1.3.1" + chokidar "^4.0.1" + debug "^4.3.5" + diff "^7.0.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^10.4.5" + he "^1.2.0" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^9.0.5" + ms "^2.1.3" + picocolors "^1.1.1" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^9.2.0" + yargs "^17.7.2" + yargs-parser "^21.1.1" + yargs-unparser "^2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.3, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.26: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1, on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^10.0.3: + version "10.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" + integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + wsl-utils "^0.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" + integrity sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ== + dependencies: + "@types/retry" "0.12.2" + is-network-error "^1.0.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-applescript@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.1.0.tgz#2e9e54c4664ec3106c5b5630e249d3d6595c4911" + integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +socket.io-adapter@~2.5.2: + version "2.5.5" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082" + integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg== + dependencies: + debug "~4.3.4" + ws "~8.17.1" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.7.2: + version "4.8.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.1.tgz#fa0eaff965cc97fdf4245e8d4794618459f7558a" + integrity sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.3.2" + engine.io "~6.6.0" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-map-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== + dependencies: + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0, supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== + +terser-webpack-plugin@^5.3.11: + version "5.3.14" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" + integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + serialize-javascript "^6.0.2" + terser "^5.31.1" + +terser@^5.31.1: + version "5.44.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.1.tgz#e391e92175c299b8c284ad6ded609e37303b0a9c" + integrity sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +thingies@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-2.5.0.tgz#5f7b882c933b85989f8466b528a6247a6881e04f" + integrity sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tree-dump@^1.0.3, tree-dump@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.1.0.tgz#ab29129169dc46004414f5a9d4a3c6e89f13e8a4" + integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA== + +tslib@^2.0.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +ua-parser-js@^0.7.30: + version "0.7.41" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.41.tgz#9f6dee58c389e8afababa62a4a2dc22edb69a452" + integrity sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg== + +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.1: + version "2.4.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.4.tgz#473bda72f0850453da6425081ea46fc0d7602947" + integrity sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack-cli@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207" + integrity sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw== + dependencies: + "@discoveryjs/json-ext" "^0.6.1" + "@webpack-cli/configtest" "^3.0.1" + "@webpack-cli/info" "^3.0.1" + "@webpack-cli/serve" "^3.0.1" + colorette "^2.0.14" + commander "^12.1.0" + cross-spawn "^7.0.3" + envinfo "^7.14.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^6.0.1" + +webpack-dev-middleware@^7.4.2: + version "7.4.5" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz#d4e8720aa29cb03bc158084a94edb4594e3b7ac0" + integrity sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA== + dependencies: + colorette "^2.0.10" + memfs "^4.43.1" + mime-types "^3.0.1" + on-finished "^2.4.1" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz#96a143d50c58fef0c79107e61df911728d7ceb39" + integrity sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/express-serve-static-core" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" + ansi-html-community "^0.0.8" + bonjour-service "^1.2.1" + chokidar "^3.6.0" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + express "^4.21.2" + graceful-fs "^4.2.6" + http-proxy-middleware "^2.0.9" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + schema-utils "^4.2.0" + selfsigned "^2.4.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^7.4.2" + ws "^8.18.0" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.1" + +webpack-sources@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" + integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== + +webpack@5.101.3: + version "5.101.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.101.3.tgz#3633b2375bb29ea4b06ffb1902734d977bc44346" + integrity sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.15.0" + acorn-import-phases "^1.0.3" + browserslist "^4.24.0" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.3" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^4.3.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.11" + watchpack "^2.4.1" + webpack-sources "^3.3.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@^9.2.0: + version "9.3.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@8.18.3, ws@^8.18.0: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +ws@~8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + +wsl-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/wsl-utils/-/wsl-utils-0.1.0.tgz#8783d4df671d4d50365be2ee4c71917a0557baab" + integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== + dependencies: + is-wsl "^3.1.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 1bbbdc24a273aa912e7a767a86124ecba182e0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sat, 17 Jan 2026 19:33:38 +0100 Subject: [PATCH 031/162] Resolve PR review change requests --- .idea/modules.xml | 11 ++++++--- composeApp/build.gradle.kts | 2 +- .../mokegyesulet/it/dunestrat/backend/Auth.kt | 4 ++++ .../it/dunestrat/backend/SupabaseAuth.kt | 2 +- .../entities/PlayerStepDatabaseEntity.kt | 23 ------------------- .../it/dunestrat/navigation/NavGraph.kt | 7 +----- .../it/dunestrat/navigation/Screen.kt | 1 - gradle/libs.versions.toml | 2 ++ 8 files changed, 17 insertions(+), 35 deletions(-) diff --git a/.idea/modules.xml b/.idea/modules.xml index f7f6104..85008f6 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,10 +2,15 @@ - - - + + + + + + + + \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index b658c82..ceef75f 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.composeHotReload) alias(libs.plugins.googleServices) alias(libs.plugins.ktlint) - kotlin("plugin.serialization") version "2.3.0" + alias(libs.plugins.serialization) } kotlin { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt index b54860a..bebdd36 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt @@ -1,6 +1,10 @@ package hu.mokegyesulet.it.dunestrat.backend +import io.github.jan.supabase.auth.status.SessionStatus +import kotlinx.coroutines.flow.Flow + interface Auth { suspend fun logIn() suspend fun logOut() + val sessionStatus: Flow } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt index b1926c6..74a0fcd 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt @@ -9,7 +9,7 @@ object SupabaseAuth : Auth { private val auth = SupabaseClientProvider.client.auth override suspend fun logIn() = auth.signInWith(Google) - val sessionStatus get(): Flow = auth.sessionStatus + override val sessionStatus get(): Flow = auth.sessionStatus override suspend fun logOut() = auth.signOut() } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index 57eda9a..b8eb928 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -12,29 +12,6 @@ class PlayerStepDatabaseEntry( fun toPlayerStep(): PlayerStep { val playerStep = Json.decodeFromString(json) return playerStep.copy(id = id, gameStateId = gameStateId) - -// val data = Json.parseToJsonElement(json).jsonObject -// PlayerStep( -// id, -// gameStateId, -// data["playerId"]!!.toString().removeSurrounding("\""), -// data["leaveFields"]!!.jsonArray.map { -// Json.decodeFromString(it.toString()) -// }.toSet(), -// data ["enterFields"]!!.jsonArray.map { -// Json.decodeFromString(it.toString()) -// }.toSet(), -// data["purchaseWeapons"]!!.jsonObject.map { -// val weapon = Json.decodeFromString( -// it.key, -// ) -// val amount = it.value.toString().toInt() -// weapon to amount -// }.toMap(), -// data["buildHarvesters"]!!.jsonArray.map { -// Json.decodeFromString(it.toString()) -// }.toSet(), -// ) } companion object { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index e13131a..7ec7719 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -6,7 +6,6 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu -import hu.mokegyesulet.it.dunestrat.feature.supabase.SupabaseScreen @Composable fun NavGraph(navController: NavHostController = rememberNavController()) { @@ -16,13 +15,9 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable(Screen.MainMenu.route) { MainMenu( - onPlaceholder1 = { navController.navigate(Screen.Supabase.route) }, + onPlaceholder1 = {}, onPlaceholder2 = {}, ) } - - composable(Screen.Supabase.route) { - SupabaseScreen() - } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 1e449f6..7488c3a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -4,5 +4,4 @@ sealed class Screen( val route: String, ) { data object MainMenu : Screen(route = "main-menu") - data object Supabase : Screen(route = "supabase") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff2dcc7..ffe9565 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,7 @@ ktlint-plugin = "14.0.1" kotlinx-serialization = "1.10.0-RC" supabase = "3.3.0" ktor = "3.2.2" +kotlin-serialization = "2.3.0" [libraries] material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } @@ -57,3 +58,4 @@ composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "k kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } googleServices = { id = "com.google.gms.google-services", version.ref = "google-services" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-plugin" } +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization" } From 11f5b2beeb313ed15fb3f68fa5deab0d730684f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sat, 17 Jan 2026 20:10:21 +0100 Subject: [PATCH 032/162] Add simple AuthStatus class --- .../hu/mokegyesulet/it/dunestrat/backend/Auth.kt | 3 +-- .../mokegyesulet/it/dunestrat/backend/AuthStatus.kt | 8 ++++++++ .../mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt | 11 ++++++++++- .../dunestrat/feature/mainmenu/MainMenuViewModel.kt | 6 +++--- 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/AuthStatus.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt index bebdd36..50625d9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Auth.kt @@ -1,10 +1,9 @@ package hu.mokegyesulet.it.dunestrat.backend -import io.github.jan.supabase.auth.status.SessionStatus import kotlinx.coroutines.flow.Flow interface Auth { suspend fun logIn() suspend fun logOut() - val sessionStatus: Flow + val authStatus: Flow } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/AuthStatus.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/AuthStatus.kt new file mode 100644 index 0000000..2c446ef --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/AuthStatus.kt @@ -0,0 +1,8 @@ +package hu.mokegyesulet.it.dunestrat.backend + +sealed class AuthStatus { + class Authenticated(email: String?) : AuthStatus() { + val email: String? = email + } + object Unauthenticated : AuthStatus() +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt index 74a0fcd..92b11f7 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt @@ -4,12 +4,21 @@ import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.providers.Google import io.github.jan.supabase.auth.status.SessionStatus import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map object SupabaseAuth : Auth { private val auth = SupabaseClientProvider.client.auth override suspend fun logIn() = auth.signInWith(Google) - override val sessionStatus get(): Flow = auth.sessionStatus + override val authStatus get(): Flow = auth.sessionStatus.map { s -> + if (s is SessionStatus.Authenticated) { + AuthStatus.Authenticated( + s.session.user?.email, + ) + } else { + AuthStatus.Unauthenticated + } + } override suspend fun logOut() = auth.signOut() } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt index c7111be..35c008d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt @@ -2,8 +2,8 @@ package hu.mokegyesulet.it.dunestrat.feature.mainmenu import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import hu.mokegyesulet.it.dunestrat.backend.AuthStatus import hu.mokegyesulet.it.dunestrat.backend.SupabaseAuth -import io.github.jan.supabase.auth.status.SessionStatus import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -22,7 +22,7 @@ class MainMenuViewModel : ViewModel() { } val isLoggedIn: Flow - get() = SupabaseAuth.sessionStatus.map { - it is SessionStatus.Authenticated + get() = SupabaseAuth.authStatus.map { + it is AuthStatus.Authenticated } } From 81c9e37a4b5ae5ca09ff2972b1bffc1ccf80e200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 15:35:10 +0000 Subject: [PATCH 033/162] Cleanup tracked files in the .idea folder --- .gitignore | 5 +++++ .idea/gradle.xml | 18 ------------------ .idea/ktlint-plugin.xml | 7 ------- .idea/misc.xml | 8 -------- .idea/modules.xml | 15 +++++++++++++++ .idea/runConfigurations.xml | 17 ----------------- 6 files changed, 20 insertions(+), 50 deletions(-) delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/ktlint-plugin.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/runConfigurations.xml diff --git a/.gitignore b/.gitignore index 7f7e692..a05c2d2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,11 @@ node_modules/ .idea/usage.statistics.xml .idea/shelf/ .idea/*.iml +.idea/gradle.xml +.idea/ktlint-plugin.xml +.idea/misc.xml +.idea/runConfigurations.xml +.idea/libraries/ # Nix .direnv diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 1d8dc37..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml deleted file mode 100644 index e8bd90c..0000000 --- a/.idea/ktlint-plugin.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - DISTRACT_FREE - DEFAULT - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 49145cd..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 85008f6..9e16242 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,15 +2,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 16660f1..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file From 8e9dfa5871f1067d9e0ee060cdedc95e710a3889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 17:18:49 +0000 Subject: [PATCH 034/162] Added `Desert` to database --- .../it/dunestrat/backend/Repository.kt | 4 ++++ .../dunestrat/backend/SubabaseRepository.kt | 14 +++++++++++ .../backend/entities/DesertDatabaseEntity.kt | 23 +++++++++++++++++++ .../mokegyesulet/it/dunestrat/model/Desert.kt | 8 ++++++- .../it/dunestrat/model/DesertField.kt | 3 +++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index d4c1592..4657d77 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -1,5 +1,6 @@ package hu.mokegyesulet.it.dunestrat.backend +import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep @@ -14,4 +15,7 @@ interface Repository { suspend fun savePlayerStep(playerStep: PlayerStep) fun getPlayerSteps(): Flow> + + suspend fun saveDesert(desert: Desert) + fun getDeserts(): Flow> } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt index 58db755..a7ea5c4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt @@ -1,9 +1,12 @@ package hu.mokegyesulet.it.dunestrat.backend +import hu.mokegyesulet.it.dunestrat.backend.entities.DesertDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntry import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntry import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntry +import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep @@ -18,6 +21,7 @@ object SubabaseRepository : Repository { const val GAME_TABLE = GameDatabaseEntry.TABLE_NAME const val GAME_STATE_TABLE = GameDatabaseEntry.TABLE_NAME const val STEP_TABLE = PlayerStepDatabaseEntry.TABLE_NAME + const val DESERT_TABLE = DesertDatabaseEntity.TABLE_NAME @OptIn(SupabaseExperimental::class) override fun getGames(): Flow> = @@ -48,4 +52,14 @@ object SubabaseRepository : Repository { override suspend fun savePlayerStep(playerStep: PlayerStep) { supabase.from(STEP_TABLE).insert(playerStep.toDatabaseEntry()) } + + override suspend fun saveDesert(desert: Desert) { + supabase.from(DESERT_TABLE).insert(desert.toDatabaseEntity()) + } + + @OptIn(SupabaseExperimental::class) + override fun getDeserts(): Flow> = + supabase.from(DESERT_TABLE).selectAsFlow(DesertDatabaseEntity::id).map { list -> + list.map { it.toDesert() } + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt new file mode 100644 index 0000000..901a370 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -0,0 +1,23 @@ +package hu.mokegyesulet.it.dunestrat.backend.entities + +import hu.mokegyesulet.it.dunestrat.model.Desert +import kotlinx.serialization.json.Json + +class DesertDatabaseEntity( + val id: Int?, + val json: String, +) { + fun toDesert(): Desert { + val desert = Json.decodeFromString(json) + return desert.copy(id = id) + } + + companion object { + const val TABLE_NAME = "desert" + } +} + +fun Desert.toDatabaseEntity() = DesertDatabaseEntity( + id = id, + json = Json.encodeToString(this), +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index b83c16b..d97ccf9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -1,6 +1,12 @@ package hu.mokegyesulet.it.dunestrat.model -class Desert( +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class Desert( + @Transient + val id: Int? = null, val fields: Set, ) { companion object { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt index 9326e47..a263dc1 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/DesertField.kt @@ -1,5 +1,8 @@ package hu.mokegyesulet.it.dunestrat.model +import kotlinx.serialization.Serializable + +@Serializable class DesertField( var id: String, var water: Int, From 8686ebd10a40476d392193ea5d8c386eab1ac2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 18:09:06 +0000 Subject: [PATCH 035/162] Add `desert_id` to `Game` and make database entities serializable --- .../it/dunestrat/backend/SubabaseRepository.kt | 12 ++++++------ .../backend/entities/DesertDatabaseEntity.kt | 2 ++ .../dunestrat/backend/entities/GameDatabaseEntity.kt | 12 +++++++++--- .../backend/entities/GameStateDatabaseEntity.kt | 8 ++++++-- .../backend/entities/PlayerStepDatabaseEntity.kt | 4 ++++ .../hu/mokegyesulet/it/dunestrat/model/Game.kt | 2 ++ 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt index a7ea5c4..55501ef 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt @@ -1,8 +1,8 @@ package hu.mokegyesulet.it.dunestrat.backend import hu.mokegyesulet.it.dunestrat.backend.entities.DesertDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntry -import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntity +import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntry import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntry @@ -18,14 +18,14 @@ import kotlinx.coroutines.flow.map object SubabaseRepository : Repository { val supabase = SupabaseClientProvider.client - const val GAME_TABLE = GameDatabaseEntry.TABLE_NAME - const val GAME_STATE_TABLE = GameDatabaseEntry.TABLE_NAME + const val GAME_TABLE = GameDatabaseEntity.TABLE_NAME + const val GAME_STATE_TABLE = GameDatabaseEntity.TABLE_NAME const val STEP_TABLE = PlayerStepDatabaseEntry.TABLE_NAME const val DESERT_TABLE = DesertDatabaseEntity.TABLE_NAME @OptIn(SupabaseExperimental::class) override fun getGames(): Flow> = - supabase.from(GAME_TABLE).selectAsFlow(GameDatabaseEntry::id).map { list -> + supabase.from(GAME_TABLE).selectAsFlow(GameDatabaseEntity::id).map { list -> list.map { it.toGame() } } @@ -35,7 +35,7 @@ object SubabaseRepository : Repository { @OptIn(SupabaseExperimental::class) override fun getGameStates(): Flow> = - supabase.from(GAME_STATE_TABLE).selectAsFlow(GameStateDatabaseEntry::id).map { list -> + supabase.from(GAME_STATE_TABLE).selectAsFlow(GameStateDatabaseEntity::id).map { list -> list.map { it.toGameState() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt index 901a370..f618a2e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -1,8 +1,10 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.Desert +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json +@Serializable class DesertDatabaseEntity( val id: Int?, val json: String, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt index 1d24f31..8c6ba9f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt @@ -2,16 +2,21 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameProgress +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -class GameDatabaseEntry( +@Serializable +class GameDatabaseEntity( val id: Int?, val progress: Int, + @SerialName("desert_id") + val desertId: Int, val json: String, ) { fun toGame(): Game { val game = Json.decodeFromString(json) - return game.copy(id = id, progress = GameProgress.entries[progress]) + return game.copy(id = id, progress = GameProgress.entries[progress], desertId = desertId) } companion object { @@ -19,8 +24,9 @@ class GameDatabaseEntry( } } -fun Game.toDatabaseEntry(): GameDatabaseEntry = GameDatabaseEntry( +fun Game.toDatabaseEntry(): GameDatabaseEntity = GameDatabaseEntity( id = id, this.progress.ordinal, json = Json.encodeToString(this), + desertId = desertId, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index eb2034d..a1e1d40 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -1,10 +1,14 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.GameState +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -class GameStateDatabaseEntry( +@Serializable +class GameStateDatabaseEntity( val id: Int?, + @SerialName("game_id") val gameId: Int, val index: Int, val json: String, @@ -18,7 +22,7 @@ class GameStateDatabaseEntry( } } -fun GameState.toDatabaseEntry(): GameStateDatabaseEntry = GameStateDatabaseEntry( +fun GameState.toDatabaseEntry(): GameStateDatabaseEntity = GameStateDatabaseEntity( id = id, gameId = gameId, index = index, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index b8eb928..ba989d0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -1,10 +1,14 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json +@Serializable class PlayerStepDatabaseEntry( val id: Int?, + @SerialName("game_state_id") val gameStateId: Int, val json: String, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt index 65fbf07..a71871e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt @@ -11,6 +11,8 @@ data class Game( @Transient val progress: GameProgress = GameProgress.ERROR, val teams: List, + @Transient + val desertId: Int = -1, ) enum class GameProgress { From 646066e9a1988040ebfe6f250badffe0a30cf82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 18:13:42 +0000 Subject: [PATCH 036/162] Quick rename --- .../mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt | 6 +++--- .../dunestrat/backend/entities/PlayerStepDatabaseEntity.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt index 55501ef..066116e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt @@ -3,7 +3,7 @@ package hu.mokegyesulet.it.dunestrat.backend import hu.mokegyesulet.it.dunestrat.backend.entities.DesertDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntity import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntry import hu.mokegyesulet.it.dunestrat.model.Desert @@ -20,7 +20,7 @@ object SubabaseRepository : Repository { val supabase = SupabaseClientProvider.client const val GAME_TABLE = GameDatabaseEntity.TABLE_NAME const val GAME_STATE_TABLE = GameDatabaseEntity.TABLE_NAME - const val STEP_TABLE = PlayerStepDatabaseEntry.TABLE_NAME + const val STEP_TABLE = PlayerStepDatabaseEntity.TABLE_NAME const val DESERT_TABLE = DesertDatabaseEntity.TABLE_NAME @OptIn(SupabaseExperimental::class) @@ -45,7 +45,7 @@ object SubabaseRepository : Repository { @OptIn(SupabaseExperimental::class) override fun getPlayerSteps(): Flow> = - supabase.from(STEP_TABLE).selectAsFlow(PlayerStepDatabaseEntry::id).map { list -> + supabase.from(STEP_TABLE).selectAsFlow(PlayerStepDatabaseEntity::id).map { list -> list.map { it.toPlayerStep() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index ba989d0..91a6a2e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable -class PlayerStepDatabaseEntry( +class PlayerStepDatabaseEntity( val id: Int?, @SerialName("game_state_id") val gameStateId: Int, @@ -23,7 +23,7 @@ class PlayerStepDatabaseEntry( } } -fun PlayerStep.toDatabaseEntry(): PlayerStepDatabaseEntry = PlayerStepDatabaseEntry( +fun PlayerStep.toDatabaseEntry(): PlayerStepDatabaseEntity = PlayerStepDatabaseEntity( id = id, gameStateId = gameStateId, json = Json.encodeToString(this), From ef588306cf507cea082593481c162af09e443dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Mon, 1 Dec 2025 09:23:34 +0100 Subject: [PATCH 037/162] WIP --- .idea/deploymentTargetSelector.xml | 10 +++++ .../mokegyesulet/it/dunestrat/model/Desert.kt | 39 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..d0b0930 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index d97ccf9..bcad2a8 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.model import kotlinx.serialization.Serializable import kotlinx.serialization.Transient +import kotlin.math.abs @Serializable data class Desert( @@ -9,9 +10,45 @@ data class Desert( val id: Int? = null, val fields: Set, ) { + companion object { fun create12PlayerHexagon(): Desert { - TODO() + val fields = mutableSetOf() + + val desertValuesByDistance = mapOf( + 0 to Pair(-8, 40), + 1 to Pair(-7, 37), + 2 to Pair(-6, 29), + 3 to Pair(-5, 19), + 4 to Pair(-4, 11), + 5 to Pair(-3, 5), + 6 to Pair(-2, 2), + 7 to Pair(-1, 1), + ) + + for (x in -7..7) { + for (y in -14..14) { + val distance = abs(x) + abs(y) + if (distance % 2 == 0 && distance / 2 <= 7) { + val values = desertValuesByDistance[distance / 2] ?: Pair(0, 0) + + val desertField = DesertField( + id = "${'B' + x + 7}${(y + 14 + 2) / 2}", + water = values.first, + spice = values.second, + effectiveWeapon = Weapon.LEGION, // TODO + neighbours = setOf(), + startingField = false, // TODO + ) + + println("($x, $y): $desertField") + + fields.add(desertField) + } + } + } + + return Desert(fields) } } } From 800698e3f8cbccdb8ccc3ca3f8dc83f686a37ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Thu, 4 Dec 2025 01:57:38 +0100 Subject: [PATCH 038/162] Implented layout, basic water and spice --- .idea/artifacts/composeApp_js.xml | 2 +- .idea/artifacts/composeApp_jvm.xml | 2 +- .idea/artifacts/composeApp_wasm_js.xml | 2 +- .../hu/mokegyesulet/it/dunestrat/model/Desert.kt | 13 +++++++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml index e52ad0e..00ef8a3 100644 --- a/.idea/artifacts/composeApp_js.xml +++ b/.idea/artifacts/composeApp_js.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml index 96d842a..01d03af 100644 --- a/.idea/artifacts/composeApp_jvm.xml +++ b/.idea/artifacts/composeApp_jvm.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml index e3c9c59..0bd6a39 100644 --- a/.idea/artifacts/composeApp_wasm_js.xml +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index bcad2a8..42a2939 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -26,11 +26,16 @@ data class Desert( 7 to Pair(-1, 1), ) - for (x in -7..7) { - for (y in -14..14) { + val size = 7 + + for (x in -size..size) { + for (y in -size * 2..size * 2) { val distance = abs(x) + abs(y) - if (distance % 2 == 0 && distance / 2 <= 7) { - val values = desertValuesByDistance[distance / 2] ?: Pair(0, 0) + if (distance % 2 == 0 && distance / 2 <= size) { + val stepsToReach = + abs(x) + if (abs(y) > abs(x)) (abs(y) - abs(x)) / 2 else 0 + + val values = desertValuesByDistance[stepsToReach] ?: Pair(0, 0) val desertField = DesertField( id = "${'B' + x + 7}${(y + 14 + 2) / 2}", From 3c544725954cf4be0587eb1033a6caadccf7ffd1 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 18 Jan 2026 18:40:03 +0100 Subject: [PATCH 039/162] Imported effective weapons and base locations from excel. Started neighbours finding --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 266 +++++++++++++++++- 1 file changed, 262 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 42a2939..0789017 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -26,6 +26,244 @@ data class Desert( 7 to Pair(-1, 1), ) + // exelből kimásolt + // ürres mezőnél Legion a hatásos + val effectiveWeaponsById = mapOf( + "A1" to Weapon.LEGION, + "A2" to Weapon.LEGION, + "A3" to Weapon.PISTOL, + "A4" to Weapon.LEGION, + "A5" to Weapon.LEGION, + "A6" to Weapon.LEGION, + "A7" to Weapon.CRYSKNIFE, + "A8" to Weapon.LEGION, + "A9" to Weapon.LEGION, + "B1" to Weapon.LEGION, + "B2" to Weapon.CRYSKNIFE, + "B3" to Weapon.PISTOL, + "B4" to Weapon.PISTOL, + "B5" to Weapon.PISTOL, + "B6" to Weapon.CRYSKNIFE, + "B7" to Weapon.CRYSKNIFE, + "B8" to Weapon.CRYSKNIFE, + "B9" to Weapon.PISTOL, + "B10" to Weapon.LEGION, + "C1" to Weapon.LASGUN, + "C2" to Weapon.LASGUN, + "C3" to Weapon.CRYSKNIFE, + "C4" to Weapon.PISTOL, + "C5" to Weapon.PISTOL, + "C6" to Weapon.LASGUN, + "C7" to Weapon.CRYSKNIFE, + "C8" to Weapon.CRYSKNIFE, + "C9" to Weapon.PISTOL, + "C10" to Weapon.LASGUN, + "C11" to Weapon.LASGUN, + "D1" to Weapon.LEGION, + "D2" to Weapon.LASGUN, + "D3" to Weapon.LASGUN, + "D4" to Weapon.CRYSKNIFE, + "D5" to Weapon.PISTOL, + "D6" to Weapon.PISTOL, + "D7" to Weapon.CRYSKNIFE, + "D8" to Weapon.CRYSKNIFE, + "D9" to Weapon.PISTOL, + "D10" to Weapon.LASGUN, + "D11" to Weapon.LASGUN, + "D12" to Weapon.LEGION, + "E1" to Weapon.LEGION, + "E2" to Weapon.LASGUN, + "E3" to Weapon.LASGUN, + "E4" to Weapon.LASGUN, + "E5" to Weapon.CRYSKNIFE, + "E6" to Weapon.PISTOL, + "E7" to Weapon.LASGUN, + "E8" to Weapon.CRYSKNIFE, + "E9" to Weapon.PISTOL, + "E10" to Weapon.LASGUN, + "E11" to Weapon.LASGUN, + "E12" to Weapon.LASGUN, + "E13" to Weapon.LEGION, + "F1" to Weapon.LEGION, + "F2" to Weapon.CRYSKNIFE, + "F3" to Weapon.PISTOL, + "F4" to Weapon.LASGUN, + "F5" to Weapon.LASGUN, + "F6" to Weapon.CRYSKNIFE, + "F7" to Weapon.PISTOL, + "F8" to Weapon.CRYSKNIFE, + "F9" to Weapon.PISTOL, + "F10" to Weapon.LASGUN, + "F11" to Weapon.LASGUN, + "F12" to Weapon.CRYSKNIFE, + "F13" to Weapon.PISTOL, + "F14" to Weapon.LEGION, + "G1" to Weapon.CRYSKNIFE, + "G2" to Weapon.CRYSKNIFE, + "G3" to Weapon.CRYSKNIFE, + "G4" to Weapon.CRYSKNIFE, + "G5" to Weapon.PISTOL, + "G6" to Weapon.LASGUN, + "G7" to Weapon.LASGUN, + "G8" to Weapon.PISTOL, + "G9" to Weapon.CRYSKNIFE, + "G10" to Weapon.LASGUN, + "G11" to Weapon.CRYSKNIFE, + "G12" to Weapon.PISTOL, + "G13" to Weapon.PISTOL, + "G14" to Weapon.PISTOL, + "G15" to Weapon.PISTOL, + "H1" to Weapon.LEGION, + "H2" to Weapon.CRYSKNIFE, + "H3" to Weapon.CRYSKNIFE, + "H4" to Weapon.CRYSKNIFE, + "H5" to Weapon.CRYSKNIFE, + "H6" to Weapon.CRYSKNIFE, + "H7" to Weapon.CRYSKNIFE, + "H8" to Weapon.CRYSKNIFE, + "H9" to Weapon.PISTOL, + "H10" to Weapon.LASGUN, + "H11" to Weapon.PISTOL, + "H12" to Weapon.PISTOL, + "H13" to Weapon.PISTOL, + "H14" to Weapon.PISTOL, + "H15" to Weapon.PISTOL, + "H16" to Weapon.LEGION, + "I1" to Weapon.LEGION, + "I2" to Weapon.LASGUN, + "I3" to Weapon.LASGUN, + "I4" to Weapon.LASGUN, + "I5" to Weapon.LASGUN, + "I6" to Weapon.LASGUN, + "I7" to Weapon.PISTOL, + "I8" to Weapon.PISTOL, + "I9" to Weapon.LASGUN, + "I10" to Weapon.CRYSKNIFE, + "I11" to Weapon.PISTOL, + "I12" to Weapon.LASGUN, + "I13" to Weapon.LASGUN, + "I14" to Weapon.LASGUN, + "I15" to Weapon.LASGUN, + "I16" to Weapon.LASGUN, + "I17" to Weapon.LEGION, + "J1" to Weapon.LEGION, + "J2" to Weapon.PISTOL, + "J3" to Weapon.PISTOL, + "J4" to Weapon.PISTOL, + "J5" to Weapon.PISTOL, + "J6" to Weapon.PISTOL, + "J7" to Weapon.CRYSKNIFE, + "J8" to Weapon.CRYSKNIFE, + "J9" to Weapon.PISTOL, + "J10" to Weapon.LASGUN, + "J11" to Weapon.CRYSKNIFE, + "J12" to Weapon.CRYSKNIFE, + "J13" to Weapon.CRYSKNIFE, + "J14" to Weapon.CRYSKNIFE, + "J15" to Weapon.CRYSKNIFE, + "J16" to Weapon.LEGION, + "K1" to Weapon.PISTOL, + "K2" to Weapon.PISTOL, + "K3" to Weapon.PISTOL, + "K4" to Weapon.PISTOL, + "K5" to Weapon.CRYSKNIFE, + "K6" to Weapon.LASGUN, + "K7" to Weapon.LASGUN, + "K8" to Weapon.CRYSKNIFE, + "K9" to Weapon.PISTOL, + "K10" to Weapon.LASGUN, + "K11" to Weapon.PISTOL, + "K12" to Weapon.CRYSKNIFE, + "K13" to Weapon.CRYSKNIFE, + "K14" to Weapon.CRYSKNIFE, + "K15" to Weapon.CRYSKNIFE, + "L1" to Weapon.LEGION, + "L2" to Weapon.PISTOL, + "L3" to Weapon.CRYSKNIFE, + "L4" to Weapon.LASGUN, + "L5" to Weapon.LASGUN, + "L6" to Weapon.PISTOL, + "L7" to Weapon.CRYSKNIFE, + "L8" to Weapon.PISTOL, + "L9" to Weapon.CRYSKNIFE, + "L10" to Weapon.LASGUN, + "L11" to Weapon.LASGUN, + "L12" to Weapon.PISTOL, + "L13" to Weapon.CRYSKNIFE, + "L14" to Weapon.LEGION, + "M1" to Weapon.LEGION, + "M2" to Weapon.LASGUN, + "M3" to Weapon.LASGUN, + "M4" to Weapon.LASGUN, + "M5" to Weapon.PISTOL, + "M6" to Weapon.CRYSKNIFE, + "M7" to Weapon.LASGUN, + "M8" to Weapon.PISTOL, + "M9" to Weapon.CRYSKNIFE, + "M10" to Weapon.LASGUN, + "M11" to Weapon.LASGUN, + "M12" to Weapon.LASGUN, + "M13" to Weapon.LEGION, + "N1" to Weapon.LEGION, + "N2" to Weapon.LASGUN, + "N3" to Weapon.LASGUN, + "N4" to Weapon.PISTOL, + "N5" to Weapon.CRYSKNIFE, + "N6" to Weapon.CRYSKNIFE, + "N7" to Weapon.PISTOL, + "N8" to Weapon.PISTOL, + "N9" to Weapon.CRYSKNIFE, + "N10" to Weapon.LASGUN, + "N11" to Weapon.LASGUN, + "N12" to Weapon.LEGION, + "O1" to Weapon.LASGUN, + "O2" to Weapon.LASGUN, + "O3" to Weapon.PISTOL, + "O4" to Weapon.CRYSKNIFE, + "O5" to Weapon.CRYSKNIFE, + "O6" to Weapon.LASGUN, + "O7" to Weapon.PISTOL, + "O8" to Weapon.PISTOL, + "O9" to Weapon.CRYSKNIFE, + "O10" to Weapon.LASGUN, + "O11" to Weapon.LASGUN, + "P1" to Weapon.LEGION, + "P2" to Weapon.PISTOL, + "P3" to Weapon.CRYSKNIFE, + "P4" to Weapon.CRYSKNIFE, + "P5" to Weapon.CRYSKNIFE, + "P6" to Weapon.PISTOL, + "P7" to Weapon.PISTOL, + "P8" to Weapon.PISTOL, + "P9" to Weapon.CRYSKNIFE, + "P10" to Weapon.LEGION, + "Q1" to Weapon.LEGION, + "Q2" to Weapon.LEGION, + "Q3" to Weapon.CRYSKNIFE, + "Q4" to Weapon.LEGION, + "Q5" to Weapon.LEGION, + "Q6" to Weapon.LEGION, + "Q7" to Weapon.PISTOL, + "Q8" to Weapon.LEGION, + "Q9" to Weapon.LEGION, + ) + + // exelből + val bases = setOf( + "A3", + "A7", + "C1", + "C10", + "G1", + "G15", + "K1", + "K15", + "O1", + "O11", + "Q3", + "Q7", + ) + val size = 7 for (x in -size..size) { @@ -37,13 +275,15 @@ data class Desert( val values = desertValuesByDistance[stepsToReach] ?: Pair(0, 0) + val id = "${'B' + x + 7}${(y + 14 + 2) / 2}" + val desertField = DesertField( - id = "${'B' + x + 7}${(y + 14 + 2) / 2}", + id = id, water = values.first, spice = values.second, - effectiveWeapon = Weapon.LEGION, // TODO + effectiveWeapon = effectiveWeaponsById[id] ?: Weapon.LEGION, neighbours = setOf(), - startingField = false, // TODO + startingField = id in bases, ) println("($x, $y): $desertField") @@ -53,7 +293,25 @@ data class Desert( } } - return Desert(fields) + val newFields = mutableSetOf() + fields.forEach { field -> + val neighbours = mutableSetOf() + + // TODO + + newFields.add( + DesertField( + field.id, + field.water, + field.spice, + field.effectiveWeapon, + neighbours.toSet(), + field.startingField, + ), + ) + } + + return Desert(newFields) } } } From ded71b2c67ba90f7f1c7542bbfa62ecdee28c0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Mon, 19 Jan 2026 19:05:06 +0000 Subject: [PATCH 040/162] Converting coordinates to the new system In the new system Coordinates with the same number are in online and not in a ^ line like in the old system. --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 0789017..ff0defb 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -28,7 +28,7 @@ data class Desert( // exelből kimásolt // ürres mezőnél Legion a hatásos - val effectiveWeaponsById = mapOf( + val effectiveWeaponsByIdOldCoordinates = mapOf( "A1" to Weapon.LEGION, "A2" to Weapon.LEGION, "A3" to Weapon.PISTOL, @@ -248,8 +248,12 @@ data class Desert( "Q9" to Weapon.LEGION, ) + val effectiveWeaponsById = effectiveWeaponsByIdOldCoordinates.mapKeys { + convertCoordinateToNewSystem(it.key) + } + // exelből - val bases = setOf( + val basesOldCoordinates = setOf( "A3", "A7", "C1", @@ -264,6 +268,10 @@ data class Desert( "Q7", ) + val bases = basesOldCoordinates.map { + convertCoordinateToNewSystem(it) + }.toSet() + val size = 7 for (x in -size..size) { @@ -313,5 +321,19 @@ data class Desert( return Desert(newFields) } + + fun convertCoordinateToNewSystem( + oldCoordinate: String, + middleColumnLetter: Char = 'I', + ): String { + val letter = oldCoordinate[0] + val number = oldCoordinate.substring(1).toInt() + if (letter <= middleColumnLetter) { + return oldCoordinate + } + val shift = letter - middleColumnLetter + val newNumber = number + shift + return "$letter$newNumber" + } } } From aee270a501d87582c25e7a200a1eaef1b83dfe0b Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 25 Jan 2026 16:52:38 +0100 Subject: [PATCH 041/162] Created neighbours logic, thus in theory it is done --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index ff0defb..9b2d3b4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -302,10 +302,40 @@ data class Desert( } val newFields = mutableSetOf() + fields.forEach { field -> val neighbours = mutableSetOf() - // TODO + val fieldFirstCord = field.id[0].code + val fieldSecondCord = field.id.substring(1).toInt() + val neigbourFirstCords = mutableSetOf( + fieldFirstCord - 1, + fieldFirstCord, + fieldFirstCord + 1, + ) + val neigbourSecondCords = mutableSetOf( + fieldSecondCord - 1, + fieldSecondCord, + fieldSecondCord + 1, + ) + + fields.filter { entry -> + (entry.id[0].code in neigbourFirstCords) and + (entry.id.substring(1).toInt() in neigbourSecondCords) + }.forEach { entry -> + if (!( + ( + (entry.id[0] == fieldFirstCord - 1) and + (entry.id.substring(1).toInt() == fieldSecondCord + 1) + ) or ( + (entry.id[0] == fieldFirstCord + 1) and + (entry.id.substring(1).toInt() == fieldSecondCord - 1) + ) + ) + ) { + neighbours.add(entry) + } + } newFields.add( DesertField( @@ -313,7 +343,7 @@ data class Desert( field.water, field.spice, field.effectiveWeapon, - neighbours.toSet(), + neighbours, field.startingField, ), ) From 4a053be62a188de6eb113d3865675321222411ed Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 25 Jan 2026 16:53:11 +0100 Subject: [PATCH 042/162] Created neighbours logic, thus in theory it is done --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 9b2d3b4..7df5cbc 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -325,10 +325,10 @@ data class Desert( }.forEach { entry -> if (!( ( - (entry.id[0] == fieldFirstCord - 1) and + (entry.id[0].code == fieldFirstCord - 1) and (entry.id.substring(1).toInt() == fieldSecondCord + 1) ) or ( - (entry.id[0] == fieldFirstCord + 1) and + (entry.id[0].code == fieldFirstCord + 1) and (entry.id.substring(1).toInt() == fieldSecondCord - 1) ) ) From 7e4cfaa63743a35af1662e1c01a1b9be5e0c70d8 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 25 Jan 2026 18:14:51 +0100 Subject: [PATCH 043/162] Added mountains, and resolved problems --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 77 ++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 7df5cbc..81cd1a6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -268,10 +268,72 @@ data class Desert( "Q7", ) + // exelből + val mountainsOldCoordinates = mapOf( + "A3" to 3, + "A7" to 3, + "B2" to 7, + "B9" to 7, + "C1" to 3, + "C5" to 6, + "C6" to 6, + "C7" to 6, + "C11" to 3, + "D6" to 5, + "D7" to 5, + "E3" to 6, + "E11" to 6, + "F3" to 6, + "F4" to 5, + "F6" to 3, + "F9" to 3, + "F11" to 5, + "F12" to 6, + "G1" to 3, + "G3" to 6, + "G4" to 5, + "G12" to 5, + "G13" to 6, + "G15" to 3, + "I2" to 7, + "I6" to 3, + "I12" to 3, + "I16" to 7, + "K1" to 3, + "K3" to 6, + "K4" to 5, + "K12" to 5, + "K13" to 6, + "K15" to 3, + "L3" to 6, + "L4" to 5, + "L6" to 3, + "L9" to 3, + "L11" to 5, + "L12" to 6, + "M3" to 6, + "M11" to 6, + "N6" to 5, + "N7" to 5, + "O1" to 3, + "O5" to 6, + "O6" to 6, + "O7" to 6, + "O11" to 3, + "P2" to 7, + "P9" to 7, + "Q3" to 3, + "Q7" to 3, + ) + val bases = basesOldCoordinates.map { convertCoordinateToNewSystem(it) }.toSet() + val mountains = mountainsOldCoordinates.mapKeys { + convertCoordinateToNewSystem(it.key) + } + val size = 7 for (x in -size..size) { @@ -287,8 +349,16 @@ data class Desert( val desertField = DesertField( id = id, - water = values.first, - spice = values.second, + water = if (id in mountains.keys) { + mountains[id]!! + } else { + values.first + }, + spice = if (id in mountains.keys) { + mountains[id]!! + } else { + values.second + }, effectiveWeapon = effectiveWeaponsById[id] ?: Weapon.LEGION, neighbours = setOf(), startingField = id in bases, @@ -330,6 +400,9 @@ data class Desert( ) or ( (entry.id[0].code == fieldFirstCord + 1) and (entry.id.substring(1).toInt() == fieldSecondCord - 1) + ) or ( + (entry.id[0].code == fieldFirstCord) and + (entry.id.substring(1).toInt() == fieldSecondCord) ) ) ) { From 716640c4e340f899a8e163bd9229c47eea939115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 18:54:37 +0000 Subject: [PATCH 044/162] Add bases to the edge of the map --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 81cd1a6..41ce8e6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -345,7 +345,7 @@ data class Desert( val values = desertValuesByDistance[stepsToReach] ?: Pair(0, 0) - val id = "${'B' + x + 7}${(y + 14 + 2) / 2}" + val id = "${'B' + x + size}${(y + (size * 2) + 2) / 2}" val desertField = DesertField( id = id, @@ -364,13 +364,23 @@ data class Desert( startingField = id in bases, ) - println("($x, $y): $desertField") - fields.add(desertField) } } } + bases.forEach { id -> + val desertField = DesertField( + id = id, + water = 4, + spice = 0, + effectiveWeapon = effectiveWeaponsById[id]!!, + neighbours = setOf(), + startingField = true, + ) + fields.add(desertField) + } + val newFields = mutableSetOf() fields.forEach { field -> @@ -378,22 +388,15 @@ data class Desert( val fieldFirstCord = field.id[0].code val fieldSecondCord = field.id.substring(1).toInt() - val neigbourFirstCords = mutableSetOf( - fieldFirstCord - 1, - fieldFirstCord, - fieldFirstCord + 1, - ) - val neigbourSecondCords = mutableSetOf( - fieldSecondCord - 1, - fieldSecondCord, - fieldSecondCord + 1, - ) + + val neighbourFirstCords = fieldFirstCord - 1..fieldFirstCord + 1 + + val neighbourSecondCords = fieldSecondCord - 1..fieldSecondCord + 1 fields.filter { entry -> - (entry.id[0].code in neigbourFirstCords) and - (entry.id.substring(1).toInt() in neigbourSecondCords) - }.forEach { entry -> - if (!( + (entry.id[0].code in neighbourFirstCords) and + (entry.id.substring(1).toInt() in neighbourSecondCords) and + !( ( (entry.id[0].code == fieldFirstCord - 1) and (entry.id.substring(1).toInt() == fieldSecondCord + 1) @@ -405,9 +408,8 @@ data class Desert( (entry.id.substring(1).toInt() == fieldSecondCord) ) ) - ) { - neighbours.add(entry) - } + }.forEach { entry -> + neighbours.add(entry) } newFields.add( From 3589cb1d051034be26e839ee93844be317f795ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 19:30:02 +0000 Subject: [PATCH 045/162] Fix formatting --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 41ce8e6..e40535d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -1,8 +1,8 @@ package hu.mokegyesulet.it.dunestrat.model +import kotlin.math.abs import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import kotlin.math.abs @Serializable data class Desert( @@ -360,7 +360,7 @@ data class Desert( values.second }, effectiveWeapon = effectiveWeaponsById[id] ?: Weapon.LEGION, - neighbours = setOf(), + neighbours = mutableSetOf(), startingField = id in bases, ) @@ -375,7 +375,7 @@ data class Desert( water = 4, spice = 0, effectiveWeapon = effectiveWeaponsById[id]!!, - neighbours = setOf(), + neighbours = mutableSetOf(), startingField = true, ) fields.add(desertField) @@ -424,7 +424,7 @@ data class Desert( ) } - return Desert(newFields) + return Desert(fields = newFields) } fun convertCoordinateToNewSystem( From 437025a32cbd8a31c356cdf02e3366d896e1139a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 30 Jan 2026 21:26:50 +0000 Subject: [PATCH 046/162] Removed empty fields from effective weapon map --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index e40535d..5186672 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -29,16 +29,8 @@ data class Desert( // exelből kimásolt // ürres mezőnél Legion a hatásos val effectiveWeaponsByIdOldCoordinates = mapOf( - "A1" to Weapon.LEGION, - "A2" to Weapon.LEGION, "A3" to Weapon.PISTOL, - "A4" to Weapon.LEGION, - "A5" to Weapon.LEGION, - "A6" to Weapon.LEGION, "A7" to Weapon.CRYSKNIFE, - "A8" to Weapon.LEGION, - "A9" to Weapon.LEGION, - "B1" to Weapon.LEGION, "B2" to Weapon.CRYSKNIFE, "B3" to Weapon.PISTOL, "B4" to Weapon.PISTOL, @@ -47,7 +39,6 @@ data class Desert( "B7" to Weapon.CRYSKNIFE, "B8" to Weapon.CRYSKNIFE, "B9" to Weapon.PISTOL, - "B10" to Weapon.LEGION, "C1" to Weapon.LASGUN, "C2" to Weapon.LASGUN, "C3" to Weapon.CRYSKNIFE, @@ -59,7 +50,6 @@ data class Desert( "C9" to Weapon.PISTOL, "C10" to Weapon.LASGUN, "C11" to Weapon.LASGUN, - "D1" to Weapon.LEGION, "D2" to Weapon.LASGUN, "D3" to Weapon.LASGUN, "D4" to Weapon.CRYSKNIFE, @@ -70,8 +60,6 @@ data class Desert( "D9" to Weapon.PISTOL, "D10" to Weapon.LASGUN, "D11" to Weapon.LASGUN, - "D12" to Weapon.LEGION, - "E1" to Weapon.LEGION, "E2" to Weapon.LASGUN, "E3" to Weapon.LASGUN, "E4" to Weapon.LASGUN, @@ -83,8 +71,6 @@ data class Desert( "E10" to Weapon.LASGUN, "E11" to Weapon.LASGUN, "E12" to Weapon.LASGUN, - "E13" to Weapon.LEGION, - "F1" to Weapon.LEGION, "F2" to Weapon.CRYSKNIFE, "F3" to Weapon.PISTOL, "F4" to Weapon.LASGUN, @@ -97,7 +83,6 @@ data class Desert( "F11" to Weapon.LASGUN, "F12" to Weapon.CRYSKNIFE, "F13" to Weapon.PISTOL, - "F14" to Weapon.LEGION, "G1" to Weapon.CRYSKNIFE, "G2" to Weapon.CRYSKNIFE, "G3" to Weapon.CRYSKNIFE, @@ -113,7 +98,6 @@ data class Desert( "G13" to Weapon.PISTOL, "G14" to Weapon.PISTOL, "G15" to Weapon.PISTOL, - "H1" to Weapon.LEGION, "H2" to Weapon.CRYSKNIFE, "H3" to Weapon.CRYSKNIFE, "H4" to Weapon.CRYSKNIFE, @@ -128,8 +112,6 @@ data class Desert( "H13" to Weapon.PISTOL, "H14" to Weapon.PISTOL, "H15" to Weapon.PISTOL, - "H16" to Weapon.LEGION, - "I1" to Weapon.LEGION, "I2" to Weapon.LASGUN, "I3" to Weapon.LASGUN, "I4" to Weapon.LASGUN, @@ -145,8 +127,6 @@ data class Desert( "I14" to Weapon.LASGUN, "I15" to Weapon.LASGUN, "I16" to Weapon.LASGUN, - "I17" to Weapon.LEGION, - "J1" to Weapon.LEGION, "J2" to Weapon.PISTOL, "J3" to Weapon.PISTOL, "J4" to Weapon.PISTOL, @@ -161,7 +141,6 @@ data class Desert( "J13" to Weapon.CRYSKNIFE, "J14" to Weapon.CRYSKNIFE, "J15" to Weapon.CRYSKNIFE, - "J16" to Weapon.LEGION, "K1" to Weapon.PISTOL, "K2" to Weapon.PISTOL, "K3" to Weapon.PISTOL, @@ -177,7 +156,6 @@ data class Desert( "K13" to Weapon.CRYSKNIFE, "K14" to Weapon.CRYSKNIFE, "K15" to Weapon.CRYSKNIFE, - "L1" to Weapon.LEGION, "L2" to Weapon.PISTOL, "L3" to Weapon.CRYSKNIFE, "L4" to Weapon.LASGUN, @@ -190,8 +168,6 @@ data class Desert( "L11" to Weapon.LASGUN, "L12" to Weapon.PISTOL, "L13" to Weapon.CRYSKNIFE, - "L14" to Weapon.LEGION, - "M1" to Weapon.LEGION, "M2" to Weapon.LASGUN, "M3" to Weapon.LASGUN, "M4" to Weapon.LASGUN, @@ -203,8 +179,6 @@ data class Desert( "M10" to Weapon.LASGUN, "M11" to Weapon.LASGUN, "M12" to Weapon.LASGUN, - "M13" to Weapon.LEGION, - "N1" to Weapon.LEGION, "N2" to Weapon.LASGUN, "N3" to Weapon.LASGUN, "N4" to Weapon.PISTOL, @@ -215,7 +189,6 @@ data class Desert( "N9" to Weapon.CRYSKNIFE, "N10" to Weapon.LASGUN, "N11" to Weapon.LASGUN, - "N12" to Weapon.LEGION, "O1" to Weapon.LASGUN, "O2" to Weapon.LASGUN, "O3" to Weapon.PISTOL, @@ -227,7 +200,6 @@ data class Desert( "O9" to Weapon.CRYSKNIFE, "O10" to Weapon.LASGUN, "O11" to Weapon.LASGUN, - "P1" to Weapon.LEGION, "P2" to Weapon.PISTOL, "P3" to Weapon.CRYSKNIFE, "P4" to Weapon.CRYSKNIFE, @@ -236,16 +208,8 @@ data class Desert( "P7" to Weapon.PISTOL, "P8" to Weapon.PISTOL, "P9" to Weapon.CRYSKNIFE, - "P10" to Weapon.LEGION, - "Q1" to Weapon.LEGION, - "Q2" to Weapon.LEGION, "Q3" to Weapon.CRYSKNIFE, - "Q4" to Weapon.LEGION, - "Q5" to Weapon.LEGION, - "Q6" to Weapon.LEGION, "Q7" to Weapon.PISTOL, - "Q8" to Weapon.LEGION, - "Q9" to Weapon.LEGION, ) val effectiveWeaponsById = effectiveWeaponsByIdOldCoordinates.mapKeys { From 4f5239fbcd8cb53921917752849c57b12981fb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 25 Jan 2026 19:47:49 +0000 Subject: [PATCH 047/162] Stop tracking run configurations --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a05c2d2..07874ae 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ node_modules/ .idea/ktlint-plugin.xml .idea/misc.xml .idea/runConfigurations.xml +/.idea/deploymentTargetSelector.xml .idea/libraries/ # Nix From 95cc7ae0d4e02d2528204577283e1e426632783d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 30 Jan 2026 21:42:02 +0000 Subject: [PATCH 048/162] Removed run configurations file --- .idea/deploymentTargetSelector.xml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml deleted file mode 100644 index d0b0930..0000000 --- a/.idea/deploymentTargetSelector.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file From c38c08724c0598026bbd2a0fa8588b46a6cdfe00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 30 Jan 2026 23:13:03 +0000 Subject: [PATCH 049/162] Add `name` property to `Desert` --- .idea/artifacts/composeApp_js.xml | 4 +--- .idea/artifacts/composeApp_jvm.xml | 4 +--- .idea/artifacts/composeApp_wasm_js.xml | 4 +--- .idea/modules.xml | 22 ------------------- .idea/vcs.xml | 2 +- .../backend/entities/DesertDatabaseEntity.kt | 4 +++- .../mokegyesulet/it/dunestrat/model/Desert.kt | 7 +++++- 7 files changed, 13 insertions(+), 34 deletions(-) diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml index 00ef8a3..54559a3 100644 --- a/.idea/artifacts/composeApp_js.xml +++ b/.idea/artifacts/composeApp_js.xml @@ -1,8 +1,6 @@ $PROJECT_DIR$/composeApp/build/libs - - - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml index 01d03af..df75785 100644 --- a/.idea/artifacts/composeApp_jvm.xml +++ b/.idea/artifacts/composeApp_jvm.xml @@ -1,8 +1,6 @@ $PROJECT_DIR$/composeApp/build/libs - - - + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml index 0bd6a39..2c0f6b1 100644 --- a/.idea/artifacts/composeApp_wasm_js.xml +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -1,8 +1,6 @@ $PROJECT_DIR$/composeApp/build/libs - - - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 9e16242..cfb8f3f 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,30 +2,8 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt index f618a2e..dea2feb 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -7,11 +7,12 @@ import kotlinx.serialization.json.Json @Serializable class DesertDatabaseEntity( val id: Int?, + val name: String, val json: String, ) { fun toDesert(): Desert { val desert = Json.decodeFromString(json) - return desert.copy(id = id) + return desert.copy(id = id, name = name) } companion object { @@ -21,5 +22,6 @@ class DesertDatabaseEntity( fun Desert.toDatabaseEntity() = DesertDatabaseEntity( id = id, + name = name, json = Json.encodeToString(this), ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 5186672..7594852 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -8,6 +8,8 @@ import kotlinx.serialization.Transient data class Desert( @Transient val id: Int? = null, + @Transient + val name: String = "", val fields: Set, ) { @@ -388,7 +390,10 @@ data class Desert( ) } - return Desert(fields = newFields) + return Desert( + name = "Standard 12 játékos", + fields = newFields, + ) } fun convertCoordinateToNewSystem( From bf0afded2751fe8c637e6448d3000d74d806aa79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 30 Jan 2026 23:30:05 +0000 Subject: [PATCH 050/162] Only track external dependencies from .idea folder --- .gitignore | 12 ++---------- .idea/AndroidProjectSystem.xml | 6 ------ .idea/artifacts/composeApp_js.xml | 6 ------ .idea/artifacts/composeApp_jvm.xml | 6 ------ .idea/artifacts/composeApp_wasm_js.xml | 6 ------ .idea/compiler.xml | 6 ------ .idea/copilot.data.migration.agent.xml | 6 ------ .idea/copilot.data.migration.ask.xml | 6 ------ .idea/copilot.data.migration.ask2agent.xml | 6 ------ .idea/copilot.data.migration.edit.xml | 6 ------ .idea/modules.xml | 9 --------- .idea/vcs.xml | 6 ------ 12 files changed, 2 insertions(+), 79 deletions(-) delete mode 100644 .idea/AndroidProjectSystem.xml delete mode 100644 .idea/artifacts/composeApp_js.xml delete mode 100644 .idea/artifacts/composeApp_jvm.xml delete mode 100644 .idea/artifacts/composeApp_wasm_js.xml delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copilot.data.migration.agent.xml delete mode 100644 .idea/copilot.data.migration.ask.xml delete mode 100644 .idea/copilot.data.migration.ask2agent.xml delete mode 100644 .idea/copilot.data.migration.edit.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 07874ae..98d5e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,16 +18,8 @@ captures node_modules/ # IntelliJ files -.idea/workspace.xml -.idea/usage.statistics.xml -.idea/shelf/ -.idea/*.iml -.idea/gradle.xml -.idea/ktlint-plugin.xml -.idea/misc.xml -.idea/runConfigurations.xml -/.idea/deploymentTargetSelector.xml -.idea/libraries/ +.idea/ +!.idea/externalDependencies.xml # Nix .direnv diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml deleted file mode 100644 index 4a53bee..0000000 --- a/.idea/AndroidProjectSystem.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml deleted file mode 100644 index 54559a3..0000000 --- a/.idea/artifacts/composeApp_js.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml deleted file mode 100644 index df75785..0000000 --- a/.idea/artifacts/composeApp_jvm.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml deleted file mode 100644 index 2c0f6b1..0000000 --- a/.idea/artifacts/composeApp_wasm_js.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b86273d..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml deleted file mode 100644 index 4ea72a9..0000000 --- a/.idea/copilot.data.migration.agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml deleted file mode 100644 index 7ef04e2..0000000 --- a/.idea/copilot.data.migration.ask.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml deleted file mode 100644 index 1f2ea11..0000000 --- a/.idea/copilot.data.migration.ask2agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml deleted file mode 100644 index 8648f94..0000000 --- a/.idea/copilot.data.migration.edit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index cfb8f3f..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 1ac484200cdf280b29443b24cfd20729cbfdc930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 16:02:39 +0000 Subject: [PATCH 051/162] Use the same set of fields for calculating neighbours --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 7594852..35da485 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -321,7 +321,7 @@ data class Desert( values.first }, spice = if (id in mountains.keys) { - mountains[id]!! + 0 } else { values.second }, @@ -347,10 +347,7 @@ data class Desert( fields.add(desertField) } - val newFields = mutableSetOf() - fields.forEach { field -> - val neighbours = mutableSetOf() val fieldFirstCord = field.id[0].code val fieldSecondCord = field.id.substring(1).toInt() @@ -359,7 +356,7 @@ data class Desert( val neighbourSecondCords = fieldSecondCord - 1..fieldSecondCord + 1 - fields.filter { entry -> + val neighbours = fields.filter { entry -> (entry.id[0].code in neighbourFirstCords) and (entry.id.substring(1).toInt() in neighbourSecondCords) and !( @@ -374,25 +371,14 @@ data class Desert( (entry.id.substring(1).toInt() == fieldSecondCord) ) ) - }.forEach { entry -> - neighbours.add(entry) } - newFields.add( - DesertField( - field.id, - field.water, - field.spice, - field.effectiveWeapon, - neighbours, - field.startingField, - ), - ) + field.neighbours.addAll(neighbours) } return Desert( name = "Standard 12 játékos", - fields = newFields, + fields = fields, ) } From bd5808ca90fef76559fcb2b6362e84a9b4304ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 15:51:55 +0000 Subject: [PATCH 052/162] Only encode fieldIds on db side Also, never encodes the default id values so they can be auto incremented in the db. --- .../backend/entities/DesertDatabaseEntity.kt | 62 ++++++++++++++++-- .../backend/entities/GameDatabaseEntity.kt | 4 +- .../entities/GameStateDatabaseEntity.kt | 65 ++++++++++++++++++- .../entities/PlayerStepDatabaseEntity.kt | 4 +- .../mokegyesulet/it/dunestrat/model/Desert.kt | 5 +- .../mokegyesulet/it/dunestrat/model/Game.kt | 2 +- .../it/dunestrat/model/GameState.kt | 5 +- .../it/dunestrat/model/GameStateField.kt | 2 +- .../it/dunestrat/model/PlayerStep.kt | 2 +- 9 files changed, 133 insertions(+), 18 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt index dea2feb..0a1dd92 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -1,18 +1,42 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.DesertField +import hu.mokegyesulet.it.dunestrat.model.Weapon +import kotlinx.serialization.EncodeDefault import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable class DesertDatabaseEntity( - val id: Int?, + @EncodeDefault(EncodeDefault.Mode.NEVER) + val id: Int = -1, val name: String, - val json: String, + val fieldJsons: List, ) { fun toDesert(): Desert { - val desert = Json.decodeFromString(json) - return desert.copy(id = id, name = name) + val fieldsWithIdList = fieldJsons.map { Json.decodeFromString(it) } + + val fields = mutableMapOf() + fieldsWithIdList.forEach { fieldWithIdList -> + fields[fieldWithIdList.id] = fieldWithIdList.toDesertField() + } + + fieldsWithIdList.forEach { fieldWithIdList -> + val field = fields[fieldWithIdList.id]!! + fieldWithIdList.neighboursId.forEach { neighbourId -> + val neighbourField = fields[neighbourId] + if (neighbourField != null) { + field.neighbours.add(neighbourField) + } + } + } + + return Desert( + id = id, + name = name, + fields = fields.values.toSet(), + ) } companion object { @@ -23,5 +47,33 @@ class DesertDatabaseEntity( fun Desert.toDatabaseEntity() = DesertDatabaseEntity( id = id, name = name, - json = Json.encodeToString(this), + fieldJsons = fields.map { Json.encodeToString(it.toDesertFieldWithIdList()) }, +) + +@Serializable +class DesertFieldWithIdList( + val id: String, + val water: Int, + val spice: Int, + val effectiveWeapon: Weapon, + val neighboursId: List, + val startingField: Boolean, +) { + fun toDesertField() = DesertField( + id = id, + water = water, + spice = spice, + effectiveWeapon = effectiveWeapon, + neighbours = mutableSetOf(), + startingField = startingField, + ) +} + +fun DesertField.toDesertFieldWithIdList() = DesertFieldWithIdList( + id = id, + water = water, + spice = spice, + effectiveWeapon = effectiveWeapon, + neighboursId = neighbours.map { it.id }, + startingField = startingField, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt index 8c6ba9f..27947a5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt @@ -2,13 +2,15 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameProgress +import kotlinx.serialization.EncodeDefault import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable class GameDatabaseEntity( - val id: Int?, + @EncodeDefault(EncodeDefault.Mode.NEVER) + val id: Int = -1, val progress: Int, @SerialName("desert_id") val desertId: Int, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index a1e1d40..7466a5b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -1,21 +1,53 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.GameStateField +import hu.mokegyesulet.it.dunestrat.model.Weapon +import kotlinx.serialization.EncodeDefault import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable class GameStateDatabaseEntity( - val id: Int?, + @EncodeDefault(EncodeDefault.Mode.NEVER) + val id: Int = -1, @SerialName("game_id") val gameId: Int, val index: Int, + @SerialName("field_jsons") + val fieldJsons: List, val json: String, ) { fun toGameState(): GameState { val gameState = Json.decodeFromString(json) - return gameState.copy(id = id, gameId = gameId, index = index) + + val fieldsWithIdList = fieldJsons.map { fieldJson -> + Json.decodeFromString(fieldJson) + } + + val fields = mutableMapOf() + + fieldsWithIdList.forEach { fieldWithIdList -> + fields[fieldWithIdList.id] = fieldWithIdList.toGameStateField() + } + + fieldsWithIdList.forEach { fieldWithIdList -> + val field = fields[fieldWithIdList.id]!! + fieldWithIdList.neighboursId.forEach { neighbourId -> + val neighbourField = fields[neighbourId] + if (neighbourField != null) { + field.neighbours.add(neighbourField) + } + } + } + + return gameState.copy( + id = id, + gameId = gameId, + index = index, + fields = fields.values.toSet(), + ) } companion object { const val TABLE_NAME = "game_state" @@ -26,5 +58,34 @@ fun GameState.toDatabaseEntry(): GameStateDatabaseEntity = GameStateDatabaseEnti id = id, gameId = gameId, index = index, + fieldJsons = fields.map { Json.encodeToString(it.toGameStateFieldWithIdList()) }, json = Json.encodeToString(this), ) + +@Serializable +class GameStateFieldWithIdList( + val id: String, + val water: Int, + val spice: Int, + val effectiveWeapon: Weapon, + val harvester: Boolean, + val neighboursId: List, +) { + fun toGameStateField() = GameStateField( + id = id, + water = water, + spice = spice, + effectiveWeapon = effectiveWeapon, + harvester = harvester, + neighbours = mutableSetOf(), + ) +} + +fun GameStateField.toGameStateFieldWithIdList() = GameStateFieldWithIdList( + id = id, + water = water, + spice = spice, + effectiveWeapon = effectiveWeapon, + harvester = harvester, + neighboursId = neighbours.map { it.id }, +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index 91a6a2e..a10b2c3 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -1,13 +1,15 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import kotlinx.serialization.EncodeDefault import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable class PlayerStepDatabaseEntity( - val id: Int?, + @EncodeDefault(EncodeDefault.Mode.NEVER) + val id: Int = -1, @SerialName("game_state_id") val gameStateId: Int, val json: String, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 35da485..861f1b0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -4,11 +4,8 @@ import kotlin.math.abs import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -@Serializable data class Desert( - @Transient - val id: Int? = null, - @Transient + val id: Int = -1, val name: String = "", val fields: Set, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt index a71871e..855a562 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Game.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Transient @Serializable data class Game( @Transient - val id: Int? = null, + val id: Int = -1, val name: String, @Transient val progress: GameProgress = GameProgress.ERROR, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 24422af..8927719 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -7,12 +7,13 @@ import kotlinx.serialization.Transient @Serializable data class GameState( @Transient - val id: Int? = null, + val id: Int = -1, @Transient val gameId: Int = -1, @Transient val index: Int = -1, - val fields: Set, + @Transient + val fields: Set = setOf(), val players: Set, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index ad71f80..02e5ac7 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -9,5 +9,5 @@ data class GameStateField( val spice: Int, val effectiveWeapon: Weapon, var harvester: Boolean, - val neighbours: Set, + val neighbours: MutableSet, ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index d6dfebf..f79c84f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Transient @Serializable data class PlayerStep( @Transient - val id: Int? = null, + val id: Int = -1, @Transient val gameStateId: Int = -1, val playerId: String, From 0cf76ba07f485d18b2682df7fbeee001e8238bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 15:53:07 +0000 Subject: [PATCH 053/162] Remove unused imports --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 861f1b0..82e9e43 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -1,8 +1,6 @@ package hu.mokegyesulet.it.dunestrat.model import kotlin.math.abs -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient data class Desert( val id: Int = -1, From a8415de6305348eb1742f3468a64bf7002d035be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 15:54:24 +0000 Subject: [PATCH 054/162] Use correct table for GameStates in db --- .../it/dunestrat/backend/SubabaseRepository.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt index 066116e..6a63fe6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt @@ -1,11 +1,6 @@ package hu.mokegyesulet.it.dunestrat.backend -import hu.mokegyesulet.it.dunestrat.backend.entities.DesertDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.GameDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.PlayerStepDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntity -import hu.mokegyesulet.it.dunestrat.backend.entities.toDatabaseEntry +import hu.mokegyesulet.it.dunestrat.backend.entities.* import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameState @@ -19,7 +14,7 @@ import kotlinx.coroutines.flow.map object SubabaseRepository : Repository { val supabase = SupabaseClientProvider.client const val GAME_TABLE = GameDatabaseEntity.TABLE_NAME - const val GAME_STATE_TABLE = GameDatabaseEntity.TABLE_NAME + const val GAME_STATE_TABLE = GameStateDatabaseEntity.TABLE_NAME const val STEP_TABLE = PlayerStepDatabaseEntity.TABLE_NAME const val DESERT_TABLE = DesertDatabaseEntity.TABLE_NAME From cb1a3a1966784e8ff5e7c5688383fed45b1bfd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 15:55:29 +0000 Subject: [PATCH 055/162] Fix typo --- .../backend/{SubabaseRepository.kt => SupabaseRepository.kt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/{SubabaseRepository.kt => SupabaseRepository.kt} (98%) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt similarity index 98% rename from composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt rename to composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 6a63fe6..8b257ee 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SubabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -11,7 +11,7 @@ import io.github.jan.supabase.realtime.selectAsFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -object SubabaseRepository : Repository { +object SupabaseRepository : Repository { val supabase = SupabaseClientProvider.client const val GAME_TABLE = GameDatabaseEntity.TABLE_NAME const val GAME_STATE_TABLE = GameStateDatabaseEntity.TABLE_NAME From eb3276a5fa4aed126716f8f024b916877844a341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 16:10:09 +0000 Subject: [PATCH 056/162] Add SerialName to fieldJsons field --- .../it/dunestrat/backend/entities/DesertDatabaseEntity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt index 0a1dd92..311eb97 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -4,6 +4,7 @@ import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.DesertField import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlinx.serialization.EncodeDefault +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -12,6 +13,7 @@ class DesertDatabaseEntity( @EncodeDefault(EncodeDefault.Mode.NEVER) val id: Int = -1, val name: String, + @SerialName("field_jsons") val fieldJsons: List, ) { fun toDesert(): Desert { From c6e66acb12b807d95ea490f065c38f1615c1c54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 16:22:19 +0000 Subject: [PATCH 057/162] Add not-null assertion to neighbour fields --- .../it/dunestrat/backend/entities/DesertDatabaseEntity.kt | 6 ++---- .../dunestrat/backend/entities/GameStateDatabaseEntity.kt | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt index 311eb97..8849495 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/DesertDatabaseEntity.kt @@ -27,10 +27,8 @@ class DesertDatabaseEntity( fieldsWithIdList.forEach { fieldWithIdList -> val field = fields[fieldWithIdList.id]!! fieldWithIdList.neighboursId.forEach { neighbourId -> - val neighbourField = fields[neighbourId] - if (neighbourField != null) { - field.neighbours.add(neighbourField) - } + val neighbourField = fields[neighbourId]!! + field.neighbours.add(neighbourField) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index 7466a5b..6ce5612 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -35,10 +35,8 @@ class GameStateDatabaseEntity( fieldsWithIdList.forEach { fieldWithIdList -> val field = fields[fieldWithIdList.id]!! fieldWithIdList.neighboursId.forEach { neighbourId -> - val neighbourField = fields[neighbourId] - if (neighbourField != null) { - field.neighbours.add(neighbourField) - } + val neighbourField = fields[neighbourId]!! + field.neighbours.add(neighbourField) } } From b7649d5c250ac75a25edc860ac73e3c1604a033d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 8 Feb 2026 23:05:50 +0100 Subject: [PATCH 058/162] Screen for creating and viewing test data, minor fixes --- composeApp/build.gradle.kts | 3 +- .../it/dunestrat/backend/MemoryRepository.kt | 32 ---- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 7 + .../feature/testdata/TestDataScreen.kt | 61 +++++++ .../feature/testdata/TestDataViewModel.kt | 150 ++++++++++++++++++ .../mokegyesulet/it/dunestrat/model/Desert.kt | 89 +++++++++++ .../it/dunestrat/model/GameStateField.kt | 42 ++++- .../mokegyesulet/it/dunestrat/model/Player.kt | 4 +- .../it/dunestrat/navigation/NavGraph.kt | 4 + .../it/dunestrat/navigation/Screen.kt | 1 + gradle/libs.versions.toml | 3 +- 11 files changed, 359 insertions(+), 37 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index ceef75f..e3926ae 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -56,6 +56,7 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) + implementation(libs.kotlinx.coroutines.core) implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) implementation(libs.navigation.compose) @@ -75,7 +76,7 @@ kotlin { } jvmMain.dependencies { implementation(compose.desktop.currentOs) - implementation(libs.kotlinx.coroutinesSwing) + implementation(libs.kotlinx.coroutines.swing) implementation(libs.ktor.client.cio) } jsMain { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt deleted file mode 100644 index 7458662..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/MemoryRepository.kt +++ /dev/null @@ -1,32 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.backend - -import hu.mokegyesulet.it.dunestrat.model.Game -import hu.mokegyesulet.it.dunestrat.model.GameState -import hu.mokegyesulet.it.dunestrat.model.PlayerStep -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -object MemoryRepository : Repository { - private val game = MutableStateFlow>(emptyList()) - private val gameState = MutableStateFlow>(emptyList()) - private val playerStep = MutableStateFlow>(emptyList()) - - override fun getGames(): Flow> = game.asStateFlow() - - override suspend fun saveGame(game: Game) { - this.game.value += game - } - - override fun getGameStates(): Flow> = gameState.asStateFlow() - - override suspend fun saveGameState(gameState: GameState) { - this.gameState.value += gameState - } - - override fun getPlayerSteps(): Flow> = playerStep.asStateFlow() - - override suspend fun savePlayerStep(playerStep: PlayerStep) { - this.playerStep.value += playerStep - } -} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 51f3a2f..6382e79 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -16,6 +16,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel fun MainMenu( onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit, + onTestData: () -> Unit, ) { val viewModel: MainMenuViewModel = viewModel { MainMenuViewModel() } val loggedIn by viewModel.isLoggedIn.collectAsState(initial = false) @@ -37,6 +38,12 @@ fun MainMenu( Text(text = "Placeholder 2") } + Button( + onClick = onTestData, + ) { + Text(text = "Test data") + } + Button( onClick = viewModel::onLogout, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt new file mode 100644 index 0000000..b08d55b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt @@ -0,0 +1,61 @@ +package hu.mokegyesulet.it.dunestrat.feature.testdata + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel + +@Composable +fun TestDataScreen() { + val viewModel = viewModel { TestDataViewModel() } + val deserts by viewModel.deserts.collectAsStateWithLifecycle() + val games by viewModel.games.collectAsStateWithLifecycle() + val gameStates by viewModel.gameStates.collectAsStateWithLifecycle() + Column { + Text( + text = "Deserts", + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.headlineMedium, + ) + LazyColumn(modifier = Modifier.weight(1f)) { + items(deserts) { + Text(text = it.id.toString()) + } + } + Text( + text = "Games", + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.headlineMedium, + ) + LazyColumn(modifier = Modifier.weight(1f)) { + items(games) { + Text(text = it.id.toString()) + } + } + Text( + text = "Game states", + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.headlineMedium, + ) + LazyColumn(modifier = Modifier.weight(1f)) { + items(gameStates) { + Text(text = it.id.toString()) + } + } + Button(onClick = { viewModel.initDesert() }) { Text("Init test desert") } + Button(onClick = { viewModel.initGame(deserts[0]) }) { Text("Init test game") } + Button(onClick = { + viewModel.initGameState(deserts[0], games[0]) + }) { Text("Init test gamestate") } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt new file mode 100644 index 0000000..d724c78 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -0,0 +1,150 @@ +package hu.mokegyesulet.it.dunestrat.feature.testdata + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository +import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameProgress +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.GameStateField +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Student +import hu.mokegyesulet.it.dunestrat.model.Team +import hu.mokegyesulet.it.dunestrat.model.Weapon +import hu.mokegyesulet.it.dunestrat.model.toGameStateFields +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +class TestDataViewModel : ViewModel() { + + val deserts = SupabaseRepository.getDeserts() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) + + val games = SupabaseRepository.getGames() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) + + val gameStates = SupabaseRepository.getGameStates() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) + + private var players: List + + init { + players = listOf( + initPlayer("1"), + initPlayer("2"), + initPlayer("3"), + initPlayer("4"), + initPlayer("5"), + initPlayer("6"), + initPlayer("7"), + initPlayer("8"), + initPlayer("9"), + initPlayer("10"), + initPlayer("11"), + initPlayer("12"), + ) + viewModelScope.launch { + games.collect { list -> + println("Games from Supabase: $list") + } + } + viewModelScope.launch { + deserts.collect { list -> + println("Deserts from Supabase: $list") + } + } + viewModelScope.launch { + gameStates.collect { list -> + println("Game states from Supabase: $list") + } + } + } + + fun initDesert() { + val test = Desert.create6PlayerTestHexagon() + viewModelScope.launch { + SupabaseRepository.saveDesert(test) + } + } + + fun initGame(desert: Desert) { + val playerCount = desert.fields.filter { it.startingField }.size + players = players.take(playerCount) + val names = listOf( + "Példa Aladár", + "Példa Béla", + "Példa Csanád", + "Példa Dániel", + "Példa Ezékiel", + "Példa Ferenc", + "Példa Gergely", + "Példa Hilbert", + "Példa Iván", + "Példa Jakab", + "Példa Károly", + "Példa Lajos", + ) + val teams = (0.. + Team(players[i].id, listOf(Student(names[i]))) + } + viewModelScope.launch { + val game = Game( + name = "Teszt játék", + progress = GameProgress.INITIALIZED, + teams = teams, + desertId = desert.id, + ) + SupabaseRepository.saveGame(game) + } + } + + fun initGameState( + desert: Desert, + game: Game, + ) { + val stateFields = desert.fields.toGameStateFields() + randomlyAssignPlayers(desert, stateFields) + val firstState = GameState( + 0, + game.id, + 0, + stateFields, + players.toSet(), + ) + viewModelScope.launch { + SupabaseRepository.saveGameState(firstState) + } + } + + private fun initPlayer(id: String): Player = Player( + id, + 0, + 0, + 0, + mutableMapOf( + Weapon.CRYSKNIFE to 0, + Weapon.PISTOL to 0, + Weapon.LASGUN to 0, + Weapon.LEGION to 0, + ), + mutableSetOf(), + ) + private fun randomlyAssignPlayers( + desert: Desert, + fields: Set, + ) { + val startingFields = desert.fields.filter { it.startingField }.map { it.id }.shuffled() + if (startingFields.size != players.size) { + throw RuntimeException( + "Start field count and player count mismatched." + + " Players: ${players.size}, starting fields: ${startingFields.size} \n" + + "Starting fields: ${startingFields.joinToString(", ")}", + ) + } + players.zip(startingFields).forEach { (player, coordinate) -> + player.ownedFields.add(fields.first { it.id == coordinate }) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 82e9e43..80c8379 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -390,5 +390,94 @@ data class Desert( val newNumber = number + shift return "$letter$newNumber" } + + fun create6PlayerTestHexagon(): Desert { + val outer: ( + String, + ) -> DesertField = { id -> + DesertField( + id, + 5, + 0, + Weapon.LASGUN, + mutableSetOf(), + true, + ) + } + val inner: ( + String, + ) -> DesertField = { id -> + DesertField( + id, + -2, + 5, + Weapon.PISTOL, + mutableSetOf(), + false, + ) + } + val fields = mutableSetOf() + val center = DesertField( + "C3", + -5, + 15, + Weapon.CRYSKNIFE, + mutableSetOf(), + false, + ) + fields.add(center) + listOf("B2", "C2", "D3", "D4", "C4", "B3").forEach { id -> + val f = inner(id) + val prev = fields.last() + prev.neighbours.add(f) + f.neighbours.add(prev) + fields.add(f) + f.neighbours.add(center) + center.neighbours.add(f) + if (id == "B3") { + val b2 = fields.first { it.id == "B2" } + f.neighbours.add(b2) + b2.neighbours.add(f) + } + } + val b2 = fields.first { it.id == "B2" } + val c2 = fields.first { it.id == "C2" } + val d3 = fields.first { it.id == "D3" } + val d4 = fields.first { it.id == "D4" } + val c4 = fields.first { it.id == "C4" } + val b3 = fields.first { it.id == "B3" } + val b1 = outer("B1") + val d2 = outer("D2") + val e4 = outer("E4") + val d5 = outer("D5") + val b4 = outer("B4") + val a2 = outer("A2") + fields.addAll(listOf(b1, d2, e4, d5, b4, a2)) + b1.neighbours.add(b2) + b2.neighbours.add(b1) + b1.neighbours.add(c2) + c2.neighbours.add(b1) + d2.neighbours.add(c2) + c2.neighbours.add(d2) + d2.neighbours.add(d3) + d3.neighbours.add(d2) + e4.neighbours.add(d3) + d3.neighbours.add(e4) + e4.neighbours.add(d4) + d4.neighbours.add(e4) + d5.neighbours.add(d4) + d4.neighbours.add(d5) + d5.neighbours.add(c4) + c4.neighbours.add(d5) + b4.neighbours.add(c4) + c4.neighbours.add(b4) + b4.neighbours.add(b3) + b3.neighbours.add(b4) + a2.neighbours.add(b3) + b3.neighbours.add(a2) + a2.neighbours.add(b2) + b2.neighbours.add(a2) + return Desert(fields = fields, name = "Teszt (6 játékos)") + } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index 02e5ac7..fa27109 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -3,11 +3,49 @@ package hu.mokegyesulet.it.dunestrat.model import kotlinx.serialization.Serializable @Serializable -data class GameStateField( +class GameStateField( val id: String, val water: Int, val spice: Int, val effectiveWeapon: Weapon, var harvester: Boolean, val neighbours: MutableSet, -) +) { + fun copy( + id: String = this.id, + water: Int = this.water, + spice: Int = this.spice, + effectiveWeapon: Weapon = this.effectiveWeapon, + harvester: Boolean = this.harvester, + neighbours: Set = this.neighbours, + ): GameStateField = GameStateField( + id, + water, + spice, + effectiveWeapon, + harvester, + neighbours.toMutableSet(), + ) +} +fun Set.toGameStateFields(): Set { + val gameStateFields = this.map { + GameStateField( + it.id, + it.water, + it.spice, + it.effectiveWeapon, + false, + mutableSetOf(), + ) + }.toSet() + for (stateField in gameStateFields) { + val neighbors = + this + .first { it.id == stateField.id } + .neighbours + .map { neighbor -> gameStateFields.first({ it.id == neighbor.id }) } + .toSet() + stateField.neighbours.addAll(neighbors) + } + return gameStateFields +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 5b39c0c..39acc5f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -3,6 +3,7 @@ package hu.mokegyesulet.it.dunestrat.model import kotlin.collections.forEach import kotlin.math.pow import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient @Serializable data class Player( @@ -12,7 +13,8 @@ data class Player( var harvestersPurchased: Int, private val weapons: MutableMap, val ownedFields: MutableSet, - var inDebt: Boolean, + @Transient + var inDebt: Boolean = false, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 7ec7719..6fe9b0f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu +import hu.mokegyesulet.it.dunestrat.feature.testdata.TestDataScreen @Composable fun NavGraph(navController: NavHostController = rememberNavController()) { @@ -17,7 +18,10 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { MainMenu( onPlaceholder1 = {}, onPlaceholder2 = {}, + onTestData = { navController.navigate(Screen.TestData.route) }, ) } + + composable(Screen.TestData.route) { TestDataScreen() } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 7488c3a..bcf9862 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -4,4 +4,5 @@ sealed class Screen( val route: String, ) { data object MainMenu : Screen(route = "main-menu") + data object TestData : Screen(route = "test-data") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ffe9565..55299ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,8 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } -kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } supabase-auth = { module = "io.github.jan-tennert.supabase:auth-kt"} supabase-auth-compose = { module = "io.github.jan-tennert.supabase:compose-auth"} supabase-postgrest = { module = "io.github.jan-tennert.supabase:postgrest-kt"} From 9fe027382e64ccb6c45a5467202155868406e229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 19:50:01 +0000 Subject: [PATCH 059/162] Fix desert --- .../kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 80c8379..4b9c58f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -299,11 +299,9 @@ data class Desert( for (x in -size..size) { for (y in -size * 2..size * 2) { - val distance = abs(x) + abs(y) - if (distance % 2 == 0 && distance / 2 <= size) { - val stepsToReach = - abs(x) + if (abs(y) > abs(x)) (abs(y) - abs(x)) / 2 else 0 - + val stepsToReach = + abs(x) + if (abs(y) > abs(x)) (abs(y) - abs(x)) / 2 else 0 + if (stepsToReach <= size) { val values = desertValuesByDistance[stepsToReach] ?: Pair(0, 0) val id = "${'B' + x + size}${(y + (size * 2) + 2) / 2}" From d855ba8da259c58d40f9442c6496716213c8a915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 21:36:55 +0000 Subject: [PATCH 060/162] Fixed shape WIP --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 251 +++++++++++++++--- 1 file changed, 214 insertions(+), 37 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 4b9c58f..95abfbc 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -1,7 +1,5 @@ package hu.mokegyesulet.it.dunestrat.model -import kotlin.math.abs - data class Desert( val id: Int = -1, val name: String = "", @@ -295,49 +293,228 @@ data class Desert( convertCoordinateToNewSystem(it.key) } - val size = 7 + val values = mapOf( + "A3" to Pair(3, 0), + "A7" to Pair(3, 0), + "B2" to Pair(7, 0), + "B4" to Pair(-1, 1), + + "B5" to Pair(-1, 1), + "B6" to Pair(-1, 1), + "B8" to Pair(-1, 1), + + "B9" to Pair(7, 0), + "C1" to Pair(3, 0), + "C3" to Pair(-2, 2), + + "C4" to Pair(-2, 2), + "C5" to Pair(6, 0), + "C7" to Pair(6, 0), + + "C8" to Pair(-2, 2), + "C9" to Pair(-2, 2), + "C11" to Pair(3, 0), + + "D2" to Pair(-1, 1), + "D3" to Pair(-2, 2), + "D5" to Pair(-3, 5), + + "D6" to Pair(5, 0), + "D7" to Pair(5, 0), + "D9" to Pair(-3, 5), + + "D10" to Pair(-2, 2), + "D11" to Pair(-1, 1), + "E3" to Pair(6, 0), + + "E4" to Pair(-3, 5), + "E5" to Pair(-4, 11), + "E7" to Pair(-4, 11), + + "E8" to Pair(-4, 11), + "E9" to Pair(-4, 11), + "E11" to Pair(6, 0), + + "E12" to Pair(-1, 1), + "F2" to Pair(-1, 1), + "F4" to Pair(5, 0), + + "F5" to Pair(-4, 11), + "F6" to Pair(3, 0), + "F8" to Pair(-5, 19), + + "F9" to Pair(3, 0), + "F10" to Pair(-4, 11), + "F12" to Pair(6, 0), + + "F13" to Pair(-1, 1), + "G1" to Pair(3, 0), + "G3" to Pair(6, 0), + + "G4" to Pair(5, 0), + "G5" to Pair(-4, 11), + "G7" to Pair(-6, 29), + + "G8" to Pair(-6, 29), + "G9" to Pair(-6, 29), + "G11" to Pair(-4, 11), + + "G12" to Pair(5, 0), + "G13" to Pair(6, 0), + "G15" to Pair(3, 0), + + "H2" to Pair(-1, 1), + "H3" to Pair(-2, 2), + "H5" to Pair(-4, 11), + + "H6" to Pair(-5, 19), + "H7" to Pair(-6, 29), + "H9" to Pair(-7, 37), + + "H10" to Pair(-6, 29), + "H11" to Pair(-5, 19), + "H13" to Pair(-3, 5), + + "H14" to Pair(-2, 2), + "H15" to Pair(-1, 1), + "I3" to Pair(-2, 2), + + "I4" to Pair(-3, 5), + "I5" to Pair(-4, 11), + "I7" to Pair(-6, 29), + + "I8" to Pair(-7, 37), + "I9" to Pair(-8, 40), + "I11" to Pair(-6, 29), + + "I12" to Pair(3, 0), + "I13" to Pair(-4, 11), + "I15" to Pair(-2, 2), + + "I16" to Pair(7, 0), + "J2" to Pair(-1, 1), + "J4" to Pair(-3, 5), + + "J5" to Pair(-4, 11), + "J6" to Pair(-5, 19), + "J8" to Pair(-7, 37), + + "J9" to Pair(-7, 37), + "J10" to Pair(-6, 29), + "J12" to Pair(-4, 11), + + "J13" to Pair(-3, 5), + "J14" to Pair(-2, 2), + "K1" to Pair(3, 0), - for (x in -size..size) { - for (y in -size * 2..size * 2) { - val stepsToReach = - abs(x) + if (abs(y) > abs(x)) (abs(y) - abs(x)) / 2 else 0 - if (stepsToReach <= size) { - val values = desertValuesByDistance[stepsToReach] ?: Pair(0, 0) + "K2" to Pair(-1, 1), + "K3" to Pair(6, 0), + "K5" to Pair(-4, 11), - val id = "${'B' + x + size}${(y + (size * 2) + 2) / 2}" + "K6" to Pair(-5, 19), + "K7" to Pair(-6, 29), + "K9" to Pair(-6, 29), - val desertField = DesertField( + "K10" to Pair(-5, 19), + "K11" to Pair(-4, 11), + "K13" to Pair(6, 0), + + "K14" to Pair(-1, 1), + "K15" to Pair(3, 0), + "L3" to Pair(6, 0), + + "L4" to Pair(5, 0), + "L5" to Pair(-4, 11), + "L7" to Pair(-5, 19), + + "L8" to Pair(-5, 19), + "L9" to Pair(3, 0), + "L11" to Pair(5, 0), + + "L12" to Pair(6, 0), + "L13" to Pair(-1, 1), + "M3" to Pair(6, 0), + + "M4" to Pair(-3, 5), + "M5" to Pair(-4, 11), + "M7" to Pair(-4, 11), + + "M8" to Pair(-4, 11), + "M9" to Pair(-4, 11), + "M11" to Pair(6, 0), + + "M12" to Pair(-1, 1), + "N2" to Pair(-1, 1), + "N4" to Pair(-3, 5), + + "N5" to Pair(-3, 5), + "N6" to Pair(5, 0), + "N8" to Pair(-3, 5), + + "N9" to Pair(-3, 5), + "N10" to Pair(-2, 2), + "O1" to Pair(3, 0), + + "O2" to Pair(-1, 1), + "O3" to Pair(-2, 2), + "O5" to Pair(6, 0), + + "O6" to Pair(6, 0), + "O7" to Pair(6, 0), + "O9" to Pair(-2, 2), + + "O10" to Pair(-1, 1), + "O11" to Pair(3, 0), + "P3" to Pair(-1, 1), + + "P4" to Pair(-1, 1), + "P5" to Pair(-1, 1), + "P7" to Pair(-1, 1), + + "P8" to Pair(-1, 1), + "P9" to Pair(7, 0), + "Q7" to Pair(3, 0), + ) + + + effectiveWeaponsById.forEach { (id, weapon) -> + if( id in bases ){ + fields.add( + DesertField( id = id, - water = if (id in mountains.keys) { - mountains[id]!! - } else { - values.first - }, - spice = if (id in mountains.keys) { - 0 - } else { - values.second - }, - effectiveWeapon = effectiveWeaponsById[id] ?: Weapon.LEGION, + water = 4, + spice = 0, + effectiveWeapon = weapon, neighbours = mutableSetOf(), - startingField = id in bases, + startingField = true, ) + ) + } else if( id in mountains.keys ){ + fields.add( + DesertField( + id = id, + water = mountains[id]!!, + spice = 0, + effectiveWeapon = weapon, + neighbours = mutableSetOf(), + startingField = false, + ) + ) + } else { - fields.add(desertField) - } - } - } + val (water, spice) = values[id] ?: Pair(-1, 1) - bases.forEach { id -> - val desertField = DesertField( - id = id, - water = 4, - spice = 0, - effectiveWeapon = effectiveWeaponsById[id]!!, - neighbours = mutableSetOf(), - startingField = true, - ) - fields.add(desertField) + fields.add( + DesertField( + id = id, + water = water, + spice = spice, + effectiveWeapon = weapon, + neighbours = mutableSetOf(), + startingField = false + ) + ) + } } fields.forEach { field -> From a5e3264c03b2e0d453e618aa60758392d008fc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 21:50:25 +0000 Subject: [PATCH 061/162] Spice and water values imported --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 95 ++++++++++--------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 95abfbc..78819bf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -293,189 +293,194 @@ data class Desert( convertCoordinateToNewSystem(it.key) } - val values = mapOf( + val oldValues = mapOf( "A3" to Pair(3, 0), "A7" to Pair(3, 0), "B2" to Pair(7, 0), + "B3" to Pair(-1, 1), "B4" to Pair(-1, 1), - "B5" to Pair(-1, 1), "B6" to Pair(-1, 1), + "B7" to Pair(-1, 1), "B8" to Pair(-1, 1), - "B9" to Pair(7, 0), "C1" to Pair(3, 0), + "C2" to Pair(-1, 1), "C3" to Pair(-2, 2), - "C4" to Pair(-2, 2), "C5" to Pair(6, 0), + "C6" to Pair(6, 0), "C7" to Pair(6, 0), - "C8" to Pair(-2, 2), "C9" to Pair(-2, 2), + "C10" to Pair(-1, 1), "C11" to Pair(3, 0), - "D2" to Pair(-1, 1), "D3" to Pair(-2, 2), + "D4" to Pair(-3, 5), "D5" to Pair(-3, 5), - "D6" to Pair(5, 0), "D7" to Pair(5, 0), + "D8" to Pair(-3, 5), "D9" to Pair(-3, 5), - "D10" to Pair(-2, 2), "D11" to Pair(-1, 1), + "E2" to Pair(-1, 1), "E3" to Pair(6, 0), - "E4" to Pair(-3, 5), "E5" to Pair(-4, 11), + "E6" to Pair(-4, 11), "E7" to Pair(-4, 11), - "E8" to Pair(-4, 11), "E9" to Pair(-4, 11), + "E10" to Pair(-3, 5), "E11" to Pair(6, 0), - "E12" to Pair(-1, 1), "F2" to Pair(-1, 1), + "F3" to Pair(6, 0), "F4" to Pair(5, 0), - "F5" to Pair(-4, 11), "F6" to Pair(3, 0), + "F7" to Pair(-5, 19), "F8" to Pair(-5, 19), - "F9" to Pair(3, 0), "F10" to Pair(-4, 11), + "F11" to Pair(5, 0), "F12" to Pair(6, 0), - "F13" to Pair(-1, 1), "G1" to Pair(3, 0), + "G2" to Pair(-1, 1), "G3" to Pair(6, 0), - "G4" to Pair(5, 0), "G5" to Pair(-4, 11), + "G6" to Pair(-5, 19), "G7" to Pair(-6, 29), - "G8" to Pair(-6, 29), "G9" to Pair(-6, 29), + "G10" to Pair(-5, 19), "G11" to Pair(-4, 11), - "G12" to Pair(5, 0), "G13" to Pair(6, 0), + "G14" to Pair(-1, 1), "G15" to Pair(3, 0), - "H2" to Pair(-1, 1), "H3" to Pair(-2, 2), + "H4" to Pair(-3, 5), "H5" to Pair(-4, 11), - "H6" to Pair(-5, 19), "H7" to Pair(-6, 29), + "H8" to Pair(-7, 37), "H9" to Pair(-7, 37), - "H10" to Pair(-6, 29), "H11" to Pair(-5, 19), + "H12" to Pair(-4, 11), "H13" to Pair(-3, 5), - "H14" to Pair(-2, 2), "H15" to Pair(-1, 1), + "I2" to Pair(7, 0), "I3" to Pair(-2, 2), - "I4" to Pair(-3, 5), "I5" to Pair(-4, 11), + "I6" to Pair(3, 0), "I7" to Pair(-6, 29), - "I8" to Pair(-7, 37), "I9" to Pair(-8, 40), + "I10" to Pair(-7, 37), "I11" to Pair(-6, 29), - "I12" to Pair(3, 0), "I13" to Pair(-4, 11), + "I14" to Pair(-3, 5), "I15" to Pair(-2, 2), - "I16" to Pair(7, 0), "J2" to Pair(-1, 1), + "J3" to Pair(-2, 2), "J4" to Pair(-3, 5), - "J5" to Pair(-4, 11), "J6" to Pair(-5, 19), + "J7" to Pair(-6, 29), "J8" to Pair(-7, 37), - "J9" to Pair(-7, 37), "J10" to Pair(-6, 29), + "J11" to Pair(-5, 19), "J12" to Pair(-4, 11), - "J13" to Pair(-3, 5), "J14" to Pair(-2, 2), + "J15" to Pair(-1, 1), "K1" to Pair(3, 0), - "K2" to Pair(-1, 1), "K3" to Pair(6, 0), + "K4" to Pair(5, 0), "K5" to Pair(-4, 11), - "K6" to Pair(-5, 19), "K7" to Pair(-6, 29), + "K8" to Pair(-6, 29), "K9" to Pair(-6, 29), - "K10" to Pair(-5, 19), "K11" to Pair(-4, 11), + "K12" to Pair(5, 0), "K13" to Pair(6, 0), - "K14" to Pair(-1, 1), "K15" to Pair(3, 0), + "L2" to Pair(-1, 1), "L3" to Pair(6, 0), - "L4" to Pair(5, 0), "L5" to Pair(-4, 11), + "L6" to Pair(3, 0), "L7" to Pair(-5, 19), - "L8" to Pair(-5, 19), "L9" to Pair(3, 0), + "L10" to Pair(-4, 11), "L11" to Pair(5, 0), - "L12" to Pair(6, 0), "L13" to Pair(-1, 1), + "M2" to Pair(-1, 1), "M3" to Pair(6, 0), - "M4" to Pair(-3, 5), "M5" to Pair(-4, 11), + "M6" to Pair(-4, 11), "M7" to Pair(-4, 11), - "M8" to Pair(-4, 11), "M9" to Pair(-4, 11), + "M10" to Pair(-3, 5), "M11" to Pair(6, 0), - "M12" to Pair(-1, 1), "N2" to Pair(-1, 1), + "N3" to Pair(-2, 2), "N4" to Pair(-3, 5), - "N5" to Pair(-3, 5), "N6" to Pair(5, 0), + "N7" to Pair(5, 0), "N8" to Pair(-3, 5), - "N9" to Pair(-3, 5), "N10" to Pair(-2, 2), + "N11" to Pair(-1, 1), "O1" to Pair(3, 0), - "O2" to Pair(-1, 1), "O3" to Pair(-2, 2), + "O4" to Pair(-2, 2), "O5" to Pair(6, 0), - "O6" to Pair(6, 0), "O7" to Pair(6, 0), + "O8" to Pair(-2, 2), "O9" to Pair(-2, 2), - "O10" to Pair(-1, 1), "O11" to Pair(3, 0), + "P2" to Pair(7, 0), "P3" to Pair(-1, 1), - "P4" to Pair(-1, 1), "P5" to Pair(-1, 1), + "P6" to Pair(-1, 1), "P7" to Pair(-1, 1), - "P8" to Pair(-1, 1), "P9" to Pair(7, 0), + "Q3" to Pair(3, 0), "Q7" to Pair(3, 0), ) + val values = oldValues.mapKeys { + convertCoordinateToNewSystem(it.key) + } + effectiveWeaponsById.forEach { (id, weapon) -> if( id in bases ){ From b7c5eaab920a77a477ce13a9a0b452912c63d1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 22:07:52 +0000 Subject: [PATCH 062/162] Fix formatting --- .../hu/mokegyesulet/it/dunestrat/model/Desert.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 78819bf..d7d3409 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -481,9 +481,8 @@ data class Desert( convertCoordinateToNewSystem(it.key) } - effectiveWeaponsById.forEach { (id, weapon) -> - if( id in bases ){ + if (id in bases) { fields.add( DesertField( id = id, @@ -492,9 +491,9 @@ data class Desert( effectiveWeapon = weapon, neighbours = mutableSetOf(), startingField = true, - ) + ), ) - } else if( id in mountains.keys ){ + } else if (id in mountains.keys) { fields.add( DesertField( id = id, @@ -503,10 +502,9 @@ data class Desert( effectiveWeapon = weapon, neighbours = mutableSetOf(), startingField = false, - ) + ), ) } else { - val (water, spice) = values[id] ?: Pair(-1, 1) fields.add( @@ -516,8 +514,8 @@ data class Desert( spice = spice, effectiveWeapon = weapon, neighbours = mutableSetOf(), - startingField = false - ) + startingField = false, + ), ) } } From 4cc182d9f1b231def1ad9e15183c1045bd1ce060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 21:36:55 +0000 Subject: [PATCH 063/162] Fixed shape WIP --- .../mokegyesulet/it/dunestrat/model/Desert.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index d7d3409..39faa32 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -520,6 +520,45 @@ data class Desert( } } + effectiveWeaponsById.forEach { (id, weapon) -> + if (id in bases) { + fields.add( + DesertField( + id = id, + water = 4, + spice = 0, + effectiveWeapon = weapon, + neighbours = mutableSetOf(), + startingField = true, + ), + ) + } else if (id in mountains.keys) { + fields.add( + DesertField( + id = id, + water = mountains[id]!!, + spice = 0, + effectiveWeapon = weapon, + neighbours = mutableSetOf(), + startingField = false, + ), + ) + } else { + val (water, spice) = values[id] ?: Pair(-1, 1) + + fields.add( + DesertField( + id = id, + water = water, + spice = spice, + effectiveWeapon = weapon, + neighbours = mutableSetOf(), + startingField = false, + ), + ) + } + } + fields.forEach { field -> val fieldFirstCord = field.id[0].code From da2d3ad56cd8ec794ae678a04cd2db1e6f7f05bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 1 Mar 2026 17:23:28 +0100 Subject: [PATCH 064/162] Add svg resources for map drawing --- .../src/commonMain/composeResources/drawable/svg/crysknife.svg | 1 + .../src/commonMain/composeResources/drawable/svg/desert.svg | 1 + .../src/commonMain/composeResources/drawable/svg/desert_bnw.svg | 1 + .../composeResources/drawable/svg/field-border-with-icons.svg | 1 + .../commonMain/composeResources/drawable/svg/field-border.svg | 1 + .../src/commonMain/composeResources/drawable/svg/lasgun-gun.svg | 1 + .../commonMain/composeResources/drawable/svg/lasgun-laser.svg | 1 + .../src/commonMain/composeResources/drawable/svg/lasgun.svg | 1 + .../src/commonMain/composeResources/drawable/svg/mountain.svg | 1 + .../commonMain/composeResources/drawable/svg/mountain_bnw.svg | 1 + .../src/commonMain/composeResources/drawable/svg/pistol-dart.svg | 1 + .../src/commonMain/composeResources/drawable/svg/pistol-gun.svg | 1 + .../src/commonMain/composeResources/drawable/svg/pistol.svg | 1 + .../commonMain/composeResources/drawable/svg/spice-rotated.svg | 1 + .../src/commonMain/composeResources/drawable/svg/spice.svg | 1 + .../src/commonMain/composeResources/drawable/svg/water.svg | 1 + 16 files changed, 16 insertions(+) create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/crysknife.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/desert.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/desert_bnw.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/field-border-with-icons.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/field-border.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/lasgun-gun.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/lasgun-laser.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/lasgun.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/pistol-dart.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/pistol-gun.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/pistol.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/spice-rotated.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/spice.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/water.svg diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/crysknife.svg b/composeApp/src/commonMain/composeResources/drawable/svg/crysknife.svg new file mode 100644 index 0000000..464ff62 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/crysknife.svg @@ -0,0 +1 @@ +Eszköz 4knife \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg b/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg new file mode 100644 index 0000000..4d269ed --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/desert_bnw.svg b/composeApp/src/commonMain/composeResources/drawable/svg/desert_bnw.svg new file mode 100644 index 0000000..4e5c8f5 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/desert_bnw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/field-border-with-icons.svg b/composeApp/src/commonMain/composeResources/drawable/svg/field-border-with-icons.svg new file mode 100644 index 0000000..914610b --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/field-border-with-icons.svg @@ -0,0 +1 @@ +Eszköz 10tile \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/field-border.svg b/composeApp/src/commonMain/composeResources/drawable/svg/field-border.svg new file mode 100644 index 0000000..0ddb046 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/field-border.svg @@ -0,0 +1 @@ +Eszköz 11tile_blank \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-gun.svg b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-gun.svg new file mode 100644 index 0000000..f589156 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-gun.svg @@ -0,0 +1 @@ +Eszköz 6lasgun1 \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-laser.svg b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-laser.svg new file mode 100644 index 0000000..93a89ed --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun-laser.svg @@ -0,0 +1 @@ +Eszköz 7lasgun2 \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/lasgun.svg b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun.svg new file mode 100644 index 0000000..524380c --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/lasgun.svg @@ -0,0 +1 @@ +Eszköz 5lasgun_all \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg new file mode 100644 index 0000000..9386be9 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg b/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg new file mode 100644 index 0000000..db89613 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/pistol-dart.svg b/composeApp/src/commonMain/composeResources/drawable/svg/pistol-dart.svg new file mode 100644 index 0000000..8a88d84 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/pistol-dart.svg @@ -0,0 +1 @@ +Eszköz 2pistol2 \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/pistol-gun.svg b/composeApp/src/commonMain/composeResources/drawable/svg/pistol-gun.svg new file mode 100644 index 0000000..e45131b --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/pistol-gun.svg @@ -0,0 +1 @@ +Eszköz 1pistol \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/pistol.svg b/composeApp/src/commonMain/composeResources/drawable/svg/pistol.svg new file mode 100644 index 0000000..061a37f --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/pistol.svg @@ -0,0 +1 @@ +Eszköz 3pistol_all \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/spice-rotated.svg b/composeApp/src/commonMain/composeResources/drawable/svg/spice-rotated.svg new file mode 100644 index 0000000..9e5ca1d --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/spice-rotated.svg @@ -0,0 +1 @@ +Eszköz 12tile_blank \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/spice.svg b/composeApp/src/commonMain/composeResources/drawable/svg/spice.svg new file mode 100644 index 0000000..fd4c8ef --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/spice.svg @@ -0,0 +1 @@ +Eszköz 8spice \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/water.svg b/composeApp/src/commonMain/composeResources/drawable/svg/water.svg new file mode 100644 index 0000000..dd12a5f --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/water.svg @@ -0,0 +1 @@ +Eszköz 9water \ No newline at end of file From 93b513275b4b6f7e21cb5faae5c61722d8bfa566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 15 Feb 2026 17:18:25 +0000 Subject: [PATCH 065/162] Added `getLatestGameStateByGameId()` and `getPlayerStepsByGameStateId()` --- .../it/dunestrat/backend/Repository.kt | 2 ++ .../dunestrat/backend/SupabaseRepository.kt | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index 4657d77..ba95ddd 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -12,9 +12,11 @@ interface Repository { suspend fun saveGameState(gameState: GameState) fun getGameStates(): Flow> + fun getLatestGameStateByGameId(gameId: String): Flow suspend fun savePlayerStep(playerStep: PlayerStep) fun getPlayerSteps(): Flow> + fun getPlayerStepsByGameStateId(gameStateId: String): Flow> suspend fun saveDesert(desert: Desert) fun getDeserts(): Flow> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 8b257ee..a455727 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -7,6 +7,8 @@ import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep import io.github.jan.supabase.annotations.SupabaseExperimental import io.github.jan.supabase.postgrest.from +import io.github.jan.supabase.postgrest.query.filter.FilterOperation +import io.github.jan.supabase.postgrest.query.filter.FilterOperator import io.github.jan.supabase.realtime.selectAsFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -34,6 +36,16 @@ object SupabaseRepository : Repository { list.map { it.toGameState() } } + @OptIn(SupabaseExperimental::class) + override fun getLatestGameStateByGameId(gameId: String): Flow = + supabase.from(GAME_STATE_TABLE).selectAsFlow( + GameStateDatabaseEntity::id, + filter = FilterOperation("game_id", FilterOperator.EQ, gameId), + ).map { list -> + list.map { it.toGameState() }.maxByOrNull { it.index } + ?: throw IllegalStateException("No game state found for game id: $gameId") + } + override suspend fun saveGameState(gameState: GameState) { supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) } @@ -44,6 +56,15 @@ object SupabaseRepository : Repository { list.map { it.toPlayerStep() } } + @OptIn(SupabaseExperimental::class) + override fun getPlayerStepsByGameStateId(gameStateId: String): Flow> = + supabase.from(STEP_TABLE).selectAsFlow( + PlayerStepDatabaseEntity::id, + filter = FilterOperation("game_state_id", FilterOperator.EQ, gameStateId), + ).map { list -> + list.map { it.toPlayerStep() } + } + override suspend fun savePlayerStep(playerStep: PlayerStep) { supabase.from(STEP_TABLE).insert(playerStep.toDatabaseEntry()) } From 47dd2ecbd5ef0e1de433584697d448e090e6db6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 15 Feb 2026 17:35:51 +0000 Subject: [PATCH 066/162] Refactor navigation --- .../it/dunestrat/navigation/NavGraph.kt | 8 ++++---- .../it/dunestrat/navigation/Screen.kt | 11 ++++++----- .../mokegyesulet/it/dunestrat/navigation/dev.md | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 6fe9b0f..5c863a4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -12,16 +12,16 @@ import hu.mokegyesulet.it.dunestrat.feature.testdata.TestDataScreen fun NavGraph(navController: NavHostController = rememberNavController()) { NavHost( navController = navController, - startDestination = Screen.MainMenu.route, + startDestination = Screen.MainMenu, ) { - composable(Screen.MainMenu.route) { + composable { MainMenu( onPlaceholder1 = {}, onPlaceholder2 = {}, - onTestData = { navController.navigate(Screen.TestData.route) }, + onTestData = { navController.navigate(Screen.TestData) }, ) } - composable(Screen.TestData.route) { TestDataScreen() } + composable { TestDataScreen() } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index bcf9862..3f24cb7 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -1,8 +1,9 @@ package hu.mokegyesulet.it.dunestrat.navigation -sealed class Screen( - val route: String, -) { - data object MainMenu : Screen(route = "main-menu") - data object TestData : Screen(route = "test-data") +import kotlinx.serialization.Serializable + +@Serializable +sealed class Screen { + data object MainMenu : Screen() + data object TestData : Screen() } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md index 8086e60..ed141d2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/dev.md @@ -1,9 +1,17 @@ To add a new composable to the navigation follow these steps: - Create a data object/class for it in `Screen.kt` - - If you don't need additional information from navigation use a data object: `data object YourComponentsName : Screen("your_components_name")` - - If you need some additional info (e.g. id of an object) use a data class: `data class YourComponentsName(val objectId: Int, ... ): Screen("your_components_name")` -- Create a `composable(Screen.YourComponentsName.route)` entry in `NavGraph.kt` + - If you don't need additional information from navigation use a data object: `data object YourComponentsName : Screen()` + - If you need some additional info (e.g. id of an object) use a data class: `data class YourComponentsName(val objectId: Int, ... ): Screen()` +- Create a `composable` entry in `NavGraph.kt` - Add your composable to it + - You can access your navigation arguments like that: + ```kotlin + composable { backStackEntry -> + val navigationObject: Screen.YourComponentsName = backStackEntry.toRoute() + + YourComponentsNameScreen(navigationObject.objectid, ...) + } + ``` - Fill in the lambdas for navigation - - Use `navController.navigate(Screen.OtherComponentsName.route)` to navigate to other components + - Use `navController.navigate(Screen.OtherComponentsName)` to navigate to other components - Use `navController.popBackStack()` to navigate to the previously displayed component before your component \ No newline at end of file From ee2efaea29ab25572f90c69303d2fdc6cc6feb56 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 15 Feb 2026 16:43:15 +0100 Subject: [PATCH 067/162] nothing --- .idea/artifacts/composeApp_js.xml | 8 ++++++++ .idea/artifacts/composeApp_jvm.xml | 8 ++++++++ .idea/artifacts/composeApp_wasm_js.xml | 8 ++++++++ .idea/codeStyles/Project.xml | 10 ++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 +++++ .idea/modules.xml | 8 ++++++++ 6 files changed, 47 insertions(+) create mode 100644 .idea/artifacts/composeApp_js.xml create mode 100644 .idea/artifacts/composeApp_jvm.xml create mode 100644 .idea/artifacts/composeApp_wasm_js.xml create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/modules.xml diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml new file mode 100644 index 0000000..e52ad0e --- /dev/null +++ b/.idea/artifacts/composeApp_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml new file mode 100644 index 0000000..a152ade --- /dev/null +++ b/.idea/artifacts/composeApp_jvm.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml new file mode 100644 index 0000000..e3c9c59 --- /dev/null +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..1bec35e --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3e42bd6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 31b3f428f9a6524b6ccb5d5b68ea8e16b7916a95 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 15 Feb 2026 18:30:46 +0100 Subject: [PATCH 068/162] random scheme --- .idea/deploymentTargetSelector.xml | 10 +++ .idea/runConfigurations.xml | 17 ++++ .../hu/mokegyesulet/it/dunestrat/App.kt | 20 ++++- .../it/dunestrat/feature/MainMenu.kt | 37 ++++++++ .../hu/mokegyesulet/it/dunestrat/ui/Color.kt | 68 ++++++++++++++ .../hu/mokegyesulet/it/dunestrat/ui/Theme.kt | 90 +++++++++++++++++++ .../hu/mokegyesulet/it/dunestrat/ui/Type.kt | 39 ++++++++ 7 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..d0b0930 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index c49f363..62a881a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -1,14 +1,26 @@ package hu.mokegyesulet.it.dunestrat -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.unit.dp import hu.mokegyesulet.it.dunestrat.navigation.NavGraph +import hu.mokegyesulet.it.dunestrat.ui.AppTheme +import hu.mokegyesulet.it.dunestrat.ui.md_theme_dark_background +import hu.mokegyesulet.it.dunestrat.ui.md_theme_light_background import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun App() { - MaterialTheme { - NavGraph() + Surface(color = if(isSystemInDarkTheme()){ + md_theme_light_background + }else{ + md_theme_dark_background + },tonalElevation = 5.dp) { + AppTheme { + NavGraph() + } } + } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt new file mode 100644 index 0000000..7bc423e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt @@ -0,0 +1,37 @@ +package hu.mokegyesulet.it.dunestrat.feature + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun MainMenu( + onPlaceholder1: () -> Unit, + onPlaceholder2: () -> Unit, +) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceEvenly, + ) { + Button( + onClick = onPlaceholder1, + ) { + Text(text = "Placeholder 1", + style = MaterialTheme.typography.titleLarge) + } + + Button( + onClick = onPlaceholder2, + ) { + Text(text = "Placeholder 2", + style = MaterialTheme.typography.bodyMedium) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt new file mode 100644 index 0000000..02f2d7b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt @@ -0,0 +1,68 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.ui.graphics.Color + +val md_theme_light_primary = Color(0xFF825500) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFFFFDDB3) +val md_theme_light_onPrimaryContainer = Color(0xFF291800) +val md_theme_light_secondary = Color(0xFF6F5B40) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFFBDEBC) +val md_theme_light_onSecondaryContainer = Color(0xFF271904) +val md_theme_light_tertiary = Color(0xFF51643F) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFD4EABB) +val md_theme_light_onTertiaryContainer = Color(0xFF102004) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xffebb000) +val md_theme_light_onBackground = Color(0xFF1F1B16) +val md_theme_light_surface = Color(0xFFFFFBFF) +val md_theme_light_onSurface = Color(0xFF1F1B16) +val md_theme_light_surfaceVariant = Color(0xFFF0E0CF) +val md_theme_light_onSurfaceVariant = Color(0xFF4F4539) +val md_theme_light_outline = Color(0xFF817567) +val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7) +val md_theme_light_inverseSurface = Color(0xFF34302A) +val md_theme_light_inversePrimary = Color(0xFFFFB951) +val md_theme_light_shadow = Color(0xFF000000) +val md_theme_light_surfaceTint = Color(0xFF825500) +val md_theme_light_outlineVariant = Color(0xFFD3C4B4) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFFFFB951) +val md_theme_dark_onPrimary = Color(0xFF452B00) +val md_theme_dark_primaryContainer = Color(0xFF633F00) +val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3) +val md_theme_dark_secondary = Color(0xFFDDC2A1) +val md_theme_dark_onSecondary = Color(0xFF3E2D16) +val md_theme_dark_secondaryContainer = Color(0xFF56442A) +val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC) +val md_theme_dark_tertiary = Color(0xFFB8CEA1) +val md_theme_dark_onTertiary = Color(0xFF243515) +val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A) +val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xebd38f55) +val md_theme_dark_onBackground = Color(0xFFEAE1D9) +val md_theme_dark_surface = Color(0xFF1F1B16) +val md_theme_dark_onSurface = Color(0xFFEAE1D9) +val md_theme_dark_surfaceVariant = Color(0xFF4F4539) +val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4) +val md_theme_dark_outline = Color(0xFF9C8F80) +val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16) +val md_theme_dark_inverseSurface = Color(0xFFEAE1D9) +val md_theme_dark_inversePrimary = Color(0xFF825500) +val md_theme_dark_shadow = Color(0xFF000000) +val md_theme_dark_surfaceTint = Color(0xFFFFB951) +val md_theme_dark_outlineVariant = Color(0xFF4F4539) +val md_theme_dark_scrim = Color(0xFF000000) + + +val seed = Color(0xFF825500) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt new file mode 100644 index 0000000..2cad406 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt @@ -0,0 +1,90 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.runtime.Composable + + +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, +) + + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +@Composable +fun AppTheme( + useDarkTheme: Boolean = false, //isSystemInDarkTheme() + content: @Composable() () -> Unit +) { + val colors = if (!useDarkTheme) { + LightColors + } else { + DarkColors + } + + MaterialTheme( + colorScheme = colors, + content = content + ) +} + diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt new file mode 100644 index 0000000..7ecc40a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt @@ -0,0 +1,39 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +val typography = Typography( + headlineSmall = TextStyle( + fontWeight = FontWeight.SemiBold, + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.sp + ), + titleLarge = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 18.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + bodyLarge = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.15.sp + ), + bodyMedium = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.25.sp + ), + labelMedium = TextStyle( + fontWeight = FontWeight.SemiBold, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) +) From 930af9659e0c22723b393a24e2ee6ed63fa6cc0b Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 15 Feb 2026 19:34:29 +0100 Subject: [PATCH 069/162] ability to make any shape --- .../hu/mokegyesulet/it/dunestrat/App.kt | 8 +- .../it/dunestrat/feature/MainMenu.kt | 7 +- .../hu/mokegyesulet/it/dunestrat/ui/Color.kt | 120 +++++++++--------- .../hu/mokegyesulet/it/dunestrat/ui/Shapes.kt | 24 ++++ .../hu/mokegyesulet/it/dunestrat/ui/Theme.kt | 119 ++++++++--------- 5 files changed, 154 insertions(+), 124 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index 62a881a..c404e80 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -6,17 +6,17 @@ import androidx.compose.runtime.* import androidx.compose.ui.unit.dp import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import hu.mokegyesulet.it.dunestrat.ui.AppTheme -import hu.mokegyesulet.it.dunestrat.ui.md_theme_dark_background -import hu.mokegyesulet.it.dunestrat.ui.md_theme_light_background +import hu.mokegyesulet.it.dunestrat.ui.dark_background +import hu.mokegyesulet.it.dunestrat.ui.light_background import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun App() { Surface(color = if(isSystemInDarkTheme()){ - md_theme_light_background + light_background }else{ - md_theme_dark_background + dark_background },tonalElevation = 5.dp) { AppTheme { NavGraph() diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt index 7bc423e..fe50f14 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt @@ -9,12 +9,15 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import hu.mokegyesulet.it.dunestrat.ui.customShape + @Composable fun MainMenu( onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit, ) { + Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -22,9 +25,11 @@ fun MainMenu( ) { Button( onClick = onPlaceholder1, + shape = customShape, ) { Text(text = "Placeholder 1", - style = MaterialTheme.typography.titleLarge) + style = MaterialTheme.typography.titleLarge, + ) } Button( diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt index 02f2d7b..a94bd85 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt @@ -2,67 +2,67 @@ package hu.mokegyesulet.it.dunestrat.ui import androidx.compose.ui.graphics.Color -val md_theme_light_primary = Color(0xFF825500) -val md_theme_light_onPrimary = Color(0xFFFFFFFF) -val md_theme_light_primaryContainer = Color(0xFFFFDDB3) -val md_theme_light_onPrimaryContainer = Color(0xFF291800) -val md_theme_light_secondary = Color(0xFF6F5B40) -val md_theme_light_onSecondary = Color(0xFFFFFFFF) -val md_theme_light_secondaryContainer = Color(0xFFFBDEBC) -val md_theme_light_onSecondaryContainer = Color(0xFF271904) -val md_theme_light_tertiary = Color(0xFF51643F) -val md_theme_light_onTertiary = Color(0xFFFFFFFF) -val md_theme_light_tertiaryContainer = Color(0xFFD4EABB) -val md_theme_light_onTertiaryContainer = Color(0xFF102004) -val md_theme_light_error = Color(0xFFBA1A1A) -val md_theme_light_errorContainer = Color(0xFFFFDAD6) -val md_theme_light_onError = Color(0xFFFFFFFF) -val md_theme_light_onErrorContainer = Color(0xFF410002) -val md_theme_light_background = Color(0xffebb000) -val md_theme_light_onBackground = Color(0xFF1F1B16) -val md_theme_light_surface = Color(0xFFFFFBFF) -val md_theme_light_onSurface = Color(0xFF1F1B16) -val md_theme_light_surfaceVariant = Color(0xFFF0E0CF) -val md_theme_light_onSurfaceVariant = Color(0xFF4F4539) -val md_theme_light_outline = Color(0xFF817567) -val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7) -val md_theme_light_inverseSurface = Color(0xFF34302A) -val md_theme_light_inversePrimary = Color(0xFFFFB951) -val md_theme_light_shadow = Color(0xFF000000) -val md_theme_light_surfaceTint = Color(0xFF825500) -val md_theme_light_outlineVariant = Color(0xFFD3C4B4) -val md_theme_light_scrim = Color(0xFF000000) +val light_primary = Color(0xFF825500) +val light_onPrimary = Color(0xFFFFFFFF) +val light_primaryContainer = Color(0xFFFFDDB3) +val light_onPrimaryContainer = Color(0xFF291800) +val light_secondary = Color(0xFF6F5B40) +val light_onSecondary = Color(0xFFFFFFFF) +val light_secondaryContainer = Color(0xFFFBDEBC) +val light_onSecondaryContainer = Color(0xFF271904) +val light_tertiary = Color(0xFF51643F) +val light_onTertiary = Color(0xFFFFFFFF) +val light_tertiaryContainer = Color(0xFFD4EABB) +val light_onTertiaryContainer = Color(0xFF102004) +val light_error = Color(0xFFBA1A1A) +val light_errorContainer = Color(0xFFFFDAD6) +val light_onError = Color(0xFFFFFFFF) +val light_onErrorContainer = Color(0xFF410002) +val light_background = Color(0xffebb000) +val light_onBackground = Color(0xFF1F1B16) +val light_surface = Color(0xFFFFFBFF) +val light_onSurface = Color(0xFF1F1B16) +val light_surfaceVariant = Color(0xFFF0E0CF) +val light_onSurfaceVariant = Color(0xFF4F4539) +val light_outline = Color(0xFF817567) +val light_inverseOnSurface = Color(0xFFF9EFE7) +val light_inverseSurface = Color(0xFF34302A) +val light_inversePrimary = Color(0xFFFFB951) +val light_shadow = Color(0xFF000000) +val light_surfaceTint = Color(0xFF825500) +val light_outlineVariant = Color(0xFFD3C4B4) +val light_scrim = Color(0xFF000000) -val md_theme_dark_primary = Color(0xFFFFB951) -val md_theme_dark_onPrimary = Color(0xFF452B00) -val md_theme_dark_primaryContainer = Color(0xFF633F00) -val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3) -val md_theme_dark_secondary = Color(0xFFDDC2A1) -val md_theme_dark_onSecondary = Color(0xFF3E2D16) -val md_theme_dark_secondaryContainer = Color(0xFF56442A) -val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC) -val md_theme_dark_tertiary = Color(0xFFB8CEA1) -val md_theme_dark_onTertiary = Color(0xFF243515) -val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A) -val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB) -val md_theme_dark_error = Color(0xFFFFB4AB) -val md_theme_dark_errorContainer = Color(0xFF93000A) -val md_theme_dark_onError = Color(0xFF690005) -val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) -val md_theme_dark_background = Color(0xebd38f55) -val md_theme_dark_onBackground = Color(0xFFEAE1D9) -val md_theme_dark_surface = Color(0xFF1F1B16) -val md_theme_dark_onSurface = Color(0xFFEAE1D9) -val md_theme_dark_surfaceVariant = Color(0xFF4F4539) -val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4) -val md_theme_dark_outline = Color(0xFF9C8F80) -val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16) -val md_theme_dark_inverseSurface = Color(0xFFEAE1D9) -val md_theme_dark_inversePrimary = Color(0xFF825500) -val md_theme_dark_shadow = Color(0xFF000000) -val md_theme_dark_surfaceTint = Color(0xFFFFB951) -val md_theme_dark_outlineVariant = Color(0xFF4F4539) -val md_theme_dark_scrim = Color(0xFF000000) +val dark_primary = Color(0xFFFFB951) +val dark_onPrimary = Color(0xFF452B00) +val dark_primaryContainer = Color(0xFF633F00) +val dark_onPrimaryContainer = Color(0xFFFFDDB3) +val dark_secondary = Color(0xFFDDC2A1) +val dark_onSecondary = Color(0xFF3E2D16) +val dark_secondaryContainer = Color(0xFF56442A) +val dark_onSecondaryContainer = Color(0xFFFBDEBC) +val dark_tertiary = Color(0xFFB8CEA1) +val dark_onTertiary = Color(0xFF243515) +val dark_tertiaryContainer = Color(0xFF3A4C2A) +val dark_onTertiaryContainer = Color(0xFFD4EABB) +val dark_error = Color(0xFFFFB4AB) +val dark_errorContainer = Color(0xFF93000A) +val dark_onError = Color(0xFF690005) +val dark_onErrorContainer = Color(0xFFFFDAD6) +val dark_background = Color(0xebd38f55) +val dark_onBackground = Color(0xFFEAE1D9) +val dark_surface = Color(0xFF1F1B16) +val dark_onSurface = Color(0xFFEAE1D9) +val dark_surfaceVariant = Color(0xFF4F4539) +val dark_onSurfaceVariant = Color(0xFFD3C4B4) +val dark_outline = Color(0xFF9C8F80) +val dark_inverseOnSurface = Color(0xFF1F1B16) +val dark_inverseSurface = Color(0xFFEAE1D9) +val dark_inversePrimary = Color(0xFF825500) +val dark_shadow = Color(0xFF000000) +val dark_surfaceTint = Color(0xFFFFB951) +val dark_outlineVariant = Color(0xFF4F4539) +val dark_scrim = Color(0xFF000000) val seed = Color(0xFF825500) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt new file mode 100644 index 0000000..42b5cf5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt @@ -0,0 +1,24 @@ +package hu.mokegyesulet.it.dunestrat.ui + + +import androidx.compose.foundation.shape.GenericShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Shapes +import androidx.compose.ui.unit.dp + +val customShape = GenericShape{ size, _ -> + val width = size.width + val height = size.height + + lineTo(0f, 0f) + lineTo(width, 0f) + lineTo(width, height*2) + lineTo(0f, height) +} +val shapes = Shapes( + extraSmall = RoundedCornerShape(4.dp), + small = RoundedCornerShape(8.dp), + medium = RoundedCornerShape(16.dp), + large = RoundedCornerShape(24.dp), + extraLarge = RoundedCornerShape(32.dp) +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt index 2cad406..c1923e4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt @@ -7,68 +7,68 @@ import androidx.compose.runtime.Composable private val LightColors = lightColorScheme( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - primaryContainer = md_theme_light_primaryContainer, - onPrimaryContainer = md_theme_light_onPrimaryContainer, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - secondaryContainer = md_theme_light_secondaryContainer, - onSecondaryContainer = md_theme_light_onSecondaryContainer, - tertiary = md_theme_light_tertiary, - onTertiary = md_theme_light_onTertiary, - tertiaryContainer = md_theme_light_tertiaryContainer, - onTertiaryContainer = md_theme_light_onTertiaryContainer, - error = md_theme_light_error, - errorContainer = md_theme_light_errorContainer, - onError = md_theme_light_onError, - onErrorContainer = md_theme_light_onErrorContainer, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, - surfaceVariant = md_theme_light_surfaceVariant, - onSurfaceVariant = md_theme_light_onSurfaceVariant, - outline = md_theme_light_outline, - inverseOnSurface = md_theme_light_inverseOnSurface, - inverseSurface = md_theme_light_inverseSurface, - inversePrimary = md_theme_light_inversePrimary, - surfaceTint = md_theme_light_surfaceTint, - outlineVariant = md_theme_light_outlineVariant, - scrim = md_theme_light_scrim, + primary = light_primary, + onPrimary = light_onPrimary, + primaryContainer = light_primaryContainer, + onPrimaryContainer = light_onPrimaryContainer, + secondary = light_secondary, + onSecondary = light_onSecondary, + secondaryContainer = light_secondaryContainer, + onSecondaryContainer = light_onSecondaryContainer, + tertiary = light_tertiary, + onTertiary = light_onTertiary, + tertiaryContainer = light_tertiaryContainer, + onTertiaryContainer = light_onTertiaryContainer, + error = light_error, + errorContainer = light_errorContainer, + onError = light_onError, + onErrorContainer = light_onErrorContainer, + background = light_background, + onBackground = light_onBackground, + surface = light_surface, + onSurface = light_onSurface, + surfaceVariant = light_surfaceVariant, + onSurfaceVariant = light_onSurfaceVariant, + outline = light_outline, + inverseOnSurface = light_inverseOnSurface, + inverseSurface = light_inverseSurface, + inversePrimary = light_inversePrimary, + surfaceTint = light_surfaceTint, + outlineVariant = light_outlineVariant, + scrim = light_scrim, ) private val DarkColors = darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - errorContainer = md_theme_dark_errorContainer, - onError = md_theme_dark_onError, - onErrorContainer = md_theme_dark_onErrorContainer, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, - outline = md_theme_dark_outline, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inverseSurface = md_theme_dark_inverseSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim, + primary = dark_primary, + onPrimary = dark_onPrimary, + primaryContainer = dark_primaryContainer, + onPrimaryContainer = dark_onPrimaryContainer, + secondary = dark_secondary, + onSecondary = dark_onSecondary, + secondaryContainer = dark_secondaryContainer, + onSecondaryContainer = dark_onSecondaryContainer, + tertiary = dark_tertiary, + onTertiary = dark_onTertiary, + tertiaryContainer = dark_tertiaryContainer, + onTertiaryContainer = dark_onTertiaryContainer, + error = dark_error, + errorContainer = dark_errorContainer, + onError = dark_onError, + onErrorContainer = dark_onErrorContainer, + background = dark_background, + onBackground = dark_onBackground, + surface = dark_surface, + onSurface = dark_onSurface, + surfaceVariant = dark_surfaceVariant, + onSurfaceVariant = dark_onSurfaceVariant, + outline = dark_outline, + inverseOnSurface = dark_inverseOnSurface, + inverseSurface = dark_inverseSurface, + inversePrimary = dark_inversePrimary, + surfaceTint = dark_surfaceTint, + outlineVariant = dark_outlineVariant, + scrim = dark_scrim, ) @Composable @@ -84,7 +84,8 @@ fun AppTheme( MaterialTheme( colorScheme = colors, - content = content + content = content, + shapes = shapes ) } From 91af27827f475c23ba034a0d10908a218f87e29c Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 22 Feb 2026 20:06:53 +0100 Subject: [PATCH 070/162] Added fonts, and custom button --- .idea/artifacts/composeApp_jvm.xml | 2 +- .idea/compiler.xml | 10 +++ .idea/deploymentTargetSelector.xml | 10 --- .idea/ktlint-plugin.xml | 7 +++ .idea/modules.xml | 4 +- .../composeResources/font/Dune_Rise.otf | Bin 0 -> 30956 bytes .../composeResources/font/metropolis_bold.otf | Bin 0 -> 23480 bytes .../font/metropolis_light.otf | Bin 0 -> 22920 bytes .../font/metropolis_medium.otf | Bin 0 -> 23200 bytes .../font/metropolis_normal.otf | Bin 0 -> 23080 bytes .../font/metropolis_semi_bold.otf | Bin 0 -> 23240 bytes .../hu/mokegyesulet/it/dunestrat/App.kt | 16 ++--- .../it/dunestrat/feature/MainMenu.kt | 58 ++++++++++++++---- .../hu/mokegyesulet/it/dunestrat/ui/Color.kt | 5 +- .../hu/mokegyesulet/it/dunestrat/ui/Font.kt | 50 +++++++++++++++ .../hu/mokegyesulet/it/dunestrat/ui/Shapes.kt | 43 ++++++++++--- .../hu/mokegyesulet/it/dunestrat/ui/Theme.kt | 14 ++--- .../hu/mokegyesulet/it/dunestrat/ui/Type.kt | 12 ++-- 18 files changed, 173 insertions(+), 58 deletions(-) create mode 100644 .idea/compiler.xml delete mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/ktlint-plugin.xml create mode 100644 composeApp/src/commonMain/composeResources/font/Dune_Rise.otf create mode 100644 composeApp/src/commonMain/composeResources/font/metropolis_bold.otf create mode 100644 composeApp/src/commonMain/composeResources/font/metropolis_light.otf create mode 100644 composeApp/src/commonMain/composeResources/font/metropolis_medium.otf create mode 100644 composeApp/src/commonMain/composeResources/font/metropolis_normal.otf create mode 100644 composeApp/src/commonMain/composeResources/font/metropolis_semi_bold.otf create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml index a152ade..96d842a 100644 --- a/.idea/artifacts/composeApp_jvm.xml +++ b/.idea/artifacts/composeApp_jvm.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/composeApp/build/libs - + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..6b61887 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml deleted file mode 100644 index d0b0930..0000000 --- a/.idea/deploymentTargetSelector.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml new file mode 100644 index 0000000..e8bd90c --- /dev/null +++ b/.idea/ktlint-plugin.xml @@ -0,0 +1,7 @@ + + + + DISTRACT_FREE + DEFAULT + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 3e42bd6..2b3b281 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,9 @@ - + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/font/Dune_Rise.otf b/composeApp/src/commonMain/composeResources/font/Dune_Rise.otf new file mode 100644 index 0000000000000000000000000000000000000000..df025f46ba7783cdb9ed35e73b9dcb54b24646d4 GIT binary patch literal 30956 zcmd6Q31C#!z4w{Ph8GM<00AMnY!M`cfS`z4Cxi(ELlTl8Vy!wP6Ec#_#F+^pR_mZe zed0!+cCpr_K9^RpDrl*Nc3Ruox?x-U6s0cpeXUl%LO<;zAmqH?@0@#P%LJ77eSKeI z?wos;|Gu4b??`=fbGgHwNSoHB$A$pbwA;vFW zA0l3Q{Aju1KRigK@6YXiUu#M3`UM!dP9AZj)XLS7;Ov z==81c5~~|9aTN#}@qM>=EKE-HY~kvA}7&#BpM+({_sjvC(Pgh*I&U)6NygxkfwfJaK|+q0`Ps z{~D)#{E1U1?{wOQV%)s3pwg9l9B@&_y6plDE1kAm6pC)Aog-?+%}zU4i~zrSex9&h zw$si>f0NTbe$rCc5e3iFM2nb?&pDz3f3rmy+SQmB z#ArnH2p?LaD%8~*4yW6x2G`FKHHio)03zU?8CQw?Rq;(2ykl+CrS~4@Ux_pj}Zh zyS$>J0zHQJOVWA2MDu>E0-vUo6NR!Oki{(gwq>ZK9BK=RPN=R-SWr^{>S_mxlr}XG z7VB_rMURoQE$X0wPV{DzZ8@M=q8Tu#!PHevz|#ikG=;EQt!H##W()3v3Li1i3R=+< zMw`?SQ{rREp(lW)ym`5?awZiGY8)QkQa-azG`la=@lN z&jQcY;GJ@{V5|QVzgCL;8-y1YK%VOH*^E{#bW^2zn{XXW`Zg%1XP&Os`Vof1&@^qK zSM}31&iEhX&_Zr#1pSp_nOF#(yr6}4Ol_`#F0{UFwA#>P?DFXL$k3=Y$na=vKdLMz z0yqKql8$Oo$8xCm4pILipMuKycEaCntP5lG zGW51!JfbYxP|=OiF140W4R0n^*vdZr4qS$G3sBk=?687mDDh#YPf^;6BI`)wNAbQx#s6!7nzNRVEw6DWB<-t*M#weoih{FQ?CN^+P$EY5~)mqFa ze)=UNQwy$1*)UFZVm|GV9&dw&cH9#_{oT+)O&Ln`h=$pv=0*Wuhkat`0zJtX5OQS7 zq>zk|#H1~QIH*BJ8d764kRGE*i_wXm#pt6c?#4ROMA=ck-DvA5#-2_<=6##fXLzz4 z9<@#wgH~fD?NH;=67Eov@hQqkBlSoKNlGa>W7$E>pcOGT(uzqT$2Cu#=%*diIw=d= z@i8lfGg3^+wqTy-!(a?5NIEwJRMe%-XiGPm*7id?q`tLu$Q5mbv02N=51DYR6~CtM z$kNbOFbiBUFD1E4YMC?0DaXGHRD=Tn-Hel#Ez;=;%jZpe_db1gW7%YyV88gmTYTci6Cl5LQvvhBF`N>jGE3kb;gw zh7Znd0Yyn#n89^~G~6?bYr8exoJyyrT#t}1;%D?ow`vnD79kr|Bg@v|uNi;LyiK5L z5_)xs%?c^q>LvjuX=+n&lkvh(Lp>#P2qOda$9V&Fnd-rImh#^cOMso;P9CNK3Ul3O zle@7%dOIb^+(&<){=(QSX?f~s69G)dZetxFDtRzH7+Xs9@`&$%tS}CKRb*489tW*Q+n<@UrEnw&j#bu=qHIHTS6*j@2kDKGLo$Pu6SH3aviV#x?iGy9Sq&=AOH-M(k8-5cjz^ zW=61ah{Wj3_M(hT(V%EyRUT1t&@}1zNzE|kYt3oiNujBWl9jf$9c>BvtcCVr^rowy zbgn~y$~Z(j(6(UVPD_Ci%Vhtf^PR+`Yaia5O1IakOLP?Eh}IlWgLs#$_Vk#E2wXRK z)yKKCADtuU$sG!^S#3@U%pB&sft9w7&3b1u7`NPCQ?|*|5#nadU!B;o895tC(fiDv z!FZXrEyf|n6Y7}fK0G&=tuk^QB%x1uw9Oh=gIk-fhUnMEle7&G3VoiKiBIp%xSywt zvPB+BnzOm*$&5MLca9dRb!DHqQ%m1vXu7DYHYZEZQJfz;&cSpxu;S8Lfi|6e*D<)I z(ZaM99n1cLwsNE$2CFLiY|`s=#Xuj|o=Hs_ThONndQYWoH@&XV)iI+W^N+E8uGZ0H zusWXgCMn59?dNtH9D%Jr;w375Xg{HiPK^x8_t*0AF{r#VrB zdFyE2c`SaSu_bM<5j2sTfv3(Ea+=-^r|vHFj)jt;C2+4syJQStzSY^8GN#4pyp%ly zrsq6U!x(Fz7liRk+H^H#Dm^R9UMRg5EeGc2gl}jVI`{JwE?G4P9o?8cr^y%TRe`of z+P1L}T}v=ZYw2i9v6S6vS(;3qnLmuC%^q{E(lu$NH5qtD+No7zwjE$P*U44P97c|* z^;&X9>S?OULj&_6vmxtGorBE|UB_9?38ROWmiAm`V(OmJkru$+96g@gu|-L>I;R@c zgdSnk;C}yT)~=zhS5c7X_69d2?OMR#nF+146%{5i8X4#eom%~A8^|V)qg$6*rK4jK zOLj?u)b4!(Cyg|DrP_NVQE8W}8K>ix{; zIrq}_1*3zOTPkMa%a)&yeD0OBYIE{>O^JYeCtTh z>ge~n#$=3RwBi$#jub7egN%9FN*iK?NlHF7-elG~x|TI@nUEQ2`2@!3TIY97H}_ag z$N;sVx&{%0J2Fze1g+HPV@;@dtI@v_^Z10xR_ko_yr~|~Dr>M?^Wx9OF18X7_4p(e zxmIG75bMPXeA*6-M$F^0w^isNY+K#)?mW!r?vFm<6&EcvjOw;6GuWORiu_@q;-JNs6H11 zDk-3}c-lc(afjRh+?=r(bTum~h>6^BCZ*Dh@k&7DdSX3K^=ir}6Ka{XB%v_Z6StoJE|340+vwd^T1K9fw9PMy+!(*8*GB48oCuDaVW#Y8;vx8`Ntn)O>MWx_phdn0aPh@(KFD2%%va+_c;z@02HHsVStE zwKR?nw_F_Y6`X19(P&p>!K_(r&O15fEuqfoWmZoh+HN=cBmVF@f2(?7$F9R$T*1Xn zXP0YW&RT+L33UYkWVOE|)Kg~rg01ZIbwon8Z=EmD;alC|w-u2-ySj3@?Taq3$O<@# zw1fj)(MWkD&{2-}dS=yE*IJe}^VokZMSp|0&aSSnYqo1^s=Reg-c<7Knf9F1?P~w( zaJMhqi?^IA&iHR3v>F<{mCF{^dLb6Sy(R>{?NFPN_{}wiI=3J z5KbuE(b^Me^;@m}b&z6Lr#~1al$KBj#1{(tqJedOn|imq!lBFjEzwAs0@2;o6$(cc zHZ|H3_G4}+X!+aPFrY~BwfI{7oq-mmnT|kkO?LpOTL8SXvpX1w2K)dGa|r2{+K z+vTT;Ia1fc2>UPZ4ut*6{9r#09%ur6KIqfLiDc}sTSK6l$b4O09laLjDglOCx)l^< zkYJ@dLfJz24om>YO(2}g0L%h!!`9T*G}qMEHCdCYY40XpO#%&VupQb3!9@JZL)roz zz@Ok!E7(rF$!INs+Wp~ak?GlJrvO_ZpD^sE(-&S#yNcM6?v{3n15CoYEo~V<80rqU zfP5k>gCPW9Pfq-zH9`=YW#xZhWzxWqoXP{Gq*Wg2^0zoft&!Wlwx|xr7DB7@N@gUA zNI{W;7ecSco0@^3ufxQ)RM?acLkHpn-(!T9(?m(n?+OJ03-%VVQk^JMes)5B#JS9P zpOX$!ab~zpCt?om*Vn3?DjF(-raSymSV5UZ|L9(gAQbIphdpzq2^KbnRD=m3XP{fz ztB$5AMC9t$heL)Cj4eTu3>0<;8vzuYkSYWy^Xt7q%-(&lO>jL3WkTNE- zmEF+G(-TR@v{Y|UW549$#&G=(jE-7_&ThfaDZ9_dhKvsFkUF5r#%Gu z2NPk-G3PXzfWE=oxU8nB365Zw+Ew*+iyWtD@P|7C5%qd1>=VJ%59WY9h!9w&gEsR9 z!tolvN)eQxgF3W23MYmXEFVbdLL|_VO~b7gA^PZ`+lyF&nFzdo1yF@OCk*$NrG-A7 zLl9a!(xNo{E)lc=3dtrFt!3OB=(<-ku^InvgZ+kjs4_^a6rgD}D zTn3s+9ce;SiEQwEp=^r@*-03F9}+JWg1``@GIi!eiGcEe{rdH7gkuzLDk?=8#!_Q^ z1QS&*Gl8wucI3&>7eyqS=|ciVh2mcyHPNoUyVDn(iQ1x76?6QO0Yh;pOkpzGAozzv zUEu($nNAQ1&qz-1^hdEUikc$e?`VyvT9E6A03cQ)jG+?KkvuJZhd`{e#>v0PWdQ{1 zbpd~m&W}V6YYjs?r@=Hr1JpUVPOwbTo))Jqmd-j|AkIf@|N1USFA%lq&QVlC@TC+7 zbUcp{MhD$6@;PEFgPv8DP>7DAPEx@Pc)t^xgDhE)Iq4TpD1aD|j35xEKc)x<=`O57 zV4@-D{70r)NX?zjGDgiK*^WzRqEVa(LY1yO&2`6~5h6EGI;KUM8)vN4A6LGr3UuJc7M^qh^nk1nI z6iHCrz;Z|jLA6MWFfF_4`6{P@{BW9IqK3&{dm&zYw%Uu9LD;4cv7Oq>O4eR!l7N7k znPuuKS#PE9>Q?&^F(8%Qs%*mjq%wPDP4kla70otgG*;F%ufhhw zuB=;SpI1}2sLb+SfSp!TlU?6v*DPzOt?@1@vuo#JakA?)a9~Gy`aJ zy;|yks_|l7wFO$LmH=|)!kXHe=2iH1OHFefVO9fZrQJ~3*j!V!qPDWpZdlQXFJgHC zV-djC)znot0*iNITlmF$ura#P7{~o^NP~!Ot9o`r$Xp_&479=GHs> zR+YNX6~Dme3Hass6mf~TO586Vu*ceuN{Jr6+T#=7H9hiALdbXIPPyaly>CCMU=(6j z@}~fO$Kmj5s2GOt2^NasfP5mp4_1Wl0ge)*#Ytj}7%Rq!lR?V_Q7lS;VIq#7Cj<9X z;5r2_aZVSfiWxZenu&+Ov+(7N+4%CtX=1LJhp%v)A?D-jQwzjd;%sq__<}fBc$>S#!m)H|0O`bBf^pt7S zPn}UVvwT*?>^Y~+op<^f^Uqvx*4gKL;arcXTv&xI+~OrQOV6uaR#)G!ys@cy#rZ2Q zSasnSFZ$8|bXhtsxiquWm)VhdKSXt0oY_6dJvE3Q`7|V!<;Nv*bA`+0viRLH{rxVN z8|td)8!r0ByZR=$`-*e=#^g>Lma}76?%x(4yyrk)-gxmZ!^guw#=B}y94}58U$J$B z$mOa$sO?m7nzuU~N@nDNU#?4hk1f$4W$ggKZ~x^2OaZ=0`OmhPg*ck!VP>^DhOZGz zamZeaqgEc{H{e*f5eKWyI9@#;$G;cgDEdP2MRAe%lDOD)J-lHx9`CHC45p|v-dn`m zt{pjZb8pW5e%`FSZTX@67xLdJ7+Mf1xO+%^NZ)ZE4jn)Ap<$OCf7S6n8-C9TJ5ThS z_|}L?BLXA7S9Hb5$4CBYRR8D+qaQfw>nHtvOzD^}jk#(}e9Rxmy2maU+cNf!an<9l zI(fs%&z$`B_@U!xOqesFX2MMq9+=QyTw1)j`1RrgCFj^3_NIxY6MsKx#iSc2{cQ5- zlY1xMIr*oPKb}&wY2Il!OuKLT9n+sW^?|aPGf$ei ztNfn*_NJYE-kq+UJNL(T=Ik8ZH>K};ho9N|5x~V4SN231zY_& z()CHX0QTEsfDhuN?A-^TSB?eE*9}K&#~xhJm;dRw8#nRRvwz%8Xk0lgp3&2L7=%?# zj&0hcmjVgf3ZST(A`<=A4Huj8F!dy2`#V^=_G7F>FPFzvADC0b%>O`uE=7%+1d#cK zfDdM*YsO+oSGSg4pjwWaIP|aV=x6H&w4l0?uV6 zrBD7vDfS;&gT#r;m>3oP56=*dMax}j017FVuB+$898zc)FdgSs3oiCN$#X=`5hZep z*L4dV@$jenz@sb}nSE9^?xC2z4`I+p3^d=7qUAxxgXup*D0t!am@?v>E8|jzH-OhS zo)J>ElnSu6>H_B~#nfu_Uc4tR#lnX@YUU6rmj04Pv;7A`icNj!eeF4q6n9@1hgu)n z0omY+1=Hr^a&I>-ic`c{^W7MnfeqimGu~4OTNWWuzMzXrq-%HeL-Jw;r_0B*WJ7Bz&7T|qI;$5oqaKt)wbtB>isWRp2OQlkyukV@O@pZK6-K zM*uU{7AxKl{A@_wW%^V466=c#AEqp&dk_49Ivyd#Tf6R7&7#NmDzP$zi7R#?Fim#! zePCNbQnId7cVCV9n1ucjYBgtgeCKU9$55 zDSEAwrTEIZuSt1l$%j&0cw<1yn@9bjME1v?hiFtVy?S088K{1Ag%s5{-zMeHVrNPD z@E&BfM>bbT`QsTU!3keDLCPP@o?e1bc6*G<`}A@q=ccIg-UoPcW@7skL zr_77@eMGv!;fJL$=uVR3J9fuFVAIEsz5;a&Yu;0w!t%hXz(p&f2rs>s$;!R5IMz1> z95b_s($#=B?P6T2enKN!HddLE1(xKmpiRfLEaVfF(N)&cg#waTF}aT=xJOn<=D$R0 zP01&TKJbQ2sKWuX7A+st`70Bz)#kDM`I83_1>oG+Y zay{jT1bp^zkA_6G9@vID)&v5lqEp3(7Yf#KjUUow$U%C12-e_wg2k*VqL>soH+JYF z*)u_5w>XG5UN^NUsd$u3+(r6+#_2~3+LU_E{a@9by5;cn$Z-ni3x?S8a&EEJsm z7i0o?!14vbsJ%(F;)(SY{0521{;kVh6BulL6K@LN&5x%ElhDcPxCB6^F=h}BQa2O!AhFW zmR+D;G{-QZH1;xZG}c2N$_brfc2KqJ$tW&gjK7R^r)fYS@S%s}q~#m{1u9JVd2ES8 z297^m=smCv_vZQ(h}m-8RLJU2TXA{6AGg=Om=?3M`_H^*q22oyLk`n(7`a_%Bhw&G z(euw921YNt7=$E4Q9>xt?V}WgN{>zRAqUa39fT@BFwTII9-v^+^mpZ0DPi4l24|lF zP{#4H1+|h*0EH6+Xh}KVfE`z?MA7=`D_H|4M0rgdOpXvn;|QjvVnZIbzK~YCN%3o( zNVtamJT_{QCFM&$k4d?5J7gmN!;9tzSAiRKQMbH^(bqbC;^b#=t1j$Q1Ia$MOi#u# zkf4iBDZcsC&q>0)1CKrs$D%ZWDsDKPvkPoMowEy7dLOQcjVgL)FzVt*r_Q4(&ZMsI zQf9CbMW_$Ytwr1zy$mGuPuYs>f|6Id-bANriOsbL1ZqT;#cG5tU9wwyrh|T*HSjD0 zt%_@1d-wkd+;F>3s2oK?IsCy^G%;tC>8(Ijf=*;GLkfHeKCJ8x~re*=-~9OH(?ari-=3R29F z2YH&1%r^;V$>uvXfgW14**xtXEyeP!$S$tXKA86{Fl<-o zB9A6Si2r8n=14?i%nd5Qx4LJw4scresUWvPL$}?ow@$X_U&MCMQfZIh-P}z8g^P9-2aw*eXC2%A#mT%F498 zC5$f7SCYB}HmXfl*)2_0*{x&2%9d#~hD_JEy9%8cFz&8XwFA^P62XOL_$zqpV#&eY3&$8zpW$HUOUCj%Wminikn zY0UX^S<)Q;;3e;3VLY7(ffQ~E^>V_;CTBcZr z9zFz6hsnXtzoqPh5(}^of=3~*Jb_d<)haAc`YI0ZOhkSd?T_N4O9$GQ!yY5ZLI%cj zNVDG97rn1QL$gKuPO*yKpJo;9>zh)bDAUn^)KA`7^svl1c@G~GOi#k!v-|2Kxkax? z_aoS3P%5KNwF(d1sid7O!i`ACuJc#)aqCfoAr{be3J{O+{ z$E2rlg!`0IbRhHBfL?CSju@A3La;ia)_(IuC*+ZajmFuSaW{t7y-`FYdo(R4FX(9}vl7uq%0IpSpO^hk8sjqB0cz zJWsJkqz-)#zky80ML4aKTbOF^>@%0_ix6zoA}PC{0kvH?a+J3+joiqCCvnm99?;x? zVn)ol)r0d;M$4tN9k%3CjFudgFLj}hv!N?2)sO%VtsA`_lr0p7+_6=pMx3I1WzN)L z^uE)LEr(c}R^C?$Q3Ov;#vBIX%m;BJ3yOZI!=<|)8>2Il%1Q)^gJS@DzEy~`z8z0J zOBwH!Ikx+Is>NV=-7-9z>wWV7paWUqoxOY-acI?3@d5-nTJom6qIb@;aKqTorSbff zj{JW8?KuUs^})S|0m0xVkk`SS61$eL$g~pLP0N;dvr_A`E6VZj&Rqqwm%FY#W4!g%QWn2=`eF{GqDo@~V=4eaubBCqIDRGPF%`CMG~f8in}7RMhP z-%X9w837O_)#nXhpQo^70E2z10Ok~?siE7WA~_0ER<_v!)Dhy7RvZt~Q05FwAE-0& zxDVzHpK<5vl33NL++wBGsqrBu)FtAXBcwsA%>nhuvDTcjf2+xC$`L3iRkMEH0(77k zlZQpG;;FhSm9kZsX}hqd=Zj&+mwyBozBY_gpuH2G!CBECt{x)YZ*t?uhg*lIyt)C2 z=Y&5kRzp(UQnXS}7mv>S)21P*6>F9)mMPW-FCH^$h8Npn`5q9UcjhodR(x{KGd%hihw2s$ZSt^q-BpkMrVYfPM?G4>>L8V;9R zp2JzpZ@N}<3%ifB_ag$iW+GklRgcGaaCXAQ>f(v+J)Iv|3 zXs`<%fYX@ju@tVL_d}}QDPEsrz&qyR6~JD{Sr{ea3U7QBq^6((JpZ}-TzFz~UruK!T`Piov zXQjclTa={n0?fwE-&ajM_T0ERE^|ug-s(9l-fMyA8Mehlj5v=2-}pQ3i^pK(-M^qU zwGev{mEY7wzKa?a8@}5yzIQFgPe4nITpKUh{{~J~&`9Z5(^Q|HX66*OGgWp6_|A36;OPeZH64V#MB+Uh~00&q2tti>tOd!_iY#Od7nYpSH^nD}EUzH{Cm zS(>4cl8QMVyYromb8=2XU;8%&wy6L52CDDY52U=LqjU&f`;gmFIcfV`^fkT+VEr*8_NaZ`gexP5u~X78*kRK8kbnEx3PJ zg5FsuCFS;uV|Xv;nHaV$JL9RX<*+LG-rEAZ-jY~)O}YBP1weQGW>lsfh4)DL`o>x* zt{idS5bQN2v_}&|9rV<%C=UlPcmW zK|&>oN1ejFEr59Qb!}20FMv#lC#I|7cz_Da(HGo8Bzy_h;lZ&fiC1m0#_%QGm&}hj z9G^1M3D8o!>05$MKCaX9rT>ZcH0U@7A$~Ve! zQO_o)f+scVI^*uRGuzdGX8%`rVfNewap*^f4!oqN+LSt8$;<4YayMTIqaYmX}`| zc09h6P$b6jZTt~m7<~*M}C-|c~%l>z1CMuA>$>*B1OL`q_`TrJr z?#Gm8DcvNCTp;E)Oudt%6nH>C+<^9I@UTW4_Z--^WK$kk(1{HvOB{5fMJkPnCsWzz zW@5zBYy#I$s`P^^KB-azALQP75Cw*_2nt9p!c=FGTFFH- zVp$eVhrWr4$pC9`AggIo=d&bn(-82Nl4wXOZuzW{GQe}9l*g0Vo_{Bdpg462Plxow zbn__E_}w3_MK%*IK`y@&XVqzdiS z#GiNw#UoS?cz}cJ$8b1Ng+pb&RWR`IRy}-6ynzLZKnmU{!LclYQ{s)yM1cWMx&~@d z^@e^yj_;oGXjnM}Vj&9_bJ~Z5tYV7lqr)8D#zdG9X2P^m`VwBm#KRzY2u_wIFJ+}exF{omZ3aY7$J_eAA?*#MWj^&^W zDHKJU`m&m;y-*p^R>~1_#eL^MAI*qd;-w0-{6*h1lTlthaSJ--6qs!g3gE-{Pa zW`%;k)*lYy8=MBKTjb+gznX#*oKIU7XI$mNw}Mfq$?I@+;d{Yu_pb=e^}4$O-w+;# z8H4`F^>TgxTlo8K|M&GI`3rD>zrlVa|5eY7AGw|#@-qCXmb$~d{%^{=l08Ydz<3${ zwavtj!6)&rjAr|h^W`rzUNcj}>i;h2$G`fie=rBXY?GH%_|60YY8<@TxG=LaKa)6< zBf$G*P_-U^X?y@f%DtJNDg)DJKC_Kw!-)AVe53nJT=jQ+b3`7BbN&?Id*OxZo8h^z z8E76e^6?FE{`;ZBVW%f@lJj#ctBH3$@M>$M74lDi_nrS@>v8IyeHO6i0k;4}1)P%j z`TVQhfIJD`TNh%6m;-2Y@h$b?;QuVxH2;T~qw)RX<@mOH6TVM872hYm5Z`d8jPUd3 zi$A@JpQ-=Gtm_jD4i&BVcVdOB44<1ttNSg{ihqr^H3$FOTs3xuXmRoH6+Nc(&=`|z*d4w#cV&Ng9Y&U4ji vn$_p;G#!|i20IfDWManECt#X;Lqq00akpg7bK!prO+NUUJHw&>6#e;Mo@uXs literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/font/metropolis_bold.otf b/composeApp/src/commonMain/composeResources/font/metropolis_bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..f9889d0a16ede57dd48a1e868ec2782f9d65fbf5 GIT binary patch literal 23480 zcmdVCcR&=!+c-YEd%K6TdIIaAf^&zcsMs4SR-{-FdvAzH2Sth66^*o_@~1MG?o zd+&{?D59|?F*b~|WDfa0yN8LN@_XO+-yg^9&Q5uzJ@d@-JTuP(_vsUi%AzzRp!R-& zf$pj}JP;wr$q035(9u62P>2xfA=E1aA>`GuM{w^1<5Dm9zK4**t&Y9>c0e4&put)^ z4({Ecam1eONeGDr@I4|lHe}5CrN&P;bZbLh`R&MI|2fve8W_TI||>` zBVrTBXK!nA1R<_0LWZo^knv-&S>g~W#K3aGvfyzL==*_iloi6yg91*q>3 zh<8V*2jc32PpcES{6Cv~9A3WlpGXj1K?u72;hFH|UE=-u7sFiP8hi_g*Fqu7M>Ldw zhZO!jIPLw#{rfkB8>*uU`~lR1--GJ?iz`As1UFQh|MH%KI`aQlmjG#B0=zHoG3v;f zP-nXLZrG5i^o?nHR`nd!|_?gJ-Z#N8e;5gI@ zoS)s5yx_Q!hv3eFdjMew-WN2-H-<3+_fJ~L(+kFGDeAxvLw^6^CP2Oa7iW+ljn+oH ztIN$tf&bz@B6q;;za2ji@MdBH9Mv}L zMYV-7I<8gas-w}|E7Sm?5^pXH;2$V;ex>gVs2%zX{1C?n!21d~e{h4q{nA(A4nQZ8O2f+_SAI*W zUq(&&lcoN$s2>k2ja~;wR6*!k>UV;9Gu$tJ%!Gu<@Q6hBZSIX5Hu82KIlIa1-Cd7}4kBJ=DJ1jQRy+eG=s7Cc0Hf-2R8-`Z@#MaZIwb*}x-2cwe zFDzkPWPF^vJ!g;J?hPB-y%F(=q49CaT15R8En3BfM2E#Ej;mgDX+}fswU&;Ao~jQMt3LEqQ`8)O%}_rG z4}~=GP`cK4!O%-#@YZTQ3gSY*-vi>cxNh(s^*{8kHX@18*FHeD8ldEVfvoku)-PY6 z*4JwutB<0VXIyEnaLAPip-E8NFKHS;+Akv-tM}1ABii73QI^DpD_&lm$8(0Dy? ze1%dfQAL}P`nSZX5LX6K4tP`T@5|f%_5byRRPTV!eBdzW!c|5Tl>oTkITL!r38)Yi zp(p4m`UO2h&(RC?61_sdqTkRTNaZSU&YXfia+RR`vOvR}kQutXDyoiZpnA|14PnTd zK_~eGRqqHW7z`*n7!82|8VQJ6j9#P9u>Sl7>qazmeF~a{Qqf%W9r^+Nh?b${XeC;U z($OZg6YWNOfLb0wIp{pPfUcrz=r&@UE3$EBXq5vh@%gERM4Qa}K z@qP79LK)~!^!}?5&HEOd1S>@cK)o!E=Qz&5iJZh4IVXs@2`Gg)Sxfl!A6F3>P$gJ< zDx)gM6V*hukQYpg){v$xOs778`184{Ty4&ivvAe8TAVvqgR99|kpWgdvw zJQY@%P0+%fXdlXkG8N8^vqFwKTqCYI=fkz-I&neVU@nY{=BDz^`SbiE!P7F!a>#Pb zQs`d6y|O!T_jGUQp5ftKQUZ{*iM;L~i0C+17BI!ad2n7_ea@R}$+h8pxo%t!ZWtHE z#R0@~{5^n}X34f3wR~__+$*_P`4@;EQ!H`-GHpWRQF$Q93Yr4E(Nsjxx4`Gj1fFR&s)(imM>P*sL*E0xvjjM* zrKmQV4|u!+7Kl}_0IWt$&>C0(*1H^%L;4D8hWtZRSa|g8YxyOcfWiU$A(Tvsdsu3H2Ia}$usY#@@Cpm{%|$3U=O0|EaF$~19i&J_qo zZLTR0fB>#5*Aoap2p7S{b4lDJZW{M3_dU0aTf=RFiL;kG#GQo+bB}w>z2IKMq)~YT z@5EQ)Eih&3@lE)ad>|jh_u&Wf!}%zF96y1d$pO9#fI49JO7=svmJ0}dz!9arRk=_ z?53C1-Tu@=x_^}>I3g)7JR~70HYOw~v6r5xm!7ECSBZM-;lcJ2dWQm&7!woHM^9$2 zr(b}*67U5qa^t@ClzqR3-PfL?Z$e~Tc>nN(kmRubqawo+!p238>mM4K5SkP_Iwov< zf4!uE{{#%O2k03F{oUpey<0-;#e~=)hw3Rq^;(T8eQNz1ru)PG&TFr6%P@Op`8~iF)RVe|HDq6QE-K zkeI{}?UA5=Cg}AD`2tpt{RgQ2srQubON@*eWd~rdqaLW$Q}^hF>OOlzG;cssLcG@Q zCXKyApdaE!g(Zv%EA{*%Zw;vxI;8ghDs%lKLVXdh&?$|gU|`m&199yJBz>3eCRv_aY-?UwdS z$ECBZDV6&OJh5upRv2KmvNwR zm@(8CYfLdtHO@4CZ(M3zZCr2MYRoY1HtsjqAm0#6O)R?;lQ|O8noObPnHTfwi>a5j z(9jlwyL6lo-oaX%^u|{C?8uB@#^XxvW+yt|l%z;U)0c%1{ zOr*7B8aRY7#}kb}Xb=2+GjGn>PDaO@91}quk#vJMf z)R5Pvhzk}i`*Eq;`lYE8Q^rqCNKRU`V!U-+e{o`ZR_ac-^VvI&?}^Z>lK z?QIK1>Lk(FGnf&H)wHECZ#7M1RV3CN{53T{kxXVPHN!@^8d0Xs#!5~t%+hUb>Jjsu zgG0M4x3Z1W_0^{iTz8Yrw8gJaX?f~Y=Mby5!`hC764C$Xx1kifnHmz)nAqru&GGU_ zT3u`YZvp}T}4%F_zh?#2#0V11S^-stSUzor`-$b9Y)>cg7Tfp0$@xN+4=x3fb=`C?{v z{t|1~C1U8zDHFy)JqxAg&@0NCnna0f6-SgcjY%`VpIlvOtS;xnh$9XJ5KM>jH0 z9J96uJfI45q_wT4qAB&U+}3z%bf?S?Zp^ioC#%k?zo|i8?_SI}wa?ng*rw~C8r_4^ zGr}#y8jGPhkK&%W(ZR|75#mW-gGXoivY1B5MyxU8D(QEZG%bX$+RPA3b_B^TMG6 zGpFvh%HQHZ@|c+3Y`>OQ=t^A~|0}C$VaJR8s1uz;D@)XyO%mm{d17o_ucX1+NSCAg zX*srEGzDViw+2`)n$nucYyfLH2ha=Aa<8aBk(`6oN_49abasJ6J>~BPV|n`YDKjU! zMUP*#RZfq>^3;hllEw`pa#7!o8jx04R&T?jacT&W7ve?4BKIL2^aFLK9j&TGHuu2P zlevg8^)Y-;et`L?7I?sf>P_Tly_4z8g2J~lQ&@RcQ>OgbqvLzI4jLZf-KfLPGuCUvBT@Vub_aYR?~>g@cot<(J9ffu?})X8ktUH z;eiXZdw}%nElpuZMOt5CN7M>*3q38edJ;Ryo{97sbAs7Y<9%J`#@uSuVKuC#STYw6 zB;RA@s9ZD`y5^6&SE*nrGS1k6mf<dN>m(RZ`ZSk4mE1#L^Fdlp{^i1@=T>zO zv}c`BR9uq$hWAITvU4rwD(#L zpqiJhgZa&kpWpn}`GIH6j%~bZ-)Q#-;6a1IWaD4U1$p&gLNm?+_I3=88XRqv16ilv z)jv{X=RH)GCAL>KM|hL0+SqDR9iEW3K@mMhTII>tu{s<&iOV8#HdW+Y^N`4W=gws8 zJA4Ss60Pv!9_4d~Txz}Bs;Cz-cFMo+!*Ua1xHw$?l}V&55{ zL>}LLqNhK}tpC}I+vQYk09vPhpic{r5P2}kgC=pOVD#EhEXdARD3%M3WBDSUiwEO5 zSiY1j1Cg5N%>!?s~oIN)<)a~+DM*507)oc zfaUXLxQNY&_5o?pu&_GeN5}o0|g~mR+E+ss?*5-@)$AZqR$J z05NJFNZ5P0A3!eM$NdO9R{Oby+-7c*rqSURaEm~?^#X0yLD0=CMu)hi+!By`>vGFr zw`Mu_6G+1KQ3GxTw~|{0Qg}nqdL0ExHiuiytp_phIB3TjfkfOGH34C5Etig(a_i6u zZUbrtOxhNZl23vhej1(OwsBjz?c5HKms@Zx6SJLoRTc*WxLEHuIJZKEMqhRhLcaysWqG=Cw z58X!(P#$*~c7Ybbj>lpUXxD>&us8RFd&=dZKHOFGh`R>*fWF)f?mFlu@1$06Ebio-I8qYkGXt~um8*vdGRsZhqV%;+-H%WNr=Rpv;UYh|96d5de| z2DlaOhQskVJQdH!oA4og5?{uT@msl~Tvcu?&z8@~MP>VzolrKd?003;%U&t_N4XK@ zmX^CyF0b6Pa&OD2CfQ^$H8Hg|bu&#dtuW=93d>h4Kcf7?@@va)D!;4z@$&b}KP&&y zv9@Dt$8L@T9pfD*I)3lC$Fabvfm3rQU#B5XGn@`Nop8GBl;`x9v*hgTT*G<0^DV`q zbW}o=#mYKmw{ll0u3)H8zC!g1Lo4j8aJE8`*~{G6+{PSijx$d%uQq3zAGsJ^yj&W& zv~}t4GS+2^%K?`hmrE{>T;99bDjF&(6{}ZlQPIC*?}`x>Cso{1@j%536-z4lR+?Dp ze5GG1dsSXh`L1gN*X6DU+?$!LO zjjonbZC$mG)%VrtStGQ@oEqzEWY#!a<4TQJHQv-H_AKjJ$t+ zYZh`N<8a1$aOX2NoL-l?K4VwL=FFX$o8Wzodv!M>BQqo8o}kt*+G%DtKToDNrM7>y zG)HwFeKm^tWL(|VCByiJnau%LBbiU3@oJNmr;Tr!Z)uQ`R*o{)9P(Dm=xvUQHU@sfLdc_53VC1B$13;t292?*LY}7M*KPE*78D0`Al55K^}2N;gao+tUC&=Vj|aaRHbpFWQ{y5`h*EI?_qMw4NL#(l1%RpIAMyryWVZPQ-hL+RzBy=LWo zH=?|zJB#;vVztV3yz>2p*~D3$4r@vFU*D=XL7K6>G}_#Y{X|+#Qq)}PBGSc@lFeMy zR(QCTkg)l3J{Uq$w-(X{D1;AaQCr@j<9=TS$xk5;HfD<~iHTqkcR>NR*|N2}=^QN2>z;B$oT z>p)ly`z~Crya1c>FqgL<=efj?0FsB#((2kLT^~f6elBO$0_S11K)u;adxycO zt_#LNTCxnRIu}jpkLjhq18BZJrcbqibmE*&VyNT8M@0EVQ(#ydupsC~qw=a5mP}fL zIr#QWO!Frbx|9VqBQb0cb-Ht$K%iD&Lqc7PUAJq+HqjAbsRs5&E|fbr8KsZ%YHgJNgi(G*e`d+eNJx4z-3_d1 z2Py$bC=e`Xw8MKEN~M!|nMAD!BFY5~qnW>Lj}Jtj zA&1UkI_df^J;*9sr%vP=06zr4Gg5$M`JFlm>P}74%3#0K4CbUuNQda}>=1P-@-vz? z;5#l8@CN*%17U8T%~OfZV;#OT6Q}ei(>|A*N^WB3Sn7C#B;CS0ys+|$wJe^ig>sOF zx=kiQ+UExRDjrkk;u0-;AMz|$i;XUty_9?`zO99w#K2VRS(4|GWtxam?DwPAP0T5k zifVoeN8bRvS*d2~`r9AWVpT(#qiwLU?cly0`}fN_GS1SxrPwL^)sQD{Cg5=&P^a<4 zwE7g$puz{Ob$vXct3Hn~kHmE@;41*CCzZ_UUp}tRBI}A}?HgF#5bBtk0$isfHD6bd za*y6pZ=3C+4+xqw2{YS5%*w8G+~)%R1dt{HBs#=M9WRR1U}TQnM7n}Gvirt1qeLbc z@9z=WkLp;!E)(s4n5YA10z!irJY1s5w&pZNZ7$k|M-d}Su{CGO@CD+qsqiqNC0as% z^2JXNzzF1hHk-Aebr_grEd_lVL|7e|4O*zZy;@Ai>(s%XogUF!UD!vXD-gA4A6Y^j zpO`+xGG+RYkW|XKGdL+^9%yT zXB0J3ADVypATpz*3<(_~?&{TPou6BG(qNbO`L5O`)kO*stM(oiN1wT#blWYGSe1;I zqaO!fwkWG?v)D{Ct@sI4Cdz?*Q@@o%#_St^{FE!5%Dyp=82Qt>a4S7cKZ)B% zt{%|Gl~t)iHP8*%LD;gg5BndZ&FMde&NV0f!BKr+u4Pcg$+`T_!U^Bz$v>vZ#- zJya8@9?ZhD^7RjA2~3RH=FWtANVd+SVQ$-Mbt1@tPtIpS@6q(yVOYB!t9cX}Y1ge;+7ab#D7h9?FzU^W z%46}0Oi_7EYcDEzOv|{MqSLPvjxGjtU+h91Hvx^-LbUZj4_p&OUcnS7jX6lJTqn+S zF?9^Trl_i;%O-5Q39HpMX-t=Dfh2#fS_?E8u=GzObgYi> z+BdBG&Y(>ADHSi*C(LJ!0Z={w-fJ1vvVixVpJg3L2;hC&&+b~TXSo0*>qs3FsgigL=)As?w1P_L`CiB4 zM^J}$ID>FGYL1ZO(({X#P6euX0)T2E8bWCCbUP45M*yq^rUT}r1LnY5&UOIAo_G?W z+3Kc7@Kyu*k@-U#fp+O+9_W{H)Imj6VBz#;aw9Sj2%r{5bB5xZL$z<9fLlpID(*<- z$5=UnX#eV+BdCrj3O2J9HLxi!qz}xJrb7sk)C|d z9iwF_=;;Kto~oM9ZH~TEKO}io@~Xtum6Xy1->E(29fkpy*cO9}^|ZR!ytuqSV3$FX@xQ zBUd|9(+wD~qDn;-%|Viqbz1Aj1HD;eqmtE`HfHY7#<*6X=)HRpLRh|SLOqg=>4o2` z!b|PkkF2VTvoQUn-tLdj>fTdB@uRx97lw(zQ34Xt;Of}RmR6ON2F-+Nc4|0Tpar#O zgQ?RMOs7NGh2P!DOxuj@8r+J>c!37vOzvr-M#98sW}B*|u6rL6-vw5A<#lrxQiE7l zN^_>qnURz9C=n1>{mIr==bxu(2&+p;w`|+05|)#m ztTui3;VoB4Wr2=U&zQ5;ZQPo^W=7glOV)jHV_e9}QEttGgPL0Vw)f1nHKnrJl$LqN z${0=C@dFK;U#kPu{pc-~`u2pqXV(Fn!}nN} zx0LvsmB#?+EdY89fZkriLFxKLNa3zh6HNtGD1HBg1a^-a7-OXim`R%t^s4dVnPWFD zXAcap48kU8_40|pg9q7OPQ`ohHJ6EG4=(V>%&jlq+~PM&X9uMPqpKJdNzd?IdK3?2Gv6;a+j#ClN5da{=VnpcsB zxavVnhox{=sA)VlRXs(Zl+y2a*T>`j>~$xSo?H|RIj*_neDM|(&8ZJ<`&`wm1*x~S z$pX4zm@L`XTrT4Cgnq+P%#(qP-pj{hiGzvd>p-}VIe?SuD6`Kr5 z-Yv2ppjR&&AMO)P^oR5n#98b7D448~Kv|C#)di(^m$2Xj>xJmxT^-!I^ot%i)~Y7K_lNg5S_2hhJ9&3zIPxX-c>NpZxHIga%uK~IXjyGS&?vx=57lk2^_({WLp+N^bbq78m~}2r*0Y_- z1u$M3z5v_b41$0h-KTF08$QsWY5Oq%Yn8f2?P9+EK&&MxF=J!I&BF(-?3;@Nw{*=O zZ0*!Wd?P6to3@E#4;-6x!YvZ0+^XwiE)6?kQ8HKw>uA4Yphu&}Of9E>tCB47Bgb{k@lREM1S? z8}(FQ%VN3^CBxSKTXS2Y>{Hv(M|%h?Wc0~JqAz0ib^yoz2&QZRAk)I70OcNST$36Y zu_Td6X@s`T*Aoi%#jcoX$rA9S6?iKK5`GUzKTMtMnuN{Q*1&s30Y+`ZTHgh?D%ssN z4A=I;Ehd(pzLzLBXdkuNkFcq2|M=XGjAM=UZ=fitDL@9CVFLmxu|q?=Rje@pUk2q& zfw`KlW8Pje>ALbt17lY1LDZ`$WQr~RTdX{T)#ep!tR4czNNRHouloLYYH><)T)Knv z1Ro)E5Ujz?i{rJxgXC1Q+Og0X_H#a6`vx%iFOVbZm?^5at?33!W31SYhg=_fZAN0B zN(n?%{`$E1=EWDTrcR|gsvuh4`4))c;dULB3i6VpI!!uvaL>g{`$zR1I6S1gC9=i| zN%?rqu8itr?Z?o)UF6hFS1Q)YgI&Zbby_hIx+ViguY^=efXHL0k%T(H#p*vct^{lH>#};>-;rfH;0?Qkyh^7v~bh1 zHLEJ?iQ;hE!@X`!wIufOBBn5`HdB{(!SIG{OhFrRbP<>!H_)FsN{Kp;a%)N-&G9Vw1B2jD4M2k3jPCAjZ(?(3&vrcs|yhlJ~YJaxY!CN%7FM!JyXpI4+-RFv0 z5Y&RBZouRWggQ(rUaV(15l>csE>i~|Adm1Nmp=NNb^thYpi$Z!6|QgN6*HY*XG2*{F=wgK3PRcUYvBqmKe2C^!xG0}_gPKH>j z=tZ2#)r*+D4tU1Uz&2!(h8io#O3>Xt@B`GiS|sSGu^&)FTh5KjRREaT3Yf&@fEvnG z_MA?w2X)ZIj+NvrV8|-SwzfTeqKO@APiSJt+ETG&?Fqn_o$cZ5$Yh9W4}jv^Yc*Tj zzA->Jje(i|W*-@*w_-_qnyxKfE3~C+NqZp8o7>Yq%wSfQY8glC8J9xF47+ZbJ4!3i zw;)Cqf?CiYh(t{qMn}ORm72_-1u!Q#x-^P~f%>H;4X{$lP{Z#n^`IWN-@Jc!yB70c z9)2}FttPg4FrmRPLcu^(dlJ3nDl~=EGqPqtV7}?UdbH>TR)^?x(J^gYJE$e2A2PISO zPxJ__0z^%90)@jp)d^ZhYybACuIdL8J9}UYTIMCvmlBKn1Y8-7qenz`#Axc33WDAA zLf~8_?h%kf*klms38-kJPcNbKEFSlnsmHJh<07mJXn;t6hOLKg(li@a5H4=pb9nVh zw@0Uj1a#`xw|QWI0}U=eK4Q|1XrSZY!92dzShM;>#w#;`xk zEk>MqG1+={?TKAi-EuDuYu{}^FbGbaHEmIjI#Zw~7g|-KyVU36ueO<>XDKwA2BvMea)+~B3*c-VqRz?(dJ&pgiGp;^={&~uvfS&O0CfK}vU zE9!jn>ei$Ct;}skV0|yQ?jaj@g;=}S6^ER<5?kN~8WSgwB_rsdrf`I)zjYCNZ*Cuy zoqH+g{Nwxsy&A5t_DtzFy0e>!tu5iUE_hvnZj9MjQqrIVOfPQ4RVXQ0_T$hLXWHr- zEI0G%P^y~OtXRHs_vHQG?6z)8+!~oNe)){G3l^_*P#P{-HEYe3MbT@9FAiVfpv>R7 z;^3kkYnCrsuy)$Y>8nx}OquSW^oSZC7ZsD}Fn{-o{kvUPPF_BK!sIEF!j^~353|Is zkIRe#04cMkPXcJuCe8=Y8RHL3-#HmFMob<)BWnDV8Q-KVnY4J~+F8pNLlGHkwq$Hy z@9<67l#npjNh_wTfh9b_x9BW4O&u%HXTG5SmDrny#O9Vt8_|T@u+3Ce6$W5idBaY8 zAK$~wV>4FLhhfw6)Y~MVHiCahk~#pxO4U|-?8LS*kXPNKoyUMUM3c;qrLF2nk$FhB zii6Es0@VRde_Yq1Yiz^&*)(%=Db5CRgNI~V2-D2xIrV)`Tc>cD>IXsntooYQJr}D> z`)O;jz7)b*i%ii!>ggZVrKCpUducc(E9Fm>yUffLrfC$C|15Qle%6{!AM=XHuu# z0J91e0YOx{c^igwYzl0+Hjo$^*RE9~SH}I)f>wHS@6hGbmJWBtk)wyi^mOYu{J_x@ zxAwnV0Mxbnd;(%!rC$~jI>sdln`~ZMz(QiWkJa)}#w94A5%pHa&}!P~DO;!57K8$i zr4My740WzjEA~yRP$S zCGo=8tO2VnIjc79JnuS}Ru*rsTT2ZqEz=-T%gMb4Fu`>ma~8Xe9pi6lYrL^#Y&Yv% zrT~D`{i)Aniw4*C58()ar9Lap9H%@0QN&A|H{Gz@GX{*^e91bGI*U~3nZsJHuuN|Y zTR)T5rTDqdWtGL?P2tBTS_Vu=i0BT5R1$s1jc5EMfQj$E5$>|eX8Kb;YgW{bHp^!} zK@%_?`9{dr!;7A?pIXpn#qC%#$;1w^wncw&Ct+>c#M+uKT@yVh>PIXg>O|KR+k01GXIC5VX{<(iDJ(QJHF+GcaW7@|IDMXzv@Z@TG6`9`lqE^XYo&l zPB4zub!Bz>F|Mj6qtekd;UjmnC|x*86U;^FAB)o^Q(Zkwjn~7}_>XYt%EV66v7cc5 zT?c`HXX`$(br4uXtp=0jEcL!my#-posKXLM`|Ml-Qbi4QGsIjfjWMg$LowYK4&!Vy zYk2?z_QKA9w=zP@ReEMlNuQ+0L(cC~?qRk1X8aSeDCx-ra+#QEY4XNobyLcQ6p+@+ zy?G8Ox=PdVwY-&-5IZ4iW@UQcxMSwl3F(QCZdVF9*lSytMGi{L#YUZBi*8IEXm|xz zR^c@hX!)_Qqtx;FW#hF)XSN?W`%nG04LRV~xYDJb_rqSMIVg)ey|`>tHq#nQ@*h*9t1`@1jh->@ z*`9TDnT7cn`>q+AzSotugIP)gjkl6_BoB2R9ud}YhQ$Os?bLEB?-nh;91at}o_a%{ zGY>~u>H1sjpfq>s+@(uhSAM_phZPpO?-pAj-P!l^u_taJnAK-)H5nOT_4dCe&6zxR z(j?ckdGNDz_Ro`8^~2N(%VHC^6l@y%-Fw;~moC%}=2ie?oc|v97V4kN{AqdPy`v|u zojn;bc$KB#k$7!X{lg95+-BX{%wlB~JGH;Y8W>v-68otq-WyG;zoVVn=ibPrC9voK zHwwF0KCqe457UXP1ZZx1+AWZEla%q`F)G;}R52>!KhhxZ=dz$1G$@xNX5YK8teaUyTCr_gT9H*`F3jbwFLlv=tRk>(`Lx>U!qc=GM<>u~0*FA}cVjk_ zH3gt_COf?wLs9Jtsi~0DOH->t1j{!-k!>EUE4kZhsBrtSSq-J(;%lQ#d(|+~V%CU( z6YXhEK^k?%WAg_iD~6>EocJD+vT)JMXku~nXeK@ZYR>sIn;wmYvkEJ*vhu`wtn@$8 z8hD_W(tZNpxdgs@MurZtC_7GUAoF$-le%Li&VE9hKLP1&Fj|l8sg;TdGRO!rR7Ca+ z>F+iW*eCnrCY&4X^q{7GZuEM)H&$Euom%){`)SW3hp>r8Fo#{V{*|sz>B6V9@^0sY zd#M8~oD-f>-@VG?G_?(L0R7Cm!@1U!tzt+(xA8u1z)#l!;?tduq-|(B*l6el47xjO z&&=I>_X|&4w<}}q;~mt;T76M!u(&sIL?)=q9l#Vp031?|r@7zodvobT#*v*CfIo1> z-sr1tuRi2~^1LSlO3A|6c-9Mk$qc}=HZ(%ZlSp@%dxz~g|LEeK2bZ>w=se5Xa>jse z?c7X@QeUepUpQyba!G(G^(j(b6;;YKk32AXcXVdv;q9lkU8sMs()dB+hK>mxbUbW- z#6E|@!>119x&GGnZdK5-xcjuLH(>dw-Tkb}E04iLdo*@sj=uxCj| z>}~%9a^N%^V(lf-uy|r>dK;^QgiDK|&{1lBE)@DyxTI+WP3-ka%w2jQP+wqa77^=e z3`eZnZoW!j#mZIlv?EcMK$?VjH7`x$0o0qBI7dhQ1M1CKJV{gE<`dy2=zu4w%QYWs zZ>FZAfVntznYw%#ZM^LFWfF^BX0)U?FDYB*yRz~-42N&uEm&B#ES2ZaD_f2(sZi9= zd_=KZE1BTfZ5`c^ye%BN>G0SS} z85Bb$Lsi{|DakO`@S|b5VU=OMA;YlSaL91VaL#bqaL@3_@XYYa@Vnuy;ZK826vT33 zMX{<_Q>-ich#kb9;s9}|I8qE3r-`$~@5CR)#bUa+Ma&V;iPywD@fY!x_*z1e3}-eg zNR=gbshZ>k)=M3vj#5{tmoz{c0v1fsQj(M^%>)ak1=3P!g|trE0(MM0rM=QI>7;Z) zx+>k0a-|2-3+c7=PWn@~WRi^Kj7~H+MJOKlwP zO?JXr{z%dgYqEL^TzX&w?0afs?NB<{j#%npwwI!=L@jW~vavdxW3Pt0kh%1J6R^?H z_fIWRwgIqF+L81$#^B5?Te5bLt$WZIXt1T&NV-dHme6$=&ViOb-lFW= z_t>q;R@L<^t`CMPO=U0gjU;b@4Vjiih9h*^@s#FtfM&_^=<*x8e%0C-54>0q|G>@E z5{_T}2?t;*f-Q;rU80|yk?*w@^CShil-vsxKHmw@d~4-B>$J{UG-#Rrlc(~HQjFWz|i zL@=XmUa?SiMls|uE(~;e> z+W8aPE`cq}-FeSMo3A@fm)K@@S2T?zW5ABcLt=f{W!95k7FkW`?dIeI6fK+EVtEv3 z`DJz%js06U?FX;W!FW=+BB z%Hx`D8zaa!zyM7hoFapMM$t5Ba`Jp4!>-Xi*fh$9J)=HTy2bWzGiiG!BS0}X|3_kq zOWK%arK|LPQ>Y8H5%yBq4kH*nfyvOxv+MWnca;;zVRAW=?-b5UnBV-xBq$P3`Ws2qozo)3DmXaCBLTfwaBC$Jt00u9<_qs*lf z`8+XsLrBbb#{mYM0htENQf$H{ym1>$xm~cuaszf>?1#NZfURDWN8+Fn-D5hr$*vu< zL$1I`WfR#S|BGo6d)l6YL6Nb?zKap}+$ebql6w>RE$l@uH_B`ATEG}7lgQdelsqkW zcrso2^MYJDD0fzh^S&q4`{}Kx)cc8S?u_O5OCp$?C11ue+=Ub+t2edtm-aJ@eRSQp zOh?f_z_2Ly&F`J=d)DgMwsxIsZQojDo5x^s2aK(@5M6x^Iq4XfF&BW{9wVEt-QIfq zq^oROUhF5oY)~jaAt4dr!$)R???05ebKk+pokOkiIM%-K&qBHr=0Fhaw}KgsAm@$C zbC!F7+WRz-+hWrWZI?7#Ht*h%v3FN=M%c*csL`Q2qQE@$AYM=8eV|X5%}rqJoYZn) zfy2S+X+chhA%sH$L_VA~vS$zAi>>mbbLSrApX<@mB7=EF8D#%TvF z@tKxGQjPv#V*~9gd0TR#WLwF-l1EUN|N2+*@Av5ZovP%{-`=l0CC|V{=c~Vx-%9j* z+`bC>+gtMM-`;=ol;r(ez~7%Gc_rum{`eOUv?IUdi1z!JU{nG(f|gv?e*YE@rI*|; z`LX0jxMa>*@0Y349JHdY1U}9XWf%kgvS4qeA<#T@ikk;ka6jq4HSnCL`!<4w+AjEQ=MI2X*(q=r;Fg{* zzdO253wZ$6Tn|dGBKe?QM*{8z_n9|7e|FGAy6FBcc7I70r2k0|`AG{w z-)eqz#O~LhL-pq#J$#Suf2gOQZhvYa+)~|t!S2_evHJ6l_T=;Q*q-{ck^QO1w$=S@ zb^k6sWfd)i54QXD@Iw7LKugQulwtkWI{-g z9ulO5plO;P?JxCfA^mkfTzjL13(4@z)BS0Bt~RAl4RV^6lK(}A@a3sN_)8D@ON-^A z^yfxB*Zoqz7CuS$*VV)8>VA{%H)(#jk1Y|Nr85rbx(u#5Sekl)4XHO+Fm`~OlDfk! zVx>4>Zzu*R(mzJ_pRQf*Kl_`&5#fnX=v8Wad4pbjIU#D02-QDC1r1+G%k%8iHn=e~zp z{0O&DO@SNevcPI{58NoV94<&X2RBe%fI4i4IuwBIFRZn2OPm!Ut}5IWD!`3wwZTf1 zhcPGzHRqu9PI}E0cxrtj=zU_)`$W|HMAZ94()+{+eKHt&LaSXFy=O4wE2p>1sJDpI zb4rlY59~{|e&qE$f}Y2q=Mmv9y(EC5^<){nMOg1iS?@_1a@hOY-piaG$Ln#t-pc~? z@_C@_T02F(ueE!`%IoEDdO5saj!;_8B(Rk&qxav}r5SXH1|6346)Ypz{uV&`H}I3; PYN|qwFMt;Q{rCR>Ut5Ut literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/font/metropolis_light.otf b/composeApp/src/commonMain/composeResources/font/metropolis_light.otf new file mode 100644 index 0000000000000000000000000000000000000000..f969dcc3a48c2e9bc956c1d547bc79de2ae2edb6 GIT binary patch literal 22920 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 a3YH1hu1}Ev9exsSnEL}`g%#$@-~R`>396d_ literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/font/metropolis_medium.otf b/composeApp/src/commonMain/composeResources/font/metropolis_medium.otf new file mode 100644 index 0000000000000000000000000000000000000000..b8b917c154d67a5cd3678506e7cc05541b9ca585 GIT binary patch literal 23200 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 b59kCB&R*Y;5`E&TTPEaL%JXi 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index c404e80..6cfc9a9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -1,26 +1,20 @@ package hu.mokegyesulet.it.dunestrat -import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface import androidx.compose.runtime.* -import androidx.compose.ui.unit.dp import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import hu.mokegyesulet.it.dunestrat.ui.AppTheme -import hu.mokegyesulet.it.dunestrat.ui.dark_background -import hu.mokegyesulet.it.dunestrat.ui.light_background import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun App() { - Surface(color = if(isSystemInDarkTheme()){ - light_background - }else{ - dark_background - },tonalElevation = 5.dp) { - AppTheme { + AppTheme { + Surface( + color = colorScheme.background, + ) { NavGraph() } } - } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt index fe50f14..3e4da34 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt @@ -1,23 +1,24 @@ package hu.mokegyesulet.it.dunestrat.feature +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import hu.mokegyesulet.it.dunestrat.ui.customShape - +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import hu.mokegyesulet.it.dunestrat.ui.DuneFontFamily +import hu.mokegyesulet.it.dunestrat.ui.hexagonalRectangle @Composable -fun MainMenu( - onPlaceholder1: () -> Unit, - onPlaceholder2: () -> Unit, -) { - +fun MainMenu(onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -25,18 +26,51 @@ fun MainMenu( ) { Button( onClick = onPlaceholder1, - shape = customShape, + shape = hexagonalRectangle, + border = BorderStroke(1.dp, colorScheme.outline), + colors = ButtonColors( + colorScheme.primary, + colorScheme.onPrimary, + colorScheme.secondary, + colorScheme.onSecondary, + ), ) { - Text(text = "Placeholder 1", + Text( + text = "aeo áéőè", style = MaterialTheme.typography.titleLarge, - ) + ) } Button( onClick = onPlaceholder2, ) { - Text(text = "Placeholder 2", - style = MaterialTheme.typography.bodyMedium) + Text( + text = "Placeholder 2", + style = MaterialTheme.typography.bodyMedium, + fontFamily = DuneFontFamily(), + ) } + Text( + text = "abc ABC", + style = MaterialTheme.typography.displayLarge, + fontWeight = FontWeight.Light, + ) + Text( + text = "abc ABC", + style = MaterialTheme.typography.headlineLarge, + fontWeight = FontWeight.Bold, + ) + Text( + text = "abc ABC", + style = MaterialTheme.typography.titleLarge, + ) + Text( + text = "abc ABC", + style = MaterialTheme.typography.bodyLarge, + ) + Text( + text = "abc ABC", + style = MaterialTheme.typography.labelLarge, + ) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt index a94bd85..a9c8428 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt @@ -19,6 +19,7 @@ val light_errorContainer = Color(0xFFFFDAD6) val light_onError = Color(0xFFFFFFFF) val light_onErrorContainer = Color(0xFF410002) val light_background = Color(0xffebb000) +val light_base = Color(0xAAFFF88B) // minden mögött lévő háttér színe val light_onBackground = Color(0xFF1F1B16) val light_surface = Color(0xFFFFFBFF) val light_onSurface = Color(0xFF1F1B16) @@ -50,6 +51,7 @@ val dark_errorContainer = Color(0xFF93000A) val dark_onError = Color(0xFF690005) val dark_onErrorContainer = Color(0xFFFFDAD6) val dark_background = Color(0xebd38f55) +val dark_base = Color(0xC6C78E05) // minden mögött lévő háttér színe val dark_onBackground = Color(0xFFEAE1D9) val dark_surface = Color(0xFF1F1B16) val dark_onSurface = Color(0xFFEAE1D9) @@ -63,6 +65,3 @@ val dark_shadow = Color(0xFF000000) val dark_surfaceTint = Color(0xFFFFB951) val dark_outlineVariant = Color(0xFF4F4539) val dark_scrim = Color(0xFF000000) - - -val seed = Color(0xFF825500) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt new file mode 100644 index 0000000..f60bae1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt @@ -0,0 +1,50 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import dune_strat.composeapp.generated.resources.Dune_Rise +import dune_strat.composeapp.generated.resources.Res +import dune_strat.composeapp.generated.resources.metropolis_bold +import dune_strat.composeapp.generated.resources.metropolis_light +import dune_strat.composeapp.generated.resources.metropolis_medium +import dune_strat.composeapp.generated.resources.metropolis_normal +import dune_strat.composeapp.generated.resources.metropolis_semi_bold +import org.jetbrains.compose.resources.Font + +@Composable +fun MetropolisFontFamily() = FontFamily( + Font(Res.font.metropolis_light, weight = FontWeight.Light), + Font(Res.font.metropolis_normal, weight = FontWeight.Normal), + Font(Res.font.metropolis_medium, weight = FontWeight.Medium), + Font(Res.font.metropolis_semi_bold, weight = FontWeight.SemiBold), + Font(Res.font.metropolis_bold, weight = FontWeight.Bold), +) + +@Composable +fun DuneFontFamily() = FontFamily( + Font(Res.font.Dune_Rise), +) + +@Composable +fun MetropolisTypography() = Typography().run { + val fontFamily = MetropolisFontFamily() + copy( + displayLarge = displayLarge.copy(fontFamily = fontFamily), + displayMedium = displayMedium.copy(fontFamily = fontFamily), + displaySmall = displaySmall.copy(fontFamily = fontFamily), + headlineLarge = headlineLarge.copy(fontFamily = fontFamily), + headlineMedium = headlineMedium.copy(fontFamily = fontFamily), + headlineSmall = headlineSmall.copy(fontFamily = fontFamily), + titleLarge = titleLarge.copy(fontFamily = fontFamily), + titleMedium = titleMedium.copy(fontFamily = fontFamily), + titleSmall = titleSmall.copy(fontFamily = fontFamily), + bodyLarge = bodyLarge.copy(fontFamily = fontFamily), + bodyMedium = bodyMedium.copy(fontFamily = fontFamily), + bodySmall = bodySmall.copy(fontFamily = fontFamily), + labelLarge = labelLarge.copy(fontFamily = fontFamily), + labelMedium = labelMedium.copy(fontFamily = fontFamily), + labelSmall = labelSmall.copy(fontFamily = fontFamily), + ) +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt index 42b5cf5..e1aeea8 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt @@ -1,24 +1,53 @@ package hu.mokegyesulet.it.dunestrat.ui - +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.shape.GenericShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Shapes +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp -val customShape = GenericShape{ size, _ -> +val hexagonalRectangle = GenericShape { size, _ -> val width = size.width val height = size.height - lineTo(0f, 0f) - lineTo(width, 0f) - lineTo(width, height*2) - lineTo(0f, height) + lineTo(0f, height * 0.9f) + lineTo(width / 2, height) + lineTo(width, height * 0.9f) + lineTo(width, height * 0.1f) + lineTo(width / 2, 0f) + lineTo(0f, height * 0.1f) } + val shapes = Shapes( extraSmall = RoundedCornerShape(4.dp), small = RoundedCornerShape(8.dp), medium = RoundedCornerShape(16.dp), large = RoundedCornerShape(24.dp), - extraLarge = RoundedCornerShape(32.dp) + extraLarge = RoundedCornerShape(32.dp), ) + +@Composable +fun hexagonalButton(text: String, onClick: () -> Unit) { + Button( + onClick = onClick, + shape = hexagonalRectangle, + border = BorderStroke(1.dp, colorScheme.outline), + colors = ButtonColors( + colorScheme.primary, + colorScheme.onPrimary, + colorScheme.secondary, + colorScheme.onSecondary, + ), + ) { + Text( + text = "aeo áéőè", + style = MaterialTheme.typography.titleLarge, + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt index c1923e4..d7656a9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt @@ -1,11 +1,10 @@ package hu.mokegyesulet.it.dunestrat.ui import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.lightColorScheme import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable - private val LightColors = lightColorScheme( primary = light_primary, onPrimary = light_onPrimary, @@ -38,7 +37,6 @@ private val LightColors = lightColorScheme( scrim = light_scrim, ) - private val DarkColors = darkColorScheme( primary = dark_primary, onPrimary = dark_onPrimary, @@ -73,8 +71,10 @@ private val DarkColors = darkColorScheme( @Composable fun AppTheme( - useDarkTheme: Boolean = false, //isSystemInDarkTheme() - content: @Composable() () -> Unit + useDarkTheme: Boolean = false, // isSystemInDarkTheme() + content: + @Composable() + () -> Unit, ) { val colors = if (!useDarkTheme) { LightColors @@ -83,9 +83,9 @@ fun AppTheme( } MaterialTheme( + typography = MetropolisTypography(), colorScheme = colors, content = content, - shapes = shapes + shapes = shapes, ) } - diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt index 7ecc40a..e68ba9c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Type.kt @@ -10,30 +10,30 @@ val typography = Typography( fontWeight = FontWeight.SemiBold, fontSize = 24.sp, lineHeight = 32.sp, - letterSpacing = 0.sp + letterSpacing = 0.sp, ), titleLarge = TextStyle( fontWeight = FontWeight.Normal, fontSize = 18.sp, lineHeight = 28.sp, - letterSpacing = 0.sp + letterSpacing = 0.sp, ), bodyLarge = TextStyle( fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp, - letterSpacing = 0.15.sp + letterSpacing = 0.15.sp, ), bodyMedium = TextStyle( fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp, - letterSpacing = 0.25.sp + letterSpacing = 0.25.sp, ), labelMedium = TextStyle( fontWeight = FontWeight.SemiBold, fontSize = 12.sp, lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) + letterSpacing = 0.5.sp, + ), ) From 96449184ce2691c9895c2964dfaa8a3cb169eb92 Mon Sep 17 00:00:00 2001 From: user93482 Date: Sun, 1 Mar 2026 16:51:32 +0100 Subject: [PATCH 071/162] Added fancyRectangle shape, and cleaned up --- .idea/deploymentTargetSelector.xml | 10 + .idea/modules.xml | 1 - .../it/dunestrat/feature/MainMenu.kt | 13 +- .../hu/mokegyesulet/it/dunestrat/ui/Color.kt | 277 ++++++++++++++---- .../hu/mokegyesulet/it/dunestrat/ui/Font.kt | 8 +- .../hu/mokegyesulet/it/dunestrat/ui/Shapes.kt | 36 +-- .../hu/mokegyesulet/it/dunestrat/ui/Theme.kt | 141 +++++---- 7 files changed, 330 insertions(+), 156 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..d0b0930 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 2b3b281..a5af57d 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -4,7 +4,6 @@ - \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt index 3e4da34..bcc320d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt @@ -14,11 +14,15 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import hu.mokegyesulet.it.dunestrat.ui.DuneFontFamily +import hu.mokegyesulet.it.dunestrat.ui.duneFontFamily +import hu.mokegyesulet.it.dunestrat.ui.fancyRectangle import hu.mokegyesulet.it.dunestrat.ui.hexagonalRectangle @Composable -fun MainMenu(onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit) { +fun MainMenu( + onPlaceholder1: () -> Unit, + onPlaceholder2: () -> Unit, +) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -26,7 +30,7 @@ fun MainMenu(onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit) { ) { Button( onClick = onPlaceholder1, - shape = hexagonalRectangle, + shape = fancyRectangle, border = BorderStroke(1.dp, colorScheme.outline), colors = ButtonColors( colorScheme.primary, @@ -43,11 +47,12 @@ fun MainMenu(onPlaceholder1: () -> Unit, onPlaceholder2: () -> Unit) { Button( onClick = onPlaceholder2, + shape = hexagonalRectangle, ) { Text( text = "Placeholder 2", style = MaterialTheme.typography.bodyMedium, - fontFamily = DuneFontFamily(), + fontFamily = duneFontFamily(), ) } Text( diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt index a9c8428..55b79fb 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Color.kt @@ -1,67 +1,218 @@ package hu.mokegyesulet.it.dunestrat.ui - import androidx.compose.ui.graphics.Color -val light_primary = Color(0xFF825500) -val light_onPrimary = Color(0xFFFFFFFF) -val light_primaryContainer = Color(0xFFFFDDB3) -val light_onPrimaryContainer = Color(0xFF291800) -val light_secondary = Color(0xFF6F5B40) -val light_onSecondary = Color(0xFFFFFFFF) -val light_secondaryContainer = Color(0xFFFBDEBC) -val light_onSecondaryContainer = Color(0xFF271904) -val light_tertiary = Color(0xFF51643F) -val light_onTertiary = Color(0xFFFFFFFF) -val light_tertiaryContainer = Color(0xFFD4EABB) -val light_onTertiaryContainer = Color(0xFF102004) -val light_error = Color(0xFFBA1A1A) -val light_errorContainer = Color(0xFFFFDAD6) -val light_onError = Color(0xFFFFFFFF) -val light_onErrorContainer = Color(0xFF410002) -val light_background = Color(0xffebb000) -val light_base = Color(0xAAFFF88B) // minden mögött lévő háttér színe -val light_onBackground = Color(0xFF1F1B16) -val light_surface = Color(0xFFFFFBFF) -val light_onSurface = Color(0xFF1F1B16) -val light_surfaceVariant = Color(0xFFF0E0CF) -val light_onSurfaceVariant = Color(0xFF4F4539) -val light_outline = Color(0xFF817567) -val light_inverseOnSurface = Color(0xFFF9EFE7) -val light_inverseSurface = Color(0xFF34302A) -val light_inversePrimary = Color(0xFFFFB951) -val light_shadow = Color(0xFF000000) -val light_surfaceTint = Color(0xFF825500) -val light_outlineVariant = Color(0xFFD3C4B4) -val light_scrim = Color(0xFF000000) +val primaryLight = Color(0xFF6D5E0F) +val onPrimaryLight = Color(0xFFFFFFFF) +val primaryContainerLight = Color(0xFFF8E287) +val onPrimaryContainerLight = Color(0xFF534600) +val secondaryLight = Color(0xFF665E40) +val onSecondaryLight = Color(0xFFFFFFFF) +val secondaryContainerLight = Color(0xFFEEE2BC) +val onSecondaryContainerLight = Color(0xFF4E472A) +val tertiaryLight = Color(0xFF43664F) +val onTertiaryLight = Color(0xFFFFFFFF) +val tertiaryContainerLight = Color(0xFFC5ECCE) +val onTertiaryContainerLight = Color(0xFF2C4E38) +val errorLight = Color(0xFFBA1A1A) +val onErrorLight = Color(0xFFFFFFFF) +val errorContainerLight = Color(0xFFFFDAD6) +val onErrorContainerLight = Color(0xFF93000A) +val backgroundLight = Color(0xFFFFF9EE) +val onBackgroundLight = Color(0xFF1E1B13) +val surfaceLight = Color(0xFFFFF9EE) +val onSurfaceLight = Color(0xFF1E1B13) +val surfaceVariantLight = Color(0xFFE9E2D0) +val onSurfaceVariantLight = Color(0xFF4B4739) +val outlineLight = Color(0xFF7C7767) +val outlineVariantLight = Color(0xFFCDC6B4) +val scrimLight = Color(0xFF000000) +val inverseSurfaceLight = Color(0xFF333027) +val inverseOnSurfaceLight = Color(0xFFF7F0E2) +val inversePrimaryLight = Color(0xFFDBC66F) +val surfaceDimLight = Color(0xFFE0D9CC) +val surfaceBrightLight = Color(0xFFFFF9EE) +val surfaceContainerLowestLight = Color(0xFFFFFFFF) +val surfaceContainerLowLight = Color(0xFFFAF3E5) +val surfaceContainerLight = Color(0xFFF4EDDF) +val surfaceContainerHighLight = Color(0xFFEEE8DA) +val surfaceContainerHighestLight = Color(0xFFE8E2D4) + +val primaryLightMediumContrast = Color(0xFF403600) +val onPrimaryLightMediumContrast = Color(0xFFFFFFFF) +val primaryContainerLightMediumContrast = Color(0xFF7D6C1E) +val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val secondaryLightMediumContrast = Color(0xFF3C361B) +val onSecondaryLightMediumContrast = Color(0xFFFFFFFF) +val secondaryContainerLightMediumContrast = Color(0xFF756D4E) +val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val tertiaryLightMediumContrast = Color(0xFF1B3D28) +val onTertiaryLightMediumContrast = Color(0xFFFFFFFF) +val tertiaryContainerLightMediumContrast = Color(0xFF52755D) +val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val errorLightMediumContrast = Color(0xFF740006) +val onErrorLightMediumContrast = Color(0xFFFFFFFF) +val errorContainerLightMediumContrast = Color(0xFFCF2C27) +val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF) +val backgroundLightMediumContrast = Color(0xFFFFF9EE) +val onBackgroundLightMediumContrast = Color(0xFF1E1B13) +val surfaceLightMediumContrast = Color(0xFFFFF9EE) +val onSurfaceLightMediumContrast = Color(0xFF131109) +val surfaceVariantLightMediumContrast = Color(0xFFE9E2D0) +val onSurfaceVariantLightMediumContrast = Color(0xFF3A3629) +val outlineLightMediumContrast = Color(0xFF575244) +val outlineVariantLightMediumContrast = Color(0xFF726D5E) +val scrimLightMediumContrast = Color(0xFF000000) +val inverseSurfaceLightMediumContrast = Color(0xFF333027) +val inverseOnSurfaceLightMediumContrast = Color(0xFFF7F0E2) +val inversePrimaryLightMediumContrast = Color(0xFFDBC66F) +val surfaceDimLightMediumContrast = Color(0xFFCCC6B9) +val surfaceBrightLightMediumContrast = Color(0xFFFFF9EE) +val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF) +val surfaceContainerLowLightMediumContrast = Color(0xFFFAF3E5) +val surfaceContainerLightMediumContrast = Color(0xFFEEE8DA) +val surfaceContainerHighLightMediumContrast = Color(0xFFE3DCCF) +val surfaceContainerHighestLightMediumContrast = Color(0xFFD7D1C4) + +val primaryLightHighContrast = Color(0xFF352C00) +val onPrimaryLightHighContrast = Color(0xFFFFFFFF) +val primaryContainerLightHighContrast = Color(0xFF564800) +val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF) +val secondaryLightHighContrast = Color(0xFF322C12) +val onSecondaryLightHighContrast = Color(0xFFFFFFFF) +val secondaryContainerLightHighContrast = Color(0xFF50492D) +val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF) +val tertiaryLightHighContrast = Color(0xFF10321F) +val onTertiaryLightHighContrast = Color(0xFFFFFFFF) +val tertiaryContainerLightHighContrast = Color(0xFF2E503A) +val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF) +val errorLightHighContrast = Color(0xFF600004) +val onErrorLightHighContrast = Color(0xFFFFFFFF) +val errorContainerLightHighContrast = Color(0xFF98000A) +val onErrorContainerLightHighContrast = Color(0xFFFFFFFF) +val backgroundLightHighContrast = Color(0xFFFFF9EE) +val onBackgroundLightHighContrast = Color(0xFF1E1B13) +val surfaceLightHighContrast = Color(0xFFFFF9EE) +val onSurfaceLightHighContrast = Color(0xFF000000) +val surfaceVariantLightHighContrast = Color(0xFFE9E2D0) +val onSurfaceVariantLightHighContrast = Color(0xFF000000) +val outlineLightHighContrast = Color(0xFF2F2C20) +val outlineVariantLightHighContrast = Color(0xFF4D493B) +val scrimLightHighContrast = Color(0xFF000000) +val inverseSurfaceLightHighContrast = Color(0xFF333027) +val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF) +val inversePrimaryLightHighContrast = Color(0xFFDBC66F) +val surfaceDimLightHighContrast = Color(0xFFBEB8AB) +val surfaceBrightLightHighContrast = Color(0xFFFFF9EE) +val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF) +val surfaceContainerLowLightHighContrast = Color(0xFFF7F0E2) +val surfaceContainerLightHighContrast = Color(0xFFE8E2D4) +val surfaceContainerHighLightHighContrast = Color(0xFFDAD4C6) +val surfaceContainerHighestLightHighContrast = Color(0xFFCCC6B9) + +val primaryDark = Color(0xFFDBC66F) +val onPrimaryDark = Color(0xFF393000) +val primaryContainerDark = Color(0xFF534600) +val onPrimaryContainerDark = Color(0xFFF8E287) +val secondaryDark = Color(0xFFD1C6A1) +val onSecondaryDark = Color(0xFF363016) +val secondaryContainerDark = Color(0xFF4E472A) +val onSecondaryContainerDark = Color(0xFFEEE2BC) +val tertiaryDark = Color(0xFFA9D0B3) +val onTertiaryDark = Color(0xFF143723) +val tertiaryContainerDark = Color(0xFF2C4E38) +val onTertiaryContainerDark = Color(0xFFC5ECCE) +val errorDark = Color(0xFFFFB4AB) +val onErrorDark = Color(0xFF690005) +val errorContainerDark = Color(0xFF93000A) +val onErrorContainerDark = Color(0xFFFFDAD6) +val backgroundDark = Color(0xFF15130B) +val onBackgroundDark = Color(0xFFE8E2D4) +val surfaceDark = Color(0xFF15130B) +val onSurfaceDark = Color(0xFFE8E2D4) +val surfaceVariantDark = Color(0xFF4B4739) +val onSurfaceVariantDark = Color(0xFFCDC6B4) +val outlineDark = Color(0xFF969080) +val outlineVariantDark = Color(0xFF4B4739) +val scrimDark = Color(0xFF000000) +val inverseSurfaceDark = Color(0xFFE8E2D4) +val inverseOnSurfaceDark = Color(0xFF333027) +val inversePrimaryDark = Color(0xFF6D5E0F) +val surfaceDimDark = Color(0xFF15130B) +val surfaceBrightDark = Color(0xFF3C3930) +val surfaceContainerLowestDark = Color(0xFF100E07) +val surfaceContainerLowDark = Color(0xFF1E1B13) +val surfaceContainerDark = Color(0xFF222017) +val surfaceContainerHighDark = Color(0xFF2D2A21) +val surfaceContainerHighestDark = Color(0xFF38352B) + +val primaryDarkMediumContrast = Color(0xFFF2DC82) +val onPrimaryDarkMediumContrast = Color(0xFF2D2500) +val primaryContainerDarkMediumContrast = Color(0xFFA2903F) +val onPrimaryContainerDarkMediumContrast = Color(0xFF000000) +val secondaryDarkMediumContrast = Color(0xFFE8DCB6) +val onSecondaryDarkMediumContrast = Color(0xFF2B250C) +val secondaryContainerDarkMediumContrast = Color(0xFF9A916F) +val onSecondaryContainerDarkMediumContrast = Color(0xFF000000) +val tertiaryDarkMediumContrast = Color(0xFFBFE6C8) +val onTertiaryDarkMediumContrast = Color(0xFF082C19) +val tertiaryContainerDarkMediumContrast = Color(0xFF74997F) +val onTertiaryContainerDarkMediumContrast = Color(0xFF000000) +val errorDarkMediumContrast = Color(0xFFFFD2CC) +val onErrorDarkMediumContrast = Color(0xFF540003) +val errorContainerDarkMediumContrast = Color(0xFFFF5449) +val onErrorContainerDarkMediumContrast = Color(0xFF000000) +val backgroundDarkMediumContrast = Color(0xFF15130B) +val onBackgroundDarkMediumContrast = Color(0xFFE8E2D4) +val surfaceDarkMediumContrast = Color(0xFF15130B) +val onSurfaceDarkMediumContrast = Color(0xFFFFFFFF) +val surfaceVariantDarkMediumContrast = Color(0xFF4B4739) +val onSurfaceVariantDarkMediumContrast = Color(0xFFE3DCCA) +val outlineDarkMediumContrast = Color(0xFFB8B1A0) +val outlineVariantDarkMediumContrast = Color(0xFF969080) +val scrimDarkMediumContrast = Color(0xFF000000) +val inverseSurfaceDarkMediumContrast = Color(0xFFE8E2D4) +val inverseOnSurfaceDarkMediumContrast = Color(0xFF2D2A21) +val inversePrimaryDarkMediumContrast = Color(0xFF544700) +val surfaceDimDarkMediumContrast = Color(0xFF15130B) +val surfaceBrightDarkMediumContrast = Color(0xFF48443A) +val surfaceContainerLowestDarkMediumContrast = Color(0xFF090703) +val surfaceContainerLowDarkMediumContrast = Color(0xFF201D15) +val surfaceContainerDarkMediumContrast = Color(0xFF2A281F) +val surfaceContainerHighDarkMediumContrast = Color(0xFF353229) +val surfaceContainerHighestDarkMediumContrast = Color(0xFF413D34) -val dark_primary = Color(0xFFFFB951) -val dark_onPrimary = Color(0xFF452B00) -val dark_primaryContainer = Color(0xFF633F00) -val dark_onPrimaryContainer = Color(0xFFFFDDB3) -val dark_secondary = Color(0xFFDDC2A1) -val dark_onSecondary = Color(0xFF3E2D16) -val dark_secondaryContainer = Color(0xFF56442A) -val dark_onSecondaryContainer = Color(0xFFFBDEBC) -val dark_tertiary = Color(0xFFB8CEA1) -val dark_onTertiary = Color(0xFF243515) -val dark_tertiaryContainer = Color(0xFF3A4C2A) -val dark_onTertiaryContainer = Color(0xFFD4EABB) -val dark_error = Color(0xFFFFB4AB) -val dark_errorContainer = Color(0xFF93000A) -val dark_onError = Color(0xFF690005) -val dark_onErrorContainer = Color(0xFFFFDAD6) -val dark_background = Color(0xebd38f55) -val dark_base = Color(0xC6C78E05) // minden mögött lévő háttér színe -val dark_onBackground = Color(0xFFEAE1D9) -val dark_surface = Color(0xFF1F1B16) -val dark_onSurface = Color(0xFFEAE1D9) -val dark_surfaceVariant = Color(0xFF4F4539) -val dark_onSurfaceVariant = Color(0xFFD3C4B4) -val dark_outline = Color(0xFF9C8F80) -val dark_inverseOnSurface = Color(0xFF1F1B16) -val dark_inverseSurface = Color(0xFFEAE1D9) -val dark_inversePrimary = Color(0xFF825500) -val dark_shadow = Color(0xFF000000) -val dark_surfaceTint = Color(0xFFFFB951) -val dark_outlineVariant = Color(0xFF4F4539) -val dark_scrim = Color(0xFF000000) +val primaryDarkHighContrast = Color(0xFFFFF0B9) +val onPrimaryDarkHighContrast = Color(0xFF000000) +val primaryContainerDarkHighContrast = Color(0xFFD7C26B) +val onPrimaryContainerDarkHighContrast = Color(0xFF0F0B00) +val secondaryDarkHighContrast = Color(0xFFFCF0C9) +val onSecondaryDarkHighContrast = Color(0xFF000000) +val secondaryContainerDarkHighContrast = Color(0xFFCDC29E) +val onSecondaryContainerDarkHighContrast = Color(0xFF0F0B00) +val tertiaryDarkHighContrast = Color(0xFFD2FADB) +val onTertiaryDarkHighContrast = Color(0xFF000000) +val tertiaryContainerDarkHighContrast = Color(0xFFA5CCAF) +val onTertiaryContainerDarkHighContrast = Color(0xFF000F05) +val errorDarkHighContrast = Color(0xFFFFECE9) +val onErrorDarkHighContrast = Color(0xFF000000) +val errorContainerDarkHighContrast = Color(0xFFFFAEA4) +val onErrorContainerDarkHighContrast = Color(0xFF220001) +val backgroundDarkHighContrast = Color(0xFF15130B) +val onBackgroundDarkHighContrast = Color(0xFFE8E2D4) +val surfaceDarkHighContrast = Color(0xFF15130B) +val onSurfaceDarkHighContrast = Color(0xFFFFFFFF) +val surfaceVariantDarkHighContrast = Color(0xFF4B4739) +val onSurfaceVariantDarkHighContrast = Color(0xFFFFFFFF) +val outlineDarkHighContrast = Color(0xFFF7EFDD) +val outlineVariantDarkHighContrast = Color(0xFFC9C2B1) +val scrimDarkHighContrast = Color(0xFF000000) +val inverseSurfaceDarkHighContrast = Color(0xFFE8E2D4) +val inverseOnSurfaceDarkHighContrast = Color(0xFF000000) +val inversePrimaryDarkHighContrast = Color(0xFF544700) +val surfaceDimDarkHighContrast = Color(0xFF15130B) +val surfaceBrightDarkHighContrast = Color(0xFF535046) +val surfaceContainerLowestDarkHighContrast = Color(0xFF000000) +val surfaceContainerLowDarkHighContrast = Color(0xFF222017) +val surfaceContainerDarkHighContrast = Color(0xFF333027) +val surfaceContainerHighDarkHighContrast = Color(0xFF3E3B32) +val surfaceContainerHighestDarkHighContrast = Color(0xFF4A473D) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt index f60bae1..fbfa815 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Font.kt @@ -14,7 +14,7 @@ import dune_strat.composeapp.generated.resources.metropolis_semi_bold import org.jetbrains.compose.resources.Font @Composable -fun MetropolisFontFamily() = FontFamily( +fun metropolisFontFamily() = FontFamily( Font(Res.font.metropolis_light, weight = FontWeight.Light), Font(Res.font.metropolis_normal, weight = FontWeight.Normal), Font(Res.font.metropolis_medium, weight = FontWeight.Medium), @@ -23,13 +23,13 @@ fun MetropolisFontFamily() = FontFamily( ) @Composable -fun DuneFontFamily() = FontFamily( +fun duneFontFamily() = FontFamily( Font(Res.font.Dune_Rise), ) @Composable -fun MetropolisTypography() = Typography().run { - val fontFamily = MetropolisFontFamily() +fun metropolisTypography() = Typography().run { + val fontFamily = metropolisFontFamily() copy( displayLarge = displayLarge.copy(fontFamily = fontFamily), displayMedium = displayMedium.copy(fontFamily = fontFamily), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt index e1aeea8..b0cf2b9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt @@ -24,6 +24,22 @@ val hexagonalRectangle = GenericShape { size, _ -> lineTo(0f, height * 0.1f) } +val fancyRectangle = GenericShape { size, _ -> + val width = size.width + val height = size.height + val fancyBitSize = 5f + + lineTo(0f, height) + lineTo(width / 2 - fancyBitSize, height) + lineTo(width / 2, height + fancyBitSize) + lineTo(width / 2 + fancyBitSize, height) + lineTo(width, height) + lineTo(width, 0f) + lineTo(width / 2 + fancyBitSize, 0f) + lineTo(width / 2, -fancyBitSize) + lineTo(width / 2 - fancyBitSize, 0f) + lineTo(0f, 0f) +} val shapes = Shapes( extraSmall = RoundedCornerShape(4.dp), small = RoundedCornerShape(8.dp), @@ -31,23 +47,3 @@ val shapes = Shapes( large = RoundedCornerShape(24.dp), extraLarge = RoundedCornerShape(32.dp), ) - -@Composable -fun hexagonalButton(text: String, onClick: () -> Unit) { - Button( - onClick = onClick, - shape = hexagonalRectangle, - border = BorderStroke(1.dp, colorScheme.outline), - colors = ButtonColors( - colorScheme.primary, - colorScheme.onPrimary, - colorScheme.secondary, - colorScheme.onSecondary, - ), - ) { - Text( - text = "aeo áéőè", - style = MaterialTheme.typography.titleLarge, - ) - } -} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt index d7656a9..eafad6a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt @@ -1,89 +1,102 @@ package hu.mokegyesulet.it.dunestrat.ui +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -private val LightColors = lightColorScheme( - primary = light_primary, - onPrimary = light_onPrimary, - primaryContainer = light_primaryContainer, - onPrimaryContainer = light_onPrimaryContainer, - secondary = light_secondary, - onSecondary = light_onSecondary, - secondaryContainer = light_secondaryContainer, - onSecondaryContainer = light_onSecondaryContainer, - tertiary = light_tertiary, - onTertiary = light_onTertiary, - tertiaryContainer = light_tertiaryContainer, - onTertiaryContainer = light_onTertiaryContainer, - error = light_error, - errorContainer = light_errorContainer, - onError = light_onError, - onErrorContainer = light_onErrorContainer, - background = light_background, - onBackground = light_onBackground, - surface = light_surface, - onSurface = light_onSurface, - surfaceVariant = light_surfaceVariant, - onSurfaceVariant = light_onSurfaceVariant, - outline = light_outline, - inverseOnSurface = light_inverseOnSurface, - inverseSurface = light_inverseSurface, - inversePrimary = light_inversePrimary, - surfaceTint = light_surfaceTint, - outlineVariant = light_outlineVariant, - scrim = light_scrim, +private val lightScheme = lightColorScheme( + primary = primaryLight, + onPrimary = onPrimaryLight, + primaryContainer = primaryContainerLight, + onPrimaryContainer = onPrimaryContainerLight, + secondary = secondaryLight, + onSecondary = onSecondaryLight, + secondaryContainer = secondaryContainerLight, + onSecondaryContainer = onSecondaryContainerLight, + tertiary = tertiaryLight, + onTertiary = onTertiaryLight, + tertiaryContainer = tertiaryContainerLight, + onTertiaryContainer = onTertiaryContainerLight, + error = errorLight, + onError = onErrorLight, + errorContainer = errorContainerLight, + onErrorContainer = onErrorContainerLight, + background = backgroundLight, + onBackground = onBackgroundLight, + surface = surfaceLight, + onSurface = onSurfaceLight, + surfaceVariant = surfaceVariantLight, + onSurfaceVariant = onSurfaceVariantLight, + outline = outlineLight, + outlineVariant = outlineVariantLight, + scrim = scrimLight, + inverseSurface = inverseSurfaceLight, + inverseOnSurface = inverseOnSurfaceLight, + inversePrimary = inversePrimaryLight, + surfaceDim = surfaceDimLight, + surfaceBright = surfaceBrightLight, + surfaceContainerLowest = surfaceContainerLowestLight, + surfaceContainerLow = surfaceContainerLowLight, + surfaceContainer = surfaceContainerLight, + surfaceContainerHigh = surfaceContainerHighLight, + surfaceContainerHighest = surfaceContainerHighestLight, ) -private val DarkColors = darkColorScheme( - primary = dark_primary, - onPrimary = dark_onPrimary, - primaryContainer = dark_primaryContainer, - onPrimaryContainer = dark_onPrimaryContainer, - secondary = dark_secondary, - onSecondary = dark_onSecondary, - secondaryContainer = dark_secondaryContainer, - onSecondaryContainer = dark_onSecondaryContainer, - tertiary = dark_tertiary, - onTertiary = dark_onTertiary, - tertiaryContainer = dark_tertiaryContainer, - onTertiaryContainer = dark_onTertiaryContainer, - error = dark_error, - errorContainer = dark_errorContainer, - onError = dark_onError, - onErrorContainer = dark_onErrorContainer, - background = dark_background, - onBackground = dark_onBackground, - surface = dark_surface, - onSurface = dark_onSurface, - surfaceVariant = dark_surfaceVariant, - onSurfaceVariant = dark_onSurfaceVariant, - outline = dark_outline, - inverseOnSurface = dark_inverseOnSurface, - inverseSurface = dark_inverseSurface, - inversePrimary = dark_inversePrimary, - surfaceTint = dark_surfaceTint, - outlineVariant = dark_outlineVariant, - scrim = dark_scrim, +private val darkScheme = darkColorScheme( + primary = primaryDark, + onPrimary = onPrimaryDark, + primaryContainer = primaryContainerDark, + onPrimaryContainer = onPrimaryContainerDark, + secondary = secondaryDark, + onSecondary = onSecondaryDark, + secondaryContainer = secondaryContainerDark, + onSecondaryContainer = onSecondaryContainerDark, + tertiary = tertiaryDark, + onTertiary = onTertiaryDark, + tertiaryContainer = tertiaryContainerDark, + onTertiaryContainer = onTertiaryContainerDark, + error = errorDark, + onError = onErrorDark, + errorContainer = errorContainerDark, + onErrorContainer = onErrorContainerDark, + background = backgroundDark, + onBackground = onBackgroundDark, + surface = surfaceDark, + onSurface = onSurfaceDark, + surfaceVariant = surfaceVariantDark, + onSurfaceVariant = onSurfaceVariantDark, + outline = outlineDark, + outlineVariant = outlineVariantDark, + scrim = scrimDark, + inverseSurface = inverseSurfaceDark, + inverseOnSurface = inverseOnSurfaceDark, + inversePrimary = inversePrimaryDark, + surfaceDim = surfaceDimDark, + surfaceBright = surfaceBrightDark, + surfaceContainerLowest = surfaceContainerLowestDark, + surfaceContainerLow = surfaceContainerLowDark, + surfaceContainer = surfaceContainerDark, + surfaceContainerHigh = surfaceContainerHighDark, + surfaceContainerHighest = surfaceContainerHighestDark, ) @Composable fun AppTheme( - useDarkTheme: Boolean = false, // isSystemInDarkTheme() + useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit, ) { val colors = if (!useDarkTheme) { - LightColors + lightScheme } else { - DarkColors + darkScheme } MaterialTheme( - typography = MetropolisTypography(), + typography = metropolisTypography(), colorScheme = colors, content = content, shapes = shapes, From 858996fb6199f67f8b619d16c89b295086d90ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Mar 2026 18:05:50 +0000 Subject: [PATCH 072/162] Untrack .idea files --- .idea/artifacts/composeApp_js.xml | 8 -------- .idea/artifacts/composeApp_jvm.xml | 8 -------- .idea/artifacts/composeApp_wasm_js.xml | 8 -------- .idea/codeStyles/Project.xml | 10 ---------- .idea/codeStyles/codeStyleConfig.xml | 5 ----- .idea/compiler.xml | 10 ---------- .idea/deploymentTargetSelector.xml | 10 ---------- .idea/ktlint-plugin.xml | 7 ------- .idea/modules.xml | 9 --------- .idea/runConfigurations.xml | 17 ----------------- 10 files changed, 92 deletions(-) delete mode 100644 .idea/artifacts/composeApp_js.xml delete mode 100644 .idea/artifacts/composeApp_jvm.xml delete mode 100644 .idea/artifacts/composeApp_wasm_js.xml delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/deploymentTargetSelector.xml delete mode 100644 .idea/ktlint-plugin.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml deleted file mode 100644 index e52ad0e..0000000 --- a/.idea/artifacts/composeApp_js.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - - - \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml deleted file mode 100644 index 96d842a..0000000 --- a/.idea/artifacts/composeApp_jvm.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - - - \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml deleted file mode 100644 index e3c9c59..0000000 --- a/.idea/artifacts/composeApp_wasm_js.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - $PROJECT_DIR$/composeApp/build/libs - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 1bec35e..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 6b61887..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml deleted file mode 100644 index d0b0930..0000000 --- a/.idea/deploymentTargetSelector.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml deleted file mode 100644 index e8bd90c..0000000 --- a/.idea/ktlint-plugin.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - DISTRACT_FREE - DEFAULT - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a5af57d..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 16660f1..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file From 3c5b04d27228ac5cae3e29e6863669d09820ff6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Mar 2026 18:08:43 +0000 Subject: [PATCH 073/162] Fix formatting --- .../kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt index eafad6a..3e254db 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Theme.kt @@ -85,9 +85,7 @@ private val darkScheme = darkColorScheme( @Composable fun AppTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), - content: - @Composable() - () -> Unit, + content: @Composable () -> Unit, ) { val colors = if (!useDarkTheme) { lightScheme From 5bd006e7e6e6856302a7a4bbde8c8ae96f486a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Mar 2026 18:18:57 +0000 Subject: [PATCH 074/162] Fix navigation --- .../kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 3f24cb7..0727abf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -2,8 +2,11 @@ package hu.mokegyesulet.it.dunestrat.navigation import kotlinx.serialization.Serializable -@Serializable sealed class Screen { + + @Serializable data object MainMenu : Screen() + + @Serializable data object TestData : Screen() } From 268ad02a3e08c0f6b35f72df35c8c92fe9abf58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Mar 2026 18:23:56 +0000 Subject: [PATCH 075/162] Delete unused screen --- .../it/dunestrat/feature/MainMenu.kt | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt deleted file mode 100644 index bcc320d..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/MainMenu.kt +++ /dev/null @@ -1,81 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.feature - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MaterialTheme.colorScheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import hu.mokegyesulet.it.dunestrat.ui.duneFontFamily -import hu.mokegyesulet.it.dunestrat.ui.fancyRectangle -import hu.mokegyesulet.it.dunestrat.ui.hexagonalRectangle - -@Composable -fun MainMenu( - onPlaceholder1: () -> Unit, - onPlaceholder2: () -> Unit, -) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceEvenly, - ) { - Button( - onClick = onPlaceholder1, - shape = fancyRectangle, - border = BorderStroke(1.dp, colorScheme.outline), - colors = ButtonColors( - colorScheme.primary, - colorScheme.onPrimary, - colorScheme.secondary, - colorScheme.onSecondary, - ), - ) { - Text( - text = "aeo áéőè", - style = MaterialTheme.typography.titleLarge, - ) - } - - Button( - onClick = onPlaceholder2, - shape = hexagonalRectangle, - ) { - Text( - text = "Placeholder 2", - style = MaterialTheme.typography.bodyMedium, - fontFamily = duneFontFamily(), - ) - } - Text( - text = "abc ABC", - style = MaterialTheme.typography.displayLarge, - fontWeight = FontWeight.Light, - ) - Text( - text = "abc ABC", - style = MaterialTheme.typography.headlineLarge, - fontWeight = FontWeight.Bold, - ) - Text( - text = "abc ABC", - style = MaterialTheme.typography.titleLarge, - ) - Text( - text = "abc ABC", - style = MaterialTheme.typography.bodyLarge, - ) - Text( - text = "abc ABC", - style = MaterialTheme.typography.labelLarge, - ) - } -} From 457d305d7a20434b5429e49857c9f792c48fcb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 15:24:42 +0100 Subject: [PATCH 076/162] Modify db scheme to avoid infinite recursion and track player_id for PlayerStep --- .../entities/GameStateDatabaseEntity.kt | 54 +++++++++++++++++-- .../entities/PlayerStepDatabaseEntity.kt | 5 +- .../it/dunestrat/model/GameState.kt | 43 ++++++++------- .../mokegyesulet/it/dunestrat/model/Player.kt | 11 ++-- .../it/dunestrat/model/PlayerStep.kt | 11 ++-- 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index 6ce5612..dbf7bee 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.backend.entities import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.GameStateField +import hu.mokegyesulet.it.dunestrat.model.Player import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlinx.serialization.EncodeDefault import kotlinx.serialization.SerialName @@ -17,11 +18,10 @@ class GameStateDatabaseEntity( val index: Int, @SerialName("field_jsons") val fieldJsons: List, - val json: String, + @SerialName("player_jsons") + val playersJsons: List, ) { fun toGameState(): GameState { - val gameState = Json.decodeFromString(json) - val fieldsWithIdList = fieldJsons.map { fieldJson -> Json.decodeFromString(fieldJson) } @@ -40,11 +40,27 @@ class GameStateDatabaseEntity( } } - return gameState.copy( + val players = playersJsons.map { playerJson -> + val playerWithFieldIdList = Json.decodeFromString(playerJson) + val ownedFields = playerWithFieldIdList.ownedFieldsId.map { fieldId -> + fields[fieldId]!! + } + Player( + id = playerWithFieldIdList.id, + water = playerWithFieldIdList.water, + spice = playerWithFieldIdList.spice, + harvestersPurchased = playerWithFieldIdList.harvesterPurchased, + weapons = playerWithFieldIdList.weapons.toMutableMap(), + ownedFields = ownedFields.toMutableSet(), + ) + } + + return GameState( id = id, gameId = gameId, index = index, fields = fields.values.toSet(), + players = players.toSet(), ) } companion object { @@ -57,7 +73,7 @@ fun GameState.toDatabaseEntry(): GameStateDatabaseEntity = GameStateDatabaseEnti gameId = gameId, index = index, fieldJsons = fields.map { Json.encodeToString(it.toGameStateFieldWithIdList()) }, - json = Json.encodeToString(this), + playersJsons = players.map { Json.encodeToString(it.toPlayerWithFieldIdList()) }, ) @Serializable @@ -87,3 +103,31 @@ fun GameStateField.toGameStateFieldWithIdList() = GameStateFieldWithIdList( harvester = harvester, neighboursId = neighbours.map { it.id }, ) + +@Serializable +class PlayerWithFieldIdList( + val id: String, + val water: Int, + val spice: Int, + val harvesterPurchased: Int, + val weapons: Map, + val ownedFieldsId: Set, +) { + fun toPlayer() = Player( + id = id, + water = water, + spice = spice, + harvestersPurchased = harvesterPurchased, + weapons = weapons.toMutableMap(), + ownedFields = mutableSetOf(), + ) +} + +fun Player.toPlayerWithFieldIdList() = PlayerWithFieldIdList( + id = id, + water = water, + spice = spice, + harvesterPurchased = harvestersPurchased, + weapons = weapons.toMap(), + ownedFieldsId = ownedFields.map { it.id }.toSet(), +) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index a10b2c3..8417ed7 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -12,12 +12,14 @@ class PlayerStepDatabaseEntity( val id: Int = -1, @SerialName("game_state_id") val gameStateId: Int, + @SerialName("player_id") + val playerId: String, val json: String, ) { fun toPlayerStep(): PlayerStep { val playerStep = Json.decodeFromString(json) - return playerStep.copy(id = id, gameStateId = gameStateId) + return playerStep.copy(id = id, gameStateId = gameStateId, playerId = playerId) } companion object { @@ -28,5 +30,6 @@ class PlayerStepDatabaseEntity( fun PlayerStep.toDatabaseEntry(): PlayerStepDatabaseEntity = PlayerStepDatabaseEntity( id = id, gameStateId = gameStateId, + playerId = playerId, json = Json.encodeToString(this), ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index 8927719..f4d20b5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -1,23 +1,14 @@ package hu.mokegyesulet.it.dunestrat.model -import kotlin.collections.setOf -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient - -@Serializable data class GameState( - @Transient val id: Int = -1, - @Transient - val gameId: Int = -1, - @Transient - val index: Int = -1, - @Transient - val fields: Set = setOf(), + val gameId: Int, + var index: Int, + val fields: Set, val players: Set, ) { - fun runTurn(playerSteps: MutableSet) { + fun runTurn(playerSteps: MutableSet): GameState { this.leaveFields(playerSteps) this.waterConsumption(playerSteps) this.checkPurchases(playerSteps) @@ -26,6 +17,14 @@ data class GameState( this.produceResources() this.expansions(playerSteps) this.deliverWeapons(playerSteps) + index++ + + return GameState( + gameId = gameId, + index = index, + fields = fields, + players = players, + ) } fun leaveFields(playerSteps: Set) { @@ -40,6 +39,7 @@ data class GameState( playerSteps.removeAll { it.playerId == player.id } playerSteps.add( PlayerStep( + gameStateId = -1, playerId = player.id, leaveFields = setOf(), enterFields = setOf(), @@ -57,6 +57,7 @@ data class GameState( ): PlayerStep = playerSteps.find { playerStep -> playerStep.playerId == playerId } ?: PlayerStep( + gameStateId = -1, playerId = playerId, leaveFields = setOf(), enterFields = setOf(), @@ -73,10 +74,11 @@ data class GameState( fun buildHarvesters(playerSteps: Set) { players.filter { !it.inDebt }.forEach { player -> - getPlayerStepById(player.id, playerSteps).buildHarvesters.forEach { field -> - if (field in player.ownedFields) { - player.purchaseHarvester(field) - } + getPlayerStepById(player.id, playerSteps).buildHarvesters.forEach { fieldId -> + val field = + player.ownedFields.find { field -> field.id == fieldId } ?: return@forEach + + player.purchaseHarvester(field) } } } @@ -117,7 +119,12 @@ data class GameState( players.filter { !it.inDebt } .forEach { player -> getPlayerStepById(player.id, playerSteps).enterFields - .filter { field -> player.isFieldReachable(field) } + .mapNotNull { fieldId -> + fields.find { field -> field.id == fieldId } + } + .filter { field -> + player.isFieldReachable(field) + } .forEach { field -> chosenFields[field]?.add(player) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 39acc5f..849db37 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -1,25 +1,22 @@ package hu.mokegyesulet.it.dunestrat.model -import kotlin.collections.forEach import kotlin.math.pow -import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -@Serializable data class Player( val id: String, var water: Int, var spice: Int, var harvestersPurchased: Int, - private val weapons: MutableMap, + val weapons: MutableMap, val ownedFields: MutableSet, @Transient var inDebt: Boolean = false, ) { fun getWeaponCount(weapon: Weapon): Int = weapons[weapon] ?: 0 - fun leaveFields(fields: Set) { - ownedFields.removeAll(fields) + fun leaveFields(fields: Set) { + ownedFields.removeAll { field -> field.id in fields } } fun waterConsumption(): Boolean { @@ -31,7 +28,7 @@ data class Player( if (water >= 0) { return false } - leaveFields(ownedFields.filter { field -> field.water < 0 }.toSet()) + ownedFields.removeAll { field -> field.water < 0 } water = 0 return true } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index f79c84f..aaed5b4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -9,9 +9,10 @@ data class PlayerStep( val id: Int = -1, @Transient val gameStateId: Int = -1, - val playerId: String, - var leaveFields: Set, - var enterFields: Set, - var purchaseWeapons: Map, - var buildHarvesters: Set, + @Transient + val playerId: String = "", + var leaveFields: Set = emptySet(), + var enterFields: Set = emptySet(), + var purchaseWeapons: Map = emptyMap(), + var buildHarvesters: Set = emptySet(), ) From 76cc323c5a9118ff0a97b7aa60654de6757facf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 20:29:22 +0100 Subject: [PATCH 077/162] Change Player.id type to Int --- .../entities/GameStateDatabaseEntity.kt | 2 +- .../entities/PlayerStepDatabaseEntity.kt | 2 +- .../feature/testdata/TestDataViewModel.kt | 29 ++----------------- .../it/dunestrat/model/GameState.kt | 2 +- .../mokegyesulet/it/dunestrat/model/Player.kt | 2 +- .../it/dunestrat/model/PlayerStep.kt | 2 +- .../mokegyesulet/it/dunestrat/model/Team.kt | 2 +- 7 files changed, 9 insertions(+), 32 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index dbf7bee..47c048f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -106,7 +106,7 @@ fun GameStateField.toGameStateFieldWithIdList() = GameStateFieldWithIdList( @Serializable class PlayerWithFieldIdList( - val id: String, + val id: Int, val water: Int, val spice: Int, val harvesterPurchased: Int, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index 8417ed7..c79cf10 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -13,7 +13,7 @@ class PlayerStepDatabaseEntity( @SerialName("game_state_id") val gameStateId: Int, @SerialName("player_id") - val playerId: String, + val playerId: Int, val json: String, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt index d724c78..1a22bd2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -3,16 +3,7 @@ package hu.mokegyesulet.it.dunestrat.feature.testdata import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository -import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.model.Game -import hu.mokegyesulet.it.dunestrat.model.GameProgress -import hu.mokegyesulet.it.dunestrat.model.GameState -import hu.mokegyesulet.it.dunestrat.model.GameStateField -import hu.mokegyesulet.it.dunestrat.model.Player -import hu.mokegyesulet.it.dunestrat.model.Student -import hu.mokegyesulet.it.dunestrat.model.Team -import hu.mokegyesulet.it.dunestrat.model.Weapon -import hu.mokegyesulet.it.dunestrat.model.toGameStateFields +import hu.mokegyesulet.it.dunestrat.model.* import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -28,23 +19,9 @@ class TestDataViewModel : ViewModel() { val gameStates = SupabaseRepository.getGameStates() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) - private var players: List + private var players = (1..12).map { initPlayer(it) } init { - players = listOf( - initPlayer("1"), - initPlayer("2"), - initPlayer("3"), - initPlayer("4"), - initPlayer("5"), - initPlayer("6"), - initPlayer("7"), - initPlayer("8"), - initPlayer("9"), - initPlayer("10"), - initPlayer("11"), - initPlayer("12"), - ) viewModelScope.launch { games.collect { list -> println("Games from Supabase: $list") @@ -118,7 +95,7 @@ class TestDataViewModel : ViewModel() { } } - private fun initPlayer(id: String): Player = Player( + private fun initPlayer(id: Int): Player = Player( id, 0, 0, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt index f4d20b5..c264e73 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameState.kt @@ -52,7 +52,7 @@ data class GameState( } fun getPlayerStepById( - playerId: String, + playerId: Int, playerSteps: Set, ): PlayerStep = playerSteps.find { playerStep -> playerStep.playerId == playerId diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt index 849db37..6f8b89c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Player.kt @@ -4,7 +4,7 @@ import kotlin.math.pow import kotlinx.serialization.Transient data class Player( - val id: String, + val id: Int, var water: Int, var spice: Int, var harvestersPurchased: Int, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt index aaed5b4..e1f9147 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/PlayerStep.kt @@ -10,7 +10,7 @@ data class PlayerStep( @Transient val gameStateId: Int = -1, @Transient - val playerId: String = "", + val playerId: Int = -1, var leaveFields: Set = emptySet(), var enterFields: Set = emptySet(), var purchaseWeapons: Map = emptyMap(), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt index 16b9269..417c928 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Team.kt @@ -4,6 +4,6 @@ import kotlinx.serialization.Serializable @Serializable class Team( - val playerId: String, + val playerId: Int, val students: List, ) From f6c6d0002211a9a0cc846f4cee4a9e53c230f383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81goston=20Szab=C3=B3?= Date: Sun, 9 Nov 2025 19:46:14 +0100 Subject: [PATCH 078/162] Shows 12 player cards on initialization --- .../hu/mokegyesulet/it/dunestrat/App.kt | 2 + .../it/dunestrat/feature/init/InitScreen.kt | 23 +++++++ .../dunestrat/feature/init/InitViewModel.kt | 9 +++ .../it/dunestrat/ui/CreatePlayerCard.kt | 63 +++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index 6cfc9a9..e8ca5c2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -6,6 +6,8 @@ import androidx.compose.runtime.* import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import hu.mokegyesulet.it.dunestrat.ui.AppTheme import org.jetbrains.compose.ui.tooling.preview.Preview +import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen +import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard @Composable @Preview diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt new file mode 100644 index 0000000..376c2fe --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -0,0 +1,23 @@ +package hu.mokegyesulet.it.dunestrat.feature.init + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard + +@Composable +fun InitScreen() { + val viewModel = viewModel { InitViewModel() } + + LazyColumn { + for (i in 1..12) { + item { CreatePlayerCard() } + } + } + +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt new file mode 100644 index 0000000..e6629fa --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -0,0 +1,9 @@ +package hu.mokegyesulet.it.dunestrat.feature.init + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel + + +class InitViewModel() : ViewModel() { + +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt new file mode 100644 index 0000000..6a4eb8d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -0,0 +1,63 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun CreatePlayerCard() { + Card() { + Text("Játékos:") + var playerID = remember { mutableStateOf("Jozsi") } + TextField( + state = rememberTextFieldState(playerID.value), + label = { Text("Játékos ID") } + ) + + var water = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(water.value), + label = { Text("Víz") } + ) + + var spice = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(spice.value), + label = { Text("Spice") } + ) + + var pistol = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(pistol.value), + label = { Text("Spice") } + ) + + var lasgun = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(lasgun.value), + label = { Text("Spice") } + ) + + var crysknife = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(crysknife.value), + label = { Text("Spice") } + ) + + var legio = remember { mutableStateOf("0") } + TextField( + state = rememberTextFieldState(legio.value), + label = { Text("Spice") } + ) + + var startingGameStateID = remember { mutableStateOf("A1") } + TextField( + state = rememberTextFieldState(startingGameStateID.value), + label = { Text("Kezdő mező ID") } + ) + } +} From 7bae2237b021893f12bb76393534156b86ec2db5 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 16 Nov 2025 19:10:38 +0100 Subject: [PATCH 079/162] =?UTF-8?q?leg=C3=B6rd=C3=BCl=C5=91=20men=C3=BC=20?= =?UTF-8?q?+=20st=C3=ADlus=20+=20ment=C3=A9s=20gomb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/init/InitScreen.kt | 82 ++++++++++++++++++- .../it/dunestrat/ui/CreatePlayerCard.kt | 20 +++-- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 376c2fe..3154c80 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -1,12 +1,24 @@ package hu.mokegyesulet.it.dunestrat.feature.init -import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard @@ -14,10 +26,72 @@ import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard fun InitScreen() { val viewModel = viewModel { InitViewModel() } - LazyColumn { - for (i in 1..12) { - item { CreatePlayerCard() } + var expanded by remember { mutableStateOf(false) } + var selectedMap by remember { mutableStateOf("Hexagon – 12 játékos") } + val mapOptions = listOf("Hexagon – 12 játékos", "Hexagon – 6 játékos") + + Column( + modifier = Modifier + .fillMaxSize() + .padding(top = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box { + OutlinedTextField( + value = selectedMap, + onValueChange = {}, + readOnly = true, + label = { Text("Térkép kiválasztása") }, + modifier = Modifier + .fillMaxWidth(0.7f) + .clickable() { expanded = true } + ) + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + mapOptions.forEach { map -> + DropdownMenuItem( + text = { Text(map) }, + onClick = { + selectedMap = map + expanded = false + } + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + val playerCount = if (selectedMap.contains("12")) 12 else 6 + + LazyColumn( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + for (i in 1..playerCount) { + item { Text(i.toString() + ". játékos") } + item { + CreatePlayerCard( + modifier = Modifier.fillMaxWidth(0.45f) + ) + Spacer(modifier = Modifier.height(8.dp)) + } + } + item { + Button( + onClick = { viewModel.savePlayers() }, + modifier = Modifier.padding(16.dp) + ) { + Text("Adatok mentése") + } + } } } +} +private fun InitViewModel.savePlayers() { + TODO("savePlayers függvény hiányos") } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index 6a4eb8d..a960049 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -1,17 +1,21 @@ package hu.mokegyesulet.it.dunestrat.ui +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview -fun CreatePlayerCard() { - Card() { - Text("Játékos:") +fun CreatePlayerCard(modifier: Modifier) { + Card( + modifier = modifier.padding(20.dp) + ) { var playerID = remember { mutableStateOf("Jozsi") } TextField( state = rememberTextFieldState(playerID.value), @@ -27,31 +31,31 @@ fun CreatePlayerCard() { var spice = remember { mutableStateOf("0") } TextField( state = rememberTextFieldState(spice.value), - label = { Text("Spice") } + label = { Text("Fűszer") } ) var pistol = remember { mutableStateOf("0") } TextField( state = rememberTextFieldState(pistol.value), - label = { Text("Spice") } + label = { Text("Pisztoly") } ) var lasgun = remember { mutableStateOf("0") } TextField( state = rememberTextFieldState(lasgun.value), - label = { Text("Spice") } + label = { Text("Lasgun") } ) var crysknife = remember { mutableStateOf("0") } TextField( state = rememberTextFieldState(crysknife.value), - label = { Text("Spice") } + label = { Text("Crysknife") } ) var legio = remember { mutableStateOf("0") } TextField( state = rememberTextFieldState(legio.value), - label = { Text("Spice") } + label = { Text("Légio") } ) var startingGameStateID = remember { mutableStateOf("A1") } From f97f68adea31f896c78ebfa43949bf7544db3e8a Mon Sep 17 00:00:00 2001 From: domahet Date: Sun, 23 Nov 2025 18:57:45 +0100 Subject: [PATCH 080/162] start viewmodel implementation --- .../it/dunestrat/feature/init/InitScreen.kt | 27 ++++++++--------- .../dunestrat/feature/init/InitViewModel.kt | 30 ++++++++++++++++++- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 3154c80..7d5acd6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -26,38 +26,39 @@ import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard fun InitScreen() { val viewModel = viewModel { InitViewModel() } - var expanded by remember { mutableStateOf(false) } - var selectedMap by remember { mutableStateOf("Hexagon – 12 játékos") } - val mapOptions = listOf("Hexagon – 12 játékos", "Hexagon – 6 játékos") + var expanded by viewModel.dropdownExpanded + var selectedMap by viewModel.selectedMap + val playerCount by viewModel.playerCount + val mapOptions by viewModel.mapOptions Column( modifier = Modifier .fillMaxSize() .padding(top = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Box { OutlinedTextField( - value = selectedMap, + value = "Hexagon – $playerCount játékos", onValueChange = {}, readOnly = true, label = { Text("Térkép kiválasztása") }, modifier = Modifier .fillMaxWidth(0.7f) - .clickable() { expanded = true } + .clickable { expanded = true }, ) DropdownMenu( expanded = expanded, - onDismissRequest = { expanded = false } + onDismissRequest = { expanded = false }, ) { mapOptions.forEach { map -> DropdownMenuItem( - text = { Text(map) }, + text = { "Hexagon – $playerCount játékos" }, onClick = { selectedMap = map expanded = false - } + }, ) } } @@ -65,17 +66,15 @@ fun InitScreen() { Spacer(modifier = Modifier.height(16.dp)) - val playerCount = if (selectedMap.contains("12")) 12 else 6 - LazyColumn( modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { for (i in 1..playerCount) { item { Text(i.toString() + ". játékos") } item { CreatePlayerCard( - modifier = Modifier.fillMaxWidth(0.45f) + modifier = Modifier.fillMaxWidth(0.45f), ) Spacer(modifier = Modifier.height(8.dp)) } @@ -83,7 +82,7 @@ fun InitScreen() { item { Button( onClick = { viewModel.savePlayers() }, - modifier = Modifier.padding(16.dp) + modifier = Modifier.padding(16.dp), ) { Text("Adatok mentése") } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index e6629fa..61cd562 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -1,9 +1,37 @@ package hu.mokegyesulet.it.dunestrat.feature.init +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel - +import hu.mokegyesulet.it.dunestrat.model.Player class InitViewModel() : ViewModel() { + var dropdownExpanded = mutableStateOf(false) + var selectedMap = mutableStateOf(12) + val playerCount = mutableStateOf(12) + val playerList = mutableStateOf(listOf()) + val mapOptions = mutableStateOf(listOf(12, 6)) + + fun onEvent(event: InitScreenEvent) { + when (event) { + is InitScreenEvent.ChangeMapDropdownExpanded -> { + dropdownExpanded.value = event.expanded + } + is InitScreenEvent.ChangeSelectedMap -> { + selectedMap.value = event.desertSize + dropdownExpanded.value = false + } + is InitScreenEvent.InitPlayerOnMap -> {} + } + } + sealed class InitScreenEvent() { + data object InitPlayerOnMap : InitScreenEvent() + data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() + data class ChangeSelectedMap(val desertSize: Int) : InitScreenEvent() + } } From ba35819e6446a3b3da7df5a83809a1f95b670568 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 30 Nov 2025 19:34:14 +0100 Subject: [PATCH 081/162] Eventek --- .../it/dunestrat/feature/init/InitScreen.kt | 21 ++++- .../dunestrat/feature/init/InitViewModel.kt | 29 ++++++- .../it/dunestrat/ui/CreatePlayerCard.kt | 87 ++++++++++++++----- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 7d5acd6..8f70488 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -20,16 +20,24 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.model.Player import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard +// 1. Szám adatok ellenőrzése backenden +// 2. Menü helyes működése +// 3. Mentés gomb +// 4. Desert lista -> legördülő menü +// 5. Új gameState létrehozása + @Composable fun InitScreen() { val viewModel = viewModel { InitViewModel() } - var expanded by viewModel.dropdownExpanded var selectedMap by viewModel.selectedMap val playerCount by viewModel.playerCount val mapOptions by viewModel.mapOptions + val playerList by viewModel.playerList + val startingFieldIds by viewModel.startingFieldIds Column( modifier = Modifier @@ -70,11 +78,20 @@ fun InitScreen() { modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { - for (i in 1..playerCount) { + for (i in 0..< playerCount) { item { Text(i.toString() + ". játékos") } item { CreatePlayerCard( + index = i, modifier = Modifier.fillMaxWidth(0.45f), + player = playerList[i], + onChange = {player, index -> + viewModel.onEvent(InitViewModel.InitScreenEvent.UpdatePlayerData(player, index)) + }, + onStartingFieldChange = {fieldId -> + viewModel.onEvent(InitViewModel.InitScreenEvent.UpdatePlayerStartingField(fieldId = fieldId, i)) + }, + startingFieldId = startingFieldIds[i] ) Spacer(modifier = Modifier.height(8.dp)) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 61cd562..4376e94 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -1,19 +1,33 @@ package hu.mokegyesulet.it.dunestrat.feature.init import androidx.compose.foundation.shape.CornerSize +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Player class InitViewModel() : ViewModel() { + private val startingPlayer = Player("", 0,0,mapOf(),setOf(),) + val playerList: MutableState> + val startingFieldIds: MutableState> + init { + val startingPlayers = mutableListOf() + val emtpyFieldIds = mutableListOf() + repeat(12) { + startingPlayers.add(startingPlayer.copy()) + emtpyFieldIds.add("") + } + playerList = mutableStateOf(startingPlayers) + startingFieldIds = mutableStateOf(emtpyFieldIds) + } var dropdownExpanded = mutableStateOf(false) var selectedMap = mutableStateOf(12) val playerCount = mutableStateOf(12) - val playerList = mutableStateOf(listOf()) val mapOptions = mutableStateOf(listOf(12, 6)) fun onEvent(event: InitScreenEvent) { @@ -25,6 +39,17 @@ class InitViewModel() : ViewModel() { selectedMap.value = event.desertSize dropdownExpanded.value = false } + is InitScreenEvent.UpdatePlayerData -> { + val newPlayerList = playerList.value.toMutableList() + newPlayerList[event.index] = event.playerData + playerList.value = newPlayerList + } + is InitScreenEvent.UpdatePlayerStartingField -> { + val newStartingFieldIds = startingFieldIds.value.toMutableList() + newStartingFieldIds[event.index] = event.fieldId + startingFieldIds.value = newStartingFieldIds + } + is InitScreenEvent.InitPlayerOnMap -> {} } } @@ -33,5 +58,7 @@ class InitViewModel() : ViewModel() { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() data class ChangeSelectedMap(val desertSize: Int) : InitScreenEvent() + data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() + data class UpdatePlayerStartingField(val fieldId : String, val index: Int) : InitScreenEvent() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index a960049..a43cee5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -8,60 +8,105 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Weapon import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview -fun CreatePlayerCard(modifier: Modifier) { +fun CreatePlayerCard( + index : Int, + modifier: Modifier, + player: Player, + onChange: (Player, Int) -> Unit, + startingFieldId: String, + onStartingFieldChange: (String) -> Unit +) { Card( modifier = modifier.padding(20.dp) ) { - var playerID = remember { mutableStateOf("Jozsi") } TextField( - state = rememberTextFieldState(playerID.value), + value = player.id, + onValueChange = { onChange(player.copy(id = it), index) }, label = { Text("Játékos ID") } ) - var water = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(water.value), - label = { Text("Víz") } + value = player.water.toString(), + onValueChange = { onChange(player.copy(water = it.toInt()), index) }, + label = { Text("Víz készlet") } ) - var spice = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(spice.value), - label = { Text("Fűszer") } + value = player.spice.toString(), + onValueChange = { onChange(player.copy(spice = it.toInt()), index) }, + label = { Text("Fűszer készlet") } ) - var pistol = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(pistol.value), + value = player.getWeaponCount(Weapon.PISTOL).toString(), + onValueChange = { + val newWeapons: Map = mapOf( + Weapon.PISTOL to it.toInt(), + Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + }, label = { Text("Pisztoly") } ) - var lasgun = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(lasgun.value), + value = player.getWeaponCount(Weapon.LASGUN).toString(), + onValueChange = { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), + Weapon.LASGUN to it.toInt(), + Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + }, label = { Text("Lasgun") } ) - var crysknife = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(crysknife.value), + value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), + onValueChange = { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), + Weapon.CRYSKNIFE to it.toInt(), + Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + }, label = { Text("Crysknife") } ) - var legio = remember { mutableStateOf("0") } TextField( - state = rememberTextFieldState(legio.value), - label = { Text("Légio") } + value = player.getWeaponCount(Weapon.LEGION).toString(), + onValueChange = { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), + Weapon.LEGION to it.toInt() + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + }, + label = { Text("Légió") } ) - var startingGameStateID = remember { mutableStateOf("A1") } TextField( - state = rememberTextFieldState(startingGameStateID.value), - label = { Text("Kezdő mező ID") } + value = startingFieldId, + onValueChange = onStartingFieldChange, + label = { Text("Kezdő mező") } ) } } From 0e803648a5c66441260a7c56f023a05b14a62dc7 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 25 Jan 2026 17:52:29 +0100 Subject: [PATCH 082/162] =?UTF-8?q?ViewModel:=20validation,=20enable/disab?= =?UTF-8?q?le=20interactions=20dynamically=20-=20k=C3=A9sz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/init/InitScreen.kt | 59 ++++++++++++------- .../dunestrat/feature/init/InitViewModel.kt | 53 +++++++++++++---- 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 8f70488..9c8c39e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -38,6 +38,7 @@ fun InitScreen() { val mapOptions by viewModel.mapOptions val playerList by viewModel.playerList val startingFieldIds by viewModel.startingFieldIds + val isFormValid by viewModel.isFormValid Column( modifier = Modifier @@ -45,28 +46,36 @@ fun InitScreen() { .padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - Box { + // Dropdown + Box( + modifier = Modifier + .fillMaxWidth(0.7f) + .clickable( //Az alatta lévő 2 sornak jónak kéne lennie, de nem az xd + indication = null, + interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() } + ) { + expanded = true + } + ) { OutlinedTextField( value = "Hexagon – $playerCount játékos", onValueChange = {}, readOnly = true, label = { Text("Térkép kiválasztása") }, - modifier = Modifier - .fillMaxWidth(0.7f) - .clickable { expanded = true }, + modifier = Modifier.fillMaxWidth() ) DropdownMenu( expanded = expanded, - onDismissRequest = { expanded = false }, + onDismissRequest = { expanded = false } ) { mapOptions.forEach { map -> DropdownMenuItem( - text = { "Hexagon – $playerCount játékos" }, + text = { Text("Hexagon – $map játékos") }, onClick = { - selectedMap = map + viewModel.onEvent(InitViewModel.InitScreenEvent.ChangeSelectedMap(map)) expanded = false - }, + } ) } } @@ -78,28 +87,38 @@ fun InitScreen() { modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { - for (i in 0..< playerCount) { - item { Text(i.toString() + ". játékos") } + for (playerNumber in 1..playerCount) { + val index = playerNumber - 1 + + item { Text("$playerNumber. játékos") } + item { CreatePlayerCard( - index = i, + index = index, modifier = Modifier.fillMaxWidth(0.45f), - player = playerList[i], - onChange = {player, index -> - viewModel.onEvent(InitViewModel.InitScreenEvent.UpdatePlayerData(player, index)) + player = playerList[index], + onChange = { player, i -> + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdatePlayerData(player, i) + ) }, - onStartingFieldChange = {fieldId -> - viewModel.onEvent(InitViewModel.InitScreenEvent.UpdatePlayerStartingField(fieldId = fieldId, i)) + onStartingFieldChange = { fieldId -> + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdatePlayerStartingField(fieldId, index) + ) }, - startingFieldId = startingFieldIds[i] + startingFieldId = startingFieldIds[index] ) Spacer(modifier = Modifier.height(8.dp)) } } + + // Mentés gomb: dinamikusan engedélyezett item { Button( onClick = { viewModel.savePlayers() }, - modifier = Modifier.padding(16.dp), + enabled = isFormValid, + modifier = Modifier.padding(16.dp) ) { Text("Adatok mentése") } @@ -107,7 +126,3 @@ fun InitScreen() { } } } - -private fun InitViewModel.savePlayers() { - TODO("savePlayers függvény hiányos") -} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 4376e94..c8560c3 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -1,34 +1,34 @@ package hu.mokegyesulet.it.dunestrat.feature.init -import androidx.compose.foundation.shape.CornerSize import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel -import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Player class InitViewModel() : ViewModel() { - private val startingPlayer = Player("", 0,0,mapOf(),setOf(),) + + private val startingPlayer = Player("", 0, 0, mapOf(), setOf()) val playerList: MutableState> val startingFieldIds: MutableState> + init { val startingPlayers = mutableListOf() - val emtpyFieldIds = mutableListOf() + val emptyFieldIds = mutableListOf() repeat(12) { startingPlayers.add(startingPlayer.copy()) - emtpyFieldIds.add("") + emptyFieldIds.add("") } playerList = mutableStateOf(startingPlayers) - startingFieldIds = mutableStateOf(emtpyFieldIds) + startingFieldIds = mutableStateOf(emptyFieldIds) } + var dropdownExpanded = mutableStateOf(false) var selectedMap = mutableStateOf(12) val playerCount = mutableStateOf(12) val mapOptions = mutableStateOf(listOf(12, 6)) + val isFormValid = mutableStateOf(false) fun onEvent(event: InitScreenEvent) { when (event) { @@ -38,27 +38,58 @@ class InitViewModel() : ViewModel() { is InitScreenEvent.ChangeSelectedMap -> { selectedMap.value = event.desertSize dropdownExpanded.value = false + resizePlayerList(event.desertSize) } is InitScreenEvent.UpdatePlayerData -> { val newPlayerList = playerList.value.toMutableList() newPlayerList[event.index] = event.playerData playerList.value = newPlayerList + validateForm() } is InitScreenEvent.UpdatePlayerStartingField -> { val newStartingFieldIds = startingFieldIds.value.toMutableList() newStartingFieldIds[event.index] = event.fieldId startingFieldIds.value = newStartingFieldIds + validateForm() } - is InitScreenEvent.InitPlayerOnMap -> {} } } - sealed class InitScreenEvent() { + private fun validateForm() { + isFormValid.value = playerList.value.all { player -> + player.id.isNotBlank() && + player.water >= 0 && + player.spice >= 0 + } + } + + private fun resizePlayerList(newCount: Int) { + val currentList = playerList.value.toMutableList() + val currentFields = startingFieldIds.value.toMutableList() + + if (currentList.size > newCount) { + playerList.value = currentList.take(newCount) + startingFieldIds.value = currentFields.take(newCount) + } else { + repeat(newCount - currentList.size) { + playerList.value = playerList.value + startingPlayer.copy() + startingFieldIds.value = startingFieldIds.value + "" + } + } + playerCount.value = newCount + validateForm() + } + + fun savePlayers() { + // TODO: implementálni a mentést + } + + sealed class InitScreenEvent { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() data class ChangeSelectedMap(val desertSize: Int) : InitScreenEvent() data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() - data class UpdatePlayerStartingField(val fieldId : String, val index: Int) : InitScreenEvent() + data class UpdatePlayerStartingField(val fieldId: String, val index: Int) : InitScreenEvent() } } From 139286231b1da04680eebf943cc211d4e30e27b9 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 22 Feb 2026 17:45:43 +0100 Subject: [PATCH 083/162] =?UTF-8?q?commentben=20a=20valid=C3=A1ci=C3=B3=20?= =?UTF-8?q?a=20sting=20hib=C3=A1ra.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/ui/CreatePlayerCard.kt | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index a43cee5..a1e00ed 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -110,3 +110,267 @@ fun CreatePlayerCard( ) } } + +/* +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Weapon +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun CreatePlayerCard( + index : Int, + modifier: Modifier, + player: Player, + onChange: (Player, Int) -> Unit, + startingFieldId: String, + onStartingFieldChange: (String) -> Unit +) { + // Hibaüzenetek state-jei + val waterError = remember { mutableStateOf(null) } + val spiceError = remember { mutableStateOf(null) } + val pistolError = remember { mutableStateOf(null) } + val lasgunError = remember { mutableStateOf(null) } + val crysknifeError = remember { mutableStateOf(null) } + val legionError = remember { mutableStateOf(null) } + + Card( + modifier = modifier.padding(20.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + TextField( + value = player.id, + onValueChange = { onChange(player.copy(id = it), index) }, + label = { Text("Játékos ID") } + ) + + // Víz készlet - try-catch + Column { + TextField( + value = player.water.toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + onChange(player.copy(water = value), index) + waterError.value = null + } else if (input.isNotEmpty()) { + waterError.value = "Kérlek, csak számot adj meg!" + } else { + waterError.value = null + } + } catch (e: NumberFormatException) { + waterError.value = "Érvénytelen szám!" + } + }, + label = { Text("Víz készlet") }, + isError = waterError.value != null + ) + waterError.value?.let { + Text( + text = it, + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } + + // Fűszer készlet - try-catch + Column { + TextField( + value = player.spice.toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + onChange(player.copy(spice = value), index) + spiceError.value = null + } else if (input.isNotEmpty()) { + spiceError.value = "Kérlek, csak számot adj meg!" + } else { + spiceError.value = null + } + } catch (e: NumberFormatException) { + spiceError.value = "Érvénytelen szám!" + } + }, + label = { Text("Fűszer készlet") }, + isError = spiceError.value != null + ) + spiceError.value?.let { + Text( + text = it, + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } + + // Pisztoly - try-catch + Column { + TextField( + value = player.getWeaponCount(Weapon.PISTOL).toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + val newWeapons: Map = mapOf( + Weapon.PISTOL to value, + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + pistolError.value = null + } else if (input.isNotEmpty()) { + pistolError.value = "Kérlek, csak számot adj meg!" + } else { + pistolError.value = null + } + } catch (e: NumberFormatException) { + pistolError.value = "Érvénytelen szám!" + } + }, + label = { Text("Pisztoly") }, + isError = pistolError.value != null + ) + pistolError.value?.let { + Text( + text = it, + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } + + // Lasgun - try-catch + Column { + TextField( + value = player.getWeaponCount(Weapon.LASGUN).toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to value, + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + lasgunError.value = null + } else if (input.isNotEmpty()) { + lasgunError.value = "Kérlek, csak számot adj meg!" + } else { + lasgunError.value = null + } + } catch (e: NumberFormatException) { + lasgunError.value = "Érvénytelen szám!" + } + }, + label = { Text("Lasgun") }, + isError = lasgunError.value != null + ) + lasgunError.value?.let { + Text( + text = it, + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } + + // Crysknife - try-catch + Column { + TextField( + value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to value, + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + crysknifeError.value = null + } else if (input.isNotEmpty()) { + crysknifeError.value = "Kérlek, csak számot adj meg!" + } else { + crysknifeError.value = null + } + } catch (e: NumberFormatException) { + crysknifeError.value = "Érvénytelen szám!" + } + }, + label = { Text("Crysknife") }, + isError = crysknifeError.value != null + ) + crysknifeError.value?.let { + Text( + text = it, + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } + + // Légió - try-catch + Column { + TextField( + value = player.getWeaponCount(Weapon.LEGION).toString(), + onValueChange = { input -> + try { + val value = input.toIntOrNull() + if (value != null) { + val newWeapons: Map = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to value + ) + val newPlayer = player.copy(weapons = newWeapons) + onChange(newPlayer, index) + legionError.value = null + } else if (input.isNotEmpty()) { + legionError.value = "Kérlek, csak számot adj meg!" + } else { + legionError.value = null + } + } catch (e: NumberFormatException) { + legionError.value = "Érvénytelen szám!" + } + }, + + TextField( + value = startingFieldId, + onValueChange = onStartingFieldChange, + label = { Text("Kezdő mező") } + ) + } +} + + + */ From e7115634bdc3c21badcf8ce4aa97e8be3c4bc168 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 8 Mar 2026 16:52:39 +0100 Subject: [PATCH 084/162] =?UTF-8?q?valid=C3=A1ci=C3=B3k=20=C3=A9s=20a=20pi?= =?UTF-8?q?cker=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/init/InitScreen.kt | 36 +- .../dunestrat/feature/init/InitViewModel.kt | 35 +- .../it/dunestrat/ui/CreatePlayerCard.kt | 481 ++++++------------ 3 files changed, 191 insertions(+), 361 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 9c8c39e..62f5c2a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -1,6 +1,7 @@ package hu.mokegyesulet.it.dunestrat.feature.init import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -17,18 +18,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import hu.mokegyesulet.it.dunestrat.model.Player import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard -// 1. Szám adatok ellenőrzése backenden -// 2. Menü helyes működése -// 3. Mentés gomb -// 4. Desert lista -> legördülő menü -// 5. Új gameState létrehozása - @Composable fun InitScreen() { val viewModel = viewModel { InitViewModel() } @@ -48,14 +41,7 @@ fun InitScreen() { ) { // Dropdown Box( - modifier = Modifier - .fillMaxWidth(0.7f) - .clickable( //Az alatta lévő 2 sornak jónak kéne lennie, de nem az xd - indication = null, - interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() } - ) { - expanded = true - } + modifier = Modifier.fillMaxWidth(0.7f) ) { OutlinedTextField( value = "Hexagon – $playerCount játékos", @@ -65,16 +51,28 @@ fun InitScreen() { modifier = Modifier.fillMaxWidth() ) + // Átlátszó klikkelő réteg a TextField felett + Box( + modifier = Modifier + .matchParentSize() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + expanded = !expanded + } + ) + DropdownMenu( expanded = expanded, - onDismissRequest = { expanded = false } + onDismissRequest = { expanded = false }, + modifier = Modifier.fillMaxWidth(0.7f) ) { mapOptions.forEach { map -> DropdownMenuItem( text = { Text("Hexagon – $map játékos") }, onClick = { viewModel.onEvent(InitViewModel.InitScreenEvent.ChangeSelectedMap(map)) - expanded = false } ) } @@ -113,7 +111,7 @@ fun InitScreen() { } } - // Mentés gomb: dinamikusan engedélyezett + // Mentés gomb item { Button( onClick = { viewModel.savePlayers() }, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index c8560c3..023fb61 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -1,15 +1,26 @@ package hu.mokegyesulet.it.dunestrat.feature.init import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Weapon class InitViewModel() : ViewModel() { - private val startingPlayer = Player("", 0, 0, mapOf(), setOf()) + private fun createDefaultPlayer() = Player( + id = "", + water = 0, + spice = 10, + weapons = mapOf( + Weapon.PISTOL to 0, + Weapon.LASGUN to 0, + Weapon.CRYSKNIFE to 0, + Weapon.LEGION to 0 + ), + ownedFields = setOf() + ) + val playerList: MutableState> val startingFieldIds: MutableState> @@ -17,7 +28,7 @@ class InitViewModel() : ViewModel() { val startingPlayers = mutableListOf() val emptyFieldIds = mutableListOf() repeat(12) { - startingPlayers.add(startingPlayer.copy()) + startingPlayers.add(createDefaultPlayer()) emptyFieldIds.add("") } playerList = mutableStateOf(startingPlayers) @@ -57,10 +68,16 @@ class InitViewModel() : ViewModel() { } private fun validateForm() { - isFormValid.value = playerList.value.all { player -> + // Minden mezőnek ki kell lennie töltve + isFormValid.value = playerList.value.zip(startingFieldIds.value).all { (player, fieldId) -> player.id.isNotBlank() && + fieldId.isNotBlank() && player.water >= 0 && - player.spice >= 0 + player.spice >= 0 && + player.getWeaponCount(Weapon.PISTOL) >= 0 && + player.getWeaponCount(Weapon.LASGUN) >= 0 && + player.getWeaponCount(Weapon.CRYSKNIFE) >= 0 && + player.getWeaponCount(Weapon.LEGION) >= 0 } } @@ -73,7 +90,7 @@ class InitViewModel() : ViewModel() { startingFieldIds.value = currentFields.take(newCount) } else { repeat(newCount - currentList.size) { - playerList.value = playerList.value + startingPlayer.copy() + playerList.value = playerList.value + createDefaultPlayer() startingFieldIds.value = startingFieldIds.value + "" } } @@ -83,6 +100,10 @@ class InitViewModel() : ViewModel() { fun savePlayers() { // TODO: implementálni a mentést + println("Játékosok mentése...") + playerList.value.forEachIndexed { index, player -> + println("${index + 1}. Jatekos: $player, Kezdo mezo: ${startingFieldIds.value[index]}") + } } sealed class InitScreenEvent { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index a1e00ed..d1c90c0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -1,126 +1,9 @@ package hu.mokegyesulet.it.dunestrat.ui -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import hu.mokegyesulet.it.dunestrat.model.Player -import hu.mokegyesulet.it.dunestrat.model.Weapon -import org.jetbrains.compose.ui.tooling.preview.Preview - -@Composable -@Preview -fun CreatePlayerCard( - index : Int, - modifier: Modifier, - player: Player, - onChange: (Player, Int) -> Unit, - startingFieldId: String, - onStartingFieldChange: (String) -> Unit -) { - Card( - modifier = modifier.padding(20.dp) - ) { - TextField( - value = player.id, - onValueChange = { onChange(player.copy(id = it), index) }, - label = { Text("Játékos ID") } - ) - - TextField( - value = player.water.toString(), - onValueChange = { onChange(player.copy(water = it.toInt()), index) }, - label = { Text("Víz készlet") } - ) - - TextField( - value = player.spice.toString(), - onValueChange = { onChange(player.copy(spice = it.toInt()), index) }, - label = { Text("Fűszer készlet") } - ) - - TextField( - value = player.getWeaponCount(Weapon.PISTOL).toString(), - onValueChange = { - val newWeapons: Map = mapOf( - Weapon.PISTOL to it.toInt(), - Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - }, - label = { Text("Pisztoly") } - ) - - TextField( - value = player.getWeaponCount(Weapon.LASGUN).toString(), - onValueChange = { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), - Weapon.LASGUN to it.toInt(), - Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - }, - label = { Text("Lasgun") } - ) - - TextField( - value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), - onValueChange = { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), - Weapon.CRYSKNIFE to it.toInt(), - Weapon.LEGION to player.getWeaponCount( Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - }, - label = { Text("Crysknife") } - ) - - TextField( - value = player.getWeaponCount(Weapon.LEGION).toString(), - onValueChange = { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount( Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount( Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount( Weapon.CRYSKNIFE), - Weapon.LEGION to it.toInt() - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - }, - label = { Text("Légió") } - ) - - TextField( - value = startingFieldId, - onValueChange = onStartingFieldChange, - label = { Text("Kezdő mező") } - ) - } -} - -/* -package hu.mokegyesulet.it.dunestrat.ui - import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @@ -138,14 +21,6 @@ fun CreatePlayerCard( startingFieldId: String, onStartingFieldChange: (String) -> Unit ) { - // Hibaüzenetek state-jei - val waterError = remember { mutableStateOf(null) } - val spiceError = remember { mutableStateOf(null) } - val pistolError = remember { mutableStateOf(null) } - val lasgunError = remember { mutableStateOf(null) } - val crysknifeError = remember { mutableStateOf(null) } - val legionError = remember { mutableStateOf(null) } - Card( modifier = modifier.padding(20.dp) ) { @@ -153,224 +28,160 @@ fun CreatePlayerCard( TextField( value = player.id, onValueChange = { onChange(player.copy(id = it), index) }, - label = { Text("Játékos ID") } + label = { Text("Játékos ID") }, + isError = player.id.isBlank() ) - - // Víz készlet - try-catch - Column { - TextField( - value = player.water.toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - onChange(player.copy(water = value), index) - waterError.value = null - } else if (input.isNotEmpty()) { - waterError.value = "Kérlek, csak számot adj meg!" - } else { - waterError.value = null - } - } catch (e: NumberFormatException) { - waterError.value = "Érvénytelen szám!" - } - }, - label = { Text("Víz készlet") }, - isError = waterError.value != null + if (player.id.isBlank()) { + Text( + text = "Ez a mező kötelező!", + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) ) - waterError.value?.let { - Text( - text = it, - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } } - // Fűszer készlet - try-catch - Column { - TextField( - value = player.spice.toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - onChange(player.copy(spice = value), index) - spiceError.value = null - } else if (input.isNotEmpty()) { - spiceError.value = "Kérlek, csak számot adj meg!" - } else { - spiceError.value = null - } - } catch (e: NumberFormatException) { - spiceError.value = "Érvénytelen szám!" - } - }, - label = { Text("Fűszer készlet") }, - isError = spiceError.value != null - ) - spiceError.value?.let { - Text( - text = it, - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - } + TextField( + value = player.water.toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + onChange(player.copy(water = value), index) + } else if (input.isEmpty()) { + onChange(player.copy(water = 0), index) + } + }, + label = { Text("Víz készlet") }, + isError = player.water < 0 + ) - // Pisztoly - try-catch - Column { - TextField( - value = player.getWeaponCount(Weapon.PISTOL).toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - val newWeapons: Map = mapOf( - Weapon.PISTOL to value, - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - pistolError.value = null - } else if (input.isNotEmpty()) { - pistolError.value = "Kérlek, csak számot adj meg!" - } else { - pistolError.value = null - } - } catch (e: NumberFormatException) { - pistolError.value = "Érvénytelen szám!" - } - }, - label = { Text("Pisztoly") }, - isError = pistolError.value != null - ) - pistolError.value?.let { - Text( - text = it, - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - } + TextField( + value = player.spice.toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + onChange(player.copy(spice = value), index) + } else if (input.isEmpty()) { + onChange(player.copy(spice = 10), index) + } + }, + label = { Text("Fűszer készlet") }, + isError = player.spice < 0 + ) - // Lasgun - try-catch - Column { - TextField( - value = player.getWeaponCount(Weapon.LASGUN).toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to value, - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - lasgunError.value = null - } else if (input.isNotEmpty()) { - lasgunError.value = "Kérlek, csak számot adj meg!" - } else { - lasgunError.value = null - } - } catch (e: NumberFormatException) { - lasgunError.value = "Érvénytelen szám!" - } - }, - label = { Text("Lasgun") }, - isError = lasgunError.value != null - ) - lasgunError.value?.let { - Text( - text = it, - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - } + TextField( + value = player.getWeaponCount(Weapon.PISTOL).toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + val currentWeapons = mapOf( + Weapon.PISTOL to value, + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } else if (input.isEmpty()) { + val currentWeapons = mapOf( + Weapon.PISTOL to 0, + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } + }, + label = { Text("Pisztoly") } + ) - // Crysknife - try-catch - Column { - TextField( - value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to value, - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - crysknifeError.value = null - } else if (input.isNotEmpty()) { - crysknifeError.value = "Kérlek, csak számot adj meg!" - } else { - crysknifeError.value = null - } - } catch (e: NumberFormatException) { - crysknifeError.value = "Érvénytelen szám!" - } - }, - label = { Text("Crysknife") }, - isError = crysknifeError.value != null - ) - crysknifeError.value?.let { - Text( - text = it, - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - } + TextField( + value = player.getWeaponCount(Weapon.LASGUN).toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to value, + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } else if (input.isEmpty()) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to 0, + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } + }, + label = { Text("Lasgun") } + ) + + TextField( + value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to value, + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } else if (input.isEmpty()) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to 0, + Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) + ) + onChange(player.copy(weapons = currentWeapons), index) + } + }, + label = { Text("Crysknife") } + ) - // Légió - try-catch - Column { - TextField( - value = player.getWeaponCount(Weapon.LEGION).toString(), - onValueChange = { input -> - try { - val value = input.toIntOrNull() - if (value != null) { - val newWeapons: Map = mapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to value - ) - val newPlayer = player.copy(weapons = newWeapons) - onChange(newPlayer, index) - legionError.value = null - } else if (input.isNotEmpty()) { - legionError.value = "Kérlek, csak számot adj meg!" - } else { - legionError.value = null - } - } catch (e: NumberFormatException) { - legionError.value = "Érvénytelen szám!" - } - }, + TextField( + value = player.getWeaponCount(Weapon.LEGION).toString(), + onValueChange = { input -> + val value = input.toIntOrNull() + if (value != null && value >= 0) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to value + ) + onChange(player.copy(weapons = currentWeapons), index) + } else if (input.isEmpty()) { + val currentWeapons = mapOf( + Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), + Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), + Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), + Weapon.LEGION to 0 + ) + onChange(player.copy(weapons = currentWeapons), index) + } + }, + label = { Text("Légió") } + ) - TextField( - value = startingFieldId, - onValueChange = onStartingFieldChange, - label = { Text("Kezdő mező") } - ) + TextField( + value = startingFieldId, + onValueChange = onStartingFieldChange, + label = { Text("Kezdő mező") }, + isError = startingFieldId.isBlank() + ) + if (startingFieldId.isBlank()) { + Text( + text = "Ez a mező kötelező!", + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp) + ) + } + } } } - - - */ From 1cf9e4e523231316572e3063decb03c5a55271c0 Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 8 Mar 2026 17:50:21 +0100 Subject: [PATCH 085/162] =?UTF-8?q?merge=20conflict=20jav=C3=ADt=C3=A1s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/init/InitViewModel.kt | 5 +++-- .../it/dunestrat/ui/CreatePlayerCard.kt | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 023fb61..a903308 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -12,13 +12,14 @@ class InitViewModel() : ViewModel() { id = "", water = 0, spice = 10, - weapons = mapOf( + weapons = mutableMapOf( Weapon.PISTOL to 0, Weapon.LASGUN to 0, Weapon.CRYSKNIFE to 0, Weapon.LEGION to 0 ), - ownedFields = setOf() + ownedFields = mutableSetOf(), + harvestersPurchased = 0 ) val playerList: MutableState> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index d1c90c0..e189c5d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -73,7 +73,7 @@ fun CreatePlayerCard( onValueChange = { input -> val value = input.toIntOrNull() if (value != null && value >= 0) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to value, Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), @@ -81,7 +81,7 @@ fun CreatePlayerCard( ) onChange(player.copy(weapons = currentWeapons), index) } else if (input.isEmpty()) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to 0, Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), @@ -98,7 +98,7 @@ fun CreatePlayerCard( onValueChange = { input -> val value = input.toIntOrNull() if (value != null && value >= 0) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to value, Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), @@ -106,7 +106,7 @@ fun CreatePlayerCard( ) onChange(player.copy(weapons = currentWeapons), index) } else if (input.isEmpty()) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to 0, Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), @@ -123,7 +123,7 @@ fun CreatePlayerCard( onValueChange = { input -> val value = input.toIntOrNull() if (value != null && value >= 0) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to value, @@ -131,7 +131,7 @@ fun CreatePlayerCard( ) onChange(player.copy(weapons = currentWeapons), index) } else if (input.isEmpty()) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to 0, @@ -148,7 +148,7 @@ fun CreatePlayerCard( onValueChange = { input -> val value = input.toIntOrNull() if (value != null && value >= 0) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), @@ -156,7 +156,7 @@ fun CreatePlayerCard( ) onChange(player.copy(weapons = currentWeapons), index) } else if (input.isEmpty()) { - val currentWeapons = mapOf( + val currentWeapons = mutableMapOf( Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), From 326d80271d785e8ea61b64e54dae4e97c57b48ae Mon Sep 17 00:00:00 2001 From: MotoZs <> Date: Sun, 8 Mar 2026 19:07:44 +0100 Subject: [PATCH 086/162] =?UTF-8?q?WIP=20desertek=20lek=C3=A9r=C3=A9se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/init/InitScreen.kt | 3 +++ .../it/dunestrat/feature/init/InitViewModel.kt | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 62f5c2a..c45ce58 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -16,12 +16,14 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.* +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard + @Composable fun InitScreen() { val viewModel = viewModel { InitViewModel() } @@ -32,6 +34,7 @@ fun InitScreen() { val playerList by viewModel.playerList val startingFieldIds by viewModel.startingFieldIds val isFormValid by viewModel.isFormValid + val deserts by viewModel.mapOptions.collectAsStateWithLifecycle() Column( modifier = Modifier diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index a903308..4631e19 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -3,8 +3,14 @@ package hu.mokegyesulet.it.dunestrat.feature.init import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.model.Player import hu.mokegyesulet.it.dunestrat.model.Weapon +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository +import hu.mokegyesulet.it.dunestrat.model.Desert +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn class InitViewModel() : ViewModel() { @@ -39,7 +45,8 @@ class InitViewModel() : ViewModel() { var dropdownExpanded = mutableStateOf(false) var selectedMap = mutableStateOf(12) val playerCount = mutableStateOf(12) - val mapOptions = mutableStateOf(listOf(12, 6)) + val mapOptions = mutableStateOf(SupabaseRepository.getDeserts() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())) val isFormValid = mutableStateOf(false) fun onEvent(event: InitScreenEvent) { @@ -48,9 +55,10 @@ class InitViewModel() : ViewModel() { dropdownExpanded.value = event.expanded } is InitScreenEvent.ChangeSelectedMap -> { - selectedMap.value = event.desertSize + selectedMap.value = event.desertSize.name.split(" ").first().toInt() dropdownExpanded.value = false - resizePlayerList(event.desertSize) + resizePlayerList(event.desertSize.name.split(" ").first().toInt()) + } is InitScreenEvent.UpdatePlayerData -> { val newPlayerList = playerList.value.toMutableList() @@ -110,7 +118,7 @@ class InitViewModel() : ViewModel() { sealed class InitScreenEvent { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() - data class ChangeSelectedMap(val desertSize: Int) : InitScreenEvent() + data class ChangeSelectedMap(val desertSize: Desert) : InitScreenEvent() data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() data class UpdatePlayerStartingField(val fieldId: String, val index: Int) : InitScreenEvent() } From df63140bd7f5915ed05dccf6f13ddd9061232906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 22 Mar 2026 16:09:43 +0100 Subject: [PATCH 087/162] Fix dropdown text field --- .../it/dunestrat/feature/init/InitScreen.kt | 106 +++++++++++------- .../dunestrat/feature/init/InitViewModel.kt | 38 ++++--- .../it/dunestrat/navigation/NavGraph.kt | 4 +- .../it/dunestrat/navigation/Screen.kt | 3 + 4 files changed, 95 insertions(+), 56 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index c45ce58..865d817 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -13,28 +13,35 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.* import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard - +@OptIn(ExperimentalMaterial3Api::class) @Composable fun InitScreen() { val viewModel = viewModel { InitViewModel() } - var expanded by viewModel.dropdownExpanded - var selectedMap by viewModel.selectedMap + val expanded by viewModel.dropdownExpanded +// val selectedMap by viewModel.selectedMap val playerCount by viewModel.playerCount - val mapOptions by viewModel.mapOptions + val mapOptions: List by viewModel.mapOptions.collectAsStateWithLifecycle() val playerList by viewModel.playerList val startingFieldIds by viewModel.startingFieldIds val isFormValid by viewModel.isFormValid - val deserts by viewModel.mapOptions.collectAsStateWithLifecycle() + val selectedDesert by viewModel.selectedDesert Column( modifier = Modifier @@ -44,40 +51,56 @@ fun InitScreen() { ) { // Dropdown Box( - modifier = Modifier.fillMaxWidth(0.7f) + modifier = Modifier.fillMaxWidth(0.7f), ) { - OutlinedTextField( - value = "Hexagon – $playerCount játékos", - onValueChange = {}, - readOnly = true, - label = { Text("Térkép kiválasztása") }, - modifier = Modifier.fillMaxWidth() - ) - - // Átlátszó klikkelő réteg a TextField felett - Box( - modifier = Modifier - .matchParentSize() - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - expanded = !expanded - } - ) - - DropdownMenu( + ExposedDropdownMenuBox( expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.fillMaxWidth(0.7f) - ) { - mapOptions.forEach { map -> - DropdownMenuItem( - text = { Text("Hexagon – $map játékos") }, - onClick = { - viewModel.onEvent(InitViewModel.InitScreenEvent.ChangeSelectedMap(map)) - } + onExpandedChange = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(!expanded), ) + }, + + ) { + OutlinedTextField( + modifier = Modifier.menuAnchor( + ExposedDropdownMenuAnchorType.PrimaryNotEditable, + true, + ).fillMaxWidth(), + value = selectedDesert?.name?.ifBlank { + "${selectedDesert?.fields?.count { it.startingField }} játékos térkép" + } ?: "", + onValueChange = {}, + readOnly = true, + label = { Text("Térkép kiválasztása") }, + trailingIcon = { TrailingIcon(expanded = expanded) }, + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(false), + ) + }, + modifier = Modifier.fillMaxWidth(0.7f), + ) { + mapOptions.forEach { map -> + DropdownMenuItem( + text = { + Text( + text = map.name.ifBlank { + "${map.fields.count { it.startingField }} játékos térkép" + }, + ) + }, + onClick = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeSelectedMap(map), + ) + }, + ) + } } } } @@ -100,15 +123,18 @@ fun InitScreen() { player = playerList[index], onChange = { player, i -> viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdatePlayerData(player, i) + InitViewModel.InitScreenEvent.UpdatePlayerData(player, i), ) }, onStartingFieldChange = { fieldId -> viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdatePlayerStartingField(fieldId, index) + InitViewModel.InitScreenEvent.UpdatePlayerStartingField( + fieldId, + index, + ), ) }, - startingFieldId = startingFieldIds[index] + startingFieldId = startingFieldIds[index], ) Spacer(modifier = Modifier.height(8.dp)) } @@ -119,7 +145,7 @@ fun InitScreen() { Button( onClick = { viewModel.savePlayers() }, enabled = isFormValid, - modifier = Modifier.padding(16.dp) + modifier = Modifier.padding(16.dp), ) { Text("Adatok mentése") } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 4631e19..144b535 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -4,11 +4,10 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import hu.mokegyesulet.it.dunestrat.model.Player -import hu.mokegyesulet.it.dunestrat.model.Weapon import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository import hu.mokegyesulet.it.dunestrat.model.Desert -import kotlinx.coroutines.flow.Flow +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn @@ -22,10 +21,10 @@ class InitViewModel() : ViewModel() { Weapon.PISTOL to 0, Weapon.LASGUN to 0, Weapon.CRYSKNIFE to 0, - Weapon.LEGION to 0 + Weapon.LEGION to 0, ), ownedFields = mutableSetOf(), - harvestersPurchased = 0 + harvestersPurchased = 0, ) val playerList: MutableState> @@ -43,35 +42,41 @@ class InitViewModel() : ViewModel() { } var dropdownExpanded = mutableStateOf(false) - var selectedMap = mutableStateOf(12) val playerCount = mutableStateOf(12) - val mapOptions = mutableStateOf(SupabaseRepository.getDeserts() - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())) + val mapOptions = SupabaseRepository.getDeserts() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) val isFormValid = mutableStateOf(false) + val selectedDesert: MutableState = mutableStateOf(null) + fun onEvent(event: InitScreenEvent) { when (event) { is InitScreenEvent.ChangeMapDropdownExpanded -> { dropdownExpanded.value = event.expanded } + is InitScreenEvent.ChangeSelectedMap -> { - selectedMap.value = event.desertSize.name.split(" ").first().toInt() - dropdownExpanded.value = false - resizePlayerList(event.desertSize.name.split(" ").first().toInt()) + selectedDesert.value = event.selectedDesert + playerCount.value = selectedDesert.value?.fields?.count { it.startingField } ?: 0 + dropdownExpanded.value = false + resizePlayerList(playerCount.value) } + is InitScreenEvent.UpdatePlayerData -> { val newPlayerList = playerList.value.toMutableList() newPlayerList[event.index] = event.playerData playerList.value = newPlayerList validateForm() } + is InitScreenEvent.UpdatePlayerStartingField -> { val newStartingFieldIds = startingFieldIds.value.toMutableList() newStartingFieldIds[event.index] = event.fieldId startingFieldIds.value = newStartingFieldIds validateForm() } + is InitScreenEvent.InitPlayerOnMap -> {} } } @@ -99,8 +104,8 @@ class InitViewModel() : ViewModel() { startingFieldIds.value = currentFields.take(newCount) } else { repeat(newCount - currentList.size) { - playerList.value = playerList.value + createDefaultPlayer() - startingFieldIds.value = startingFieldIds.value + "" + playerList.value += createDefaultPlayer() + startingFieldIds.value += "" } } playerCount.value = newCount @@ -118,8 +123,11 @@ class InitViewModel() : ViewModel() { sealed class InitScreenEvent { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() - data class ChangeSelectedMap(val desertSize: Desert) : InitScreenEvent() + data class ChangeSelectedMap(val selectedDesert: Desert) : InitScreenEvent() data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() - data class UpdatePlayerStartingField(val fieldId: String, val index: Int) : InitScreenEvent() + data class UpdatePlayerStartingField( + val fieldId: String, + val index: Int, + ) : InitScreenEvent() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 5c863a4..198bd95 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -5,6 +5,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu import hu.mokegyesulet.it.dunestrat.feature.testdata.TestDataScreen @@ -16,12 +17,13 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlaceholder1 = {}, + onPlaceholder1 = { navController.navigate(Screen.InitScreen) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) } composable { TestDataScreen() } + composable { InitScreen() } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 0727abf..9804171 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -9,4 +9,7 @@ sealed class Screen { @Serializable data object TestData : Screen() + + @Serializable + data object InitScreen : Screen() } From 342fd7067b1c11658aa48e49a08494b66e296c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 10 May 2026 17:33:04 +0200 Subject: [PATCH 088/162] Player id integer, auto-assigned --- .../hu/mokegyesulet/it/dunestrat/App.kt | 2 - .../it/dunestrat/feature/init/InitScreen.kt | 4 - .../dunestrat/feature/init/InitViewModel.kt | 15 +- .../feature/testdata/TestDataScreen.kt | 1 - .../feature/testdata/TestDataViewModel.kt | 1 + .../it/dunestrat/ui/CreatePlayerCard.kt | 198 +++++------------- 6 files changed, 63 insertions(+), 158 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index e8ca5c2..6cfc9a9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -6,8 +6,6 @@ import androidx.compose.runtime.* import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import hu.mokegyesulet.it.dunestrat.ui.AppTheme import org.jetbrains.compose.ui.tooling.preview.Preview -import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen -import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard @Composable @Preview diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 865d817..c5de65e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -1,7 +1,5 @@ package hu.mokegyesulet.it.dunestrat.feature.init -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -11,7 +9,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button -import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuAnchorType @@ -19,7 +16,6 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.* import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 144b535..998b09d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -13,8 +13,8 @@ import kotlinx.coroutines.flow.stateIn class InitViewModel() : ViewModel() { - private fun createDefaultPlayer() = Player( - id = "", + private fun createDefaultPlayer(index: Int) = Player( + id = index, water = 0, spice = 10, weapons = mutableMapOf( @@ -33,8 +33,8 @@ class InitViewModel() : ViewModel() { init { val startingPlayers = mutableListOf() val emptyFieldIds = mutableListOf() - repeat(12) { - startingPlayers.add(createDefaultPlayer()) + repeat(12) { index -> + startingPlayers.add(createDefaultPlayer(index)) emptyFieldIds.add("") } playerList = mutableStateOf(startingPlayers) @@ -84,8 +84,7 @@ class InitViewModel() : ViewModel() { private fun validateForm() { // Minden mezőnek ki kell lennie töltve isFormValid.value = playerList.value.zip(startingFieldIds.value).all { (player, fieldId) -> - player.id.isNotBlank() && - fieldId.isNotBlank() && + fieldId.isNotBlank() && player.water >= 0 && player.spice >= 0 && player.getWeaponCount(Weapon.PISTOL) >= 0 && @@ -103,8 +102,8 @@ class InitViewModel() : ViewModel() { playerList.value = currentList.take(newCount) startingFieldIds.value = currentFields.take(newCount) } else { - repeat(newCount - currentList.size) { - playerList.value += createDefaultPlayer() + repeat(newCount - currentList.size) { offset -> + playerList.value += createDefaultPlayer(currentList.size + offset) startingFieldIds.value += "" } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt index b08d55b..237933c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt index 1a22bd2..3969f38 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -22,6 +22,7 @@ class TestDataViewModel : ViewModel() { private var players = (1..12).map { initPlayer(it) } init { + players = (0..11).map { initPlayer(it) } viewModelScope.launch { games.collect { list -> println("Games from Supabase: $list") diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index e189c5d..76b09c4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -14,174 +14,86 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun CreatePlayerCard( - index : Int, + index: Int, modifier: Modifier, player: Player, onChange: (Player, Int) -> Unit, startingFieldId: String, - onStartingFieldChange: (String) -> Unit + onStartingFieldChange: (String) -> Unit, ) { + val weaponLabels = mapOf( + Weapon.PISTOL to "Pisztoly", + Weapon.LASGUN to "Lasgun", + Weapon.CRYSKNIFE to "Crysknife", + Weapon.LEGION to "Légió", + ) + Card( - modifier = modifier.padding(20.dp) + modifier = modifier.padding(20.dp), ) { Column(modifier = Modifier.padding(16.dp)) { - TextField( - value = player.id, - onValueChange = { onChange(player.copy(id = it), index) }, - label = { Text("Játékos ID") }, - isError = player.id.isBlank() - ) - if (player.id.isBlank()) { - Text( - text = "Ez a mező kötelező!", - color = Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - - TextField( - value = player.water.toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - onChange(player.copy(water = value), index) - } else if (input.isEmpty()) { - onChange(player.copy(water = 0), index) - } - }, - label = { Text("Víz készlet") }, - isError = player.water < 0 - ) - - TextField( - value = player.spice.toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - onChange(player.copy(spice = value), index) - } else if (input.isEmpty()) { - onChange(player.copy(spice = 10), index) - } - }, - label = { Text("Fűszer készlet") }, - isError = player.spice < 0 + NumericTextField( + value = player.water, + onValueChange = { onChange(player.copy(water = it), index) }, + label = "Víz készlet", ) - TextField( - value = player.getWeaponCount(Weapon.PISTOL).toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to value, - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } else if (input.isEmpty()) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to 0, - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } - }, - label = { Text("Pisztoly") } + NumericTextField( + value = player.spice, + onValueChange = { onChange(player.copy(spice = it), index) }, + label = "Fűszer készlet", + defaultValue = 10, ) - TextField( - value = player.getWeaponCount(Weapon.LASGUN).toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to value, - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } else if (input.isEmpty()) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to 0, - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } - }, - label = { Text("Lasgun") } - ) - - TextField( - value = player.getWeaponCount(Weapon.CRYSKNIFE).toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to value, - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } else if (input.isEmpty()) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to 0, - Weapon.LEGION to player.getWeaponCount(Weapon.LEGION) - ) - onChange(player.copy(weapons = currentWeapons), index) - } - }, - label = { Text("Crysknife") } - ) - - TextField( - value = player.getWeaponCount(Weapon.LEGION).toString(), - onValueChange = { input -> - val value = input.toIntOrNull() - if (value != null && value >= 0) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to value - ) - onChange(player.copy(weapons = currentWeapons), index) - } else if (input.isEmpty()) { - val currentWeapons = mutableMapOf( - Weapon.PISTOL to player.getWeaponCount(Weapon.PISTOL), - Weapon.LASGUN to player.getWeaponCount(Weapon.LASGUN), - Weapon.CRYSKNIFE to player.getWeaponCount(Weapon.CRYSKNIFE), - Weapon.LEGION to 0 - ) - onChange(player.copy(weapons = currentWeapons), index) - } - }, - label = { Text("Légió") } - ) + Weapon.entries.forEach { weapon -> + NumericTextField( + value = player.getWeaponCount(weapon), + onValueChange = { newValue -> + val newWeapons = Weapon.entries.associateWith { player.getWeaponCount(it) }.toMutableMap() + newWeapons[weapon] = newValue + onChange(player.copy(weapons = newWeapons), index) + }, + label = weaponLabels[weapon] ?: weapon.name, + ) + } TextField( value = startingFieldId, onValueChange = onStartingFieldChange, label = { Text("Kezdő mező") }, - isError = startingFieldId.isBlank() + isError = startingFieldId.isBlank(), ) if (startingFieldId.isBlank()) { Text( text = "Ez a mező kötelező!", color = Color.Red, style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp, top = 4.dp) + modifier = Modifier.padding(start = 16.dp, top = 4.dp), ) } } } } + +@Composable +private fun NumericTextField( + value: Int, + onValueChange: (Int) -> Unit, + label: String, + defaultValue: Int = 0, +) { + TextField( + value = value.toString(), + onValueChange = { input -> + val newValue = input.toIntOrNull() + if (newValue != null && newValue >= 0) { + onValueChange(newValue) + } else if (input.isEmpty()) { + onValueChange(defaultValue) + } + }, + label = { Text(label) }, + isError = value < 0, + ) +} + From 96bb190d577a0dfb927a6fd6fb05de53d4c3c51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 10 May 2026 17:51:52 +0200 Subject: [PATCH 089/162] Starting field selectable from dropdown --- .../it/dunestrat/feature/init/InitScreen.kt | 8 ++- .../dunestrat/feature/init/InitViewModel.kt | 21 ++++--- .../it/dunestrat/ui/CreatePlayerCard.kt | 57 +++++++++++++++---- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index c5de65e..db6a5a6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -113,6 +113,10 @@ fun InitScreen() { item { Text("$playerNumber. játékos") } item { + val currentFieldId = startingFieldIds[index] + val isDuplicate = startingFieldIds.count { it == currentFieldId && it.isNotBlank() } > 1 + val availableFields = selectedDesert?.fields?.filter { it.startingField }?.map { it.id } ?: emptyList() + CreatePlayerCard( index = index, modifier = Modifier.fillMaxWidth(0.45f), @@ -122,6 +126,7 @@ fun InitScreen() { InitViewModel.InitScreenEvent.UpdatePlayerData(player, i), ) }, + startingFieldId = currentFieldId, onStartingFieldChange = { fieldId -> viewModel.onEvent( InitViewModel.InitScreenEvent.UpdatePlayerStartingField( @@ -130,7 +135,8 @@ fun InitScreen() { ), ) }, - startingFieldId = startingFieldIds[index], + availableStartingFields = availableFields, + isFieldDuplicate = isDuplicate, ) Spacer(modifier = Modifier.height(8.dp)) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 998b09d..c8206a6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -58,9 +58,13 @@ class InitViewModel() : ViewModel() { is InitScreenEvent.ChangeSelectedMap -> { selectedDesert.value = event.selectedDesert - playerCount.value = selectedDesert.value?.fields?.count { it.startingField } ?: 0 + val startingFields = event.selectedDesert.fields.filter { it.startingField } + playerCount.value = startingFields.size dropdownExpanded.value = false resizePlayerList(playerCount.value) + + startingFieldIds.value = startingFields.map { it.id } + validateForm() } is InitScreenEvent.UpdatePlayerData -> { @@ -82,15 +86,14 @@ class InitViewModel() : ViewModel() { } private fun validateForm() { - // Minden mezőnek ki kell lennie töltve - isFormValid.value = playerList.value.zip(startingFieldIds.value).all { (player, fieldId) -> - fieldId.isNotBlank() && - player.water >= 0 && + // Minden mezőnek ki kell lennie töltve és a kezdő mezőknek egyedinek kell lenniük + val allFieldsAssigned = startingFieldIds.value.all { it.isNotBlank() } + val uniqueFields = startingFieldIds.value.distinct().size == startingFieldIds.value.size + + isFormValid.value = allFieldsAssigned && uniqueFields && playerList.value.all { player -> + player.water >= 0 && player.spice >= 0 && - player.getWeaponCount(Weapon.PISTOL) >= 0 && - player.getWeaponCount(Weapon.LASGUN) >= 0 && - player.getWeaponCount(Weapon.CRYSKNIFE) >= 0 && - player.getWeaponCount(Weapon.LEGION) >= 0 + Weapon.entries.all { player.getWeaponCount(it) >= 0 } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index 76b09c4..986090f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -1,9 +1,8 @@ package hu.mokegyesulet.it.dunestrat.ui -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material3.* -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @@ -13,6 +12,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview +@OptIn(ExperimentalMaterial3Api::class) fun CreatePlayerCard( index: Int, modifier: Modifier, @@ -20,6 +20,8 @@ fun CreatePlayerCard( onChange: (Player, Int) -> Unit, startingFieldId: String, onStartingFieldChange: (String) -> Unit, + availableStartingFields: List, + isFieldDuplicate: Boolean, ) { val weaponLabels = mapOf( Weapon.PISTOL to "Pisztoly", @@ -28,6 +30,8 @@ fun CreatePlayerCard( Weapon.LEGION to "Légió", ) + var expanded by remember { mutableStateOf(false) } + Card( modifier = modifier.padding(20.dp), ) { @@ -57,13 +61,46 @@ fun CreatePlayerCard( ) } - TextField( - value = startingFieldId, - onValueChange = onStartingFieldChange, - label = { Text("Kezdő mező") }, - isError = startingFieldId.isBlank(), - ) - if (startingFieldId.isBlank()) { + Spacer(modifier = Modifier.height(16.dp)) + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = it }, + ) { + OutlinedTextField( + value = startingFieldId, + onValueChange = {}, + readOnly = true, + label = { Text("Kezdő mező") }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable).fillMaxWidth(), + isError = isFieldDuplicate || startingFieldId.isBlank(), + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + availableStartingFields.forEach { fieldId -> + DropdownMenuItem( + text = { Text(fieldId) }, + onClick = { + onStartingFieldChange(fieldId) + expanded = false + }, + ) + } + } + } + + if (isFieldDuplicate) { + Text( + text = "Ez a mező már ki van választva!", + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp, top = 4.dp), + ) + } else if (startingFieldId.isBlank()) { Text( text = "Ez a mező kötelező!", color = Color.Red, From 6426a727804e9a014fa62e07f44da0ca79a57a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 10 May 2026 18:05:19 +0200 Subject: [PATCH 090/162] Add validation for desert selection --- .../it/dunestrat/feature/init/InitScreen.kt | 16 ++++++++++++++-- .../it/dunestrat/feature/init/InitViewModel.kt | 13 +++++++------ .../it/dunestrat/ui/CreatePlayerCard.kt | 13 +++++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index db6a5a6..7c36541 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.* @@ -46,7 +47,7 @@ fun InitScreen() { horizontalAlignment = Alignment.CenterHorizontally, ) { // Dropdown - Box( + Column( modifier = Modifier.fillMaxWidth(0.7f), ) { ExposedDropdownMenuBox( @@ -70,6 +71,7 @@ fun InitScreen() { readOnly = true, label = { Text("Térkép kiválasztása") }, trailingIcon = { TrailingIcon(expanded = expanded) }, + isError = selectedDesert == null, ) ExposedDropdownMenu( @@ -99,6 +101,14 @@ fun InitScreen() { } } } + if (selectedDesert == null) { + Text( + text = "A térkép kiválasztása kötelező!", + color = androidx.compose.ui.graphics.Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(top = 4.dp), + ) + } } Spacer(modifier = Modifier.height(16.dp)) @@ -107,7 +117,8 @@ fun InitScreen() { modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { - for (playerNumber in 1..playerCount) { + if (selectedDesert != null) { + for (playerNumber in 1..playerCount) { val index = playerNumber - 1 item { Text("$playerNumber. játékos") } @@ -141,6 +152,7 @@ fun InitScreen() { Spacer(modifier = Modifier.height(8.dp)) } } + } // Mentés gomb item { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index c8206a6..c6807d5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -86,15 +86,16 @@ class InitViewModel() : ViewModel() { } private fun validateForm() { - // Minden mezőnek ki kell lennie töltve és a kezdő mezőknek egyedinek kell lenniük + val desertSelected = selectedDesert.value != null val allFieldsAssigned = startingFieldIds.value.all { it.isNotBlank() } val uniqueFields = startingFieldIds.value.distinct().size == startingFieldIds.value.size - isFormValid.value = allFieldsAssigned && uniqueFields && playerList.value.all { player -> - player.water >= 0 && - player.spice >= 0 && - Weapon.entries.all { player.getWeaponCount(it) >= 0 } - } + isFormValid.value = + desertSelected && allFieldsAssigned && uniqueFields && playerList.value.all { player -> + player.water >= 0 && + player.spice >= 0 && + Weapon.entries.all { player.getWeaponCount(it) >= 0 } + } } private fun resizePlayerList(newCount: Int) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index 986090f..c160bd3 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -53,7 +53,9 @@ fun CreatePlayerCard( NumericTextField( value = player.getWeaponCount(weapon), onValueChange = { newValue -> - val newWeapons = Weapon.entries.associateWith { player.getWeaponCount(it) }.toMutableMap() + val newWeapons = Weapon.entries.associateWith { + player.getWeaponCount(it) + }.toMutableMap() newWeapons[weapon] = newValue onChange(player.copy(weapons = newWeapons), index) }, @@ -72,8 +74,12 @@ fun CreatePlayerCard( onValueChange = {}, readOnly = true, label = { Text("Kezdő mező") }, - trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable).fillMaxWidth(), + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) + }, + modifier = Modifier.menuAnchor( + ExposedDropdownMenuAnchorType.PrimaryNotEditable, + ).fillMaxWidth(), isError = isFieldDuplicate || startingFieldId.isBlank(), ) @@ -133,4 +139,3 @@ private fun NumericTextField( isError = value < 0, ) } - From 915eb8ffeb2f74128a5fcfadd01ef4a4e8134e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 10 May 2026 18:16:32 +0200 Subject: [PATCH 091/162] Single card to enter starting state for each player --- .../it/dunestrat/feature/init/InitScreen.kt | 38 ++++++++------- .../dunestrat/feature/init/InitViewModel.kt | 30 ++++++++---- .../it/dunestrat/ui/CreatePlayerCard.kt | 47 +++++++++++++------ 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 7c36541..e17156e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -25,7 +25,8 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.ui.CreatePlayerCard +import hu.mokegyesulet.it.dunestrat.ui.GlobalStartingConditionsCard +import hu.mokegyesulet.it.dunestrat.ui.PlayerStartingFieldCard @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -39,6 +40,7 @@ fun InitScreen() { val startingFieldIds by viewModel.startingFieldIds val isFormValid by viewModel.isFormValid val selectedDesert by viewModel.selectedDesert + val basePlayer by viewModel.basePlayerState Column( modifier = Modifier @@ -118,25 +120,29 @@ fun InitScreen() { horizontalAlignment = Alignment.CenterHorizontally, ) { if (selectedDesert != null) { - for (playerNumber in 1..playerCount) { - val index = playerNumber - 1 - - item { Text("$playerNumber. játékos") } - item { + GlobalStartingConditionsCard( + modifier = Modifier.fillMaxWidth(0.9f), + player = basePlayer, + onChange = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdateGlobalStartingConditions(it), + ) + }, + ) + } + + items(playerCount) { index -> val currentFieldId = startingFieldIds[index] - val isDuplicate = startingFieldIds.count { it == currentFieldId && it.isNotBlank() } > 1 - val availableFields = selectedDesert?.fields?.filter { it.startingField }?.map { it.id } ?: emptyList() + val isDuplicate = + startingFieldIds.count { it == currentFieldId && it.isNotBlank() } > 1 + val availableFields = + selectedDesert?.fields?.filter { it.startingField }?.map { it.id } + ?: emptyList() - CreatePlayerCard( + PlayerStartingFieldCard( index = index, modifier = Modifier.fillMaxWidth(0.45f), - player = playerList[index], - onChange = { player, i -> - viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdatePlayerData(player, i), - ) - }, startingFieldId = currentFieldId, onStartingFieldChange = { fieldId -> viewModel.onEvent( @@ -149,10 +155,8 @@ fun InitScreen() { availableStartingFields = availableFields, isFieldDuplicate = isDuplicate, ) - Spacer(modifier = Modifier.height(8.dp)) } } - } // Mentés gomb item { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index c6807d5..9246e68 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -27,6 +27,7 @@ class InitViewModel() : ViewModel() { harvestersPurchased = 0, ) + val basePlayerState: MutableState = mutableStateOf(createDefaultPlayer(-1)) val playerList: MutableState> val startingFieldIds: MutableState> @@ -67,10 +68,8 @@ class InitViewModel() : ViewModel() { validateForm() } - is InitScreenEvent.UpdatePlayerData -> { - val newPlayerList = playerList.value.toMutableList() - newPlayerList[event.index] = event.playerData - playerList.value = newPlayerList + is InitScreenEvent.UpdateGlobalStartingConditions -> { + basePlayerState.value = event.playerData validateForm() } @@ -82,6 +81,7 @@ class InitViewModel() : ViewModel() { } is InitScreenEvent.InitPlayerOnMap -> {} + is InitScreenEvent.UpdatePlayerData -> {} } } @@ -90,12 +90,13 @@ class InitViewModel() : ViewModel() { val allFieldsAssigned = startingFieldIds.value.all { it.isNotBlank() } val uniqueFields = startingFieldIds.value.distinct().size == startingFieldIds.value.size + val basePlayer = basePlayerState.value + val baseConditionsValid = basePlayer.water >= 0 && + basePlayer.spice >= 0 && + Weapon.entries.all { basePlayer.getWeaponCount(it) >= 0 } + isFormValid.value = - desertSelected && allFieldsAssigned && uniqueFields && playerList.value.all { player -> - player.water >= 0 && - player.spice >= 0 && - Weapon.entries.all { player.getWeaponCount(it) >= 0 } - } + desertSelected && allFieldsAssigned && uniqueFields && baseConditionsValid } private fun resizePlayerList(newCount: Int) { @@ -118,7 +119,15 @@ class InitViewModel() : ViewModel() { fun savePlayers() { // TODO: implementálni a mentést println("Játékosok mentése...") - playerList.value.forEachIndexed { index, player -> + val base = basePlayerState.value + val finalPlayers = playerList.value.map { player -> + player.copy( + water = base.water, + spice = base.spice, + weapons = Weapon.entries.associateWith { base.getWeaponCount(it) }.toMutableMap() + ) + } + finalPlayers.forEachIndexed { index, player -> println("${index + 1}. Jatekos: $player, Kezdo mezo: ${startingFieldIds.value[index]}") } } @@ -127,6 +136,7 @@ class InitViewModel() : ViewModel() { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() data class ChangeSelectedMap(val selectedDesert: Desert) : InitScreenEvent() + data class UpdateGlobalStartingConditions(val playerData: Player) : InitScreenEvent() data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() data class UpdatePlayerStartingField( val fieldId: String, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index c160bd3..11d053b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -13,15 +13,10 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview @OptIn(ExperimentalMaterial3Api::class) -fun CreatePlayerCard( - index: Int, - modifier: Modifier, +fun GlobalStartingConditionsCard( + modifier: Modifier = Modifier, player: Player, - onChange: (Player, Int) -> Unit, - startingFieldId: String, - onStartingFieldChange: (String) -> Unit, - availableStartingFields: List, - isFieldDuplicate: Boolean, + onChange: (Player) -> Unit, ) { val weaponLabels = mapOf( Weapon.PISTOL to "Pisztoly", @@ -30,21 +25,23 @@ fun CreatePlayerCard( Weapon.LEGION to "Légió", ) - var expanded by remember { mutableStateOf(false) } - Card( - modifier = modifier.padding(20.dp), + modifier = modifier.wrapContentSize() + .padding(20.dp), ) { Column(modifier = Modifier.padding(16.dp)) { + Text("Kezdő állapot", style = MaterialTheme.typography.titleMedium) + Spacer(modifier = Modifier.height(16.dp)) + NumericTextField( value = player.water, - onValueChange = { onChange(player.copy(water = it), index) }, + onValueChange = { onChange(player.copy(water = it)) }, label = "Víz készlet", ) NumericTextField( value = player.spice, - onValueChange = { onChange(player.copy(spice = it), index) }, + onValueChange = { onChange(player.copy(spice = it)) }, label = "Fűszer készlet", defaultValue = 10, ) @@ -57,13 +54,33 @@ fun CreatePlayerCard( player.getWeaponCount(it) }.toMutableMap() newWeapons[weapon] = newValue - onChange(player.copy(weapons = newWeapons), index) + onChange(player.copy(weapons = newWeapons)) }, label = weaponLabels[weapon] ?: weapon.name, ) } + } + } +} - Spacer(modifier = Modifier.height(16.dp)) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PlayerStartingFieldCard( + index: Int, + modifier: Modifier = Modifier, + startingFieldId: String, + onStartingFieldChange: (String) -> Unit, + availableStartingFields: List, + isFieldDuplicate: Boolean, +) { + var expanded by remember { mutableStateOf(false) } + + Card( + modifier = modifier.padding(horizontal = 20.dp, vertical = 8.dp), + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text("${index + 1}. játékos kezdő mezője", style = MaterialTheme.typography.labelLarge) + Spacer(modifier = Modifier.height(8.dp)) ExposedDropdownMenuBox( expanded = expanded, From cfa63227b5761f2b8817f3a58b555131d4c4eb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 10 May 2026 19:25:34 +0200 Subject: [PATCH 092/162] Team data entry, game creation in DB --- .../it/dunestrat/Platform.android.kt | 3 + .../hu/mokegyesulet/it/dunestrat/Platform.kt | 1 + .../it/dunestrat/backend/Repository.kt | 4 +- .../backend/SupabaseClientProvider.kt | 14 +- .../dunestrat/backend/SupabaseRepository.kt | 14 +- .../entities/GameStateDatabaseEntity.kt | 1 + .../it/dunestrat/feature/init/InitScreen.kt | 263 +++++++++++------- .../dunestrat/feature/init/InitViewModel.kt | 150 ++++++++-- .../it/dunestrat/model/GameStateField.kt | 48 +++- .../it/dunestrat/model/Student.kt | 7 +- .../it/dunestrat/navigation/NavGraph.kt | 8 +- .../it/dunestrat/ui/CreatePlayerCard.kt | 76 +++++ .../mokegyesulet/it/dunestrat/Platform.ios.kt | 9 +- .../mokegyesulet/it/dunestrat/Platform.js.kt | 3 + .../mokegyesulet/it/dunestrat/Platform.jvm.kt | 31 +++ .../it/dunestrat/Platform.wasmJs.kt | 5 + 16 files changed, 490 insertions(+), 147 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt b/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt index 9d05a82..204a67c 100644 --- a/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt +++ b/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt @@ -4,6 +4,9 @@ import android.os.Build class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" + override fun openUrl(url: String) { + // Supabase Auth handles this natively or via dedicated launcher on Android + } } actual fun getPlatform(): Platform = AndroidPlatform() diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt index 5d79873..ede178c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat interface Platform { val name: String + fun openUrl(url: String) } expect fun getPlatform(): Platform diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index ba95ddd..d217fe3 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -7,10 +7,10 @@ import hu.mokegyesulet.it.dunestrat.model.PlayerStep import kotlinx.coroutines.flow.Flow interface Repository { - suspend fun saveGame(game: Game) + suspend fun saveGame(game: Game): Game fun getGames(): Flow> - suspend fun saveGameState(gameState: GameState) + suspend fun saveGameState(gameState: GameState): GameState fun getGameStates(): Flow> fun getLatestGameStateByGameId(gameId: String): Flow diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt index 5c233af..983f06f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt @@ -1,15 +1,27 @@ package hu.mokegyesulet.it.dunestrat.backend +import hu.mokegyesulet.it.dunestrat.getPlatform +import io.github.jan.supabase.SupabaseClient +import io.github.jan.supabase.auth.Auth +import io.github.jan.supabase.auth.UrlLauncher import io.github.jan.supabase.createSupabaseClient import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.realtime.Realtime +import io.github.jan.supabase.annotations.SupabaseExperimental object SupabaseClientProvider { + @OptIn(SupabaseExperimental::class) private val _client = createSupabaseClient( supabaseUrl = "https://lnwvuwepwaexwybselsf.supabase.co", supabaseKey = "sb_publishable_Y8MlS_tbyIRvvsvbHvb1_g_wbyg6EfI", ) { - install(io.github.jan.supabase.auth.Auth) + install(Auth) { + urlLauncher = object : UrlLauncher { + override suspend fun openUrl(supabase: SupabaseClient, url: String) { + getPlatform().openUrl(url) + } + } + } install(Postgrest) install(Realtime) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index a455727..9124769 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -26,9 +26,10 @@ object SupabaseRepository : Repository { list.map { it.toGame() } } - override suspend fun saveGame(game: Game) { - supabase.from(GAME_TABLE).insert(game.toDatabaseEntry()) - } + override suspend fun saveGame(game: Game): Game = + supabase.from(GAME_TABLE).insert(game.toDatabaseEntry()) { + select() + }.decodeSingle().toGame() @OptIn(SupabaseExperimental::class) override fun getGameStates(): Flow> = @@ -46,9 +47,10 @@ object SupabaseRepository : Repository { ?: throw IllegalStateException("No game state found for game id: $gameId") } - override suspend fun saveGameState(gameState: GameState) { - supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) - } + override suspend fun saveGameState(gameState: GameState): GameState = + supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) { + select() + }.decodeSingle().toGameState() @OptIn(SupabaseExperimental::class) override fun getPlayerSteps(): Flow> = diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index 47c048f..8837260 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -63,6 +63,7 @@ class GameStateDatabaseEntity( players = players.toSet(), ) } + companion object { const val TABLE_NAME = "game_state" } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index e17156e..638133e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -1,22 +1,9 @@ package hu.mokegyesulet.it.dunestrat.feature.init -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.Button -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExposedDropdownMenuAnchorType -import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.* import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -27,145 +14,205 @@ import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.ui.GlobalStartingConditionsCard import hu.mokegyesulet.it.dunestrat.ui.PlayerStartingFieldCard +import hu.mokegyesulet.it.dunestrat.ui.TeamConfigurationCard @OptIn(ExperimentalMaterial3Api::class) @Composable -fun InitScreen() { +fun InitScreen(onNavigateBack: () -> Unit) { val viewModel = viewModel { InitViewModel() } + val phase by viewModel.currentPhase val expanded by viewModel.dropdownExpanded // val selectedMap by viewModel.selectedMap val playerCount by viewModel.playerCount val mapOptions: List by viewModel.mapOptions.collectAsStateWithLifecycle() val playerList by viewModel.playerList val startingFieldIds by viewModel.startingFieldIds + val teams by viewModel.teams val isFormValid by viewModel.isFormValid val selectedDesert by viewModel.selectedDesert val basePlayer by viewModel.basePlayerState + LaunchedEffect(Unit) { + viewModel.navigationEvent.collect { + onNavigateBack() + } + } + Column( modifier = Modifier .fillMaxSize() .padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - // Dropdown - Column( - modifier = Modifier.fillMaxWidth(0.7f), - ) { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { - viewModel.onEvent( - InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(!expanded), - ) - }, - + if (phase == InitializationPhase.STARTING_CONDITIONS) { + // Dropdown + Column( + modifier = Modifier.fillMaxWidth(0.7f), ) { - OutlinedTextField( - modifier = Modifier.menuAnchor( - ExposedDropdownMenuAnchorType.PrimaryNotEditable, - true, - ).fillMaxWidth(), - value = selectedDesert?.name?.ifBlank { - "${selectedDesert?.fields?.count { it.startingField }} játékos térkép" - } ?: "", - onValueChange = {}, - readOnly = true, - label = { Text("Térkép kiválasztása") }, - trailingIcon = { TrailingIcon(expanded = expanded) }, - isError = selectedDesert == null, - ) - - ExposedDropdownMenu( + ExposedDropdownMenuBox( expanded = expanded, - onDismissRequest = { + onExpandedChange = { viewModel.onEvent( - InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(false), + InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(!expanded), ) }, - modifier = Modifier.fillMaxWidth(0.7f), + ) { - mapOptions.forEach { map -> - DropdownMenuItem( - text = { - Text( - text = map.name.ifBlank { - "${map.fields.count { it.startingField }} játékos térkép" - }, - ) - }, - onClick = { - viewModel.onEvent( - InitViewModel.InitScreenEvent.ChangeSelectedMap(map), - ) - }, - ) + OutlinedTextField( + modifier = Modifier.menuAnchor( + ExposedDropdownMenuAnchorType.PrimaryNotEditable, + true, + ).fillMaxWidth(), + value = selectedDesert?.name?.ifBlank { + "${selectedDesert?.fields?.count { it.startingField }} játékos térkép" + } ?: "", + onValueChange = {}, + readOnly = true, + label = { Text("Térkép kiválasztása") }, + trailingIcon = { TrailingIcon(expanded = expanded) }, + isError = selectedDesert == null, + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeMapDropdownExpanded(false), + ) + }, + modifier = Modifier.fillMaxWidth(0.7f), + ) { + mapOptions.forEach { map -> + DropdownMenuItem( + text = { + Text( + text = map.name.ifBlank { + "${map.fields.count { it.startingField }} játékos térkép" + }, + ) + }, + onClick = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeSelectedMap(map), + ) + }, + ) + } } } + if (selectedDesert == null) { + Text( + text = "A térkép kiválasztása kötelező!", + color = androidx.compose.ui.graphics.Color.Red, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(top = 4.dp), + ) + } } - if (selectedDesert == null) { - Text( - text = "A térkép kiválasztása kötelező!", - color = androidx.compose.ui.graphics.Color.Red, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 4.dp), - ) - } - } - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(16.dp)) + } LazyColumn( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { - if (selectedDesert != null) { + if (phase == InitializationPhase.STARTING_CONDITIONS) { + if (selectedDesert != null) { + item { + GlobalStartingConditionsCard( + modifier = Modifier.fillMaxWidth(0.9f), + player = basePlayer, + onChange = { + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdateGlobalStartingConditions(it), + ) + }, + ) + } + + items(playerCount) { index -> + val currentFieldId = startingFieldIds[index] + val isDuplicate = + startingFieldIds.count { it == currentFieldId && it.isNotBlank() } > 1 + val availableFields = + selectedDesert?.fields?.filter { it.startingField }?.map { it.id } + ?: emptyList() + + PlayerStartingFieldCard( + index = index, + modifier = Modifier.fillMaxWidth(0.45f), + startingFieldId = currentFieldId, + onStartingFieldChange = { fieldId -> + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdatePlayerStartingField( + fieldId, + index, + ), + ) + }, + availableStartingFields = availableFields, + isFieldDuplicate = isDuplicate, + ) + } + + item { + Button( + onClick = { viewModel.onEvent(InitViewModel.InitScreenEvent.ProceedToTeams) }, + enabled = isFormValid, + modifier = Modifier.padding(16.dp), + ) { + Text("Tovább a csapatokhoz") + } + } + } + } else { item { - GlobalStartingConditionsCard( - modifier = Modifier.fillMaxWidth(0.9f), - player = basePlayer, - onChange = { - viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdateGlobalStartingConditions(it), - ) - }, + Text( + text = "Csapatok beállítása", + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(16.dp), ) } - items(playerCount) { index -> - val currentFieldId = startingFieldIds[index] - val isDuplicate = - startingFieldIds.count { it == currentFieldId && it.isNotBlank() } > 1 - val availableFields = - selectedDesert?.fields?.filter { it.startingField }?.map { it.id } - ?: emptyList() - - PlayerStartingFieldCard( - index = index, - modifier = Modifier.fillMaxWidth(0.45f), - startingFieldId = currentFieldId, - onStartingFieldChange = { fieldId -> + items(teams.size) { teamIndex -> + TeamConfigurationCard( + teamIndex = teamIndex, + team = teams[teamIndex], + onAddStudent = { viewModel.onEvent(InitViewModel.InitScreenEvent.AddStudent(teamIndex)) }, + onRemoveStudent = { studentIndex -> viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdatePlayerStartingField( - fieldId, - index, + InitViewModel.InitScreenEvent.RemoveStudent(teamIndex, studentIndex), + ) + }, + onUpdateStudent = { studentIndex, student -> + viewModel.onEvent( + InitViewModel.InitScreenEvent.UpdateStudent( + teamIndex, + studentIndex, + student, ), ) }, - availableStartingFields = availableFields, - isFieldDuplicate = isDuplicate, + modifier = Modifier.fillMaxWidth(0.9f), ) } - } - // Mentés gomb - item { - Button( - onClick = { viewModel.savePlayers() }, - enabled = isFormValid, - modifier = Modifier.padding(16.dp), - ) { - Text("Adatok mentése") + item { + Row(modifier = Modifier.padding(16.dp)) { + OutlinedButton( + onClick = { viewModel.onEvent(InitViewModel.InitScreenEvent.BackToConditions) }, + modifier = Modifier.padding(end = 8.dp), + ) { + Text("Vissza") + } + Button( + onClick = { viewModel.savePlayers() }, + enabled = isFormValid, + ) { + Text("Játék indítása") + } + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 9246e68..e19d54c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -5,11 +5,18 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository -import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.model.Player -import hu.mokegyesulet.it.dunestrat.model.Weapon +import hu.mokegyesulet.it.dunestrat.model.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +enum class InitializationPhase { + STARTING_CONDITIONS, + TEAMS, +} class InitViewModel() : ViewModel() { @@ -27,9 +34,14 @@ class InitViewModel() : ViewModel() { harvestersPurchased = 0, ) + val currentPhase = mutableStateOf(InitializationPhase.STARTING_CONDITIONS) val basePlayerState: MutableState = mutableStateOf(createDefaultPlayer(-1)) val playerList: MutableState> val startingFieldIds: MutableState> + val teams: MutableState> = mutableStateOf(emptyList()) + + private val _navigationEvent = MutableSharedFlow() + val navigationEvent: SharedFlow = _navigationEvent.asSharedFlow() init { val startingPlayers = mutableListOf() @@ -80,23 +92,76 @@ class InitViewModel() : ViewModel() { validateForm() } + is InitScreenEvent.ProceedToTeams -> { + if (teams.value.isEmpty() || teams.value.size != playerList.value.size) { + teams.value = playerList.value.map { player -> + Team(player.id, listOf(Student(""))) + } + } + currentPhase.value = InitializationPhase.TEAMS + validateForm() + } + + is InitScreenEvent.BackToConditions -> { + currentPhase.value = InitializationPhase.STARTING_CONDITIONS + validateForm() + } + + is InitScreenEvent.AddStudent -> { + val newTeams = teams.value.toMutableList() + val team = newTeams[event.teamIndex] + val newStudents = team.students.toMutableList() + newStudents.add(Student("")) + newTeams[event.teamIndex] = Team(team.playerId, newStudents) + teams.value = newTeams + validateForm() + } + + is InitScreenEvent.RemoveStudent -> { + val newTeams = teams.value.toMutableList() + val team = newTeams[event.teamIndex] + if (team.students.size > 1) { + val newStudents = team.students.toMutableList() + newStudents.removeAt(event.studentIndex) + newTeams[event.teamIndex] = Team(team.playerId, newStudents) + teams.value = newTeams + validateForm() + } + } + + is InitScreenEvent.UpdateStudent -> { + val newTeams = teams.value.toMutableList() + val team = newTeams[event.teamIndex] + val newStudents = team.students.toMutableList() + newStudents[event.studentIndex] = event.student + newTeams[event.teamIndex] = Team(team.playerId, newStudents) + teams.value = newTeams + validateForm() + } + is InitScreenEvent.InitPlayerOnMap -> {} is InitScreenEvent.UpdatePlayerData -> {} } } private fun validateForm() { - val desertSelected = selectedDesert.value != null - val allFieldsAssigned = startingFieldIds.value.all { it.isNotBlank() } - val uniqueFields = startingFieldIds.value.distinct().size == startingFieldIds.value.size - - val basePlayer = basePlayerState.value - val baseConditionsValid = basePlayer.water >= 0 && - basePlayer.spice >= 0 && - Weapon.entries.all { basePlayer.getWeaponCount(it) >= 0 } - - isFormValid.value = - desertSelected && allFieldsAssigned && uniqueFields && baseConditionsValid + if (currentPhase.value == InitializationPhase.STARTING_CONDITIONS) { + val desertSelected = selectedDesert.value != null + val allFieldsAssigned = startingFieldIds.value.all { it.isNotBlank() } + val uniqueFields = startingFieldIds.value.distinct().size == startingFieldIds.value.size + + val basePlayer = basePlayerState.value + val baseConditionsValid = basePlayer.water >= 0 && + basePlayer.spice >= 0 && + Weapon.entries.all { basePlayer.getWeaponCount(it) >= 0 } + + isFormValid.value = + desertSelected && allFieldsAssigned && uniqueFields && baseConditionsValid + } else { + isFormValid.value = teams.value.all { team -> + team.students.all { student -> student.name.isNotBlank() } + } + } } private fun resizePlayerList(newCount: Int) { @@ -113,22 +178,49 @@ class InitViewModel() : ViewModel() { } } playerCount.value = newCount + teams.value = emptyList() // Reset teams when count changes validateForm() } fun savePlayers() { - // TODO: implementálni a mentést - println("Játékosok mentése...") + val desert = selectedDesert.value ?: return val base = basePlayerState.value - val finalPlayers = playerList.value.map { player -> - player.copy( - water = base.water, - spice = base.spice, - weapons = Weapon.entries.associateWith { base.getWeaponCount(it) }.toMutableMap() + val finalTeams = teams.value + + viewModelScope.launch { + println("Játékosok mentése...") + + val gameTemplate = Game( + name = "Játék - ${desert.name}", + progress = GameProgress.INITIALIZED, + teams = finalTeams, + desertId = desert.id, ) - } - finalPlayers.forEachIndexed { index, player -> - println("${index + 1}. Jatekos: $player, Kezdo mezo: ${startingFieldIds.value[index]}") + val savedGame = SupabaseRepository.saveGame(gameTemplate) + + val stateFields = desert.fields.toGameStateFields() + + val initialPlayers = playerList.value.mapIndexed { index, player -> + val fieldId = startingFieldIds.value[index] + player.copy( + water = base.water, + spice = base.spice, + weapons = Weapon.entries.associateWith { base.getWeaponCount(it) }.toMutableMap(), + ownedFields = mutableSetOf(stateFields.first { it.id == fieldId }), + ) + } + + val firstState = GameState( + id = -1, + gameId = savedGame.id, + index = 0, + fields = stateFields, + players = initialPlayers.toSet(), + ) + SupabaseRepository.saveGameState(firstState) + + println("Játék és kezdeti állapot sikeresen mentve! Game ID: ${savedGame.id}") + _navigationEvent.emit(Unit) } } @@ -142,5 +234,15 @@ class InitViewModel() : ViewModel() { val fieldId: String, val index: Int, ) : InitScreenEvent() + + data object ProceedToTeams : InitScreenEvent() + data object BackToConditions : InitScreenEvent() + data class AddStudent(val teamIndex: Int) : InitScreenEvent() + data class RemoveStudent(val teamIndex: Int, val studentIndex: Int) : InitScreenEvent() + data class UpdateStudent( + val teamIndex: Int, + val studentIndex: Int, + val student: Student, + ) : InitScreenEvent() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt index fa27109..4446cf6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/GameStateField.kt @@ -1,8 +1,12 @@ package hu.mokegyesulet.it.dunestrat.model +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder -@Serializable +@Serializable(with = GameStateFieldSerializer::class) class GameStateField( val id: String, val water: Int, @@ -27,6 +31,48 @@ class GameStateField( neighbours.toMutableSet(), ) } + +@Serializable +private data class GameStateFieldSurrogate( + val id: String, + val water: Int, + val spice: Int, + val effectiveWeapon: Weapon, + var harvester: Boolean, + val neighbours: List, +) + +object GameStateFieldSerializer : KSerializer { + override val descriptor: SerialDescriptor = GameStateFieldSurrogate.serializer().descriptor + + override fun serialize( + encoder: Encoder, + value: GameStateField, + ) { + val surrogate = GameStateFieldSurrogate( + id = value.id, + water = value.water, + spice = value.spice, + effectiveWeapon = value.effectiveWeapon, + harvester = value.harvester, + neighbours = value.neighbours.map { it.id }, + ) + encoder.encodeSerializableValue(GameStateFieldSurrogate.serializer(), surrogate) + } + + override fun deserialize(decoder: Decoder): GameStateField { + val surrogate = decoder.decodeSerializableValue(GameStateFieldSurrogate.serializer()) + return GameStateField( + id = surrogate.id, + water = surrogate.water, + spice = surrogate.spice, + effectiveWeapon = surrogate.effectiveWeapon, + harvester = surrogate.harvester, + neighbours = mutableSetOf(), + ) + } +} + fun Set.toGameStateFields(): Set { val gameStateFields = this.map { GameStateField( diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt index 6df28b7..03aef2b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Student.kt @@ -6,4 +6,9 @@ import kotlinx.serialization.Serializable class Student( val name: String = "Ezekiel", val batkabankId: String = "", -) +) { + fun copy( + name: String = this.name, + batkabankId: String = this.batkabankId, + ) = Student(name, batkabankId) +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 198bd95..3289b3e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -24,6 +24,12 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { } composable { TestDataScreen() } - composable { InitScreen() } + composable { + InitScreen(onNavigateBack = { + navController.navigate(Screen.MainMenu) { + popUpTo(Screen.MainMenu) { inclusive = true } + } + }) + } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index 11d053b..2ceb95a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -1,12 +1,18 @@ package hu.mokegyesulet.it.dunestrat.ui import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.model.Student +import hu.mokegyesulet.it.dunestrat.model.Team import hu.mokegyesulet.it.dunestrat.model.Weapon import org.jetbrains.compose.ui.tooling.preview.Preview @@ -156,3 +162,73 @@ private fun NumericTextField( isError = value < 0, ) } + +@Composable +fun TeamConfigurationCard( + teamIndex: Int, + team: Team, + onAddStudent: () -> Unit, + onRemoveStudent: (Int) -> Unit, + onUpdateStudent: (Int, Student) -> Unit, + modifier: Modifier = Modifier, +) { + Card( + modifier = modifier.padding(20.dp), + ) { + Column(modifier = Modifier.padding(16.dp)) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = "${teamIndex + 1}. csapat tagjai", + style = MaterialTheme.typography.titleMedium, + ) + IconButton(onClick = onAddStudent) { + Icon(Icons.Default.Add, contentDescription = "Diák hozzáadása") + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + team.students.forEachIndexed { studentIndex, student -> + Column(modifier = Modifier.padding(vertical = 8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Column(modifier = Modifier.weight(1f)) { + OutlinedTextField( + value = student.name, + onValueChange = { onUpdateStudent(studentIndex, student.copy(name = it)) }, + label = { Text("Név") }, + isError = student.name.isBlank(), + modifier = Modifier.fillMaxWidth(), + ) + if (student.name.isBlank()) { + Text( + text = "A név nem lehet üres!", + color = Color.Red, + style = MaterialTheme.typography.bodySmall, + ) + } + Spacer(modifier = Modifier.height(4.dp)) + OutlinedTextField( + value = student.batkabankId, + onValueChange = { onUpdateStudent(studentIndex, student.copy(batkabankId = it)) }, + label = { Text("BatkaBank ID (opcionális)") }, + modifier = Modifier.fillMaxWidth(), + ) + } + if (team.students.size > 1) { + IconButton(onClick = { onRemoveStudent(studentIndex) }) { + Icon(Icons.Default.Delete, contentDescription = "Diák eltávolítása") + } + } + } + if (studentIndex < team.students.size - 1) { + HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) + } + } + } + } + } +} diff --git a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt index 0687b0c..58982d2 100644 --- a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt +++ b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt @@ -2,9 +2,12 @@ package hu.mokegyesulet.it.dunestrat import platform.UIKit.UIDevice -class IOSPlatform : Platform { - override val name: String = - UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +class IOSPlatform: Platform { + override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion + override fun openUrl(url: String) { + // Handled natively + } } + actual fun getPlatform(): Platform = IOSPlatform() diff --git a/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt b/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt index 3eeaac6..e7d949d 100644 --- a/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt +++ b/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt @@ -2,6 +2,9 @@ package hu.mokegyesulet.it.dunestrat class JsPlatform : Platform { override val name: String = "Web with Kotlin/JS" + override fun openUrl(url: String) { + kotlinx.browser.window.open(url, "_blank") + } } actual fun getPlatform(): Platform = JsPlatform() diff --git a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt index 547fc52..c497e17 100644 --- a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt @@ -1,7 +1,38 @@ package hu.mokegyesulet.it.dunestrat +import java.awt.Desktop +import java.net.URI + class JVMPlatform : Platform { override val name: String = "Java ${System.getProperty("java.version")}" + override fun openUrl(url: String) { + val uri = URI(url) + try { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + Desktop.getDesktop().browse(uri) + } else { + fallbackBrowse(url) + } + } catch (e: Exception) { + fallbackBrowse(url) + } + } + + private fun fallbackBrowse(url: String) { + val os = System.getProperty("os.name").lowercase() + if (os.contains("linux")) { + val browsers = listOf("xdg-open", "sensible-browser", "google-chrome", "firefox") + for (browser in browsers) { + try { + Runtime.getRuntime().exec(arrayOf(browser, url)) + return + } catch (e: Exception) { + // try next + } + } + } + println("Please open this URL in your browser: $url") + } } actual fun getPlatform(): Platform = JVMPlatform() diff --git a/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt index 2ff504f..ec9d2db 100644 --- a/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt +++ b/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt @@ -2,6 +2,11 @@ package hu.mokegyesulet.it.dunestrat class WasmPlatform : Platform { override val name: String = "Web with Kotlin/Wasm" + override fun openUrl(url: String) { + openUrlNative(url) + } } +private fun openUrlNative(url: String): Unit = js("window.open(url, '_blank')") + actual fun getPlatform(): Platform = WasmPlatform() From 746cf332e2a63046138c516273f5d1a1ae3d7142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Thu, 14 May 2026 19:18:52 +0200 Subject: [PATCH 093/162] Fix ktlint format --- .../backend/SupabaseClientProvider.kt | 7 ++++-- .../it/dunestrat/feature/init/InitScreen.kt | 25 ++++++++++++++----- .../dunestrat/feature/init/InitViewModel.kt | 9 +++++-- .../it/dunestrat/ui/CreatePlayerCard.kt | 8 ++++-- .../hu/mokegyesulet/it/dunestrat/ui/Shapes.kt | 7 ------ .../mokegyesulet/it/dunestrat/Platform.ios.kt | 6 ++--- .../mokegyesulet/it/dunestrat/Platform.jvm.kt | 4 ++- 7 files changed, 43 insertions(+), 23 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt index 983f06f..3a2c17d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt @@ -2,12 +2,12 @@ package hu.mokegyesulet.it.dunestrat.backend import hu.mokegyesulet.it.dunestrat.getPlatform import io.github.jan.supabase.SupabaseClient +import io.github.jan.supabase.annotations.SupabaseExperimental import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.UrlLauncher import io.github.jan.supabase.createSupabaseClient import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.realtime.Realtime -import io.github.jan.supabase.annotations.SupabaseExperimental object SupabaseClientProvider { @OptIn(SupabaseExperimental::class) @@ -17,7 +17,10 @@ object SupabaseClientProvider { ) { install(Auth) { urlLauncher = object : UrlLauncher { - override suspend fun openUrl(supabase: SupabaseClient, url: String) { + override suspend fun openUrl( + supabase: SupabaseClient, + url: String, + ) { getPlatform().openUrl(url) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index 638133e..b669283 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -87,7 +87,9 @@ fun InitScreen(onNavigateBack: () -> Unit) { text = { Text( text = map.name.ifBlank { - "${map.fields.count { it.startingField }} játékos térkép" + "${map.fields.count { + it.startingField + }} játékos térkép" }, ) }, @@ -125,7 +127,9 @@ fun InitScreen(onNavigateBack: () -> Unit) { player = basePlayer, onChange = { viewModel.onEvent( - InitViewModel.InitScreenEvent.UpdateGlobalStartingConditions(it), + InitViewModel.InitScreenEvent.UpdateGlobalStartingConditions( + it, + ), ) }, ) @@ -158,7 +162,9 @@ fun InitScreen(onNavigateBack: () -> Unit) { item { Button( - onClick = { viewModel.onEvent(InitViewModel.InitScreenEvent.ProceedToTeams) }, + onClick = { + viewModel.onEvent(InitViewModel.InitScreenEvent.ProceedToTeams) + }, enabled = isFormValid, modifier = Modifier.padding(16.dp), ) { @@ -179,10 +185,15 @@ fun InitScreen(onNavigateBack: () -> Unit) { TeamConfigurationCard( teamIndex = teamIndex, team = teams[teamIndex], - onAddStudent = { viewModel.onEvent(InitViewModel.InitScreenEvent.AddStudent(teamIndex)) }, + onAddStudent = { + viewModel.onEvent(InitViewModel.InitScreenEvent.AddStudent(teamIndex)) + }, onRemoveStudent = { studentIndex -> viewModel.onEvent( - InitViewModel.InitScreenEvent.RemoveStudent(teamIndex, studentIndex), + InitViewModel.InitScreenEvent.RemoveStudent( + teamIndex, + studentIndex, + ), ) }, onUpdateStudent = { studentIndex, student -> @@ -201,7 +212,9 @@ fun InitScreen(onNavigateBack: () -> Unit) { item { Row(modifier = Modifier.padding(16.dp)) { OutlinedButton( - onClick = { viewModel.onEvent(InitViewModel.InitScreenEvent.BackToConditions) }, + onClick = { + viewModel.onEvent(InitViewModel.InitScreenEvent.BackToConditions) + }, modifier = Modifier.padding(end = 8.dp), ) { Text("Vissza") diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index e19d54c..0f74453 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -156,7 +156,10 @@ class InitViewModel() : ViewModel() { Weapon.entries.all { basePlayer.getWeaponCount(it) >= 0 } isFormValid.value = - desertSelected && allFieldsAssigned && uniqueFields && baseConditionsValid + desertSelected && + allFieldsAssigned && + uniqueFields && + baseConditionsValid } else { isFormValid.value = teams.value.all { team -> team.students.all { student -> student.name.isNotBlank() } @@ -205,7 +208,9 @@ class InitViewModel() : ViewModel() { player.copy( water = base.water, spice = base.spice, - weapons = Weapon.entries.associateWith { base.getWeaponCount(it) }.toMutableMap(), + weapons = Weapon.entries.associateWith { + base.getWeaponCount(it) + }.toMutableMap(), ownedFields = mutableSetOf(stateFields.first { it.id == fieldId }), ) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt index 2ceb95a..228c21c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/CreatePlayerCard.kt @@ -198,7 +198,9 @@ fun TeamConfigurationCard( Column(modifier = Modifier.weight(1f)) { OutlinedTextField( value = student.name, - onValueChange = { onUpdateStudent(studentIndex, student.copy(name = it)) }, + onValueChange = { + onUpdateStudent(studentIndex, student.copy(name = it)) + }, label = { Text("Név") }, isError = student.name.isBlank(), modifier = Modifier.fillMaxWidth(), @@ -213,7 +215,9 @@ fun TeamConfigurationCard( Spacer(modifier = Modifier.height(4.dp)) OutlinedTextField( value = student.batkabankId, - onValueChange = { onUpdateStudent(studentIndex, student.copy(batkabankId = it)) }, + onValueChange = { + onUpdateStudent(studentIndex, student.copy(batkabankId = it)) + }, label = { Text("BatkaBank ID (opcionális)") }, modifier = Modifier.fillMaxWidth(), ) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt index b0cf2b9..229956c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/Shapes.kt @@ -1,15 +1,8 @@ package hu.mokegyesulet.it.dunestrat.ui -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.shape.GenericShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Shapes -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp val hexagonalRectangle = GenericShape { size, _ -> diff --git a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt index 58982d2..1f814c3 100644 --- a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt +++ b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt @@ -2,12 +2,12 @@ package hu.mokegyesulet.it.dunestrat import platform.UIKit.UIDevice -class IOSPlatform: Platform { - override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +class IOSPlatform : Platform { + override val name: String = + UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion override fun openUrl(url: String) { // Handled natively } } - actual fun getPlatform(): Platform = IOSPlatform() diff --git a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt index c497e17..a5a61cc 100644 --- a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt @@ -8,7 +8,9 @@ class JVMPlatform : Platform { override fun openUrl(url: String) { val uri = URI(url) try { - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + if (Desktop.isDesktopSupported() && + Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) + ) { Desktop.getDesktop().browse(uri) } else { fallbackBrowse(url) From 5abff7043f39f1112703e0da744976f499c22e5a Mon Sep 17 00:00:00 2001 From: domahet Date: Sat, 30 May 2026 14:28:51 +0200 Subject: [PATCH 094/162] Enable game name assignment --- .../it/dunestrat/feature/init/InitScreen.kt | 25 ++++++++++++++++++- .../dunestrat/feature/init/InitViewModel.kt | 9 ++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt index b669283..8bc7175 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitScreen.kt @@ -19,7 +19,7 @@ import hu.mokegyesulet.it.dunestrat.ui.TeamConfigurationCard @OptIn(ExperimentalMaterial3Api::class) @Composable fun InitScreen(onNavigateBack: () -> Unit) { - val viewModel = viewModel { InitViewModel() } + val viewModel: InitViewModel = viewModel { InitViewModel() } val phase by viewModel.currentPhase val expanded by viewModel.dropdownExpanded // val selectedMap by viewModel.selectedMap @@ -31,6 +31,7 @@ fun InitScreen(onNavigateBack: () -> Unit) { val isFormValid by viewModel.isFormValid val selectedDesert by viewModel.selectedDesert val basePlayer by viewModel.basePlayerState + val gameName by viewModel.gameName LaunchedEffect(Unit) { viewModel.navigationEvent.collect { @@ -121,6 +122,28 @@ fun InitScreen(onNavigateBack: () -> Unit) { ) { if (phase == InitializationPhase.STARTING_CONDITIONS) { if (selectedDesert != null) { + item { + Column(modifier = Modifier.padding(16.dp)) { + Card( + modifier = Modifier.wrapContentSize() + .padding(20.dp), + ) { + Spacer(modifier = Modifier.height(16.dp)) + + TextField( + value = gameName, + onValueChange = { input -> + viewModel.onEvent( + InitViewModel.InitScreenEvent.ChangeGameName(input), + ) + }, + label = { Text("Játék neve") }, + isError = gameName == "", + ) + } + } + } + item { GlobalStartingConditionsCard( modifier = Modifier.fillMaxWidth(0.9f), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt index 0f74453..b212fa1 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/init/InitViewModel.kt @@ -39,6 +39,7 @@ class InitViewModel() : ViewModel() { val playerList: MutableState> val startingFieldIds: MutableState> val teams: MutableState> = mutableStateOf(emptyList()) + val gameName = mutableStateOf("") private val _navigationEvent = MutableSharedFlow() val navigationEvent: SharedFlow = _navigationEvent.asSharedFlow() @@ -68,6 +69,10 @@ class InitViewModel() : ViewModel() { dropdownExpanded.value = event.expanded } + is InitScreenEvent.ChangeGameName -> { + gameName.value = event.name + } + is InitScreenEvent.ChangeSelectedMap -> { selectedDesert.value = event.selectedDesert @@ -140,6 +145,7 @@ class InitViewModel() : ViewModel() { } is InitScreenEvent.InitPlayerOnMap -> {} + is InitScreenEvent.UpdatePlayerData -> {} } } @@ -194,7 +200,7 @@ class InitViewModel() : ViewModel() { println("Játékosok mentése...") val gameTemplate = Game( - name = "Játék - ${desert.name}", + name = gameName.value, progress = GameProgress.INITIALIZED, teams = finalTeams, desertId = desert.id, @@ -232,6 +238,7 @@ class InitViewModel() : ViewModel() { sealed class InitScreenEvent { data object InitPlayerOnMap : InitScreenEvent() data class ChangeMapDropdownExpanded(val expanded: Boolean) : InitScreenEvent() + data class ChangeGameName(val name: String) : InitScreenEvent() data class ChangeSelectedMap(val selectedDesert: Desert) : InitScreenEvent() data class UpdateGlobalStartingConditions(val playerData: Player) : InitScreenEvent() data class UpdatePlayerData(val playerData: Player, val index: Int) : InitScreenEvent() From edbbf69139a99567e0f1eb31d9004e525726a583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Mon, 9 Feb 2026 22:14:03 +0000 Subject: [PATCH 095/162] Tab navigation --- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 6 +- .../playerstep/PlayerStepInputScreen.kt | 60 +++++++++++++++++++ .../playerstep/PlayerStepInputViewModel.kt | 30 ++++++++++ .../it/dunestrat/navigation/NavGraph.kt | 20 +++++-- .../it/dunestrat/navigation/Screen.kt | 3 + 5 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 6382e79..7a31990 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -14,7 +14,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MainMenu( - onPlaceholder1: () -> Unit, + onPlayerStepInput: () -> Unit, onPlaceholder2: () -> Unit, onTestData: () -> Unit, ) { @@ -27,9 +27,9 @@ fun MainMenu( verticalArrangement = Arrangement.SpaceEvenly, ) { Button( - onClick = onPlaceholder1, + onClick = onPlayerStepInput, ) { - Text(text = "Placeholder 1") + Text(text = "Player step input") } Button( diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt new file mode 100644 index 0000000..c9901c0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -0,0 +1,60 @@ +package hu.mokegyesulet.it.dunestrat.feature.playerstep + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.PrimaryScrollableTabRow +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel + +@Composable +fun PlayerStepInputScreen() { + val viewModel = viewModel { PlayerStepInputViewModel() } + + val tabIndex by viewModel.tabIndex + + val game by viewModel.game + val playerIds = game.teams.map { it.playerId } + + Scaffold( + modifier = Modifier.fillMaxSize(), + bottomBar = { + PrimaryScrollableTabRow( + selectedTabIndex = tabIndex, + modifier = Modifier.fillMaxWidth(), + ) { + playerIds.forEachIndexed { index, playerId -> + Tab( + selected = index == tabIndex, + onClick = { + viewModel.onEvent(PlayerStepInputViewModel.Event.TabSelected(index)) + }, + text = { + Text(text = playerId) + }, + ) + } + } + }, + ) { paddingValues -> + Box( + modifier = Modifier.padding(paddingValues).fillMaxSize(), + ) { + playerIds.forEachIndexed { index, playerId -> + if (index == tabIndex) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxSize(), + ) { + Text(text = "$playerId selected") + } + } + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt new file mode 100644 index 0000000..dc378ac --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -0,0 +1,30 @@ +package hu.mokegyesulet.it.dunestrat.feature.playerstep + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameProgress +import hu.mokegyesulet.it.dunestrat.model.Team + +class PlayerStepInputViewModel : ViewModel() { + + val tabIndex = mutableStateOf(0) + + val game = mutableStateOf( + Game( + name = "Teszt", + progress = GameProgress.ONGOING, + teams = (1..12).map { Team(playerId = it.toString(), students = listOf()) }, + ), + ) + + fun onEvent(event: Event) { + when (event) { + is Event.TabSelected -> tabIndex.value = event.index + } + } + + sealed class Event { + data class TabSelected(val index: Int) : Event() + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 3289b3e..e23d900 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -7,6 +7,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu +import hu.mokegyesulet.it.dunestrat.feature.playerstep.PlayerStepInputScreen import hu.mokegyesulet.it.dunestrat.feature.testdata.TestDataScreen @Composable @@ -17,19 +18,26 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlaceholder1 = { navController.navigate(Screen.InitScreen) }, + onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) } composable { TestDataScreen() } + composable { - InitScreen(onNavigateBack = { - navController.navigate(Screen.MainMenu) { - popUpTo(Screen.MainMenu) { inclusive = true } - } - }) + InitScreen( + onNavigateBack = { + navController.navigate(Screen.MainMenu) { + popUpTo(Screen.MainMenu) { inclusive = true } + } + }, + ) + } + + composable { + PlayerStepInputScreen() } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index 9804171..c27268d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -12,4 +12,7 @@ sealed class Screen { @Serializable data object InitScreen : Screen() + + @Serializable + data object PlayerStepInput : Screen() } From e882e16400a25b0fbfaaabfd329606561e3506b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 22 Feb 2026 20:08:53 +0100 Subject: [PATCH 096/162] WIP: player step input screen --- .../playerstep/PlayerStepInputScreen.kt | 183 +++++++++++++- .../playerstep/PlayerStepInputViewModel.kt | 234 +++++++++++++++++- .../it/dunestrat/navigation/NavGraph.kt | 2 +- .../it/dunestrat/ui/TabKeyNavigable.kt | 26 ++ 4 files changed, 428 insertions(+), 17 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/TabKeyNavigable.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index c9901c0..03200a9 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -1,25 +1,66 @@ package hu.mokegyesulet.it.dunestrat.feature.playerstep +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Error +import androidx.compose.material.icons.filled.QuestionMark +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.isShiftPressed +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.model.Weapon +import hu.mokegyesulet.it.dunestrat.ui.tabKeyNavigable @Composable -fun PlayerStepInputScreen() { - val viewModel = viewModel { PlayerStepInputViewModel() } - +fun PlayerStepInputScreen(gameId: Int) { + val viewModel = viewModel { PlayerStepInputViewModel(gameId) } + println(viewModel.gameId) val tabIndex by viewModel.tabIndex + val members by viewModel.members - val game by viewModel.game + val game by viewModel.game.collectAsStateWithLifecycle() val playerIds = game.teams.map { it.playerId } + val leaveFields: List> + by viewModel.leaveFields + + val enterFields: List> + by viewModel.enterFields + + val purchaseWeapons: Map by viewModel.purchaseWeapons + + val uiState by viewModel.uiState + val purchaseHarvester by uiState.purchaseHarvester + + val gameState by viewModel.gameState.collectAsStateWithLifecycle() + + val focusManager = LocalFocusManager.current + Scaffold( modifier = Modifier.fillMaxSize(), bottomBar = { @@ -44,17 +85,141 @@ fun PlayerStepInputScreen() { Box( modifier = Modifier.padding(paddingValues).fillMaxSize(), ) { - playerIds.forEachIndexed { index, playerId -> - if (index == tabIndex) { + Column { + Text("Field count: ${gameState.fields.size}") + Row { Column( - verticalArrangement = Arrangement.Center, + modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxSize(), ) { - Text(text = "$playerId selected") + Text("Lelépés") + LazyColumn { + itemsIndexed(leaveFields) { + index: Int, + fieldData: Pair, + -> + FieldRow( + value = fieldData.first, + onValueChange = { it: String -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.LeaveField( + index, + it, + ), + ) + }, + validation = fieldData.second, + ) + } + } + } + Column(modifier = Modifier.weight(1f)) { + Text("Vásárlás") + Weapon.entries.forEach { weapon -> + Row { + Text( + text = when (weapon) { + Weapon.PISTOL -> "MPT: " + Weapon.LASGUN -> "LSG: " + Weapon.CRYSKNIFE -> "CRK: " + Weapon.LEGION -> "Légió: " + }, + ) + TextField( + value = if ((purchaseWeapons[weapon] ?: 0) != 0) { + (purchaseWeapons[weapon] ?: 0).toString() + } else { + "" + }, + placeholder = { + Text(text = "0") + }, + onValueChange = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.PurchaseWeapon( + weapon = weapon, + value = it, + ), + ) + }, + modifier = Modifier.tabKeyNavigable(focusManager), + ) + } + } + Row { + FieldRow( + value = purchaseHarvester.first, + onValueChange = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.PurchaseHarvester(it), + ) + }, + validation = purchaseHarvester.second, + ) + } + } + + Column(modifier = Modifier.weight(1f)) { + Text("Rálépés") + LazyColumn { + itemsIndexed(enterFields) { + index: Int, + fieldData: Pair, + -> + FieldRow( + value = fieldData.first, + onValueChange = { it: String -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.LeaveField( + index, + it, + ), + ) + }, + validation = fieldData.second, + ) + } + } } } + Spacer(Modifier.weight(1f)) + Text("Csapattagok: $members") } } } } + +@Composable +fun FieldRow( + value: String, + onValueChange: (String) -> Unit, + validation: PlayerStepInputViewModel.Validation?, + modifier: Modifier = Modifier, +) { + val focusManager = LocalFocusManager.current + + Row( + modifier = modifier.fillMaxWidth().padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = Modifier.weight(1f) + .tabKeyNavigable(focusManager), + ) + Icon( + imageVector = when (validation?.isError) { + null -> Icons.Filled.Check + true -> Icons.Default.QuestionMark + false -> Icons.Default.Error + }, + contentDescription = validation?.message, + tint = when (validation?.isError) { + null -> Color.Green + true -> Color.Red + false -> Color(1.0f, 0.5f, 0.0f) + }, + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index dc378ac..4064d13 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -1,30 +1,250 @@ package hu.mokegyesulet.it.dunestrat.feature.playerstep +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toUpperCase import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository import hu.mokegyesulet.it.dunestrat.model.Game -import hu.mokegyesulet.it.dunestrat.model.GameProgress +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.PlayerStep import hu.mokegyesulet.it.dunestrat.model.Team +import hu.mokegyesulet.it.dunestrat.model.Weapon +import kotlin.collections.emptyList +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.stateIn -class PlayerStepInputViewModel : ViewModel() { +class PlayerStepInputViewModel( + val gameId: Int, +) : ViewModel() { val tabIndex = mutableStateOf(0) - - val game = mutableStateOf( + val game = SupabaseRepository.getGames().mapNotNull { list -> + list.find { it.id == 0 } + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5000), Game( - name = "Teszt", - progress = GameProgress.ONGOING, - teams = (1..12).map { Team(playerId = it.toString(), students = listOf()) }, + name = "", + teams = listOf(Team("", emptyList())), + desertId = -1, + ), + ) + val gameState = SupabaseRepository.getGameStates().mapNotNull { list -> + list.find { it.id == 0 } + }.stateIn( + viewModelScope, + SharingStarted.Eagerly, + GameState( + id = -1, + gameId = -1, + players = emptySet(), + fields = emptySet(), ), ) +// val game = mutableStateOf( +// Game( +// name = "Teszt", +// progress = GameProgress.ONGOING, +// teams = (1..12).map { +// Team( +// playerId = it.toString(), +// students = listOf( +// Student( +// it.toString() + "a", +// ), +// Student( +// it.toString() + "b", +// ), +// Student( +// it.toString() + "c", +// ), +// ), +// ) +// }, +// ), +// ) + + // Expose members as a stored derived State (not a getter) so Compose tracks it properly + val members = derivedStateOf { + game.value.teams.getOrNull(tabIndex.value)?.students?.joinToString { it.name } ?: "" + } + + val uiStates = mutableStateListOf( + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + EnterStepsUIState(), + ) + + // Expose uiState as a stored derived State so consumers observe changes to uiStates and tabIndex + val uiState = derivedStateOf { + uiStates.getOrNull(tabIndex.value) ?: EnterStepsUIState() + } + + // Compose consumers often need to observe list mutations directly. Return the SnapshotStateList + // for the current tab so composables can read it and be subscribed to its changes. + val leaveFields = derivedStateOf { + uiStates.getOrNull(tabIndex.value)?.leaveFields ?: mutableStateListOf(Pair("", null)) + } + + val enterFields = derivedStateOf { + uiStates.getOrNull(tabIndex.value)?.enterFields ?: mutableStateListOf(Pair("", null)) + } + + val purchaseWeapons = derivedStateOf { + uiStates.getOrNull(tabIndex.value)?.purchaseWeapons ?: mapOf() + } fun onEvent(event: Event) { when (event) { is Event.TabSelected -> tabIndex.value = event.index + + is Event.Save -> { + val playerSteps = uiStates.mapIndexed { index, state -> + PlayerStep() + } + } + + is Event.LeaveField -> { + val lf = uiStates[tabIndex.value].leaveFields + val gs = gameState.value + val value = event.value.toUpperCase(locale = Locale.current).filter { + it.isUpperCase() || it.isDigit() + } + val validation = when { + value == "" -> null + + !gs.fields.map { it.id }.contains(event.value) -> Validation.NO_SUCH_FIELD + + !gs.players.find { + it.id.toInt() == tabIndex.value + 1 + }!!.ownedFields.map { it.id }.contains(event.value) -> Validation.UNOWNED_FIELD + + else -> null + } + lf[event.index] = + value to validation + if (!lf.contains("" to null)) { + lf.add("" to null) + } + + println("size: ${lf.size}") + for (i in lf) { + println(i) + } + } + + is Event.EnterField -> { + val ef = uiStates[tabIndex.value].enterFields + val gs = gameState.value + val value = event.value.toUpperCase(locale = Locale.current).filter { + it.isUpperCase() || it.isDigit() + } + val validation = when { + value == "" -> null + + !gs.fields.map { it.id }.contains(event.value) -> Validation.NO_SUCH_FIELD + + gs.fields.find { + it.id == event.value + }!!.neighbours.map { it.id }.any { x: String -> + gs.players.find { it.id.toInt() == tabIndex.value + 1 }!! + .ownedFields.map { it.id }.contains(x) + } -> Validation.UNREACHABLE_FIELD + + else -> null + } + ef[event.index] = + value to validation + if (!ef.contains("" to null)) { + ef.add("" to null) + } + + println("size: ${ef.size}") + for (i in ef) { + println(i) + } + } + + is Event.PurchaseWeapon -> { + if (event.value == "") { + uiStates[tabIndex.value].purchaseWeapons[event.weapon] = 0 + } + try { + val amount = event.value.toInt() + if (amount >= 0) { + uiStates[tabIndex.value].purchaseWeapons[event.weapon] = amount + } + } catch (_: NumberFormatException) {} + } + + is Event.PurchaseHarvester -> { + val gameState = gameState.value + val value = event.value.toUpperCase(locale = Locale.current).filter { + it.isUpperCase() || it.isDigit() + } + val validation = when { + value == "" -> null + + !gameState.fields.map { + it.id + }.contains(event.value) -> Validation.NO_SUCH_FIELD + + !gameState.players.find { + it.id.toInt() == tabIndex.value + 1 + }!!.ownedFields.map { it.id }.contains(event.value) -> Validation.UNOWNED_FIELD + + else -> null + } + + uiStates[tabIndex.value].purchaseHarvester.value = value to validation + } } } + data class EnterStepsUIState( + val leaveFields: SnapshotStateList> = + mutableStateListOf(Pair("", null)), + val enterFields: SnapshotStateList> = + mutableStateListOf(Pair("", null)), + val purchaseWeapons: MutableMap = mutableStateMapOf( + Weapon.CRYSKNIFE to 0, + Weapon.PISTOL to 0, + Weapon.LASGUN to 0, + Weapon.LEGION to 0, + ), + val purchaseHarvester: MutableState> = + mutableStateOf("" to null), + ) + + enum class Validation(val message: String, val isError: Boolean) { + NO_SUCH_FIELD("Ilyen mező nincs a pályán!", false), + UNREACHABLE_FIELD("A játékos nem birtokol szomszédos mezőt!", true), + UNOWNED_FIELD("A játékos nem birtokolja a mezőt!", true), + } + sealed class Event { data class TabSelected(val index: Int) : Event() + data object Save : Event() + data class LeaveField(val index: Int, val value: String) : Event() + data class EnterField(val index: Int, val value: String) : Event() + data class PurchaseWeapon(val weapon: Weapon, val value: String) : Event() + data class PurchaseHarvester(val value: String) : Event() } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index e23d900..b644b37 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -37,7 +37,7 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { } composable { - PlayerStepInputScreen() + PlayerStepInputScreen(0) } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/TabKeyNavigable.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/TabKeyNavigable.kt new file mode 100644 index 0000000..cfef7d4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/TabKeyNavigable.kt @@ -0,0 +1,26 @@ +package hu.mokegyesulet.it.dunestrat.ui + +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.isShiftPressed +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type + +fun Modifier.tabKeyNavigable(focusManager: FocusManager): Modifier = this.onPreviewKeyEvent { + if (it.type == KeyEventType.KeyDown && it.key == Key.Tab) { + focusManager.moveFocus( + if (it.isShiftPressed) { + FocusDirection.Previous + } else { + FocusDirection.Next + }, + ) + true + } else { + false + } +} From 2f2dde1b2653a967c60a8b609fc04f6e352ca21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Tue, 10 Mar 2026 23:04:50 +0100 Subject: [PATCH 097/162] WIP: player step input screen --- .../it/dunestrat/backend/Repository.kt | 4 +- .../dunestrat/backend/SupabaseRepository.kt | 6 +- .../playerstep/PlayerStepInputScreen.kt | 147 +++++----- .../playerstep/PlayerStepInputViewModel.kt | 253 +++++++++++------- 4 files changed, 247 insertions(+), 163 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index d217fe3..668604c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -12,11 +12,11 @@ interface Repository { suspend fun saveGameState(gameState: GameState): GameState fun getGameStates(): Flow> - fun getLatestGameStateByGameId(gameId: String): Flow + fun getLatestGameStateByGameId(gameId: Int): Flow suspend fun savePlayerStep(playerStep: PlayerStep) fun getPlayerSteps(): Flow> - fun getPlayerStepsByGameStateId(gameStateId: String): Flow> + fun getPlayerStepsByGameStateId(gameStateId: Int): Flow> suspend fun saveDesert(desert: Desert) fun getDeserts(): Flow> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 9124769..9e51f74 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -38,7 +38,7 @@ object SupabaseRepository : Repository { } @OptIn(SupabaseExperimental::class) - override fun getLatestGameStateByGameId(gameId: String): Flow = + override fun getLatestGameStateByGameId(gameId: Int): Flow = supabase.from(GAME_STATE_TABLE).selectAsFlow( GameStateDatabaseEntity::id, filter = FilterOperation("game_id", FilterOperator.EQ, gameId), @@ -59,7 +59,7 @@ object SupabaseRepository : Repository { } @OptIn(SupabaseExperimental::class) - override fun getPlayerStepsByGameStateId(gameStateId: String): Flow> = + override fun getPlayerStepsByGameStateId(gameStateId: Int): Flow> = supabase.from(STEP_TABLE).selectAsFlow( PlayerStepDatabaseEntity::id, filter = FilterOperation("game_state_id", FilterOperator.EQ, gameStateId), @@ -68,7 +68,7 @@ object SupabaseRepository : Repository { } override suspend fun savePlayerStep(playerStep: PlayerStep) { - supabase.from(STEP_TABLE).insert(playerStep.toDatabaseEntry()) + supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntry()) } override suspend fun saveDesert(desert: Desert) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index 03200a9..079ff41 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -1,34 +1,28 @@ package hu.mokegyesulet.it.dunestrat.feature.playerstep -import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.QuestionMark +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusState +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.key.Key -import androidx.compose.ui.input.key.KeyEventType -import androidx.compose.ui.input.key.isShiftPressed -import androidx.compose.ui.input.key.key -import androidx.compose.ui.input.key.onPreviewKeyEvent -import androidx.compose.ui.input.key.type import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -36,10 +30,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.model.Weapon import hu.mokegyesulet.it.dunestrat.ui.tabKeyNavigable +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PlayerStepInputScreen(gameId: Int) { val viewModel = viewModel { PlayerStepInputViewModel(gameId) } - println(viewModel.gameId) val tabIndex by viewModel.tabIndex val members by viewModel.members @@ -59,10 +53,33 @@ fun PlayerStepInputScreen(gameId: Int) { val gameState by viewModel.gameState.collectAsStateWithLifecycle() - val focusManager = LocalFocusManager.current + val saveOnLostFocus: (FocusState) -> Unit = { + if (!it.isFocused) { + println("Focus lost") + viewModel.onEvent( + PlayerStepInputViewModel.Event.SaveToDatabase, + ) + } + } Scaffold( modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = {}, + actions = { + Button( + onClick = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.RunTurn, + ) + }, + ) { + Text(text = "Kör futtatása") + } + }, + ) + }, bottomBar = { PrimaryScrollableTabRow( selectedTabIndex = tabIndex, @@ -88,31 +105,17 @@ fun PlayerStepInputScreen(gameId: Int) { Column { Text("Field count: ${gameState.fields.size}") Row { - Column( + FieldInputColumn( + title = "Lelépés", + fields = leaveFields, + onValueChange = { index, value -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.LeaveField(index, value), + ) + }, + onFocusChanged = saveOnLostFocus, modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text("Lelépés") - LazyColumn { - itemsIndexed(leaveFields) { - index: Int, - fieldData: Pair, - -> - FieldRow( - value = fieldData.first, - onValueChange = { it: String -> - viewModel.onEvent( - PlayerStepInputViewModel.Event.LeaveField( - index, - it, - ), - ) - }, - validation = fieldData.second, - ) - } - } - } + ) Column(modifier = Modifier.weight(1f)) { Text("Vásárlás") Weapon.entries.forEach { weapon -> @@ -142,11 +145,12 @@ fun PlayerStepInputScreen(gameId: Int) { ), ) }, - modifier = Modifier.tabKeyNavigable(focusManager), + modifier = Modifier.onFocusChanged(saveOnLostFocus), ) } } Row { + Text("Harvester: ") FieldRow( value = purchaseHarvester.first, onValueChange = { @@ -155,33 +159,23 @@ fun PlayerStepInputScreen(gameId: Int) { ) }, validation = purchaseHarvester.second, + modifier = Modifier.onFocusChanged(saveOnLostFocus), ) } } - - Column(modifier = Modifier.weight(1f)) { - Text("Rálépés") - LazyColumn { - itemsIndexed(enterFields) { - index: Int, - fieldData: Pair, - -> - FieldRow( - value = fieldData.first, - onValueChange = { it: String -> - viewModel.onEvent( - PlayerStepInputViewModel.Event.LeaveField( - index, - it, - ), - ) - }, - validation = fieldData.second, - ) - } - } - } + FieldInputColumn( + title = "Rálépés", + fields = enterFields, + onValueChange = { index, value -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.EnterField(index, value), + ) + }, + onFocusChanged = saveOnLostFocus, + modifier = Modifier.weight(1f), + ) } + Spacer(Modifier.weight(1f)) Text("Csapattagok: $members") } @@ -223,3 +217,34 @@ fun FieldRow( ) } } + +@Composable +private fun FieldInputColumn( + title: String, + fields: List>, + onValueChange: (Int, String) -> Unit, + onFocusChanged: (FocusState) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(text = title) + LazyColumn { + itemsIndexed(fields) { + index: Int, + fieldData: Pair, + -> + FieldRow( + value = fieldData.first, + onValueChange = { + onValueChange(index, it) + }, + validation = fieldData.second, + modifier = Modifier.onFocusChanged(onFocusChanged), + ) + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 4064d13..79d4033 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.toUpperCase import androidx.lifecycle.ViewModel @@ -17,9 +18,12 @@ import hu.mokegyesulet.it.dunestrat.model.PlayerStep import hu.mokegyesulet.it.dunestrat.model.Team import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.collections.emptyList +import kotlin.collections.map import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch class PlayerStepInputViewModel( val gameId: Int, @@ -27,7 +31,7 @@ class PlayerStepInputViewModel( val tabIndex = mutableStateOf(0) val game = SupabaseRepository.getGames().mapNotNull { list -> - list.find { it.id == 0 } + list.find { it.id == gameId } }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5000), @@ -37,9 +41,9 @@ class PlayerStepInputViewModel( desertId = -1, ), ) - val gameState = SupabaseRepository.getGameStates().mapNotNull { list -> - list.find { it.id == 0 } - }.stateIn( + val gameState = SupabaseRepository.getLatestGameStateByGameId( + gameId, + ).stateIn( viewModelScope, SharingStarted.Eagerly, GameState( @@ -49,176 +53,211 @@ class PlayerStepInputViewModel( fields = emptySet(), ), ) -// val game = mutableStateOf( -// Game( -// name = "Teszt", -// progress = GameProgress.ONGOING, -// teams = (1..12).map { -// Team( -// playerId = it.toString(), -// students = listOf( -// Student( -// it.toString() + "a", -// ), -// Student( -// it.toString() + "b", -// ), -// Student( -// it.toString() + "c", -// ), -// ), -// ) -// }, -// ), -// ) // Expose members as a stored derived State (not a getter) so Compose tracks it properly val members = derivedStateOf { game.value.teams.getOrNull(tabIndex.value)?.students?.joinToString { it.name } ?: "" } - val uiStates = mutableStateListOf( - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), - EnterStepsUIState(), + val uiStates = SupabaseRepository.getPlayerStepsByGameStateId( + gameState.value.id, + ).map { list -> + if (list.isEmpty()) { + gameState.value.players.map { + EnterStepsUIState( + playerId = it.id, + ) + } + } else { + list.map { step -> + EnterStepsUIState( + stepId = step.id, + playerId = step.playerId, + leaveFields = step.leaveFields.map { it.id to null }.toMutableStateList(), + enterFields = step.enterFields.map { it.id to null }.toMutableStateList(), + purchaseWeapons = step.purchaseWeapons.toMutableMap(), + purchaseHarvester = mutableStateOf( + if (step.buildHarvesters.isNotEmpty()) { + step.buildHarvesters.first().id to null + } else { + "" to null + }, + ), + ) + } + } + }.stateIn( + viewModelScope, + SharingStarted.Eagerly, + (1..12).map { + EnterStepsUIState( + playerId = it.toString(), + ) + }, ) // Expose uiState as a stored derived State so consumers observe changes to uiStates and tabIndex val uiState = derivedStateOf { - uiStates.getOrNull(tabIndex.value) ?: EnterStepsUIState() + uiStates.value.getOrNull(tabIndex.value) ?: EnterStepsUIState() } // Compose consumers often need to observe list mutations directly. Return the SnapshotStateList // for the current tab so composables can read it and be subscribed to its changes. val leaveFields = derivedStateOf { - uiStates.getOrNull(tabIndex.value)?.leaveFields ?: mutableStateListOf(Pair("", null)) + uiStates.value.getOrNull(tabIndex.value)?.leaveFields ?: mutableStateListOf(Pair("", null)) } val enterFields = derivedStateOf { - uiStates.getOrNull(tabIndex.value)?.enterFields ?: mutableStateListOf(Pair("", null)) + uiStates.value.getOrNull(tabIndex.value)?.enterFields ?: mutableStateListOf(Pair("", null)) } val purchaseWeapons = derivedStateOf { - uiStates.getOrNull(tabIndex.value)?.purchaseWeapons ?: mapOf() + uiStates.value.getOrNull(tabIndex.value)?.purchaseWeapons ?: mapOf() } fun onEvent(event: Event) { + val gameState = gameState.value when (event) { is Event.TabSelected -> tabIndex.value = event.index - is Event.Save -> { - val playerSteps = uiStates.mapIndexed { index, state -> - PlayerStep() + is Event.RunTurn -> { + viewModelScope.launch { + val playerSteps = getPlayerSteps() + gameState.runTurn(playerSteps) + SupabaseRepository.saveGameState(gameState) } } is Event.LeaveField -> { - val lf = uiStates[tabIndex.value].leaveFields - val gs = gameState.value - val value = event.value.toUpperCase(locale = Locale.current).filter { - it.isUpperCase() || it.isDigit() - } + val lf = uiStates.value[tabIndex.value].leaveFields + val fieldId = event.value.toFieldId() val validation = when { - value == "" -> null + fieldId == "" -> null - !gs.fields.map { it.id }.contains(event.value) -> Validation.NO_SUCH_FIELD + !gameState.fields.map { + it.id + }.contains(fieldId) -> Validation.NO_SUCH_FIELD - !gs.players.find { - it.id.toInt() == tabIndex.value + 1 - }!!.ownedFields.map { it.id }.contains(event.value) -> Validation.UNOWNED_FIELD + !isFieldOwnedByPlayer( + fieldId, + uiStates.value[tabIndex.value].playerId, + ) -> Validation.UNOWNED_FIELD else -> null } lf[event.index] = - value to validation + fieldId to validation if (!lf.contains("" to null)) { lf.add("" to null) } - - println("size: ${lf.size}") - for (i in lf) { - println(i) - } } is Event.EnterField -> { - val ef = uiStates[tabIndex.value].enterFields - val gs = gameState.value - val value = event.value.toUpperCase(locale = Locale.current).filter { - it.isUpperCase() || it.isDigit() - } + val ef = uiStates.value[tabIndex.value].enterFields + val fieldId = event.value.toFieldId() val validation = when { - value == "" -> null + fieldId == "" -> null - !gs.fields.map { it.id }.contains(event.value) -> Validation.NO_SUCH_FIELD + !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD - gs.fields.find { - it.id == event.value - }!!.neighbours.map { it.id }.any { x: String -> - gs.players.find { it.id.toInt() == tabIndex.value + 1 }!! - .ownedFields.map { it.id }.contains(x) - } -> Validation.UNREACHABLE_FIELD + !isFieldReachableByPlayer( + fieldId, + uiStates.value[tabIndex.value].playerId, + ) -> Validation.UNREACHABLE_FIELD else -> null } ef[event.index] = - value to validation + fieldId to validation if (!ef.contains("" to null)) { ef.add("" to null) } - - println("size: ${ef.size}") - for (i in ef) { - println(i) - } } is Event.PurchaseWeapon -> { if (event.value == "") { - uiStates[tabIndex.value].purchaseWeapons[event.weapon] = 0 + uiStates.value[tabIndex.value].purchaseWeapons[event.weapon] = 0 } try { val amount = event.value.toInt() if (amount >= 0) { - uiStates[tabIndex.value].purchaseWeapons[event.weapon] = amount + uiStates.value[tabIndex.value].purchaseWeapons[event.weapon] = amount } } catch (_: NumberFormatException) {} } is Event.PurchaseHarvester -> { - val gameState = gameState.value - val value = event.value.toUpperCase(locale = Locale.current).filter { - it.isUpperCase() || it.isDigit() - } + val fieldId = event.value.toFieldId() val validation = when { - value == "" -> null + fieldId == "" -> null - !gameState.fields.map { - it.id - }.contains(event.value) -> Validation.NO_SUCH_FIELD + !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD - !gameState.players.find { - it.id.toInt() == tabIndex.value + 1 - }!!.ownedFields.map { it.id }.contains(event.value) -> Validation.UNOWNED_FIELD + !isFieldOwnedByPlayer( + fieldId, + uiStates.value[tabIndex.value].playerId, + ) -> Validation.UNOWNED_FIELD else -> null } - uiStates[tabIndex.value].purchaseHarvester.value = value to validation + uiStates.value[tabIndex.value].purchaseHarvester.value = fieldId to validation + } + + is Event.SaveToDatabase -> { + viewModelScope.launch { + val playerStep = uiStates.value[tabIndex.value].toPlayerStep(gameState) + if (playerStep.gameStateId != -1) { + SupabaseRepository.savePlayerStep(playerStep) + } + } } } } + private fun getPlayerSteps(): MutableSet { + val gameState = gameState.value + return uiStates.value.map { + it.toPlayerStep(gameState) + }.toMutableSet() + } + + private fun isFieldIdInGame(fieldId: String): Boolean { + val gameState = gameState.value + return gameState.fields.map { it.id }.contains(fieldId) + } + + private fun isFieldOwnedByPlayer( + fieldId: String, + playerId: String, + ): Boolean { + val gameState = gameState.value + val player = gameState.players.find { it.id == playerId } + if (player == null) { + return false + } + return player.ownedFields.map { it.id }.contains(fieldId) + } + + private fun isFieldReachableByPlayer( + fieldId: String, + playerId: String, + ): Boolean { + val gameState = gameState.value + val player = gameState.players.find { it.id == playerId } + if (player == null) { + return false + } + return player.ownedFields.flatMap { it.neighbours }.map { it.id }.contains(fieldId) + } + + private fun String.toFieldId(): String = this.toUpperCase(locale = Locale.current).filter { + it.isUpperCase() || it.isDigit() + } + data class EnterStepsUIState( + val stepId: Int = -1, + val playerId: String = "", val leaveFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), val enterFields: SnapshotStateList> = @@ -231,7 +270,26 @@ class PlayerStepInputViewModel( ), val purchaseHarvester: MutableState> = mutableStateOf("" to null), - ) + ) { + fun toPlayerStep(gameState: GameState): PlayerStep = PlayerStep( + id = stepId, + gameStateId = gameState.id, + playerId = playerId, + leaveFields = gameState.fields + .filter { + it.id in leaveFields.map { fieldState -> fieldState.first } + } + .toSet(), + enterFields = gameState.fields + .filter { + it.id in enterFields.map { fieldState -> fieldState.first } + } + .toSet(), + purchaseWeapons = purchaseWeapons, + buildHarvesters = gameState.fields + .filter { it.id == purchaseHarvester.value.first }.toSet(), + ) + } enum class Validation(val message: String, val isError: Boolean) { NO_SUCH_FIELD("Ilyen mező nincs a pályán!", false), @@ -241,10 +299,11 @@ class PlayerStepInputViewModel( sealed class Event { data class TabSelected(val index: Int) : Event() - data object Save : Event() + data object RunTurn : Event() data class LeaveField(val index: Int, val value: String) : Event() data class EnterField(val index: Int, val value: String) : Event() data class PurchaseWeapon(val weapon: Weapon, val value: String) : Event() data class PurchaseHarvester(val value: String) : Event() + data object SaveToDatabase : Event() } } From b68becdb8565457c996d5effcec8a3104d869455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Tue, 10 Mar 2026 22:31:47 +0000 Subject: [PATCH 098/162] WIP --- .../playerstep/PlayerStepInputViewModel.kt | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 79d4033..bb6b56d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -1,24 +1,13 @@ package hu.mokegyesulet.it.dunestrat.feature.playerstep -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateMapOf -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.snapshots.SnapshotStateList -import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.toUpperCase import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository -import hu.mokegyesulet.it.dunestrat.model.Game -import hu.mokegyesulet.it.dunestrat.model.GameState -import hu.mokegyesulet.it.dunestrat.model.PlayerStep -import hu.mokegyesulet.it.dunestrat.model.Team -import hu.mokegyesulet.it.dunestrat.model.Weapon -import kotlin.collections.emptyList -import kotlin.collections.map +import hu.mokegyesulet.it.dunestrat.model.* import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull @@ -63,9 +52,14 @@ class PlayerStepInputViewModel( gameState.value.id, ).map { list -> if (list.isEmpty()) { - gameState.value.players.map { +// gameState.value.players.map { +// EnterStepsUIState( +// playerId = it.id, +// ) +// } + (1..12).map { EnterStepsUIState( - playerId = it.id, + playerId = it.toString(), ) } } else { From 0020aac72ff29be3f08aff24b912fe17231043d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 14 Mar 2026 11:05:17 +0000 Subject: [PATCH 099/162] Navigation --- .../mokegyesulet/it/dunestrat/navigation/NavGraph.kt | 10 +++++++--- .../hu/mokegyesulet/it/dunestrat/navigation/Screen.kt | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index b644b37..dd330cf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -5,6 +5,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.toRoute import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu import hu.mokegyesulet.it.dunestrat.feature.playerstep.PlayerStepInputScreen @@ -18,7 +19,7 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput) }, + onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(0)) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) @@ -36,8 +37,11 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) } - composable { - PlayerStepInputScreen(0) + composable { backStackEntry -> + + val navigationObject: Screen.PlayerStepInput = backStackEntry.toRoute() + + PlayerStepInputScreen(navigationObject.gameId) } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt index c27268d..3457360 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/Screen.kt @@ -14,5 +14,5 @@ sealed class Screen { data object InitScreen : Screen() @Serializable - data object PlayerStepInput : Screen() + data class PlayerStepInput(val gameId: Int) : Screen() } From 61092cc40779c9007583c05243ff6433b86d66a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 22 Mar 2026 19:15:23 +0000 Subject: [PATCH 100/162] Can save turn to DB --- .../playerstep/PlayerStepInputViewModel.kt | 72 +++++++++++-------- .../feature/testdata/TestDataViewModel.kt | 9 ++- .../it/dunestrat/navigation/NavGraph.kt | 2 +- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index bb6b56d..72cafaf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.feature.playerstep import androidx.compose.runtime.* import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.toUpperCase import androidx.lifecycle.ViewModel @@ -36,8 +37,8 @@ class PlayerStepInputViewModel( viewModelScope, SharingStarted.Eagerly, GameState( - id = -1, gameId = -1, + index = -1, players = emptySet(), fields = emptySet(), ), @@ -48,7 +49,15 @@ class PlayerStepInputViewModel( game.value.teams.getOrNull(tabIndex.value)?.students?.joinToString { it.name } ?: "" } - val uiStates = SupabaseRepository.getPlayerStepsByGameStateId( + val uiStates = mutableStateOf( + (1..12).map { + EnterStepsUIState( + playerId = it.toString(), + ) + }, + ) + + private val _uiStates = SupabaseRepository.getPlayerStepsByGameStateId( gameState.value.id, ).map { list -> if (list.isEmpty()) { @@ -64,15 +73,19 @@ class PlayerStepInputViewModel( } } else { list.map { step -> + + val purchaseWeapons = mutableStateMapOf() + purchaseWeapons.putAll(step.purchaseWeapons) + EnterStepsUIState( stepId = step.id, playerId = step.playerId, - leaveFields = step.leaveFields.map { it.id to null }.toMutableStateList(), - enterFields = step.enterFields.map { it.id to null }.toMutableStateList(), - purchaseWeapons = step.purchaseWeapons.toMutableMap(), + leaveFields = step.leaveFields.map { it to null }.toMutableStateList(), + enterFields = step.enterFields.map { it to null }.toMutableStateList(), + purchaseWeapons = purchaseWeapons, purchaseHarvester = mutableStateOf( if (step.buildHarvesters.isNotEmpty()) { - step.buildHarvesters.first().id to null + step.buildHarvesters.first() to null } else { "" to null }, @@ -80,15 +93,15 @@ class PlayerStepInputViewModel( ) } } - }.stateIn( - viewModelScope, - SharingStarted.Eagerly, - (1..12).map { - EnterStepsUIState( - playerId = it.toString(), - ) - }, - ) + } + + init { + viewModelScope.launch { + _uiStates.collect { + uiStates.value = it + } + } + } // Expose uiState as a stored derived State so consumers observe changes to uiStates and tabIndex val uiState = derivedStateOf { @@ -106,7 +119,7 @@ class PlayerStepInputViewModel( } val purchaseWeapons = derivedStateOf { - uiStates.value.getOrNull(tabIndex.value)?.purchaseWeapons ?: mapOf() + uiStates.value.getOrNull(tabIndex.value)?.purchaseWeapons ?: mutableStateMapOf() } fun onEvent(event: Event) { @@ -117,12 +130,14 @@ class PlayerStepInputViewModel( is Event.RunTurn -> { viewModelScope.launch { val playerSteps = getPlayerSteps() - gameState.runTurn(playerSteps) - SupabaseRepository.saveGameState(gameState) + val newGameState = gameState.runTurn(playerSteps) + SupabaseRepository.saveGameState(newGameState) } } is Event.LeaveField -> { + println(event.value) + val lf = uiStates.value[tabIndex.value].leaveFields val fieldId = event.value.toFieldId() val validation = when { @@ -256,7 +271,7 @@ class PlayerStepInputViewModel( mutableStateListOf(Pair("", null)), val enterFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), - val purchaseWeapons: MutableMap = mutableStateMapOf( + val purchaseWeapons: SnapshotStateMap = mutableStateMapOf( Weapon.CRYSKNIFE to 0, Weapon.PISTOL to 0, Weapon.LASGUN to 0, @@ -269,19 +284,18 @@ class PlayerStepInputViewModel( id = stepId, gameStateId = gameState.id, playerId = playerId, - leaveFields = gameState.fields - .filter { - it.id in leaveFields.map { fieldState -> fieldState.first } - } + leaveFields = leaveFields.map { it.first } .toSet(), - enterFields = gameState.fields - .filter { - it.id in enterFields.map { fieldState -> fieldState.first } - } + enterFields = enterFields.map { it.first } .toSet(), purchaseWeapons = purchaseWeapons, - buildHarvesters = gameState.fields - .filter { it.id == purchaseHarvester.value.first }.toSet(), + buildHarvesters = if (purchaseHarvester.value.first != + "" + ) { + setOf(purchaseHarvester.value.first) + } else { + setOf() + }, ) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt index 3969f38..82be7ae 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -85,11 +85,10 @@ class TestDataViewModel : ViewModel() { val stateFields = desert.fields.toGameStateFields() randomlyAssignPlayers(desert, stateFields) val firstState = GameState( - 0, - game.id, - 0, - stateFields, - players.toSet(), + gameId = game.id, + index = 0, + fields = stateFields, + players = players.toSet(), ) viewModelScope.launch { SupabaseRepository.saveGameState(firstState) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index dd330cf..6222d17 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -19,7 +19,7 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(0)) }, + onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(19)) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) From a23d7734f0a694adf60775ea1f91d1f33b6a1dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 19 Apr 2026 19:27:17 +0200 Subject: [PATCH 101/162] WIP --- .../it/dunestrat/Platform.android.kt | 5 ++ .../hu/mokegyesulet/it/dunestrat/Platform.kt | 6 +++ .../it/dunestrat/backend/Repository.kt | 1 + .../backend/SupabaseClientProvider.kt | 16 ++----- .../playerstep/PlayerStepInputViewModel.kt | 2 +- .../it/dunestrat/navigation/NavGraph.kt | 2 +- .../mokegyesulet/it/dunestrat/Platform.ios.kt | 5 ++ .../mokegyesulet/it/dunestrat/Platform.js.kt | 6 +++ .../mokegyesulet/it/dunestrat/Platform.jvm.kt | 47 +++++++++++++++++++ .../it/dunestrat/Platform.wasmJs.kt | 6 +++ 10 files changed, 82 insertions(+), 14 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt b/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt index 204a67c..8cf263a 100644 --- a/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt +++ b/composeApp/src/androidMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.android.kt @@ -1,6 +1,8 @@ package hu.mokegyesulet.it.dunestrat import android.os.Build +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" @@ -10,3 +12,6 @@ class AndroidPlatform : Platform { } actual fun getPlatform(): Platform = AndroidPlatform() + +@OptIn(SupabaseExperimental::class) +actual fun getUrlLauncher(): UrlLauncher? = null diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt index ede178c..810a269 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.kt @@ -1,8 +1,14 @@ package hu.mokegyesulet.it.dunestrat +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher + interface Platform { val name: String fun openUrl(url: String) } expect fun getPlatform(): Platform + +@OptIn(SupabaseExperimental::class) +expect fun getUrlLauncher(): UrlLauncher? diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index 668604c..00d9ac5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -4,6 +4,7 @@ import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import io.github.jan.supabase.annotations.SupabaseExperimental import kotlinx.coroutines.flow.Flow interface Repository { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt index 3a2c17d..c2d2032 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseClientProvider.kt @@ -1,10 +1,7 @@ package hu.mokegyesulet.it.dunestrat.backend -import hu.mokegyesulet.it.dunestrat.getPlatform -import io.github.jan.supabase.SupabaseClient +import hu.mokegyesulet.it.dunestrat.getUrlLauncher import io.github.jan.supabase.annotations.SupabaseExperimental -import io.github.jan.supabase.auth.Auth -import io.github.jan.supabase.auth.UrlLauncher import io.github.jan.supabase.createSupabaseClient import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.realtime.Realtime @@ -15,14 +12,9 @@ object SupabaseClientProvider { supabaseUrl = "https://lnwvuwepwaexwybselsf.supabase.co", supabaseKey = "sb_publishable_Y8MlS_tbyIRvvsvbHvb1_g_wbyg6EfI", ) { - install(Auth) { - urlLauncher = object : UrlLauncher { - override suspend fun openUrl( - supabase: SupabaseClient, - url: String, - ) { - getPlatform().openUrl(url) - } + install(io.github.jan.supabase.auth.Auth) { + getUrlLauncher()?.let { + urlLauncher = it } } install(Postgrest) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 72cafaf..15fdf4d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -265,7 +265,7 @@ class PlayerStepInputViewModel( } data class EnterStepsUIState( - val stepId: Int = -1, + val stepId: Int = -1, //TODO: find latest player step here val playerId: String = "", val leaveFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 6222d17..a2191c0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -19,7 +19,7 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(19)) }, + onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(22)) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) diff --git a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt index 1f814c3..de97ee5 100644 --- a/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt +++ b/composeApp/src/iosMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.ios.kt @@ -1,5 +1,7 @@ package hu.mokegyesulet.it.dunestrat +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher import platform.UIKit.UIDevice class IOSPlatform : Platform { @@ -11,3 +13,6 @@ class IOSPlatform : Platform { } actual fun getPlatform(): Platform = IOSPlatform() + +@OptIn(SupabaseExperimental::class) +actual fun getUrlLauncher(): UrlLauncher? = null diff --git a/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt b/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt index e7d949d..803ef49 100644 --- a/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt +++ b/composeApp/src/jsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.js.kt @@ -1,5 +1,8 @@ package hu.mokegyesulet.it.dunestrat +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher + class JsPlatform : Platform { override val name: String = "Web with Kotlin/JS" override fun openUrl(url: String) { @@ -8,3 +11,6 @@ class JsPlatform : Platform { } actual fun getPlatform(): Platform = JsPlatform() + +@OptIn(SupabaseExperimental::class) +actual fun getUrlLauncher(): UrlLauncher? = null diff --git a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt index a5a61cc..4778a42 100644 --- a/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.jvm.kt @@ -1,5 +1,7 @@ package hu.mokegyesulet.it.dunestrat +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher import java.awt.Desktop import java.net.URI @@ -38,3 +40,48 @@ class JVMPlatform : Platform { } actual fun getPlatform(): Platform = JVMPlatform() + +@OptIn(SupabaseExperimental::class) +actual fun getUrlLauncher(): UrlLauncher? = UrlLauncher { _, url -> + val osName = System.getProperty("os.name").lowercase() + val runtime = Runtime.getRuntime() + if (osName.contains("win")) { + runtime.exec(arrayOf("rundll32", "url.dll,FileProtocolHandler", url)) + } else if (osName.contains("mac")) { + runtime.exec(arrayOf("open", url)) + } else { + // Linux and other Unix-like + val browsers = + listOf( + "xdg-open", + "gio open", + "gnome-open", + "kde-open", + "sensible-browser", + "firefox", + "google-chrome", + ) + var opened = false + for (browser in browsers) { + try { + val browserCmd = browser.split(" ")[0] + // Using 'which' to check if command exists + if (runtime.exec(arrayOf("which", browserCmd)).waitFor() == 0) { + runtime.exec(arrayOf(browserCmd, url)) + opened = true + break + } + } catch (e: Exception) { + // Ignore and try next + } + } + if (!opened) { + // Last resort: try just running the first one anyway + try { + runtime.exec(arrayOf("xdg-open", url)) + } catch (e: Exception) { + // throw UnsupportedOperationException("Could not open browser for URL: $url. Error: ${e.message}") + } + } + } +} diff --git a/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt index ec9d2db..bf9ff1b 100644 --- a/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt +++ b/composeApp/src/wasmJsMain/kotlin/hu/mokegyesulet/it/dunestrat/Platform.wasmJs.kt @@ -1,5 +1,8 @@ package hu.mokegyesulet.it.dunestrat +import io.github.jan.supabase.annotations.SupabaseExperimental +import io.github.jan.supabase.auth.UrlLauncher + class WasmPlatform : Platform { override val name: String = "Web with Kotlin/Wasm" override fun openUrl(url: String) { @@ -10,3 +13,6 @@ class WasmPlatform : Platform { private fun openUrlNative(url: String): Unit = js("window.open(url, '_blank')") actual fun getPlatform(): Platform = WasmPlatform() + +@OptIn(SupabaseExperimental::class) +actual fun getUrlLauncher(): UrlLauncher? = null From 103a9bd63c0c6ed45ea4a217ee4d086c110fea50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 19 Apr 2026 20:49:06 +0100 Subject: [PATCH 102/162] Only using flow for the steps --- .../it/dunestrat/backend/Repository.kt | 4 +- .../dunestrat/backend/SupabaseRepository.kt | 24 +- .../playerstep/PlayerStepInputScreen.kt | 266 +++++++++--------- .../playerstep/PlayerStepInputViewModel.kt | 151 +++++----- .../it/dunestrat/navigation/NavGraph.kt | 8 +- 5 files changed, 227 insertions(+), 226 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index 00d9ac5..d0b1064 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -4,16 +4,16 @@ import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.Game import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep -import io.github.jan.supabase.annotations.SupabaseExperimental import kotlinx.coroutines.flow.Flow interface Repository { suspend fun saveGame(game: Game): Game fun getGames(): Flow> + suspend fun getGameById(gameId: Int): Game suspend fun saveGameState(gameState: GameState): GameState fun getGameStates(): Flow> - fun getLatestGameStateByGameId(gameId: Int): Flow + suspend fun getLatestGameStateByGameId(gameId: Int): GameState suspend fun savePlayerStep(playerStep: PlayerStep) fun getPlayerSteps(): Flow> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 9e51f74..5541ec0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -31,6 +31,11 @@ object SupabaseRepository : Repository { select() }.decodeSingle().toGame() + override suspend fun getGameById(gameId: Int): Game = supabase.from(GAME_TABLE) + .select { filter { GameDatabaseEntity::id eq gameId } } + .decodeList().firstOrNull() ?.toGame() + ?: throw IllegalStateException("No game found with id: $gameId") + @OptIn(SupabaseExperimental::class) override fun getGameStates(): Flow> = supabase.from(GAME_STATE_TABLE).selectAsFlow(GameStateDatabaseEntity::id).map { list -> @@ -38,20 +43,21 @@ object SupabaseRepository : Repository { } @OptIn(SupabaseExperimental::class) - override fun getLatestGameStateByGameId(gameId: Int): Flow = - supabase.from(GAME_STATE_TABLE).selectAsFlow( - GameStateDatabaseEntity::id, - filter = FilterOperation("game_id", FilterOperator.EQ, gameId), - ).map { list -> - list.map { it.toGameState() }.maxByOrNull { it.index } - ?: throw IllegalStateException("No game state found for game id: $gameId") - } - override suspend fun saveGameState(gameState: GameState): GameState = supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) { select() }.decodeSingle().toGameState() + override suspend fun getLatestGameStateByGameId(gameId: Int): GameState = + supabase.from(GAME_STATE_TABLE) + .select { + filter { + GameStateDatabaseEntity::gameId eq gameId + } + } + .decodeList().maxByOrNull { it.index }?.toGameState() + ?: throw IllegalStateException("No game state found for game id: $gameId") + @OptIn(SupabaseExperimental::class) override fun getPlayerSteps(): Flow> = supabase.from(STEP_TABLE).selectAsFlow(PlayerStepDatabaseEntity::id).map { list -> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index 079ff41..68995d0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -7,15 +7,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.QuestionMark -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.PrimaryScrollableTabRow -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Tab -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TopAppBar +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -25,159 +17,171 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.model.Weapon import hu.mokegyesulet.it.dunestrat.ui.tabKeyNavigable @OptIn(ExperimentalMaterial3Api::class) @Composable -fun PlayerStepInputScreen(gameId: Int) { +fun PlayerStepInputScreen( + gameId: Int, + onBack: () -> Unit, +) { val viewModel = viewModel { PlayerStepInputViewModel(gameId) } val tabIndex by viewModel.tabIndex val members by viewModel.members - val game by viewModel.game.collectAsStateWithLifecycle() - val playerIds = game.teams.map { it.playerId } + val game by viewModel.game + val gameState by viewModel.gameState - val leaveFields: List> - by viewModel.leaveFields + if (game == null || gameState == null) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text(text = "Loading...") + } + } else { + val playerIds = game!!.teams.map { it.playerId } - val enterFields: List> - by viewModel.enterFields + val leaveFields: List> + by viewModel.leaveFields - val purchaseWeapons: Map by viewModel.purchaseWeapons + val enterFields: List> + by viewModel.enterFields - val uiState by viewModel.uiState - val purchaseHarvester by uiState.purchaseHarvester + val purchaseWeapons: Map by viewModel.purchaseWeapons - val gameState by viewModel.gameState.collectAsStateWithLifecycle() + val uiState by viewModel.uiState + val purchaseHarvester by uiState.purchaseHarvester - val saveOnLostFocus: (FocusState) -> Unit = { - if (!it.isFocused) { - println("Focus lost") - viewModel.onEvent( - PlayerStepInputViewModel.Event.SaveToDatabase, - ) + val saveOnLostFocus: (FocusState) -> Unit = { + if (!it.isFocused) { + viewModel.onEvent( + PlayerStepInputViewModel.Event.SaveToDatabase, + ) + } } - } - Scaffold( - modifier = Modifier.fillMaxSize(), - topBar = { - TopAppBar( - title = {}, - actions = { - Button( - onClick = { - viewModel.onEvent( - PlayerStepInputViewModel.Event.RunTurn, - ) - }, - ) { - Text(text = "Kör futtatása") + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = {}, + actions = { + Button( + onClick = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.RunTurn, + ) + onBack() + }, + ) { + Text(text = "Kör futtatása") + } + }, + ) + }, + bottomBar = { + PrimaryScrollableTabRow( + selectedTabIndex = tabIndex, + modifier = Modifier.fillMaxWidth(), + ) { + playerIds.forEachIndexed { index, playerId -> + Tab( + selected = index == tabIndex, + onClick = { + viewModel.onEvent(PlayerStepInputViewModel.Event.TabSelected(index)) + }, + text = { + Text(text = playerId) + }, + ) } - }, - ) - }, - bottomBar = { - PrimaryScrollableTabRow( - selectedTabIndex = tabIndex, - modifier = Modifier.fillMaxWidth(), - ) { - playerIds.forEachIndexed { index, playerId -> - Tab( - selected = index == tabIndex, - onClick = { - viewModel.onEvent(PlayerStepInputViewModel.Event.TabSelected(index)) - }, - text = { - Text(text = playerId) - }, - ) } - } - }, - ) { paddingValues -> - Box( - modifier = Modifier.padding(paddingValues).fillMaxSize(), - ) { - Column { - Text("Field count: ${gameState.fields.size}") - Row { - FieldInputColumn( - title = "Lelépés", - fields = leaveFields, - onValueChange = { index, value -> - viewModel.onEvent( - PlayerStepInputViewModel.Event.LeaveField(index, value), - ) - }, - onFocusChanged = saveOnLostFocus, - modifier = Modifier.weight(1f), - ) - Column(modifier = Modifier.weight(1f)) { - Text("Vásárlás") - Weapon.entries.forEach { weapon -> - Row { - Text( - text = when (weapon) { - Weapon.PISTOL -> "MPT: " - Weapon.LASGUN -> "LSG: " - Weapon.CRYSKNIFE -> "CRK: " - Weapon.LEGION -> "Légió: " - }, + }, + ) { paddingValues -> + Box( + modifier = Modifier.padding(paddingValues).fillMaxSize(), + ) { + Column { + Text("Field count: ${gameState!!.fields.size}") + Text("GameStateId: ${gameState!!.id}") + Row { + FieldInputColumn( + title = "Lelépés", + fields = leaveFields, + onValueChange = { index, value -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.LeaveField(index, value), ) - TextField( - value = if ((purchaseWeapons[weapon] ?: 0) != 0) { - (purchaseWeapons[weapon] ?: 0).toString() - } else { - "" - }, - placeholder = { - Text(text = "0") - }, + }, + onFocusChanged = saveOnLostFocus, + modifier = Modifier.weight(1f), + ) + Column(modifier = Modifier.weight(1f)) { + Text("Vásárlás") + Weapon.entries.forEach { weapon -> + Row { + Text( + text = when (weapon) { + Weapon.PISTOL -> "MPT: " + Weapon.LASGUN -> "LSG: " + Weapon.CRYSKNIFE -> "CRK: " + Weapon.LEGION -> "Légió: " + }, + ) + TextField( + value = if ((purchaseWeapons[weapon] ?: 0) != 0) { + (purchaseWeapons[weapon] ?: 0).toString() + } else { + "" + }, + placeholder = { + Text(text = "0") + }, + onValueChange = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.PurchaseWeapon( + weapon = weapon, + value = it, + ), + ) + }, + modifier = Modifier.onFocusChanged(saveOnLostFocus), + ) + } + } + Row { + Text("Harvester: ") + FieldRow( + value = purchaseHarvester.first, onValueChange = { viewModel.onEvent( - PlayerStepInputViewModel.Event.PurchaseWeapon( - weapon = weapon, - value = it, - ), + PlayerStepInputViewModel.Event.PurchaseHarvester(it), ) }, + validation = purchaseHarvester.second, modifier = Modifier.onFocusChanged(saveOnLostFocus), ) } } - Row { - Text("Harvester: ") - FieldRow( - value = purchaseHarvester.first, - onValueChange = { - viewModel.onEvent( - PlayerStepInputViewModel.Event.PurchaseHarvester(it), - ) - }, - validation = purchaseHarvester.second, - modifier = Modifier.onFocusChanged(saveOnLostFocus), - ) - } + FieldInputColumn( + title = "Rálépés", + fields = enterFields, + onValueChange = { index, value -> + viewModel.onEvent( + PlayerStepInputViewModel.Event.EnterField(index, value), + ) + }, + onFocusChanged = saveOnLostFocus, + modifier = Modifier.weight(1f), + ) } - FieldInputColumn( - title = "Rálépés", - fields = enterFields, - onValueChange = { index, value -> - viewModel.onEvent( - PlayerStepInputViewModel.Event.EnterField(index, value), - ) - }, - onFocusChanged = saveOnLostFocus, - modifier = Modifier.weight(1f), - ) - } - Spacer(Modifier.weight(1f)) - Text("Csapattagok: $members") + Spacer(Modifier.weight(1f)) + Text("Csapattagok: $members") + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 15fdf4d..08998e6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -8,11 +8,10 @@ import androidx.compose.ui.text.toUpperCase import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository -import hu.mokegyesulet.it.dunestrat.model.* -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.stateIn +import hu.mokegyesulet.it.dunestrat.model.Game +import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.PlayerStep +import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlinx.coroutines.launch class PlayerStepInputViewModel( @@ -20,86 +19,64 @@ class PlayerStepInputViewModel( ) : ViewModel() { val tabIndex = mutableStateOf(0) - val game = SupabaseRepository.getGames().mapNotNull { list -> - list.find { it.id == gameId } - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000), - Game( - name = "", - teams = listOf(Team("", emptyList())), - desertId = -1, - ), - ) - val gameState = SupabaseRepository.getLatestGameStateByGameId( - gameId, - ).stateIn( - viewModelScope, - SharingStarted.Eagerly, - GameState( - gameId = -1, - index = -1, - players = emptySet(), - fields = emptySet(), - ), - ) - // Expose members as a stored derived State (not a getter) so Compose tracks it properly - val members = derivedStateOf { - game.value.teams.getOrNull(tabIndex.value)?.students?.joinToString { it.name } ?: "" + val game = mutableStateOf(null) + val gameState = mutableStateOf(null) + val playerSteps = mutableStateOf(listOf()) + + val loaded = derivedStateOf { + game.value != null && gameState.value != null && playerSteps.value.isNotEmpty() } - val uiStates = mutableStateOf( - (1..12).map { - EnterStepsUIState( - playerId = it.toString(), + init { + viewModelScope.launch { + game.value = SupabaseRepository.getGameById(gameId) + gameState.value = SupabaseRepository.getLatestGameStateByGameId(gameId) + val playerStepFlow = SupabaseRepository.getPlayerStepsByGameStateId( + gameState.value!!.id, ) - }, - ) - - private val _uiStates = SupabaseRepository.getPlayerStepsByGameStateId( - gameState.value.id, - ).map { list -> - if (list.isEmpty()) { -// gameState.value.players.map { -// EnterStepsUIState( -// playerId = it.id, -// ) -// } - (1..12).map { - EnterStepsUIState( - playerId = it.toString(), - ) - } - } else { - list.map { step -> - - val purchaseWeapons = mutableStateMapOf() - purchaseWeapons.putAll(step.purchaseWeapons) - - EnterStepsUIState( - stepId = step.id, - playerId = step.playerId, - leaveFields = step.leaveFields.map { it to null }.toMutableStateList(), - enterFields = step.enterFields.map { it to null }.toMutableStateList(), - purchaseWeapons = purchaseWeapons, - purchaseHarvester = mutableStateOf( - if (step.buildHarvesters.isNotEmpty()) { - step.buildHarvesters.first() to null - } else { - "" to null - }, - ), - ) + playerStepFlow.collect { list -> + playerSteps.value = list.ifEmpty { + game.value!!.teams.map { + PlayerStep( + gameStateId = gameState.value!!.id, + playerId = it.playerId, + leaveFields = emptySet(), + enterFields = emptySet(), + purchaseWeapons = emptyMap(), + buildHarvesters = emptySet(), + ) + } + } } } } - init { - viewModelScope.launch { - _uiStates.collect { - uiStates.value = it - } + // Expose members as a stored derived State (not a getter) so Compose tracks it properly + val members = derivedStateOf { + game.value?.teams?.getOrNull(tabIndex.value)?.students?.joinToString { it.name } ?: "" + } + + val uiStates = derivedStateOf { + playerSteps.value.map { playerStep -> + + val purchaseWeapons = mutableStateMapOf() + purchaseWeapons.putAll(playerStep.purchaseWeapons) + + EnterStepsUIState( + stepId = playerStep.id, + playerId = playerStep.playerId, + leaveFields = (playerStep.leaveFields + "").map { + it to null + }.toMutableStateList(), + enterFields = (playerStep.enterFields + "").map { + it to null + }.toMutableStateList(), + purchaseWeapons = purchaseWeapons, + purchaseHarvester = mutableStateOf( + (playerStep.buildHarvesters.firstOrNull() ?: "") to null, + ), + ) } } @@ -123,7 +100,11 @@ class PlayerStepInputViewModel( } fun onEvent(event: Event) { - val gameState = gameState.value + if (!loaded.value) { + return + } + val gameState = + gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") when (event) { is Event.TabSelected -> tabIndex.value = event.index @@ -225,14 +206,16 @@ class PlayerStepInputViewModel( } private fun getPlayerSteps(): MutableSet { - val gameState = gameState.value + val gameState = + gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") return uiStates.value.map { it.toPlayerStep(gameState) }.toMutableSet() } private fun isFieldIdInGame(fieldId: String): Boolean { - val gameState = gameState.value + val gameState = + gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") return gameState.fields.map { it.id }.contains(fieldId) } @@ -240,7 +223,8 @@ class PlayerStepInputViewModel( fieldId: String, playerId: String, ): Boolean { - val gameState = gameState.value + val gameState = + gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") val player = gameState.players.find { it.id == playerId } if (player == null) { return false @@ -252,7 +236,8 @@ class PlayerStepInputViewModel( fieldId: String, playerId: String, ): Boolean { - val gameState = gameState.value + val gameState = + gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") val player = gameState.players.find { it.id == playerId } if (player == null) { return false @@ -265,7 +250,7 @@ class PlayerStepInputViewModel( } data class EnterStepsUIState( - val stepId: Int = -1, //TODO: find latest player step here + val stepId: Int = -1, // TODO: find latest player step here val playerId: String = "", val leaveFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index a2191c0..a6d5914 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -41,7 +41,13 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { val navigationObject: Screen.PlayerStepInput = backStackEntry.toRoute() - PlayerStepInputScreen(navigationObject.gameId) + PlayerStepInputScreen( + gameId = navigationObject.gameId, + onBack = { + navController.popBackStack() + navController.navigate(Screen.PlayerStepInput(navigationObject.gameId)) + }, + ) } } } From 4258d7b6bc0df2a75b10a0ab493e1d9e44e2c513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Tue, 28 Apr 2026 18:50:49 +0100 Subject: [PATCH 103/162] =?UTF-8?q?Playerstep-ek=20k=C3=BCl=C3=B6n=20flow-?= =?UTF-8?q?ban?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/backend/Repository.kt | 4 + .../dunestrat/backend/SupabaseRepository.kt | 10 ++ .../playerstep/PlayerStepInputScreen.kt | 2 - .../playerstep/PlayerStepInputViewModel.kt | 149 ++++++++++-------- 4 files changed, 95 insertions(+), 70 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index d0b1064..90d71c2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -18,6 +18,10 @@ interface Repository { suspend fun savePlayerStep(playerStep: PlayerStep) fun getPlayerSteps(): Flow> fun getPlayerStepsByGameStateId(gameStateId: Int): Flow> + fun getPlayerStep( + gameStateId: Int, + playerId: String, + ): Flow suspend fun saveDesert(desert: Desert) fun getDeserts(): Flow> diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 5541ec0..1913ea4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -73,6 +73,16 @@ object SupabaseRepository : Repository { list.map { it.toPlayerStep() } } + override fun getPlayerStep( + gameStateId: Int, + playerId: String, + ): Flow = getPlayerStepsByGameStateId(gameStateId).map { + it.find { step -> step.playerId == playerId } ?: PlayerStep( + gameStateId = gameStateId, + playerId = playerId, + ) + } + override suspend fun savePlayerStep(playerStep: PlayerStep) { supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntry()) } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index 68995d0..96628ba 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -105,8 +105,6 @@ fun PlayerStepInputScreen( modifier = Modifier.padding(paddingValues).fillMaxSize(), ) { Column { - Text("Field count: ${gameState!!.fields.size}") - Text("GameStateId: ${gameState!!.id}") Row { FieldInputColumn( title = "Lelépés", diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 08998e6..372f1f5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -22,31 +22,30 @@ class PlayerStepInputViewModel( val game = mutableStateOf(null) val gameState = mutableStateOf(null) - val playerSteps = mutableStateOf(listOf()) + val playerSteps = mutableListOf>() val loaded = derivedStateOf { - game.value != null && gameState.value != null && playerSteps.value.isNotEmpty() + game.value != null && gameState.value != null && playerSteps.isNotEmpty() } init { viewModelScope.launch { game.value = SupabaseRepository.getGameById(gameId) gameState.value = SupabaseRepository.getLatestGameStateByGameId(gameId) - val playerStepFlow = SupabaseRepository.getPlayerStepsByGameStateId( - gameState.value!!.id, - ) - playerStepFlow.collect { list -> - playerSteps.value = list.ifEmpty { - game.value!!.teams.map { + playerSteps.addAll( + game.value!!.teams.map { team -> + mutableStateOf( PlayerStep( gameStateId = gameState.value!!.id, - playerId = it.playerId, - leaveFields = emptySet(), - enterFields = emptySet(), - purchaseWeapons = emptyMap(), - buildHarvesters = emptySet(), - ) - } + playerId = team.playerId, + ), + ) + }, + ) + game.value!!.teams.forEachIndexed { index, team -> + val flow = SupabaseRepository.getPlayerStep(gameState.value!!.id, team.playerId) + flow.collect { playerStep -> + playerSteps[index].value = playerStep } } } @@ -58,7 +57,9 @@ class PlayerStepInputViewModel( } val uiStates = derivedStateOf { - playerSteps.value.map { playerStep -> + playerSteps.map { mutableState -> + + val playerStep = mutableState.value val purchaseWeapons = mutableStateMapOf() purchaseWeapons.putAll(playerStep.purchaseWeapons) @@ -67,14 +68,15 @@ class PlayerStepInputViewModel( stepId = playerStep.id, playerId = playerStep.playerId, leaveFields = (playerStep.leaveFields + "").map { - it to null + it to validateLeaveField(it) }.toMutableStateList(), enterFields = (playerStep.enterFields + "").map { - it to null + it to validateEnterField(it) }.toMutableStateList(), purchaseWeapons = purchaseWeapons, purchaseHarvester = mutableStateOf( - (playerStep.buildHarvesters.firstOrNull() ?: "") to null, + (playerStep.buildHarvesters.firstOrNull() ?: "") to + validatePurchaseHarvester(playerStep.buildHarvesters.firstOrNull() ?: ""), ), ) } @@ -117,50 +119,24 @@ class PlayerStepInputViewModel( } is Event.LeaveField -> { - println(event.value) - - val lf = uiStates.value[tabIndex.value].leaveFields + val leaveFields = uiStates.value[tabIndex.value].leaveFields val fieldId = event.value.toFieldId() - val validation = when { - fieldId == "" -> null - - !gameState.fields.map { - it.id - }.contains(fieldId) -> Validation.NO_SUCH_FIELD - - !isFieldOwnedByPlayer( - fieldId, - uiStates.value[tabIndex.value].playerId, - ) -> Validation.UNOWNED_FIELD - - else -> null - } - lf[event.index] = + val validation = validateLeaveField(fieldId) + leaveFields[event.index] = fieldId to validation - if (!lf.contains("" to null)) { - lf.add("" to null) + if (!leaveFields.contains("" to null)) { + leaveFields.add("" to null) } } is Event.EnterField -> { - val ef = uiStates.value[tabIndex.value].enterFields + val enterFields = uiStates.value[tabIndex.value].enterFields val fieldId = event.value.toFieldId() - val validation = when { - fieldId == "" -> null - - !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD - - !isFieldReachableByPlayer( - fieldId, - uiStates.value[tabIndex.value].playerId, - ) -> Validation.UNREACHABLE_FIELD - - else -> null - } - ef[event.index] = + val validation = validateEnterField(fieldId) + enterFields[event.index] = fieldId to validation - if (!ef.contains("" to null)) { - ef.add("" to null) + if (!enterFields.contains("" to null)) { + enterFields.add("" to null) } } @@ -178,18 +154,7 @@ class PlayerStepInputViewModel( is Event.PurchaseHarvester -> { val fieldId = event.value.toFieldId() - val validation = when { - fieldId == "" -> null - - !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD - - !isFieldOwnedByPlayer( - fieldId, - uiStates.value[tabIndex.value].playerId, - ) -> Validation.UNOWNED_FIELD - - else -> null - } + val validation = validatePurchaseHarvester(fieldId) uiStates.value[tabIndex.value].purchaseHarvester.value = fieldId to validation } @@ -249,8 +214,56 @@ class PlayerStepInputViewModel( it.isUpperCase() || it.isDigit() } + private fun validateLeaveField(fieldId: String): Validation? { + val game = game.value ?: throw IllegalStateException("Game is not loaded yet!") + return when { + fieldId == "" -> null + + !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD + + !isFieldReachableByPlayer( + fieldId, + game.teams[tabIndex.value].playerId, + ) -> Validation.UNREACHABLE_FIELD + + else -> null + } + } + + private fun validateEnterField(fieldId: String): Validation? { + val game = game.value ?: throw IllegalStateException("Game is not loaded yet!") + return when { + fieldId == "" -> null + + !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD + + !isFieldReachableByPlayer( + fieldId, + game.teams[tabIndex.value].playerId, + ) -> Validation.UNREACHABLE_FIELD + + else -> null + } + } + + private fun validatePurchaseHarvester(fieldId: String): Validation? { + val game = game.value ?: throw IllegalStateException("Game is not loaded yet!") + return when { + fieldId == "" -> null + + !isFieldIdInGame(fieldId) -> Validation.NO_SUCH_FIELD + + !isFieldOwnedByPlayer( + fieldId, + game.teams[tabIndex.value].playerId, + ) -> Validation.UNOWNED_FIELD + + else -> null + } + } + data class EnterStepsUIState( - val stepId: Int = -1, // TODO: find latest player step here + val stepId: Int = -1, val playerId: String = "", val leaveFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), From a5cd6911644aff12c755768a3e5d01ab42145bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 20:12:49 +0100 Subject: [PATCH 104/162] Filter playersteps on db side --- .../it/dunestrat/backend/Repository.kt | 6 ++-- .../dunestrat/backend/SupabaseRepository.kt | 30 +++++++++++-------- .../playerstep/PlayerStepInputScreen.kt | 30 +++++++++++++++++-- .../playerstep/PlayerStepInputViewModel.kt | 29 ++++++++++++------ .../feature/testdata/TestDataScreen.kt | 7 +++-- .../feature/testdata/TestDataViewModel.kt | 14 +++++++++ .../it/dunestrat/navigation/NavGraph.kt | 3 +- 7 files changed, 87 insertions(+), 32 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index 90d71c2..a3fe81a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -15,14 +15,14 @@ interface Repository { fun getGameStates(): Flow> suspend fun getLatestGameStateByGameId(gameId: Int): GameState - suspend fun savePlayerStep(playerStep: PlayerStep) + suspend fun savePlayerStep(playerStep: PlayerStep): PlayerStep fun getPlayerSteps(): Flow> fun getPlayerStepsByGameStateId(gameStateId: Int): Flow> fun getPlayerStep( gameStateId: Int, - playerId: String, + playerId: Int, ): Flow - suspend fun saveDesert(desert: Desert) + suspend fun saveDesert(desert: Desert): Desert fun getDeserts(): Flow> } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index 1913ea4..d75f81a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -10,6 +10,7 @@ import io.github.jan.supabase.postgrest.from import io.github.jan.supabase.postgrest.query.filter.FilterOperation import io.github.jan.supabase.postgrest.query.filter.FilterOperator import io.github.jan.supabase.realtime.selectAsFlow +import io.github.jan.supabase.realtime.selectSingleValueAsFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -73,23 +74,26 @@ object SupabaseRepository : Repository { list.map { it.toPlayerStep() } } + @OptIn(SupabaseExperimental::class) override fun getPlayerStep( gameStateId: Int, - playerId: String, - ): Flow = getPlayerStepsByGameStateId(gameStateId).map { - it.find { step -> step.playerId == playerId } ?: PlayerStep( - gameStateId = gameStateId, - playerId = playerId, - ) - } + playerId: Int, + ): Flow = supabase.from(STEP_TABLE).selectSingleValueAsFlow( + PlayerStepDatabaseEntity::id, + ) { + PlayerStepDatabaseEntity::gameStateId eq gameStateId + PlayerStepDatabaseEntity::playerId eq playerId + }.map { it.toPlayerStep() } - override suspend fun savePlayerStep(playerStep: PlayerStep) { - supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntry()) - } + override suspend fun savePlayerStep(playerStep: PlayerStep): PlayerStep = + supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntry()) { + select() + }.decodeSingle().toPlayerStep() - override suspend fun saveDesert(desert: Desert) { - supabase.from(DESERT_TABLE).insert(desert.toDatabaseEntity()) - } + override suspend fun saveDesert(desert: Desert): Desert = + supabase.from(DESERT_TABLE).insert(desert.toDatabaseEntity()) { + select() + }.decodeSingle().toDesert() @OptIn(SupabaseExperimental::class) override fun getDeserts(): Flow> = diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index 96628ba..014e1ac 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.QuestionMark import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -17,6 +18,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.viewmodel.compose.viewModel import hu.mokegyesulet.it.dunestrat.model.Weapon import hu.mokegyesulet.it.dunestrat.ui.tabKeyNavigable @@ -63,12 +65,27 @@ fun PlayerStepInputScreen( } } + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner) { + onDispose { viewModel.onEvent(PlayerStepInputViewModel.Event.SaveToDatabase) } + } + Scaffold( modifier = Modifier.fillMaxSize(), topBar = { TopAppBar( title = {}, actions = { + Button( + onClick = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.SaveToDatabase, + ) + }, + ) { + Text(text = "Mentés") + } + Button( onClick = { viewModel.onEvent( @@ -94,7 +111,7 @@ fun PlayerStepInputScreen( viewModel.onEvent(PlayerStepInputViewModel.Event.TabSelected(index)) }, text = { - Text(text = playerId) + Text(text = playerId.toString()) }, ) } @@ -119,6 +136,9 @@ fun PlayerStepInputScreen( ) Column(modifier = Modifier.weight(1f)) { Text("Vásárlás") + + val focusManager = LocalFocusManager.current + Weapon.entries.forEach { weapon -> Row { Text( @@ -146,7 +166,9 @@ fun PlayerStepInputScreen( ), ) }, - modifier = Modifier.onFocusChanged(saveOnLostFocus), + modifier = Modifier.onFocusChanged( + saveOnLostFocus, + ).tabKeyNavigable(focusManager), ) } } @@ -160,7 +182,9 @@ fun PlayerStepInputScreen( ) }, validation = purchaseHarvester.second, - modifier = Modifier.onFocusChanged(saveOnLostFocus), + modifier = Modifier.onFocusChanged( + saveOnLostFocus, + ).tabKeyNavigable(focusManager), ) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index 372f1f5..bb56b2e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -43,9 +43,11 @@ class PlayerStepInputViewModel( }, ) game.value!!.teams.forEachIndexed { index, team -> - val flow = SupabaseRepository.getPlayerStep(gameState.value!!.id, team.playerId) - flow.collect { playerStep -> - playerSteps[index].value = playerStep + launch { + val flow = SupabaseRepository.getPlayerStep(gameState.value!!.id, team.playerId) + flow.collect { playerStep -> + playerSteps[index].value = playerStep + } } } } @@ -113,8 +115,17 @@ class PlayerStepInputViewModel( is Event.RunTurn -> { viewModelScope.launch { val playerSteps = getPlayerSteps() - val newGameState = gameState.runTurn(playerSteps) - SupabaseRepository.saveGameState(newGameState) + var newGameState = gameState.runTurn(playerSteps) + newGameState = SupabaseRepository.saveGameState(newGameState) + + newGameState.players.forEach { player -> + SupabaseRepository.savePlayerStep( + PlayerStep( + gameStateId = newGameState.id, + playerId = player.id, + ), + ) + } } } @@ -162,7 +173,7 @@ class PlayerStepInputViewModel( is Event.SaveToDatabase -> { viewModelScope.launch { val playerStep = uiStates.value[tabIndex.value].toPlayerStep(gameState) - if (playerStep.gameStateId != -1) { + if (playerStep.id != -1) { SupabaseRepository.savePlayerStep(playerStep) } } @@ -186,7 +197,7 @@ class PlayerStepInputViewModel( private fun isFieldOwnedByPlayer( fieldId: String, - playerId: String, + playerId: Int, ): Boolean { val gameState = gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") @@ -199,7 +210,7 @@ class PlayerStepInputViewModel( private fun isFieldReachableByPlayer( fieldId: String, - playerId: String, + playerId: Int, ): Boolean { val gameState = gameState.value ?: throw IllegalStateException("Game state is not loaded yet!") @@ -264,7 +275,7 @@ class PlayerStepInputViewModel( data class EnterStepsUIState( val stepId: Int = -1, - val playerId: String = "", + val playerId: Int = -1, val leaveFields: SnapshotStateList> = mutableStateListOf(Pair("", null)), val enterFields: SnapshotStateList> = diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt index 237933c..97d4c14 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt @@ -52,9 +52,12 @@ fun TestDataScreen() { } } Button(onClick = { viewModel.initDesert() }) { Text("Init test desert") } - Button(onClick = { viewModel.initGame(deserts[0]) }) { Text("Init test game") } + Button(onClick = { viewModel.initGame(deserts.last()) }) { Text("Init test game") } Button(onClick = { - viewModel.initGameState(deserts[0], games[0]) + viewModel.initGameState(deserts.last(), games.last()) }) { Text("Init test gamestate") } + Button(onClick = { + viewModel.initPlayerSteps(gameStates.last()) + }) { Text("Init test steps") } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt index 82be7ae..23a95ac 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -124,4 +124,18 @@ class TestDataViewModel : ViewModel() { player.ownedFields.add(fields.first { it.id == coordinate }) } } + + fun initPlayerSteps(gameState: GameState) { + val playerSteps = gameState.players.map { player -> + PlayerStep( + gameStateId = gameState.id, + playerId = player.id, + ) + } + viewModelScope.launch { + playerSteps.forEach { + SupabaseRepository.savePlayerStep(it) + } + } + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index a6d5914..3a2ba64 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -19,7 +19,7 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { ) { composable { MainMenu( - onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(22)) }, + onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(32)) }, onPlaceholder2 = {}, onTestData = { navController.navigate(Screen.TestData) }, ) @@ -45,7 +45,6 @@ fun NavGraph(navController: NavHostController = rememberNavController()) { gameId = navigationObject.gameId, onBack = { navController.popBackStack() - navController.navigate(Screen.PlayerStepInput(navigationObject.gameId)) }, ) } From da5d4d6cda8bf0f8c30ee18f9bd3876657e80da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 14 Jun 2026 17:31:13 +0200 Subject: [PATCH 105/162] Use RPC to save gamestates --- .../it/dunestrat/backend/Repository.kt | 2 +- .../dunestrat/backend/SupabaseRepository.kt | 21 ++++++++++++----- .../backend/entities/GameDatabaseEntity.kt | 2 +- .../entities/GameStateDatabaseEntity.kt | 23 ++++++++++++++++++- .../entities/PlayerStepDatabaseEntity.kt | 2 +- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 2 +- .../playerstep/PlayerStepInputViewModel.kt | 13 ++--------- .../feature/testdata/TestDataViewModel.kt | 15 ++---------- 8 files changed, 45 insertions(+), 35 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt index a3fe81a..c06cfc5 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/Repository.kt @@ -11,7 +11,7 @@ interface Repository { fun getGames(): Flow> suspend fun getGameById(gameId: Int): Game - suspend fun saveGameState(gameState: GameState): GameState + suspend fun saveGameState(gameState: GameState) fun getGameStates(): Flow> suspend fun getLatestGameStateByGameId(gameId: Int): GameState diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt index d75f81a..e396de3 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseRepository.kt @@ -7,8 +7,10 @@ import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.PlayerStep import io.github.jan.supabase.annotations.SupabaseExperimental import io.github.jan.supabase.postgrest.from +import io.github.jan.supabase.postgrest.postgrest import io.github.jan.supabase.postgrest.query.filter.FilterOperation import io.github.jan.supabase.postgrest.query.filter.FilterOperator +import io.github.jan.supabase.postgrest.rpc import io.github.jan.supabase.realtime.selectAsFlow import io.github.jan.supabase.realtime.selectSingleValueAsFlow import kotlinx.coroutines.flow.Flow @@ -28,7 +30,7 @@ object SupabaseRepository : Repository { } override suspend fun saveGame(game: Game): Game = - supabase.from(GAME_TABLE).insert(game.toDatabaseEntry()) { + supabase.from(GAME_TABLE).insert(game.toDatabaseEntity()) { select() }.decodeSingle().toGame() @@ -44,10 +46,17 @@ object SupabaseRepository : Repository { } @OptIn(SupabaseExperimental::class) - override suspend fun saveGameState(gameState: GameState): GameState = - supabase.from(GAME_STATE_TABLE).insert(gameState.toDatabaseEntry()) { - select() - }.decodeSingle().toGameState() + override suspend fun saveGameState(gameState: GameState) { + supabase.postgrest.rpc( + function = "save_gamestate", + parameters = GameStateSaveDatabaseEntity( + gameState.toDatabaseEntity(), + gameState.players.map { + it.id + }, + ), + ) + } override suspend fun getLatestGameStateByGameId(gameId: Int): GameState = supabase.from(GAME_STATE_TABLE) @@ -86,7 +95,7 @@ object SupabaseRepository : Repository { }.map { it.toPlayerStep() } override suspend fun savePlayerStep(playerStep: PlayerStep): PlayerStep = - supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntry()) { + supabase.from(STEP_TABLE).upsert(playerStep.toDatabaseEntity()) { select() }.decodeSingle().toPlayerStep() diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt index 27947a5..75c07e2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameDatabaseEntity.kt @@ -26,7 +26,7 @@ class GameDatabaseEntity( } } -fun Game.toDatabaseEntry(): GameDatabaseEntity = GameDatabaseEntity( +fun Game.toDatabaseEntity(): GameDatabaseEntity = GameDatabaseEntity( id = id, this.progress.ordinal, json = Json.encodeToString(this), diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt index 8837260..1cb26fb 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/GameStateDatabaseEntity.kt @@ -69,7 +69,7 @@ class GameStateDatabaseEntity( } } -fun GameState.toDatabaseEntry(): GameStateDatabaseEntity = GameStateDatabaseEntity( +fun GameState.toDatabaseEntity(): GameStateDatabaseEntity = GameStateDatabaseEntity( id = id, gameId = gameId, index = index, @@ -132,3 +132,24 @@ fun Player.toPlayerWithFieldIdList() = PlayerWithFieldIdList( weapons = weapons.toMap(), ownedFieldsId = ownedFields.map { it.id }.toSet(), ) + +@Serializable +class GameStateSaveDatabaseEntity( + @SerialName("game_id") + val gameId: Int, + val index: Int, + @SerialName("field_jsons") + val fieldJsons: List, + @SerialName("player_jsons") + val playersJsons: List, + @SerialName("player_ids") + val playersIds: List, +) { + constructor(gameStateDatabaseEntity: GameStateDatabaseEntity, playersIds: List) : this( + gameId = gameStateDatabaseEntity.gameId, + index = gameStateDatabaseEntity.index, + fieldJsons = gameStateDatabaseEntity.fieldJsons, + playersJsons = gameStateDatabaseEntity.playersJsons, + playersIds = playersIds, + ) +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt index c79cf10..4c6d990 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/entities/PlayerStepDatabaseEntity.kt @@ -27,7 +27,7 @@ class PlayerStepDatabaseEntity( } } -fun PlayerStep.toDatabaseEntry(): PlayerStepDatabaseEntity = PlayerStepDatabaseEntity( +fun PlayerStep.toDatabaseEntity(): PlayerStepDatabaseEntity = PlayerStepDatabaseEntity( id = id, gameStateId = gameStateId, playerId = playerId, diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 7a31990..6828f74 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -33,7 +33,7 @@ fun MainMenu( } Button( - onClick = onPlaceholder2, + onClick = {}, ) { Text(text = "Placeholder 2") } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt index bb56b2e..c83402c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputViewModel.kt @@ -115,17 +115,8 @@ class PlayerStepInputViewModel( is Event.RunTurn -> { viewModelScope.launch { val playerSteps = getPlayerSteps() - var newGameState = gameState.runTurn(playerSteps) - newGameState = SupabaseRepository.saveGameState(newGameState) - - newGameState.players.forEach { player -> - SupabaseRepository.savePlayerStep( - PlayerStep( - gameStateId = newGameState.id, - playerId = player.id, - ), - ) - } + val newGameState = gameState.runTurn(playerSteps) + SupabaseRepository.saveGameState(newGameState) } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt index 23a95ac..2f0c5c6 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataViewModel.kt @@ -125,17 +125,6 @@ class TestDataViewModel : ViewModel() { } } - fun initPlayerSteps(gameState: GameState) { - val playerSteps = gameState.players.map { player -> - PlayerStep( - gameStateId = gameState.id, - playerId = player.id, - ) - } - viewModelScope.launch { - playerSteps.forEach { - SupabaseRepository.savePlayerStep(it) - } - } - } + fun initPlayerSteps(gameState: GameState): Unit = + throw NotImplementedError("Not implemented yet") } From 456d28fb3e088fa09d059444fe7a9de820c2fa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 14 Jun 2026 19:39:48 +0200 Subject: [PATCH 106/162] Improve player step input UI --- .../playerstep/PlayerStepInputScreen.kt | 102 ++++++++++-------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt index 014e1ac..faa629a 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/playerstep/PlayerStepInputScreen.kt @@ -77,6 +77,7 @@ fun PlayerStepInputScreen( title = {}, actions = { Button( + modifier = Modifier.padding(horizontal = 4.dp), onClick = { viewModel.onEvent( PlayerStepInputViewModel.Event.SaveToDatabase, @@ -87,6 +88,7 @@ fun PlayerStepInputScreen( } Button( + modifier = Modifier.padding(horizontal = 4.dp), onClick = { viewModel.onEvent( PlayerStepInputViewModel.Event.RunTurn, @@ -132,61 +134,65 @@ fun PlayerStepInputScreen( ) }, onFocusChanged = saveOnLostFocus, - modifier = Modifier.weight(1f), + modifier = Modifier.weight(1f).padding(horizontal = 16.dp), ) - Column(modifier = Modifier.weight(1f)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.weight(1f).padding(horizontal = 16.dp), + ) { Text("Vásárlás") val focusManager = LocalFocusManager.current Weapon.entries.forEach { weapon -> - Row { - Text( - text = when (weapon) { - Weapon.PISTOL -> "MPT: " - Weapon.LASGUN -> "LSG: " - Weapon.CRYSKNIFE -> "CRK: " - Weapon.LEGION -> "Légió: " - }, - ) - TextField( - value = if ((purchaseWeapons[weapon] ?: 0) != 0) { - (purchaseWeapons[weapon] ?: 0).toString() - } else { - "" - }, - placeholder = { - Text(text = "0") - }, - onValueChange = { - viewModel.onEvent( - PlayerStepInputViewModel.Event.PurchaseWeapon( - weapon = weapon, - value = it, - ), - ) - }, - modifier = Modifier.onFocusChanged( - saveOnLostFocus, - ).tabKeyNavigable(focusManager), - ) - } - } - Row { - Text("Harvester: ") - FieldRow( - value = purchaseHarvester.first, + TextField( + label = { + Text( + text = when (weapon) { + Weapon.PISTOL -> "MPT" + Weapon.LASGUN -> "LSG" + Weapon.CRYSKNIFE -> "CRK" + Weapon.LEGION -> "Légió" + }, + + ) + }, + value = if ((purchaseWeapons[weapon] ?: 0) != 0) { + (purchaseWeapons[weapon] ?: 0).toString() + } else { + "" + }, + placeholder = { + Text(text = "0") + }, onValueChange = { viewModel.onEvent( - PlayerStepInputViewModel.Event.PurchaseHarvester(it), + PlayerStepInputViewModel.Event.PurchaseWeapon( + weapon = weapon, + value = it, + ), ) }, - validation = purchaseHarvester.second, modifier = Modifier.onFocusChanged( saveOnLostFocus, - ).tabKeyNavigable(focusManager), + ).tabKeyNavigable(focusManager) + .padding(horizontal = 4.dp, vertical = 6.dp), ) } + FieldRow( + value = purchaseHarvester.first, + label = "Harvester", + onValueChange = { + viewModel.onEvent( + PlayerStepInputViewModel.Event.PurchaseHarvester(it), + ) + }, + validation = purchaseHarvester.second, + modifier = Modifier.onFocusChanged( + saveOnLostFocus, + ).tabKeyNavigable(focusManager) + .padding(horizontal = 4.dp, vertical = 26.dp), + ) } FieldInputColumn( title = "Rálépés", @@ -197,7 +203,7 @@ fun PlayerStepInputScreen( ) }, onFocusChanged = saveOnLostFocus, - modifier = Modifier.weight(1f), + modifier = Modifier.weight(1f).padding(horizontal = 16.dp), ) } @@ -215,18 +221,26 @@ fun FieldRow( onValueChange: (String) -> Unit, validation: PlayerStepInputViewModel.Validation?, modifier: Modifier = Modifier, + label: String? = null, ) { val focusManager = LocalFocusManager.current Row( - modifier = modifier.fillMaxWidth().padding(8.dp), + modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { TextField( value = value, onValueChange = onValueChange, - modifier = Modifier.weight(1f) + modifier = Modifier + .weight(1f) + .padding(end = 8.dp) .tabKeyNavigable(focusManager), + label = if (label != null) { + { Text(text = label) } + } else { + null + }, ) Icon( imageVector = when (validation?.isError) { From 11ddc2e694dcfd9751a45b36a3d0f1169032cd56 Mon Sep 17 00:00:00 2001 From: cstomi74 Date: Sun, 30 Nov 2025 16:02:59 +0100 Subject: [PATCH 107/162] =?UTF-8?q?ez=20egy=20hatsz=C3=B6get=20arr=C3=A9b?= =?UTF-8?q?=20tud=20tolni,=20de=20nem=20oda,=20ahov=C3=A1=20szeretn=C3=A9n?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 21 +++++++ .../it/dunestrat/util/UnitVector.kt | 62 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt new file mode 100644 index 0000000..91e3957 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -0,0 +1,21 @@ +package hu.mokegyesulet.it.dunestrat.util + +fun hexDrawer( + absoluteVector: UnitVector, + newVecX: AbsoluteVector, + newVecY: AbsoluteVector, +): String { + val vecX = AbsoluteVector(144.0, -83.13843876) + val vecY = AbsoluteVector(0.0, 166.2768775) + + val transformVector = absoluteVector.x * vecX + absoluteVector.y * vecY + + return "" +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt new file mode 100644 index 0000000..2df1c48 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt @@ -0,0 +1,62 @@ +package hu.mokegyesulet.it.dunestrat.util + +data class UnitVector( + val x: Int, + val y: Int, +) { + fun toAbsolute(baseX: AbsoluteVector, baseY: AbsoluteVector): AbsoluteVector = + x * baseX + y * baseY + operator fun times(number: Int): UnitVector = UnitVector(x * number, y * number) + + companion object { // ide beírni, hogy az A12-ből csináljon egy számpárt + fun fromDisplayCoordinate(coord: String): UnitVector { + val first = coord.lowercase()[0].code - 'a'.code + val second = coord.substring(1).toInt() - 1 // így tesszük origóra + return UnitVector(first, second) + } + } +} +data class AbsoluteVector( + val x: Double, + val y: Double, +) { + operator fun plus(absvector: AbsoluteVector): AbsoluteVector = AbsoluteVector( + x + absvector.x, + y + absvector.y, + ) + operator fun times(number: Int): AbsoluteVector = AbsoluteVector(x * number, y * number) + companion object { // ide beírni, hogy az A12-ből csináljon egy számpárt + fun fromDisplayCoordinate(coord: String): UnitVector { + val first = coord.lowercase()[0].code - 'a'.code + val second = coord.substring(1).toInt() - 1 // így tesszük origóra + return UnitVector(first, second) + } + } +} + +operator fun Int.times(absoluteVector: AbsoluteVector): AbsoluteVector = absoluteVector * this + +val vecX = AbsoluteVector(144.0, -83.13843876) +val vecY = AbsoluteVector(0.0, 166.2768775) + +fun main() { + println(UnitVector.fromDisplayCoordinate("A3")) + val vectorX = UnitVector.fromDisplayCoordinate("A3").x + val vectorY = UnitVector.fromDisplayCoordinate("A3").y + + val newVecX = vecX * vectorX + println("$newVecX") + val newVecY = vecY * vectorY + println("$newVecY") + + val coords = listOf("C2") + var s = "" + for (c in coords) { + s += hexDrawer( + UnitVector.fromDisplayCoordinate(c), + newVecX, + newVecY, + ) + "\n" + println("$s") + } +} From bd18e791b81ff5d3d9708a67ad15c57f79a44ae1 Mon Sep 17 00:00:00 2001 From: cstomi74 Date: Sun, 30 Nov 2025 18:51:06 +0100 Subject: [PATCH 108/162] This code generates the coordinates of the vertices of the hexagons on the map in SVG code, based on a list of tile ids (in the form of A1). --- .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 20 +++++-------- .../it/dunestrat/util/UnitVector.kt | 29 +++---------------- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt index 91e3957..8e9081f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -1,21 +1,17 @@ package hu.mokegyesulet.it.dunestrat.util -fun hexDrawer( - absoluteVector: UnitVector, - newVecX: AbsoluteVector, - newVecY: AbsoluteVector, -): String { +fun hexDrawer(unitVector: UnitVector): String { val vecX = AbsoluteVector(144.0, -83.13843876) val vecY = AbsoluteVector(0.0, 166.2768775) - val transformVector = absoluteVector.x * vecX + absoluteVector.y * vecY + val transformVector = unitVector.x * vecX + unitVector.y * vecY return "" + "\"${148 + transformVector.x },${183.138438763306 + transformVector.y} " + + "${52 + transformVector.x },${183.138438763306 + transformVector.y} " + + "${4 + transformVector.x },${100 + transformVector.y} " + + "${52 + transformVector.x },${16.8615612366939 + transformVector.y} " + + "${148 + transformVector.x},${16.8615612366939 + transformVector.y } " + + "${196 + transformVector.x},${100 + transformVector.y }\"/>" } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt index 2df1c48..4d8b755 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt @@ -4,8 +4,7 @@ data class UnitVector( val x: Int, val y: Int, ) { - fun toAbsolute(baseX: AbsoluteVector, baseY: AbsoluteVector): AbsoluteVector = - x * baseX + y * baseY + operator fun times(number: Int): UnitVector = UnitVector(x * number, y * number) companion object { // ide beírni, hogy az A12-ből csináljon egy számpárt @@ -25,38 +24,18 @@ data class AbsoluteVector( y + absvector.y, ) operator fun times(number: Int): AbsoluteVector = AbsoluteVector(x * number, y * number) - companion object { // ide beírni, hogy az A12-ből csináljon egy számpárt - fun fromDisplayCoordinate(coord: String): UnitVector { - val first = coord.lowercase()[0].code - 'a'.code - val second = coord.substring(1).toInt() - 1 // így tesszük origóra - return UnitVector(first, second) - } - } } operator fun Int.times(absoluteVector: AbsoluteVector): AbsoluteVector = absoluteVector * this -val vecX = AbsoluteVector(144.0, -83.13843876) -val vecY = AbsoluteVector(0.0, 166.2768775) - fun main() { - println(UnitVector.fromDisplayCoordinate("A3")) - val vectorX = UnitVector.fromDisplayCoordinate("A3").x - val vectorY = UnitVector.fromDisplayCoordinate("A3").y - - val newVecX = vecX * vectorX - println("$newVecX") - val newVecY = vecY * vectorY - println("$newVecY") - - val coords = listOf("C2") + val coords = listOf("A1", "A2", "B1", "B2", "C2", "E6") var s = "" for (c in coords) { s += hexDrawer( UnitVector.fromDisplayCoordinate(c), - newVecX, - newVecY, + ) + "\n" - println("$s") } + println(s) } From 3ff7dd66eae96e998fbcb5b611e3fbdc3907ce8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 1 Feb 2026 16:52:55 +0000 Subject: [PATCH 109/162] Very basic svg generation --- .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt index 8e9081f..ca8c3ae 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -1,10 +1,9 @@ package hu.mokegyesulet.it.dunestrat.util -fun hexDrawer(unitVector: UnitVector): String { - val vecX = AbsoluteVector(144.0, -83.13843876) - val vecY = AbsoluteVector(0.0, 166.2768775) +import hu.mokegyesulet.it.dunestrat.model.Desert - val transformVector = unitVector.x * vecX + unitVector.y * vecY +fun hexDrawer(unitVector: UnitVector): String { + val transformVector = getTransformVector(unitVector) return "" } + +fun getTransformVector(unitVector: UnitVector): AbsoluteVector { + val vecX = AbsoluteVector(144.0, -83.13843876) + val vecY = AbsoluteVector(0.0, 166.2768775) + return unitVector.x * vecX + unitVector.y * vecY +} + +fun getSimpleSvg(desert: Desert): String { + val builder = StringBuilder() + builder.append("\n") + + for (field in desert.fields) { + val unitVector = UnitVector.fromDisplayCoordinate(field.id) + val transformVector = getTransformVector(unitVector) + builder.append(hexDrawer(unitVector) + "\n") + builder.append( + "${field.id}\n", + ) + } + + builder.append("") + return builder.toString() +} + +fun main() { + val desert = Desert.create12PlayerHexagon() + val svgString = getSimpleSvg(desert) + println(svgString) +} From 20007e9b6a9dc2c4a55f7777d02d457e6be229b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 17:25:02 +0000 Subject: [PATCH 110/162] Set the size of the image --- .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt index ca8c3ae..6b45a7f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -22,20 +22,35 @@ fun getTransformVector(unitVector: UnitVector): AbsoluteVector { } fun getSimpleSvg(desert: Desert): String { - val builder = StringBuilder() - builder.append("\n") + var minX = 0.0 + var maxX = 0.0 + var minY = 0.0 + var maxY = 0.0 - for (field in desert.fields) { + val fieldSvgs = desert.fields.map { field -> val unitVector = UnitVector.fromDisplayCoordinate(field.id) val transformVector = getTransformVector(unitVector) - builder.append(hexDrawer(unitVector) + "\n") - builder.append( - "${field.id}\n", - ) + + minX = minOf(minX, transformVector.x) + maxX = maxOf(maxX, transformVector.x) + minY = minOf(minY, transformVector.y) + maxY = maxOf(maxY, transformVector.y) + + val hex = hexDrawer(unitVector) + "\n" + val text = "${field.id}\n" + hex + text } - builder.append("") - return builder.toString() + val width = maxX - minX + 300 + val height = maxY - minY + 300 + + val viewbox = "viewbox = \"${minX - 100} ${minY - 100} $width $height\"" + + val svgOpening = "\n" + val svgEnd = "" + return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd } fun main() { From 36fc5888989bce94282d7f818de0d9e9c245a033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 19:46:12 +0000 Subject: [PATCH 111/162] WIP --- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 13 +++++++++++++ .../dunestrat/feature/mainmenu/MainMenuViewModel.kt | 4 ++++ .../hu/mokegyesulet/it/dunestrat/util/DrawMap.kt | 8 ++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 6828f74..5505eff 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.util.getSimpleSvg @Composable fun MainMenu( @@ -20,6 +21,7 @@ fun MainMenu( ) { val viewModel: MainMenuViewModel = viewModel { MainMenuViewModel() } val loggedIn by viewModel.isLoggedIn.collectAsState(initial = false) + val deserts by viewModel.deserts.collectAsState(initial = emptyList()) if (loggedIn) { Column( modifier = Modifier.fillMaxSize(), @@ -61,6 +63,17 @@ fun MainMenu( ) { Text(text = "Log in") } + + Button( + onClick = { + val desert = + deserts.find { it.id == 28 } + ?: throw IllegalArgumentException("desert not found") + println(getSimpleSvg(desert)) + }, + ) { + Text(text = "Get desert") + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt index 35c008d..242159e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt @@ -4,11 +4,15 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.AuthStatus import hu.mokegyesulet.it.dunestrat.backend.SupabaseAuth +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class MainMenuViewModel : ViewModel() { + + val deserts = SupabaseRepository.getDeserts() + fun onLogin() { viewModelScope.launch { SupabaseAuth.logIn() diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt index 6b45a7f..4ae4ae0 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -1,5 +1,6 @@ package hu.mokegyesulet.it.dunestrat.util +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository import hu.mokegyesulet.it.dunestrat.model.Desert fun hexDrawer(unitVector: UnitVector): String { @@ -53,8 +54,11 @@ fun getSimpleSvg(desert: Desert): String { return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd } -fun main() { - val desert = Desert.create12PlayerHexagon() +suspend fun main() { + val deserts = mutableListOf() + + SupabaseRepository.getDeserts().collect { deserts.addAll(it) } + val desert = deserts.find { it.id == 28 } ?: throw IllegalStateException("Desert not found") val svgString = getSimpleSvg(desert) println(svgString) } From 3ac5dfcc1f27ba18108e3556fd624116cf546548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 8 Feb 2026 21:36:23 +0000 Subject: [PATCH 112/162] Added spice and water values --- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 5 ++--- .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 5505eff..8914abf 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.util.getSimpleSvg @Composable @@ -66,9 +67,7 @@ fun MainMenu( Button( onClick = { - val desert = - deserts.find { it.id == 28 } - ?: throw IllegalArgumentException("desert not found") + val desert = Desert.create12PlayerHexagon() println(getSimpleSvg(desert)) }, ) { diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt index 4ae4ae0..35c20e4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt @@ -1,7 +1,7 @@ package hu.mokegyesulet.it.dunestrat.util -import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.DesertField fun hexDrawer(unitVector: UnitVector): String { val transformVector = getTransformVector(unitVector) @@ -38,8 +38,8 @@ fun getSimpleSvg(desert: Desert): String { maxY = maxOf(maxY, transformVector.y) val hex = hexDrawer(unitVector) + "\n" - val text = "${field.id}\n" + + val text = getFieldText(field, transformVector) hex + text } @@ -54,11 +54,16 @@ fun getSimpleSvg(desert: Desert): String { return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd } -suspend fun main() { - val deserts = mutableListOf() +fun getFieldText(field: DesertField, transformVector: AbsoluteVector): String { + val textStart = "" + val textId = field.id + val textValues = "(${field.water}, ${field.spice})" + val textEnd = "\n" + return textStart + textId + textValues + textEnd +} - SupabaseRepository.getDeserts().collect { deserts.addAll(it) } - val desert = deserts.find { it.id == 28 } ?: throw IllegalStateException("Desert not found") +fun main() { + val desert = Desert.create12PlayerHexagon() val svgString = getSimpleSvg(desert) println(svgString) } From c6937a768df3a2d818f9f06d8c612db007170075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 15 Feb 2026 18:32:07 +0000 Subject: [PATCH 113/162] Refactor --- .../it/dunestrat/feature/drawmap/MapDrawer.kt | 76 +++++++++++++++++++ .../it/dunestrat/feature/drawmap/Vector2D.kt | 30 ++++++++ .../mokegyesulet/it/dunestrat/util/DrawMap.kt | 69 ----------------- .../it/dunestrat/util/UnitVector.kt | 41 ---------- 4 files changed, 106 insertions(+), 110 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt delete mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt new file mode 100644 index 0000000..4cedb2f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt @@ -0,0 +1,76 @@ +package hu.mokegyesulet.it.dunestrat.feature.drawmap + +import hu.mokegyesulet.it.dunestrat.model.Desert +import kotlin.math.PI + +object MapDrawer { + + private const val HEXAGON_SIZE = 200.0 + + private val distanceToVertex = Vector2D(HEXAGON_SIZE / 2, 0.0) + + private val hexagonVertices = (0..5).map { + distanceToVertex.rotate(PI / 3 * it) + } + + private val numberOffsetVector = hexagonVertices[1] - hexagonVertices[5] + private val letterOffsetVector2D = (hexagonVertices[0] - hexagonVertices[4]).invertY() + + private fun getOffsetVector(coordinate: String): Vector2D { + val letterOffset = coordinate.lowercase()[0].code - 'a'.code + val numberOffset = coordinate.substring(1).toInt() - 1 + return letterOffsetVector2D * letterOffset + numberOffsetVector * numberOffset + } + + private fun getHexagonSvg(offset: Vector2D): String { + val points = hexagonVertices.joinToString(separator = " ") { vertex -> + "${vertex.x + offset.x},${vertex.y + offset.y}" + } + val style = "style=\"fill:none;stroke:#000000;stroke-width:2.5px\"" + return "\n" + } + + private fun getFieldTextSvg( + fieldId: String, + offset: Vector2D, + ): String { + val textStart = "" + return "$textStart$fieldId\n" + } + + fun getDesertSvg(desert: Desert): String { + var minX = 0.0 + var maxX = 0.0 + var minY = 0.0 + var maxY = 0.0 + + val fieldSvgs = desert.fields.map { field -> + val offset = getOffsetVector(field.id) + + minX = minOf(minX, offset.x) + maxX = maxOf(maxX, offset.x) + minY = minOf(minY, offset.y) + maxY = maxOf(maxY, offset.y) + + val hex = getHexagonSvg(offset) + val text = getFieldTextSvg(field.id, offset) + + hex + text + } + + val width = maxX - minX + 300 + val height = maxY - minY + 300 + + val viewbox = "viewbox = \"${minX - 150} ${minY - 150} $width $height\"" + + val svgOpening = "\n" + val svgEnd = "" + return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd + } +} + +fun main() { + val desert = Desert.create12PlayerHexagon() + println(MapDrawer.getDesertSvg(desert)) +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt new file mode 100644 index 0000000..0a556d2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt @@ -0,0 +1,30 @@ +package hu.mokegyesulet.it.dunestrat.feature.drawmap + +import kotlin.math.cos +import kotlin.math.sin + +data class Vector2D( + val x: Double, + val y: Double, +) { + operator fun times(scalar: Double): Vector2D = Vector2D(x * scalar, y * scalar) + operator fun times(scalar: Int): Vector2D = this * scalar.toDouble() + + operator fun plus(other: Vector2D): Vector2D = Vector2D(x + other.x, y + other.y) + + operator fun minus(other: Vector2D): Vector2D = Vector2D(x - other.x, y - other.y) + + fun rotate(angle: Double): Vector2D { + val cosAngle = cos(angle) + val sinAngle = sin(angle) + return Vector2D( + x * cosAngle - y * sinAngle, + x * sinAngle + y * cosAngle, + ) + } + + fun invertY(): Vector2D = Vector2D(x, -y) +} + +operator fun Double.times(vector: Vector2D): Vector2D = vector * this +operator fun Int.times(vector: Vector2D): Vector2D = vector * this diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt deleted file mode 100644 index 35c20e4..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/DrawMap.kt +++ /dev/null @@ -1,69 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.util - -import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.model.DesertField - -fun hexDrawer(unitVector: UnitVector): String { - val transformVector = getTransformVector(unitVector) - - return "" -} - -fun getTransformVector(unitVector: UnitVector): AbsoluteVector { - val vecX = AbsoluteVector(144.0, -83.13843876) - val vecY = AbsoluteVector(0.0, 166.2768775) - return unitVector.x * vecX + unitVector.y * vecY -} - -fun getSimpleSvg(desert: Desert): String { - var minX = 0.0 - var maxX = 0.0 - var minY = 0.0 - var maxY = 0.0 - - val fieldSvgs = desert.fields.map { field -> - val unitVector = UnitVector.fromDisplayCoordinate(field.id) - val transformVector = getTransformVector(unitVector) - - minX = minOf(minX, transformVector.x) - maxX = maxOf(maxX, transformVector.x) - minY = minOf(minY, transformVector.y) - maxY = maxOf(maxY, transformVector.y) - - val hex = hexDrawer(unitVector) + "\n" - - val text = getFieldText(field, transformVector) - hex + text - } - - val width = maxX - minX + 300 - val height = maxY - minY + 300 - - val viewbox = "viewbox = \"${minX - 100} ${minY - 100} $width $height\"" - - val svgOpening = "\n" - val svgEnd = "" - return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd -} - -fun getFieldText(field: DesertField, transformVector: AbsoluteVector): String { - val textStart = "" - val textId = field.id - val textValues = "(${field.water}, ${field.spice})" - val textEnd = "\n" - return textStart + textId + textValues + textEnd -} - -fun main() { - val desert = Desert.create12PlayerHexagon() - val svgString = getSimpleSvg(desert) - println(svgString) -} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt deleted file mode 100644 index 4d8b755..0000000 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/UnitVector.kt +++ /dev/null @@ -1,41 +0,0 @@ -package hu.mokegyesulet.it.dunestrat.util - -data class UnitVector( - val x: Int, - val y: Int, -) { - - operator fun times(number: Int): UnitVector = UnitVector(x * number, y * number) - - companion object { // ide beírni, hogy az A12-ből csináljon egy számpárt - fun fromDisplayCoordinate(coord: String): UnitVector { - val first = coord.lowercase()[0].code - 'a'.code - val second = coord.substring(1).toInt() - 1 // így tesszük origóra - return UnitVector(first, second) - } - } -} -data class AbsoluteVector( - val x: Double, - val y: Double, -) { - operator fun plus(absvector: AbsoluteVector): AbsoluteVector = AbsoluteVector( - x + absvector.x, - y + absvector.y, - ) - operator fun times(number: Int): AbsoluteVector = AbsoluteVector(x * number, y * number) -} - -operator fun Int.times(absoluteVector: AbsoluteVector): AbsoluteVector = absoluteVector * this - -fun main() { - val coords = listOf("A1", "A2", "B1", "B2", "C2", "E6") - var s = "" - for (c in coords) { - s += hexDrawer( - UnitVector.fromDisplayCoordinate(c), - - ) + "\n" - } - println(s) -} From fca608de8fa59ca0582e3da3fdc2abfbe0909d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 15 Feb 2026 20:16:46 +0000 Subject: [PATCH 114/162] Lines done --- .../it/dunestrat/feature/drawmap/MapDrawer.kt | 62 +++++++++++++++++-- .../it/dunestrat/feature/drawmap/Vector2D.kt | 3 + .../it/dunestrat/feature/mainmenu/MainMenu.kt | 11 ---- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt index 4cedb2f..0b8a8f8 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt @@ -6,6 +6,9 @@ import kotlin.math.PI object MapDrawer { private const val HEXAGON_SIZE = 200.0 + private const val INNER_HEXAGON_RATIO = 0.65 + + private const val STYLE = "style=\"fill:none;stroke:#000000;stroke-width:2.5px\"" private val distanceToVertex = Vector2D(HEXAGON_SIZE / 2, 0.0) @@ -13,6 +16,8 @@ object MapDrawer { distanceToVertex.rotate(PI / 3 * it) } + private val innerHexagonVertices = hexagonVertices.map { it * INNER_HEXAGON_RATIO } + private val numberOffsetVector = hexagonVertices[1] - hexagonVertices[5] private val letterOffsetVector2D = (hexagonVertices[0] - hexagonVertices[4]).invertY() @@ -23,11 +28,8 @@ object MapDrawer { } private fun getHexagonSvg(offset: Vector2D): String { - val points = hexagonVertices.joinToString(separator = " ") { vertex -> - "${vertex.x + offset.x},${vertex.y + offset.y}" - } - val style = "style=\"fill:none;stroke:#000000;stroke-width:2.5px\"" - return "\n" + val points = hexagonVertices.map { it + offset } + return polygonSvgFromPoints(points) } private fun getFieldTextSvg( @@ -38,6 +40,50 @@ object MapDrawer { return "$textStart$fieldId\n" } + private fun polygonSvgFromPoints(points: List): String { + val pointsString = points.joinToString(separator = " ") { "${it.x},${it.y}" } + return "\n" + } + + private fun getWeaponBoxSvg(offset: Vector2D): String { + val points = listOf( + hexagonVertices[1], + innerHexagonVertices[1], + innerHexagonVertices[2], + hexagonVertices[2], + ).map { it + offset } + return polygonSvgFromPoints(points) + } + + private fun getWaterBoxSvg(offset: Vector2D): String { + val points = listOf( + hexagonVertices[2], + innerHexagonVertices[2], + innerHexagonVertices[3], + hexagonVertices[3], + ).map { it + offset } + return polygonSvgFromPoints(points) + } + + private fun getSpiceBoxSvg(offset: Vector2D): String { + val points = listOf( + hexagonVertices[0], + innerHexagonVertices[0], + innerHexagonVertices[1], + hexagonVertices[1], + ).map { it + offset } + return polygonSvgFromPoints(points) + } + + private fun getCoordinateCircleSvg(offset: Vector2D): String { + val center = (hexagonVertices[4] + hexagonVertices[5]) / 2 + offset + val radius = (HEXAGON_SIZE * 3) / 16 + val d = "M ${center.x - radius} ${center.y} " + + "A $radius $radius 0 0 0 " + + "${center.x + radius} ${center.y}" + return "\n" + } + fun getDesertSvg(desert: Desert): String { var minX = 0.0 var maxX = 0.0 @@ -54,8 +100,12 @@ object MapDrawer { val hex = getHexagonSvg(offset) val text = getFieldTextSvg(field.id, offset) + val weaponBox = getWeaponBoxSvg(offset) + val coordinateCircle = getCoordinateCircleSvg(offset) + val waterBox = getWaterBoxSvg(offset) + val spiceBox = getSpiceBoxSvg(offset) - hex + text + hex + text + weaponBox + coordinateCircle + waterBox + spiceBox } val width = maxX - minX + 300 diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt index 0a556d2..1893773 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt @@ -14,6 +14,9 @@ data class Vector2D( operator fun minus(other: Vector2D): Vector2D = Vector2D(x - other.x, y - other.y) + operator fun div(scalar: Double): Vector2D = this * (1 / scalar) + operator fun div(scalar: Int): Vector2D = this / scalar.toDouble() + fun rotate(angle: Double): Vector2D { val cosAngle = cos(angle) val sinAngle = sin(angle) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 8914abf..0787087 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -11,8 +11,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel -import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.util.getSimpleSvg @Composable fun MainMenu( @@ -64,15 +62,6 @@ fun MainMenu( ) { Text(text = "Log in") } - - Button( - onClick = { - val desert = Desert.create12PlayerHexagon() - println(getSimpleSvg(desert)) - }, - ) { - Text(text = "Get desert") - } } } } From b959874cd11d1fc85530e8ee207c75fe754ca9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 15 Feb 2026 20:41:30 +0000 Subject: [PATCH 115/162] Coordinate text done --- .../it/dunestrat/feature/drawmap/MapDrawer.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt index 0b8a8f8..08c4e37 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt @@ -36,8 +36,12 @@ object MapDrawer { fieldId: String, offset: Vector2D, ): String { - val textStart = "" - return "$textStart$fieldId\n" + val center = + ((hexagonVertices[4] + hexagonVertices[5]) / 2) + Vector2D(0.0, 20.0) + offset + val position = "x=\"${center.x}\" y=\"${center.y}\"" + val font = "font-size=\"20\" font-family=\"Times New Roman\"" + val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" + return "$fieldId\n" } private fun polygonSvgFromPoints(points: List): String { From 99294cbb87976027d119c8978ebc06f141fc30e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Sun, 22 Mar 2026 23:28:35 +0100 Subject: [PATCH 116/162] Add correct SVGs and template for usage --- .../drawable/svg/mountain.svg | 2 +- .../drawable/svg/mountain_bnw.svg | 2 +- .../composeResources/drawable/svg/out.svg | 2174 +++++++++++++++++ .../composeResources/drawable/svg/temp.svg | 12 + 4 files changed, 2188 insertions(+), 2 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/out.svg create mode 100644 composeApp/src/commonMain/composeResources/drawable/svg/temp.svg diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg index 9386be9..398ce0f 100644 --- a/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg +++ b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg b/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg index db89613..dddc3dd 100644 --- a/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg +++ b/composeApp/src/commonMain/composeResources/drawable/svg/mountain_bnw.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/out.svg b/composeApp/src/commonMain/composeResources/drawable/svg/out.svg new file mode 100644 index 0000000..49f462c --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/out.svg @@ -0,0 +1,2174 @@ + + + A3 + + + + + + A7 + + + + + + B2 + + + + + + B3 + + + + + + B4 + + + + + + B5 + + + + + + B6 + + + + + + B7 + + + + + + B8 + + + + + + B9 + + + + + + C1 + + + + + + C2 + + + + + + C3 + + + + + + C4 + + + + + + C5 + + + + + + C6 + + + + + + C7 + + + + + + C8 + + + + + + C9 + + + + + + C10 + + + + + + C11 + + + + + + D2 + + + + + + D3 + + + + + + D4 + + + + + + D5 + + + + + + D6 + + + + + + D7 + + + + + + D8 + + + + + + D9 + + + + + + D10 + + + + + + D11 + + + + + + E2 + + + + + + E3 + + + + + + E4 + + + + + + E5 + + + + + + E6 + + + + + + E7 + + + + + + E8 + + + + + + E9 + + + + + + E10 + + + + + + E11 + + + + + + E12 + + + + + + F2 + + + + + + F3 + + + + + + F4 + + + + + + F5 + + + + + + F6 + + + + + + F7 + + + + + + F8 + + + + + + F9 + + + + + + F10 + + + + + + F11 + + + + + + F12 + + + + + + F13 + + + + + + G1 + + + + + + G2 + + + + + + G3 + + + + + + G4 + + + + + + G5 + + + + + + G6 + + + + + + G7 + + + + + + G8 + + + + + + G9 + + + + + + G10 + + + + + + G11 + + + + + + G12 + + + + + + G13 + + + + + + G14 + + + + + + G15 + + + + + + H2 + + + + + + H3 + + + + + + H4 + + + + + + H5 + + + + + + H6 + + + + + + H7 + + + + + + H8 + + + + + + H9 + + + + + + H10 + + + + + + H11 + + + + + + H12 + + + + + + H13 + + + + + + H14 + + + + + + H15 + + + + + + I2 + + + + + + I3 + + + + + + I4 + + + + + + I5 + + + + + + I6 + + + + + + I7 + + + + + + I8 + + + + + + I9 + + + + + + I10 + + + + + + I11 + + + + + + I12 + + + + + + I13 + + + + + + I14 + + + + + + I15 + + + + + + I16 + + + + + + J3 + + + + + + J4 + + + + + + J5 + + + + + + J6 + + + + + + J7 + + + + + + J8 + + + + + + J9 + + + + + + J10 + + + + + + J11 + + + + + + J12 + + + + + + J13 + + + + + + J14 + + + + + + J15 + + + + + + J16 + + + + + + K3 + + + + + + K4 + + + + + + K5 + + + + + + K6 + + + + + + K7 + + + + + + K8 + + + + + + K9 + + + + + + K10 + + + + + + K11 + + + + + + K12 + + + + + + K13 + + + + + + K14 + + + + + + K15 + + + + + + K16 + + + + + + K17 + + + + + + L5 + + + + + + L6 + + + + + + L7 + + + + + + L8 + + + + + + L9 + + + + + + L10 + + + + + + L11 + + + + + + L12 + + + + + + L13 + + + + + + L14 + + + + + + L15 + + + + + + L16 + + + + + + M6 + + + + + + M7 + + + + + + M8 + + + + + + M9 + + + + + + M10 + + + + + + M11 + + + + + + M12 + + + + + + M13 + + + + + + M14 + + + + + + M15 + + + + + + M16 + + + + + + N7 + + + + + + N8 + + + + + + N9 + + + + + + N10 + + + + + + N11 + + + + + + N12 + + + + + + N13 + + + + + + N14 + + + + + + N15 + + + + + + N16 + + + + + + O7 + + + + + + O8 + + + + + + O9 + + + + + + O10 + + + + + + O11 + + + + + + O12 + + + + + + O13 + + + + + + O14 + + + + + + O15 + + + + + + O16 + + + + + + O17 + + + + + + P9 + + + + + + P10 + + + + + + P11 + + + + + + P12 + + + + + + P13 + + + + + + P14 + + + + + + P15 + + + + + + P16 + + + + + + Q11 + + + + + + Q15 + + + + + + A3 + + + + + + A7 + + + + + + B2 + + + + + + B3 + + + + + + B4 + + + + + + B5 + + + + + + B6 + + + + + + B7 + + + + + + B8 + + + + + + B9 + + + + + + C1 + + + + + + C2 + + + + + + C3 + + + + + + C4 + + + + + + C5 + + + + + + C6 + + + + + + C7 + + + + + + C8 + + + + + + C9 + + + + + + C10 + + + + + + C11 + + + + + + D2 + + + + + + D3 + + + + + + D4 + + + + + + D5 + + + + + + D6 + + + + + + D7 + + + + + + D8 + + + + + + D9 + + + + + + D10 + + + + + + D11 + + + + + + E2 + + + + + + E3 + + + + + + E4 + + + + + + E5 + + + + + + E6 + + + + + + E7 + + + + + + E8 + + + + + + E9 + + + + + + E10 + + + + + + E11 + + + + + + E12 + + + + + + F2 + + + + + + F3 + + + + + + F4 + + + + + + F5 + + + + + + F6 + + + + + + F7 + + + + + + F8 + + + + + + F9 + + + + + + F10 + + + + + + F11 + + + + + + F12 + + + + + + F13 + + + + + + G1 + + + + + + G2 + + + + + + G3 + + + + + + G4 + + + + + + G5 + + + + + + G6 + + + + + + G7 + + + + + + G8 + + + + + + G9 + + + + + + G10 + + + + + + G11 + + + + + + G12 + + + + + + G13 + + + + + + G14 + + + + + + G15 + + + + + + H2 + + + + + + H3 + + + + + + H4 + + + + + + H5 + + + + + + H6 + + + + + + H7 + + + + + + H8 + + + + + + H9 + + + + + + H10 + + + + + + H11 + + + + + + H12 + + + + + + H13 + + + + + + H14 + + + + + + H15 + + + + + + I2 + + + + + + I3 + + + + + + I4 + + + + + + I5 + + + + + + I6 + + + + + + I7 + + + + + + I8 + + + + + + I9 + + + + + + I10 + + + + + + I11 + + + + + + I12 + + + + + + I13 + + + + + + I14 + + + + + + I15 + + + + + + I16 + + + + + + J3 + + + + + + J4 + + + + + + J5 + + + + + + J6 + + + + + + J7 + + + + + + J8 + + + + + + J9 + + + + + + J10 + + + + + + J11 + + + + + + J12 + + + + + + J13 + + + + + + J14 + + + + + + J15 + + + + + + J16 + + + + + + K3 + + + + + + K4 + + + + + + K5 + + + + + + K6 + + + + + + K7 + + + + + + K8 + + + + + + K9 + + + + + + K10 + + + + + + K11 + + + + + + K12 + + + + + + K13 + + + + + + K14 + + + + + + K15 + + + + + + K16 + + + + + + K17 + + + + + + L5 + + + + + + L6 + + + + + + L7 + + + + + + L8 + + + + + + L9 + + + + + + L10 + + + + + + L11 + + + + + + L12 + + + + + + L13 + + + + + + L14 + + + + + + L15 + + + + + + L16 + + + + + + M6 + + + + + + M7 + + + + + + M8 + + + + + + M9 + + + + + + M10 + + + + + + M11 + + + + + + M12 + + + + + + M13 + + + + + + M14 + + + + + + M15 + + + + + + M16 + + + + + + N7 + + + + + + N8 + + + + + + N9 + + + + + + N10 + + + + + + N11 + + + + + + N12 + + + + + + N13 + + + + + + N14 + + + + + + N15 + + + + + + N16 + + + + + + O7 + + + + + + O8 + + + + + + O9 + + + + + + O10 + + + + + + O11 + + + + + + O12 + + + + + + O13 + + + + + + O14 + + + + + + O15 + + + + + + O16 + + + + + + O17 + + + + + + P9 + + + + + + P10 + + + + + + P11 + + + + + + P12 + + + + + + P13 + + + + + + P14 + + + + + + P15 + + + + + + P16 + + + + + + Q11 + + + + + + Q15 + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/temp.svg b/composeApp/src/commonMain/composeResources/drawable/svg/temp.svg new file mode 100644 index 0000000..a8127f0 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/svg/temp.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + From 79293d84978b9574eaa19ad4f028661f9b548948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 22 Mar 2026 23:45:55 +0000 Subject: [PATCH 117/162] Background working --- .../composeResources/drawable/crysknife.png | Bin 1044 -> 0 bytes .../composeResources/drawable/fremen_ikon.png | Bin 2474 -> 0 bytes .../composeResources/drawable/grid.png | Bin 146633 -> 0 bytes .../composeResources/drawable/hegy.png | Bin 78973 -> 0 bytes .../composeResources/drawable/lasgun.png | Bin 2168 -> 0 bytes .../composeResources/drawable/pistol.png | Bin 2099 -> 0 bytes .../composeResources/drawable/sivatag.png | Bin 51899 -> 0 bytes .../composeResources/drawable/svg/desert.svg | 2 +- .../drawable/svg/mountain.svg | 2 +- .../composeResources/drawable/ures.png | Bin 13240 -> 0 bytes .../it/dunestrat/feature/drawmap/MapDrawer.kt | 68 +++++++++++++++--- 11 files changed, 60 insertions(+), 12 deletions(-) delete mode 100644 composeApp/src/commonMain/composeResources/drawable/crysknife.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/fremen_ikon.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/grid.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/hegy.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/lasgun.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/pistol.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/sivatag.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/ures.png diff --git a/composeApp/src/commonMain/composeResources/drawable/crysknife.png b/composeApp/src/commonMain/composeResources/drawable/crysknife.png deleted file mode 100644 index 96d8b4ebc16e89f7456746222138e5752b7e2a0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1044 zcmV+v1nc{WP)xqS0(m?la3%oc;s5}~xG_BTfjh8@o)<4lXw!Rj|At`!)i+mf zATIl*-u7N8E|bX8{oMTQKBi}~K={VYZV~gWD%9;zg1Y$34SqlIw$#zD%w+d`Nn{HE zdg`sB9FsV~Gm+%HzR}aqC`QTf2%dh1j^S}U{fuHHj(KE@AHeWl|4+i#b8c|yC*ji7 z;4AxY)*tj*q1OjL=&LLNcWYF@Y;a>5%PO-w_DdzDY`qqq;g(iZ*j^6q@0m=x{0x}4 zjMlt25Lvh=F6NF#o&@TRjH$SSf1&7Wud3oV=+<)wx?20H9zf8@@ciidF2;@&WaB<> z)vk1J3Dq=f2f9AruAQ*m8)BV9(j8dn=5`L*#%7Czacxxx9l`ylc3C z;6akoj+58CwfELj08(CaF#4G44DN0xB^@`&FQk%Mr28jNX!YuGJ)Tq*%TYak6;H?T z6Y9~vkjiXukM@O9W>ZzW0*ef7$!M?ec(HvhHnXKNq9~m^KW|z@<0FwOuF&Jf@v)fn znMRYo%;R4bO5#*hBzhMmG5&pO^VFYG*znnRiq*L&P5;9Pq;`1?i_ZpB&WFYyP-Mh- z@%fpgzdME>$eL6Q&n5h) zHtL5my!Nq0kG*?AHv1rn{!4r8AZ~a~@r4{j z_{P5B@Ox16QhOXiBFqMYyy>1Mqif}j5QQkyhxRZjyTqYO$F>;Os08*E%vakXG4*e`V z)VM)(^D^yU55PXV9@1L(r4e(u488$e?DvR_W=z0d+m4vB{QwjAzJouYYYgWFH>KkM O0000^5H zgb0O%O~{>lj)tH2H@xrXdwjm%zu-%_ZfgPIm*fWk01%X=nZt2*{VQJZvDdiP79R(9 zm?_GM_ZZQ<;y>>wHkwaz9Ms;9{;HLkoS@c5v3m>Qt2B7nVcQH}LSc-0JAAHpwk2L3mGfWd z6ct01`ka&nngq~O)z4@fhf;Kw_>??3!aa>)oII$qBj}G@g3w|Y2c>vjoNW? zUpp^@YQdf|O?3{i1Nud8=z?t;!B=Sa4EYbwk@DdRKvhCp?x7!AY$-mXLr-Av$Q{gi zxJ4*YOpbSt*ZW82l=3qm=h@cBNt-y2lK*@udSIJ~m3G+f|ieJ`_{ z7q91`zNB6)_k>GPv$qIm;mPq;YdO|>MFN3}F`%bs-V4R5mcN^XEGxte=1WHahWK@3 zF=TkW7^I*~UuirD`nM`2a!iFo!M^rdD?~QFAspHQpGNI#2S%-KxdJCP_ zu~M1i$fvU+<)C90V58s>@#e6z786a~yjlg@g)QWgyVpggHXIG-Js9Z?Ur?^wyDA}m zDIcipzqZfKpuG(>;Ml7e7B_=%Kln#@rP;{i!-6K;P+r&ZfyXW@v?7gZHSFM5)nDzJ z?wKPNR41h?k*Ca*8q?NajazuUy)PUpGItB_k8@C5Lwi6B&b=B})vc_r-;Ym8&3!Zn z97^~tmsg_Lsx`>OKJIRlAF;m?Y9}b``R=ybwBbH)yP0Xg3VF zZNPmA?Kq-twb=O74YDRaE8o+I{}c)v67=AsjhpNx;V(Cj*`(Yw7T0%1mZ%2}i8_x5 zcB7sQpm$WXFh+al+9w5RD!0BNRO{OiJ)0vJHSP}#^rD}uh{}y?M78GQfqRI{Gdo=f1jMk&%6odH@)8^{-)dw=xy;) zOWc<~^WC0A)Ll06V7~7R&w&Lb^OZ(rXEV)zg0f-rOmv2f`O6pLvIm8m()h2Ex7?8X zK@#^;M-k?fR0;tunZmaTNA4J_nwVlD zc`FQfK&*ykFR4%OBeGB66%22PU3V$A_*gGp&luGvihus`_+yW=^h-V^| zbn1}mMx!&(nUF`03b$20d2>p!lt3Qd8 znTnOqn}ZJ5s9l16(pl_z@XlhO@9`{XwES zG#2vn!S0UC>g$!F&Kl`=&S$Q(IjMKUC@Yo>JW+)gzWFX&c}^%qe=T+cQL7e}8aM3r zg!D2>N!^~!^}!E4WKYlJ?A#~aG~kL(Uka1z{RC)qMh7nWEDxs@wb0?`Q~u_(-1h*)@|p4`rxwStIYTD_eExdDRHIa zcF%$*gKkFn&+q)v9f|lM*#aTw?GiQ~3#_<$ddDdGRn}$n^i*k4N{9cNnhTAY*$9^F z-9+DsOu!ZwPr-H0xO%B9SyjprJ#|fdZTSqjqhG8AjrGw$OP{nPmhBl@<@L+2w_i_W z4xCi%GVG8FZ-C&=Pv6-Qb6vHTKtzNkV6$|Z59=gZ$R8d_h@w)6wbE_t!~;LC>LA#< zXn0riX%fa;dHtS~UGG3v{{y!X*}dcj0iopYViR)~=v{+HoQQ*3Ff3g#xp7KqC_zQM|OSUwE>y(x*b? zsHp2thJ3|GT6y}09mbI@!pM1}`FnR;JLr05o+kJ783rk(#>K&)HNC-oVzn0>YsuM@ z?avJ|uE1a}uIaAZ<@hk%aozaw#SOb)$&!45 z^=dfd>M~=J(^oM*HLK~%OHEjIr=gkzgxKx6#`}K#Qi9WU$yQn=WXhfD%Wu*$r0M0l zfo6QP=Ne)h*#$fNdVL=G5~&f8H-46JnCM?Ek8l$ZzAY8|X!$M{RsP(R`n)ZRObv5W z>`bDIq1$F2-QSSF8N}dQ`ulnMKz-alMX&$T@G+i3vIOxf(#5JTJ#6UEGLH|!`M>m@ zuLU7WG>Wh=pSKJm-nujiZ)4;IU<3Zg_cYj@*Zq{=&54`;>5a$}EjT<7BrEUEcI5HF P0sv6vwq~`a-iiMQ{#d3F diff --git a/composeApp/src/commonMain/composeResources/drawable/grid.png b/composeApp/src/commonMain/composeResources/drawable/grid.png deleted file mode 100644 index 709618e61a1eb977df1f9c4fd7610ab1e4dde29b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146633 zcmZ^L1z42bx9`j_Ff;-x4Jy*zor+3#NlHjJNW+XMh*C;}zyQ)6(lH3q3erf8A|M@7 zlJ_0J|2^lPbDxLr`2^nC@7imx_^sbsn{W-aheY_)_#hC7Nb%8qEf5IG1pGIGiv#?` zth1RL_#fCq>!CcT^egQ$2y_dicwbJ($8;^tclx9D-uax&J1hJGtHKv5ijN*ytI6lS zdZBUmgZ#^D{7k?P9zD|1k$?1*=v_hFi@VA9-YwV4OtyUWXv|oW9v2%IvmwdY^ONEU zuG^n=Xdd_4@F?{v`EmY)4@}PcKfe&9*A9ajMTZ}z!10p)=NE`r>5%z<`52g-5)TYx zdhmQE?0@~BDC-Y@6jbHnYvm3v*H-UM)gYXt!q2DGqcZS3quJr9iaOFuOvxx(3yMt|ljK>!NFt_}+&1>J4B0Rt3oh=pu#e&)(JI zF$@T7xCb0tK>L(UHd1V6mtUaE(Bx|Q-wE&#vYB@~UjH8ELHt47gA?!zJTWqNGjHtQ zt!cRWl{v(O%vB>xS)--@>Q($q<-+AR%GGj@=0J|n>7;@)y{UgvnBzlfZ;ztn@aC|tUZTN-Fo91ZOayArjx!lL3sA|)Q&09(|D9gv zHP_r1E_heMv&N!4QKzUr0^Uub%#&Y_&7&ZW6?p#OAP}Okckr4k%_ik`?HTl z58LPIpiliL95V>8kg0#~=w;lxiH4eoe^T)mg&Hp6oarW+2zOoWtB?sexsRo_KQ7;b zOl|L{w)!NYzpL?pq-&6Pn8OBgyue!a@@-^OnMr&j1* z8!2?*7Wr^gGfEy6`0{&$XA-OX-L0Dc|Ff*`Q55h3L^FQvpZjQe^oLLD)xQ5$l>}CO z?Oj16c)98eR52<8KFMD0{Q%8vo{R7JG5p_?f|w_tl|>3%KKTkIf*^nkuvh%mMiHVY zI4Z`UUip^nTR^X@LnXq!7dk=mL;ApBFOVHO@sp?C=8EWz-nE+gf2$H=R&~oEy7c$_ zB$SK1Si3OFg~PJkl+|$Zxivlxq8}46?nRM#%AHlI^bQOUiD8Rg7teX-tt2BJk7r;t=CRo2@)d&2vYeh zBVOckd+$+lfaSEsdx%ok5g$HSpml8dx9me;*)`**FRzwG2SXQexM7QYB)K^0AXAH* zS1X!Bd`Vq3?(hT~8eN&APdX}E-u$Nz+L7@u141402O^Og_AidfN&{N!?$RF{xw52m zmU8xTg7TEND)Q(7)C#I((c;Qim}6jousd6^A1_ah7oi3ThK@m;`4SXyn_v@o2JOcG z2_-wX0E46ts!X^NYfBPA2mgzpi?lL^$lwh>ih>5&OaDjYWpY5|KnBBm|J?uX7AJah zvuBRH48{$eMNf79^WR&6kTcELmgwTls~t$HqgPR1_{dQ@%<55Nh5tywcpHuEcvOC@aV3SV3VI0zt`N-J z{)tZ!aNU6H(BNgwl-1R5yF-BxHCfmD`qKV%RM4lWMO4ELf#08Z=de;C;|G-!4*$^y zjyiCBZ36rM!oVO8q7~fNA&qysp*wu!{W2GM4B9O1v7NHN5?%~*iYzXM`WLT5$X)Xx z;`Mq%BZH+f`ZLG~JPgioa2rUe5yv{@L+&Kjd~jlr0Z)%7xX=Cz3GR1*6HuG2EWO;H z6S)DAD87&38=4{=^NO1X{(_P^JlMZm6BeN(w|3uCPS~~ho!n>*KV|>^ebdz3LH79Z zmGDS*2(gmRf-{Z7g=m9vvWQnOqaXu1C^{111t%f4_=CTHa-*DXYxRYbZq3(^C|bN# z+tI5}o8JNqj1pJR<#J;&V1y;22r?Ef|-y`++oUL&h86p>x#xY zwm}x@{xh;MTv66<)JiUwry4_w_>3S&)LLa#?Mi|T?vMv+YyNqg6d0qt+;^{S;17f( z4>IU?m84NfJW*CY+fTD|7v^(I0g~9+`L>m~HI3)ek$DlEz^mWpOchsO*r_Z7RB+imQ{^G=2Be>O?R(g%5a%h_mETn_w3C}I zAOHxYgkH71SEe|a1ejLjTVKTGXFH+$klmSD?@r-q;$qPB+X|u(oj@jYZ}SB?dVX}X zI)wWbA|A}Juzn@Ac{N7mT)`QaQu9hes$N#41Vc^5niyg3Lyu9ysP9;lQs=5JkTIC^ z*NM1fxCA0upr)-b^%8E#>H`b>#*gLUzlch#a8$t)zt7R|Vf^w1Nx;cYAdYs!FQT#D zfTn8emtXB4N%98vA5p%=mma(F)BnfZvsua|U9W9LeJdJzjY9xh0uj_y8i}P!5~*NG zYQIwS?&2#yXZ4vtros&C3`(mRqk5X;F6zp0i#z94 zuZA?PqsD8wn)a(#cLHeO)4_{N)h$D%$x?9GwdLi)hwf8>wXp^go*LZqUxWogSVKMt z){6?-%)ky6>_R8$uiW|?5HbQ7E)6J74Lx@|RaTXofoE{=ogeG&eEK_oM^5Tq&k+*q ziXf10$yE^gfB{-MQkGXx>iaEMS6e?H7h6i7M>MHS4Pm(|#RMm}HBWI$#*X39;1ngT z!T`nudOGbwL`~m?+(=Wv(nePOjV{hChdc>ZcRi`r^`1VCG^|*wE-hAIvkcV;pCYI+ z`y#=BW%{^0Quxc|C$%u{x#E-Rgt5^Z*F%~4etUZ5x znZ43bdKOpX*{jc2T7Hd*@1)Mvq?aaZi8~D`rh52v46+15|JW)IGH1eJfEiO&!lKGL zOq1(z%CWX)Upv+mTt5C1i1%5puqjE5((_~B^xp;VxOd?@1q&t!h>TGCsECn4gAd#+*Fq{I|jO%*pNeg$?dU5>U(A*aA`^S_3d$ zbJCx0yc^GYl5Pz5`ITK2B#65}6ny)FrufRPGoW!_TdB%TGmhgb0}@d4dgfzVgs*E( z@RCYyr5*__96b5dpNrZ%b^i4aZu4TIc*N_bflDA~8;dfzEnsek-gI(K`D^MQ+W*!L zWK9uwtM^D0Ci)R-`MB~&+HDW3xryoO9IocbD?=wt1R_)W!us%KbpOtRWFr)SWVi&c zu6Z}H>`odG9@;D>a8ewx(30hmWz{7p;L8md)bG}ypihVr>b8H}EK z3?cn5$9~6n0nPR1OE`sM#WluWgBnY{ZWoaFlZq;XTM7&x-$DnW(fzyhFB?)mRp4x8 zYSL~9Tm@>RKA?zQrLU`(+I*lWOJZ8o#fQuuVN9$t!l<~KRnFDF0|A`;&Tb;i$>Td0uf6D*+U%u+2t=xnrnJfjKmlSVLbeSV~v>UA1d?0n_NjZL}gmGUEHSZ zYaWwNNJovUO#UVS0E0|!u~k-sD#rVGj7F-*7nrPU7*#rkmv&!Ei#*6(YK&joIspXwgNS#Jc7Bn3Bf#rt1-C-`PVs-npObIr+Jr z$FS1ARzin7iiNE9zy|dM73amRZ?If|_;A8IA&)7EOun3K2JdbUX{oxA}B>Pn;*u@)`D1 ztY>|S%Wj^!e&=i`68>7tYb&i>hX$%DLT{OECi$Sci&)QkCvpEl$@jqKza;y<#JnR; z*4)e71}%k6S>?v5IyeIEBY>I`PZOw^f#SsAbD%OVR6>nv9rK~DKU&|(Me_NnGv2de zyc0X9&GDm4kVFcU3l{g$fyJ2hB9h=iyJg@L-wwZOuM=xN8uU($FsFIOJb3$14_wpz zf(IPkQNZysX{44a6`P?9KcH?iZD<1d!Q77ee#wPMRv6PEd~bCFV@XJB2ve@217Q~Z zr@8O^MaH!p@m;NOF-zAw`g7F<2kk#vGH9+PYJG^StkgPrB7q4m=4e1`l%f_47d(d| zDQkVEv0XCwbXv4GW&*n|D?^pcxF`Z1kN849;xxg&&Jb{7(0_|+e=;+C!$glb4 zi#Lk`{&jsxi{=9TYu`n_^&iK2NYQQAFtMi9eBJ)$-k1Tw#0L#rrag_{qY60(6N9gQ zHX`o%WB7fIFia&Wj$0&H@9{P^C8T#iZ)fNnWl#PKK zE-vWFt_WBl+$7Tuq@cG5)ZYKdKA z2DAOty~$BN%8YSJ*^ij@#`c7#fwSU=9J#epyQ(uiTjgq4`8I&TU%u3?H(mXhgg1%v zqr2(jCpZLZiKI;>1EOk+Iu;k;1A3I^#U0JU7hGLBKmxwlgksdint@vSgmn~wTIv(o zJLh~a^C9RQYJen5odeTe>(gWB`y2eUkSeP)<1CvjI|Dx`{iaU?02*_jJFcW{H~)is z8vlht0}g@xMZ#M!zpqT9XN!Nj#Y-Uz1epHPHgC;|#nnQh8Bd7?c&tbpdjQ zv)royN6mbk$6~%q8NVjDWn$t_B=(dj({Kx}Bo)n`-fPrTOtB+d4`_~cytd|oJ4}E? zMbWJ}2yK`=MZRmY~>OVV+ZWyHKI(B^tPr zL6lt`cUa$eL?qW-Vn`*{h#R>qr=UXjCF7!jDMj$zb5$|97C{VQfapcf@7lwvv>u&~ zeF%*wz~WOcU)D08{j|37`Mn*y3!b=NDY`8b9DcCj6uv4(xK|32raP-K5sPRK2)&49 zC|3YR^3i^kzJT$es+HtYr2SsD!0S{552oY#&O}Hf#eeuK9GsB&9vT0^vBzK z$NJmgGH8+B8?C&mn@VZ=a7I~=+8ou>)*AvZ)NDPpH?1#IsyMJ< zL{zi7FlO5T?;+$$x0NF2nEp52>@|rm%*r`zq?KuG+~HTj0ye|}Bmvsw0`-bm-cp_9 zO`yPSqOGGyn$@@jIK3ErMio?j1LMH}1-1Foj@+grICsYaa{hXf%nvKA=y#@nka?Fe z5oP(T40aSqu;PyXx4Un(;UY`+DEQv2(4mj8c`Z0&{;IeR#l*$ivilcx5AR*bCRC?k zyYzNi;yhm}76-)WSH2{2b3G4Hz&{#HY2OO2yA>5p+vyu3I{yy6B!w^o$P)SeCUS$D zheo&eCkQcKmXG1OM;m5C0fW5$2z}sYJiCYeeJodTg{uPKRD?z7hD;wqL?91|mYa;D z|KcOV#@hJ>&D+tIr^m9P4J6tNUT`kN0m&bYj7tp;N&|-T-DZUj6G6?dLu2nGRATp3yK_#YuOP*9V7;wDb(HZ&|UWgAR#@>L$RXQ(6q51)< zd4|3*VeGxul;3e{V%1LY!);Z$8@;Td7xTS?1zEu7n0zi%Jh$p z-Xi6XDa-!Ke!_9N5yFz8Y|s^_JN#;zfo0a){2R!1Q&jbibwa^PP2gI4=JGu!#i`(; zoWzjVPQG2oKsLFq0N7O4w4Ehjw2zs~_1_O|>AX3XCi9o6u2@jEcznd}3Rd0*c^&s zK{9{tKLHMdinM#VD|@xTzp+B+$X&?Q*rhM5VdaAI%S=x>e_~JEaHm`WCwGk^;Kb*B z`8z~IPrj(j);1ITX!QekJ+x7K$@Uaj6Ccx(dP4kB3|O<|nnvgg>W4v<@iREB;abmy zs9nO^7lF4xt$jJ}93B#Enbq6JKXgD(kzq<6 zQQ?92_%#lKPgSFyF(7G;tuam#iaCHMZ}%}TslTS-(Z>SXbLdt)yQY02L_NUIhfog5 z7!}o`!mWSBFJ20b8c_H_y6P(%P^Ui-WBlu0TUQa*ikC%l65L>&kQM7T#dgOBV@Yc% z%*qciD#_fJ16EGHHb_&v`e`K6#VTv_kA2}}&d{;`7nM!T{6@{K?S=~dcGj<(d;P|K z=DYT0sjG_-Ibt3k>z8Bp8`$@&H0|D6_d?y-t5&P;H}eSoUDA-4xG4KTKs(Fi0%Y-A zGr0Tg!MINg?ZU%tuA@mYcA5KWn?ymTte&4*U0&JLc#Uvp7A&u!N}ZexeoM=M)NsD` z4z}c2&VR49&OheWo#i|9X`w_>E!I1w?J<}yCMA!LrC(N0m3M(-NN}zi0``G*$Ca;~ zWa|xhUFNsbQdH!%C8}LHs?9RKvZIYD2}>Ghm>y`Odm>b>u!XE#qI~BT3Dwr@insd{ zN=4tHiyFV+d2|L;$2qac7P`id60WB?Or)Y=?scCqBkK=dq?5m)j*xC z?Y)X8zu?i-zxP|Q1aCiJ3c30JhTDkS$Rkr#;lqbuEBZHVXXo6g1YuQCptfS-&I45c zYG0gZEIUIc>yNh7{ye<5yhdHO4acv5wCFi`hD&*Fs4@kyv^3+9n798NIRADLe+DO@ zA7%|MAY2xDH4_0v;qO~W9ZcB4&)gw3_cbq)U$bi`+}5}mP7hIv!3IUjkFXj5Xg?h% z7o5%p?VStBNQ_V=CEzP?5zrZ{lyTLZlgOz+JpZr~=D9zs@S#`aCaF6q%Vao+ zo3_mUK#zZVShq`0W3N$+{*HAjyYrZw7a-d7|IGpb_2*kaS~0&b;y@ayRzRmrT-(%l za!Q(z&QVGI4J*oxnWm|Fa5TxX@G*ryvwC0VL-8+=m%rKX`Yk?LZAGQ{?D-d=E^+o~ z=(8`^j_vceZ3=eUd`Mih{_(QRK-Ud#js{T7N3uiSa(CJ;J^qkp&}qqBqZK2I<&W%j@fyTfvo1~e zB49EDVD^OzE}-ehcqar&P?A z7nGee+v>)&-`hVbk6Io^t;A0hgQiAN#+ahO3`df(>+hIFO>Rjs_@w@32%_kD#*LV7 zrzRn`ei2Ie>ZNT4Qy0uJhM(WW0tEk#+fTakEEub)hQD366M6l-kGm_nHe*23%6Z1Jwca4XSMzA^)PB1IZO(C4>o5{o)!+nY47Uk;~ zT^~Sd5{%Rj(EWoyPOhV2@l6PwFo$i4kke<(YaKqBDEkKp@v)j5Ds1Y zU?P`K z1D*6LS45D5#n2R@9d4b<+57wZ;mUB7Atc%e zVR$^vlCw~$z{qE#tXM$0nnebiIcR05pm(C@F%rfub@3REI2d}RF>~8qZl*=V{R+ zWD#~fIM$ME5|1XanO;>&CqzhTDwXGP(WHk{$q&^0gGq51uKk@z@f*A()Ggfi1hm8_ zj@MFcnwC!Zn$4Em>id@D;nok(f9h%YL8qH|fxAxSX+M8k^XA zT7ON!=4Uh~l2Th%;F@Z?PhAKF`1;iJi2sFivIB?x=rj2k(6=(b2A!n|?kEv^^@U)f z>P5Br1s|>rKBIhr$AD|Mwc1r65Hb;L;BWu#l~PoYL@}0t_>ykL$d6Hg7EUkhogT9J z%aJ_A$GYe8I%6Rf<^?~vs6jO`q>#?9Kwyvo#fy7_Z9rA$L>Xr7_h7@WZH|~|n;S>1 zKMvr=&#Zm^H2EKL8jDA?w z$=Z@CdaS3gvzf8bmm7a%x2pVpOD1Iord;>9yERPc#gN-;^BJ^866TvffFb4)asZ^a z7<|#(3haRuH@`lE-pUkH%SZ~0SEGwQVNIx^MydE2YI^6OK{p^?UBt&|=>ZAcItJ0Y zfk1nM7R9X!rBT89J1mBbhdwabg=YzD?W^Dn1fk?1mY5@gh!0NmRNsKXtWfbQC=6V1Xv_oH)Wm`?y!26YO^W++@ z%Zb};bG(;XfmW^0NTWiHOv z(H3Z(`AM_ep{uGUh3&q3pss4JosifuP|>^2`32XJUZ%@P&}gv;Q#9UE1(4W>WeNvt zNCBCTb>H$_$_cOTo6Z%v#XwXj*M{M+o)W=W``I=gjTDD#P=In$c}h=VZaUUf*LF$| zI~5CGG1dv6H52)$lOLMiyzTy;ziCOJMFzR`GlLXzjc7{Md0UVBwo`LGE9)PMg#kx{ zbWYV^DX#fkH{if7u9wC!BYeUcFeaImdId+&bo?fsvF_f!kBE?WTw zD@hHeGBBORU8}6ug_|#xL~0auT4_hVV+}&HbXXF8dz=htq*UD}<|QB13=|y^Dfpri zThki5i7OWy36~(Zw#7`xkzr`1J5lA-%)o#`<2*@s@ZI8~hxhlF-fLT)p4pu`32cqx zOMk)i2&fJf7wC|j;VBj18 zwj>c{FAEm*?!cLUH!k_P7w! zsr%7(oltqi+=m0tV@ZNvTR@$Tp7*#n{Qa+gW#xv#(Q)1o?%uef$=OYrKJT1TTZ^Lt zmv_=#{w%rUJ}a!<1V^9=Ee}Q6W$6;i|K)~tV^Xc%#`o3P=mAy6K?NHQpM_x z77b`AQP3>ep$W6-hrE<#lf>{AwtV0FYu~o^uE#CQhn3+l6!9(nFfm?LPG>9++S-O8 zi1@)fXNO{@BEa~ChDXhzm|k=q?R~$(&ChFMtPYrx>H%hE>YM990dovFA{H3jEBfL6 z*tJT+u_%VQl@m$$!ub9&%D%r|9y5cXE+jQ#`$Z(uAUd~8=0@tO^o-||obe@%ry^rQ zD{ff>J1^spiVG&|<0ien=R^=&+p&doU2tUonG-;;k^0aeYm1cP2UOqRoNn%NU;l=# zMsEZbqq6K9_drRpjx<`W6ePoUYXFj-bU8&k*@Wc|c5mElnJEhp5xNWdor{^oc2QfW zKx2pZ4u*olZHg8GvY0V>7tDUwz&Xnk{RO_~t{u>QL{A$Kz~kB-GvG8YnuqQE&~1J2 z)O3=Ux*|DynzHL;HZFVT-jHvu=no*=i1og0>)QFFc~Omda8#|{uW~z~`LJ{6(&*(e z`0^TnFJl>zFuzFJW7p1Cal}mk@b45R)Y|ifDk-W!GWGw!&iK4H?!Jy;p{dB(M_H%l8Ns{j!?h-q~{ zez9Dlg1iOk<@O;w)B+2Oarp$Wl;Q+X8#IF$BrxB!NG2?*n8Bl$KtTX@1Q~r7zP$tp z&Ur?8(sa{Rc`Al~PJO}a0A%)cnz$g?_;9wKziC&r1!Uw15b(0ffdAOyeW>L_JjZQf zjtU#OCyNK14-29|nKxH~M(KEavl>k?M|lyP>+8Wobcz2V6?*4_n_6L;?e7X0G<6J3>Cq>wq7pu5WQ zP|m9%#1etbi)ZYgk<#m|e5!uohl}eaft765z6FvxYQi>zYH>e{g4S3w0ed>liHqNx zAOHH1X=egEiB7{e`JxcQfEQr0N0tTQ9cK8`|79(|J*R#OE1i!jT0MtqY%u*DO>cYx z)!^XASmRPcywk1L%Wg?$BL`M0Oa4^3E_*?LX{-qzk$P-ZeJa@GHE&& zlb$xu)5z~QgWVSYeYrt7v}Fb3ehs$vx43XiI0J>_e4L6ux$8}2!QGtK!_PB!?e6x0 z!mwO;_Lw(5m6G+Xb*7knst)Hi7MoPIXO)m75xp+j%AjEi43Wji04B0?kjcf1sKGs< z&`1mtddaD)_NGbH^N5WDz1x*Z4drXOw zUJW)y=y_{I3BeZ7*|E19l$6;bH%H{c1DXLX7y?>g?aQd?g!(aU-9W3M%S}bPZA}!j zM^ns$gNxhEYJ0L&%1VT|4uKv^1;RMDboKFiG8j9<^xNKRcWxcunFpxIg(N{k<*Ueu zUGpr(=Q`58H=Co}$WXl&VpeoHL&Hl2c-G+u}z`QJ>ss z$@4x~$6{BzZD1Ipu5VG+Kk{mY0gT@;dW!;l451h|cdXu?=fkZER1luh0dNU1M(}66 zhz7i3(%c(spr%@RLM7OkLE9>Rtx2H#2KXuK^w;fvZSnF4DE8la4I?RwEpnXJ6Xl+; zt{w6C;((8s8nw89Q*ps76}+muXqoJ<&1z^UlYrE) zto(&9J9sE2R4vDDg-Q@}lh^x(t!ngl{@~8p-d5bR8IIYdoY}=cpF7iCI7d-Qkt+4C$-q=dBO)GH_4I&1sxc z0C*`1n9Y{c6NndC5{?g{k6qg0dB$i?9<~`C>!GLaLJ+qYSKXK$zopBv3yA%RST4#x z@XN%+P9Q+mcYKHpi5>?_PgrR(kF!%|a)u}KfAiL`|B7c!)Gw5e)l>$UWeTwVqhX%0 zUMP;Ltju&UR)EJK%)0zsy25UnLSXP?`0G@VU(#fCPnLWcmk^gvddx}y8?Q?yQ1j{a zAoSwL*>X;6m}T*s_jj&o%mGbQ-s3nr*W0dIPoC{rL{aZc6VgufSztznx?=$a`5t|F zoGL2=jr$pZzqJ@7%;va$iU`jYlGKPXsZ{X#O>)ypTPFA9)|RMPRR5YyM%-9B5iP;{ z*mqitBRL)K1mnk-*SGT?b_?ZXC*oR%bz!-vDi%n4X_AC9B5L>Q)fM{~uubU%#FZ=i zFyL7QGY{J$q=J-&*!$J7Z1o+fWE6i^f|(L%Jd?xADvoBEH>f8A=&#UmVA#m)GptE? zpJtD~=re3|^kK;(i>1VoCjAAk=nhh3_!Ig9aeO6K%b!M!qgl=Ex}``qgYoKG>wO6) z(g#^B1QY!J%8r@)Px6?}1M4vP=*Kv!%c8(;%HLlG zaF-4Yj8D2(9EC@igpz{yikyj|J=~{Fmr+OCW#Ko(`j*?eY#?)eldr%lXJtuTnIePr zuV)Jjr)~dxZ^VQbqF7QW=Ye(9Xq6m?s(eD-lZkT2u>=@I48zxZ zP{Ab#j4ZE_4#RaDo=|1LhY+KpTQ5|=B|w4H1l*el%iLc?y@n5kcw7&3ks(rv$KZ~? zz}QS8ztbS-X}I+x8S#iR7#5$hFM}==|yH z43#Zr;Uq<2M1QA+qdbyLyswWFOV6@L;jKF>i0d~qN0p~B(#5|Ux!-Cy!t!+N?#C)E zAfA-~WoedB+-x`W9rIRNQg3lBF$RJG7N5HHK+$Ac#vZ2r$o!&JMQ)lrhE4E!=>5xK zEe=DcGa9T2`=gZDy8cpsmo$cefJgli%R!^R%TA8^B7j2A+rfIP!9{SJk}(SJLOONm_MFQDSxRnc`#WS+hp<=licUJYFu5R zI?<)nT{ZUu2^Z}g1Z3sNy^!M*e9A7+T~wLp-}Bv?0CbTl+3=7)revc>+A+aGsa=}w zRCVczNJf>xM`bx;2$9b%Somun*!^BjibwqMYK@td`cQDMis{5~Oot_qf#G{&_-L69 z5AxszsKk%;Bhz_QYvT6_bt16=xAtXW4ZrB`fPHDEb~(8iL5V^1Q6vIR>te40jT^s9 zjE;sxdq>H@{pPHC@RFSSK-~nAE_%j575gW#$#-0do0_SSJ2&A~%E> zz>D4ay9=3U8!0Oe@_`{ba84fs`EjUctwptdL2J*q4XlxFJpAs$uyGrj66%@H8R4f* zPyr6-{~m|F~{XO%U;2ss%t>a;*aX6^1mHTvm6q`kauSz!>7SPz56>(-Ri$IQ$z zmMq>mSY~*%G8MipcLcBZ=k%8j8#3Ta>n$hwr5OMZC?}ibF{LL7R+veYY48xoFZK^Q zPIi}_IV45oEX&wC+XKln364o*3Mxn{MC#9C$I-2&yJ`d*vqpX)UUXZL8;N(_a#BoI zoQ4V&ZDVW5Xg=fQu8hXSigMNYyDaDcC2L_3AkWGWmo{?@P-W&ZtpsNF$T<6nZPS$! z^2IL&`2SRnZTK13*2RD|rliEdVF?Kb2+cd1T?Qq0@d6~(rUrjk&?+pWJh0AtHUHjT zZgSnK=!DgX=~_Q3Jl~{@VFe7v4AdS4B_^AbE z<*v?;E`6%ow^ti2x3&ZL-~zw0jl|Tn)1Er}zPr!ynyI?S2)uV@$bUO6C4Z+1J>M?R zL#XP287@^pU=$#wg7k-|hv8TagHr6$vRUa2T)xS?$7C48`9r~_v!A!q#`pQ~#S2I+ zQnRh6#@t4Z*Rg*$x@>B440Zhl@;-Qw@=4ztyk7=@PF|CTMDsw^Dnq)Mcy(EG0{Bmw z_3BMZvq;PPKvTc$QP*}`AcvluMl_$?N*J~=R0ra((cC0WitH1NyZ2`b+n7U%NrYMe z=uI<$*FI?gum_<&UtFX5R^s;Hs+PWLdat83_mSxN)&VH}7q2>n5vS!NQtT%sEz-gfv}4I3qD$Uz=8k-;L4G!bDqLv( z!2PRZ3w1z%k?lyq+*_c3i0A?4$v)MF;;E>}l5S*x2vh>>cJwhNGxw3d3aM_I%f^m@ z%J#YUMrd{Mc|k?TLUqCc-REaYFg>ZGzuBUaKLk8@021K19&L%uBAvktw0{%)0g0-- zJ3@Wy*D*HkreG&? zCIigpN|x)=ZLIbxU3C_(VJiyZYi65s@m%X}1}ae=W&DI1uZ+TC>tKMAV0v}gPsHo2 zwrZE&2A{p*S{GSysv(jdqg+{kQ1WU(1fheO!Y#BZ)@O$*t)&-xq9rUtN_0e{wW76# zKLpc+kc~%?Im_E5cHdjDN|AQGFaQ2wff5_QMGz{paX75D7>GnE-$>NDAL-x%HPwAB z7x-P1o#e02J=q0oj28JYP@C%sM4MS-<-J`Fmc#zRMhlqrW9GDbTUS^|MELg^WB0{Z z)SHEqb5-#Y^&bFkptQdk?2=lWd*ql;s7ZB~Nf9$oc=&*d5x`xmHnGt0Qp!r1edx%m zaey1b^rwLCYYJRkdH@`dyL!Xdaf!3zy7l9xf# z+V0Vj7uP)IhkDmKD>Xtau*XM~GxaoGh~gKUsx(0OHSZy|Ru#aBAVc5+OaT%}C;&*s z{CRRqqjKl)R=Hf8u$t9(UANBW+;?%Tm_B5eNQ%6>OF#88fU(DCX&VkWN=`}G zIK*UUlsKzs-mLx>&HO=w^($#-OaNprU+mtfr7+i(Qk-2t{!wXp_|b0GstC%#!K-{- z7}0~7K|Bb>i%>_WKDj;$OqdhEJo%8--^{K%FZnD6vi3`xNsX~2I-Z%fbsZVCdSmWT z)LgJg1hs+IvMz7+*`~ktBAFC+#u)Ym%bg zS8OJ6Y~|Nn&6Y!>DmK?B4MyT#4=KzX@1geFhluDbGkRA|=@@<~i3H}M=f|-!fW8gi zKG%(aVii*0{oKkRpvvHy8FX7aR(C{|ppBNZ=IDSp$UdVnWdoT3!eo|_R!b=}#4kpy zJPSxIx!9W&#{|D46^JS|RW|49a4fSUi@+-c*^WunR%)`=k32?!!dG*_izHODY*^Q- z>aw$WCsmoJie^;_n<Ut(@!&mU zE-j(j`bI@7>w@t^p_{~JjZJt@OfB*<`7d>PhHt{*A3s7rU%R@`C z5c>q>oAKiX#tH{sPCN4kU$Otk;u%ry~|qg+_A)JRa{rj$Fn_0;h4w#~{s zm&N0)gqKrhMvLls5)QsqO)icb1@4X13&iHW=&#Qs%eem76mzKKOSjzG=i`|80^C(% z27I!kBO*Y74*E2lWWH+b$wr+XJDow~h)$Vtp)o@ja#o`dK|Y~-kLc_zDiWm`e`N6V zTztePm@_J!gY;gclvd|a#V&P#5Kl9)M!iW%f1E+((W#dWpu_C}n{M z#b?9>TqU+%KM!;qRzY$Adi8wSL!m2+jvJgbDkz^TL5;B`8@N9%rytU3D0fPqg>ufzA$^t8yoviJa7>();jJyoBZF&Gr|A zSf%RjerqlYXQzm_(uuB#qqf!cO-uQ+G0K?uF2RYs%POe)ZuY08gS56T!1n7+)+<5C zbOM5#IPwc#N8DpJlMwH&MCNl|Ld-l~2-!!!*=OElheSvQ?^dCNb5afXB5)BFdpm}! zuy1tAlA!p;rf&``@y==}SusFjEAi#4ODJ(FYWt=QHQTJlsw(#B{zM+19bi*283_}` zmg_j%Aj&=;o2rs3lg5wtv{EZNBZz*E)DjW3L+vqh93|yU525A&W|oOY$zir=N4!}``XiGxlVkQK zk2Q>~5ed~C>v~tgqJ+0!ZQU-wSA?Fd5vKhi4HW3UU+*xE-2R*$Oe%olR@4+*wSt6T z>o^Ns&e7KQ>^_Q*yLmD!`fPRjwt&iM-Cs+F8<=`O;$}XqU&j zzoK_l$EeH>nxKgHp5DJ8pbW&zT}3k&&JazqJw<`G1bjzw>F34OMg>chuH`Zpvj!c2 zwF^D!i8gMzUUQXkEiZ^rM!vZISe0xN>{Avf$&DZzdLI8R8=!mE=H2iDZhcTJW6%!q zc<_tAvB!T?m+yutIfr`!nKEo@w@Y=qD!?N(mDraDD9fo1`P}S>M8ei67;GSnEWnn! zP0X<$+&AY&E17SQre(BOFbUE$-Y~dvs7#D{_`wp>vW;??R-+|trHwkZwq?&F-b`FX zu8E2_^gCO01@Z|KkI?+4&H2UL=k(XoTGsGMQT4|kxceBwAyXZTHRK?NaVs)f5b+j> zq81+x0}pZWPOgdG_LMmLhU?Y+!!`%2XkYu>JvPm9a9O*aP1-8ZLyf?cU06w*2e0oJ zUX$3aju@4Jr1m53s@1Pe0%{ip^N94~p?y3a)Q=v~qoJ#H5^uZ}!!CnF$MNN;FQLT`u zFa3T1!Mj}?GY!Q62QC=xw=**gY`+b0{{O1#HZC9eAL80QMf%R8hNNY7PA9 zfUR~z(=#O#BdL*UwiJAz=S#Ce)smVOO4HiZG;;ln%8;X>mGMVv-GY3 zW2H!dU!hOld8KjMY}jV~>`NMrJ}u4(Bb2#i_zy9U>BPRvYTnPe)SJ4ydP11Y|DcSd zb1@QL7RJ-f@_5=bGH7;iD5zn46E^a4c8CVNM<>d?t*PsW1PI5uYXHe0&_T8Am#?V1 zYk21Pp80KIRKe<=hk1plJimg;DCW{w0ZdKH>Yz!@ACw}wyLmr;Wa%*D31o~6XrhNa zruHs@tM$$^?xi)Q&0&}rJyIpDPB<-(WjyyR*JZF| z3YxIum@2Lz&&1jK20+bBI!Bc`R0xIV(dsY^51@J3OSYEM9$m-uk}0?gdL`sNOsvst zMxTb>%W^lT$8lwv?+kxY-Nh&OiNd6E^6$5h@Qxqwp!>Pph1o$AuKp{pnRDkhP@eGF zmK@+mEuvrD)lJ6Zc8#PD4dyBK82P;>#i8Bl0*0uE%XQMTD@R7Gr;-9IOjce$ZuP{J z52Y^d-&FRVA2EIqKLfhML7cgVOGp=d@fFpC=b~kgPWE$PGCMI+p>zxFD}dV^C#bQgEsDE-bI2WJ)-TUSoEJ(@PU zg?b($CVws+#wX`^>;t#u6jb?!m+mCG>+I2JvmHbhfU4=Q{xC*Pz7u!aw8SL9 z=1-=1x~pV5SAEkGAWAV?2WW-<8hmNfn;JOs+&_Wu(uY`oF@Rm{YaD;+OK{aC6S%Wh z(Kx)d{}%tVY%v$01b=Bg9k&bT7mv%yA*Ao?fV#6`26VO4Ml?)MyEvKuUF4U8A*P?s z%9J$>KW}V}jK5P>8|Shaq{y!?4zLFLzrJU;*|p6aA3irok2MnD2U^OT^Y44t#7nf0 zD$A#s(PDlC@O|gwclSslbT(Rzfq(Ca3+yIJFbT$=%dy`od3g0_JPPazLxoAOc$8Hu zxc9z=?3bX%8=6|i`bxv+v4s3MMKr(9KSlXnM0XNm=@tr6tL~*5xA%Zp*>%E5N6N^F ze$6=IV!l?9h5VBk&vdrK!><#f?vEbU_(eSM>Kl0YvBAMNbwTgx_%b;DhMo8r^Fn=& z?wj4lU3DM_xW|h9gqDpj=GN`eNdYnW!DfiDkf?AJ+t90SV+E7vd0@e+N#O1U1I~!8 zqStZ#>mkGejQe6-S4w?rI|tQ-C2bQk1vH2)8oWV%?U-wNpY7Vn{ntOBEv?)0r@0?~ z+tdtXQNJL%#W0<2TkdG`!BR@Hs_7_0kSL3qZjb80(%vcA)+5fI-cg)YDF^`mB0zrN zhurimG)+t%Mk7d%3Q;pL4{e;RGvE^2d}5z0G3BRR0BDQ<_;gQ{ql@t?v9umsQ|7t* z%{*-N+5f}UTgO!ubnV}CND0!ZbR*J@bc3Wcf^2B$G=Xl@G z^Lsw;e}{AS-e=D2*)wZ>=eibNtsy*iR*{XmBbd>dCWVMG$3Gi?HI^_o4r;rG<^FOT zf+GJ6C{&1}@m~*U#Qdr@qMJ!ki;rhCq%C*-@Qve%*~LwTQob=t&$3;6`diMRRE6x* zGr$bILue#h((wg5Vzkg;>HY?5w?D;<_qqM%O`@e2`h5l$klZ_wwu|NuhGM?}J9K|s z^MSqKdPTKMXrTa=v9cBU+A%dX+Jfyrc?^ge_Ntq~+UdyhYLa{UF7JIiU?(&6@Ei*3 zzUrlwq5e1$`hnA=3Z~DJwowu;*9~{Ol;PACy5pbIANF=hYkk^n=wWY_NSkZ`mEcyqJIH zpmUPvtNCNM8TJ!{q#OVUgA_Dp^!a6FEELC}UPhQaL)`gAgR1w}L^9yv-K>aZx(mK; z@TiQno~VG^!zl}^xTTw7rQu0X<3E8^m%0r5vi^_XurQxH!Aa)?%`F&mEsO+;K2S4c z!`kaU((^e{Um>;G-nh4%e-e8(gOY;~wV^8|6D_K%Z$@EHFY$u{HI&T;h|? ziIV|KbFWmt$PvVwR{p5^-C0a4tuU+4g@LRPF66rYd>#r79RJTw`o%w%a2^-5TZ!^w z(GK2h7y0Y#%v&HU;J;rCNnD8{yH&O|sg<}IL!o*Y^WywhAEF`qvt+!6I{f<*i-Di) zT`}@p4sfl@xPu9tiFxV_>=+Hjm2#O(#{;7TB~8uo)AKO}xlJ@(ko4o) zcrp5Zve5J{R@M1YRp2{;)d?@gQI3|%I&^S5K^(%>-N|#pT%{Z@No1f4y0kSYMnkIj zxHQhR^y_UqaNPZ44LZb}Y=q=}ehBnN2DwucS=#X?`0!K7BgSy+U=jb$BgKQ@i?-9- zUB9H4cEeTgiuP!wP%R+n^yHH33pVIms;_RWmnpo5_a6IiWb7hvSDDr5~%7fDG9 z678;r`~#K28;W))zBAO!P-bwuR1M67P`I%2uIq2tINom`ge*%H!^*B1r5D!3rtKfD zz4HAiiiUc*8)3oVBmwdB~~H+fT0Su=t%;ce*BEOv378%tL~{=?F4?2$Bt4Q zk<#Xw?5-Q=2EB5`CSReJacvJ@X-wEekaa9En4Eoz(G~NrI&<-n;n6}F%*}twn%93T zKd6|`kd*^)!RwfjN~+yhu%R_c*+pEy`VM~Vc?=uYdiWzoQFA`|npN!lM4%rMDZT*p z?AkhbEkfd5#xs~%`m3A0?-FPkxN?1!hd1fW^`ptUmj4e@DjPqXO-? z`XoP0%yo4A;vyumT_jhj%vDx5c47et?nwP6q-VIZ8HZwFC87z%y#c$MLiI%332}asT{rP`o|&az`B;B_o3^I;vAn;%){aTay@LBbuZzv& zd&WSh9^m$rp!ziT=v>vb_1pxaz0~x#pKp_vfwoR-4<|$!pU?t zM9y8MNnU2!*U)-<-r@?;{;D)w0yhU=uo=kSQ@#0{)GTr1Od4>Tf)lkrQ7~bBk{n5; zlDrou9iHqXIAg{n)TCZqEiG5bMi8~m4kW}SMaMD3*rkgVnhfnohMO*+#6{(FcNf-r zn8E`R%R8%s*f%pVK^kJ|QhN-vMp5}Ir`!CdkNS>upB~fojT~O&waBM9oU1HU?%O0m>xK z3`FN5ZljFZ1OD^-zrTkQ_*wC{J`yH<2a~>9w~C*_`g@q18uKW&invykgiqGXm;YCI zuzY3yw^r8J8pt!c*7(AAxfop%@bY-dpgQriJ;7qS_+@2S?OZb? znqXBECaewmuNJ(YSF-}aYrg7FJ-dKReHl#{Km+luo&bnj{3x$}mr;Y}NWc?JvJ$lC zmLC~zzj?G;^fdkIbp?UgEUvLKiJ1*iF9{Ri7UQ_BUa*M^+8Iq5+kdtQq5Ep@E0|%$Kz` zRq&+pvtyC~Y?MHQx>DCBN#Y6&*%lkLIE@YUIu1Qeux7l3dfb`v-Hjvy2<+6|p_NsX zVlp?6H*J2xxIAec+?Q8>Z2mHMP;k%;V1_(1na%lmgSE^<221o;8>f{I$N3?XjC>O- zG=q5vNf9ZCdq2L`xyIqXde6Q4`m=GwA|Brm33;zeN4UBmcpcq9od5FpLA3cnZUHpEL zRe#D_uG^x&3(oq|bF*jPVCoIG5+&rMG>rSTa8B`{DF${AulDd~^6i&6sMM&&t+ny+ z;RADwZ&BsKLmXq}P_ym{^D9O5`k7kI-R7exP5!DqVZ8d$cNGzUGmL_*wY{=_js@6G z^0gFOvduTGM>1P!_-!BP^QT0>J*8DPI^^v)N=JY7A031EQfP6FZ*7r;qBc94bOO-;3D1aR$ z?fS&mMP!1nU?18ANZs!~YZbTz3?4vksAlVopx9&k+~i9q@zy5w3i8FJp=m8tuI{=$ z)n$+p;FzL+h3TdB4W;<>;P}WlyplNzWPAA@bI@^nG% zAiK7zHKl6RVc5HUSD`cmGC4q$sruESa&|u7Bs}1~l@{%O?-Ok}4$b2ROkLZXUl!IG zEk!g2Gn0efZ=rLLka^IA+|RZezL{W3%&9{vK2jg;5paKdI;MPczav`hr2>nve=JrBYU_4E#o|y>=^; z1)S;h{ciroEfw3jl^)?)xXNb%sO{OZ?>J?}$acs+9W4oXu4)bJ_<61vXE7P#v}#no z8B-fr_U2ga`Yh6$^JL*(WWe~?Xk(Aw!{6R4NQdN#s(!v8E_m$480t%`gBcm{dkWlr_zU0$QXva+I9) z5)Uq__)(2|@YhZQajL3-p+LFF2{$Las+GUQCZ@6?u2#1_kAb|2vq*^w8L7IiA^ zHL1uN;*ZL*+UNr1~Rycx1&oY6R~En9h> zgb$79;eN8~E|R1!EY3d8z|Xz&EOC8&3^*{@DzGxjYw4jdGtSt~=5b5#p+VbQ3xtOFCp{BgoN(hZL4%vn}G`qZ%KK?Dj4%r+rxA6iv$n#5bu zR-P4!yq5bXumOWwGB`MOp`wr&@HGO6B~l8(AH!RYB~V`g2Zb`(zQ%OrE4mwTUKeYzKS-JMy%8surjuxXz zL8ELbZMNdP+=jZ@tn38fF)LbpiE~l)15h->R|xj+N~i9`;{6sc9E}Z`#1%IAh)pu% z+t|1h44LTirSq(nKivO2huU}{Zl{wM3Obh=%9Et^gNue^XiI@ze>#5-Dl{-{+ zS^O=5UnF0(oa5)7gU0muM1V+PJ@W7?<&DaiteW3EsdO5{m;RD;la!_RCcv-&=OAr4 zV%eXO=)VN!%uPlmw2YeVf@i|MZ+z*t_e`SvHOL--(fc(*dnF21NoMpUbBKf9zVr}< zS19w??Z~Fhbv|Jv2r4Vit_XS6F5~m10k^q8u^Xzf4qypbsjppQu=qv_!rN^2%UxIqBCyox#azLDy}HZXQfA@ho{7wC`F4|iDS57+}UrG(@04#kMQq}*^&Ug(GO$``#VFkSZn)R zWj^L927#iqOyR8>6yVU;?q8V7c2960k5BmCCqwDY=$>n*NEVaIOf!h)GBXV-j=MJ~dAh;5k*d zgg$Zoz~e}ahPaj+sXT>Z%L(8;M@>xD233E7?V;YqFXpBTLx=A_dz&1oV!>TXE7uR#pmQ5FeSwlde40i&!ns06t$9ig&I{ zF2=o-(0q@2ZToYtI!Kr zO7>)!V%#gwo=9`Uf0hMxuQwIdfiZ}m8Oc`Wc7&`FWlff_GrV8=3tO`EszES9gQlRH z8`kg#Cyq4D~M~n-W_5W6~8as|^wsLuWGX46~R>N3~@+eXMK;{oZT_Gx~ zy)XIl(A{uUt0B|IpX`#v*xEG-r2T1q_PxKFfA=aof7p6@G8VPW?d)5OHkf)+xt~z+ zaqO?rI;nn~Io7X zaeDF9SH0F$+faX=^iOzNc4&i(ab<9L1cDDvf{w>cd`yxMVI*WL0k>o_F8M}v%e-tO<||0JK+*y@Au^* zp-nDh!uvS+Gpka4ZM|d28C28pmh+hjqjc%6Pz2;i&l=*TxD<%jJkwWwtabUe0{B1b z)Ao!j9b4ytp|29tWj6-T?s~2Ie^n?@p~`0W`;R$UUnJn)clBo-(>JR9Tl?6 z> zN)YhhG?{ZKzMM_E;h2e836G*sW&lj5t{RBt!N)Nn;|KCRsPDMuSL%ns6B4@j@WDH3 zuancUT=PbiFsP(Q5O9fig8Zk=&PH6SYVBYy@L?Wq&Q^z~t11m4z9$9~c8mUQ7jf)O zs@U;4aG*rGnC^024_r3?SFQq;sL0F7o2r<^Bq$knP)7Hr<*Ory5y4 zf8&nXs|J%W;%t3jxBk|4K1^xY4#R zuWnF2LsJFl@3(z)Jart(!JP4gpD0+KH3P<+urLKL^+yFgm3e}9-l(eUoGH~6i&SPW z4z#mktU+Gkye$B0>dO!@zLq61devpV9!AL(R;n+IUPf9gC1+f%cR!&8`FNN^fx@Wa zDIV}reUt|n+5;N6xOOjBxA9;AD5T~BkKx~3U{7#bZKdHOycWCuRJhd!?x=o4xXBv; z)Z(B-X<6@Oo^R>)_dpsx-lW;x*7upFXp1jyXP`=_*Myzuoz1RTqnOaMItU6s(XUz& zg#wXXfw%4stdt+U*~!wmA3_CeLyQouT=&_+H_GC0bo~EfrnJf}6J4Y7F>Iaxlo?Pr zXav=98Ib;3hE4?7BbpsLWhNvI;^%vnE{>d$!Y1%L?59e;~!XFG1_L z0{J!8)Xj+8=#vi@$MA^VUs?9yL1x!gYJ3v{0Mj~JJWfUlNe<#&vjBP)xowt*(W(t|#F$yljNeC-9*Tn0@96o$_s zve@!X*e3kVZtMZyAt)_Mq;0z<@S{OI0B7uAP=Y~D1i`;ZrPQLeSTUeLu>8s-nxZn2z9WGvN zz|O*%4JcG*kLY}}EgYi}Thx%cXD3Mc2@+>5H)gh-cs|11Cdc6)0NKh4DeoI90dSvl9NSat#+;&_WE1!4b$)cNN0FVJnCpQHyCM>FdKFw{^>*} zdFK~-?!bN*;Yx7F2ilJM=_WINQT-sNgysMs?{HN2mElhwvr*vsTMFk#gi}iz(=WvG ze$&Ns-PJhyBs9+nu&Rbg(`VpTL@K|$s#}dqWo~pCEg5gB`L1bc{R8`8{Y@)_xSyr_ zs}fYV$>=T~TQY5{_|1lE3ySuWWum5%VMV8v!MEY3LOWBCqoae?^{`)l&i@25_uHpk zbHHSq@rKV0ZHB@dF;h5^d?t?*1TCNiRtW_LI7$64Q~-%TKQ|~N^ndse@>S1lHhuCU)pX=r`O?xC=L#L}%M#Uphc8kZk#b|owGj>o zXO9m}RuhGbsHo9qRkTnbaQC4nETr?@04yapP z9X@Rcm{rx_K})BL)9{%y08YX)KS~~#ho>Lns4KwV96a=`YANiET)Nz6rBGE4nxMi5 zbS7jPTErY4cAk)TlAwA5U^|wR&o=IJS_k8s_F@Eia88q`>tqeXNhbrR^E3^k|65^! zS6&b7vc_TscDruma+k8?ty45oRclI)?@I;6AK&{UcIJGb6GStN=bK9FTGG6yJ zx5$B0X-QJ4e3g|i#Xy(UQUozk5fn~Ai!qdWeOkU=Z}Vc>?@%31-G&j4M@7mn+P94Z z(xxTsJ{Fei_*17cM*;aOgef;sP=*0;sT!y*iNsS8U4Zmj8x%O}kh@wdHA&io8g;ss z0%N8LFKScvR!Zih8HLUV*5|LR_KD;JuRg))j=GoN1N%h)g2jxM+dtuyW)1h*iBXY7un0u1=sNXcLz^>aX zl$?@>+9sO(<1)cO6}JMk*qxe@Ce|GQY9&L@(kNDN3Q9}{Ji*qBWjAqUN)7$ASNd2Z z6j+%dWGJI)Y8nmXOpF=^kAjSrc^EY;9U@&INU82;e^Fy!yS3$k=s=idcD5q>EhOW3 zBs}WZ3r!B~=?kGo4f@UB6_Oapr)gz>kmlWt-jrp1-fPkLQHd018^-U@*%lSGMo!$G zdqMv+en3_-5y(ZZK8&b_yR?4=zb9tD8|8nCNt*0_E+eF}Z$C`o$x(f)QDUpkhAA|a z$9T)kH2rRpnzl|2Qv!sqDU8}tK?F^$*dF4g@yg1>ulVjWNAHi^mLHC;t`@Az+h^zR znvb`eEBD)5DtxVl_JzyZuXL`?AAFCyN4MT4ht`V#hUQKHL(}~O>z1F7gaq*b2hOl! zh6UcBF2jP=&ZzZaiLy*OKEMzng-^oTtdlP_s!7DAqui!2sc>X{?$J#cRvl|B#H5ao4eB| zHj^mY8B}0PR?Y~+EBpw|fM?-yTM(GOt`Jgt?ikaI^`tqzGCteCghUWa(ZX;_OXB7z zbCkmURH7n(CDu*w85uEVCFsv~Eymh$_X?@^N4f~w5k)75f?zC_=b|pJ3EFk;(9@Fl zhGZ%$>qShnrPs`-VfQoxsT6Ym^w|}%9PEf=Tt>n9)VmV7+mxIh$=;bax`wjd7wI25 z-R}f@t@Q?^&TU_BDe3hnJG0$RB{|{TD%izw2oo6W6zxO_y|VQ5u`VbY zt%iwC#A->RHUhYBB6pRhAsnm0W<13GSuf&4Y~-?j%F%7E54-g5q4Y4p9-H@8&2ON) zr+RcULY|@7`I*C)nkG9gTo!(G`WTGZ$QDchS#p;EP3Y(;BoPWF5LMRv(0y3C84j+9 z7xiZK!6IEfH4Rpov-MO)(86fL@yR25E^2#UTz30&!JKR}^&1lr!SMsB$*@brt{{So z^o48)jzCH+*8ajDzVgsp!IBlj^vS=121svHnP`^!a?dYw9v{U*l}Gh`yRNUVM9Q|*R#VOgGBm`y_3M**;h$;g?jp% z$JN-O;JhxzrB8zN_V!LK4`j*0DhrRD%sPf|Yj8c^o%JDu8eHQ5pS#;y%|3c`&M-s% zDgj|>Bv(Jdnb2c)Td0JGq=cb6boWo3!+b8RiP=TJD|vcm{OEBNyJTe8g;fy($ClYx zbE`o-l_i@j3QPCGl~q@OA=|D(B`o^Em059*x$dmjsys%6cQ7JP;bm{!?+=^#)48z7 z!lF1P;iW8@0qbf}Z-2OxB@Uj`clOT~BO0*7lUus3cc+=W7#k^fG2n*Rm4UOwTpB4o z9ddCY*6{i|3$wL)rL2~I{NdKzsY&lR8OFJ5>iP0tui94-90RFTPMXv9IHh7Iw`9c^ z&sr6@n{?H1IGzOH(sc{9ckezPZRaeu%1yA)Jea-1yl~K8?dn1~Ui_hhoy^NO{SAo< z>upB)_du-*^7D1uR?b`(Eff!5mh-? z5rWf{-L1-P#b4FcSz9~sEoT%lto?gnG8-kC9Rxcp#-s5Wnn6dPKmlis}MVb zn7ASgxt^^H(hQdsCYI>(zm{g1gYl`p!WH%uylnqFX*iQADlSWjbt#q5z!*E1k z*pBl_#oX=n&+@C5i``B)&}de|j{&SbhqrPnpfUEKF=y9uvYa~Kko9dD;3Mw>N=M~I z5#TWu8gU?E_4()$Uf0N&-sL_?)=^sKFXw-VD5{RZ2p(wa4v5x;KFJJEbR3OaU_He` z@_nX%?{l^$uEk6-AC~r%{n+_?Nb|Na!sy>e@Ckf%#yhUq2hVe}UV6N){X3TcL+)dI z+Zz@y<68Xd^K(5e24i7=_#ZzcA6n;emR=Dn5c8gF^#qi1B~gCQV`6q677(x!rW_l% zy~q555+5zbv|l=ceU2ThIinSI6<=3PeTMTZ2Yg)8E0RYXv`E7idjQG%(bSPAGD57L zsfhY{!@yc4TKi#O_r5okpAjWsf`!o$A>zlV@*Z~q=tA9tRB{q8!EV%iK8IM zQk&?|U?iIl6RR64q8|V2UG1Rj+kps(cU&de{GnQ%cK+A+F!2CwL!x>DMAd7-|3C*vu# z@lq?S95-M~pn%{kMlR=rK~5s&*gc1J+kIOXWhZO6!=%w>zEF)%7i5DXjk`}f6^EwV z(Oo;1_(*aVEZekRUFf$=7Tx zAf9wL@lM*kH`8MBK}t^m7huGjU)NN zG=cioZTMDc?GQg)sg|U&S36|&uC7sRc-0=2vvvHUO?|@fK%muW-s}c0jDJ3lA^d5Y>WbaZl%0=k>m~70(y`aC4!!J2*4pZDEpri(QI|0RJA2JU1C`se+m* zJPDy46VIz)KGAGNR^>{XD3QwXK%qLQ;=7MNq^U-mdjkUo!K}}-T&KN7 z4D=W`90t;>U6nO=f>u#HJe(xXtvKc2*d3Rbu_;zs5Ft)fxTDMrAvY9qcgw!aaYutu z35rUJwjLV;j(3PH`r)n>)YMqQD>T=ZsW{`(;MuA|z!X-IHKWU1Oo;6AlcxEGopSI- zx(B*1etjK^W{+hr_*4X(R`I@3EU)4HUt0FSX1eyae*t5EhLn=c7g z<9yd+`!mTu)mv2$skkZQ&BwwtbEF2`$ujNN*$tmpci~Ugb0gt_HZ(+07igZL&(9ZF z$|byk4vN@9ys`Xc<%wuTS(L}CzN*zYP}Fm|Vf;h4bMeieC$W=JD>ZilY8tPp&J+dg zVYzf#4uf9kVV17BF42PJ{$>dDsG$FC8f%s`##*q;)xoW2VUu%NW_Xv_K~dFbQI{xF zOTxCBM~LWjo_Kfh0c1&?WIMiFItkUHCXW}tB4l zGLSYoHTid%%G-mg$HB<`4QKx9#fj@?zMFb_!&OUh7DRH=Uvl8lD)!|hNous<#%-yPJ$5Vn;H# z9uSK+mV%2e5b>ipzeRxm8{mqEGHq4LakBoI{H^@vT0x#O7;Df8C=~0ByU2;ru~csy z?clp*_}#fDnX1)ZlEw^dPY`Q~;=VtjE$;C)d{SgN(IMjPGdO$X61B^kJv}*ZhpE0G z5~F_akUz)bXI^Hq!@b(QTz+Bc8o$?THpW5YGwl(}G;FsC*D+V8{y6Lr^{I2>Ac<{_ zwfI%{=hr>(^RjUFtj03W9M|x;&Hb^<57Ae$lG@BDT+x2f= zC+awac-;H@B#C^>w&!bdPVD|O7jS8nXvWj**cH$&Zb7Are0SgG5{P)^@h4-pWrAMX zn@vNP1Ac+1#$R7^R1?UX#q1;tLX9X z3+L*Yo{ciAOxevGog74$OdiZ9em2La4Sp;1*KFK*J7hp)M^YF|y23etHcx^RJ1!fi z-Acc*YBiHO|F;)OrS%SocUPF0zoAmnlhy94*g7J$eGmMzoU=AkAzt54w0d{+T6Te7 zUOj)#h3T7Cf_eU2eWerAdxyKtEmvx_gWnIK+`8Z-Q8xpviPAKhl8J+P0(57(?~CXf zCm$2dX3KT_6va6-4}>_mXegttcau8oq>oK&f4(JiuTG}M9YTsT*!%Sd@opCz-jz;NyCbQC z<~O=At4H4q7g`;@5#>l4Jyl#F>o@^lV%LJ>4k7JnzYhAm7GS4U?t?@OGTvm!VC+i5 zPXy+IbPeld*fYdm*WrhlhS%>zr#e|sTl4`XFft~@RWDK`x8b=X1T*F~ZQV1R{d zXWUD^rPOjB-80)iBGa~LdDN7g#Mt-AQs8?^D|6sO)EJ;!wFkz_O(2nD|8sNt-%+!@}THrh$V@`SgFe>Odq^svOq8PJEE3REjO&{ zYYxp~#B)SQZTk;b6UWDnem=jfU#hDo`{T}P%mHSEaPP*3kbaMmKG|AF6)Po(h- zvrB;U--)7=(W7V5Qp)-j4Bb!SL-ts*i-glH=Kf^zO#}2d+g2wdH!6y0M5(3cDc3F; zd5Wqgfn*TUs5>k~Pb{)+((_Q|wi9@cJMjWc17#eQ)q4Jsl-tx?T?Hl&@t#Z*$IFEa zRUhR$`)IzQB|2AD5woatJ)cdU8zK$1j|FF0tD^}adVcYTW#a{4@9Q!`i^poSaPffPVTR z`6&$hdT6TtMd~QNRkeB0o8}1vGe;${%?c&M^mKHV9(a`z&F0cct!=3pZ{*;wwcWW= z+aIse(uaQYVCg8kNNm{fjDav7j*tt=^43|#ZGtHE!4PP(5NhWbUlC#3{bu)PI#_5p@${43-Z-)_dpuqJkpO?; z;>hEQJm+EZU9SF;$losVneZ(?;CDw)=278`KEuPE5mC#ZJCsJY^5@b`FohI<-3Iz3 zd4v>!hvemlEQUaIy&tq(G=SXQA;ge-CXxTI7QiDVeBs`)WpdjW-}ifZr}vT8rB}CU zvkFnD+IF@Z8zxrNSk>6>3RJdbh{9<(0Mq2r=yLddlt)OJ>H+{g3c%n9AVMF=D)ty;B9)Op9% zu<*u72+Ha5?^=4aBrCh)*$q(Lnd-`*Xil`2Bqry3VCp`<=Vt)dibnsjKLZQb+;`LA zvP)Kx_{dYFJ-@wbDCRFXc2J=BF2Ib3Tr%DGx8CeH745_U1cGxd~i!A=y_1a540>gug|ChA`|hGXlK(e06~%D9JnT}?96KO zXtALht|(@BTh$eC{Iw_laG&7}@!B)6*sIeXb~fX09QZY?L3c9qayYrYg2WE1y7Dap zx#v!2HzUP8kyv>n-gIgrN4OnZ4ad5=7v z>9sVUwrqRv8n7aJdjv(&$zI@WL`K+{s8t=Yuk%Onr^|YCW+>?o=oW4Mzd|hAltRFHa8NG6;(aj=j8+K)NAX^Bia$Yb0XuJ!G&e) zKFB$qb~ftv6Pesv%-rNe=1hMswm4M$bndV~1#l}{S3qO(#kvRe&>t>x=H1P>tOXA) zvnDD|-?mv8q)QTnS@(*~B+CLzn<>Ud))c2hmcMmccA6@hM9kP?*t7kAX`GeZW z5a&PSuAsQbLe@X-#3n|G%WOSnyASkU7T$1$tyx6}5$&dt(WH zn>XUFgVPGfQdg_S`;!5@ydM}6xV~9#t@AG*JI|q^w0LFE+Sy~qto~0&JaG6-`{aQX zbP#S@AApdZc>005S{(hXcpgG@ojC4Pf%28`;--QP^`|ZPo)ZO-qEq#8_wWTZm4knq zUJexemU5iW)NyqJ0DIG5aX%#C18B}+bu!9cf2Y^Ayee1dUfL?@(j`W$5?=BmFvnmk~-*1b)nDQi$2?7f|K5t_$;gS{sh8f^wS##x#KrN1F6ph-Jnj*{f<1UG*w218l?(YK@=Ya4fRjm6E{d1vN%;xN4l^%yg7x75e`hA&32 z+{W!3Bj|{S?b;NQ<=1Y|Sb|kCY%$z#uCO5fGQ`hn6X?NboCM3#c%x8cD8MsWXesB= zJuw|f0U@`$YCQeogU8nBLiydOb))ICC3Vr{F{@=GYdEUw0f9}AkeV!|P68CTg)mSj z6FoR2xi|_ZGrZ`U?~JdFhrhiHqgJ`I0kuA$A1Q*Fsz5 zW_uFF_zw?(3@-%j7fYt#&d6Z;e9Ic3(-pWHpxO%KRXMV#9YrISRz4FjBF*IlgNp)2z&X-7NtomAx z*fh#hgw2M6p{$1{s>V)8hvm1BgR<;P$*cuQo(zHC=ySB5S%W`AHJ6ZzyFC?QtDmGM zSg0Qw{NGE9-%FI7>PFfT2wlYF6H~D4u8+f$Mc7*nw`fCfx&lS0itCb7I_E1)7_Qz+ zxGWaT`;6@H9lTF40szj?{1(nZOTLPZuo<3|2cGrRR_~x|N13V)0s&2_mXN zfdytoR8~!h&PiQP6%Q^7;VbK7VKH5zxzAP9V8?LQRPSlqKc$12IdMXO|iE_!d- zl4wLAH?NJu_9_3pW!0)GSYxm3|NDZ=KpH0}c_07m*DraJsk8+=oa2($Pr5i^8*BtP zZctXI+QBe=O%P`RuF@;duRr0Z@Zz9M5w$}5i_Qs|?OIRLB=!6aA5Af0@-rvDj(0?> zU6PG2qH4~vcLJh+Y2mP-dXh0-78xa*bp<@po=#L^l3(jb{R3;T$Vh)&@ZEc|>aF`cVh{QAl^YQQ0L2a$V<(iv~ZHD8;dE-$5U?(cYlD9yJedUb9plJv|V znsn@c!;f?NTQuv*YM*A!%a<4{wLPV)G`YO@+)9OEXmwYBfdw;#$#nq9G>Lu35G%knj zU|NDsB4!9_==0&EY2s$=c5o9BDW^)wL{7^@Zqw(ua+0~il;e{6hV1SjQJVWASv?S2 z)yw@SoUX#kYpW3+_+DOyIvidA*v7u|m5dR;;C3*Q`shnz0ZQ7-5V4s$V+vbPNM(U9 zn|77O2rneb=d}9wQTe8#t>9M3JG3-z&Xo@)Ds#9D(>XCMreBsY<1QijT|QhiDyUlp zt-Z`6AH7-ityVQe`hWL&G`S{b(^x1;)Lsz@Z9j~|?Gq}0eE*uwl0Nl#iJfj7GX@|x zR4O{z7N@!#mq6iudSz0HZU1ehX;}f44!S#l`j zDZYK&4H=s%q<+xvlDKD|_7IYtMT#u9mJyM2+3Ll1cc5NhHq9AGi4BCj!C&pNz)X9+ zdDEP6DJL6t4mxEi!_wqUCLS~Pl`>aI3oJ&lv-mIaVMOO)N}>yX--sJ#EnOV(MV*QK z?zq!A_3h+^yAM@k{GsQt3!FDb+Z^Y@0p_fnWujA~AAPt%f02m~fo2 z$WJiHQWR#nM6><7&c?7u7)urNsz_P94l8~_Dq>-X>2G#ir}KUsc8E}_yvdqy-klL|6l3M4=b&?Gf+8z)NO&Z9yhYRGd14JrKIfNe|MlT{QcDvZC|nNlbP zg@pd_hK4vhfhb6OLHZ#0nNE5GvlgDt2yh$v6tcPzct_KGl`Vwv+S)%)v@$58R>S}O zE&3l0sXs9K&$~r^R7|+bN?A--MPID>Dt`Senv9s(pU{(1eq4ZnDftd9x+0!^aF}C~ zbY7cPu&cSVR=;rttEyp}hN?U8X^_~m(3753-RH5RyJF_^|SC z*MkSa@%z)#&Nw>Oi4}Hnkk5>9jQV9W^$Hpd+8zBWSU@V;x!C*!@yc8HCXQ}CPH2f=SL8|^+{7%OKb{{;b8+j|D zI4_RA_57L6`NDjq9pj{yZb-j-4w~It|IN7fW8(IO$1so>6e0MjPbB&93-s3Kh+2mz z&5hdpTpJDT7=zh&@*}6(-GH5muA>ICnaIuduz?ZPm&adFb-^qL^K}MoLP(NCeIf@8 z>9xqgtM4WmDwk5!=z#G+-y%FrVLRxzGA17XcJ6xoiJ*u=rtzV3|+RVKiwCLjZCS z0+vsdYixr6<62XaYpk*#u9zgJqp;8ej+02d8PP==+P1H0Ccy;?n(%xN?kS~aIUhU< z-rN4QSA7ve3~wpiYCeS-cpKzp0=x&l@ge#Y1+9X!)z0NnOIAzHQ@n8!-;;AET3J?% zz8&aO#=6C(a1UYGG%a1180#w(X|*dsnJ- zL5NOl%RS^H3)0&EA6@6YdhJTIQ(xL$b3W1V16&l=j)JoYHgz( zA4xt;jnE<*&9m7%B6CK=G9AGQ?TqEN`kE0_=-cpip1cT~@3xGu;T>)`r8aEHPzSuQ z^`@{BJ+L`v?l-2`C(nataAL-V2CTW5Rs;0_)tPIhz4_CI#cO=AkGoukT4VL^7OlZB zMBc*_yaW!Fd`JBf@XB84Q-#l}kE^$p)@(9d^yoqo7sjSdTM1asoZ-YvmlyvyV5>jL z%5i%yS3m+4jul{Y&OP_S$(wI+{rjd$Ord!kbnxyI0UtT&&`!Biit#Q-25#p@u7a*e zRqd;fAWX(-XJ5pnMJ~4CGCdpV0~QR)hP^(A4rDVTkS9zXcw*s;`-W0~>xdU-n}7~i z55v14o!MD;S@nt(ZSBl^lBZGZiK>F(<2;OeZgQ%&=7P$`>1EHER{)^kV8Y5fD!fS@ zYZ_So6stjr=;}6+oxVy~*hKG?pNH75%0h<|F4+Y?q}%T*{sI>@zZeSAZ>@huq+Iuo z@i_#!8TZpjoHrX(?4>&>8m{`3GlwT!d2*f*Y+iOLyb(6yt8flqsLMYU={EE;OMC++ zU}B{DJGZ(HKs76A#}wOt)~T^#7B-Inf7!yFUy5C(-x;`if98*#Y0-VAqp`f6d-X_r zy*k2hB8wvl2U!!v*Z#i;>dVdTmwWA(D;|tSnd3uuTB^_FTe(Vp;7oJc+3hryKA?F; znf)hys{>!OD}EZe^xT+sUgJ5Q_*l2*VB&(Y)Q=_%-c^U1dpoj|53l`Gc7DK>eonRZ zdP}C`)FI_*Et~OuyVw=VaXd7W6Q&6X0xHcU5_WSeIEXsgu$+>Dpf$^FfxB!oeeiHh^Z>%Jt8=dslQk~gF?+v;+8`Kc+# z*~i?Bm{-T4N-jLDm=g$ls3dp-+MBZ}eaIZ#GlLV|Q>au{?2&oz@)TJ!D)1X!J zzzu)NUsmMlRROpsd7tq|=LcBP4I}#n=YOTZ?UxT`C(bg2rB-e9lI8?fheAst;!Rv5 zcqp%amv?3SR;{pihDBP&l|8J73AalY_RbXrUpYMrxWN`d)3VsDb|rmi_Zsct``N#> zpKcI@6#_fIxU^^pUI&c;9yfJ;iI?8$b8MTF4NYn18 zjo?}AMcuuJ$SM@QcYfbmt{~U&mL>Q=uScC}I{VefE~1Z^@k7F!kVl#OI%cTZ8KxNT znA>U2b;Znn{N~F2hUqn8aR~U?ab1!xG(w-w`(i^)&pk7sO99ElGA8Py&a}_mP0eGX zKuA2|e(Gn)G3aM1ZD!Oo^H~u?rYiHsYi7QQ(xpvsI8WQB>1kc0jD|tmoW%oIDwOq< z4{@C|08ZHO$QWZBo8qGD0pF}aDWje!~*~=jFL)D2f`@t5vN&a070f4Ci`B&XZ*Pr{%-2b2mrbZ-M~_a;=wB9A9|_ zkmgP1OF!iSv29_h)gm#v$r>O$fp6gd8`Zz019J-Ov$LTO)NWY5g3_Urequ2$!>f(xyjOyn)hb@roLIfiZ zR?kwdy^V?v-a!Pc0z9SH<+@hO#CN<;zw=6O@s4pts~fG(N!A>ig>B~b%Yq?5gs5ls zRCpe$7T!qtNrF+cu->P&;XPP@&03$00YM}0F?8IOIwxf?!55yE26~UH_k6ZT1^71i zN}y!L$QL1HU*(gZ?!Uu{6uf!Ccu{&g1Mm{I4YoUo)SjsspJiVV&h){Ul3eg!strCSS3Q42Ni3{S)Fwt`-=W;KB3!> z2W~l2;b@K#iqC~=#@(kLSUthW5pWVJ?c|ymGgsdc*F#DOTo6-qZVuW=@B8i0^cHat zK~2(aVy_PjiQGtli_~!{Cz#tmr^~DObenT((MA#0d$ot|WRq%=yBA%T9_p~i1gnWT zN|qKFYeZH>|PD*w>ppBNLgZA-vN~B0gjd)Di_Kw+}(v9 z7mdsr_v$rRePE8a2pUJmo*pm>80$Qf99+Y4tWkTT*K(D)Myau622kNi<{yP+UxB4n zHhCVf=;0(Z@KG2^BDInE)X{M$m z^H_9sj)}$X{Qho&MHuS?T;aWwjvRC>9t1Z#FdTm~Q6M0E?@rXK3rGVKdsXj=$2UYC zEe4(^3o~HV{r5=BJ&uEtG{A-cjvblgzY@Jr`vrYV8?R;mXLDK?l>RE7Q2VH=l`+1d+)G|iR*Qdg*u)- zeL>l41?EM5vgrvo9Diuxx)vNykY6i)O2Aoc-M3S{Evc4r4MvSd#cr&#U$h9%4QAf} zFV10qL{cPrtyw}ErOKaHerw)5z$xr}?Ak3)+d#j; z4o=g^3e+($&v{ZeK^kN9s>MO@_8>3fzC?yo=qGYeC zZy7q=#;q);=IY{g=!lIKqs~XO00-hB1N+Y2csclv{53}8^w9Y~4l#kGysuqEYN=DW zh~oI&X^BpBX0^hW;~D=mu34}x7%`Gg7;^Czhn{_@{kQtv0zOtLh z?HrQb(|BVi4CckxJ78zA_yLQN@~GsEDDH$z zYqjr};9Vq_N#q@RxR%qBu7Ce}FZo?m?|}3qp2&v7YUs92|3mc^z?%VRB}d0=OPf@1EHjZTwc0C>_krcvK%fG zSj>`RqM_?oVDYMA&GGU+(8J?tCA7bumMN922{kjz^qXzCWoZNCBpC8^BE$NSJP!Nw zkdSV?g~(JpQZ9d<7{dmM((na-Vw7JqD$dkq_I-*>3NQ;W(B0|?q$1G3)%s*Lgxp>~ zx;X|*IWL7{Z#%%-jfM~|a&k`jkGx;SQ<)*oUg=3!=jW3y$PRN+qt!`R<)U*ha5R1P zRmX>LAh7b%HJ4V_PzcYRVegYsnb~%tc96ztZx<`YflcrfBd)!Nl%j-8G1;nX&07N> z2W?FtBC~kQOy2L|nZpc00J&>H4%OtY(dW;v9?%S%Rk2o0^Nb~Ql8rcK&Ct-bIU<8> z-kh-Fi;HTEJn2YJ;Lfy|A3cu`*-Pfdz^vDGTB=(rgf|mHU@$_?!(`zzaGcg%q)69# zxeI8rfu8pkmi?8=lBU6x`D>@I<(^VTSvHwO@fE!_r*%dJFWuR zFBs;bmm8@D#G+%e0=Va~_xB2+@z#fKc976$6euWi==GN4uK2b!FPy*$CDPA{ypZ4Y z$O6?@J_CGTtmVRQ3>T(SM|gXPoa+{MDIL=X+OkgXR}suTy%*u&TR{Tj=IodW_s^{?Hv-!Cb&>7$ls@U()SH_&qT<{&*(@jf~*5Wf()nCC)mleTl{_8glt%x@M{^EM) zE;1hx(|Wrck^!5Tbmp zi>+bXJ~~H;+K0qV57%AjG+~vr>&^v}Tw^8Nmr4QkV!sN=ql`>HQQTR5+dDF*&+UZk z#aj&V37s0?R+}4dM|kP7B6-d0-Q^}Vo2zJ7XSvuKr6lQyDWZ@9{*UryZ)5fC)-Im@ zmHUlA*H{uFCZi}0+`0onL^Pk+xiwYG#=6WY2m=BlC|QaW(yQJuyqyWdwYxpqu9Mtv zy%YKlJ?Jmot`~Vs4wFKd0IsZbq1SijJ=Rq-C;;rNHUQ zxInjIWyx91tja(+#czt6T~*48|xTjUL| z7*l(PcUskrM0-jx;(|q9hPx+DalO$Qd4H%6Q?PLR$<1Kug$;n-k!^H&>SCK}7#oJV+G#k% z^i&p3TAhl}aAgx836h3Vg+dSW zyKfkD{MJN=A%7%fC9ZRzue((G98sCff^mQGDR_w+V8_{QwdJ(*tm!r=)`tW~+S_jZ*afFZOG9KJZX6p%#hUB+rYS*Zg+jF%v41{C<>jj{i zgEz3ef_pboal8vBZ48Kn;kNf7bm;O+Q7!vLG<>mgh?Y9iOgt#G|D+~*R{yoR8YgU!DoN&)W zZb)6+c_8J7vcRaSsswjh(^`DTg^L0~z^9DyA`A{e`=sI~EChn?iytN^o**t(btNb^!CVaXeN=;EyM?5QBvIUL`Z z)gxsHHEBt0>Ze!n_lfqbTYojPPR7TxNf3<|YJP?MW}gIu`q%Cg3Hhj?&%w|^p&y% zQX1WwhjFUOM9MCK0#{H}3uUoVg=I<7Y53j?v&~ftMb{1rx}lUA2Ox`oCUtup#$O0n zoN#-9Yivj0MYg3sgGnpbTPsPjrb^Y~gN_GIuQ2;r(%13@kWv7=3KnQk0x zBC7WDhqtw88N**4U2Xs6A1MMhmG#Pl%8w|f2_1rcCAz9^CjMD26)r)huIk_kmCy=T zurof`EaVH#6XH|95oi1B&hL5qIt3F8W5GcrDgmvKjWq=x%Tt&LeHR00att@ z0ie8SuEj@$4^9amxw=q*`P#h&*C5#RxMmiR+qBynvxJ%ZO1x9G+X80T_-bep4ZC*5 zDyP7M?$saG!}V5JQ~_&i*3S^aTQw;NU;uBQ&Dez{X;Qi2US=U9p1qEI&5^T^2U{{7 z_GW8n6K$qG@8_eJY}jm*%0RKZd?BHL?qdQ}wS9EVX8JLAZ&+inhGqXfBYQE%4UxVe z6G!AD!F2&slO@&p2)FkTTCDN3zzjS^!Z|TiC|^P$Bh>_tevW3zoUA{z8}@@z1Ms}V zNbK>DlhVtnP=V~>?fR9}4O*)bVb{75VDwNkzHybGvXb=-Kv{S*Jg_ZoQVkMuWK1KG zv=p1PR*yG>N3)D|i**5UGNC0IrN?mz_Kq_bg%rD_a<2&6>D~PgRN#+{#I}wsv+w?( zqJ4-1DAx|xAUe=CM|C2u@@VPxId;BkH9Yvt_Ow&k3gU!_SvS^Tz+Y6=bB~}sBSXUa zuYSYS8uXbyAz5^6Gd&kt%2%+E(=k3ANIcgChM#a-K%~4%Va4#*Mb{dfQc80Yu-sN| z?E6cYCiom7XbA=4DH-@)WwcWIj@2`OJb+`_khs-B4R|}1AINpVQ_M}>YeAwb7fNmL zpBB)-zk_k?c!aaoh2oL(+QYhvBPN5GC^}bGlE*%*hm*L6m&!AE{j+1+vw`0cHVN=1 zXLr_4G(X-chm?ltOFdrtYe zVOAg__}8F!bG>KqkMwf6X9;YBDLWL^WYLGGiv8zE&#JR?-oD$W)+^F{81ShM?cJ-{4-@J@wplPGvaV(S=iO!ECiGq}nm>rr2T24HmpG z0se)LHt&zr^_#^TnUef^iX~?Kj^wK-co? zK-c~ti$A@SI;jI4rH8cEa8iQp!DX{q-=~9~>-=j8VqSkfP$f)JaXRmN!2J+Q$L#w@ z7s%%fwYteTZ^)x77V_dZJ*RygdkuC)%N@@-EVq|8nOnLC3Ot^0#iF73=5a4yBsZpA zQ-Uno(L0loJ%j3G(QBsmRQJG9ZvW0y&n4s92ffXlif(DzjyAIg$zHComUNL>>em?< z1N4{#uWtrkNyw@1Zd#oNqXmzohp8WOYg{dJ@^04VW0twow zv<^4FPv5fdEmT9Kj#EV4OLZ9V{cmM7Uw)k)6`mgLuYXV$@#gs`KtTH-{T(H>BcI#o zrMw`6t1Q=HBpOv7c}8h;L%Gn;elmhr!1BE3WTd_irDe?=ZpADWmUk^}Zgj1V=ph2h z*0)RUxDHzBh_)+wxuhVxj)A0zXS_(tTga&TL2H`9?dWHZ!t84NNUG>*#XiCA)rw=w zw*^ero)5qHQv`9EJnh}g-hcei9D!!k2#llEtk;(D{u)=y4OK#3zh!@~m7|jA)^bIV zpM~dW^Jg_*O>;Lr{{wx<3j}17ZC*5R5qdtnpttSOakVNZ(YoB^HcpglC-%fSp&mJ$ z9tf;RJX<$cxyKmzvU{!8;?TLl->mUaQ!kWIq>!0jUm(&w4{VkLLtR z&K4;tY`!w8)jKXqES$}iAqb+`&+^p7bm7~~NCkPbgF1;~pPrk-~En9RUnUx)W1t%6+9r8n3&>Ggb z%dffuoy+U7^NAT1k9~2ph@hBkdJ_ofsv(iP8Nt}nATGDr4qs#d=nhO{#Ea&8poOvc zwc)<-36VAVQwM>?9x`KLSXTUFQypWn*wnOpo%m1Kk)W_H{!P1Orti(jO-uiUJLw6f&d<@ zYdDQBc1+sZ>Qj?71qM~yW^eDaXn`@6mj=E?)|!Bl(gze}*7@J&X{?HMG~^S+6RH@f z*GF_~_78sF7+IP8H41v;6=6wf^}WhAWr?Rr=NA{Spt?ZLC~E45{GYY$H5B+6aKaBQ zwi^#JJPDwHFrSv`0()j1c$({fdm=i?jz0MiF0}JXogOXy{gy^azi|n7i}(sgki3dK zGV+Kq?@c|Oia5x8A)Y6MRBYw~#$7Bpk-BsnuPq6&YReB4|D$scJt&^y>mlAbb?L`|;87K7PQOkCp56$p^yM;=H{H`# zLA0d78%x9v33Mnk8@nuC*opQcz=o)e8glkz^UYm!lkoZZ?PFx$;_+)SP%8kU-h?8CqIYpvN2vrSNXX*>LY_?Ri8h>fgBjL85%Q1X z**@Dn?}AaYsj}QJ{#xqc^h^I7Gz(NwDH)d+nSs;%0K zgrpJkq`6APkoDBwyvz4!EwZ^p^?SUKco!v@X-}gf_Z{zuzub4c3*^shOq!H<9 zdW=PHf>9^GboJ|8(nH9ZPHdoR=<|T3AbaaVwnKB0oMX)qzN1yv`fSri>pkCvU!py(7`XBixIWfwUxdk} z_&y`L!jF3H1hC0(`O5$Mp%|pdahr4id1KX^&RCr-`Rw-k z!}$XIIjwKq=URx>#s5`=(yUV8C_;L}8}J*WBz;qBIlI1>qXBzn1bo#g^h5mvjId|5 z>Is*J;lJ*ovDT-W#l+%B<2bQ2!_8oWTPo$UC=J|5*jIcZ@jlM`t$P;#i_4s^0R&u|Ch1Y16j9E}eW#a5O{nEB;-4RfDdCS9L!UrA&hGVsJ@GQW{b ztzVI|Y=qsNen6FWa`H!$t%jo@6mChQI^X&%Qj4|X>ft;fU&{EjLLkJ&4InoK zVgcB2YWAaIBVmx2g@BuFAc)3-h~5&9s3Lx&(_sD3*HLrMC1)?`>3t+cyXTdN@=S?f z>8Ss5DsT4$!BXLcLU}_K?L0w#bu;*0@RMV=lLOTE8{ExX(K#FGeXR;;+N zfjq&M*?FA6590zUIFotZXhP|Urx?PHnFc6XKJ;4^(CgJeBzz=W9?a3FMOG_uYeh;> zQ4-s89#)D#-7WRe0-vUiHR{OLA1==FI4vPkSJA^5M0dFXIi1X=LwMq)5aVYOhQ!Z) z!A{m!tE7}qfN5v@&vg6Ye|fXzk1xTp9s~3wdJ3DErT^eIT!lVVq) z`Y+^)fUCT%?hg=KhG+&N6nhm!^5sx(dy7Pi0!eVih#kYG=;<8y(f-+cq!k8GAqQF# z?x|$}Epk0efkGa4zZF=yw0rtV%g!k^Ow%SUadoiY;WtA6b?Q$2wU1ww2pb6o;nmym z7eZr>;;8$nQHiY(pR0-2BG`F1k3`aX`xJ7gw9i+Aks)JgWvel=Y0+Q(*k*7mV=ZMx zfqSuF4GpE!` zR&$LW^}|A|`|~ijm7d?>{Y%8ZuF-L|=tJ1E)~SNz;D^pMtpVake(*)W)uu^;5i?P! zhr^;AT%I(!|a0za*?@4(XoN!z$5< z)Ke`5fk20j)0b?#Qfi1@esYK3U1iwx`hBD32xL+FdH9s7R=E7^`UJr!=ESU^W7uuF z7g{+N2$&>J@#uVbJ&@wCdcZkv(AG5-G1DMI@2e#0NknEu%g2oZol67v?DXFwf#j}P zlyux>5t_tO#&^o;i2%$ufS1uTT%*Dh?7FzIOQ9g`fJaL5=9L62eGfeCk{=I+=)VZ( z4_IQ40d4s#8(zIVG9^&*2t|v_>6ecQ4WX~-(FfEw>6eGZFb145mmE59HM{HR{FgVjo1q zn8X#kEtBMi2eNMe{5$OlFq;FS-9$pXM)RJ{*i8&e#8&|4-rbwS;9NzUQ_?;gg2)}R zUJxU>#p#N{j-<8gGLTS&pVPUmTULfv&Z**cRaJ(1U1Z}jWknrsY{VyACUW;=tm*Kd zGG8`k$iCSh0>;B#hW2>@x*Q>nxZXAll!8tg}j}=9Ja{S zgRB8aj1FdtyL!ptOKgBBnIAS`R-PG-TRDi^zAALK=Ir(WPi}pvMtOt?FI@I%lPg)h zK`Yv(5;~Bug)|864CPyAan9@I8`_C{9-dMkQFq{gG%Gkq_*+_Re1Cn9 zSk^N(ek?Z*d2M7Y37!YMCwB9qzrIDjZ~vupKqLS|!aX($$KLE%`OwhKubA{rC0Tg# zaL4h%TnQi?8A7e5=QVdeAIOR8Zn~cv@m&zy;f7iwycv{SEx-s*lzC)`kLd1Vx3i(P zQ5`SN=dIU)m79$pHofcWaoW6qvKN2G;gpUyQUsdKY~^`G-ww|-K%v>DOjkm9&O6q6 z(||C<@^^Q}+#wUNGD<)MF-^a%G`fR$Vd5YAyNBKTw3Nyu4X@+MO+WB1TWhwwe;own zaYg=@$5pN#I8gh)JZ?5ldkE zHKYsHaJfbO4vprJh9a+>ENqv-t*aJfFu^_-498;*(-uFZKttn3lYZ3EOVvEZ*ot(4 z3*fjAeZ1d0)S@9Oetl&t>9V|STGAXP)IsA&iTCRiE7|)t#uA_FG*s8TV|gLEJY8i$ z%;ftYl7_@$%d38orac%Tl2lK}ceEE5L-#Ci_u#;Av!l>E^S9+FVUjgE!u}j{kt^Ws z?1#cd$^Ckg@KjJuLnX76R~L1V!p(H`PgJ8yHW6Hi)3W~l1K;s*#ZVg42%7YF84(K6 z1>jD^6Ie}*YYWB&WOaN>c|A8?{eMo0AKmIO#O(_-kJ7vKG0+^8AbvtGQtA)rzoMsM z+Epy$h;KkK6Z@XGjT^BvaH%464=9~d2C8n zh~5cVtNf1MnA6%)a2ws9OK4OD+*CNsuXuKB*O@{D=^1wCN*3s}JILs1pQdtv&?k`Z zuzu_RkO;>yarYAnTa>XYkLdmust5^ud`^ zA3?sA_e?*~otv&vSeO$eJ_RinfMeMT^0mU7;=g6*z&*|AxxPZRc&Q&{`{EhE?bD`< z&7Ei~PdHrV?Ik7$TYFi_Y zY`L5|6f?5Rj5`nFK6^CP9U$p4(pEY;fRgdpZ&RPu1Asq%wz0%0y|#lUXXS;R4GcT!b01sO*LNc}Id~}??*rLkwl0{-i&cftaoGlPJwdO)+7PRpU|8eUA>~*y)>zD9`;i@te`m;# zqeXN0|F;g$H_pi$=6e7^v^_eEOggtqJuLqHExObVD0Mmyrm>8vC@Nku@&uwA&Wg2tJrxJahMU8W;?i3g4n%aRV%YeE0nuUaI`|4uB+;%&94L-JDNc6o2xJb0kuZJ0( zcXz*;OBlp=qqK$ zcvrjUMq(O_Xsx;Q(}dZ)lQId-**$nak1B923w#61scxD@2Of|%$-7K~ySc1eN3_+z z_#4#us3G!kzxgGjh(>tk>hg+2?WpcvIT&mFmCk8b_tW!`gF{@8Q)u>j z-19*~m}+wy8~%J2ENCM@z)y?wC!%iH%(2E|b29!s)LM2B3pcA|$EgR&9^zA^*Xi+o z=)nE7q2vAlyZ`|1aLCWJE?Fi>OZ!0s9!Sw6yr&)#Y-q&`!Oi= z!nKO7UXoi#k16JVE3>;VSC>&Qsf^g;bBc#}^b}AfUMkHh4Mg-v z_ZZBL{x4_i`+^6xPF|0CiU$^C4^G(>o&qrELGNxF&mgb3h#fhP@)uO<{!Re*Fb5j3zs7LAdfF*EQ*|QzU{?y?0Dmfy2Rv|Jcb5Okt<{NqUBfETfHn@kQ zz0a@#ZD;8KJO|8*=b^4F_Ft7DdHf95*T`z?z6&5%<-__tVx94d*HJ9U!FEr|;J2PB zaPW+Nbqqn&bEuj-311DCr6omdHK8S)F7rJmq@C7!hKIvH;#P%}?B^at`UnlCJ6Nh1 z{at>>gn&>YhkO=!de6fKBdGJxV!gWDVE`KbK~O5wmAO|@fAySTarKnxn~2ERnJ7H%E+@$> zopBzq@!={PZU)dGF!XMFN!i5M%6^v0Q_ph0aW{{&qAH_5N6W_oHh~nZj9hPg-EPio z+E_>wY>(>3&p%-Qz*<*U8!@LfekKifxmJ?yy48g$@O`Brag@v#=fbfPF#DZPNF$h| zWW)fQ2`<97SIs4cYZTd^dCZa}Xk9C2qtz>TEmXqZfb25a0EKewQ56IUUDb!VEN_8HIgTW&{1?+%pZ(nbMR#dIi=GZR>eY?35K^!&BbmM`P=zk^F4jgB zq_@`VK6tsu0%G`V?bSGYi~JKFR`7kQr$pbqe|7wpaO5ziT1Ke4HvY)rucf#7pt zsI60JW9* zW@3E0ltna|)U}=qkYBf47M*yh;s1fc2N zMR+H;hPt-UN;#4z6x2aWQC2E5Mt9(VD~8X11$_c6{((5^@TqPFK02EH1yu}C5!ld+ zS1c7C1GKlb#f5g6jCrmz%Ou?YnSE*=Pr}a)11E(c zV0=!6pqbQ24L{&kgp;Lq*_<=G#@gu(yRL=LSqtt}#q`5@6j(Rqu`RutRK-hBoj1V& z?q&?uLQ`p!nKMWbwnbdZaMQ=}UTHBu_`V=&I!)4UjsWgu`zD8Zr^H)c5x#x4fqGcK zNM3p<=CTKcb2DPUnInP%z9ylNQ6IVC11Q8BSKwii3kn9*Z-N;5>epZwU5tBCppLJB zyL{gXSSfNf&IA(2oUrwzs&6HVAq0v7vB2(#Lq_xuMZ}3P^nzt1)!RaRFYp2kUKR%a z@jU{Wj68MiU#+7SZ6pl1D@H!Vr9VO1x&FkUVUHw{UEFTbDN=HLGt? zwWvUVzKNu?WXW}Gi)UkPvHnnb0 zfqhdrvQ0m>TgW?4465*qK2bDu0@?$jxGo>D|LJ}ix+oG=)0+>j_%me(U)I$!GJ@s6 z+&d>X@WDKe@MObKc}}-O48$bG@LA3dI*U!;>jA`ui$d54E8oeK9v+$J{+jAE0kGL36Jhx|oWQ({S%dO0n*&8^47v&^J3b41%ddq+q*7@1;K8y8r z)H9kb1>7*S7GQGrCt>z21fZAhk7iy*H&7QXO&=q&oGFETa)9RuO(3!*`#*&s;B#1A z<+ywC1BG0|6az{z{qz+alXY!uR^4}YZY~*3S|H0;LT;zOhdUiS+9oPyo2uXDtyjBN zRl@xXZ$TbfiFNtNq8~q20h;NeznsdBP~4l_7^4P7dS8E-pJ>Y18cACM8`rM=C~@|n zin=vm;03O#3l5%E5eT?6x{Bj%BOf39(pr4PHK_TFze2|*-Qvmvb_&|*QfA%1(c;=D zcXt^==yos9#sxZh|il0gRyu((AM!p1MOl%rI-n8F-LY3GJy)9bn67U2vg85LS~ zCg;LpV<9}?Q`uRdDQ z1b=XA_CDdPPhA~F_d_9<+swXK;6a0_6R}53rXfY#4JRl_b0O?}l8=NnA>cOtc4RQ@ zktdF#Vo30kR()>i%#i04i@ge(-ZmWu3h2a+_po+yi4 z2>CzKUqMQiIP?+*+cOvCPNvD25FZfWB8&X%(m?~E#riolZQ@2Mw=yE|v0$4V{0Yfz zf^W2^kV9QeSxyQ7&r9=lDpRWTog2{WB>}JGvpod{1ILxRo>XA*;KADJwSSoiO@qj3 zC)~$`jk7{pcYI1E(T8?Jg~0$xRW^r^ZVQCzzzPaYcLuL}ndEkb!eJrM>ITDB9KA;F zn(v`XH{>+>AbYl($C++`a)G`u888#hU3FOI0~eG~bJMZucYa>t96pA3oDlaIYzqef znl5SrF)GR<5WSu069u)(kdTy)DGEMqV()^jeRP*~( zc7Oe5SNiRx)IaVXj?ICDc9@1mo0tEp)n3_LLQ0FRLqC;SIR)J{lMzGWfg(XA2pcow ze_o6m?ly9BzfeiN`=wf5e8E(`>9?GN3GBY^q0 zx=uJEH~;xyEg|Kaw@H0r3h~f3yb44GSG0en5;iqR@xlbk*mXEhj z!Ks?Z#+B@YQ!#h6@tS0}mn+Ro!b%=^? zyqXEMe|@X*UuHSHHf@j-;_Ed&K2`HUOiB^I7@JtybLl5r7RA}sO z9*xyS@hpfxS+?3Q7!sHgF=gO7=?ffJqrelG!8*#53b`i}4uD*zn>_bS#tW!Z5o~6$ zr*L#SATOAw?RIQNFn^kHd_)_~udkL3UO=yB8w?x|5wW6fD_}|Z`vvX?Mf=${-6vpI z3-uTZl(TAx)WvON{t1OceE$Q5rbctVa#MbgESuNn#utv>#K#6_EQh#>4Q<);Z-)iy zgpv|{h0oYm?s=!qhR=z+%qNt+1gdEUS$04q$jiz^U*2jOQMY4oEZAsZZs!RWnZnwskei?hM zXQ#=k-uZjfeSI#pU6O{I<;Yo>cq#UboD1x6(eU8wABKhEfAQEzo}cUAQ|*^qZE5Ge z;~lR|8iIEfS(nv0y!ID}^9r&wNxef#EJZ(?OWC$t7c07JihOa*nK=EJv!NgLlo%>2X ze5bls=Pob1(Ohz%F=V-K457CB&%S_*o-l~mWP=JjNUHk{hW49|kiAz9s`1R!oef{s zkY@it;0fY5N!t$My?!7l0l__B1<0!A6Z1|u9|ebSH{|6Z`^uUv3(l2jE)S*gkb*yZ z{jA}S*W8Ib%kobUP)&CcT-cwTpNH0Eq_*L6ITYXa>1`RHjVZVwPj8P|8K`{);Bnrm zBH)+V5|;tpVj4%^>c$^;WUTEYu6I)!1pJLWT0RQ!I)>;SUO1;{*B0AT+cZ|FW6nYj zm&I;TD97IX(&LjCLUWq_=KP*^Yn5pe)VJ5OCeoxDOpXcn5zZ?yL|h(`&dZ znlqHWe5pSK##D=6;3@JszjfiB&v<=QaYkAFq&}Z2KK}TK9kvkP;b;s-kY(mY99sApw8AslYT;97 zOMvH1lbA_YfN__;I|y_sBl)O;>gO~QbPT{;ihONB)kt) z>vJW%!MA(=`d+p&Y8zYDoQcYHfPI(ld}rT5fm>2ffazqM#bDdGfnG&G0`z%zl){Wi zi|XCHC68ziRZI&*({l80;r9J^Uf%PqJ2NgE@zIA@V2cW04NSdDnTR`5=d`3vbWyI$ zy#4>^+~C&psC|&Fg`B#tvj6R2u}Qx3JDr|!nt^JaSsYdA`>Ev_JEu)D=cY*+u!C@^US zErNwQQL=l|KDAqa4MOjTTkbk3S(@x4*YmHu6rt6q)bZS7*?{=7X>dDmH66C=RwmE> z2>}~Yu#K$1-EJi0Gu0)vk@Rk^1L+^bKYnMS`vh+5`*5oI<1W10-**K5j!v0V)nZUy z(9E@Uqv6{hJ=Uqg?31hltOleQn@0`u)5Y?J0Th=g$@T;4@L>GN`VGwP!C-?Sz#^24t502Z`bdx4niZu4)?3R>QgS0yn=9}z7f zVns^*)Zt86yZQNW``zXkg`X!u%5k5DY3<9#jeAM>M=!k*e}o&#*uydPq=wx11S10J zwOyzq@AB1&X0yECoBEIYmY$j`zaQ88Kdg<;c`I`#fXQfP&ktbE$lm{A55lh6k*_da zaAwu;U*N5~427>@^Ga5SKwDR2YSf5ZlvuYgO39A7=+?+^kzBfCD zzm0*q=WB@3rWGb6=aHFdAcx-Y54jz9)_@@-2T-;34U`eOQ&R5+Tp5%p^t+{ zkRk1=+WnAtcF2K?BY020*Io0hpmT6L!Kw4--+o?swSdsrJVLJ=+S&%&ofPvH2B~D4 zS#=D^qHEn@fkeAo>!JhqYI#+;@DKu0BwXJl8@kC4aF@6ac^$@Aq%6=b6YJO7|9&tF zpNM+DzEc0e%l=>(m1oW6Lq`Ks?`<41^?A>S4qd?K8e?qbK$?{nI?4fDDgKCR1X;*Z zExWSej|`)OSSk4cy`WDbOq01J zc8e{r#-lHRXg;sA|BK6t*QUOFzW92whY18+w}0?*psOCQzhdh3*3RafW$^IFszTQV zn?RVqy8q04)!YSi(E(4K?|y9GPnoqfeVC~fl&}S=bY3>5F#oq$7W5aoY@7v`r$|KS z2p{)7^cRU4KD_$g&)H%be8dT&vPjsy-Z!I*ZSfzkNS>K83iXuocq4!$?sVix$&xpn zE;&D>=q=@XMPi6G^Vx;lnte}$a;GnmbrPA{KMuy{J#(M}wKU;!?6Y$N%3Cj1*AUj$ z5Ns#FW;4T0fiJ`HRuL%4hc?$c=wkKXk5W=Xn@I45B^~y)awy#|20K?A30B%gFjaQG z$07$CSG>hY3WPSUkS9qE#F-AlgF>!Vx-TXFY{)Q%-`yl*>8?f=S!Xbcn2*)xI!Ua~ zD-9IeY@0;Gn!ubU6Gl6*+m3ob-Nr6QC>Ke(d_1Y!m@?SZfwDyRof$?F!O*t1%z(RZ zWvgH(0SRHRJn64hPuE^pWjnxZ`rm(oDu41~Gm51UY)n=F*?h>PKr|LCXq6tA!xwV{ z$HPr>m7+;Xmzak?QkexHWj%WHvK#V4=LdM5YjEBeNf>^bE2cY0`6!r3hq87`AskJ|1?7M%yc@T*Azv*<612|)8@G((;SdH z9T9XZUZ6HbEEtORjKy`}*2FoPml=EUj-9WZ_z<&H_W$;36DOALK=onrIGILl7FXF#gtaX=R!sb!Sp(VVXNS_ECuw8fS zB+dV3ijvApm1KF)W3k#~GlRbf@gmZpni9?P{PNZ#G;C8mbI@LS6YU9gQxT_La^gpS zVL*&EilWLJXa-Pb9}c4pjP=$dEacMVxN`5K2=e+3N{d~ef5r9NU_hO38Uu2@r+&T{ z`u_@B-@^>QV%()o+<$^7Wg8(r3(3T|A$iJx5W!hLc^W4yb{49{`&Yd;obYf9Jon=r z2&mS>r+GGD^3sGO$H5dlx%3rN8urWDGVXCa?BNV@jl;yT-`%qw+c`!0w&VU&x@7+L zhvZ~_8Yaj4z0e-yJ3d!GoMg^=NYQogM#>iy7|#6Sk@_1;p||F>je?V1;O39W1z_6q zJ=zvIdZ@d9({MLH0I)NhV677cHR#qgJ*3b5JD>_F`#wA#oUKUkztykf)FUvnti0oz zUHF*cFXCA0hIo%skio`_`X-660pfAscoVXn#>-(}RO3*8h z>0KcXZd=FvFeG=~XEhcQ5r2SK`7k0T+ z2-A;gHK9i&XirWC8VIzT_`!+Fx4{$H79f!80ILASvtFZ1({I)YWiH-+ zwy!=45o!6WzniNCrM3v6T?hQzA1}8;5M$_IjEK8HT7MnMF0y`S+XToR?LNQUf?^#j zjuoY49d(ek%sjCF7Sqep$=(V7#9Nf3v;3PfP!TsQMM0slZMr{O1663PrABXJ4MnKM zFQSrN;&0vTD+$FPu>bxTD##3UrdpGIAxtOS`1)6W*Jvcz0t&l}v{FHW7pOHD09B$C z4{MiHq94f=gHkf*l1E$;o=qXDpu|?@yHyNybjx+}RYZeVQo~1c!B-pgQv(l?gcU(G z!St*UrD^jiJ&bXH_iuj1l6Tjb&LkqOqM9(1@R_$dzzQHX?)=&XVcJh~N=1#N1f27P zk!JOqr$eergAPxW;=E|c-mZRX?1GZK&!e<*R-|WPotE#g7>W%9Nf>E%clL6VX7^_9 z3wJl#AoP4dzqp~?4@7N)=?Mtj&|d#>oaOll!eJjspj$Fn|5@_A3!#zxh4AmAwI}+l zKkVB0yYBRw1=iRWk$D-K@D@=ft5tAt9z8pYD3slKSYH-N!|XIDgm3)#BlDeNE>#U` zxatPfNBhW_-l;SlXAKR1*&lFX#2ZeK)b7Ll>-_&Tm@=V`R1~4N=hw0S|CbZ-}vdJa*8)h zTo2czHiuy8rGzpR04T_5>OLWb%&j&hJ!DZSn@||RcZotq^q6{y%ZWQh|_V{^fQXY!;gv$cH`TZU$tWrYV@{csOfIgLazfZ zZrJe&`p0{%b(Y+AH@p;n5IxnTBM=rHia=Gr^odWAQt{i`Z^3yN;-Jb$lI8Y3dziI% zqY#NH;UJ@?rLuY~vA_?*t73!W{ce*0gfL<`kfc6xI!fcWs{UD-t+gf@pA*?R6OxN@ zk~mnYsoHj3p6`}UY^JN;fkWqH&559~Gw*DTzcir#AO~b+3KIvrJy8@_ba5nbUgaTH za*Ve(qD*9U4g9< zAbE2Z{ut?Ud!w3WEm#arvd<_>HugkGsBVQ4xOqc@QV47w_@ zWudrhnOm{Z%?wB$Cm~PC%uRzKt3|3F3CV|t@!H>&v&;%Z5f*xq;(c-aJZ6xWSiRgp z5m(uaM%t6bu&}Za1*$)PD_tV=mY}|NUI_<7WD~3@1xYj@7i(SK1sSvrf{4$BkxsdU zpW{T;BqKcW+>gE#E+K*|!q9wR+wA7BVQX!jp`|2Rra)V~{(Z$4PE{|cM05f(8pZm3 z5vScxKLH3U|V;X8O}^ ziTyDvdPK6Di@RPEko`!;_RjHUxrcK94Vu#+(AYM)=l6vkU1)-n+k?jYEqR75EpH1a zNjSoUE}X)e;-ZC$PzWq(JFW-pM zW0l#>QUVIIDw5V2sr&q9nK=g^5pXXISL4++@&BSP3?Fk0r!WJ(?S2-x9&4c^&>r=x zCFCfbHu=JTsyn<#=s?Q$THuYm4#=uaY&UfiYaI^iTu*WkdAAXHz$S0(%sJ~0*C9|| z1%mHoeNjSnqsXj6I4>ZIZG+JshHCGMumaxq&g=O`$^fSTL%g`@b`V0qM>5!O54P{u z;3V)y#gN|V#K22H@au7dVN1&2klGKyw4fd&Y9#C63kVww@0`TXoa@5 zrOyb?Cv4_3mtgrW5zw&N|JR6GlC0YNLnSaA$m*X3KR@61ZJYBqq16P|i1@{tBhNd7 zJ*{!%V)=T+cHLQKRDet`!c}R=U|A>qG;h`#+}SB}gJfGQB2Yumd!s845opmnD2%~S zJX`IuEPG4#A_M`{JpX?cQ5(Jc)Z+hx8MTqfixR=s#SczrV)wZb z4rax%;#q<21HkXDshYC-0*)-sHs9pTn8^Ti0SFl$80Jr!)r`ViP`@B5YEd3Gwc7qo zuSLYsZc&8&E4U4-e_f`$P{nxA-Lj_80*#xzR8w;5S$-gvb`y$!dl2vOOG5o8=Ez4S^Mxb6&wQbh0(1RZyJmIZ__BUqS0iu!OKg{TK&`DVOleqH!r;s}+Lp_kX!*~Aq zFaFDwCE#yt05epR=8BV~;-J)$OIJ2F;-puB*7hLRI1=U#zDm zJq9LF(L=wGsL+MS0qPlCnKKxhAcHr8NX!_0alwg~D@R6WF;x_d?tB$AG%o5L0Z3oP zf4}KW^X1XY#lWYaC{Y7%Pp+w$wD8nN4{GvD@r|^9G}gm2)c{l# z3FW*2oQEVdK6pt1s5wf~FC?O|7@{73mdOl8g;JTAE1P}}xIH}g8k_Q*J@jrg+w(e^ zT@&p0NV}|^?RGieUC^-Faq643nmetBKbYgzC4$dbz2{o2oscZ1t~&e=uBg9jGfh{` zau-bf29boTXcwM8Ij}ItQ-UkeQ*#=sL-R~;e8ee|+VD~#q0+%8`N= z0vT1GK=^-4h#cJPQ_-NsZjggp$^FB#uE+99(bLs0aEF|z;!kpUOSNyMjm*Bu`OLtA zpv5!_P34@hMGv#! z?s3#Dc%+zNHSA@-%_G?AeQC;*kX9vK+u2Y?`t^G+;rp>cI#uYh4M*(HIW?R0<-J=r zCs~R`$CtL{^To0kAC)ql@*Tiyo*8FZa6@5ek9q_OBufo^XuGU{eUT@+|kL!JEE@@1qwou#maKJYTGEcHiEE_;_ zD=`$E=AS}c*=l3?=tzIB>+>o$IY_~s@?f+-I z8VnD4X)vmdt_ zd5PNb{o%Zm(G6A{B%Y_6*OR(TpzpjwPz|PtPRM>JRm{*8b`H^&T5m#|blI)wC?rXK zSfdl(wbIM+hd-f9uA=*4GOAe9b!+{aX&+KQSXaI8GHGM!OP^Iww-sH%)2ePh5ydK4&Ewx8-4d*H&CK@Q2iPs0A`VieYNUBWdEI zC0%X`C%Xt>y|?>=J+EuWv5h}ELG!1OxlQQr*e(;$A9zAQs3%)roZVX(J%6>AX3@;2 z5JL>NuK(?~p?uit6Lr{csS1lnq4%6_Oq5%ASOY*JFu>Nd@8hUz=J!l{D++lKhpe*W z*zxPc^+{!s)bfim(xR2mnSENw`9rGMA)!)Q;o!(r_LdwvA2t4_x|%B=oFbV#44t1b z*kyW5v)-Kce)M5~D5D?>mnqZ0Y?S>wyPj*KnVpzuA|Dxgh#MIhA^|tG53kfHj~xdh zz!zebVxPtg5^$44%beOSpB67k-QwaFJ1EO8Rh?l&Pm_ny>0xPe6BQxTEN)L3S8~vg z^cBvm+48UBvX_Jqd(pk_aZEPlDY)u?nC4;^c?4%Kj#78?bWTu*711oa))*Z$Db~Nl zYcQ=CC}(ge;6oVi>32MRR13O*bzb(t*sxGN@-PPoz;%Zzn7gMWxY85Qg+7&MNch<_ zZrhAY|7Z1>jQohQWu~MfW+9Q4NYzLTAiQ**lp4s}KWd6$yD7&p`f{u5C#*Y<~OA$C?pC z%`75zVdl*`Y1K;9ea>6&8!_suAL}T^2gm zk&*{adi!SQ#OVbi_Y4#th@!Z==f^?i=51o>ddyD-^xq=fVjuJ;`gYhhN3A$!DK4E# zPxAh7X)jOFp=q|C;>wlaQkRXIP^ItopGZ)PS=rninft=cTz&J5zSbnsR4sKEzZc=P z$M8k2tguLPleIypG{3NrYnC7)M%>BvxKbYdIoG-(_HiXC+@=`ms4gNPo{ zUmm3!6Z@qDPowm~L5H)Zi3*aR8PfFOk0*&GJ7E?Gr61*DtTC zu09;i)OSWbYq-qI{z@B{M^$F^w&aaQnuMuZV_$`N^Dz!jDKl`}oANO(L&<{Nt8>DI zqR}DNE7W5fPbG4Kym?0ZI8mp}a)$JSY0rdbo}~zIOAPi7@H>dD8F&p_({|Y<<_Z2O>p&;dc zY+UtJFpB^`BhD>1X5Y~yV#1Xaj4+q=XP8WODqjZ^z_YVOCj8~<@(w3S8GcjJkjF`n zD-N^gxBslo&_%UJxa|$)J_eQ#Ouyf~$ouQI7oT5sK3w&JqwYW^a%h>YsJ##=1g%Er z*x0eqUx3Y)qBB7Z6VVcZ|Hv`97q8zDeNF!-RDTtX zJREJ?&y=`{_Dm6)(9{ts1=qaKsjhbLw%=n&CxIcasmCFl)K%R zVTER+@Cdvq)@pn^gx~u+cMP9T@a()}^V)m4)L`}0#FLurTscdN0uNg~9c(#VWXg>A z?$=oZvpxABx5Pq*()esCX~Q#At>q6n*HL;Q_YU7rw--6IAvAvE zy<$Dy5$?7?N!OFKh!Ky|0_>)P&M#EYFTmYmhsW(;sujC>3Z zcJs9R=FRJM?s>t)C1ty;8@maF#2b_5tNVC1-O^(`b{d_sEmsYQutPv z`&ePqMH%j>=932+!d~`c6CnRnr02>twk=R;KoALDDqa{GvP%bQEWqiPwxnD6?vKZ{`E}`=W|?I!xHVFMHwog#Vn_ zS;+Jdy0i=B3-ywR*9iV55hyMCJh!a&q%rdCN^^5M!^6@WI#z?mP6;siNp#;hCG$)c zzEQfEI`q!8@Cy|<>45qWoBJ3i?N3*AOjj@Qod1lznGl|4iV($5i)WJ4K4)DulzF^k zS!YFq@(Gyj!6kg`>lDjoNaKDopRnOO;eB#-x!SZgux7V?Zzwrf6t0(wrhZ6H@Tx~t zDy;t}HajcFVnxpU@f%6;%DVT$k7#fQ@(#v=i9@T}${q$ggM%b{@z_}|vJpfA{qAjH z)Ji_^#?Vz@g@hpKT0Z9NZ|e8+!4rG7|LB}m(p@GaYqhy>AR>uYfEGfL_XVybYNw?r zg2Wh@{E1RVv*w0HW$H)uyNL9QSX&B}L6r!-Fz=H9^j5RPU#t! zFx#6i#utDIbS3l9eR~WJdq*Sfeb6v+&YIhFE;>U!E93eJ;s-j4xQd^3c@wu{8{?64Lz4xlK0&gL~ zi$(iif=8?E3%R(&1ULK)?+;|&g(rsSDzYLS9{p4(K%BmYshuPy&YK}n^ZQj$5A|0= z@yWfvG3j){>G`zxJmCDp@RU)l)`H!igD$Vm+~mx_{?}r0`JADp3is_eM2fvs)U9c( z8|su}Q09^rigM*D9} z43*1BmfC$efy$3z;S{L+_+GtqK~;{NcqpZmOZ0Zk#Xcm8qpkp`{5s9SO}j5~Wt72_ z^RcG=wXAF3dF?R{`)6jbUvCh5o8=@>d&8n+9Xvs67sDN5$mPIFMGJO92R^@X`5fJ2 z%UkdYvq*0(*4WiwX~C;`Io;4hL&u89u*(T{PYzuxG4twBMe`_jGzqNBc9(n0%9R7p zah{Y()KOLO)6YZ_;SQCZg5-34Eg8k{ow>wR;dPr*A<(q1$J%AF08Z2tvbY1jlq?&g zj|oLGxr%Wud(K2RQ>YpYS2fMDtIyDj__#oeEHjW;SuGmTR~g$?yr6g&#>=kx_xai6 zM!sfO?xPpSUk<6tj|nZPxHD-mBshQ; z7H%f%kqkz-`F_N^L}8TamiL5;Nm2K7PLih&^F0IvCGFPN-L`hHHYm^h_A_cD-%if;(*0-Xo6m> zPe4ojt^&OZ7;{gg5$^2TQAV+wtseB!N9@ZM-sf#0=_Hh1FxJ}7Pf|Pv$#vQ%-w9lQ zy}{%@`q}%w1d}=&65|JwCEEM4R3>n?;RHgTX(>e0?plhDjYAGx_Xb!oR?X(0 zbVOz_MDc&p+TR9&x4ka?&i=zn=FLuOk=Q(sm5zM#m$0+WZmEw&`E^D9@YAr+yS$sW zAS)X1Gwb)*1k;~93})B~$yb&#(ojn=DE!%n^EV~>sJ5K$ee&3N{vvZwFRY8Nt_)OjnU<=)Sxm~yt;=AFW-)?Dc#RbLjNWoASln@?GoQ0yaxWc0dhjwoWs>zn!*p~efqNq)Jr z&F^eZ9`%qxXWmy8l6Z2*%HF+jA}jvjIGb?a#uD3TnG^c8oKHI7(gUpg;(IYBm+ll1 zjSe~F!x9YfPDEHD^%5s7=rp++!HC(zcTQu=t*c%~x|t}lvqId;fa)!mX;v`Lk|com z+tUxPFw^q$)=WK?k_GGtYucCOO?k9)8)X-7Fj)h&eXCGwufkI4nqq=pCF8lrOgvnr zI#z18+2pUbTQa@il}CS_P^Yz@yRho&Ls~qpdSECJE=6J;L$<~ihU|7hPGd7h!`@d} z-(@-t`P@bgmrFKRA%CY2RE%d~%79M2Az?yY?!!%8A0{=f(SFFdAg?}?*8|Cuk1 zdJ-WF+cA9H(ZCR5vqAvf>n`bth;puE_Y(~2uV0ysPFv#Z!YvJJZ=#W}X21xZWqNy)EXU$>^mFnD(3 z9b-$8)L5GBN1ED59O3<{G+H55$XACt8iTBYT$Ac%KiAwH3F_APW#7R@n8``LxtTt? zw21~*nNXqoz&{oE@F?^ ztyZnLZ20K}Gm1iT%tmRPxRT>;DNfs+F$!acCg`%H{UjtrY=Po46n@xpRZ2tjQ{*SjE~33TSE6|b zdNhO1-SOi#a%%QWluGHSSuabt-2c%ZGH5iU{(~%)e`Ny1ffF4&PA97>F%lBs-I^|{Cc_5Caw^#_FpU^nO zksCR?$ST%+^?zCbH4r2IqEo{A13NrzgSSH|b7kK&agIyQBatwTIs?fmwQ<^gzlPlh zwL7spdBrWU@t$KHnS(rR>&zKC#*Zz`v)XUZpuo1&*)LFZaNRqOj{tP-O`r2Q;Eyf_ zt0i{7Ff`XVpi9vR%zY|%%nx418Kj**Ho_&_tL3AjJbMldc-=Tw+h`M6U0~E&| z$;PZ|Z{D>eqghHfh_Fv75t#2~BFy)`zIKYtlKN%OP;a$wfH2>y(J30|=cLiKh*mD< z`wQy2$6-At^#r`|fvecCmn%8aMf<{=aSX1>(8(Hk-#Q?l!RwKzvQ2ImhqLNfuB(no zz&oH!J8z9-2U7MwuqFtu(b)NNg?2)i6M{HXdiAE*=x4c4??Q0DLN!~p21z=WQVCKD zdu-1*_T@19RKdk<$%!UYHt74-UsSg=1+w?XkykBOKFL7;o}E%7m_BhvqiWZVY1MAM7cv z(bCMUuvO+hyM8vDNGS#QT~kH3_-8v$49gz%45TGK`^ALI&e|yf8iPIrf!j7U;?Ub+ z!X*tNp`H=_5G!C`+Uay(527Hd+iAz1zT!s=X{(^U9?{@vAl$s-_7rv|&|I>AT^UdJ zm2(!dOJ2R!(uyJJ#pnz)b+idpgwsc!zMTn^qp05pwt9`e?35Z#fCyYgL#15LFULPoYgbyVc!(PTcmxCPDM#bdjj$Y<3PK zuMFyHPM7!rg>@JU@bm2;Eo@@E%<2t1jI|d7#(OGLWHevA^b90{@g8Eh@fs=%_0x&u z*Y^_j(&0jkDsFjRg;|Kc#5uV~Vo@z}J1E9R-hPi9rUWg)6udWzW)}wL;XTIpI`HiZ z^Z{@ChLhdYwS(9ch0&Agt)~oKTajzuHV|~|tO&~ccNNPX%q_n}EHZ1!={@J@N6SA- zI1-1(NpyH<)guY3^!J2`ZCKLYI+;(UR0nz88raR;OthA&2xrr4?vH(oa&`TMvSF|j z+7oaG>)`E4=63QzkrbnQVBl%&>ZC`OX5I{Dke;b-RcL|<1^Gw_3Ua87lf86IJvV=U z51pLSjUtnivH}Z@Zxa3 zxEcyZYx}6yKsK8WBlBXOLkf(8QiVSE(A5JYj%o_?W;PFf=zjg15_?p*qIu@WAtni1 zH89f3KoSE9xDvnipObI@pok|XzZuh^aBk@Y5M&VjHv4B5F-7DHK^e5VZ|&ik9a>4^ z-@BJyU#QFRi6NZ#=+}W2XG@06Vbf{v>$iO8{@b8g=0!F37^eOAI=DihEnK`3^LVmL z7N76NzMgEPVu%RbIr-;i3~6*On)?nX9Xn+VbUv@FUe8#EMoo7;2FFfM+Wqw=BD6oK z{1TuxZnuDfD6AD@s=N^C^#U*W*%-xi-8!R7eu`^eG&@ScalFB zowTyzTx6DJy{K}J_>k23Qn;ek#p{W5kb#?iDXfIkxpr|?o(=Kft^6vsh!=Vih6#ihj&tQOb^Aky_M@KzG&kTYV#b5Zu!?SGC)a{#TqGT^5<8VHyq%wD$&J)+JHx|tamj_Qt1lJ8hqZX`@FS>*B&QfI`$2#M8Kd! zd&asd-%iKa^;kA?lP$1yyY|ZbT5-!}!c4{Q$voclDc`1qrX6F%lIEs_@XcGICxcz* zeP=-lXK9?>k!#xQ2avDsEsrgl(@WST1V4hA?7b{18jWe>9KU#!%_RbEZ1&Gw3Qh># z7t&L7{v_rv#v3`WkG73EYA~&ncd;5LpNkGq@&Gg1{pOb8$9ukS6pdYbUsP0s6x=Tp zwkk6h>;@N9xd%i!Zs852sP8f;SS=#@)MoqdD_|VG&2!+@ARGp%r9kwjdB@wdL%KrP zM40l@K}VQ-+Z8o-4_7X00`tAsqbSFaND}4*s{>qDCtZ}IkjFKQFYmNx~SP^ zO&Dhh;K4SfWhnc4@|KP8@%L&15vt&*KdD(D5*riT>Z)eC1@yeC(5Bhejn;*Ya#aVI zkSKAuZfdf#l6Sa2e^)WulcR-}r8s{TB@Q&~p4u3{Z?z^0%=cLG(tTsioA=^?`QGU; zPO-HGcys{F_vWP1aC@$*4a98Kz@|7I&u&_gxlO0X5c4^VBolqYOR~L5(6QE_W0!Ks zeTXP-{-VJBKj3f!97vmVGnBB?rPnq2Y*Y*C5ysx@$#+hZgg4-uNZ#ngitFsAC>M7Y zy|z%Bp@B+(9zNhUITdc|`Py|0$=aI4R1@I7_p4Zc~0y6X)18nAQdLQF-Ey?xzP+TndfW2oe3tQ{Dk4qk_&0Y`_O z9orw>Q%j4&rVS{F?{t2!iPOeXI`%&<2X9Fr=lE2*^h2kRUUk7m6UjPG?={_tW10|( zFD8Zy5Sr`EAfwy4XY_-y^MZo=VsnPts-gNv*s-yHa`?PmG7#o_Zzl=Hfcc(%CMyvu z(it`xevLZ~&hS}aTVD@J@q_EVifng>wr_|K+z}9hi?U?$-eIOI+3-eQvKALaWWcu- zOj*4n91bE}4AIk<8bJu|Pvf>Z4Sn2aBf%4h5L~%-6^M1{ily&lL@C1!61$NIl)eMr z)HU`&gwhpFuHqvuPRYgY#M&a=U@T0PtsQ3hfQL|`j{oNKXyL)nvu;gu1?S;)g!vu- zmJFfqnI}h9UC5`ASb_Q8Vlh6XdT_;bM-TA5p^BWv>eNRZ4Fapc2R`Di^?3JU|L=aIW|Zo`_I2zqIfv zHv|zh?(h$TJeiXkR|aLa9nNFv<#Z0Eqizwi9p-w~f!0GJMUCUm}e=Yn{l zzBFq`A%I0f&c#|(w6~wYM}{#+v%3)WrX$>tN<=p>vXQ#~WgLlsE-5OCAp3pvSKv_` zy-%kez3Y)J)tsBkB2uo^mHu{O%d`}#fqW>eL#sb0az{axl+ti4x@6Ook;+le58)%1adu3&m(O&>aeCVn1DI|R$i4x42;hxK`41P*8!{>QmB+$(Dl@7Gv z;SZ`@qaEJncR8>ZyRtrv;30}7EJ1!>lf5EP0aia9ze@w&`c%zv#`fo1ztbDD^+JZ` zppBYnb>~m-JFcEVNf9*h`?<PX#=odutECPt5TY5mM^~~d@`1nD>c$M8S7+_( zd*`O%M`_0bVwCd1ZzPl7&>kE{_q}j(Gn2>1u9M@2DXZd9-wL3nS_Nrr)JZk;SupRt zg)%~GZ8S00=ivjsnYcxR&831Ai=m1j3m4pJLjixO16jD8M0U5anAYh=-tmpGV3fyn z%W(1*OqqVc{Ife{CLt?NJ+wUw091<=`18r z27R+6mhb?8Fd2|MQA7FN-|ixVp{+8Bjq>Lvld!lS+9Mx`Z&G;*Lm4g_{KvsV&+;KkdkVsQ}ulr@<0MXHx!jPK(3ET zv(EIQE>NZpE>wV0;KjQnJT^Mv-wxr*BH9S_cSW5#BZqf~^Q3i5dDKA~?tI8WUJ=H$ ziwac*ke9f>3!))m?(t_y_zCX*1Dl7B=I;;qZoH*Zd7kj|eO0p0dXw9PS)X&LA066W!b^>yUGE=y+gMzB1~9tWcOhA8mkDds^Tud0xKA~hqx zNB1c?(J|uCz7LwHJn%TmU!bIZEx1rjfG=r}65aVu6iqTHTo`iqDAlH|X zD^74I8~i!Vj8j&^f&lYfVgHeiIiX_QD(L$M0K-l{m&5-L;al=~6=tv-O{h=}Ib&m? z-I%lS-~7Sg8#P@Q`fJcthkfVP&`B!rS)>4*Lkc{ftVO;-uW2pIBy*l;?j7M@;|Hkk zumj87Db<53_E5iRD!0i0^509MPGSB=@K}QJmqK<}$W;U|+zFqwBnL!?eH9$WS z)?(YN=J%nya0+!qRfXyp@$X)xDP0{XcRhhw0pGn=&Ir)Ai3J`f5=AqLh@8^H(&-qKng%w6&GCBk=4jMl46 z{tMo0JcDbe%qsu_&ngdE=63)Z#2xDezI!#PQuBcioa~mkn_UKD_dyo!3y_8D@8Kjj z@&Ws{!8h!=VPC!3HWm7LOHrCX8Sa!%OL^Gk)Hhf$!@eoeX!+~uGj=7EEZTm=QubRk z6sJ{BTJmY*ugF#HXzQfWuchsGY~ruvAi#F-7etrX65f!l=6;9#*v-AQVNY*A(U7e2 zQ4#vDihwBGeqw_2nPx;3F3wJx!w;^X3dB5Jl|av9&L{Y7aKWRg`sZs{V+gH-lRdPB zeOQNo7LN_ZTiFebeu|uK!_xC|ohF;Fb)Qr7JM?=cI#=#N#}U~ctbxWke8AtS#lJNm zTQAul>9jhYyXwN^j`hV)(l9N%&Nh2>cGF(>cxH$lkUzQV@n#6lcb#<*C%Q|*tw-a3 ztoJ(WTU7;6hMBxw3Ysj#XR_XF%KJQ*{iNezIbdI`-FqLf*Pu%*PfM>)*gbBO@wD8M zR~la7VS#dgzNbKXXGD`qPG>8~9rQpelyAtd7}I6-qCq%@|BxV+U3JYEkJaFhR?p*S zv+B$5$$&N^Jvb1=$WYnEWXjz%Sc=j6+y*6T{dp9R>6nG$qcbEGijgWC z=9Gof$Tyt8(|x7C{2%)$xU{ zhnIQoqD#3Hot|zDXkm9vE?3Xkuj~%6@BtBoyW0qAM%K9H(TlU>3mmIIXOBtyQEZdL zQ|A|3Pz2~j(Q?nrtx;b>sPJnr#$0TH5GdvTn0~)H)$kc>blefyGFd6Pt%`pJnx|9| zr`tNn!JVD;^!@H0st2H7N@NTZx;r_`2|@}Nk5+JrDferE&oy`a*`2IU3ivOzI_!yh zwJYo3v+~(UD-o>YRq(D;Z3welu<}2Xz8SfW+f>#aE*=$(!?2u zzT%ad7Jr%QkBK54@-Jyv$$!2j_o)n(Su#e zT_GMP3K&?o*>QGEj*`$t*GN|vqJ#pdV6`Y-92y}vy7xadAYac*e;ifdxM%i;w)J4n z0pYl3mncLf6%o6@?=d!CY(=d&b_lya7HZ)J$T|>eNrY1{_*VpA_k`U{>S9_qfE3(2 zzE=b|VO1<}g;=^7_*v3X>*ZmOuzDhFzCS zQja~k&~HB{aKjp%I4pmVgj+(BU>Og2TUk|D;vz()25$b$#aJ%0RMMY|9Mz2lCmmxa z9>)KHME6zE8GYyAv5{HkoXmdxU=Q2LEb}SgP{`%BFjMUXhCFs7-;?lwB~PPW$}QXU z*tRnj{=}N4c}0+cyXPGDkz6dyJz?UfhW1jxp*6_B9XC!%rJhA|Cl|Sh_y|;Qnk0?w zZql`L*{^w<53l-)bbzQ1NrQn^9vnI?X8zEfDjM=V#9j_I&Ei1XywZw~?fy4U|LnCe z_pr9wsjSwv77$ci)lD9P_F?m3^gdT%AkYn}^dju`P#~zj0}`MM?(5-I=k9LMX2gLA z+8mdYGMeDV>%*EYHv=W`1m$tUe+#$q?x=)cJeKw=!R8<+g`_{ znZT!f`RA7t$Wm~_7lYXh2UvFrxZn}r+2F`U=r+Bk6b28BDZo3@|N3v^dsw5E&s z@9cK!n5GkEXf5allvQ>wpJP z*PXjtiDT~mX(OlHB8VY70E|!{m7C4<+ z*Ca{DWklKMU?*5!N9X%NB`k#|$*Hi)Mn*LMC0fhJ7(m}5IhbLj(CNI!lE*M(ROQhS zW~;3DI7Tjte@ei^wamT&Umx6wT0<@D7&EpPH&AQL!qPMV&!V!~G}%`?<9GhY>GI?; z!`XttPV2we_xp+OygcTPur1{1Ez*Tnn;9q*4gDUT?{zCGzG~H7KD1xfrq45!fkB>a zFb*DuC%<4mtDS#*`R2FFacb6ZMEVQn1avB1^1tFc#h4YyU{3nYOw1e1`Ao21TXInZ@CKTmfB`dt z;cn4S-Uk~!uI(?>vqtP506H4x>WNH`nTv|(H1jm>`n0Zv=6;^Hd#BV94E~zY5MNhY)9Lb7rxVteuxdrM#o3O zpP_b18Q_H1jmkjw;rfHRr1`Ae-!J<=KnW2VKgqdXG5MbNsV+WHw@!GM^cZm4i#OMj ztZi`b=GVmoszKn-5?K55-L7GS-3sx0Gjni>?1hN_4)6c7;a}G`8asb$uv(Qx!xHMZ zN<1G%t7(#QWrR`--1ex=Iy!34q(^~)#FJM>emtD)ep@(b7mxY-*jFRC4w`U!xAme} z_8X!z9vxHdC?uDE=$@SUd;EZfd_!#vVY+Eh%t|X3*2fKW~qH8QT3oL(u zgMA}s@$s0)9sN=WA%*Pjk!@TXpE^2-7@A8v#gxZ4o-XBiebFs?N&P!ut$}N`gS_#l zLfldR$%3gna?(EB*kJLO4?CgC7jv~K9cl(eV7Ir*eBnP8n6xunB4=9~l$a2sSYEiK zW`=1L;(+uRawp3ZY)aZIc?5#IE#JL%{pcQrTg;47{1qU$8I&X6r@`+f!x~@r=)-B# zY^1X>lT4mIDgJ8YcXbUyfoutMCniHrRrWnR5;e&7m9v1B(VO5Zh6uwqsLO}_m&yZj zE*#xJ?_s71hQtHWCo5qOJf?s!PMu-;&6VQNuVc5w$+-3v&#U3D&W8z~(mbv#kILxf zh=jYR#XGQ_LG10t%WcdsYp-d9-JVbCc=OcDSvb2v4mj*1NtgU08eeZ`{Jg&X#*TZb zX{0q*L1)*ZM{k-vv>nbK#30{Z+Xc2HhS2TF5gC7CLn3U-%N1w0%ukq`&um(&*dYlX&6sB+Ps2}mMt z+I>t_uP3rH0gluA;nX+w-REPzwFb~rBZ6;Jrv{JRf(>PRSlpA#H69lRd*U+u+(v~U zJ})i=`ny)8CxGbje+4uy6k`vQ?)LHT?oG|sM`CHMNcuA4zpz%)(}%u%EgwZ__5LQ` z<}<7?=lp*AYr7x`4;hpEtkghzGL1n(IItqke0Nqap=EKH?&N=3fSReaAE0F>_HIy+ z`*5WG={+`>BUI%gpqYD^qEQE$^4jBrCy+6I>h^X8``&!eIu#^xm^<`Ro=uM-ZBsUS zfS$Ta7St0N6bRG)gK|ZdwY%u)wK2b6>gn zn`VgZM7T_=t0Dv01A*6C!9&Q+WtC0ewR`aD`&@jecp6jW#B+%d#QR+3M_x5tvJ+9>aE;aK#eW3tsVwhx&8u{HWNC}pe)|&*O@30 zAPLvtE(sT}h+VdmmgwYn1YSf|#7bkADV^O^XEQylN36H{k2 zF}PFd?SMq)B)re4PZHlYP4)UXVCtT|I&nS3AwSdptz`y0_?}za@!X~*<>=^Sb#3WK z?E|8RSQ>4e>TUdLYN6%=)>eplycZKwCll$v1fIZO?m0igdSZ_ zw7DHcw}X)9g#mdUd(rjh>?gG{%8Q)`NjNw^ez34I7oG3WMiH{^BS;=a*q_02hGwc; z1`7>nHR>8x|6h?^kqS`TfuX`BDWrOOVMMXd|Gu5tSgzTs^d>4?XJouAQ~T~k!V(O? zIza7IoQSBuLiuxgUs6qvAy&5V(#boR?0CAdnZQogiFn7|x`&MTD+8j_eyzA4rL85` z(NS{mWu0O$tN+<8jrxT(L! zUi67@3jBZi$;-S!7Btnr_nunR(FHqH?nrPirRCF5ESOA+Cr`2kE~Ca!R`R{>x#Tc; zpn18%&ibRX!sZE~3=p7+1mWp;Mhnc1E5 zIaf%!X!&wMPST)<%Y1ITOlJVp^os_6HD{zf1yaG$1;ryp`#;+{=}+d~R-}11y8d=k zj9P^)JXsxoJm_o#K;lh9)~Dz(n+MXv4=jHADOzCHA!ju$(fsL!c-snR7s;7B#kgF5 z0h}-BN0E?f?*vy#GIi`zz%sqBur+-PmV^dW7F+WzHU_loIlRX$BS7$GrQw4u?XOy3 z9{FqMPMx2-LCygk^{3y^7K3(F#4lnbZDChLq_ln|wi&2vkIP6K9KeDrMA~ArP|he*Iu`=Eb+1!9 zZG|IYJ+(fD&%||i<6(6^ao#-^C5fz@&T{S_#ks@{+A}!Bqj|(ZpE+H3FVnds%*YW) z8-_P_=goty6lt9P5;j6W*xZeB4rorJpT>~j(XroscEdSi(}W-XGqO!gow*~3AQ237 z_b+?@E|dUv-EwSmAk$CE~))&UbLa^wB9cXjf1$*YH^Qw-kwp41bLD+hEa zhEA>cs(*wPW={Z;3<;^;RhhZmO-ca>dKfyJe(L71k!t`oS6JDT$M*F@IlD@H%!eF- z3nwz|Y{B=L`~Zn8(i!Tm+b2UJkKMXn>GS?;a#f#YZ0x>91>65>+M?E;EnPEe4ToXLhz7QHV>nssspqg_LC`AT$c?XG1)$JoT$ zAvQV}JQmHRL?Uv~pM$xXC|77q&yy)jE1q-hJz`JLu!Il!>#Tyip!hv<5Wgp4AMZ^j z^xju`L&F>NP|SuIv!W?6*-?ioNoAb$y4{7*Al2ErRr-DOBhh{AL>eP~9Ab=6N$M zZ&>3c^iKQ3D8tZ^aG1MaVvo)07$0D$t-q(2gRLZ_jnIJ5rQ9Ji*X z>aJ_c-P3d&S$0Ql&J!kBh(qjG4?ljECmYOurCeAWIB9*?ye|R2E7G9NbDNR}w?}u# ziTXY9+NVWEXJ((E+iF6#RvyrTYhvg>&O1W1ahE+~+A}~X8RIjPB!0F(>N)u`50b6-nuG``KCo`8rFBE{q`La~tT_xp_{dLg zqc5746nN$#_}w>w#hy?J?j8Ry^)czolJt)cFaqgT^#~>Vaky4J@4A!_*1g4j)I#Wt zo}*7T;CbMuQnX^NJDH@M>PiLNgq{E4q7_|f@k-q*otO6oLOcHHqAMyg8Xz+}`BhT! zVp6yR`{GPjqVsXIdF#kaFrFJaq-D}*e#g$r@|yPBnYJmA32L*=Dx+#nP?|F*X8VwW z=)IK5)yEHauI_U{(R<()!%<3A3U}LU1ZfL-b1M&VC(-!OL}sDKCwAq!Qu(`CM6(o#asgZ`1zt7**Ut32f?&D1TlP4JNATCuk@O(Xkds*%lCO6i)iAF*;BQGMjZ+sTC`*=h=hSx#F>->B#p@1N_HWvj)=e)co|Vhs>bx2}x`ggBPF`U64kAl? z?PUT-3h>QhypA-W2NE(=4xkKX03EpReI+)uV@|4i*7qBC4^#1kKLEracTOiu(Yyay zVc{MBxIU8uCfDo`wTT9W<4`-u-h=G3{Gyby=md8aRTRMV9RXPt|tQKre7v zJnJ*?C7)uxK!fv!@(j?oly%xDoirJdeHhQuAh){TP7fr4u*MW>+G(~RDn%zQmE9i{ zgg*RxR2wbKIQ-3eY`=m#8H%?K6M??K3jO9=#_wA7ZIqCeFRH_iT{j=82yUm({9Sm3 z$JVlGk_KB#g~)MZw98YjrLkP6zJBM)g2V+p`l|ab29drouYiOOj9&O^{UzDn@Ve^Z zf)iVN7uq?2wJekfpU7>!aD+I|lSTSFlBqUVWotiTA`(;B48}5 zeJ?f%kZw!i6EhkF)j%X@+Jw7J!i~5&o0TMcQZhb&PK* zD@&}u<`y`|L%a{DOnZxC|6;s?zP%{BfQ&P)Df2wF*4^`H>cAn4JdV#e1X)>5I2m4$ z6FB!5=)uJWTCvU15cFX}wxITB^|r?|tny4)C;s8S*1n+dmpvKWWB5{GRm>NZB@G)T|y5Qj&0V`EIqIh6IY=qYvYJ z@lIrPIn|%hd9(XtBCV`ShAh1{%M}^;>H>;BH?(ucIZcn4Tyqsj*_P}lGAr)!scwd@ zfpF}pL2Zcw#-_gZ?OVM-(K&U?qg=~h73U9OfsuP;pcGq~mAScUM|1XK6I{){qLgB1 z#2kgdN_*~Pp$RV{m~X9h%<)T z%U&XB<%ci>`$LUVTW^?km;vBEbuXOz6i;Kg=j?S|_aCEzToAP2;#>hE z@3$4|Bt1)XJq^8{EiY`$fUtvjHsriBjGhFBReN|9_j;2D+XwByt%OS2j1&=W1Bsrk zKzs9L?4yoY37^AHt=$c`L0tY}#Q~K=v7IRo4*0_wQjFn?4_P=SGYKdVMTw#-vfJXtI^f}q>Z(1i=TB@7AI|27{<$8QU4Xz$Ta=eZnqY}o5d!XzA7(d%8 zs(1tU^+kz*cHZkir_a9~`-KU4{IlBfz3AcccvaQUCVw9kmum*SfoqMvbp4(#4n^(( zU?~YA_Xdl&i?`+}oS?`(kEGbjPEZD_d)0*Qq(h zZ9eifOStdC7wLj$L+cJvuwPrbF%QFb-pBOJ$~nVvX)zQ`BqK=lAS;i3B0voQe^^g!K}sn2xtL} z9FE3y2G(<&t?fVgwf8K%J*TzC3CUo0c!YOxz~FOD?u`9oWc*-~_1U~Xy5s}h?j^ws zfgAqPbVs}k&}T`>sbRRdlEo>%=k)W`e)KIOj}(9+T#42&@GUVpTHh;{qWbDjiqq8F zC33H`euqCPc@Z3DdMSFCgWv+47X^IW>MsIpJG-;z30S72{@bRyuluB~u>Ipp#Xk?q z=GZg;7U51me@ePV_KBN@=(q3H5tV?JeIptpm3jOM4?f))WDu-U2^!p8`j5P9aLDQv zg4M+`F5F-uKfwnM*fJ@$`^3h5n_#~#N1;EWKzxs?WeFuhqem8e(;>8WxJ2Y7<*YnR zIejm{??SU=vN0lb?`@K&0zhkF$NX%$Knf@aLiZX}HDYppg3!HV_gNwW5TIbfvA6bG z9zu3zMio7s?y7$@zE40{E#@gcU{GL()EnVz@q&J6O_6WGYWa}ImX(1--)N1^fCK*v(Y zt6XARO~JIIOeYfB=;^2E5~Giry-yXxvENu9;He*ln7KVpZ-0+$r%k(A_YA@2_Heiw zM1dfE1lkXVpA|u5dI;#(6Fb-aUj5o<^cswlS7!jxdl$jhJJ>-7sX;TO1i{ul=RO$TZ45}o(tQQd!@HFloSqb748(|2wAdS7_T zzNk$daUR7ipheH(v#C{R78)bhQP_EsdzaPI{+4aTS>s3~p=pC(1T_OW%XhzEj`-3@ zDgzS!_@~6!eTb_SFpakw*ZU7Oy(-aQtCBGLw=7XzdG{>@Qzvt);z3Korefy(tmuMhtNj%P> zJQwY)2G7Xbz@%~+VHh?~@4cS(_Y!HS{hH0a zVnGLAJiTK0HA%?Q!f0RMFvq5N{*sMv#d-^A^{Su8{(ubMbAa}J8(|p_9!r@7N`Zyp z$j@vCals%hWZ^v+{;yJQY#aPD+HQi&1M0)o3Y4%{oG(hi%?$#{GfM*0gGM?Oc0dw9 zbmsjb#qU3y&)71RxN$H4>g~kP8oGtMGvP-Z^E<1cM8oVq3kNVg4<7vW;Ew#`dt7q% z@iXcNkUUROFSvF6BnYx|{&m?Rb(5(8WQ22`3VT~3XaxCz2GZYP-4a{+9s0Z%0^EcM*d&+01#|J?d!d`p-hz<}vFy%5FcCCxfa zv;U@pJQ-^R;AZ2ZQNFQ>vb01VgBJ-~Bl&5GXvnkNK*>G!cd^K1JB}|LXz!G`G-Zb7 zXqq1%_>N#X)fTklh$T5cLGfJwhF(?+I=Aem;&pkAWgR1jUO4mTCabJaY_fkO@sVDk8a_bK@Au6+bOTU6U~2n&vOT|vovZVE%g@wD*4Q!PwAuzn zR-zXLWckEEBE*ko6e9_G!0LK_hGq8Zq3y1a9wDrQ(BXt^`Rn)%UY7avoWUGEmVMObQTy<2y^eM5(Sxa%dff@ zbb_$Qkm0fpEmsGvaQ1b8QQSz$_3QxbGfdWn6r2Tf#6Dr9rm zf_SploZ63Q(rkSw67eX=XsQ29Vh-1}*OyoSWn(89al7QF#TD`YLNf-T@pDp$Ey}oOWz(k?K8K$(W4-2BAzk8lRcpr*YI3(rvXp7F85$ z#NK-Xx%hd%Tp){X)*4gzZt31O+}QL8bsiGL;5sK@>K~pdlJZ|%iS$f|`&Frjk}{od zg$wH{tgD+Og5_Q?W1+H&MnyRAQ9ydgBo3uVjC6>sJc;iKkE#{W_bqG@?qNQ=sjmC) z2t;I!oDFS>ao`uH3$b4uc7X|O&# zdU0G3a5uk$LP*Fw&Ih6-db9O8?!`Zh>rkvJvIRes%a{NH!Ew{z9%1DfYme8y?pP&| zpa_i%*ko3VLG8DvvlwQ*de`p(i1SJO25$}Fc#sD-5ChUS8|SjXI&4~Y5DV9u z)%?Kf)@rXh?x+b7u6NX6a9NdeL~?W(@6P}w(xHZz2v(KA_W!8?yL7Ng+;YNuL z1#F1H@p#oge1HA@-gD!lJZj$CS}5&q_~Ts`wY_rfnvv^$t>x=7Rs31~=1z(;P<`tp zRy6$4cWXV4Ah8Uv=Dy@?{DHvFG|dXNiKRd4HNF&XJ*);l{-)0QIY$Jdd3I5)PnQOx z)4`b;nG$B#z(s5tRc$Q)G<%TN9@oh+RsaJP-KB&s)xMQu>HZthAYzPiM{2S;QrlZ) z$Eyr#c*;;`4>=kLX;g8~E_<~N{0Xf+a(#;=bcu#g^DXVtHrau2$JY6v$19+E8yPxh zY7iUxLu=oHp1kJhumgCIJj)U-{`qQOu^Uhsx8WyCQT3+hj0 z)=}Z9cQWQLC;Dl57xCr&X=VKl5xuvMfc&kv=Y-V7YIELYKIq^RLhQDLdoBNe4qN9| zRn`d4(itx**~bMqoI!iv>^==EkA8z=FCz6LhmI{^AfrOFg{jC?qCN=o?zBNxcW(R8 z%B8VXQ|xDoZKP@_qpvn*%1Lh=m7)o#pGXiSvnf5090dUa=QU8pD!?|1(6=AfDHIh- zNkwG6DuN`q=ZxuRkWSE!(f9{h6ol^P@R-hjWkJ2SV8(R+C5Gmck=VU0zsNf*k)C4P zPxk7uZM^G(6H?~*zh-tn5F7-~MT^ttdTe@?qF_7KZ@{jIXx`nS-B@f3fDF5Y`(mq^ zhYM|{J%`YmPc6tvaAof4P47vLm`l}7l5rASzX!fr!2vHy%8X~gR1TDHqnJ)M9QviuyIBg|as|Ns3SScH|4$3>e$PvH9DQqv9P+fJma18Ote$>00&dBd{%Y{01`m z^75lE11#T|ZEl(bRsLF{9!ylLHNv82A`BPf0F{|M`;HM8y&ML(@!! zw7y(D_n_miDlkO^$KDtFI-p||f64}w;J!O}qpPMIbONMgLDXLcztPXtn*ATMGgr0p zfiVtmZ{mM(C7?FJ$*Xj>Q;RZoIFV`*!iZ=IP0qS&*cXH<+fgkYX66HsZi%9=@MjUSv+PM9^XV>a!ozZ&vHthY&s=|Bgc##EFR z2I?H~W?dvTMH^6?G$~J8O7f?<3>*CfiIReAQ-cN1cE4muLMC5>u@MvTJwJ-X`#`UX z%+3A-2yU9tOl1?a-&uxiSSSMWcNBs%ntNJ&F*`hw4*rdmqz&pi^$ES7GvZt{f_%z6 z(kiuu^50Qe=r`V+J|1S?tUDF&I!1%}(cp4FvX?P|&|M55PQ)-@;E{D35bowofdq91 zj44a{n~?hz3}WkWGdTGIO%FDk7DX+TfYC0heE!lez9TwDO7{-Ia3Aynwl@gtnc+X{ZPS`p|KUn&ZJ>#iy75te+=T|%82zAE2!=xLQA@#m z|0u!~3ntr2Ki>KaQpH|~`Yj>V0@JD$y08K+gyO>fhvF>D`VXcMep_&v)|DUqaV(+! z3-}D7w+BXAD&yu}?B83X#Zkwvj>RwH;51sU?jUI%vHnM=p^5-M6 ztG0wkpPu*sCnyHKE&(TucW{e-Lpg2#GYjPwhoh04###rYzWry(ILNTHO!<5u=_rka zkAr*km&A`EY3jlUBzSdc{twkXcn7ivtb^G+3jERBjSOzT*?FEtq2N2E1>0t91zD@e zJa^V=OVP7&aCOSj?7tz^!`9Qnvt;fshszb10-v1XFP02NXRCcfDY1gaPU-PIYcOY_ zWuSQ})IOUP;t_dsTBl_c|9C05-g-#u?j&U#t>9T98_({!tox4)kDoX^-BbfUYcv>m zN;d)3vAC~mCwEC;p1N=x9?L<>Y332LGhcn10HL0?M4#^(Jw%5J8#B^XL_EZC$0>Cy)j<>uA zDuV3F_Pa(6>QcPMhP!(fKgW;&UE_Y)d`jEfdxEcS0^TL#0_L{QaGY+?Dc2c@miNr^ z(ZCeJNPm;mLe`>R_y>gVoRN8MDO)%*pAo@4l8P{-z}+1#z|yA5|cSEEt54y#_D{3cG`#oGEkpyaAUSxY;LCjEfy9fIC?~ z?giRFsq=bS5wYO6fZaWUBy^GVzQ4ivTQ{Sp2*mTvv5|lua9e|F`z{JBfaY$)lWZF- z{)oxwcZ1YBs0Md^I=HA&`jMKhL+4aR?{{U{=>SO>kZ5&+zk6t80xe!}9-w<;d=Qya zaMEJE$d1+gFh=4_bVx?=pM1*)gJ-wbkpx1S6YF)@1bf5cT`R3Bj|;FJiru`QbPX2*nwk2La0rL;XMZh@ ztsW&t_-y4s#8xcHdQ-Rbq@~w|)PaIlxQ{Rh>jcJIO!*V~|3Nv$Vf#1dBT0$amxn_S z--NGIcSNJ9v}b7-P95?NPbR_KIFtM0M(^PAv7BXchKEgM-pv9-L?=@djFf$s=lTxz z@wf#S_YqCFKu2cw7+tDs7!Q)TDG}}^vm%q5coZzb^PWOdhjjrZ^VoyAMswTUerZlL z&Lq~C>NJBVZ*(UhDtB~;+=&V{Ci7GWJ$Y#&e3lm6?dECw3WzVYXOq0>-|}sCjrl`+ z=9V{$9WqGDe>vkKAa;q?@K`WsS8tf{eV}hl4dl*3d>nqt9CyI0t7hwHZek%^SR36_ zWFfC>d-Z-k6&1s9UKdv7uNiJ-1ES;X4zO_$UASRZK`Q*r%_S-Bf1R6?-Z3eDZvXU4U(oiJ`5E8f+=xiY(Y`()#zaK>#s<$HI;h@m{3GWm@rr>iyZ9 zC;Mf=@U4@;4KgQPjIs7$B_pTN>O6))X#D%!0|a`it@`4D4De zVo$wV!C=yjfS{~NxN1wDet6HDk+)5&n zrt7f%yJ3IBUx3fHEV%}NWDwSvTo2C$(SGTZZ*eWZ>ImaA#(I*z= z0{d+!;CxlVtW!j5m1VXM9y~xPcv^gd{vb~k>HA9(t;eyXc z&i?~M7!j(M@4^{xFqT2zd`BYqaGk-+CwS=}kNZq<1hB6v$GFt`(&*It`k@RVV3cL7 zX?Jr9i?UZV4B<3}BzY!4k{4vXQ%qerpcsxT5Lw<NjH{|==HSmA6zj&p=XZCC zx}8sksyWFW4K~FE3oJ8d`9XXJ@Z3F?VcN+NUwUeu*z1yBYhB=b#T%Gp32_b+Dl*U4 zetSg%4oltQQW{(&#;$zqzvqjpLo#2(rkqh5+tpiTk~;8bQ&?izoBzPOstJ^bbi#|U z3tW>q>b{SbqZc<%J;Kde1xJ&|LcYb7u56Qk0ltU2;JM4ANJ@L-*bgG}hRF16OGPv) zbQV2C-r$xG?om^;^LBfv03%v3@zOFenlH(bhDRtwSIqHYYtk(_6CB)OZE7DO4=kGZ zs<^KXHjCF1^?@s>hv0C3j2^v;ib&H_j~$m$tA0In#a?g#oTTP~kNX#FEvqXt)FT|(spyH5^N|CZ_5ye6zjq_4~zVICVw?QkMqra9Jcnm{(TYo+;;nCTsviT(ca4O zzE)*I1va+x(Z({+WcvWXdO%5&wTz$ro_B(?neMO!p)Xk4XE!N1V9rxKk zlJ9x;;ZkbaOZ(m3N+ND^PsjivfS)@gf4 z#gWIxEvQD}K4^}qTGFwpfLvjp80&;nrBxLGj)hKIj?fh{CT=;Q9yv3f9~ ziyyvW_ZI*i8dmfo4@|BJVUV8hAiOW?xZ2vwd@6E2vAPOXq>t93Kv?cO-W(lW<-EG_ zu4`N*5>}#PqDgoZ3nEkSA6jci72tV+<}NvqIkzCVPZzKLWs4s?;k)4Q;jZW0N1S|s zwp!dl6Rx~HR9kVw8Npt#SKkCzeO2T&&CIj>RN4f@fCeChR6#BJ!yN0^5Q0XWP55F# z8|y&L6_|*Hl)DurZr|8t9hRblS!~E3Y^`9#2S}tNR)ps2mo zTxbB<gK_5`U(PcY*zYtm@Gc-oEBlVMp9N5 z>RyApn!LL&pU&a%i|{y2y=v$aj&YRCVFU4L-a_94by>+G!e_j_-qyKHjAl$)1ejdy#RH4`o7ET$kJ=_CDxGul#LAL|0bnWkP zTu!>W1q|ALzD_l5tfd1PjMXPthDT%EJ^q!wpvD3e;TngJ?tduvJ#flKT?0K74VZ$! z9t)q9zQE`L$o`6=k!<8DTpz0XK*2=0{lc*?tS5J>`lf=`ZCI!7_pPtNgs3;=!&*m zyZtlJYC|O5e-9hE~qdCu(^|M0s`LWAUtdVPh@+qjneFSQ7A4vu-*Juv;k<)l9 zYYVbb*1m9Y2R#~`pn_%oOL*#oh7Rc$OiRG(Ez)Dd6oNOr+jeJ$6r_I1+zsWmnz?Ox z_wp-h3OgjUTX2}6TzE#@v6aK(TZlSz3K9$&2?eC?mYR`f5j?GY9vaPKym3D`+|!w! z{aqcJS~`#k1{}Z{_PcJ3Y!?J+rXL)(o{w;&`5VnC#fCkS^#`{YRq{jc!?g)Bu$3v_ zYAJ!Fx!|!|VD2D!3(mUF;v~EM9>))4B-mb{X)53%3MQKW`CLCAv?VYA-uB-XGCeDh zw}4HxE2EzAOWr@^U*$xXMbGv_g`R_SqSwH=AG;ZJbQWtx$cWFzXLEI`GPhJbaJrL>c3 z@;DiOC$4EgIW;p)==awVtGPC_gM^c1vWXxcbtA|dPJcRhoSIx;wznt27XW(LEx4T# zkDlWVYO!V9m5h8hSH3>vp!i%4vXQ7nFEbQBU= z-DfY-^uzB_eM=rw0(TijXsWD%6w0`6EclFxkZ<J$If$lxw{=(*Axt3O7YgjNBj{kZxr8-m zNO1pCS%jk8&0_(SAk27E=0vXvj?dP;y5Uw>au2K8hMZJs5cj*l06PHH;5y=UseH?Z zB@wtK)>~hhed`3#Ir2QDdel7ZsNmcwGyIRA748ZAyAP9Pqigff#Ft81xOX-`%k#=X z62Z$q)NBjE#$>rI`JFaEK=^pXnG#0m;5>j%5kT)C^TAmoW^Fs4-cx#CiMnuhMHgh{ zv5dqw$e+@I9^AEtHzTbF)xsi78-WzTI~$=@DCvKewM@pR#2?lFP2-^-_8Q|z=d7a! z61xBF=KCjPUww0KnIMf|1v_U8?c6LM4^1p}#z)EsAA~s1O$8Tgd>Bs#;l> zRtvzADuR?GewBx8-8llz67hH`O4;9&QFG!ey;x7Eu)$aZx)3 z!dKKPl(SF4E@7inUBFiB5*E^kFaDa-rh((;32g&iFZ(i|lFW6FaWd;`)qOfMnTI&K zVD#FQa}P$qBbqy}?|E7EB?t^RdxzB_#s}ne?!W$_JA3!y3^nTdVkq*C#G$>CS|?KT zZ4#1O2`kqx*T?vfg;t8mA_5j|1WFz%-DP4Mz8VXPZ-A6JUnI($cInCVqo_06rieTB z_$Fis^DOAWJFNJCU~FSg51;nn08cA85b<*fh%mcq0%GE?5%y5!ggC~ks+-SD;#dg# z9@Ah6yVA9+e+JUG%Tii2*^iy_TC_`?#$8X%rI1`fmn@jUn~t)(5|gZe50wy7_fssI>V`H|>hUaq<%y!rIJ=vRoOZid_1dMyy=f}c9`v%@~6 zxg8`P%uAx0{#avt&hDmmu^@ zY1rUrLMN@y)l|XM0Y}%?8N!uoF%EQ|z?Bd72w$_WE4o1L~n``)xP$_)9V z2%3gv=PG8SXwTPPRL4oDKhr0YBW5oDBTev)`v4H%Jg(c@{Rh?M7e#ne-e|-Pyu|Vf zfCYl?+wpc3?hlM~8)UB1L|Y_!nuK=R%7KAnfy8g?wX|my*orye6{2W20i}2J)aUBB zp8UBkWfRcVG%PTsa*UOL3qFn9Bh@_pV_5(TbknGdg3H|ad>!*Y{v*Rd5aJX!$Q(3R zt>hro`Q6VbPWc=Y=CQsoe(Tr)G-kXwIM)M}L`wKnMD@rtnQq3xTI5E}foMTdKYvy9 zDd4NGW5B!rYd~&)WkJ!2%;o;1l%ouu|Kiq_$yk$&%@ma1ib3VKK(42)&U2U*>k$!V z(KgFV%yJbm5iqBxeyx|J+_H9F49aiWi4PnJlwV2O>vBWoxAF%gn525us;Q@XX5VgK z^o+k5@@HdVJ*-&h_nSeIXVPs$E@*H)t*!!lsfjPcmK@*I`x>-bcx0ZKxUt{-Aeh7> z=W^kpwFHpz#eFL60EY@^4YaeS0B`GRz{@Z#?5b6+lmOGSGl$ad@-%|Vxiy%c;}_G# z2S`s%-4yBeZ}56>aAUHl>%I;XugsTW(0o%L_|+_?=Hsb>7g4e~R5&qb(03(Nlc5H7DofnY5B_L>k?bpKN@9IPyRhk~#5u)^Dd9TAZG!q^?<|~TyM&^ar z12u}Z&N-+CqduVnv{;^h-XIYna?LXT^eZRRIc)%=q0jER_bmCEqMo!uMmS@(zaxQ> z^JDk1dzOXVXB4Clp#BzI(DUJM4aHsl6o5XCnQ(go%2WeTe=A55Z1!_%x2VLzAqTO@2tM|nH!h( z${s7;^o#uyP5-a{R`cP6^4fYCeRB@?bPDCfMc$Q=HnH(AV`DCFH4bD7>@^8=phR1b z4T|=qVu1J@{3H6AA<@0uR1kuv`0ZVXr0oY#Q8Q8r!8;lR(|brk7rU)%q540qUDNvK z%rE~hBkxcq!3}Pg4W*lcubIG%F@G440$S;2MjZa$ta(*|3J%)Y5xfMn7rjq#7Ga5p zFrB_wzP{Y)@a7(FZ#1O8dj!n8h#k7*5*}OT6Z|jB2kN*e1kyY2cZ1Kw1VRSYz^n}v zdlyWNiW8_@)Cdq`>&2z+K3|%#gDRtcpc6Ud%^m)FhT7&`U)yJ{CF{>*R@%Se(u~JH z`=&ScXc0Y$<<~H4=`sO~_ea(}it^%cJ13BDHy`iuaS54ND4GO(NxfTpjbL-1JaP`O zfFL~+Wak$tl@^*W6Ct3VHvC#MdU27id=-q7Irb<(x}CNHkG(JhxD)v2zx6Kox}hu} z2l;QmT@B5FNUInU3In{l4>FK{7Jxhf7Sfn zl0$a^L40X?6A4w|e$1;<8~Pr;3*-8nJXT*u9T+{ZlYM`^X)o+}2Vs%T_aRBK)YwCK zWI;m+f5n*OBGz-9Li#)W`$*wL(&}IuF&Kx-7b{{jM}1e~Lbl-z{eB{`Jr^bS8Si=DQS zkExv%V~8Iw)ky4JDjre#^Z~E6B8sCWHu#jt%AKjT*tr!J8VU)@ZjW#EieSjZKKpp1 zH2hSC?<>VvavlI_G;grwcZHdZlPHcN57YYDMxSEABatHFjeVhRi&)P}UlNul09$r7 zB_pNFB}WN}j>-B*Liv4GD(RVwN)_#cWz1gAeW&|h&?x$p- z1p4>eC8m$y#6Y<3R??6s;-9$|DD$8e+;d&Qy!<%gyn z`K2I=6VR#GM0Gp@A_1VcqvYS$I8|ZhN=`unR$ES(4zg958fygf;IHrYe|*n6Z+Lt4 z8$zB(VoN4oYzeX!|2b=Mlvg{x0c6yKChO(3A`1C|K2v-$IdA`p_vSqRm(N3W&t*2s z0nfpO#TyM)D#v~C2w&{FJmYz(Aes=)4KU-r-*x)ePTS}WT)Bu6jIw{wZ|M^a)BmG- z=P7UXVr!Rw;q8L+SsHffIF(vsCZ$b%#++_buAwAz)i^%=PUqe-c5%-`b+0&PPmhC3x}IOMeTn_N&@kYB1NO&BagaAHmI{CGK$Tty;R`X zX5}RdaU&3*%)fNJ4EU_X9=>XcNZic+Nz(s(xxk7mVjh5O}-(qd|h-)QZHk9PAJ(g%YO<^@(y5W94YkI$q*q5si zkH=j`q1mXD1A3JETeLaxLKB9@GeO^Met>l8n}LU`?m9&yGK^VuQGXbyj+DKK1u?1|Ig|0~?sg5ykpjHwjdcGA$|H zyd+1mhj2a)tW8OTgLL@Wmbv-IGl#g}=V_T=$tR^cw#$T+FA@tp6chwGveeg!1Vao@^KW?5EDvcsdKdO-^|G ziQML3g>RWg6M#jRRmd{TF65ITkDOWp%YRTlU3kaT$7d0Z#ghY_b0DTATCi;e>dMfH zIewV_a_#*RShmFGTJ2>QA*%jNJEao-p_^KpVls3!b*Bd%D$4*`KdnY5!xsnrE2yzh zTvkJjCS`oC6ZuY9XN(fiBQ)C-8Dkw)u;*HX2MadrFyE_U(uC1!S^1VNpkgMopxNLG z2Z?29mULBBR?U6r1D?jhObMa`fgP`kTI`cZfhfqZiEErA`X1L!Kn9Hv8lQxj-_*$C z_2${I=e^|otW>p?XPVlk6iYDx`q3>@OTX0g3lM^2aVwsiH+LdzoPv|+9S{<~)Swnm?~R-YYBq7{O={qlzXPHzDE3a9DGk{+z47PRorFD{ zf2_|GFheF95a3w7Xh#~>DU|cK@3y_(OmKA;;)^qY3WfnUu{||gMY!w0BvCefST{Hx z)GnZHHpWE@mY_t_d@%I8C@@qYh`T`rSTV0|^8B%ouW~RURC1Eu=O{w4krI zzkD9*y|6z8tr-R=jOZYkFEDCnhrvuEqywtU=zcDiRC(`xWs8q>u_plZlm_5LFlTR14>`h^h-Z8n&BAIfGqZ-jUe; zLxCh3-S)S76b2491Y^7lf@-)CB;3S?YhTYqHQPgOsDJ`4BN}dbKpwu!lZz*}FW6XS zN(cmYFFtfh;{gNq{)G1XD<)VwqEMsmGH!(%G@V|gkP2>r+X81jO)9_<*I7&V)xl(m zP5Yu}nu|BJTdzr)vHYTft(-n6ISmwjYzG~JrWll>Tvr#!YCCdv6rMbA$ji1P$0$Q2 z8vp*E>P_x(U;8!d?4tt`y6SS43H$yI8I~8C5_$(Rh^~we4=k1E#{a(Td7Lm$mc6Vg z(|UPWG{Dnd>foZzqr`%*(6Ax(Vb~;sxI71$_RH9eVrTl2UtXoB6T!MBcwO$?%@2!X zr8teH1Y1|Tp(D z>cJvbau66V*{GR?+gyn17*xG}CRE+Op9Xi+63KYvl!i&Bi{rd!A4vYZ6*-4TNgH zBwBKp_--M;_BttGxnOqooA zL>-DZKMXIBi7%tQrIkp@lF5yVKNveiJVzrN7x3YJYG2a7R*1(~)sJyjH>xPkx9FHV zxk)?@4@KSn;qntGqHRuYjB1cm1?p8_ZEB}f@XjRbA9F`nfb7_NJTfVrazTv+&bVPR@Qm zb7(t00$Tk|Rvjj{ z2D3kF+zA!*NEvr2?or9*F{L2&m&|KWAAG-wRh{=I-tQrt8W3kOS1=W1`}_85sJVSj zo{eE~TGiF9%fvq48p9Lxn67gG(p8wc7@&iUT`fQ=X$G^OzCJ*%KAd@9#0b z9kCeQ-QNxdx2%&Vyi-ccdcI@mvhJn{TsrGGt1rVTGT%ui7nNwA^_14_x?s>g{`p}<5n!KUy8vLH; zU1dYtlVjERj8%VZYz4yU9ky@85(>Hbz^6VIek^6^pT}8yii$kr&&nf)FQ}3^VV^&0 zZ)^nrQSX5n(S~4+{3VF98nLxj;5y7X4@%H ztpzE3&){#29M4-q?tH%?hk>J5(fuD@YijCC^F!_0Nn6aBBGoI*ef51tEl&5YjI6dZ zGbn{GzoP#$o$~$xI)nX(c>L?#(}mwuOvpJ<3SWPmAqtzwyp@}_(pEH*f)`Gd&pop~ z2Q=5v-o$DKNM*o?OX{?bxAj=@ylK?+~-28Hf{VSxh1 zDjKJutKSZM`PwzY_U1*v&f|@5fk_|XJhB5|FN-8!c!?xCds}xuniTbaj{-iEGtB=m zeCK*4Wc%*&Z;6NBgqKCcU4j%o+43az6U$6BI?0#U4RYY6Z!SzphU261G!500k9aCyYRo@%$_7O_j+RFl-%Qsnt_=-Ju}<2Clhlv|s~l!!&7 zTi>R=*h^bV)VtaD$8iVEf*m8xThe##*NM0D$$Qw^fe^mp{&PNB!$w)k-)a-LtFf)q zpLEv_6->96H=lb%qZD+A?B)H>L&Kz$j5%2bpW@Vvs z?oN}^wd!l1Mj5vp&Bog<1p^yjd7&6 z1h2MyI{4(>gf{5d9^JtTYn-5#MRyu;b5cBiSNYKucL|B1EBKDv6gbtbwvOK)(7-B< zJ-S+g3mE2xvuB69(+P!7j(-!a%7n5CcGtaHwb;+W3AJ($~n5@&0)3H#2#wQ@=JX(xg@{YdfqzD zm#r_g+P&E{{nuiv*MCpyYtx8aQ0!o9oxeMGP81a<+zq5cbEn#f4$fg|1C8L+2F77`d96?`R!AmP*zTD}2_wzT>#=^l09UR@em+`=u%zk|~)t zSnxaua!g*~G8&AC$5r=w#~sMXpEq^#O^9Sr%;x@`<^V-yt4b-Re3B{ixM~Uwo#vUL z`sf42LDT(@fl7B~?=$7A+5)NkHatD2WPY_Km}23J0neuG7gWdmQo{HdBL#ci_mw2+ zzmT{<8E*2nA1K4+e_64L>6AJVB=IhrhZ5EKY1`l+c)M%7-qzjpHDkxiB zsX0ts)a=A4cZA^+ObhSWH7rz<&)h^Z2EW=4pUK!M+o!dp6CB(Y=TOmU-r0NphO_n{ zhk>!`Q<4yPGX3S1SEq>L@sH}!$8D)=`$+4Z4&0&_5-v6_q75$JCxWuRiWB!AxL|Z8 zWOs9eUrZ=~kL1oAzRN#w zquw6G_4>0EYwfx#H@WhDV~Q~l`q{)I$IIP>WDXdt^05)oUTh(&S0`Y@_{1GPc!J zP-du`TD0=Wq{m9a=o+OM33Hq0-mJ*P+_EPN*ZASR`sQ?xOKqk7#(lmGv6C;{y)Am2 zW}EK+A5&)?5JeaEZ91eu=@OO_1nKUM1tcVtMmm)4R0QcR>5!06xJCa4#7OX`ZTTU5KG0Iacell>xIu|Q`Jo018*72 zEvmRV54q(FLedZT_@598)w$V2A3VR`wVkyQ3NJRlvI$0-pq*1$bH-BfcKmUCbD~O6 z>fKIWwzZ~~4(6%+DA@g<`g0l~K7&5`j3FDuU{59+;Y16m9$1EkEh7Tg`RD^!UWMX` z1`b?{nOdh67nIdcucSm-*|xTR$27BroOfFln=ijA?6i>gi#fE3l;<{Ei3AT?n`J&3 zC zlyXCNl4XH^j_=Z^^g0}#fXyeHKqXF2QTcM7^)3j@CY?Zpe3!&LhTZ$s{@mo(LXpF~ z^5Ts``cpkygy{y5;S`=J$lDX2fAu=ve{yWMo{NqY`PriqvC71Iu>19J^1U@GzQeEK z_IjHp4KmXs`ggS4ir4kBSyTQrziFuhq^%OpbRsR-?xR!kyeIf)`$VB?l`6{H%;NBq z{L>%K44zOEN(2KHuo!{^DP{;$qy@MW+B^G@*~*zBdnTki=z8YR--v-XO!wPg@~ z0|qM*n&IB7#;V#4*H|h2Z0*OoItG6?<04C;=wEJ{ajEtfU-<2`PcZ{5qqk zW;&2|M_RPQv-nB#Jkq5BT!?o`a=QQ?lG}B!iTP?b7c>OCY1wb0We=o%>UNIHrGeyU z)TZVXjy06?xk0EH+Lc}6-%SVoYw^mcjrkcVko+#!SZASjM^ZT;`ghAgzR@=;Pqiqk z1fK0)T$}(`UeYK5Um85O{`0!vkIuG$huwzR6&DnpRl8pI&}D1w3=VX`)jD!`%|L@$ ztr(%0BoiQTyC37S7n5Yzm2S?tpR9mClC6}Ql*gm=^fGS*$6SYD!!ely{@ZQ#mo)O> znzUH4Jx)6Dnouiy74BxS(UF&kHo?|<1W$wPh$b#P`-S_*L)4yMorAnRx20NWA##FK zQZ5x_TzHA(WywTkNWnkL&k0z50IPX0RBVa-{&g_26}aC3MHRR|?@sH_A8j04H>R3% z$~^HERm3kJ84`#zAwAECv+7SW>Ex5H*2*V6dF`vZN}U+8=MvLQjh?mMU0nik0c~&- zE$Gm=T=6k6+z80oLRz$j_h{y3V#F-d3HZC;8Z#q@3{=J!LzV4C#bDm~QIT`x-MuOj;4G53Q_P-Cwb{xt_M7+5s zJY%sZp-w1$dNV8X%AG&hjv9IY*_PG_B1LFf?#s!7^OLr-k)Kv?Mj3Y|b0z{75MRq9 zFd;0k)*mglnRPd1qt;7#Sxl5Mb*SWn=>1KX5lK<*i3?(R zsz}OBZ7+4}$$`;0IKmr#`8@CbH|7lydGjWC6Yo>4&ZToU%1vGd#G_&Ogl}QA>OF6} zn_0e9VT2{_p~|yyaVeo2C8A-l`kV3k93-G&M1W$qGIl8XqUfULPn^VG7xXzBjdf8; z=L$=zVIA`gQEuoJNZ?Y)FK%(1=JhGS3UeSr-aL%uz@`Gof!dZdKJ%2Q z=KB2iC^xO3%cU4Gh;Lq0ts=POCLV_ucD6)Gjg=q?ijRJ! zyWQYw8=Q?>PRT1kUiw9EXTt-{PRme_(S29=;@msD7Y>!R-g}slnd&p_O`{u$$(+hE zj(p@|39f<%_-wZ+%l=CXo6qK_@<`qp;@V{}QXew!nLMf2ldo=pVoGQT*T+9{Y{_^@ zQXQdhDEVAM%im5bC0;@?#XUt2BwT3(%!Et4<@_;n$*(a=5*_%4zWQ|@>DUM|CHag3 zA!voW#945Cj!1Ob7If$#w@l3N@zu{k4&N!ZY*Ec9E-|1N@ZqTVj!cEx*PgJcqczKR9IiJ1kw+|rrm%5IWtduzgQ;yI zVf=Z)mhLExuj=naTq~HN`Kxm%u*bxA>{!G4-`c8c5lt-1Rfpe>pwjEqKjr#MB7cwj z0?${;SzmTE1EF)0JC`a?;G_%B?Hy!g@ojc*a&$ln2jOz71#tb=F6+|I9?1uvNYpSd zq}!QrWZ<#j;V5ti)x%>II!XSb96)v&pdmRGxFbAa(R%$f$c* z#18qOe6{7V|DgH0H^RXi3vK^`jGikSg-rbZePp)Vmn_Cg6_Y5X!;VTO#*)EF|Q0=f0f1i5>`Aa_qImNMC@9~J9xnemKRUJQ+jj4qR81r9q*q+f=!GvMq21ZD32^^rS`9&#p`o$Tmc_K1PX#ou|Ex4jc6Tj4nSC zS5W;7aY{i~1%DIa0A(9-bBBHrackyY1$+nm5GcvpEON8%ZpP5sP9Gz_TPBl3(8FyL z^DegrbMM*Dn0{`?UmrP%BO(5Ue?klnSd-+UIO|aus3Np;F3e9-uT~dkpHpUXAZ2nb-2I${1mZ?|m!d z{$!yb^%6rw@V)6-MGuYlnqaR0FE``O)gYpA2eT-){X5Do)qPn%P=NaZPx_uqBj!sP z89X6oHfkJFi{iv&zu056=eRFZ@&KeLNrzu<6$858?-?33)a!3uA5lKtD`$ibx|e|Y z`j^A|7am;gh>ic4+cfQj8$(XP-@5(ADGPRcxgTUQr(|29AoOb7t8JPy zIo?>UaQ)6M;~=T-XQySmg$ai$!xu$i5lN_HT36;l&4vBUrlFbeO{+k>88819Z`?x% zT(_zMnm+Hzl`k3_L<#~mL(&_ygzCFV-;TeDl<{k*Bg8L9%FljNG2f_t_1I!ynCUt> zT>Iv7n26NSdwJY!RnGIRCxl_OR8$;*-B}O%0kG%3g$Ppj46LxfU(h!G3}7B8y)D^!Liong zWzV|5>sgB{n5Zid%Dzg`tKsn5M>gtGR>*cI2uhi6&Uf)3sJNR*lQ zLZ{rb(zE*o!7IA6(jvDn(O~^=cTW}jZU7uPEQ)EPcQ1t+R zni=@9fN2&{{~OpubDN$g*)$Ed#in51R%mc1ZQNo576{$*uP@HsINQH`fzd45_S(NQ z%Ty2WI9TXjwG9lf{n7uR|KY+2a4I-#%#HJ@Jr$kl==snvOsSrFK-k;@ksa9<7xf3{ zWOdG>-)Sc^-_bBw!e#q>84*vbZG6W|@>O1)&?**nZYtdbe0HNL9gGk@7bEA@oCpQYqC=D`N9-l zSTzv+-S=+65TQN-?Qd!GHA`NjET&s|`RC>|q*X`dG-bIG#^lnmf?gLS$i{v9PWvcU z8}TtomwEtq2(<+m(1x%#m)TP33y*TZkte;>7ws3K4Zb}3i2^WJ4d!t7?{gfy=`VX% zXXKJK#enK~&Nw!@){GH4IOWYi6J1V?E-p-51zZuU+Si`|KDog0PQHj!mpsu$&~{(; zfMqK=h>L3V&xwTs9IBbiO?sfz@KSa#%MtBRmvfaJi8eEngn@Mf#lqm1gAvr(X|9gS zBIhBvXKko-Skd;*qRw{ahfmvaKzz0Es^nD-_cD$%=U>I zMuU4=7)(U7nlr3^{vl~dF0sBz^R!xuWkCr}2>FPUaGC=@dDlX>0lMD`cpW0by5B~0 zZcqvMaA{iWd_!vJKxS z&Mzeecq8*W#$p&dy(t>sXzXvFy3S_)WEg9x>P$W?_}#$n4g+v*#PF}{iWhbSJWzDW z!}_ER7|d6s6BDaMS9eYGo83n|NaGq(EN!nG`a&lO27djut!XKnR@B|l3JNl6r_(AP z-<)~2BE4f7g%@3sH#jh(gezHr1qh(+VH*ngxZT`g@q33(IAVOQ>awH^HoEwx8XRwil+PPc{6Dylueu4Zk8?^+AD1al%ANRl-y)m zqFe6=@Dn6>LtNb$)eoqpJQPnS++g8@COi2C%3#CO(l*lgtt|$=3 z@64BPG=THx-E_-@A&f6tjICOOPi{ntB?|_$kt_}mj<*pWmhbPbJko>oN?#X>JGG`a zMns+wcv5APtcbi78yD;U;9tl-@+>0wf31C)e(`u&yk|r5V>@$uk}P*}DuD=$gos#f zS|4~O8?b=*tUpbF*nB3+S59e6elL$Bfss#`8iJKC=AF~<&@f#In;Yd=bjL$ou#oXqt z7z1`(F`uSaIr9ZI!3iBk{2+nKN~E9>CO&goTqN%lT`?_j8B${gMY~lj=d$hkZLbB` z(QO*mH_!+7+}c-xNA?w9eQIuCA|tB-K%VuDWzGYxgQ2`C7~jjueu?5rnN zlp&~CVQ=?o+AZY=9~_S;)f=j1W&i(;|=m5B^ocy5O>RdAT<} zKFGVLMF;e*ArE?3AJ5yhvCDRlrxJ&>Fqq@bw)Nt;WXM&+o<~DH;1h@;csH8^Z{kyt zYx{6xv1DZOnHX`ycBp;L_ zONBq4bl%y63wj(-#$Aq?WlAXMe!hga+4wpo{%*Jb_D!6pmSTNX?h;F{KV>NfIWh6? zb>VMM-iN5Gga4C0?>sId1iB&BFmzraz45bPrNeq zdoTL#?lz~KpnCpM4a*iQzlb`Ca0HAqX-!&d8z~qMRFavT4!&`i%Ih4F zJpd~kLMaOuw|njy?-R|dwx+uWPUT1+L7l&x9z>^ZMRPqiD}2udK6O@BIf1Z_i*TKo zaV_!^K%R@wQHvU_nS!KcpnDQ z2vn>b2+Qx#h$$e%Lz%03HS%Emy^}xRNl6mx@?{LiT0LNQYA7Hyn(bhzV)Fmj1~-je zF`GQ2?{UKl7}lXyo7LZq4v!1qtAlr@KpWh8Jt;{HZ#dPJ zhl2QUspdPLbbno>ji-D-bGhp(HABbQ2AA!M3=gx2)uU|mAm5TQL2QCcxZL zD-Mj|J8_P|-gk&;HyMGyipxFcJN15*NM(#Lf7A5YMg_{fs5?4FgIcF#lZVE&MCTpl ztAvkrf?x93e?0^A!QDV6yyTXL^}(gui{67+$M_n~Qk&Z<#3A+lHUa5-;a{QED<{qL zA&G$}6kWrU&XsgxkG%5s(8VJJl}2%F&e`SU`hxH*D|Hif_hPJ+R>HUVNA{v)miZBI zSbOZ!=^@@-NMqSuP0fW9_PD~sbbqcBpg#lbh!)%q+KP%Gy{ophL;Yco7Gc9 zKX`uKoBk2Mv;Z;en(5xx!zeylnyVpT+}#XP1q+`%={&gQ9!G=p0vleuaR>C14?+118K<|l>MOi z!k}FK23^dZRm)7;7*8LL*f{tox>s>hu`!PiS~%Y;iu_UdK5E#O5qC-$D3PeP z0)A#w<~B1=7A4LjhE}V{%~nSFU0MXDAl6hsn6U`oAPm^u+(`=}2KzFR8NU2F1HIDo zP`1j?245}-pGF06txa&muuihqkqd_r#Kc&6jz(X-k*Yy}B(o!UEPH>0lP?px+dC%{ z*ML-vC%ZA2d?0@h3j>zFH>_D(rpUS6I!vO96=AcJOL{LcTK@p=YG%i&m)k~4G?E10 z8OpdE5H__=3dyh`10MJa|2et`#WT6r`>k zvQrSiCtkzclb?Ra4QTci_^zdX{gAdcr|=NKr^uxdFoMVb!VL!YWcqY^2RV8dhj$}z zeAw8CC{=$}Dc!JJJyMXo3!B2@10+%;Qg;Q4IRNGzQp|77NjxazO!H^s&F1LDTMa7RA%9iPMqsVbQJg}uzt@-SRSQEB26XO?-fD>T_1A&wHd@tCpTtjsY$6yJ3 zsWf@IPS_%+^?yMEU#Z4*L3sBn1y^=_K{SVcm$$9ZItC5lwDDDJxcEmqt?UIwe064b zR9o9IFbqk+sjZ{?A%c&=@>4zp32^=Lk^Nldl zamU#4m0=7X3EW$svpO}5V(Bua(zgcE&NRfz?U_T;aG462OhSsxUxAQY`88juQIKqq z>zi^++4Hy3SB?Nf^f`LH-j+-32i4m=h|_>B=%Ler3WfExJ<}11E!5`Ye=Sgww#RXs*W5TkF8%ew~htrJ`&>l91lb;wOfl9ixT(*%h}o? zW4rXQ7(USN$Kd57oA1=$ymd2^%+TNZrY;A+Qp#}#!r%0R@CR1^yyi4o{82r5_fcKK z$~J_f(r;x!cvOrSk<<%Ss$J5GcHRFDkjp<)WSOg3K>eQZ_hbnsMF*-rQlS1PR!rP! zt70(2CVoWozzFale6GU#mGQk@oxV=MS2KNZLv%)i-WPz-~hDzT}reK`#2}x~E zR^Y$gSZMjHmVi4QQRPj%N)1*^1%y1u*A*vhQO<-G2g(|KsrA-l^GY{Bp(TF_bh9u#3)-HuiR)faD>cgf$ z)ZymDg(viyl9C3?f9qAPie}W0J|xJ~sH(9RJL}UTsuv=gCw0Z{>fDJ1HLop?PM~}; zN1ZL!Oj%g%g-1QZ&I*^K*gbLILBiNS`6*oH9RD)nLa(UW1EreX6u#kGdI>Ob-ZY7( z?89v;mk~w_Oq)Mf2HH?l_}db2`uT}9X7126^%)LBgjQT?Mp(+CaCVgBa1mL$oM60* z*~&k0K+L>qx!>WkRbypJ!9&%|XKi&*KSr!}FD+J`!V8#i>!#AUWTDS~%)eZm7TP78Q2mT;c_It+!@&h8x^@PT||7yw=11=lAV{vAE+BJvsHjA^oKRXwdyl( z!%_np-MiDys#Zx(=NH#&L(*LmgV|Vc)&Uef9rt4aE`!C#4vi_8vVtGMgW~iH zXT=ety{ym!WSkSODr~z3-#&Jr!TMu`!;H7gz^aeXatqf^<4E8I?qABH?gXA0oL3Mz z$^bL4PjF0r`i=X(+E*a*NU4?YC;XU+I77nF*BTw#l{Bj^=o27&HWmq?Sclk9ZwHTO zKVUvpbTbIaI6cl8y=Q`@h^BQSe9CvBF0x7(b_0XdiS7{O?s*=eqW?k!Z0s773#cY+ z+=;X;bRp>4N`l2;`oD_t`G{baqWLh#ufSIpEZ+lXu(jg_V*$+XEB%MiM8F(MsE;T- zgQJk?NI2DZu>Sz4PRrQNM&8mmXNkdI4@59U!4_XsMdJq?=a>Sc!}V_sX;^CK)B-7+ zB6Ay3^9M4C9@SnDVNP8C>Nk<>KcKCq{ZdyvQ-}Hyah!psyl!r2PbbJu3$VfJstvb> zBlQGpgQtZ~^ARhIRcX?Hdy2 ziGWM^ftf|426q1Y#nmsO!^VPg^)x07$wyFvN^q3S_)*J|=8J~&93Nd&vmXPjJt>UX z8t~ioGoZ`+92Yz+MAcznKN;)y5o5}cPLj7JJ1 za_zY#hzfXYt3lE4L_2QIqB_D~f+L;&{-HQ5#@k1yZc|SP5M806G+q1*z6VWqcKPLP4)maPw z`U}^L={lhaQo*{b-lf&AnoUv3y@3Iu>ZA_0sf`i0!oV2a>g&&6YPaR}zS0a5JP9ha zL%}BwGqDM+UGwlSaA}Th&|`d?PCP>PmlgT^AFvN2o76`{7*JLRh#5 zf;~N60Ny|Yl`DD7k7AOm_B`_H2$bAZ`cHhupU4Tk!>K$C`-{HX10 z#*cE^;@MP&5tU=-rPa_1Q5(24z}nzGNElmNLpG$PV!|}D2_VzFd%W$6s8*> zb`M6n>)D@OZ0@Yuan;>VkP_5gr=Hh zj#6p{L^lBMyaQw-Z}LLsZmjCZHhc(uex|i}?Q5NRw$at8l6+F%$Z`eem+;~8;i)4- z&@Tl;%+*9|c#s{g>qG^JusG%AjKy5Ph(XUy5VcR#2s& z$#>Z-`{T2I=Ptt3EbcO8$%I0#uM^b>U4Q+Rw}CntURZ(w_cj3q(USs z@qRTDeMLY$hS54kz|HDPb7Wya?dlB>_N;b+l1HkUmNZ_{Y4KhRgzhylTxUT=10z&R z_z@{BTvlF=GX)JeQwST=43ri!wYAW4IW*Z#u^d|rkz#?}-X-X{G}~R%5jvQDM6OX0 zOtW{hldTb@3#G+tFy*zpW@+r1LkITVh!TwCUH1|Iv8)JSGXrzjg9XFQhQ}&TnruqB z(bzZnz2H24q(N2hqSL?VN0p^pxa7b(;W{=h1{|+%{Rn?B0oxEzn;l6L&%9Kk*;=Ov zpbOXD7`12L62Fx%yO4NQ=RSw9d-Mas)m)NWEDT$P<5l4{^P+Y>v!LR9U!o59nQTTA z0Eg;`CU;~=+bI4lzX#Nb`a}Qy>TSis5IugQPKP1J8J>>wxbt5U~JRYfN8;tW|1)Qs#@w&q> zpOA$8;bw-&^phJlFXby$?<{D=Au(2JeMVGeq3p)S>dDi|Fh9GE2_NHGe9^z8t*t&a z*)vtP3cH4)k63hV2Qa;$_*U5)54xZV>*DJ%A-xm_S-w*C(j0uutF6Al72=<_U9MM# z4mMj#I3gNY4X-7Vhx2Yexn*hoWE(6p64|Hd;Zd7=p!i4l2h4@jEMnKi&x8ognW%-& zT&#cxQ%EeNE3D_@4BY`_RYCN5X{(8nZj1(64KJ1y0YoP*Fh75;#CvE5BsDK!hTXha>YxF6ohtZi)Ba3=cx1`xe8=L6W-1N3K$)DxgMnZI!IySRZ~uzSDK z7aoK0@c4luOIQ&9TG;I`Pz+a`k!Cvi%yDZbUcJry%hkXN4UzHxEWwPsx7JdGT;@(I z>v@TaePg-un&@h}6wl>94!8eu(>YV_0$@BCdYh#EkJC9vJkYzn*w`xtRX=BJo~&Cf zw~^o9V9?ZSGb!r*qgj0VV!k)uT{^}HVx^g1P8(6QG zD25%ap*6N67w84J8kj(>cD}StK(tc9#hjqwm?7M|EwS&C6a0>7$ zKYy07%Xjsu3w4QoEi?j7NN?-k;2#)RFvVF}Shx21stzMbjjAT)NrC5b>JCMU+!E>| z(&y@uMWE^d7zw3>jd~t+!mXXmXPS-klObqbzyhBjsFA zVpopdi!bcFnWTCQQ|lVia@pi03iyB6GKc7axIH>ubcBI4q;`Zwq%Orj2jF(T+oc+! znzA8w`jIQbIN|k z#;is61~&!q@=^}?UBNN(6wmQZ_U3%WRx8|n^GD&n*C@ex)@Uu&k(&D0*<^D54GLG9 zr(#`ym@VVDw{N}5y(`N}6S;cp|5{WTp0~b=5&JAw6KdD!T2A6nw&~A`N2Tk$^A%;M z3c2HsOui=WbPmCo0_!NYu+yHsUhi}m&_ofL(XJd+0m7y66lcJk{E&T>)hwwvr)Ek` zn9jAOoaggry+=0rceUdT>3#+nm|P*e(^4$=<#D?wP(YhBo);g*z`|0Om?1+8z@=`Q{uwZ{Od-JBP8Zp^VtGjY=^TVRUa zSk}iI5-HBg<5RuQ)3z*MRs%!x-2T5GxbL8C1lgNU^*v2F9ja;uAxJ*h2Cx1mAC%Uxic{Hh(F9RJVLus zxYh<7DQ3@}AP`qG(k}1k{DHYfP)QEav})+EX69VHS>AArS-RPjED%c!U2;@s!fc#; zu;ZO%9z0N>N;^6EqsmstAcM%gbIk3#+g53x@~4fI_Lx$6Vf5?O60FNcxJrVE`e z69VXdYb3eZH%3?&nU6Fj1I(5Ex-o&6OZV1G_qI7YN6}AC2T>X(AKd*=Wh(^)-hWhh zAz>96tZf*&l`1oE`RYLlL_nqTpwQ)$eWGjf6L{jm{beWv;vu9=D|umqZK!+gXd0U? zd9yicl79+in8H&6@wwGxtn2(i%ZnFQ{$oMwf}rOGjLn@sbidUfO|N)d3pz5i^aV3~V!;}g={d@;5rzNh~3 z33g~uZ<$e1Bqh-3K5a@qg>>|PT!84)uYTX1v9@Fj2XEdZQIhx6x+WJmnB`{XhavO5 zNI_&!cK0d$+9ZMhrx93_HDBkF`puCPI|9|hv+TqqpDn0@{G`LMtz#~9lbugsHV_l#CSBGOmX+vYz&1Iz-X3ePr0-^ZcE zW8bR$nNP{bl9HTD-VA~SK3tH%rx8os^Or#Wo|2KI1L04|<@7|ZEKrce5dl&YmchsU z*(HN+(aGnBn1fR>61KDIB=8;X9LymB5X50i(;kZou?O&{rXl$$0cA}ye6)4S`q6JYkZu-I2*|MIs+K#AwkcAKi?&-b=IkP zJYB_E@iy0ie%lBGtDx_+P3zYxmbztfUmhh<4rwjjSdSb}11J*>jvnmMKL4Y8`5X5tYj!|>849-WW zn*nGj+kvDNMta~iVe>rQ*Fu&@bJ9^wRT>4HA60FlLA)$5-7x|7YN)PIsNC+SPwGRpO96@-x?1D;ar0UFaekMz$asnc&W`l} z@Zf+KIHXLTd~5SV&fqfQ#~>8zQA_iQAMsIdWINfHF7~)4yJuSI;Ia zdVcsZ@Ht-#Ah!DBVVk1P=13Fj>=#p_g$F*GD=kANDpv*`lJXT6-9rMO{&P@AoAH;A zVdC;*%;28m2!H9X$Aa|5Ld8SY*b{MLbQdNuJUe4E{H0Hjbvd1SeavVHeML?`qG)Ga zpJ>Z}#VmKprF)4Ublu~-xQon_y}8uLFJkU1+#XQFk;PRBfmPZrx87T_;ZdG^_`yqK z^iVMi9K~=Q4nN`d4r!ledW}z$-y3D_;h(o$n6V2q{E96A`?{cNC2^BRTyNy5aLSgXk0uL!`6w@zK*)U{Z?qM7-w@U3aK3lQ@-Y<@;cL zFa6vMN+U3;r*0$uE2VYzNy~*59)Mj7lR9;oL!QRzgFoFgd}{Ui%#WxTRNKbahd}9d zzQVS+QntLJ$v#5MJGYbM1=VMuw4koZQ=~uOK;`gN=6gmREx_hFZ==ed&%sbzEm0QH zQy*=>to#t$FK?Z%)d^i7=b#hr%O9naB8ZEIZS$cuHHjx@FB7~W*h{PyIQRk}3?qw4I$ppVJ(1`_^JCzjtPc+#3=52162geRD}6Bh%-u=Bc2= z8K>4#H^rZ5{z$a7^Pqud-yZIehq43#n zjeiHy-CFon-P`xLgL({Xs+(4jz89av?tv<&a69s)S)et51Sf^rCe$;n@L9pIRw^sv zCUzsIR+BG@X>^-p@_8jJmC0*YcQGC6%?@t&O1Pbc*)AXHKrsh^@cRNvC2 zB~=6_Uk8a$IiM<4b&uHD{Pn(z8XU-BI;wWTvp*k;^D*52;n@Czmv+Jh=ul(kYb-PGp!<7`+TT<xRmyl+jG{t2*C58;?{R1@EAY2!5#Q_NwRG90{STn z{F82niCvU|L9zp#6Dl5WDn8^5L;p}vD!ko)CB6E!828k5lYdyvvBrBe)2~4=lX^9w zEB@3KRM^(ENw0pgQ)nD<3%BnT9$PnfVzs5uco8tfLFs#5vEkdfEb;sV$MTT*M=bIu zo4~fR@gd|)YQJsJ1{Zt{`9vp@qxB8!eS0M$JBEr7$4fvPT$%Rh1?AV%BY^{w2HH~X z1{d!OI~y4GGW0&RXs~Hb|4KJO8Y!;oYaQ6-Z+8C&|FS*(&&0t0JLF(Ad@HpXe(N}T zdgXGB8!9?FmI?Ryml)#WldSN+c9IWmaIa1%JkC9)k$2=$?JoajuZoar!U|;B=e-|i zIuh!VMi%@GXqX_5rW?-?r*``e6YrR0o&sn3+rxPkJQ?4L2%s7bmon~*^n4{KxosTY zm7#?nfmguzKQC|fDbHotmNM)l4kLa&nFO&xpgy8QfnjZBz`17|t& zEak<<)Vh;!WTKIAk7kUj>0b*k3txlL*MQ^6X0+JAqW9JZ{9w_0-ya~Gy&U&l9$0o* z{05drG}SonbICZXZ{+-c@UH|U?|JsK8&IqYd-Wp>{brB;NLk_oxFO`~jii=7Di_Kw-2J647ftZ4R&U7HSLAMZ zVjGJsHH`wM>M+E$wy!OJPWnP6JyB#qHI$23m`u(ppX8WQ2P_OXa!t6w;v$5|jxo`k zk4xr5{{r`o3%GCoWTY%_bMg@}Zd_enfxEL`aNj`zz^HU?JCqaQi2VRmWGZUC`=nvw zWaPK(Uc&&9wkSYv4J-z?t zj!ST`%`dx{cmm4t21Lha_UQhxy+u$yTxA-CTu0@brq!lZj^ zqG}bDWu%^IW;Jha9>lvOXX2(iG4SGPv|+N|U%0o}CE)Up+pg@4f+&%666^%jMQoV${+}?ayejrRB>L2fGEA!D7ri zC%lX_N&=5czLg2c_RBtSzeSbN=l@6e?hK09yY-IodrF}KVLS3yE8oQb&cuny?N6Yr zB~6rl8T4%|%n86{T*#)81*?+vNLT22uT91ImsmOTe0vea_f$w12!**VJ7Fz5Lo7A5uLD}x?7DeF)Y zcLAtiTa-=ZT01&HIg(Y((vW-&n&6`4OILz%_Hv?|lmh$z08Ml{o>zRQsMe|cRug~lBnTU?Nab}qkq5UhtAbT4}6YH39tw-(%b0>r6SP8)%|QUg4A9=zMTmf z^h(POZ1}QzsO{t@LboN1?VT~P_9+RD*SCyjPF+2`CgHTQTbz~PH50;st31^zRjgEd zqZG4r=A}8v+jIAq_JQKV^7dpsO$B>WR;iMBx`F0K*XD4cozw2TVi~)4(O4$`XKmT5 zi@nmB!WKOW!NAf}B5DOY_7ri>8xw!@hmjO%$zgcg`KQiiL}~1rUy1P9 zjE-xPm?Zv#hA=L;*wE+;ci=0A&RhA57-qxZ2kfW(P3@R_jt{Ey^&=}V+s<|DKc=)T z0VtQV|(2JrCGXZ@P(_>Q# zEhvO*qNvJX&~R=3C+YfYLbVm}%gUN*4CaA>mQ%?I3pAtv=Hb*<aP zLw$h1ZJ3@e&6ur}v{hd)D<~gBg1k%n4z$$wy?H=G=2@f|KkoTCMtYecWMK3f+ zvz2p4jwpia+n9JM#Q^sO-=p==WNeKgcBMu2aD#Cv7ay6_3F7@?& zM2&m*oWM6MC15m46fCP?-zYQ;$W!-sc8SYhZ55E--Q@ma+;TdItRkNAkZ1Q zO%wV5Q|LCyZ#?JF#!F}xTc4YFR6`7NS_5-*0SL^gLc;D3y<5m8oObdh4(!dBDF3SS8 zGWWWUk7l{lZnRZ@S3g1EFK#FwPHabRknLA~{uRf}sUz9@Gm>jQwyBHPUg5c9gF69^ zjf3~IO&ANMPJyp_^U)8N<<9mYYS$$43{sD*dbP8+ljC8|`z2L0*AB4rGx8f{9fbF& zMmv70Cs7p(tYSJ6SBK^9RXsuQZt`nSY0VZYqA7=GG-D&QBFmvBxlltFoW;LlbV0Su^ z1RhAw;}uh4M)R0nZo^b_k@4l**1#q*3dIx_*@vT}Z402XpZAd2)O;qYtv=i>uygyzj?7Tpk(_#(8lNo7!371z1O&L#uz2qy;Ug#`Rhh zXbZS*d^cpR_ZC)c3#xVfZ83FPS&Pj<938#ZL1exlT>V(FbT0OpPUzU4j*PvD4pF?2J-Q~BO!#dCTD&B~^m;krrlyS)h z{B;$H5L`RYja3a)1E81j>)>^CW`Abk)hfHiD*<&ziC=c2om2E2>s7c0$Yt(4Dg_2g z>#P=en`yHOQ;WG5@LZ#%9PKK=rBlpm+5g3#+}UH2Lr_R7jnB3nS%{pT1V7~U9n1D~ zRHmwv;NR}O^rN;h$uG!29$%`UQo4A-1#=z$bVp7a`t0&G0X+f#$^r{x z@I?qXfQIbDK?LbNvpwvg{Q__ddh%}OMry?4>h^?CSG$6nK>byBK8HFCuBUX_AyJs| zoAEFfXOI$Sd|=&Jn&r6a?F};U=z}wz`b#CNdPoi*VFY=GeinSN^8A(>+UXXC9t6AB zZH?JNpupBU?R6Wl%y?^Pla0IfMJs1OT@_rt<8%2kr-TAnVeO0IG&Dy}jkZ`&{99wC zml$NmGUsUltT*zh(dXU^5sPDHA%Oyi#%#)rNq^~@Izrnq&c zARwL6p|o^2?0fKe_WL{U-e;fdT<1Ff0W-|pv(}om?)$U8kq361=iO+>Rrc-F?audf zC7=vE8gjzsQqOFjFb6`O7~Jya0;~Ew!H$5bgcJ7b=oH33)kDF3Clt~jc~J;M_{i)2 zqCHhmfQwZ3Be3;Hrg5qQh|2;6xWk7?52y_a;)eVC+)bNqSN|Bs} zjMwmb7W;8CkUF^qAd+TfE1MQ*bnM$a*kVC4li`oMbazS2WB4aM$S%OP3OKT&G{~*9 za{c_k3~ipfr3m&g@4L*tii8Tm#)kHnI%5AKxbz1iZ1X(%5$az3M7GpE#nxV-Is0Z1 z9Mc`5*r^Ze@igY~b;#}`(>)89J<(qd<=Pt76u)#zAQtbRPb?*h9Z?_C6?G|yYcb&G8 z8*1m~-N2}weiB>RJ1q?@z^wqI{DeSxv68}#-yoMm98W6&MTx^OpY<;mye3wyvTJN8 zx0oLg6o=!q_#B>|C%lzjm|O#|^VI^7kL*`7_s|A~TzSL+&1f zwOx?X-Vi!c7lvOi^H#IUI`}toQhC_9)Hm1kFuACF-(v!{>R%h);LG( z2kqUCnHj6DpF@40PDUFacl|m)?T2)95>7sv=8YYr?l$)kk5US9(`Nt0WLhjpF_h)u z+lHDD;%-6Vo5c23M(CYSWxio^ka`r?FK-M@1<>jS&&3J0JY*`)2V3mL2Q@vQHw6h|Ho!-(TLGM3;tpXmuR21|g-VY(5 zbk+<27cElM6^yj$#-5qU-Qn-fcz8hKxLuHE+zr3U01W156aHA}im#DSe$ThATYRr= zeSL{a()JY~hR0(&L#!V&3k@<%huQWqp{hMfG!BVq|FW6UnPD^lOO6VVOJDW-H|C$a4GyE>s;nCwra9u<6C_(7PR_&9oLl$AvH7P0R+%{;9te zoDP#4wiLz~GWnfXBnq34CPOuBKe@i`0zc}up`jayCkUi)RQSf7y}5Z!I#ttoSb~&A zWSA}Qpy+O*y7bFJV#@X?KxT{!me2K`wjwEXyHPDyj8p^Z@dK^<1h`q{_9hkpYI#CX z!5)DpkmO0erJ;5%W7Q;5bi_B@|KVdcKAcSf4MRW%#vokhPwyRdq@t2v*I0rYi2X+q z+EsI$VFI%?l43x5*KVAYQ8nii8bkz5yc>VBL6|u=x~uTx_v zfNfevc|2DvRL|8^spIV$PuGv&n(pl$htPbAwN@vb73bm0u~zLT7e`mmLG>+F@AGGkN%9>{=i%E7f1IcfN8R|mm7ef4 zGVO!vTfn8*AF6Llpq^S=Fj#pNxGd)SLoTQMvWgUcYf4z@#`cv}kRs@Kc$aFD4Zl0= zJpWDw0o<1kYcn19N4&pI;o7)kDB{iD`hfX+H~mT&qd^eNxsTz9F8pJC1fo9hwsdz= zw#s2U@mF=Dxqj$YIDm4#z~$SxTlojf7d1jCa8-oif_RUb6(^*sXEx3tAlX^CM?-1- z2Lu{jdWCRO00VJdR&srpLMsx>=cNiW7wa@^{^=;VFf}c9c)hp}shgJ%a>2N;^Pe0F zNrm%Rk(S}x#R!2uSJlTn!FKmwxY#f4#TbNoI59YX&2kSMrs^`S0Ron4xD4^?73h=w zy|J?q6zc{uRNXmU7Trc$OQv9u&Il`NY*VF!0!9DT0*uTBBP}js3c^hh5>N$!x$>*bi#BB^Iq` z9eUnn4Z>~AKsltWO0H6N?#N?t&&w){4$H;f2gj`M?8VO@p?b>?#Q!ikPh1aTT5N#y zpcpRe^dDxYo{;*Q?w8P0)^mPl>WG4hojVy_%}H3mA4#66o5Mc7s*Ju@%Unk4*I^Q?kBpdNzF_cS`X}vL1Jq9K%n$% zf6c5D!z@9F_Lypv3D8*P<^mIJhTdN8!YZBu)CQZvl+<DWbh+9#r7cPUiaRu^0!ON6f`*>Pvr=z7l=4aGZfPsVScp zLeL`r;=RS2B5)6riz?TZEo~1+_l6`xXtV7h4KHf`htG)7TJP8uVMX!XN%;VT1$xp+ua zae7tZ^+qEX!I3*)h8O41-^b=Dyt{~!X!*{q_*fwAw(p0J5@8Lt2);2wIiQO2L9%_Z#JQF+tWHBn{c z-+tF8h^Iu{P~xJ;;qinYkLn$WVxCHFgPEVi-pN8=y#Ih5P>1gteez1IGal0gJP6ge zo!fvPHo*-J0&qm!uINzR*lC2@vUv=pp@zCwouGezJ?AvN_Z|&#$*Kf|B^2U$JGYqnKAk`3J9k2YBt?yY%p~URFsaA{2)J z9@96~hHCnItV?W{ls26j0mE0^)P>Dkt-n8*MNB1r*j$r;eEI7)$k!VpMh$f}PQGVE z5kFFY9NG*L`rPQ2k}YmfK?|`kXk*e`iKx|uWUxO!*J}19s_!ClfYHEaX5=e z-~vFnyfp@6)62<1W*A_Ey|$lqZfVb#g+taOI5KUC-)H^RbBZn#?FOROH4k%ZeJ@;xOjeJP#d~^?7e#{TQSm37w@j)cZ9at5SZ?to;!=q;@ z9=xlCN{($$ALN0Mj*q$@@rIOwekOkg$*SbXWulIXP`@l4@XLx`v%rCnz>>4#iD|}6aHZRuTqUlNcxfE)-p+L<04zK(it2Tw6ba8$Gfa|Ek(;3W<%_O$* z6`bDL0bkzX92wwts$i!_C?p{;6W7{s0Jr3T8?jy{*Nq4c5qo4mCUSd^Q!0%pc<=r zYAWu%ng>x&Wq}yu*1|YSl7=#Z8UmmQp%aF}ZD~`X`Sx75b zLu$pq(mt6#UwRaeX&_pJ)}+I^RpGKkibeZ;1a#pBR7~bCVDCoXlaK;|-_ci&I!x#@ z@FW=*cof+%nS-c5oh=Xi1UV6=xjBz>bA?SB?f2qzA6<63Edgcu=6q3dG$_Lb=2p9k z{8LJQrURR7A-^`8-%uOsy=o~dIxRT#!X*j*3=2nK$|81bw*|xaubZSKR^(sVK84#o`2g2{|4Jc7T|xC-EH+w9YDT7`H<_kqS$ac zc&gMY1g7L4z%+ikM>p>6 ze!9c@A=~2nj-I*5xn@CX-#0>@&!APf9Ed=nnK%Svr~d&S@Itc~iNxztuMlx&r{<{m>tMO( zrA^r~F2qOV@a2O;yc6lfNbmS(V|Cwmgj^rK=mZ=OB%}u+W+e$hlAZ4h)`70%8(4Dz zf!$IQcoLAowRL-yA9=chMZ1x`5wNGy!*9NcW)?Cr<1=M`H82pKxcKFG)NIEB7OKxd)NGMz6O%u#%~Zrzkvv@#WB}3GR0+=f8J% z*IK;ejY&~HCN%zLc8EnH`_uEBcp|hP)iusOmBToP%l)(WF0d*=J*XqZ%NlpqP(2^m zZ1PDSK-Cs;x9U_he9N#W(d8mqqGp-o4-c|mk-Y66v_9v*wJ2GY+gu5o==?j25%TIi zmG&3Uo0s9Sg@gCl4K#rtc6qm?``K&0CZN{goh9@zeIc#+J6Az2T8XTF=go#qwu>%Z zXvz{aw+n~if-@F><$CsSF9yW_`Pymc0DC|(DYMms><;wc`rKps56)ED4ueb>KzKeU z3HErp@>{=<0)BcYb1Bl`C}Ec(M{Y%aE8V}^y`+4;yzlnr4dd%RCm^=N{PnRYlMv9; z+d+G9XABXFzw1@?oW6S;L*=u2`4w8DU`ZvhX=&Y%H5Wo^ea zvJDs!f^y&-PizYm6ov2SqKSF0Q_5^tpB(n4OMN)<>@-Ad{XoTs21;lxL6<}v`v*yX*oD-Jn3ISo>1QZ z2SFa|D>kVWTegV^&}PBAZd0IeBPjV@cq)`ipeJbtp(_12xyCj-B>T@Q!{)$}k@)vk z6s~n;*X-LzX=j&%?BZ6}vV8U-L8uxy-a@NSk}XC*vWAADr?;W05*C+>mWUr)$ebj^ zznCfk2HP6`|KD|aE|ZgAlqU|YjB=T`fv8TMZ7VBKy z;}EcD8Y=b%Vo?8ULelu8-uYc~Y1peaXBzKPHuurV!?B&r(ns)FtL8891S~Swve44w z%=+`vy7&d+`{pY?^(v^p*xVm+%r02Bxy~1bb=^-1i1r-tzG#TOdxD+gRwf@hY3u)T zV(d1?kNJXO<6sY@)Zoh;2&X3bVR2fRM!w3n0Rtq83Zg&8Z+TZo_r>JXsQB_nc6E~V zWp0TcR5v9DQuH#Qt9$x!)*VU{@V?p^2@Bkpvu5WHKEyv?G3*@5U)v-C9qU6*y8Y={Mx6NCi~P&7s2_Y zjB;VR==%OV=k_-L&9z$5-EjXd^W239h_nMaK5`T8OScu_*k(hT1hiHz z8eZ`cM208HQXm*Fc?tEwYHOQnG0@_<=|U2rv@mf=`(KDpgx-MfCyC=gQjhGR?v?OE zcAnNOLY0;~w6ww_Z)T865bO!3)P^t5ocPATRUg6+`qnAPTL~yEY{^31+dz^pBr_IM zGoWFA(ht5}Z7c=qDczqdAn+9(+eL00itYy()zQV5A++7ptJVXg@gd6?Fxb}>u-m;V zWi`XE%5!ihU9&wpg(h?ONrA~di|HIft)oRo(g36|_Dx;MOI?*N{%5KeVl7B4$n605 zAt?Z%4?~v0`tDID3>vhiLF9eX52SOuO#FXDbaNn;uFtZb8T9jEFM(VDv*z1q9r1 zINspVTb}Oof6*3igC>T`4nCAdm}#_EV6MfMxGf&lSEpy3yp^dmKM_xF-T%Q=L*M zyQia*XhGUL>Ens^vWjee@jz*K!jn1;x#CAf1c+TUBw4 z^#J3}#w9z}Vu28j9g66F=8;QTsN^PHmwXE-*d!nX z2gh%v(DE->mur>%7&=A~5xvZ=Iu=CUC%WE^VNyUL)GiqHa_^{|i%xX!BMF(>*Rk>`Ja4e=rj;uIN_)hgTXZn@Dy&5z_cMz|Pzj|{1RYM# z>nk^?admdUOh$1mS5q9{YD!ZgOY&QMD7X_`WoZCMeE%Zt3DWlTq1+(i^1d2 zb90wKVH2=7guch{r^stecT3{3ZU|NHCUoWhAJuF^|&`4v=xj1JNLU2301j&;U@mwG5iS(rzGk@bFn%f(k$KZYK= z*?81aCD%AtnWn+$-N6*)p@W^zwNs-Y_Je56p0L9yk`#Dw>#unh9{UV^;?S&+{pl*r zS$NoelD>KNS{f|v1-?=?H|Fmr>ua8Tl_jY-t57vPsr+wpHCpMV_>(iZ-$LXOjwS>X zF^FA!1P0Q1<)o$*&8~o|y{)Mf>i`20tCnjdwGJ|S6%|)~fGbS~N@5=Dsr!^$wH)>< zLsYoa1`MDK&b*ZU{9NI2$uYR?tueM01e##?xjlxYgSxaqata_#o4ftj$Om`GxDE<9 z>+9xyGsxCs8g%ai_g?O%QG!URn?=EXOs_1)wZg;`8)?Pe>?i7$y&~qqFy8Z+pzFbx zX3He@B7`1_z6npRW_K!%&ZA^5R+SPjr$E|>_!ZRP_`-*Ws!!TiVkI5AUE!$>7Hh*2ocY{`9q%|S*xcF zsleF-Dr;$bQDuwS(rtCNAU{?U*fV44R!7IG!{-m*iSde)r!*&cpesft7ipTD35W_} zelR(Td*(!lcq?@+Yj)#i1p3+%`ZvAp_YxNfPlEez5;P%ZeA!43*AiK-JsDYGqpb)DTjQL!H7w6 zHG~z8tLBIt6e7~|9J3gzJ#ID>q3v4P&J>zZ9H!~RJn7ye{`SfTLY~AiSUGhBrj=G( z;Xq8X9qiKmy{3SsYX;(@D^AYazFuFI zVkHi{zH86L<%!U#;%I*U2`GfvDTm)gAfT@M==WTB)D%%oJT6A?eT)6q|uzvvE4?qxhH3Xuyt+}_3yER5M!|(G$>o)=_vyP2t&^(&* ztJZdmP?tcE;3Z}2Os(o&s|1Ooa6b6zwoVjsQF%z`0nOHaYJ9%}P>l)Gp)|;Vs)ji6 z?0jv!%2zeW2t@d_0yy*nbJAxFz7R)D_$#0Pt8greg|=dfKL$YeGSJt?jyK z`GN->Y77ORLZ46!kLAm^nn^&4%Fdy*6>VXwZb+#O3orE^oV#dl!F+8A7(^$B{!=;@ z7V%;l<AUw5+E9vW-{w3X22`6M=dfd6&N-3y1ws`z?8fK~(B(&wjA>Y-S zmvWG1Y+xw?NHuU4rIK-}-0JDT{hX{Of#vr=p9>3^pXIwb4dZh)}B9Fv&5N`O3hR+sqX(4 zI*=q-+^9-Nxg_VeEBLUJ93=t`c~O5$jffKb(-mmqoXgvlf79LyW=>VVfDJ_#Q;%k+ ze+Rj#UouA4+Z{u2ga*AKwI?5=0u;qAicnzaLR=~jZEawg*HWFIaV`c;syB?^rPJ|-Yl=>P<;a!tAmO8*5@ zhW5fh8bA2a#1Y|yFMH9{WJPH)66C)Dk7|Rbg-YQ0Qo)1m^TnC};I#qxe-UI$R57-C zXetwswVmJO1)hptS6$D9@#cXs40t|_L96e7HmlyTW4TH!Lmrw{G5w1>C&fS=Lo?Jm z`F{>{gLyL0yJRcxdZi6*Tj&5my+9u$GJ%_X69e3zo1oc24q`B?0Y*forx>2~P|5ne z_2R!btOCU6m;fe`8y^VJzo9Z*Tp%Ry$&NK&D~Me@y{8QQV&pp{pxkxi*LmmF)SEb{j;#SrJL%8PuM8Jb zX;*IfG#jj+d006}&^$MlFB+wXQXqgB>Je4s5%DI6OKZNJ&ol+qYwOp`#&7v zhW#uqhq%}9GgD9f7MGfKmGj0qa-%U9BA!+Wz!Y9g`$<}W5a21FhXNmJRwk7+t5Y5d3h&s;@Q z{x&R(FR}82Rx^mMU!k7y1v@=Ra3flJhuzHfZ-iTRgdzy66EG8;#EOB|F?18+P{HAI zUh(k~t}v3m*z)|>2f9eNr7hiKlG>BLP};=Dt8S{l1c1ddf@zz8qLK^l@*F^~{#r#z zpO~ea%a}9w(hQS?qkr zWC@tMP@;MN6KKIuzspj5i@A0Yc79!T7i9=?sH0(pmdFpTAW#Xtne)0cOb1IpU z^3Zb1`VH5fvG871O9;Nb_Dk=`6wq)%i%yrIVQT09=BtILnGEcEKSUg4Wm&X@a@wtX zZRs-gs21HXEv9_b;eQ)-?-(i*t9q0$@&Rcq9JmH#q-EHpsY)nIC{<&rrEBtI^JB+C zqQ#|aMkrNcA4{ixSkj%jZP{OOU)o6DnBPeE+`rYBI@P_i+i#t^D!&%knBUgzbP&v> z91V@3usMnrKrHLua-=Bbm^fN-T;BSSqgLl?Z*J_e>uv^zM;ME|W!k_;d=eC0Z&4?Gu`TxFHo~OxmXxW+{J9aB zm?r&Lw_8y@Kf_#eDUdl~~~#epuzYuUv}UM5^LZ zD>|fPwCyDOiH1h{Ij`uSV7u8Oesj3VGKD_5^14}9H23N z52wC6xkc|+eIf=rr8(SG-9NOl+gFTAF@g#g>Fud_ZkQhW!Yjf+hq zbG43jme+^(0jn$Me#HDVZ});bD5_uoevyq-LicXAK-l8?P*3G^ExoXmM*p2i{M;lN z7G=c8nmkLELJGn&_}?`*g^_1zXIz|!vdsn>z#39%<(j4O2tWRyK3N)4yBric(MvJr z&-??u{sv+Ggu1$$V~cbxyDQ51$QNazH!rcey3%HpU_9TFj()qZhxPX56$N6FCMCgC zCj5LIUZ7-co*D^fd_u(LY*1oT>2O;-Z#f*r_$o?FUmR3b>bd|16d_=4fxByEPx#@8H0$(u#$n#ir)h5BMp0;XJ44 zfyp8^jw7k8$bq$0e>X_9^~p%$8b7|cH-$J=`8cZiJt}78++F{70V}uQxjtEMgja}F^v|+pUt2PTpO2us>Xb0V$3SeY_ z{jxw7t;!F{TpIp& zZq}ex1zt;|c<-wgu0lC+&1>eX@>P6iPXi0ZYXh>rU5f;abrf{c(n$#KPLsFIE21@* zIvFq09zh}%8qI{+q8J|Q--UFPoN_gmr|f)O&FC`W=+;jOr+%_n;TL&v41X80N|r#> z^?ZzJp11T;@m%Sx&zqw|yhb7SiPozYpRCoD3$1qBtOTMsn=-f}*s=)o`!=gNkIv6{ z1RP6`FP?oi(z9to**>%&5y32j4*lI{vNS{Gn$Bl2wNJZ@r^V ziyNL|9dearA4rxZw^Oip5A4Tv?8I4lTjyF$*}QVS7?Qqyl$PqVH1}X{40v)CO0Z3m zE*7wu#YmK!b84o~hv)tH18nR1IUzETB@5_F(5#k!PlhRat^WcJiK^wrIyt88b5{!n z#tZ%u5Q<5*_^y_Wu@JGH>ik*A>?GdmbKX|pX1wr0E}JJHyY;;$OY`^IWxT!}lf~wk zvm3W%ZpQhEneN&C9g`-u0PMr-6+`sPi;PBr4V{ST8!vrB{cFh`boQ)h**J39w=ob# z=vSjKIZWhcbe=GzvV-xpK&>F}xu#ji;^!}QwafRiLXoT~hl#mVp)iIVpTe@WP><;K z`?Z^A16to3TAFuM#LqCg-An$Mv4N}*G?r-5fs;apEd1!nkxRtI1xX_ZO|_q9Y>i*< z>pM{j$thrZ!8(svk_*IMG1cxwVNKn7P1b}o#Z*z)D8*+tvCtE>FuUg1XtZ`*N66RN zme?2TGN`kox0oE&IsY12mvyp0Ppffvd-V>^Uax^#@5dUNo;Bp4%;ASj{9GMn)eoFN za?2W*O9v0>gZE^6+lfZ4s+A6ksYJCPVsT1nuoCuJuax%eY>`d$EKpL^+zU` zgN==*;C*SI!4twHsaknirUCxVLUEU6OLS93dA;)(jJ7hl|P*s zO{N(Ow8WL7S&V-|6fu#j+o;CNpR|6{q6WY5xh#m_<{W+lhSY4L=e{w9Su)(AEWCV+z^LmHRCee=xNrZ_@57tpjh-%Cq3N1~$c zX$p?JORb?I=TS-{N+`7ULb;H$4n{Xu7&PxVjK<3t)MKNJn`NqU*A$h*4|S-yo?5Sc zv0Ea@_;r%4Ii*2#aQv|*V@=xDN@~&5T0VJ`!lKv+{uW_4$dDG$050|74>;8`HsV=FvU+xB^h`wewqLJ4YY)mHB=k(o1ZN+SuQE!AI$0BIOEOdVMQbbIZ zqlw2)eRs9zm{nUo!d28Gyf8Fo(M&T@Pr0VHj1&$1aa8%Mb|1cZl>~=#8i(tKw&-=Y zI1(bL1TBCx(+6PHi|g2MBq%RSyHD8WBTtcQ75UH(BW7xbiBimBfh#cS`WuS9@uIc& zviY7;jTsI&{ct zRSp!?nXwZadinKB8G6xIh4?86_aXSgVlmPvAw6=gUVei+trL^ArDLrI-xJ9^A6jJ<&4VciGDbdadTyL|f<@<29(G$tO+@0J-dn-p zqW79(0gN?TCxiA*dj&@`tnt4ujXz>~~GCZ?4Hl$s)4~R7R4!A_}#}XM>r6~gnlx&I^TgIM7X;VI;Yba&SM%!8wyn>d`Qnq8bc7W1}67uHol$W+pSikeXV?Hh#M7 zq(6zBkQF_|$w3&FdNt^`^d>|jv?o3j@8dU+RcPF)@^lTq>dc9ocpck_9k zmeE6+HjEl`3!-Gx@Fpj^5>X8%cm9nAhd;gJetOyC54dp#ix}V#tTuz|fy45L8`jhaXSdujf6^r5pgR;uW@^26Gfzc$eU(TUc6) za%@V>J4WZ3NsNA11hR4aD~|`XBX^Zwvu|*HxulnoN39>QInX-SpgZ;K7ics%cst$> zc6o<4CPvxe`QBb2^Vau-#2NIB;NHFPfn?NREw0ltaEfR`?RyK!7H@3Z4qG2BHWOaf z@>Vi~5%l>XPl4sj38~YCqbfx7<+=l>uL|k3!VJ#AsfIKK(RpeQeF46jj?If!NNMj+#HcFfZs1 za?I&ix*Azm=B|An>0geS7R8Ug{u0qJ?w!;-8)~o0daU(4T}0Zv!xk1KcHIT0Z6u_K za*3Q))hE_<8MvlVxWU8p=l?uBZV{FCKFD`|Dv60r!3O@C;gZ| z1tU+l<(UTgwetpmL2KyqCGh)bg-()d+$3I2B&wz1w%WRLfj@8 z;M8_nW6M@k=1YQ=gIuS)egC-3-`7=|td;yt7w66f(q57G$uw@KDTZ0jVTB7yrQ;jH zLx&ke^511Gr>CE_GiMQ|Emn#Jk=Hc#OUwBpk|1l(=S12}lEDUIqw}@CkCQDc+Dj{w z!H2cn{bS}TfATxz>UKE1esEg72V>#l%h~XkEY&607%F>Vva9VAjoe7SMQq8dYBnN& zWe-6KJNw3)dS4#YR=!6!yr!L9s7Y;od49c+Fe?;eH@JdR4}!&4>aWRgSZIG$j1c%2 z=Pf?^TFlugRqI-%O8?c#=bG!cnxP(v^F&M?&O%#42Z~knw&;qK(b8HFq^V6hD=AH8 zs|?3^Vsz8`)LmodEH}2$;FifZk=c6ASWJu5JMK5PSG?+(4tXyz#?&aT4mkGYIzJty z)5^Z3R1^fCakBXxyeVdg4?Ob_-Q73v1!(#4iEM}%x!LQFx&+EABj+w{C-BV?e&Z7v zqbvN}>xXI;nqc0HR(|fwKr9~0b2GOk{Un9k((~2Y{Y{Z7G)eum)fLmiMPUWPoud-H z)A$?SMUOwJ@p+JpEq_`V*AfUJWkTwLWeG962Giq*WsA#wu(3 zMq=^J2d6f%qeH4j`_&JTJ15Eblf*uEB1O$V8#Dm#K=mN&`vg=uHGdpsi||^XU2p1!__$F1h%*1lmJS4jHFJTBS>rEp3C81v!o) zc+Y&+98l1dDT-pEV`3R>+vhf0Edaia^Ld-|rV+Akb@GRYrwA)?WD>4e9jb%ve@xnFMWrYQJ zbB5-1b9!dxnGn`Lx)v!50k28Wh>!$~!qa6I`6S8OQF(HVC?<5^j={$^5Sq|d!ST;3 zsvH^aHP7Rm;Vfme9qb4(6H;peySW*eTR~`%cskb!xoB^7Zf)$tYYa6TYP)9b)EYTN zMKzg{C1Hd;HGf8(G(b!i9brCJh{)`9KG~NSTwTp{|3*6_K%oq#qX4g@ zpuC*k_aD^Zbz`Cbecm>&OmIO8P6C{TmExOKUE~lf`qA(aRR+=5$cf$h=O;aIB@3!5 z0JgABfz6w@tmBF=%Yoge_0h4j<~VG{3xNWRV5AnE%e?i0m@&cwm$j9-!e`X#1u=uN zu5~%rud~I$p&)aT$`=(VQX#w-Z2b0kvo+Os9(`pa)zhjgs)^q3HHM9b1!=3g3P#Z7 zVo@FPxGc$A0lloD`pR{EkFC5<%}0(LX+k&`X6l4ZQ3x`W;J6mADmni8Xd)SjAFT|p zFs3U$6a!xGIg*^j)Z#N9ypR&gI+^e^Em*LmS1UL(r(`w$t*_sz2Cmm6JTDcVl|B_i zAE%Z)lt(rG$l84-vYeLZ_tI^b4?^RI{M0J2R5lw244SWFlR5#jxZ?fvXPZk5yEFlM zpXs^+Z9T2d&+i+1Fxr!g9mYPRqBh^<8m$D2oa>HG}wYk6&2_L>zU2_>S z(WOUEb(Is+C3SlrC}dM0dYlSL@_UrrK)duN;_Erw8_Q8Q>qJNZIB?vAU+cezi+S}- z9)?2jE0sHjov6?CyXFPX2sOfPbp>f5AED2uEMbpZ>nBGoT%{=@hx=2=gPLsg>yr9) z{_OJ{eDYwjGLJuvipz3^@llStMyg(ghs=~*{z67>YT5~V0&74paoxvdXttY`>D3}n zA}f4i@a%Oq7x=6e$HiyiGRrxQ=F@n2HmW06*D!j>YRs=Ul-^piGG4g;7>+sf*(yGG ztU+o!x`ddn7}BOwe3_!1>FD$aOG(=p)*1J!JI#xE5u-9A|rDct!m2{uoR%Fh`2G&%x!(^t*@>+2DUaqbj`}0p) z<=0E=zXRJjm}>pB0@FQKR97je||5x(7as$&(-g*{ zdX))1hLt*HfsRXA_<;m#E)_Dnc{X`1HX3BQpK4#~A&^IttnqQm$i;=~N=22A@>s`0 zXu)wC{%W|c4kx6tcNl?O@GDp7XqGyBL&f0d2cU4#{A0Koc8M@n);cLGtvS8N=+kWe zq&XY=?L|)f6)_b9)`v2AO(uYjlIGSdQxVcy?y;l7r)k2m7axwDrces{YIbFCq|r zyH^%~GhrF0{i$vXVK}`@scil7tW5s*=gh1xxOUd5nNHi8+q|TwK4~{Z=QdCVQA8uW z51X#^JVYU%x{d~R)U4T=mqj7p&C{67pw^m$+(*>d#XJ&$4|;XQ3Aa&Z^(Pn)AWfzT zD_8ll6wJve>G1va1pP;y>6Puy#A31b(jLAy%5}`E*LTy>Bz)^Nk^TPPnWHY#mFuI* zv)wwcN|kX*M20^b8v}?MpGI8)u4(z?_@iRKZy^*ftIbta$RS_h4$hUh|BPM{SpUJ& zeM^@CcJ7#9@m(C5qL$`C!?D)NI5lilG&{HoNdE&6>~%=!)d4M-wrfZy#>H^s>|MVU zbq*s4-x>+tv>=T*B5%%i3N4Sa94npzs~a0?OwrKZc5)gdwuHioBR1@0d2DT~vHtFP0U;aGt)(<-o8{5r4CIrc zzUoz??I4Cba=gv!$Jw_plQwRepx4P?hnKMXC^|NL1+5G9O+ZtXbV7nwRxlm2R?J zB`#~l)&)#FYjD2u2!Ga9rp<0xWf-_fs~C8*Jia9%dNROUswfgQ=wH(ttVBuX5AKT6 z{@Q)v-+>aJj@Kf9DVo5KZetUtF3ES0H8z)G{J8F}2%MXr+|;}}&5EC8bvH3xMvmkA z-07J6$w^Y+UoF6kMP3Lp-+WDX#UgrF=O0l-f(Gj&`gf{-FFu?`EHNnM;%Tr_^)VmaUiM=rsGFk5#e(BbdhD#6;8A=k9+5dj4-udQF!3!Y zMC{o)-u!(>PAov<@9ZDwf4bgMt@mu6Ica^qUC-iFEzNfWFl28=X@D-~wP;px?|Z}P zq=mAU?xadGz$?ToxYO%reG_SAxO5wwUnyjne5(!@M+0VZM+1Uite^~`3Zn%zm@9IUvGTJBft@mSLM1p)a33<35jW0ylIL>dBLeAeubWM7sG#SEPz5n z&?jCB@B$V%1tbH?85JbxTBw!%@}io^E47I&i1xGg(V+x{7iX00kD$<95%im*v_zI?x6@KFTfI=HE~B+un1@HhN}-_T2Q^t^^XLf-2mRb$v%hH zyBtq&?+001t!K#4BOT4B_ru|DV3w9M>w&{x)fPqLQ6&G@ZnAEyPMMXf{%Yb z!XD~@mG|K~TlZs0{h})DR(;*%?#o$bqdSsMC1R`vIc58+m;a-^uMCK?Tib`08R8qRe0+jBM5TsKSX&41TL<#8-M+WH*X*lZ{aPN1Y?|eVb|NZ`Y z8He?(b@z2&_j+&=Jou8baPenHA6hk)2{DJeReLczl2Og9^Cx1rY2c}%LiafpMb%gq zYyv_MsM<&?8x3H%`-MH(rUuvWMkhg4DUt6F!3BC_X7Dc(E zoZIAc&qK_bXSJ(dGezeO==c1jL*KcSdd&{T#L)96V}!foDh&fTh zz=Q4D1?<^ZB*7+a(e3v1dtZJfYuP^C><#TWZrPv4Cu;HJ;l0t*s-G2)ul;+=oumoR zs3`k;atoVWhr*x1-z-_RGrDP;zb3?Nrb2tr$T^pd>jzjwH~C;y0*N!#3S}Es1S;7y zpd@F67KVOTOXx>>1XyW2~Kj{AG&aVzf93VX!S(-Z2~~=YsX*eglAU*b?&J!G$(^? zyi>U&jfnfQMD>o%&!TObDc!s~Ff{mz=7;CZ?l#YXtH|d8S20*H`WA?EUWS*y+Z8shT-Hct>E+p)QJ3)ick;;*ak+(xza>G3uuS3LsdJ z0Ck~%EtHebv4RG}>>srV^b3idg0F!N{1ry*VmFrD-#POWFzeGK$BTOlly_CPsxp9kgXfhwE(nJajYG@WTAc7oYz$? z@#$nWkM8@MW97VgO5LfA)To_|cCU_Ezb%SeBx>o0W^*RT{(Ud?7^ICkHdpo(6~aB?ogE+h+u%~QO(`y z>=U8sk0sJI_qOJ#9P z63^#xLKEg<1m*%7IFWF|vRzw=i?nx|;gRWmnU7^)MuHvi(GLCY*R=nTXp*KLX65mznaZq{P*YfM$j#cnyte{*GH#fhE z7&|vXB8~@o4@1-Zdv~`N@Cc(d5Nq36bd?XUQ$*tse`CK=ozD2A5DuX=Z+Sq6e6Cn?~ z7uEuoHO_T=Q8|CLu0=i~(dl|-Z2v)x(}xjf^bB%Ef-WETts!2wMi#oE#?txy`g`Q9 zB0RwHIY&A0M&k$d$9$_!9{uKN3ukAso&xg%;55a&E>Nm2f-=sr@q})t4 zS?y93o~%W~E1@NS9UemodLYa(TSI0g#rD7-X@^&ng-lKAel1SBC)QS%(ZbwR~d9Mh1~pY?PPj~=cXCtbdoe6m!)_aEM? zV*(vfDE7C4NGQA%pqY#$gk61&o0YL|*EUj}CAM%x<+%2fZeQN=2Z%)gqlRHsGGP{aV}@A>H>(h@h9&y!T<;QU=9rd31R-QjgCCyy8UT zFpUny;ZH-Q#)~iXfy7)-2092`w7##`(-IN(O<&6ey&a?AN>EYn`D}$5c*#Ht z)!~Y1GM!6wV)}PmG=c*(JYvfbpeSNF+xusEIGDvuUM!4xcJNe9ef`$DGQlgbMh2E1T}1Nre?l>&}OT@ zC26v>oi4v3y7|^EskYBwX}D?TKjd{S>V??eL2YBuGC%$h-m&qkN1>10w1fv0A&pZ3|wZ6{(e+mH*gvHEVhm^>o zRZ$a%cVsIp#gj%U0ppU46eplbpy4#zNs=FhL#%w-e#+zH@hUTc+so?O7mj@#!@SS0 zg}>3`@!xbQz^m}bE}(R5C$1A%yl@^GoowM2TaUzxW&a`rI(9^U7>qk2tZ5uxTG>HG zIOi>KLFkb-s4tljC)xngi#>+3Rf@yd3vgHQN@0y^)ci^Eoem#JaAZ^bRp(}IQur{t z!Yj`R@ndt)?9e;-GCez&;W7A{cxmk=7yFBuP-s9P4f#d_>);dYa+7FuWhC-BaP_|f zRSAK0fH@-{@_LaVDK&xd+2pm5hu zTj7TtJCjQ)r=bS=a4*v#^#^MDc6adVitKKjxvB5LK;u@`#6bb|`JyLqwqAaql>Of% z)v@wS1PQ>J?=m~~{+s6h{#ZSB7RzMXEi7+NT2qEq-kZoZ-GN77dNg+4Q?ha)X3@C$ zFk*W4N537q0rz_4*9YQu3|OW_jrVJ9^_#{78;YiOTnV*0TTRXgRtg69iesyB^^<&olM2&d4dqC|{aBxNa^ii#3 zK0(LyE^-r9MSwf_VNmH`2>-$Yo^&O+s0=&_V84Px3!T-zJNxWbnN@(IWmOHkoZ}HmKyl+f@biS8+@;1>1*~ih@q` zomwVl*#xB$ei0}}r`Xeu{toLcZ;3~*YHDowt*p(|-2e$K90)S0v8;@oCK8#UueN5m zq4=WDtKRqFR$!PXx}1%%FbjT|Q$j>H&Y2ebRD0@Geg*kYlfF+f`Knx-tZSOgU((Cd ze{Q^~c-~B{TmPniM7ww=L~OcKAuz*;JPR$C|NSNY@ZzI4CRFJ1To=9MR1ULX&MYG5 zL3E3VwRapos6-xvN<@H$vm!18{)O2RFv&rZJj1KO_uk!aZ@h~u6mVZVJ#vZWqlXGu(5xKBgRkeJx0OU>w z#jWA_hC_%hUT(;d>F3|)_%ti7#zRQ}cKnHg>sP4idW?uXj68)4Ai^G!ooEC@NY+=E zy*9(AnSD#k1r&016F4NDGPIM1VFatHvQh0X@u_oMm|_I6W?c6$2vv%g|VO0x$? z)i9!r`Rl^v6ZDiS5*yAb!axSS3gY#^lHRn%4oLfryK0S=jg7fMk zU~cWdUeX)@I8U*^m8Q)1XC43YAmxc_P!uD@@Qzx^F^>m$q2gNTak0;ZZ#f;R0ZH0C zDzgs7#%ifQgfIEN2lAl-@_8zu@es%d0nHh;B7TxI=KQ#zzT8LJ#2@u-dvn>(JS>peRdDSdGi?hu zfN?yow3WsmTyy$Ngk2)qgT5@m`S7NQoTT3+anTiQL^dHb)eM4eNCpe;iC5iKV@RZH zQO;Gg3ny6TSx+JFT2h-X?FJ2>i-4+5fq6zJgupqJP{+vG?=3X31ru>dH{>g1@`X^e zedA53HIX`j$;_t*yD7rqXcg22%;-8(YyiEchBb?3XO&at`;84uGd3@2G?FabL|qUa59Z{LVRW}QgyUP zjwi}}1(V^g4}fbnae#0-yrL@s3PlB2G!Bb33yk8b`nTMjB)oF0oHHE8cXB83MqyFt z#{e?}|LDj#Wy+r6g12QaCTUGzWCn(8z=92c{1mIp2d@DW19fI65r4}VSH=MtINK>1 zIsQy+gdJ8y0ob5^6{|08ACP`!k~F*0t`|x5RUH$D3N+g;#`(`$`jyEJ@j(&egxyl- zBi(f<;L2|$iWAftsw?qZE{b`?BQ15_nkF1JUUqf+ro{L0eISCaf9D`5aXbj_D!o>O zVc9y^m{#M{m>!tcElY7Qfv#_*yOf=f3js=!nO1}sQc5ud%>AgHvf-dJiEIc_M|g`V z&2ZNd2QT6+MSXvo5mH7AAuV7x$wD_B#R4UmA8C7Ic)bJPjZL8t58BG3j?Nw_EI9|{ z5U!?Y!b5A~VN-&4T0jbm-XVX~vo&GVu##CZ-7?Z$22zJrvKWW!Q&;`q*;@7xu(EUa z?e2>lX{06%RGjP$Yq~q59v3}~?e(B-hA1wY1kN}JKD(DN%8>T9iB`}=AB3DFP+ZxM z9*prJya?J%wm_lm2>;-O@OgV!4-I9r&RuL$7_x7h@v)c6{(zs_6+#odAF6bCm^D2+ zjx{W1<&px}&E^EgA=bH}M=xJ~5uGG_`TfzGF)mQ;FhhgfvOqA#>}&d{I%E$Iex-(A ziQm6@%ku3|VN3rKhZXIvJi>8N6#B7y=NEsBkW6^gpKsLfn)^9g%MO_WLvn)&F z1&oNo$1{koTK6|0u{P1V7Ot#$UB(N+)n^k|_|oKtc&>`U3A2KD#eyOZ8gGYl6Y9o! zW*HN`Y%RY{TdU-2X0v|OtSjdPLT@h@2)&V#=5K)D6x^?$`VSKDxvY- z&F6KtkYItkpZnzePJy0N#uA@`m&r54vb!yMtaVxhP}?Hrw+{-(xhhPkd;%l8`#B&h zdja6~#{&>i>{F3uh_?tz-luD3mSQOX#4)kLJF^^eF7cfDg!TCei<7Zk9+P*Tf3*I- zsUV+FsYqHbIw~mkKEMi^i0<*~?9j%`l$fq3=Zm&fv{9XS1-Bj4h0@Y61S2#DjzAgt z{ahor_OGyZE0NB(AwDU-UKp}bGcMX%uQeY7gi1KlwQz1PVvrCw${007A?=~3d?fm_ zfm&k$$(ZHgx3S5Hyd*Q^CR-fHB2pwnusa<<)d0FbG4k`^K?y6@x06HQ^~|p9q$1;n z58vF;{!`eOuiG1Rd2csNTF?XBym%`WO(2K<<&W_3?;X63dg`YVzq3Yn;MW6zOUznb&9<17yav<$U;LEaiO@f5` z?I0wbZB}#X4{5o=2Yu8}{c<|?koW#hENe1i8`oboTVOin2b0sa+$w|+W{6d7UHs@I zXz5$PVDg{;Yz5k7B!li((On9pQA-*-u!)#TubByL#8Dc3&FK`J*yR!QgCcN0ryaeC zGfAZ@+=~Exn+oNZeEOSJ#^rNyezL#%5}>s?sF^> zM_t77t6vK!MY@rh*0OdJ;}#j}QB$jN5LC#%1Gb;`u<02{Q*TMd>BT8`eO`Hr!vEki zngP(EbFkOhL5DN0Jyj|2lI-XBFphe}-BsiFjnuAID-`<0n(N;7E-3KsqHEYq{8~Yi zShY4VyGV3c&FgHnh|S2BQ4HeAhrEC6h?XB4^x-Ns7%O}ZZghfieos^uW%0@Dfv8Dx zDIB?!0R%%P1<`spwruaOOpyoMD~FYSI*9iB=>ha$)*R{y`<6_j88%aTK~MP8os(qc zk@A60_f{@L=kNsQP}|Ag2j?)O04x7S40|=Lr|gUqgz>HL({4(LIJZ35SO?Mj=_QXA z(=?Bhk6W2Z^(S~Ny*@wQT}+v+OXieQ-fL%?t!vJ+du-TbKP5UWNS?#_ZSx5lW~|Br#H7Y5bm5 z$x8PujopxjAJXpv2e8qi8z_Op5KAIS^ZW*YFQuz3v2xxs5momp>Cv z{ILqKc9!Zg0}Wg%z%KcBpULuwDHMRo-Ku!?3%%9aJP+VY_-*6!&FVJGZEZ0r^{)t( zQSH;SbBY+@34l#_d=m+OwgcQS%o$ghNCSuUm3KPihJ6tPa zSRDQ06??bJm$t&!Hkv5vUJ#kIuUYiNrMHe*IL+TTvCzf@sOCE7(w-{^#p8T+BpvBe z29=8~gRhKj?0#@)_s;9wssVt4O-B@Xx$S}WGC0gSd6>Z-NxsUz6hXD6z7@#DB11JKqBFSt|Pt!_BYpYoG&`sRe^cJ`KE*^4`63 ze3cY!+Yo0mD@!gQqYT9qW&|#jc)UR30Z#@atFrud>IwHdr>k48y58!(k#acyIIGr- z(VwP?48XF4Yp@}**w%xGA1MC}UW&{rfl6nML;%S!C>KQZ3{tMV%z6rIsR=eUi*C5= z*so0V*n*UNM2SqGDZW9yi!lBVnvrI#gUPsn!)WQdd5~(S96LJ-V0{*6TY>@0;p%lL zz5;at@DRzrV|^Ob?peb6oL4B2kMYa}eq2yEBdrW0r?jHoixSWC1n#MCcu3n=n9^jh@w$Ns?t-jpVha`^ z&ZRmPq$C%Afy<_>l4BJ^_gP#$v{8xGw1sBS(#B9pyB-K7{bWM8O@|k!Nnj&45PQzv3*}t&9%*^2`0c^_=4w9Ke#V|XQvg_2gzGO>9$FtyQ zyrG4XM++eEfQ4Eiuuji^>c=ny17aINos_-w#B%~moYZIFNjKe&fD@L$dADzFOa>#z zAU9x(UCiIf88`~u*i=At@w_RxAf% zOmaYM=ywz*K|hcY3?yB;c0P(4>jUc}@@iEZxMF_`os*EWivMl==JSeps`uU9H`db; zpGob_zS27!5%&gx#GwZ4vDmXjNfqd1mme{gLCQ*&dt7f@RM(-TDRWV&PT6VYjoZlp zC)%UVu$?PH&4R8;B&jq0mRiltBglzWEV027jSP??2Gp7JHD2PE>Qsb-ix3}$M{a3)ckxA_3EliNd6dHEA%Z!oNZR*y@3kNuMm z72ulo=dVf{GgzpFt;*x~N`%Mu=OqKF#Y@TjTfd>E>$uCri&t$0_b{YBT=^WvyN| z5a>1Q&2mAY$0Lvymur-HjXm!D?Kcm<3vwux3}x#T1S?(jw-^U24V??PA{HdwXw{1Lze?-Rr~4-Ih0uM#h`529 zkI{JfeG{8fiQ^LKzB?+ivg|v_g`|hxf6=0g1zRG zJ2&OK@iGYx8(GqY0pA9q0~Ii|CpDpvGm8?ujPX-%WVO|JX%xPfJX6!&f*Hvhk+O}t zf~)9torwGnlSQ|~k;q8@QzE*VrEF#IH+M+>_y~~3KQjN0V`cyVI9QTH5wu30$ooe? zi)NA;vC5&!MunPYa8d3Qi1yC6_&ukbZw}`HFd|VE&AOb;r{%xM2S2XELL1ih*9T_t z(Ye!iKM5?$72W@taWJ76fBW>nv{3fCZ0A%A?e5OG36)XwD>>qquz(w@d~wJcL{mur zdE0k5q_*ezxeQKJ3y<=r(VqoNgRA_Cyu7$xO!<%9(B$#;|5VZl@a!?|Eqm%wPju~mG|nnSe0Sq zmxP5nkp`>v;v&P!{B6vUb!i(rk4-A^<w{R3LG$iWp(Y;C-tQ9E}3;&DObZ)6(7 z;_$Ye?`gXqP^4fPd+3c8&oV0JAraiWymkqt%Md3)XVlPoH08fo?VF|;Ixbp0DE3y{ z>WK{#`@Xs2ywXn`&OnK;sgJ-*;GdIaCduW+YnWZHwChO?&I^#~1o)NE{!;2OMgk90GNeFbNoJ{4t1~ z0_WH(PK)$raDR@vFU6y^nR`e-FlYX5#89J)_O?la@mc$vI)CTg02sX7`+#K1&kLEw zJU0aX40Q3VjI7K7W(8{oUMAI)i2g>jxu2tNu`Ny}eoaImk|UYD6Y6}a_Op5Msm^oH z*BfrEf0q}oGr*`Ca0gJ|H=@MA027Mtk=9;tWvqN;L#1xXYuO=f^;Xef7Hb@Kdcnz@ zxb|Ev`bqaD4Mu`7@^-*bJfD4!JT;s1@ISaYzShEj^zU(!=F*F)Ai~wp=JJ^VJ_RU%k!(4!0CLNO?eI_0UBT^|Y1KjMC4%HY*Gh@Z&HQh-~!_nsEw|!twuO z+EX_m#i2PRPQLrmlv;H6#F|`F7{{#Q`nbp)%pyF@U=bLnBc>VAPT-uZPGPkW0t(A< z>|j_@5!M$qBnhJ7mZkbI$IC?j2Zz~Wy9AN_S$dgvE>%;bdj+D?d~*-^zyqege|J6ogc40#B`M==CC4H=nhE z?o+Pw_txqj98p{rh3tm{9Ig&jhWCd<*99 zj|Of)PhNH8PB!qw!^L)n&X*tpSjN&l-%`Xr7|5F@QA-zvYgx zV$4yuzLKKymNnPP-xhBjeL%hS^0RKMpLIyl(P5&ricXB{>Cgd3toAkV$4Hd<9GF4X z`H~`DbeTS@bN*0S%*w;&f+AC_m@w$D?%lBbHqrL(GBhUy{jwtmQ2o7C$8;z;c@dNn z2SE2v;L$MR&$hf^xWz(ktpJE+6o7XxI>9O~Mh%6j@{PL9w;miD zm1Ice=SMHj4jdcbH+^aLQ|R)ji6C@JJ|=T zQvH+CF^&=At{tXCuGY(Xnj2Eh`~HLudEIwP<Y$hISc_RwqHbYc&(Kh?6b&fu9aZj!7y|<44GqO63xhjx(3bYq zP!EuI<}U~RZsrkxZJv&^j;#L_v*_3&onPjRclaKr;DTdHH1Qs0B79mLu~n)-$VCFP zpKMB)izdm&Fc!XnzOLBL$NHFHYLy#$B4QK`wTb6smU`5%7OsdhtFRK zh9Q6(YCK17A|esXoDVHKX4`iDY$M4TR!#c7z%4NilIe z@>G5+Y2?!Gb%f_6X6guf^T0*}LOtX$hp)Y|oeaYWo{y69L0u%~VJg{Be|u|8hKCcQ zK5OPmKG$oNlQe%AXOl2>wp_OY&)C6Fq#!pMRsYesUV6{>J}Ua@d|Cb3?tLMA%(N}$D2R>!5$D4~@+aD1>^yEFuRAfMq7k_ai zLePvEAwX8E?C7&cg!;p-{qMwm6gmck)L2@Eiv3W_{M3``V+;*ezS5Z~u( z#4{qO+{)8DajHJmi9FB=wwB(_(`B*NbWsh+yWeTTkS2(8YV_B72hGoxi9s5G0FZLt ziv)Coq3siDqQHD1gP1eDarh0Y38FA_a$f433db{brGbhf=gzQG?dfw=P=m-T48;ja>w5;eZd zS}01y>=6W}e}q+Rf&^uhED}_1UHXBgv?rXJXZHO7rHfT;T}sfl5H z0=RSn@&j@Y>Cfrs7);_%;)+dbdRnuBaCIK)uaPPj<)8j8U|wU%DlfV!pgloz<`Azp z0HXn}WROhU0}E!(&g#!K;Yp&%t!|^DSV44_hnW$NfF`&;-znz7Yyye}i~St$;Vj}5 z`l|naAmlICLW|1J9*BpC)VJ;c!vVDHne2XOCb>g!NLn(f%^5Qe} zwfv@p$;)5^*|adey4JO0-% zDxJezMk*Hc-@&t5Xs6?StL|}T7T=&oUFo8TDaIvvI9!!cj}ipl1l$yTc6AoVyZDT! z12!ZKrsEVhYeKM2WuymlQ9)mKiKn|UWH}X!G(>0*TSkbURk&=f6hNSpr`Vhs;YVyapffEC-s=h3pl8&I^-mO9cr7r&{ppg2?E@!99-nN3iX1(!{0&q zG(Bk85hDouP-Icv6ZH7gv__dqvPy+l`z3(RJ+<=}z<5871Ns%JICR7iG+CF9Xc&zO zQ-g7C!^^K41P@~BHlQES(%3m&T_xKA#)RDsBfc8(aXh3X1m{A(37E$@H6ogrkkr%b zJ*VZs2lxXb)A$0n1grKRh*elgLQdr=Bm!$)lLp6b74mp`x*KrCyh1INoEHpFtvuyA zxruT`sXv`7Njo(pz_~pJiuaZG2tWI}4vZR$4u|@W=}8C7AC|stsy%pieichaAS5OD zocoz2T!+7hLxX1xdIxxx;pL)T2QT5$mv(JC4(c=hib4i*D?4IchST3crVuP)D{^;i zGh|RS@gyzVSB!vbt9Gan;+?-4BLwaTjgD$Fn+a diff --git a/composeApp/src/commonMain/composeResources/drawable/hegy.png b/composeApp/src/commonMain/composeResources/drawable/hegy.png deleted file mode 100644 index c4bf4aa1076dfdf5cffed0b048bb5db8ae258ae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78973 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 diff --git a/composeApp/src/commonMain/composeResources/drawable/pistol.png b/composeApp/src/commonMain/composeResources/drawable/pistol.png deleted file mode 100644 index b77b278fffafb5d6af67a80357a422d4a56d238e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2099 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

6fQTP{8ssTg0tQ*mCa(F+j=N=A1{ObE z!mJA&3x2yq)H&GrI`|3R@sF#e`|2K$kN2r3GyDd&G#M595_Fx%AP*|T$nmjfELhn+ z-DF5j?c&8^cS`TljXMZ_6_EjyR-%rQu&}>HPY4u{MQ_4xddk4=1}6$r-;$qPN{Tt# z!=tLTcrmC;b0J++&atFPD@Ra)s|3V{tGHqVS1N;$gxE7PUF*F_s2mp=QH?fTa0W`N zO~V9r>d65C*qig$|LQHUARF?Yy__=+X6(y<0{0jHp8c zakG>EHp#L&20o3hhga6a=};`|I8$cE>p%;#9+hZR1Aka~DqGJnlep3F=DO(K@P#5! zy&ir>c-B0D>G`V8S>SV8RHziBs=A{k%f zi`iB1&^FL*z*VjTNsOa=hY|H@715$Aadiexr#1I+m4li|AI6uyUO^^-v?a%5xQ!8F zG+kc%pNg9C5AI7t>p2|lM5`bo+1|`BX81tgv`g{h;=x|iETB&7>j&o^n220Z6>!{) zORC~Nhv9u-iB_ddNU(L!?EN$abSSVI1N2Q2J-l1^!+cSo$_tOI7;9X(d%We3k||R& zhT98x%pV zJxn-ROfvb(NrCbT@}3;Vs3x71a{AyyxgPAjRNC7?VK6j(@=Y+PW-NIRjPb=1m;VunT1TA$q%h zuif2a3^G@4Apt%RKU-Gr0=;w#XmkF-g?}_=CG%T~2c$pw(-!Xotl$Ie)3|p1?Qma1 z00R}Fa9gb~iOu3uT13{3X%Ckfo@bvjQjh|~lLp_b%{@TGx}Mw zZI7@{oSVzUM1$K%Gt^svi>Od}o@&NeDpyCNf+J*|FFZg~47Lgf%$!R!^I^oY03<}) zZYFeGJBwFf3CjLse{@qx)jgHW{B38%vf=kRA)MkvQFE-e+x}e>BYa_sLQ*a9_pzZm zeqMNwp+8~2SlH$JpLVWLzBhBmm z#dbQsch2MI{PcVjhs+8PKU$qlv;NkrGx z4Zs8&h#cgjJ%isL)eo3Iq>F8QxMEOs9;*i43G``6`ClJy;`ql`w-tS=^%NTKWv#vUsc|;BVWiPxq*zv!aRzfTz&#`fMOXjPoI`B z&Pwni)rHSelI#J`4(-kkJlKQx^!FKMBhblU@@lSb$ZqLTHtjWA?NzV&V@YRS$U#$_ zDEikGFZDU2%eZodHT}WIHjY@(J>g$Ho3}$YnV^-Qa%I>Mf{bBpN6tHA`%iH|H^F*C zM2Vg=Z0iDPw(aiV%qi>oJD944{niyR18Fr=z~~ILFYzw5=J{pFueNW!bZHUD-`-)I zI0gTYFutPg7=bYbbgoGzSTWBEFzBF)QQcqxKfr@@Ia=x-@VQc>KAjC*O) zbH)_G-VHqJiC7Bd*2}_p3WIf?S*5GK@c4BRYzD{{WcJ4|@Xxw+wi%9(7k#+O51`nL zwz@cCc2JX>U6=n=i6pW@J{dh`gneSdD2lC8+s8WnI`aUlgS%{qo5P+2_woA6-ZGxK*Hl(!40^A{i1%$Ih{lR68N*k_Hqle^_8 z^`A9hQ>cWsr>coW&w3=P)7@rqyEbT88LJJq8ekyMCXV*j-irK9*KllK>s~B%8--vk z-TY(=k*RAi_b)9K^;i%?kWVNfd0RI)wM1gc%>f8CGq2cfZ#Wt+Z&rY3G2=euC}t@- z4yHSqDh~dKl>mr&-?tdGA1yM_2Jh9&N}ip$h^=_dK01Emc@K9Cf0;8+?~|}q#SBVy z+f;rINDWrm%&#*?Vp)&?(s9D^)6ubhMB^h^ad>9&;jl8s9rUK~`6FUXKmrd1^6{HF z^4mJ<=vQQnyHuli8tYW+1A~sGe`O!5hhEf4#6DWj7}0z;N-jt)9`rycrEei8dL=kv zNhyut!A<;JiYgAqwKomH*&tYz=%myy>Rc7~=9ivid#sA6gXV>;fN(^PwC(EA2nXa= z&><_39!YZ78$CqSgYf)ZcVsZI4imwOFvk9 zL+%W}2xcISYCOAoCg^Iz`g`$-(HV8LOn&_V&r2>;qXGnSvN<11@aYxj8Xqk#m0>|9 z5Y0L-fg&AMSnw=uKZ{KFbLVS`NLSpIh>tIspmFB}98CynCbEfyadExSPo*i8PkTna zIJlk_fz5*kkHmxS6UU*6Oq#bNdW``h#wa~qNoFLz@zs>q z`_JZF*-5Z(y7g@2R^zU2O7VfRtvoS$1avYtSD`A-s5eMZS(YK7%y$Jqd`J0za( zd`J0|DB_A^t5tut7AqHBA{olSYz@8h*l@`CI=ze3A{G$HtJoG$IRsk!9?UF=4eV4F zk;7K1P*cvC_w0WwHIrWXRI0!lwo6*9;HGzcnE9(a zZ2m`&{4R6UJ`A4F7Ae^APhe7nDZMiRH6G~ zVl{(FXQ~t+Wc*u>qu!sT$&1ZP_T?6Yx*{&6lsO-l{inUI+@;VafYm{Rj{g)#3p>7+ z3o(+KJ=qg!bTi$y7{>RXa-s{V-jnfzi!cLN}X6T>{s^@;x zMSU!na@>8Dd+9&c-1^_l*}t!-zab&pnZy@!tBH!xESeC^;U@NuFc;AlBVQ^HWFZy2 zE9tQ(#%L`XzWug{Ydjwj4;@!p*S6nEWrfWy390?uZuFUNO*8w1v(bRNFok`Q!|hhZ z1X(CE*N!Q4eT)$8eJoCy@b}5FwPWPtn!yG&n?AB+Y(J7IkDJmmvWJ?%S%6|4T`Lh! za{rJRl}XNYgGP*UGHs&#;L*lJISZ|ftXyw9brTauWB&ouAn$%qi!!=QZzu;Z$w>WL zYD`R+Hz)YJ0R*w=Cnf+L^FGb9cf!UPK_^y3S*}^fOE3%gAOs7&`9`7b>5mDuOZxJZ zeq*T*!z5iUf@{fXS35CE9NQK>#$T%?VEBt+L_wBOYAx_QZyg~u(mE!47qdM@CDUhaY(G;k1I=WQ4?(CjNPhwS;W zN3ZDU{%G9!vFS3qXoQb94)$42Br%DF`WE!3RP79~m0wZ``Pk=2tftA*)xQuw7a%Lw zEz5WSMXq!J`APqI|?BhI9F$3pM5uV-2X@&Ri)dky;xBU-w2-!=)w zS+QvL#OZ~}ODJ;!pUIJgEfzmYU>*?s(V(MED8NPF>`IT%usG-rYF@^RxT+&Q67M!- z8pEm~Y55?sZlvo*zv|hPnZ01XH|O?k-}WeDGcVGKi&ev$d8XQI*BY`^4v9q;)A36H zXqH5`C$0DpWiv;!#N!PV^yUExJPofdzGyZgeHsGd%nv>^+&H_|Qm7@(t(? zVx27;i|7Sn8Fdiw&F)ZQTAW_WmgSZB6nmJ+O3WZ;FNJLP-0q_hrrO4D>6I7r^H-?`NSmlpk^Hx4`VAeSW25sgvkKudA_l=) z+jfZch?plrG!P4(HFkbAYK)S{eCT*4fk|UvF!`58AV9z|hLC&YXeUPF@(X!f{kNbQ z#huBvP>T??POpJ(g6 zvNI(1A8`DJ_OyUSXm2#xffwj?M-0rO7go>WHJxtVxgyb`_-`Xpzm zfu9M!mTr!W%qs4rRquKIkCkymTMd!*m}qJ@9>ki`MC!RJ7!%PWRf-t-#0MBFMvTh% z3kOr0|3wiVaum$_u3;`KOqf+lU)J6fUQiYI&_3FUWylsg&bxjz^}kbmkq!+Yl^_`k zd%bHPy)kkf>x|J{dgw4y0lG{~iSp)%9dnf@?F>hS^URqzyr^cf73xm0Vm)Dj?D`s~ zm#;AX2j1QmHnNYHU;F$&gGDk>IwKeRhK&CHGHd?P_p>qo&EtqMQX>oUeKAQn<*6E= z3`{;bX}c6yd#^igrAMD1lIlvm#64K^g6;wn!1?AI{4=6tDe5LIx}#bclA`QPdekE6 zcFa!?9n1+abej!wFSpMW#U)}w^S=g5%#U+oSO-m{xb?8L%w(R&Gvc;?W%(YnwY)s~ z-2w#T8%F&C_^^**tmLQIr#zOSMGQh%jG~|V4JdOEnf)=34jUf81b727MJ;gM%MiC6 zM`xZGrvSo_nfR%?;TkUCZ(H}={Q+F- zgA735wALJ(@1_s92>zm@1l<}q?Cfd2c}&!=UiD|m#ccv>&+CQeU$=%h(i7p6XVJzo z58Vbmv0*o1m&na=+zlN4`0K)qd(J>`_y-p9`LkqyHhQGTy&WYVAO+eqrlC?e>llZo z22HO)|25$P-s0uo*Hz^mA>k*+iBjJa6ilM&_`=*Tibed#$B@{uklAJmU>2ETIq-RB z%PAtv$|bT5bI@5hzm~;y@3hV&XCK%?wk1t39e;19OEsn)?XLYhhb1P_oprwL>ah7W zwW&+lUc_Svb|G2hF1xF>RaL*Pv3^N1|tyfZ)53dsz;sE0WLByBoQl!#}(x z(eH#|_Dzw2c~!>7c|Rri+~yxKLrK{%tPY$MwVHm3dl}n!y)Jk@P_bLY&jipqQge|( zL)?=V8&e4o*uUXqz%@8xQYP@t$=D}Rd ziXgIv!x2CzvIRd{g5+5`(V-opv7U&kXX8m zJc)a|3zxzUd1!1uXrc>HH4}Hf^1=ZkUOl#0MF{5(!G5b?2ccD&o!DxLYsKsYFB>#!O4RyqQsLSuq!N`HHKtlws=N02^aINz7 zfq#l_b^yeQ@~+L}!nQ|u zF~py+`8ZZsEQdu;@`?a*C)8nHe4Fz?GjBxiv6W4RBK}28IOD^yx%Wz51sQG&Q9EwY zv#F;H5$D-Ie0iLso~V+NOB>V^OZfw`CVbpb5Ebox@Nj)+nBJ{T049=N&OkhBC}6uAc9*gxac`t(L{ zER@ia{nby%;O~-FrAX(yM(Ae(3^TrM^^7Y`hR@;QADcF~fs)sFJu`F$u z6k#5dl%0M~+Wmm0VD32btOl8p`L_b&ZmR*kCgBC~VOPH7${27XTtPM4zuJ!+{_e(= z6A_rS6?syE9sI-8WARTD$AmYrEaGh`BCH7j^`N&Qw=9US5ut>MT$#S>W5%i{zh5pyJdvfgS2-bwob?K8?tE{=_Hna3m(d_oIgvOMk1j@# zJ)n@K%(o=4QiK@WG5^D>r$b$he0p-2l`o93LEyEbn`Fl3R309iqseTF@XvH2HW~DmJCt{ zITDb9n07J)`D(TF2HY~LR0Jr`q5RLqaaqQl6!fo!pmp_ zI3>NY(>_{G=PQkV{>z*twu72@;;k5Gm&O+|v>2K3{fe~hj5KJ~J%QU8Ee|P_JWzmE z)+rG+ee8^jxG9o|tZ{qX78PdaMNHEcfzz<{3;z=4p7ZQE0}^b?r1J-iorgv8;kBjQ zVu~Yy5jOO~YHedZwLc3%7J!=GA9zfZKI^l_48O3vwM z2|PFk@fKv)Ns3BG35KNKGo%S7s{x7hWeG8;H7~!ktgP(*g$)5Szm4M0<{O9ZV|p#( z4z8i0FZ%(7BQQ%6nPcToB0YkAuXPTD)CF?2nGnLWN0iWTvMl>^qbX7lNHFV>N2(V1 zD*wJ~rBsc1eCYS@KUs$UXrAMb=LZQNj7*eLjh~WK4WvhU<~E`H;n__=XeN~{hZ>|bw z6)r)!;HO%lV^!=i=@+-|2g|N-3hUol06K(9d!!jIcOULR5qJkmkF}4be}5KQ5F{7B zGNu`+e2i|78cUlDCqFkv_s6r_Qpiz1g~_Vg_#U^k(0;9(M4janlS_&{{(=)))^L$& zYVG_V1*8vd1%N6`S>@X$heCKs#>to!-Z~1!%Io$#EyzF|9 z?z=Y=?3X$CJ>sl`YySW1bqXbR=wvfhS?N*JYI54G#x}TUCT=4u=#m!=SbHr(^}bfy z3!ElB&RFz#!peKgLdI5-u}2!p!)FMrRF&x~TH06{PZ~Gk?_Arii#PIrb7!e8WXu<& z7*7PSB&x6Rx|ZLIo5l}{z$$bv)oZ!?%w7yZwkAhpK7mafcG<#Qwb&Zj5yvLhc}|dB zm2DcbpMCVmC6S@HADe1x@;surf#1$w=(8JOW2fP0mEo+hgYjEu7CJT0gb3V%x)j)S;XS!kLC4I+NP#cw~8PkZ7RcUbzpafqH z*S~^%n`vtM?tkJhqZKK<1WJQz=#kGuN}YXZxE(8JY@6bX6Sev)B+(I?I^(hI(S8&?n#}-{k9pa*7G-@ z%2LX4dHG`UmOV86EzVQz>~rcK1*L|!flPygqmYZ(UfBa@e!3HY-`=uoT;YBud(BDb z+mU+50_2+CRDa-8H<4Z2f7FBpTs2By@TTM~+cG-9?PlD*_w>5Q_mr!5w0^}LutTR% zlxm2A{w zMobphJkh-$ODJP4d2rw$s$8mnC_?3a|4a|S8yMG=#6>GNVxI_p<(Cflsl)^@4YA(g zD0$jaT=dJ#D07UVV9wxFImh--!0|h#^+)J|&mA-P^rCBTtL|-F({BE=)El^S?dmbw zS{qNC_1PohpH}^O!`N0=S>kmjZs!$+PumK#hvVk7d8N|6N)O_S%?IU#u628`zckW;BH7c0f zx}9-!E3dUQG)qT+NTT*#}-MRB{jqfkObOl+V zqJU!JEG>8+W^!RCuG8youD{de5%5zf#t|!DMy*v$8lZMD<&T}57(G$SPNdMwj1BfG z|2r3u=`DjV9tKYlU9IqAaC$8y%*Su6(1eth_e z9M!oRDco`|J70{XX$~5K3@1ZjYx`@J`OxNsSTRBXOs3V>=MG`8Mhqn$m>3I4)dqKn z{yUfPDvp~UW@_yF-4GiN#)xhl?LZXv`~kpNsKrO$I`d}~076`uOm4G`1yq~vN(>`_ zlfNyH@bWYR2aAk3N6WDgyVg>2j_Zs1J*T6Rgi&fV zEg?njAo8%_tN80EygLxfGNN33gr*xmvA@#Qx&2Lu8bSyHu+RdAm5K-mO<6nM;aif0 z{d2XZYPU0;2@aHu-eJGDH$tiIB$9#}Ga}&*_#mNcI}Ksd(gBz3{xx!siJ7{1hU7px zc>Z#(GP4^GCIW7`Dt%On9oh^hoSprJJbyxYKf`E%(*#;$lPu*C4wvu5%b(Z{mS)ZF zTGTWWNpB&I><(BpH6Q(MG8{R4b8OwVJyymA>D~Fci+^K>1-xMNpD-FM|JLb-Hp}1( zr4uV$>a^H%N*pf3`Cf{Bw1AzT436TET!EpNJhpCGMdn|0B;6-hPlwD4KWkR(a?YU2#774%R4w+l1Y9CtploBn4*a zgX}y>K^INcm^0sFA-*vQr4zC##$VWOj+c+qeSDm}1D(})_rN54!}*lv2cduOm>^E~ zSm2bOG7K1|Ezdp8+ICY(swIbp)3z1=W3(JQvX2!1Op7X!CHv5zc6p%Y)6;!9X#%Ta zN+-;Yd=Fxe%4fWIo|=PIE7oMhoH)+M@9D-(SgjYchdkO zFd1b|J0;V-I#v5m7ePF`N2U2AD z09CO-Dquzyvvj+Zv*?=KUVkl|>=wxFrWEDGglZF&T3Eaqm^Q~24g@8rGc>Ob<$$eQ z8qF-Q)nWn*;h;uMP~Ie8(oIx9bb@JyJ8X$4Sv|9(8I0x0Pd)wz?Up`y%iGF4*B|uJ z6MYh}MEf^+-QFsIi4i=(#C$;K;`c&=O>9yuN+|KtPqZL)k!MKg3q2T@}tv+{KnWMK(5R{(U zlvnNVv1Vh(b+F#)s2!-`r0&lDovSJ~9`ibI4z8eeRCzd9mFW&VBDttWLYkGi3_HoK zBx%mJ*l5Al4jm2F6l3%iYVQ!-(+4e&H1ylG#kd9*xxT_NKi8}EqP8%1yvPU0psOqO z$>7mPUxxH~$4w2kO?>(~L-DMEb?w6L5- zct$m<|C32@$4GiGBL8N zUh*St15?xa8LchMw9+ESPvoD!`tu~|_oi!a>aYg*5TkRd8R2B}os2IQp!nPZh9(j? zf{mT)GQ{Q$ncE8-`{ln+U8qO3*Vbch^mgB~I@?V22?d=+%-l@aVIR{;YTc#G@YDnj zW&ouxQI@6QMOtu-R&^s7?vHHldIoRix`^_B$WlP(rJ=RMMU z*so?u5-}4+bTIham+-pDt?b%PJCsxGMnJpMZ68j546`j?jLNV_{n>X#R0n9{4me)C z^5OD64?)>UhR+?KiRc@^)&LgEF>S;iR@WbOX6t*GkSpM~(9s9ld}T%`0x1Vb=6uS_ z*X)mOuYWiwOe$@3j45i~UUzyv8|T@Tep}B60E-(a0K&%!Gig3pafNsgI5BuaR9BV% z)6Bearz0ejAqA(0+Y3K!xD7qOBtYzvGXiaWK1;KBwhQuCMIZ(+Y(Eu9=$ zvsHh#GWICM*G+V5%%oZLl&8e(OBq@_Mzg^|QL8z5#>m=WlDEFd6% zwyVNw|AO_$qlbvc-4&onMfx(a}s;>MKWfm9{8yte{Um( z0cOU`I8I56QpTBib1Tqum}lmA9d;CqeVLt4N?&py0^v-mdZG(oqV6qKu`3}W9dy4Q zYSSV2?;ZR(FmbG@Z7j5j{D^jgI}kAcd<00Odp)4#pN>S>0jMW^(qxMab?n$ej4a9v z7{XsWhJmKJ9CPH=K`#rINBg`syqR^Ze>$Aat0Jqj&r7H-m3y0}TZiIJLN;_FbMrf&=66)fNHm z`84`WR7GIbo>JTn)>gbilCX>Xw+<|!65RSa3yWrqc`(5u|i1QOnh1;{zj-n8vLWt;BZ{?@mWlPm@ zho6l9?uuv_BaYhi+}$TULsO})Mm>{pE2f$QT_8ENA^~L=p-dPAo64y?5dX7g2`B;k zJAVcDBubJ{&kkg|iAL%TPWdHoT`YFwTwxt&x6El{Bj7?0(y4U8aeq?pQ|9E4CI>NJ z1%2mUs-nR9Yt$1%sc<}y2if+g>4t|F`|L*g&bWfWS^AX-5}-brG>7N_(pE4KNqD)A zH_p%Z;sBBm^uv$2-+LGSiQ_Nhe&CkTr5pqhL$=`?qD=&;0?SfS#m-TTUjq|dmi&$U zeCUyvS@^w#+1bHV&LXfxb@~7jI*}o%%?bxXfz-llo3!p{#}r}y_8oX5^h5GpW*Y{{ z=bc|2VGL<74FGCy#axh;m#SwfZ-4@wvxDH(J<%5Ue<#9D;%@C;6cTZDW|I<>qt1Mn z025Zj9t$+<^zT!4G+*&F(*n3Vfuxk6+`m<{=mRl9S>7c{(5@;;wcfYFfCT#_M8Ai8 zc&cjP@hQGdDXM^Zgy3h0*zUOBisQAvV}M2do5za@s%6j08RCa(9g5y+ZP_>L`jPqk z{+h`;B4Kq8&KRZ4TLp*?%iw+}WflP}z$jU5b5c}sYDJJ3TSzjyX8|>{Q z%;665T`?%lK z7%?s`yy3Lo=nt;l&pQeIyl~S{`WW}~BNN-FQbUXk!QK6d+jv%jTVjMyUX}o;Xah%P z!eub#jyI~#XfS08(ckj&vGU%^4_9DQV^!4Z%aUCX8?GUdC8!ZnyvnUR(HZ?M85iX2%FE(}bDc*Tb> z#9OfQm7w@v%pN?r#P3SSD70E%_#~Yq2c%#D=N>7r^$*)!KWrU-*d_uVAK~`vn3^|# z#i{FdyF=PRme%{eLo>j~l&`vz#$?^Wdky)c*EbX;2zhd-FFwhwc0@%$1JH}Und=4M z%|VPxebfx;))U~2ka@u?p8iTn^tL?9!T0iY_UFT08ZWSaQU@`)(sWtDYw>aKz@(Y| zgTmR7kxW77q`(O_8voMcRK(^ae`9k#9tLc(MP(-+uPV+AXD9*tk~qKPvmv ze^DE1-fE|1ebZI38wp@E_Xwj9B~4Ungd9%mwBp>$-w)RiUuM4C9#3P&J1l-w-03XVEb~>!}D_3L4a7cz|kN4o6(8)82w?#UZh*oAk|q+bvjO!b|8pfM(iT<)Z$89S zs5&bmJ%L`HrTy3j6AHX>FVWK90p2Y^B%0BhSGM1UHsZxjNyvz`61L^dfjQl#A4#f# zerh&dvgAK@=Cja`%q^Ly`;7jbj6bWpI>}Kwz9dzR&r@3%TL&S@_^A$8nkUMLU%B-Oczg1@_fN4aJ?eiK`!@nZ z-+z{*+g<610}8iIdkC;!gzg5{`SZv@-zA&1HtKG+n4Z~OFGWSd*S0}{PBcJyhBAoT z70;c`*ZO=tlh2n&Fb9MoaiNO>RawE)y$pO&DirhD4eYISKfx41+bX#aaG||6{rjO@ zHN%sg=+j!>nn}|1;55N_q=fg|oC7^RUVaVW{zK|?naoN#GVnIlr~9X0caeJaai=`Z z995WhFW_iN9p7C@B^j3TLtT;h7Q*M_886xDad0-Bh`q$7rW$F2*`^wJ0tfK>@_6v~N#|8+jqS0{qeZq+ z=?(g4)4+Hxby640BV%oLGbCkE?S8*-2m%rU!bjd!!r16yva{$5Me`LI8&yag{aFOL zcI-;Y{svfE&U;3|ML8MvB|T)UDUVd|dirU{Kb@7Wp=tQoTa@j{P9DV-mVtpOGau7K z6N?(Lr+A~I!;2(n4Jwb}onQr0X}(qWkC|#u{IFF@F!@khFr$l<;XnJ)39{Qt8q?vN zzw0!Qew5X=KJCaD7srNjF-Z<%pz@={C#eYs^+Y@=nkI(ACYr~~G-M<|K3AJw0{hzh z_z%z*G~OT6nLI+=cT3~l^NX8jcc2>n0nY(8r^LO2oEhKXJYCWIn@ap(Wm@o#;o)vw ztQ^V_D<^;kcAQ`=b8<*EdtgQ_eVAuw@IVwB^{_^I@bjcsekkTs-9t*tpE$V3Ka90c z&NI{gq^7R+ZS*wziz8y8*!aDm47NJ`b?%zx-*a0V(|Suz1jG3;NMNXAWTf&VU!70RR9+EbzHh1 z6fkR(zHw;-ATy}^CwGXq#PK6_=e*U`S0|@;4c0Wk@vD{(51!H?Zl%Fau-#ZVjYHh3 za*LW|ZG^(E3-WzWVa=NYU@yd(8+eg$TUuh)?kp)SxT; z%*0Q$UxDu!J-HIn9Q1FJmn=Sr@+luL!wxGU)F2fa7yV#Gb+wP54*{G)amH2&HeZYR zV(v)W4?C_})4@4DQh6LmUxpw}U{8&4MG+s0@2IzVZFeEaqzjraDKU>@G#TB(E^&w` zTprkdrHHr1sX%KSX2j^7&3=ZBhHy5YNsNAm8aX|kU00j1RPzH`6t`q!A8fs!{Z_5v z+i@N5Mu0%XqX+0x$RAbw*6@v=s8-@hzm+GiOwoP22-mV1n#Q-97v@D(>`43HsF_4R+2lS2@ezB*EIw2c3JHkL`k-s3=63roHroTW z)?<|m_>a7m9;PISm@ng09iMP)9l(~i_WMB=3hQZ76ZZPG^-B$miX1R;uedM4kb|ud zR904kdiC%?`jofYRrvO+qD$8i58f86E+K(6$5v)pfC`kD7rg?n%oC5mG^7M=XK_0{ z1+Rae?5Ii?Bjvy{TDnL}t#6kTPDd~OfDjb^{H#`pHe#&GY86HM`$WBW_@_#v!|C4u zF^~11#jf6c6IWpNt^{e64NgO#&0mkpm{C5)+|CeMg3yW(bg?`TM7&!cYYe1=-%l?+ zo;+4w(rC@Grfqz<8tTY%~SLYMeHd6;pq>QKpp{-gp5uAZss9+dvoe{ol@+eZbtnZ{9)*N!~l?0 zjYfO8S9ie+z*IFEw)_|?!sTKBT$mtQbU`{f71!V%{jhST?*a5Pv2{aQf=g1YJt(-! zni08;m8yr;h;lu4&WNnnq>V~x4p0(hQd0H2Uk~8HKkkg#?U+ZN#C&S`&x-mQ0nwYl zt9~OE%Kk&0Gc7Uiw3mr$1R2Qa`_rB21(buH!EfLU16+}e!fG=NNSlOK1JII#wByDp zW}w5G^c>nbe$%W{+u(-;p(N2Gn|%_35CL%c&sVBwC9{+xd4F`4bD!6K3iFG4*EzfYtx&BR|9LBYkRsZRo#pl4DEPwh{?nr8@gA@z4OJr8BenepHe58c!-Fg00l|a+( z#5l22jVUD(55)5(XC*{L8|++^{u5QucGSpqaVpeKf#k{kZh;QwmnuYfoIK0`h=kvc z%8_{2jG$nd<#QErp=^`D7{$PX^-%NH`wA#WoZp*#?0W;2{@D&#HAfyl@+>`48PF+Q zU2c*z`3ZJKij?2q_C*eheK|+i$qb%LonfC1P`^j^&s}}1 z2lDQiO|8HjmJ=G{-f`M&Bia%^KgD=+fL3RyfX`QJ|JClIu#BX)a+a)Xajm!lPFuOe znSQDhuE+@9>MA;pw062)9lxw7dJo3B%L(qMi(xdONrF}w`rHw65YZCp4d09H+_mg~ z)HSvJ(>Ea_=KJaw(@lUrff%G}JMb->r+-+z>{^53JEyS4BV%Ac1d?+t(ks`anj7 zN&jxwf9Pvoxn!A4^Gfzj{=r87l6B>0+~*6*TWFC7k{`oZ#~6L|y}{*L1^H|hD5$p9 zPlo!D2(vwDR7p zRsy$D^tsY1IqrbQVs}a>hPRcjj=X!ihY3EE}G0;xx!vnS=wBw7sM8#G`FA|?(U}2VHREjMS&mB8z}YlxtjY_ zK~4)$Ux*m;*`58Z(*u%c^iUW67#1)VrsJz(V*d_@N{Li8y#!&AO}iodr7BK(a1+ro zSs)j0W(mapmoNd4-*kSsAU(nH+v9CqOYGHh*^8=pOy`z>b-Mq9a6? zwj;MZnXHz2Lp*cg&!-cnsK5?=%$!$DK!<(_pY=8R!-L*jSAEgsTWm?|x2pG~YZz6* z-y@{W;qlQi)3W-dq8|2Y=NYNxMWHMz0pD)G^a^Ki>}**B(y8QTFH2ub+*`)(C>JBvC77>c6mzg?^hSr?S1=*VE(Yo|*{?m_ud& zj$;$_W$6eYp*Xsaz(3m>fZo*<~8vH@k}&|SZmYx?%E7te|uqGwM5t#<$M)xRX%k`F-yzUw>lXmXqgI1?F$q_Li!Vb5)VSmEC)cA}e)ieT-~2JFCgej5 zftv`Nfl2d7JKzd;^}AbPgpRMm;Qsi#jdGeUNI~BxrzPif0Yp^o1INhBD61`XLxADA z&`ukc$tY>uBk+$qe@YhRuSD5zck$65-^+j+bD63opl_wyjHw8Hk{zfow-s2`)Yt*! zKaeXXFPaSU;i@y5>Ib)&NdJ1!^icmkZz%Mm=WAj=)iM;ITLS?}DOjG6XM=SgBZXo# zezQOSydjT5b$m|+{4uokZwTX>T^14=MGTvSIco{pkg$DMu-^4tRs&i;PTR6NoEO*mQ#$tx$x}Sz zkw>H7#plW}EKJ62YXLJ0g3M-o{`ScUDc0tB8_f6PetbBjFrXeVB6Jd!8ymNBU)Ltg zyQVEAw{|EjZ*@ZXKXp39UOri4tVX*f@Z5`=CR3o}lHWIwaN#zLKAe}*iPNNixEUkL zF*-FA$3j#o>&#d;?qRSFPmqgvLkurH*^zkw|X6TkT9 zqd(HcSyB?T#-%Xl-6*Rw-d@Qe1rXa)QKKWQlj;*$ET~aHz@O+#fDZ%PVS#Oz;3W30 zxgnAh0twm^k-2N+-(e0Gh9R1a2b2VT*=5AAp%`v76z|?URe_ei3b#{RuhfCNXOO?j zR?I8uHobf^r_`eFJAQ?JS}j=|;RC7mXRMMM?%TzkO^ zWI>&}wWi23NM|;A=K-#RMN&tckItU)tCiQAXv!e=QX{J}SoeLzI-$3m&Q#2`@^CM|- zBng5zX!H__pgqGk)?gTV1^q{Z#(O{B{s#Lu=e#yH0R2}L#@DeHqpIRtoBD&wIEx!y zZoik=j?sB|{SUiYNk_Q($7l3$W6fb9oVNEpDWP4eiSjJyEC_;fg0aWkVej@7?6beDX=56*G7) zufL3Q31=x%ZjtGf*T=>u2G_CFnS$gzH$uGz^9z=nX#p3WWbVNo-@&JhO_7atu)M8| ziy#dURhK4iZ3rX&$3WY|3VzM3Y&;StGGwghp6~!SuctO5t~DiYZbP9*AKbEeqL`W{ z5$}HnyOlYA2?$`#=Tl8X=C=K+K{s5$ZTQeU1fy00P#``7+FTry@U3F06H^PJX)s(T zHqR%(Z{&k&9d~Yb{2fDUpbuQ?ryp- z+R_oXVdZQOz)I3rpI3+VAGuifieOk8-#9j1+gON#B9?L485iwwH!56#F8_4I53=hY z>3Bv3h+eJ6HZLF^I#;mJWQKxc8Sy~l{O)5U`yS*{Bw9(~#*TRe)VfA_Evpz%eK%ik zbI7LClFHO3H?v}xG zUw#J2i?i;LCtDCYjHB%9%EYIK1rCa$qkncjumNy_gFsTE5^d#)(Ia^Ljp-%oLjJ(O zfO{czpSm1hRpSHh5FnQ+OHYtWSAF@(ao|y?J;1B2YWt=XJ??A@B@k43Td|^ zZ>m7}_$L~SYbv7M+eIgCGcJqdo-I1=6Z4h7F$e%+-6kMt!PP@$2mf~6k|x;vp4%bY z-tgdMEm|A6V8Cx7Qs%33_;jFb`!T!`n5IJ=#t9tcXsTWv+B- zwr|Gi-ihZwbN-1jVMa&Srfx+9y;9 zgMbAo2aApX0Q?tGsyu7yt2L+@RTA)f^`sE7#-0G3EluZ*AW`3Zl0%h<79|WZqry~*9DUclEVMhJ2djNSGd1)Z)h{27o6Mtmf&fjdah zGl0}iS40D5%iPFU7g}Ru@KqP1yIH>{HiF(MQI$0^fhwc7T;1X~-?$|)jn6moPoK=x zK664jaEdf%XggA3?#kJIqO-3HE&YL_31VvleUp5M1{8@xE_YT6NXFemSQ#t9XEBsI zLpUYHSkA<#i6kKW*eEThoj(#QpBNnxyw7KY05zs7O=VR1;vnYlrwf6=c*fSR#J|No zlN7T4K{xM4{@aR%rIS5TH1Ezh9p8_8I!-c=9>49}(f0duM&ybqMH(ebg#l*n#5{db z_b?0Y&hGYv0KdxvoF)M)f-HQXL{riK(+Rx%aIP!Cuf1#)=i^Ppe82($ok$q=~jMGZxLMyBqI{$#*j^ zA9T?eD`#Wk{UaL;6xTimlyPSJ z_KKmyxJMZ{yqI8g1Fiva-%ZX_7mY?b9lJMcCB{k>AI*(%g$b#U@R$c+p$kpIsK4_H zuzg{F&DD?ltZF9MyeOMM9K_FmY|Xm%ulCllmTlo0de+bKN#~OqCr9w}7j#&cO3p`A zWJH|5o)r|cx9}z)n46*KStZ-xit+)5L2P-h;n6*FOdcr|0Ku~qT zG~1dYw#nA;;dUSdbb@KLg;}i4FJVNsZ%h@0bU*!B#qo{dEbCw=qpnWuIj-jcP)eii zf42+|%+8Wr>H8#qVjWzadxR#3sUV}i&h^+cm`N)6z}PmRb1i;b00`J8(j4chXAxTPfAo^X`}iO0TClyyl4(wlmimP$sqWQ z&y<@GyfSMmxfM-#BOFovr>r|qk<_S_Zd>;0_NlM4_*Esrxw^|1_cqoglDpWT`GMSu ziErGb%j+yZ%)dwoC&!)?Nk|Zq0#zm`Mfa7MSAvmH-Y(=#flc;Ocu5_pHy5*&(S=uS zDYh{_^*Ss>$ye^;F@SS2Dpm-v0ygWhb!_B}T7gZ{4G3r`OAXbgCMLD|0A48_hC=8EU_+B7@B4&++7EMD z1Rk7j`a*LccY96>mfdiT1nitedw^_;%e<9Tn6mzDie}#)>HpyF+&Fi#&G^+Xw`@K> zH*%Wr!N|*UsHY`}wl$|D<*$T;Wf2}paA&g-(%h`L-Ff4^Kl5s=j0&qEuq2PP#KIZ4DXUrve6r7w=0%uH;@wa3J^SGWq#F1%ZnI%2y|{@4m@>}~Jw8aV z72Mb!XHZS0VsP%9q=V_5&sYM=Troh5n$m3~S&z=P+gG%iU-DBz7_}MyM{5GzcZ}Hw z*BCZ91{aFbCOLgvE1JYww~_Lg1B8u#AS=L0v!wM2C62^BwB@QJV0l`;L@sIUG}}5w z+qX_;Xk1ze$i{yHTgNq)v5lywy@52He*-)AnJYh~Lxs9`+QLS2C^=_B@fpI~^T&OX z(*$P}nfrmm7Tj0#{JEPmPyn_G{EF56XUWD$q^=%db(F){(rdpQlRT}6YAU)V-aLpg zOvsIKzM={-;u1451&?3W?qRI9P95+H9Rj8faG_7%@W6a{W>V5$=3J&)ofU<8TbzQPRn+g?*{wyV2d#+*=mAnP=j=;4( zdj0b1vWp5dDk^I5skd22tVntZ_PxW(*<}}>1jQ&byd3K5-;2ndpp2G9?AqG+*$2!P$$-%?)w8#ymZc}DA7Z*P7UIY%TC8(w@>)gRN#I&v3!o^_K08^vit-wZcMS;o66lpAe;(S|=h}IQrt}r-J!E#loh{Ruz-%jHt6n z(DH@ffwIAW!8=~;*$vAoHr8m~poHU1X5H)Q< zxgra3YvJKr(uz8c5FTelDe_vc9=k>STalmfDdQ*)WlgNiLVZ+uu7^l^dd}FMX=eP+ zm{snLGj(B0pN2CEI}%ec+jZ}Q{Z!V3r8ru5 zI)C88zUIj0bkV*C+)ZC4jR}}Nd-0`7Tj%WvfwnYWZ0&2dm*bxvg}$Ba#9}CQG2Iws zd*w&3DGE2V&Oaf*m#D_`5Gb}uR6KZDFsX0NRy~UmdIP&}=t1;rZlwhE&jnvUx-Jp< z>-^`PZ%yJB?hf5fn>M)=t9a@AVhk7%FeOmMC<%*sC51nejIr&CV4ANWZ5!k4o$vFg zG$_{Z#qV{n21<>&94*}E-b{%SA609b?F`1jZx#shhAEyW1m8?>xt#X?hyhhaTu7QL zH$W-=TB!84XBOX^#cy!qyn@{$u(E*d z*hsL_TKjX8oe$1%Kw`$QptXbrUya#qZ1rNXJTS4}_YAIDg`T-@9Lo%_uw5dy35i>frs{{PluDw-1yOB%b1naa(QqXy? zzejML+FRCt1*;_<_B=%M2(h`u4Jc+`9$V6j+|j_}>K8Q`FnH9O&F-XYQZ$f=o3{E^ zrzIfc8zqO`WzOlsm#Wg`KpMJC1QQgdOhu`x8DK4eS>&q5`R&Tr;(x)x>Dw4SJuOYT zh=9E-U#6>@k+&vm&yI1MvTN8JaeO`4w{5nlT_P9p?A_a4NG$NOkA0!}?> z{B0WL3|QLi(xChR?Rh=ls8{@MWi^+8zsI$(w_MFWmgk<57FZuZpoHR|u919`(v4H2ZvL)7#x6McfYU`!DXJRvL}+b3aik ziaw+%6}&k;v&5jLg(i=&=JNbiP~KR%Yd&1uwb1R94t;A0M7|Vo6%C~ZxV$+9>_J=V zyuOsCcsf+s*{jVHQny*0!7_MiqQL`clrr8POyj5K#pmfgwR5Qp4M)0*G>>|bD6Kb( za?IUA6Mr9Q4&z<46K27-vCoe#_xuTz5`u|k63a^B0*#r$JE zP$VPdWt*e3nBA5|j7R=z9aE~yjo-)mpoBL9~fc zqApt|^OUzxv3Bp5z+bRh zWf}>ABo7BGrcFkF(P%k=jllWTP3j|})GfGWZ^a`*V%0QQ541)nk>!lhhRCGFWzuc7 z7}bVpFmyMgdq>=iJZkCbbuqkh^PWIlYo6XtK91G);K2jq{>CkBG*`1_4(~ASPS2yK?Tga049utyJ6Bld+Upu8sRqK6M7A2BKoCsc^Qo%PjWjjF^JHIzFp zNln>EZ`tbk==rMZ%jXFD4$_K(7N<>uPh!pO-WC2F`hP8em6D+CDzN77W?(BlcJgw` zy4HG(@bMrYV|SVh0>P0ziwk3N)z#hgsPiLb?0l~JTk2Mo-yTRI&i~q2Tf$m(`}%*V zL${@9RMd|TeFqfJjSbZmilJY)i%xjeO=|J+(z2(kf!k9TOENyWn~?MFUKi|6LG56? zLtq;S>|f6w|BPpscg?=SHfpb#zE^`o|OvLs5#twH8d`7$Ata8S(8nbdDd{hO+O`nGM9{jd7%_^;4m;Zoa&x8p4a<4#BA&YA4 zCLxXl#nDs0gok-6Qw7jMF4a=Wy&b!mPEA<6l<&Xb(=dH$hx0=#NK2KgS+Ww(l#fk1 zdq3mH7YC2IUln0LMbHhm_EVI(4xlu}*S}QJ@>8tiU7){I`90qhxDNzc9`qYueQKvW zL5k962H?u~Gr}UhvkwCk-6c)#G8Db_^yS7tOf_a*uQD_yElLp``hPTic_38Z|31TH z-^RXW9m3cuqbwC6dqpv}XXh$E*6>Fg7wJ+27-&JYv`T&$HCiE#z&WFD${K2>KvaAE_qoY0ku5)H=(F zmH@qaVI-lfiVlZZEulx{hs{z^t^Jx~S+8+r2nP!-R&!G^?oTIfk(; z)}at>xM3JsTDB%}UEwibygdh8J$Cq_|6QGbqXc5wzR=mlhvj~Lg?Jyj^s z=Y?xH>IVdS)Q1w@IN*zI@u7R@VA~fL|GvhK4%YJ!r{r@5ZFaBU#{Rq&7ePyoa`H8B9lCZls4c^9r+}%scF@z2$z?8dSh$#&v^zt!S^@&}3RH{-P$y9NndPeg z@wx0}GE|Hh-@)zU6^iIeNPQM5Nf|($I_2{kT7%a58VJ5Iz6d zv-YkJh@;y>c5n6gXvcxc(B_@NY7rn?lQF_Cc!{qvI!oCyVYbNnL;C`pg4-2<%iXI! z3uC+$;OxKN=K5T#T>{$Di!@d}smVjKD-Sj)5Sc-AQ3J10Vn>x}qCdV+U#nx!>?3_g z5ur}8GK(X_woih%^Y?&Q$RJpG9q}dA3+}Ue`vl-!;wzaS7|3J6edvOJTA_#Edo`!F z)R2_&>2`6z5549i!1_|GU?>QTFWGaV5V!n9k3%6`747R!W~Dx`JeY9W#>OEt^ zz8V$>`cyQoHe9t z4n7hLcR*oZU%%k#wiLC&` zMoo~5bM)5hplp^Dj@p7CnH|0OHe3QVN3-U=pDw zriutBOK!#XA=XfX`}2*UrAPp3^RNekbg8yRmWI)f!z@6~CY=k{S&r#BrPifUwJ`Yi zz*s+9aX)xii>0eVAt$xp#&k&ezG}csjYwbMEIFZsIN^%QphqfeeplJ}$jC_J+oLG+ z1?3Vj8PE?DlP2_0T6RP-^5ZTQHWb@3&%0Q%SZ2}$Yq+k4F)1r;{pnT&4(#w|-9v2M z&;bwZVSd`nZr}TYuZ3a3Jh4`vFFi&Boiq@jVxOly1zK2+~@{UhTw3JTRdD%y&Ua0@Yho`|V-p$VX{u33mL^1I#@m>9@)Vx$m zOF&^m#iTDSHlE4VwYvd@y`o7d{wbj4&AeAM;l@5O)XF`p3Uz*b25NyL8K-SHr{qHm z_hP?>I3>wUJtAmr-);Rvn~+|MeMB!WPY(*(4fG8<+4+3c{({c?8ejwc31Pta1sL1M1Hv4NqkvfLudUU)rmVdu6znE^q->Sl~tJ zWa#7AI1zVHXmHA^R|qv;CcQXh_6&8EeLpo-4xvYE%H--oI61eD=3#EJs){CzyP+(+ zLiCq5ofK=&K~@xSsXtlI_rkp*2lYoBj6hWfN~@W#pV}|+5t^lyV6eeom|VMlRn=(% zMu2WcsHxg3V?~F=#G{)6$K}fP~SxM?KoiA}9hTUv(v*(x;P)5UpKvCRL zBq3jgJ>A4qKqOA4$+oSo)`{Zsspfc8DR@(N@%1JtY32^}tWDt?j6GmQyU^OAu>`yS zsn&G~q4kUzsERc?S7WWn20+qHb;;7zd@^8E`+#4f2^cBabr04;F)?QVz`!EY%%W8V zH*H^S;2K~|&$yCX405Z7OPHUX`MmScYxRp^9#wvim?m{pnaafxNc*KHGUex3Bq5hrcOe!ekE5@i#KiIXUr}egCqn zG97R{$hTEN6a3t+PHrMQa<13_P&Z+=idW%CO>mb!m{A@rP&8qZ%R0}zu@0Q)WuGqE zod-Ya%*iZIScFbP_j!%}4V0Q-88ITqB%$gSCTU4Q%sitrTrqJtqTr3&3<*N zosTE>cV8vs>Nf3rsA)Bjm#SV(NSzbnfwx_18mai&~UAIk_3BcM$tx^w}+MaD@s!ZxNKt?N`rk)tuT^4%0C*fxtV>H~gLm z=LU4k)tjf6ST~eAmni$Y{SuBUsZ;lN2p(T>REiM51(4H8LBt@ki-LvytN>oJ!Sb^t zNh{U2`!?o&`l_VfekxY-AgMOfQaOvNt2YE7`xkgg7+3$!yG+7`n??9L%lFWmk%Yp- zW6g&jC_vccC}-4w+_Y3SAmn~T(?g|!JzPOQDNn_el9S$Z)7E%XF&lm(_q&W)Za8-d zr|$kvzWZYFcJ`NMw_GD6x%EMQtv6Cetn;M?f*e5+{B=c)hOT%IRlVOo;4+_8s~HEl z%&fz}IPb+A$)}mvTxpq4hCH3+v#|N3Z=@D;p*&mcq zIy%3Vsy*IcfG;%NmrSz#d*Qi;{^|1)&Vs;Eq92i1ZbY_y2LR!*H9-W0u_K~bZ@~1+ zYBaljOhiG?qP5*r56U%=rZs_4C%^omMF_ejq~;iqk%_ z*RQ9&(H<{46qvnR1~Y&JXetEMvaphpWz!P+I2YOcn#50vQfwA>;ts|IW#fJS+j%0( z;J5>aY}FcmYeJf*>yX02QYfY76gi~pz{P`P6C@g2AF7KN#`Ap^&S1Zh!VpmfR)6_p z5-wY_)NzJ)3j$EzL&ix8C zU6YvI4;KcVN-wFO)7SY~Zo&Qc@1g6rPP2-7q14)@#-tG0zZaUqH-dU8vU zErL>jJI;ZoPAE)DOd&S$kMH)q3CS%7_H5K^tsT>XsKoB?NEwv|6mA>Q(6hq(T8baI z%mu!Ykfo=xPv70YR`7M3Pr3qWX?Pmea0R;6`pDX68We9rbT;Y+q)rVXW|!z8XuIVh zV=+O;?Ef6h{@6D8KkB;MI;MzU5D<;oXAUo z!bDJY`@4##(?9Rfg|Oa8N4@Yq=|L-@4uExS^HWa`-*-ZK(XGEW;q8ezD+I zTxuKP>3LX}xX|>?TV6mG%TAI!yP_+H)=~ZH2eRJA4CH1~+7S%{KY@54_kc6ER{w3x z5kj%aukw@}R-5m;k!#m%fTWTXxj~;zeQzg6$6Y1NtZ?z>unY{Dli_YD`$`t#TH41< zA9tC104f*bFar2@;hh{!;AA6`pmaYkN7oZYcIM(KytRA+EEMG z&)j&0i3rdqC!tP4MrLQ?pllb^Zk9R38q*;+&3Wfh>WYaPjKO0Z?W9Bmm&pg;H8;U) z>@C+WC?V7xs@l~bM}E)6p$hCf`);CA%-K`?E=&$lm_};))cDwzRfR@tc|;oAxESGu z=n~(meo&!5ozFL>mFKC<^k$_R&y)odSH-u;v(wEDo4HyDAG6TjtJWZ|>MU0S0SVzo0i&JcmlIC8zOx5b6P0^<*@-09qbs>+`1sKV z21iToQ^(MU-Ue?$-{bGS&@w5tn+z_D3+J%{G{^Z9dh(?yBC0YeLGCG6H?I!=2|y$c zK_U3YWBr&0k@^SUQ$#%2`hEK@{3fSsqBrM8V%&K-O(4rH9S#iG1Ml&(dNx3dw&vR2 z<_|_xj05Mxey|8d)rM&MptWX9I!z42zrNN#U`~pdGC>PfT86CN@(H|HIIjisepJj1 zJ4zw>Ig(>BspSq(%Ln6E*YR+q%r0_+Atpq^$%UAWX<>Pnq+A6Lf9>{jCA;)u&b|Vz z3B$i5fyp$C2#M0K7@B+vH{pBxH%?33w#Jixv}HG+;IDCq_(v#{a&F0RQi}>0y`0;SV!I7Iqi{Ky2@vdj$_928~jJsxLn^@|YY0-s?6>5DxMK+vw zy~?k2AX%~`=#;zI!Hd70X%gY*Osko*)NYw87;Wf4N&nbz3A%TrWTHC{6Ds(PVo%i} zRfc4-&a>W9VM-AXpQLo}K0uDGgLO*lYpsPX``aMF7NW-VtN>KdMUjko@6lSbTJ}>5 z%+Uf%Cj`Es;Ggz(mTzy6pio66BX?6zvccus`>6<_p8gz=rJLS|D7(Lqs$6i9Z61)t zN*3cu)HzN&3Je}=mV68ogH9@QTIX#f{p6F?`75idGN8hs#|c0T^kgKBF3rluK#7zmVL0F_1YvfZ09h(x`?GOKzV zTgaQyV?@83uf1lW$hHsol~R}YmljO69K1hTUbX7rb|&=u0t8B)g><3eGdwuSE}n2b zjw?4lIC%PH{G@`SEnK-lb?64k1WDc92y-SjFCcvCRBn=yp`6$wv{Bwy>0L%AiTRl2 zZ_;X%ly-!kN7LsRX1BgkITDF9GldDN@!_Yx*#KSq zkP@lv!OF>pB6eXQ2y3JiRCI1vC;)L1jQKY!UFoE|~0j^n^l zJpQ&0dkTiul9>Y5x`R`cVOXcB`T<49K(*dYj?oj7=a&b1WV!=tSqPl=37mdfgo!jG zMiR0;OvEA{mD}E69}7rNf1E0$19oJ`Yz%&jIzeu_!?V`TkMRMLC?;+U1$E$Z{2{0V z(B#vDgk~2`kJ?1-H<^KM*y&eip>Pk$Q&h|??Fj%@@JbzP9{eG{Q+3uDzJ(Jugb^Nc zp|#Le;KC-+H|COOt0_=XlzF zcpm0YO0EEH+vX7+RLN_gVB9mmOh`L(>VLd==zy}Z=ShcQZr~ZN+)=BIC){J4E;_t< z@TpVuF@!#&Yl@F!71(#LVl+XYXKi1O4#>cvwoUDlk1UvBfCf2DBSuAxMrFrbR+0>_ z!>sOa>gq^7LXd5G?L>7}cR3Ew*B%WBBkC~eM28|*)cLB^DPnhM~ ztMk$>V&Y~>((JJ}gZ+-WVD}-pv*nYFxnV}c-zDpOOdK}qa)2<(j`3omkzj~ffQ?uw zU2Obm5)^#^E%6be&3iiQCwD%;mx)Ub1f0L$fftX8U@A~rvRQO!p|&-SkdBn*BS>c%oJN>`dTAb~q({3O7Q07Ck2sf!Xb5%R1C}pb8c+`s%w&zpIC1J4 zbv9c2rhJrpr64&#uIa#ftLdBM1I3`nsZ4vCp`H;r>B2(?S{^0z8?o^4}O^>@UM~(?YhuPR05s*2Kst>r)I zIpIJH4fyS8$@3MHu##~8p#W-RW-cl;^cv&i^wBM8X|9q;-WNP&vaKiiVZ`S-yg=DOwjurQP~7OS93!JM|k$hfXV5nUl+-8Q!+Bi2pV{ zO8r5*OV>*vwbLtYZe~WhwrX}HW?YrYcpn`HJF@mOpiIoxldTe<2%#4hI6VZb)r)~Z z1w;k;X@PB0mllZU;jg89gPmyArqoYtq?7NvZa+<8&bu3Rc~qGqcE@jRp{{U;`!BEk zh;O~ra&)h`uxHbTP)X3*h9!3`ui3!n+Q_SqC9Yuu%}G2>O--ExQ1{fSQYy>UoWNms zmU*g-4V3TPyY;hGXlP!v9T6TvQ~jrp4^ktiWSAW#6X$ior+4$mV>rE-(p$wWY>mBS zL-b@Dh2`r^p|Ca5c2Ak9uXPlv-ck!W!}ZJg&!+`Q3v@C}HO}yx@*CjgVMj$(`@cU? z_Wp`2{#d^4e!?S?;JYVGd@4vvC+4uQx@4q+o=<>$dXQ$nWT)As)+veh^pFM zAn{uC(`myUNUBNL&AzKde_WdMveM{f#!zRV4`k({vKJTAa(#fDj-9JT4F+wAf|~R@ zn1gFcqp?#!B!&I$0!3r(w=Km8{Ajb_;6%B?PJPjIM=2`o-tcIY<==7}LLQ`+r9ILg zV|mCk5A}Jf`W}mI>3#yz`KPvRsXF#|*um{~N3XCO0@Am;FV^TCQP?sWlaeblDUjvo z#ZcH?0`vv`H%AgFUV`kli)|I1hW$P)(JkmG#(7VPi!fe)N5KiyIll(JU2B2dYX)m= zLL(jfdOU@hV`a$xqnuh}up`kvRz{7zle7)gW>S@karZ9+a4#gjiLTveZw{>6^r^(r^1t_iwo6 zV{q&o}9hGYy6Zs`y`y0H{+PItG*VFdBOD&U~Mq^bqjYxnW-WEfu zO#`%&_QD^4XO31dv!vK%8K9ogDG$l5F57y8mwPuc=Gd^P=}c*+%w|81knKhC?6EPr ztzQ0erN&ta|L~wZFUS3t#yQJPPf5?G6-NRaN)HlRUx&i=BVA^0@cbD~=Ixk1vSoV|aTUACm9qd2%hg$VqjvbP{!Ve+shqgw%&@35O zCgpqijwk+VYPqKTHlwZq7g&R=F2@8Ev%Hgh#7=sux)2i%9*o@ogaweL%jzn>QlqaS z!d3N%NnDG9F?)VRMMSNu`J5n+?#t)#kk}e|D$!?iL=VgRwU(DOFrT~U@%nv^a>|Tl z{N?RLH4j2-dmfFGPaA4gk}x0H){N!?qp>pW*A*&R5T}jdm8_N8<7AXp%{$bxre?Y@ zz|%C@Y*Jx$CLEteVs!gFsSK%2M|#$f*yezuB2S`uQLfrK&UZ|JhU}I}_&!ta{`x1e z#c-Rz@f$?TUltqf=J@3>5}x}?>dx8UVd~*R51#pdv-N)dSzS(r9uqKBzrP|p@zX1X zz8BjKIMySw?YUrlpx}IsC{`H!aA*tVC};$p3OL7DMbaSMC8|*l@C!C9uaZ&Oh_Zpi?+aq|={^0T$#P#4H2pG)!(-d7 zdg|FwJx`KtPZDFl){6278LInDUWDfYWOzEVBn7XFQ#rz~y7t#XToO8#Ds3yLG*!2v zAEwOZh$X&1dzygxU;3UGx8`{lBf{xgOloyYn>DQZVism}Zj>6=Jlkknq~;CvuFYff z58mewj7UeCLS5j}(=)ekk4qhh6h{XsKW`!acqq9#*xWM^RVwSP3()<_To z2t0zQEvfKh$V!#72ST3>{qlNIoGDGgJz*I<>7v}x$!r2!J|Upy+mOPj5Izzo;T}CI zRz9k53(fVZ1k^D5j|-@2>o6DQ#^i_#+hg|?)jl9Z z)OLu8gN^_nlsT8WlJzz!;umhD_-ShrzE_EPVU5OmF+{&?!>R>u!0q|9Oe9AHyS!>5e$D8?&h`XJ=whu7bgjA8HYf zt_1%&5S7)>J{FPQbYkuxw)&d*mZX91#Ems*iO{@E>1?TWkul>Tq??AZhI{(C@!AanN6FVSwcG|ATJGKu6TWzDhN1@6&_ z-rY#j*|^OMSwGKB2VvDR!wqq3m!;^QHY(q4wM?W0WxYQGB=+CCr5gZT=05X^gjZLE zh?2-j3r^SV+IiT5*~}~LcP5?t4&Y*IY)Q4mS~0Amh&wI>QdFAdooKJ5Eo2^myQ=i0 zE5(>iqkD@yuMEN*I1W?t(dnpfmiKqteGAghqYnBD;(2NH`wqBqVZ{c+GnDxZH%fOW zq4>!ze zlwTi@FieC9hw$C9RVR}| zc*@8Sd*}PvXP=yi$S9a-}#9#LH2Vou-s+M5~Besl^GA1206xZFmaWepXli(iHPs>BUj0>j*vbWZlmNgX9Qn%@yx^rd@UDJ z0%_^*ZSapA z+Hzj{(Qj72(~{9JAJ+i=S-QA`={c;(c2@IlYy7b?9r~BWYkuV$F`Ay&B0!!^>8;IO z-+n#B^>syGh8=WjRnNP!n6H%0Kvv0}isHwOqt+#bn5jP-^qL==nDpDCViul%CHcJ# zrNQwu5LKn;>u+_e6Gj;bi+10dJ6!RYw_z`hc-QAv8Bts)>)YL4n!JdGeR=wmPmHUmpe1_Yp2hYL37AS-()jfDH9e7k1 z*vFOlq598HKB8OEmPWXXh%sIwT_VW?xMH-gl?ERU1WMUW*-)wNi=fPk~JUh0##LtO-c zeZI=VPtS<@1gTuB{2^>{&Wao|duTPgN}O2{dX(j{iIlNcecboa0d<*(%ZJ%;E;!Oc z1;xR~k|gC$ar2BA=(UV?jm$yrAi{Ky@IlVVk5IEBw#5I-ygv_MQqV0)5iey1wn%V& zv$OA27FgXcM6s^Gtgiyag}VwY={V1H1Gopx_%g*K%Au0Q$M{iiG0MPU`M?hk{-Jw` zcV@qlJ8GA412rcB?FMooL#V~@F<*;5u8CFgWj_N#kR?9Z2w!eBY9HI9GfQYDS}6i4 z-motqX0m7_Er^1_tnPLDw``$Q!|!*%Qg&l}F>JV>kvFCy4oq5X@89<&t@#td4k%P; z7|~)_SOq9oq-Zm8KFev}g5doUSJ@%I{^|ORN1hxVdhllmSwZ>G#;#>kFx(j@yUbS^ zzwhjkC$;wpG2ytrTt}!r5WL4W+o!ju^c)HVUrUqLA4t|KA41$}p$4<|i}ILhSZpUQD4hv+(1r6egACfZ9yB;9OxS{yN9w6+|-GB`yq)p1vYsrg_L!9V)mSjYz-4mK%(^WmXMo zWY9XDmmJ3?E5cAjI8OG%G--~y^-Tn44EEUr`hc>wp$h0+rdA0u^yPy0&c3Y)v^{*N zKB+?OIsW)Zv6}YdDfeC%8UF(;GOZ*-!xjR}2?wX!o#JBMsFPcdOpk_4B|ZTcBiosq zNqA=(H%*l>@&N#9;5o&TYkMGid^0upSm7je78~pHjE{CTEJ?6k`RIkq&VX948E1Ic zWAf=lH{U}t+~($TYz$B)PT!s0&ayj4JcBDMYge*uyMO1QBow&*1pj)aRTVt(pRpBT zeEbk%OIDUnF&{&d(tehfCJp1_fI_~?)8MV*qvVyH6bHc?)}h_boC7|%la1WPT75M2 z%cp02XP<6*#ne0rWS$t5*!&;0WP`dp+CD#9LxkG;Ov)69dMs~Iw5m%~3oz~w;TO0E z{QMbGA$#)Jp|0sc@kpe_aTpkSrTkE%H$iL_?la+Z;1e{_w|itRog4IFjQt3Ns%;E* zJEf%Ahu@pmjXnm4)NB>7OfDUnWJG0kFcF?c3L3NO1na%@^62M*DM(gH!_3TJs;Ano zhpubF|D-Qes>Jy4Lz}(7XXaM88|2noNuT-FTTi4T$0HQdS4HE`u{<1rPSy0J5nGxa z`tHCD!Pj$#rgmhna<-qPSPqRXEsWgq_O{vGKb|UfMkT{c@t=EI;@!c8Ld%@@Qk>Zp zo(9Z^p2&1ou*%#B;6mK`h9tk20P^+8E?NWxFFd=h!&+5O~c4(Iq(KX7nlo8!T@s{B6xJ#eq$Nvk_bzc-44)M@C%_NDZ zW%-Z)LTuF~v8a<#44G-L+i6G%$>`>PQJ{e(E136dqMdPix-9iPUf|W4*_lBBQ<21> z#5)ji|DwPIT5fsN_;sBuyAqXov*zM#Z35`R@vpr+FV%EFBF-n_&uT z3e5A^3Bw)dV`^kT8T#n}anRk8sx+JkCN7Ki5FM?y57#E9{VHt+RM?oi>uQ8*e%z|fQ#F<^N!z1N!hSrG>>|D%+K@l zfA&*KY_yX=M%w=2eZ%@}U=&9yEAZkUol0fX&xJv6+SU$^U~+ z0<~U%M~KJ5j&WO=018Q%+iqZOVv+}pFB6ZmrOP$;QfBc(sCGd7r|)+ zD!Smd_qS#C`Ncco&qT~Q{8mYDN)XMIonaJ699Iq2>2X`QgPmSqK_9Ma@VFtc9gVY< zY}LBPj!qZZ3$IQpQBAfyCK=96(GE2M1GM`}c=}Z$(j4(o5fSAf=5a6A0>@viOnPrP z#1SA)07B}Q%#sr-wjL2W7e$o)nH{XQ?ak$geR=YR$CSd4$HVhrE$My=cBm|Np4aaN ztvdsPi#FZ&8Z{Z(3ENIeb!)8UU+1;J#pL&1Tmg$<0W|Z9zAb4KR#qRyi;~}n> zmA>O@-d}-ab>~kHU^J^rbV4o^gFWskN}%rW(jZ%)vE#;>jHRI(riZZBF`s0zWt{5f z#Fy8rS!1maQ!HQoqgNl5D{m;KURk^zvo+q?TVMHtg0~fd%g9(&c+G0|uRAt5&>n(z zj6VF`)M)54D{d#8%4^i(DZBXPaj$^=BNpKoEn~QT?Q{P9H#vj_DAAENPx2?o)Zvftxnyc0wk1$6fU0 zpOinH{2!tZ3p&}%UA3R(gsDG3J}&S1hE#n-*F#8CwLNUS#X6bY&w{J;Jp#Ho+asSl zmRQ%gyTr{~RXrBHbJ9JH^(Bw_1O+L9mp)M!DxZflj_(|mpwpw4&Rml`QrZZu9KgR5 zL5$VGBj}mGl+HAuH9#7RvA}0|oi$`GB2B029EqoCF@|=bZ(@uQfy*~Y5#Kj~hQ9p= zR~lA0G&I>VAF@C~QGVK}au@Vg#JO)N>FVX{vW6WjY8dVlqAPO1h-8u&;6ncH%v}kl z;FaAN7Z5&bGOg@J^}y+&M$wwL$5FfOX8L-rZoWA1^&i{6=INqeW=$4M91MAWe+ezKb5?C_(>8w$(NyI1~(qV2=K3I3W}6`p%h7j-b64#iw05kvnOk z)o*ce^@pagH{2HC;m${_*qx)(Mea7%}NZ1s#XxA}<(k2(3@ z|6sretu0~@is%GrU>}knTm%JB;$0-v@W%**&#VSDY6_i?8)3N`k(Awsi)10`b_wBx zB#3QN5_^6-dCoqqwuD0aqw@P3a4>P$-ji z-n2#(=yZ<2hemzi!$2nP3*35{NM`cYouO=`<$C9Ri;C*=|0(QtVtdnt+}N(B^eCb} z)p7)yw?`c?Auk!Wc)Cgdn|uVtJRr8FyXsou6$hyKPVV0`+@Uy8Y2DIyalmvvAqJ87H>+s$=}+Dhl${I+3ZVrJd(wsA^O` znPmR453ulMv*7Lo>DS)NSTc)Ijp)kwXrMZ6@JIc&78=H=3flP-h*M@&*=X7MKySyg zeiB=9_99KKMxp~(<6zZlQ1nh{8{YtSYUQ&+J~tg*u<>I zNl|c(jn)GmD%$}YQAuac8FZZ7JM^frDqzeQl7!7!*>4^)E6#|qsyxBe#| zAt^FDjUzeoH1`UIk?Uhw$U(!MM9BJ4$6@{D^g~MRH8+2KIPiAiM&`DYaqoHAf`%fO zU5r;A?Ct}-Vf)q<8Z1#<4b2xmX;B9G+41<7#fFXEqz*UP@Vr#`ZT9)=ERVPIL#k;e z;JHUuVTidL(Wjw|Se9h6)fWC`nO6 zdjS&koH{!Z&YwHX&2##Ck{`6Rx4m3}ts61M%}_GgMno2IC2y`im&wb-gg0r)>~o4B z4tLx`blq>jKw0JcMOPblV3>#G@~!)@e)bVOG?8Zx3@yQiTldgNHC0^Uhz6Y#0r5b3 zAy^TU%f%=ue)y2c%`o1@usocmuQ;GA9#Kacb;qkO5P?utvlYEG-N#f?M7Ryld3T&_x7T}MQ?q)(8@J_epvBFug&u#gF+jXQKw|bl++xXt_|q`~ z0ZB=b#Dw=Tzt8`MSxXV*8+X~`$Cw$*2C3)zCxIu@Zh(ZdI{gki=HwG;6lBm_J^d^1 z^`i@2aMaxQZj%Ai&&NFvZ#FUAjz1s=#l2T#<9nb>eeCs~LJyuZN;}Pj=4(rmSo~Un zS@EIian`7mGp+vN`@R9w)^lMA=?+kVfMgY~sI0k^pk7Ik1qH{}tw<0+Nk<^LDAH>O z$Tg>s@i0kt`>!L#Y95Z9Kfc>b#Y-E9M|Yu*09R7pyW)G*ME*FJ)qt6e>W>XN**mnxAt#;p4*+u z?X%u(j;ito^1oxyNP^=%_krg%OqmWzP5so9tEbNOIbB2O!xak}p*AhP&S>00ptI?# zHiO{UXwMYD9}E|`t;smVV)YL-mT;kshIFB|@N9=$rd5+mD69Xis|=f=DV84y=u}C7 z*`6-Cv0Ub9EoL|uS~0zqc@{tEAx-Bmp>gbud*7Z?AFA(aFHhEdHJnO;U&)fz1mnkvEUP9e|+Llb~CnmUZBjKilIAY-kaiP~9 zpIw$<=5TJP1z_HsDs^r^*Mc5&9U?)|gE!96-*}im7kj;W{Efk*zg=AWl&T}_F;nLVtuH*zeCGQBg&lwXP zIDJU!fHN%@vp$O7VQRT_LC;vyff`px$=G^}0&^K`8vt4;-yf~>xpB?5m}N4jQd1)Q zQC7Q|T2R3Xs=NK0JK%i{PMCImxo~LgE&}@fgV@>U`{~7ZQ;=HdzGr$~{+}df{dnDd zS8tzrV{62Dk9ybM`0LVVb74$}Bz_FPWEO8`;Seb4L=-t3TMoIWR3tZeq5c+Q)YL0b z#Qg*5Ewr{9_FbzbFZPJCpUAv=pDz-q?dfqaMc+k$vt)$3o58k*CFYtBd3)WgS<)vl zih@&`^vvehq84>gFse3H1P}oNu+m^L3hYs=GDF&n;k)z0r4g8Q^*m^;??CWe1Wl*U ze7`K2;qh73PA4snBDc>w|UW);Gh3q#R?kiu1cVfQl}1;_)c2cWDKmP=bM1HJx%Gw7W7X% z(u})&{oS>H1oIUjOc-z||4;6h=ecbV_&pkzCe)F%=JwT^$H(j}>?mFfmu*QDV#|Db z<=qJNfl6oGD8mY#f>?4@?tcbEG`%nq$8%I)m}71dfWnmtUy3#Lq0CZWviyV2A)@btO;d#kvOOif<&y_n znE4!2mLLr4*8%r=;Yx#H{R2B+JlQM%IG+UXk^{2tulGLE&!bEht*jN*!?Z>Fov_pC z0x+mm3l^8Zk;iPhHoO;vt5FS72T$It5rp8%&2Oh^36og+H66SoZd~UBjHZr0gWDGL zk;A>IdDruA;$&_xY@VbQt^8jAz5zl0cAHqs63m&COsoL5d5jZS7qJdcUkXS=uIf=~-Z0!&Lix$BY=VA(8 zFfG%oBCWm-hr%WWu>r)&8v{45L#MEwN$(*5Rxg4n&czhIKBJ;7xTD#C9`w$`!*alm zjexE532aqWp1?LCVEL`a^Zh2aK)}Y)R`ChsAt-1m80dft@0Fs3JC(rlz}m!)#fE_Q zuoT$RKwz=Nh6CFY<_R(ZtEOd&k=9U@jJ9^@xPSvTttGI5t>3(PbE(Cv0jyJEm)bGG z5GZV+Bvw9~40AEXCy)zWFfFqSHNqqV8=$Zjv6DA%uD1o20-Nj!@TrTrIYR7c(>kJC37Y;0^+8XI=_x0lRtgJ7bc7os`KF1So7nR!fi(SP>5t=VFGY{zLxS z9{Pi)`tq%9+xGkZ{(sm%Wc8Z6dWWj|-W9*5#krWFsJ|OktRbpmq%{=vnIf)3RK+HO zp{aj4BWXGPRh)|%qWZ_b{ik9Tygv1*k<&!*H-Gat{~vb!xM1U~u@V3P002ovPDHLk FV1kYTOqu`y diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg b/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg index 4d269ed..e26abf3 100644 --- a/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg +++ b/composeApp/src/commonMain/composeResources/drawable/svg/desert.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg index 398ce0f..16634a0 100644 --- a/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg +++ b/composeApp/src/commonMain/composeResources/drawable/svg/mountain.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/ures.png b/composeApp/src/commonMain/composeResources/drawable/ures.png deleted file mode 100644 index b44be7a07ea6adab96141a7ac1b04d3134df2e5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13240 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\n") + builder.append("\n") + builder.append(desertSVG) + builder.append("\n") + builder.append("\n") + builder.append(mountainSVG) + builder.append("\n") + builder.append("\n") + return builder.toString() + } + private fun getOffsetVector(coordinate: String): Vector2D { val letterOffset = coordinate.lowercase()[0].code - 'a'.code val numberOffset = coordinate.substring(1).toInt() - 1 @@ -88,6 +112,20 @@ object MapDrawer { return "\n" } + private val backgroundOffset = + hexagonVertices[3] - hexagonVertices[2] + (hexagonVertices[2] - hexagonVertices[1]) / 2 + + private fun getBackgroundSvg( + desert: Boolean, + offset: Vector2D, + ): String { + val fullOffset = offset + backgroundOffset + if (desert) { + return "\n" + } + return "\n" + } + fun getDesertSvg(desert: Desert): String { var minX = 0.0 var maxX = 0.0 @@ -102,29 +140,39 @@ object MapDrawer { minY = minOf(minY, offset.y) maxY = maxOf(maxY, offset.y) - val hex = getHexagonSvg(offset) - val text = getFieldTextSvg(field.id, offset) - val weaponBox = getWeaponBoxSvg(offset) - val coordinateCircle = getCoordinateCircleSvg(offset) - val waterBox = getWaterBoxSvg(offset) - val spiceBox = getSpiceBoxSvg(offset) + val fieldBuilder = StringBuilder() + + fieldBuilder.append(getBackgroundSvg(field.water < 0, offset)) + + fieldBuilder.append(getHexagonSvg(offset)) + fieldBuilder.append(getFieldTextSvg(field.id, offset)) + fieldBuilder.append(getWeaponBoxSvg(offset)) + fieldBuilder.append(getCoordinateCircleSvg(offset)) + fieldBuilder.append(getWaterBoxSvg(offset)) + fieldBuilder.append(getSpiceBoxSvg(offset)) - hex + text + weaponBox + coordinateCircle + waterBox + spiceBox + fieldBuilder.toString() } val width = maxX - minX + 300 val height = maxY - minY + 300 - val viewbox = "viewbox = \"${minX - 150} ${minY - 150} $width $height\"" + val viewbox = "viewBox=\"${minX - 150} ${minY - 150} $width $height\"" val svgOpening = "\n" + + val definitions = getDefinitions() + val svgEnd = "" - return svgOpening + fieldSvgs.joinToString(separator = "") { it } + svgEnd + return svgOpening + definitions + fieldSvgs.joinToString(separator = "") { it } + svgEnd } } -fun main() { +suspend fun main() { val desert = Desert.create12PlayerHexagon() + + MapDrawer.loadResources() + println(MapDrawer.getDesertSvg(desert)) } From 8b4b31e44c149da813b4519b1c2ac7a5f55ec243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 29 Mar 2026 20:10:48 +0200 Subject: [PATCH 118/162] Water and spice text --- .../it/dunestrat/feature/drawmap/MapDrawer.kt | 50 ++++++++++++++++--- .../mokegyesulet/it/dunestrat/model/Desert.kt | 2 +- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt index 3d0af8f..4c203a4 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt @@ -9,7 +9,9 @@ object MapDrawer { private const val HEXAGON_SIZE = 200.0 private const val INNER_HEXAGON_RATIO = 0.65 - private const val STYLE = "style=\"fill:none;stroke:#000000;stroke-width:2.5px\"" + private const val POLYGON_STYLE = "style=\"fill:none;stroke:#000000;stroke-width:2.5px\"" + private const val BOX_STYLE = + "style=\"fill:#DDDDDD;fill-opacity:0.8;stroke:#000000;stroke-width:2.5px\"" private val distanceToVertex = Vector2D(HEXAGON_SIZE / 2, 0.0) @@ -70,7 +72,13 @@ object MapDrawer { private fun polygonSvgFromPoints(points: List): String { val pointsString = points.joinToString(separator = " ") { "${it.x},${it.y}" } - return "\n" + return "\n" + } + + private fun boxSvgFromPoints(points: List): String { + val pointsString = points.joinToString(separator = " ") { "${it.x},${it.y}" } + val style = "style=\"fill:#DDDDDD;fill-opacity:0.8;stroke:#000000;stroke-width:2.5px\"" + return "\n" } private fun getWeaponBoxSvg(offset: Vector2D): String { @@ -80,7 +88,7 @@ object MapDrawer { innerHexagonVertices[2], hexagonVertices[2], ).map { it + offset } - return polygonSvgFromPoints(points) + return boxSvgFromPoints(points) } private fun getWaterBoxSvg(offset: Vector2D): String { @@ -90,7 +98,7 @@ object MapDrawer { innerHexagonVertices[3], hexagonVertices[3], ).map { it + offset } - return polygonSvgFromPoints(points) + return boxSvgFromPoints(points) } private fun getSpiceBoxSvg(offset: Vector2D): String { @@ -100,7 +108,7 @@ object MapDrawer { innerHexagonVertices[1], hexagonVertices[1], ).map { it + offset } - return polygonSvgFromPoints(points) + return boxSvgFromPoints(points) } private fun getCoordinateCircleSvg(offset: Vector2D): String { @@ -109,7 +117,7 @@ object MapDrawer { val d = "M ${center.x - radius} ${center.y} " + "A $radius $radius 0 0 0 " + "${center.x + radius} ${center.y}" - return "\n" + return "\n" } private val backgroundOffset = @@ -126,6 +134,32 @@ object MapDrawer { return "\n" } + private fun getWaterTextSvg( + value: Int, + offset: Vector2D, + ): String { + val center = + ((hexagonVertices[2] + hexagonVertices[3]) / 2) * (1 + INNER_HEXAGON_RATIO) / 2 + offset + val position = "x=\"${center.x}\" y=\"${center.y}\"" + val font = "font-size=\"20\" font-family=\"Times New Roman\"" + val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" + val rotate = "transform=\"rotate(60,${center.x},${center.y})\"" + return "$value\n" + } + + private fun getSpiceTextSvg( + value: Int, + offset: Vector2D, + ): String { + val center = + (hexagonVertices[0] + hexagonVertices[1]) / 2 * (1 + INNER_HEXAGON_RATIO) / 2 + offset + val position = "x=\"${center.x}\" y=\"${center.y}\"" + val font = "font-size=\"20\" font-family=\"Times New Roman\"" + val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" + val transform = "transform=\"rotate(-60,${center.x},${center.y})\"" + return "$value\n" + } + fun getDesertSvg(desert: Desert): String { var minX = 0.0 var maxX = 0.0 @@ -145,11 +179,13 @@ object MapDrawer { fieldBuilder.append(getBackgroundSvg(field.water < 0, offset)) fieldBuilder.append(getHexagonSvg(offset)) - fieldBuilder.append(getFieldTextSvg(field.id, offset)) fieldBuilder.append(getWeaponBoxSvg(offset)) fieldBuilder.append(getCoordinateCircleSvg(offset)) + fieldBuilder.append(getFieldTextSvg(field.id, offset)) fieldBuilder.append(getWaterBoxSvg(offset)) fieldBuilder.append(getSpiceBoxSvg(offset)) + fieldBuilder.append(getWaterTextSvg(field.water, offset)) + fieldBuilder.append(getSpiceTextSvg(field.spice, offset)) fieldBuilder.toString() } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt index 39faa32..f07a39c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/model/Desert.kt @@ -216,7 +216,7 @@ data class Desert( "A3", "A7", "C1", - "C10", + "C11", "G1", "G15", "K1", From 82ec77af269c26e7d7153a7c0f1017e836edc16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 15:12:22 +0100 Subject: [PATCH 119/162] WIP weapon icons --- .../it/dunestrat/feature/drawmap/MapDrawer.kt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt index 4c203a4..e17b4b2 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.feature.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.math.PI object MapDrawer { @@ -27,22 +28,47 @@ object MapDrawer { private lateinit var desertSVG: String private lateinit var mountainSVG: String + private lateinit var pistolSVG: String + private lateinit var lasgunSVG: String + private lateinit var crysknifeSVG: String + suspend fun loadResources() { val desertBytes = Res.readBytes("drawable/svg/desert.svg") desertSVG = desertBytes.decodeToString().replace("cls-", "desert-cls-") val mountainBytes = Res.readBytes("drawable/svg/mountain.svg") mountainSVG = mountainBytes.decodeToString().replace("cls-", "mountain-cls-") + val pistolBytes = Res.readBytes("drawable/svg/pistol.svg") + pistolSVG = pistolBytes.decodeToString().replace("cls-", "pistol-cls-") + val lasgunBytes = Res.readBytes("drawable/svg/lasgun.svg") + lasgunSVG = lasgunBytes.decodeToString().replace("cls-", "lasgun-cls-") + val crysknifeBytes = Res.readBytes("drawable/svg/crysknife.svg") + crysknifeSVG = crysknifeBytes.decodeToString().replace("cls-", "crysknife-cls-") } private fun getDefinitions(): String { val builder = StringBuilder() builder.append("\n") + builder.append("\n") builder.append(desertSVG) builder.append("\n") + builder.append("\n") builder.append(mountainSVG) builder.append("\n") + + builder.append("\n") + builder.append(pistolSVG) + builder.append("\n") + + builder.append("\n") + builder.append(lasgunSVG) + builder.append("\n") + + builder.append("\n") + builder.append(crysknifeSVG) + builder.append("\n") + builder.append("\n") return builder.toString() } @@ -160,6 +186,32 @@ object MapDrawer { return "$value\n" } + private val weaponOffset = Vector2D(innerHexagonVertices[2].x, hexagonVertices[2].y) + + private fun getWeaponSvg( + weapon: Weapon, + offset: Vector2D, + ): String { + val totalOffset = offset + return when (weapon) { + Weapon.PISTOL -> { + "\n" + } + + Weapon.LASGUN -> { + "\n" + } + + Weapon.CRYSKNIFE -> { + "\n" + } + + Weapon.LEGION -> { + "" + } + } + } + fun getDesertSvg(desert: Desert): String { var minX = 0.0 var maxX = 0.0 @@ -186,6 +238,7 @@ object MapDrawer { fieldBuilder.append(getSpiceBoxSvg(offset)) fieldBuilder.append(getWaterTextSvg(field.water, offset)) fieldBuilder.append(getSpiceTextSvg(field.spice, offset)) + fieldBuilder.append(getWeaponSvg(field.effectiveWeapon, offset)) fieldBuilder.toString() } From e2ae2df63ab707e8f002c6ce56f7961447af581a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 20:36:49 +0100 Subject: [PATCH 120/162] Move to util package --- .../it/dunestrat/{feature => util}/drawmap/MapDrawer.kt | 2 +- .../it/dunestrat/{feature => util}/drawmap/Vector2D.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/{feature => util}/drawmap/MapDrawer.kt (99%) rename composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/{feature => util}/drawmap/Vector2D.kt (94%) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt similarity index 99% rename from composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt rename to composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index e17b4b2..178f67d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -1,4 +1,4 @@ -package hu.mokegyesulet.it.dunestrat.feature.drawmap +package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/Vector2D.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt rename to composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/Vector2D.kt index 1893773..c820d2e 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/drawmap/Vector2D.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/Vector2D.kt @@ -1,4 +1,4 @@ -package hu.mokegyesulet.it.dunestrat.feature.drawmap +package hu.mokegyesulet.it.dunestrat.util.drawmap import kotlin.math.cos import kotlin.math.sin From eed75c853b8a8bc2331a88e31e7dba58b8ef387c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 21:05:16 +0100 Subject: [PATCH 121/162] weapon icons --- .../hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index 178f67d..d3562c7 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -186,13 +186,13 @@ object MapDrawer { return "$value\n" } - private val weaponOffset = Vector2D(innerHexagonVertices[2].x, hexagonVertices[2].y) + private val weaponOffset = innerHexagonVertices[2] private fun getWeaponSvg( weapon: Weapon, offset: Vector2D, ): String { - val totalOffset = offset + val totalOffset = offset + weaponOffset return when (weapon) { Weapon.PISTOL -> { "\n" From 1c139f5436d21d6461f0467e57617d6ddd8374a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 10 May 2026 21:39:38 +0100 Subject: [PATCH 122/162] WIP spice icon --- .../it/dunestrat/util/drawmap/MapDrawer.kt | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index d3562c7..b47667f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.DesertField import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.math.PI @@ -32,6 +33,9 @@ object MapDrawer { private lateinit var lasgunSVG: String private lateinit var crysknifeSVG: String + private lateinit var waterSVG: String + private lateinit var spiceSVG: String + suspend fun loadResources() { val desertBytes = Res.readBytes("drawable/svg/desert.svg") desertSVG = desertBytes.decodeToString().replace("cls-", "desert-cls-") @@ -43,6 +47,10 @@ object MapDrawer { lasgunSVG = lasgunBytes.decodeToString().replace("cls-", "lasgun-cls-") val crysknifeBytes = Res.readBytes("drawable/svg/crysknife.svg") crysknifeSVG = crysknifeBytes.decodeToString().replace("cls-", "crysknife-cls-") + val waterBytes = Res.readBytes("drawable/svg/water.svg") + waterSVG = waterBytes.decodeToString().replace("cls-", "water-cls-") + val spiceBytes = Res.readBytes("drawable/svg/spice.svg") + spiceSVG = spiceBytes.decodeToString().replace("cls-", "spice-cls-") } private fun getDefinitions(): String { @@ -69,6 +77,14 @@ object MapDrawer { builder.append(crysknifeSVG) builder.append("\n") + builder.append("\n") + builder.append(waterSVG) + builder.append("\n") + + builder.append("\n") + builder.append(spiceSVG) + builder.append("\n") + builder.append("\n") return builder.toString() } @@ -173,17 +189,24 @@ object MapDrawer { return "$value\n" } - private fun getSpiceTextSvg( + private fun getSpiceSvg( value: Int, offset: Vector2D, ): String { - val center = - (hexagonVertices[0] + hexagonVertices[1]) / 2 * (1 + INNER_HEXAGON_RATIO) / 2 + offset - val position = "x=\"${center.x}\" y=\"${center.y}\"" + val totalOffset = offset + innerHexagonVertices[1] + val position = "x=\"${totalOffset.x}\" y=\"${totalOffset.y}\"" val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" - val transform = "transform=\"rotate(-60,${center.x},${center.y})\"" - return "$value\n" + val transform = "transform=\"rotate(-60,${totalOffset.x},${totalOffset.y})\"" + + val svg = StringBuilder() + + svg.append("\n") + svg.append("\n") + svg.append("$value\n") + svg.append("\n") + + return svg.toString() } private val weaponOffset = innerHexagonVertices[2] @@ -237,7 +260,7 @@ object MapDrawer { fieldBuilder.append(getWaterBoxSvg(offset)) fieldBuilder.append(getSpiceBoxSvg(offset)) fieldBuilder.append(getWaterTextSvg(field.water, offset)) - fieldBuilder.append(getSpiceTextSvg(field.spice, offset)) + fieldBuilder.append(getSpiceSvg(field.spice, offset)) fieldBuilder.append(getWeaponSvg(field.effectiveWeapon, offset)) fieldBuilder.toString() @@ -259,7 +282,18 @@ object MapDrawer { } suspend fun main() { - val desert = Desert.create12PlayerHexagon() + val desert = Desert( + fields = setOf( + DesertField( + id = "A1", + water = -3, + spice = 11, + effectiveWeapon = Weapon.PISTOL, + startingField = false, + neighbours = mutableSetOf(), + ), + ), + ) MapDrawer.loadResources() From 8f95e9c2fe0e434008d398d6555c496bae1f87ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 14 Jun 2026 20:05:07 +0200 Subject: [PATCH 123/162] =?UTF-8?q?Ikonok=20helyesen=20forgatva,=20de=20m?= =?UTF-8?q?=C3=A9g=20nem=20j=C3=B3=20helyen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/util/drawmap/MapDrawer.kt | 55 +++++++------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index b47667f..de16dae 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -2,7 +2,6 @@ package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.model.DesertField import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.math.PI @@ -77,14 +76,6 @@ object MapDrawer { builder.append(crysknifeSVG) builder.append("\n") - builder.append("\n") - builder.append(waterSVG) - builder.append("\n") - - builder.append("\n") - builder.append(spiceSVG) - builder.append("\n") - builder.append("\n") return builder.toString() } @@ -176,36 +167,43 @@ object MapDrawer { return "\n" } - private fun getWaterTextSvg( + private fun getWaterSvg( value: Int, offset: Vector2D, ): String { - val center = - ((hexagonVertices[2] + hexagonVertices[3]) / 2) * (1 + INNER_HEXAGON_RATIO) / 2 + offset - val position = "x=\"${center.x}\" y=\"${center.y}\"" + val totalOffset = offset + innerHexagonVertices[4] + Vector2D(-11.0, 17.0) val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" - val rotate = "transform=\"rotate(60,${center.x},${center.y})\"" - return "$value\n" + val transform = "transform=\"rotate(60,${totalOffset.x},${totalOffset.y})\"" + + val svg = StringBuilder() + svg.append("\n") + svg.append("\n") + svg.append(waterSVG) + svg.append("\n") + svg.append("$value\n") + svg.append("\n") + + return svg.toString() } private fun getSpiceSvg( value: Int, offset: Vector2D, ): String { - val totalOffset = offset + innerHexagonVertices[1] - val position = "x=\"${totalOffset.x}\" y=\"${totalOffset.y}\"" + val totalOffset = offset + innerHexagonVertices[0] + Vector2D(2.0, 0.0) val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" val transform = "transform=\"rotate(-60,${totalOffset.x},${totalOffset.y})\"" val svg = StringBuilder() - svg.append("\n") - svg.append("\n") - svg.append("$value\n") + svg.append("\n") + svg.append("\n") + svg.append(spiceSVG) + svg.append("\n") + svg.append("$value\n") svg.append("\n") - return svg.toString() } @@ -259,7 +257,7 @@ object MapDrawer { fieldBuilder.append(getFieldTextSvg(field.id, offset)) fieldBuilder.append(getWaterBoxSvg(offset)) fieldBuilder.append(getSpiceBoxSvg(offset)) - fieldBuilder.append(getWaterTextSvg(field.water, offset)) + fieldBuilder.append(getWaterSvg(field.water, offset)) fieldBuilder.append(getSpiceSvg(field.spice, offset)) fieldBuilder.append(getWeaponSvg(field.effectiveWeapon, offset)) @@ -282,18 +280,7 @@ object MapDrawer { } suspend fun main() { - val desert = Desert( - fields = setOf( - DesertField( - id = "A1", - water = -3, - spice = 11, - effectiveWeapon = Weapon.PISTOL, - startingField = false, - neighbours = mutableSetOf(), - ), - ), - ) + val desert = Desert.create12PlayerHexagon() MapDrawer.loadResources() From d37040d6f9c47f55305854b170ea6d6d56f0772a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 19 Jun 2026 12:41:08 +0200 Subject: [PATCH 124/162] =?UTF-8?q?t=C3=A9rk=C3=A9p=20gener=C3=A1l=C3=A1s?= =?UTF-8?q?=20m=C5=B1k=C3=B6dik,=20csapat=20jel=C3=B6l=C5=91ket=20k=C3=A9n?= =?UTF-8?q?e=20m=C3=A9g=20felrakni?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/util/drawmap/MapDrawer.kt | 147 +++++++++++++++--- 1 file changed, 126 insertions(+), 21 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index de16dae..10c1074 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -2,6 +2,7 @@ package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert +import hu.mokegyesulet.it.dunestrat.model.GameState import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.math.PI @@ -35,21 +36,35 @@ object MapDrawer { private lateinit var waterSVG: String private lateinit var spiceSVG: String - suspend fun loadResources() { - val desertBytes = Res.readBytes("drawable/svg/desert.svg") - desertSVG = desertBytes.decodeToString().replace("cls-", "desert-cls-") - val mountainBytes = Res.readBytes("drawable/svg/mountain.svg") - mountainSVG = mountainBytes.decodeToString().replace("cls-", "mountain-cls-") + suspend fun loadResources(blackAndWhite: Boolean = false) { + val desertBytes = if (blackAndWhite) { + Res.readBytes("drawable/svg/desert_bnw.svg") + } else { + Res.readBytes("drawable/svg/desert.svg") + } + desertSVG = desertBytes.decodeToString().replace("cls-", "desert-cls-") + "\n" + + val mountainBytes = if (blackAndWhite) { + Res.readBytes("drawable/svg/mountain_bnw.svg") + } else { + Res.readBytes("drawable/svg/mountain.svg") + } + mountainSVG = mountainBytes.decodeToString().replace("cls-", "mountain-cls-") + "\n" + val pistolBytes = Res.readBytes("drawable/svg/pistol.svg") - pistolSVG = pistolBytes.decodeToString().replace("cls-", "pistol-cls-") + pistolSVG = pistolBytes.decodeToString().replace("cls-", "pistol-cls-") + "\n" + val lasgunBytes = Res.readBytes("drawable/svg/lasgun.svg") - lasgunSVG = lasgunBytes.decodeToString().replace("cls-", "lasgun-cls-") + lasgunSVG = lasgunBytes.decodeToString().replace("cls-", "lasgun-cls-") + "\n" + val crysknifeBytes = Res.readBytes("drawable/svg/crysknife.svg") - crysknifeSVG = crysknifeBytes.decodeToString().replace("cls-", "crysknife-cls-") + crysknifeSVG = crysknifeBytes.decodeToString().replace("cls-", "crysknife-cls-") + "\n" + val waterBytes = Res.readBytes("drawable/svg/water.svg") - waterSVG = waterBytes.decodeToString().replace("cls-", "water-cls-") + waterSVG = waterBytes.decodeToString().replace("cls-", "water-cls-") + "\n" + val spiceBytes = Res.readBytes("drawable/svg/spice.svg") - spiceSVG = spiceBytes.decodeToString().replace("cls-", "spice-cls-") + spiceSVG = spiceBytes.decodeToString().replace("cls-", "spice-cls-") + "\n" } private fun getDefinitions(): String { @@ -171,17 +186,22 @@ object MapDrawer { value: Int, offset: Vector2D, ): String { - val totalOffset = offset + innerHexagonVertices[4] + Vector2D(-11.0, 17.0) + val midpoint = (hexagonVertices[1] + innerHexagonVertices[2]) / 2 + val totalOffset = offset + midpoint + val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" - val transform = "transform=\"rotate(60,${totalOffset.x},${totalOffset.y})\"" + + val rotate = "rotate(60,${offset.x},${offset.y})" + val translate = "translate(${totalOffset.x}, ${totalOffset.y})" + val transform = "transform=\"$rotate $translate\"" val svg = StringBuilder() svg.append("\n") - svg.append("\n") + svg.append("\n") svg.append(waterSVG) svg.append("\n") - svg.append("$value\n") + svg.append("$value\n") svg.append("\n") return svg.toString() @@ -191,19 +211,22 @@ object MapDrawer { value: Int, offset: Vector2D, ): String { - val totalOffset = offset + innerHexagonVertices[0] + Vector2D(2.0, 0.0) + val midpoint = (hexagonVertices[1] + innerHexagonVertices[2]) / 2 + val totalOffset = offset + midpoint + val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" - val transform = "transform=\"rotate(-60,${totalOffset.x},${totalOffset.y})\"" - val svg = StringBuilder() + val transform = "transform=\"rotate(-60,${offset.x},${offset.y}) translate(${totalOffset.x}, ${totalOffset.y})\"" + val svg = StringBuilder() svg.append("\n") - svg.append("\n") + svg.append("\n") svg.append(spiceSVG) svg.append("\n") - svg.append("$value\n") + svg.append("$value\n") svg.append("\n") + return svg.toString() } @@ -213,7 +236,7 @@ object MapDrawer { weapon: Weapon, offset: Vector2D, ): String { - val totalOffset = offset + weaponOffset + var totalOffset = offset + weaponOffset return when (weapon) { Weapon.PISTOL -> { "\n" @@ -224,6 +247,7 @@ object MapDrawer { } Weapon.CRYSKNIFE -> { + totalOffset += Vector2D(0.0, 3.0) "\n" } @@ -275,12 +299,93 @@ object MapDrawer { val definitions = getDefinitions() val svgEnd = "" - return svgOpening + definitions + fieldSvgs.joinToString(separator = "") { it } + svgEnd + return svgOpening + definitions + fieldSvgs.joinToString(separator = "") + svgEnd + } + + fun getGameStateSvg(gameState: GameState): String { + var minX = 0.0 + var maxX = 0.0 + var minY = 0.0 + var maxY = 0.0 + + val fieldSvgs = gameState.fields.map { field -> + val offset = getOffsetVector(field.id) + + minX = minOf(minX, offset.x) + maxX = maxOf(maxX, offset.x) + minY = minOf(minY, offset.y) + maxY = maxOf(maxY, offset.y) + + val fieldBuilder = StringBuilder() + + fieldBuilder.append(getBackgroundSvg(field.water < 0, offset)) + + fieldBuilder.append(getHexagonSvg(offset)) + fieldBuilder.append(getWeaponBoxSvg(offset)) + fieldBuilder.append(getCoordinateCircleSvg(offset)) + fieldBuilder.append(getFieldTextSvg(field.id, offset)) + fieldBuilder.append(getWaterBoxSvg(offset)) + fieldBuilder.append(getSpiceBoxSvg(offset)) + fieldBuilder.append(getWaterSvg(field.water, offset)) + fieldBuilder.append(getSpiceSvg(field.spice, offset)) + fieldBuilder.append(getWeaponSvg(field.effectiveWeapon, offset)) + + fieldBuilder.toString() + } + + val width = maxX - minX + 300 + val height = maxY - minY + 300 + + val viewbox = "viewBox=\"${minX - 150} ${minY - 150} $width $height\"" + + val svgOpening = "\n" + + val definitions = getDefinitions() + + val svgEnd = "" + return svgOpening + definitions + fieldSvgs.joinToString(separator = "") + svgEnd } } suspend fun main() { val desert = Desert.create12PlayerHexagon() +// val desert = Desert( +// fields = setOf( +// DesertField( +// id = "A1", +// water = -3, +// spice = 28, +// effectiveWeapon = Weapon.PISTOL, +// neighbours = mutableSetOf(), +// startingField = false, +// ), +// DesertField( +// id = "A2", +// water = 7, +// spice = 7, +// effectiveWeapon = Weapon.CRYSKNIFE, +// neighbours = mutableSetOf(), +// startingField = false, +// ), +// DesertField( +// id = "B1", +// water = -10, +// spice = 0, +// effectiveWeapon = Weapon.LASGUN, +// neighbours = mutableSetOf(), +// startingField = false, +// ), +// DesertField( +// id = "B2", +// water = 11, +// spice = 11, +// effectiveWeapon = Weapon.CRYSKNIFE, +// neighbours = mutableSetOf(), +// startingField = false, +// ), +// ), +// ) MapDrawer.loadResources() From 0d72c1a43bfe41cbdebfad6748c314287bc74841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Fri, 19 Jun 2026 12:58:19 +0200 Subject: [PATCH 125/162] =?UTF-8?q?harvester=20piros=C3=ADt=C3=A1s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/util/drawmap/MapDrawer.kt | 90 ++++++++++--------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index 10c1074..f3e0752 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -3,6 +3,7 @@ package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res import hu.mokegyesulet.it.dunestrat.model.Desert import hu.mokegyesulet.it.dunestrat.model.GameState +import hu.mokegyesulet.it.dunestrat.model.GameStateField import hu.mokegyesulet.it.dunestrat.model.Weapon import kotlin.math.PI @@ -210,21 +211,25 @@ object MapDrawer { private fun getSpiceSvg( value: Int, offset: Vector2D, + isRed: Boolean = false, ): String { val midpoint = (hexagonVertices[1] + innerHexagonVertices[2]) / 2 val totalOffset = offset + midpoint val font = "font-size=\"20\" font-family=\"Times New Roman\"" val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" + val color = if (isRed) "red" else "black" - val transform = "transform=\"rotate(-60,${offset.x},${offset.y}) translate(${totalOffset.x}, ${totalOffset.y})\"" + val rotate = "rotate(-60,${offset.x},${offset.y})" + val translate = "translate(${totalOffset.x}, ${totalOffset.y})" + val transform = "transform=\"$rotate $translate\"" val svg = StringBuilder() svg.append("\n") - svg.append("\n") + svg.append("\n") svg.append(spiceSVG) svg.append("\n") - svg.append("$value\n") + svg.append("$value\n") svg.append("\n") return svg.toString() @@ -327,7 +332,7 @@ object MapDrawer { fieldBuilder.append(getWaterBoxSvg(offset)) fieldBuilder.append(getSpiceBoxSvg(offset)) fieldBuilder.append(getWaterSvg(field.water, offset)) - fieldBuilder.append(getSpiceSvg(field.spice, offset)) + fieldBuilder.append(getSpiceSvg(field.spice, offset, field.harvester)) fieldBuilder.append(getWeaponSvg(field.effectiveWeapon, offset)) fieldBuilder.toString() @@ -349,45 +354,46 @@ object MapDrawer { } suspend fun main() { - val desert = Desert.create12PlayerHexagon() -// val desert = Desert( -// fields = setOf( -// DesertField( -// id = "A1", -// water = -3, -// spice = 28, -// effectiveWeapon = Weapon.PISTOL, -// neighbours = mutableSetOf(), -// startingField = false, -// ), -// DesertField( -// id = "A2", -// water = 7, -// spice = 7, -// effectiveWeapon = Weapon.CRYSKNIFE, -// neighbours = mutableSetOf(), -// startingField = false, -// ), -// DesertField( -// id = "B1", -// water = -10, -// spice = 0, -// effectiveWeapon = Weapon.LASGUN, -// neighbours = mutableSetOf(), -// startingField = false, -// ), -// DesertField( -// id = "B2", -// water = 11, -// spice = 11, -// effectiveWeapon = Weapon.CRYSKNIFE, -// neighbours = mutableSetOf(), -// startingField = false, -// ), -// ), -// ) +// val desert = Desert.create12PlayerHexagon() + val gameState = GameState( + fields = setOf( + GameStateField( + id = "A1", + water = -3, + spice = 28, + effectiveWeapon = Weapon.PISTOL, + neighbours = mutableSetOf(), + harvester = true, + ), + GameStateField( + id = "A2", + water = 7, + spice = 7, + effectiveWeapon = Weapon.CRYSKNIFE, + neighbours = mutableSetOf(), + harvester = false, + ), + GameStateField( + id = "B1", + water = -10, + spice = 0, + effectiveWeapon = Weapon.LASGUN, + neighbours = mutableSetOf(), + harvester = false, + ), + GameStateField( + id = "B2", + water = 11, + spice = 11, + effectiveWeapon = Weapon.CRYSKNIFE, + neighbours = mutableSetOf(), + harvester = false, + ), + ), + players = setOf(), + ) MapDrawer.loadResources() - println(MapDrawer.getDesertSvg(desert)) + println(MapDrawer.getGameStateSvg(gameState)) } From 88d79a1de7f4990313a64e958bb2db407ab66bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 20 Jun 2026 14:46:42 +0200 Subject: [PATCH 126/162] =?UTF-8?q?j=C3=A1t=C3=A9kos=20jel=C3=B6l=C5=91k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/util/drawmap/MapDrawer.kt | 169 ++++++++++++++---- 1 file changed, 132 insertions(+), 37 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index f3e0752..5820f1b 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -1,10 +1,7 @@ package hu.mokegyesulet.it.dunestrat.util.drawmap import dune_strat.composeapp.generated.resources.Res -import hu.mokegyesulet.it.dunestrat.model.Desert -import hu.mokegyesulet.it.dunestrat.model.GameState -import hu.mokegyesulet.it.dunestrat.model.GameStateField -import hu.mokegyesulet.it.dunestrat.model.Weapon +import hu.mokegyesulet.it.dunestrat.model.* import kotlin.math.PI object MapDrawer { @@ -262,6 +259,21 @@ object MapDrawer { } } + private fun getPlayerMarkerSvg( + offset: Vector2D, + text: String, + color: String = "black", + ): String { + val textPosition = "x=\"${offset.x}\" y=\"${offset.y}\"" + val circlePosition = "cx=\"${offset.x}\" cy=\"${offset.y}\"" + val align = "dominant-baseline=\"middle\" text-anchor=\"middle\"" + val font = "font-size=\"30\"" + + val circle = "\n" + val text = "$text\n" + return circle + text + } + fun getDesertSvg(desert: Desert): String { var minX = 0.0 var maxX = 0.0 @@ -307,6 +319,22 @@ object MapDrawer { return svgOpening + definitions + fieldSvgs.joinToString(separator = "") + svgEnd } + private const val GREEK_ALPHABET = "αβγδεζηθικλμνξοπρστυφχψω" + private val COLORS = listOf( + "tomato", + "steelblue", + "mediumseagreen", + "goldenrod", + "orchid", + "coral", + "slateblue", + "darkcyan", + "crimson", + "peru", + "teal", + "mediumvioletred", + ) + fun getGameStateSvg(gameState: GameState): String { var minX = 0.0 var maxX = 0.0 @@ -338,6 +366,24 @@ object MapDrawer { fieldBuilder.toString() } + val playerSvgs = gameState.players.map { player -> + val ownedFieldsSvgs = player.ownedFields.map { field -> + val offset = getOffsetVector(field.id) + getPlayerMarkerSvg( + offset, + GREEK_ALPHABET[(player.id[0] - '0')].toString(), + COLORS[ + ( + player.id[0] - + '0' + ) % + COLORS.size, + ], + ) + } + ownedFieldsSvgs.joinToString(separator = "") + } + val width = maxX - minX + 300 val height = maxY - minY + 300 @@ -349,51 +395,100 @@ object MapDrawer { val definitions = getDefinitions() val svgEnd = "" - return svgOpening + definitions + fieldSvgs.joinToString(separator = "") + svgEnd + return svgOpening + definitions + fieldSvgs.joinToString(separator = "") + + playerSvgs.joinToString(separator = "") + + svgEnd } } suspend fun main() { // val desert = Desert.create12PlayerHexagon() + + val a1 = GameStateField( + id = "A1", + water = -3, + spice = 28, + effectiveWeapon = Weapon.PISTOL, + neighbours = mutableSetOf(), + harvester = true, + ) + val a2 = GameStateField( + id = "A2", + water = 7, + spice = 7, + effectiveWeapon = Weapon.CRYSKNIFE, + neighbours = mutableSetOf(), + harvester = false, + ) + val b1 = GameStateField( + id = "B1", + water = -10, + spice = 0, + effectiveWeapon = Weapon.LASGUN, + neighbours = mutableSetOf(), + harvester = false, + ) + val b2 = GameStateField( + id = "B2", + water = 11, + spice = 11, + effectiveWeapon = Weapon.CRYSKNIFE, + neighbours = mutableSetOf(), + harvester = false, + ) + val gameState = GameState( fields = setOf( - GameStateField( - id = "A1", - water = -3, - spice = 28, - effectiveWeapon = Weapon.PISTOL, - neighbours = mutableSetOf(), - harvester = true, - ), - GameStateField( - id = "A2", - water = 7, - spice = 7, - effectiveWeapon = Weapon.CRYSKNIFE, - neighbours = mutableSetOf(), - harvester = false, - ), - GameStateField( - id = "B1", - water = -10, - spice = 0, - effectiveWeapon = Weapon.LASGUN, - neighbours = mutableSetOf(), - harvester = false, + a1, + a2, + b1, + b2, + ), + players = setOf( + Player( + id = "0", + water = 12, + spice = 12, + harvestersPurchased = 1, + weapons = mutableMapOf(), + ownedFields = mutableSetOf(a1, a2), ), - GameStateField( - id = "B2", - water = 11, - spice = 11, - effectiveWeapon = Weapon.CRYSKNIFE, - neighbours = mutableSetOf(), - harvester = false, + Player( + id = "1", + water = 12, + spice = 12, + harvestersPurchased = 1, + weapons = mutableMapOf(), + ownedFields = mutableSetOf(b1, b2), ), ), - players = setOf(), + ) + + val fields = (1..12).map { + GameStateField( + id = (('A'.code + (it - 1) / 3).toChar().toString() + ((it - 1) % 3 + 1).toString()), + 0, + 0, + Weapon.PISTOL, + false, + mutableSetOf(), + ) + } + val gameState2 = GameState( + fields = fields.toMutableSet(), + players = fields.mapIndexed { index, field -> + Player( + id = (index + '0'.code).toChar().toString(), + water = 12, + spice = 12, + harvestersPurchased = 1, + weapons = mutableMapOf(), + ownedFields = mutableSetOf(field), + ) + }.toMutableSet(), ) MapDrawer.loadResources() - println(MapDrawer.getGameStateSvg(gameState)) + println(MapDrawer.getGameStateSvg(gameState2)) } From d01d578f473fa1da0af90e08a2a92ceeecc44adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 20 Jun 2026 14:56:44 +0200 Subject: [PATCH 127/162] =?UTF-8?q?csak=20egyszer=20lehet=20er=C5=91forr?= =?UTF-8?q?=C3=A1st=20bet=C3=B6lteni?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hu/mokegyesulet/it/dunestrat/App.kt | 2 +- .../it/dunestrat/util/drawmap/MapDrawer.kt | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt index 6cfc9a9..7406d05 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/App.kt @@ -2,7 +2,7 @@ package hu.mokegyesulet.it.dunestrat import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable import hu.mokegyesulet.it.dunestrat.navigation.NavGraph import hu.mokegyesulet.it.dunestrat.ui.AppTheme import org.jetbrains.compose.ui.tooling.preview.Preview diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt index 5820f1b..c9edd24 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/util/drawmap/MapDrawer.kt @@ -34,7 +34,13 @@ object MapDrawer { private lateinit var waterSVG: String private lateinit var spiceSVG: String + private var resourcesLoaded = false + suspend fun loadResources(blackAndWhite: Boolean = false) { + if (resourcesLoaded) { + return + } + val desertBytes = if (blackAndWhite) { Res.readBytes("drawable/svg/desert_bnw.svg") } else { @@ -63,6 +69,8 @@ object MapDrawer { val spiceBytes = Res.readBytes("drawable/svg/spice.svg") spiceSVG = spiceBytes.decodeToString().replace("cls-", "spice-cls-") + "\n" + + resourcesLoaded = true } private fun getDefinitions(): String { @@ -371,14 +379,8 @@ object MapDrawer { val offset = getOffsetVector(field.id) getPlayerMarkerSvg( offset, - GREEK_ALPHABET[(player.id[0] - '0')].toString(), - COLORS[ - ( - player.id[0] - - '0' - ) % - COLORS.size, - ], + GREEK_ALPHABET[player.id].toString(), + COLORS[player.id], ) } ownedFieldsSvgs.joinToString(separator = "") @@ -446,7 +448,7 @@ suspend fun main() { ), players = setOf( Player( - id = "0", + id = 0, water = 12, spice = 12, harvestersPurchased = 1, @@ -454,7 +456,7 @@ suspend fun main() { ownedFields = mutableSetOf(a1, a2), ), Player( - id = "1", + id = 1, water = 12, spice = 12, harvestersPurchased = 1, @@ -462,6 +464,9 @@ suspend fun main() { ownedFields = mutableSetOf(b1, b2), ), ), + id = -1, + gameId = -1, + index = -1, ) val fields = (1..12).map { @@ -478,7 +483,7 @@ suspend fun main() { fields = fields.toMutableSet(), players = fields.mapIndexed { index, field -> Player( - id = (index + '0'.code).toChar().toString(), + id = index, water = 12, spice = 12, harvestersPurchased = 1, @@ -486,6 +491,9 @@ suspend fun main() { ownedFields = mutableSetOf(field), ) }.toMutableSet(), + id = -1, + gameId = -1, + index = -1, ) MapDrawer.loadResources() From 16baceb6905d007176dbede7c8b3661876f854b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sat, 14 Mar 2026 11:05:17 +0000 Subject: [PATCH 128/162] Navigation --- .../hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 3a2ba64..75dca91 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -1,18 +1,21 @@ package hu.mokegyesulet.it.dunestrat.navigation import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import androidx.navigation.toRoute import hu.mokegyesulet.it.dunestrat.feature.init.InitScreen import hu.mokegyesulet.it.dunestrat.feature.mainmenu.MainMenu import hu.mokegyesulet.it.dunestrat.feature.playerstep.PlayerStepInputScreen import hu.mokegyesulet.it.dunestrat.feature.testdata.TestDataScreen @Composable -fun NavGraph(navController: NavHostController = rememberNavController()) { +fun NavGraph( + navController: NavHostController = rememberNavController(), + toRoute: NavBackStackEntry.() -> Screen.PlayerStepInput, +) { NavHost( navController = navController, startDestination = Screen.MainMenu, From 49d60129fdaf0d8d1a8f59848103a84fe380be8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1si=20M=C3=A1t=C3=A9?= Date: Fri, 23 Jan 2026 13:49:32 +0100 Subject: [PATCH 129/162] Basic UI and state dataclass for step creation --- .../entersteps/admin/EnterStepsAdminScreen.kt | 23 +++++++ .../admin/EnterStepsAdminViewModel.kt | 45 ++++++++++++++ .../it/dunestrat/ui/step/EnterStepsCard.kt | 61 +++++++++++++++++++ .../it/dunestrat/ui/step/StepCardState.kt | 22 +++++++ 4 files changed, 151 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt create mode 100644 composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt new file mode 100644 index 0000000..a4f0bb1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt @@ -0,0 +1,23 @@ +package hu.mokegyesulet.it.dunestrat.feature.entersteps.admin + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.ui.step.EnterStepsCard + +@Composable +fun EnterStepsAdminScreen(players: List) { + val viewModel = viewModel { EnterStepsAdminViewModel(players) } + val steps = viewModel.stepStates.value + LazyColumn( + modifier = Modifier.fillMaxWidth(1f), + ) { + itemsIndexed(steps) { index, step -> + EnterStepsCard(step, viewModel.onStepCardStateChanged(index)) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt new file mode 100644 index 0000000..e16a529 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt @@ -0,0 +1,45 @@ +package hu.mokegyesulet.it.dunestrat.feature.entersteps.admin + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import hu.mokegyesulet.it.dunestrat.model.Player +import hu.mokegyesulet.it.dunestrat.ui.step.StepCardEvent +import hu.mokegyesulet.it.dunestrat.ui.step.StepCardState + +class EnterStepsAdminViewModel(players: List) : ViewModel() { + private val _stepStates: MutableList = ArrayList() + init { + for (p in players) { + _stepStates.add(StepCardState(p.id)) + } + } + val stepStates: MutableState> + get() = mutableStateOf(_stepStates) + + private fun onStepCardStateChanged( + event: StepCardEvent, + playerIndex: Int, + ) { + when (event) { + is StepCardEvent.EnterFieldsChanged -> _stepStates[playerIndex] = + _stepStates[playerIndex].copy(enterFields = event.enterFields) + + is StepCardEvent.LeaveFieldsChanged -> _stepStates[playerIndex] = + _stepStates[playerIndex].copy(leaveFields = event.leaveFields) + + is StepCardEvent.BuildHarvestersChanged -> _stepStates[playerIndex] = + _stepStates[playerIndex].copy(buildHarvesters = event.buildHarvesters) + + is StepCardEvent.PurchaseWeaponsChanged -> { + val x = _stepStates[playerIndex].purchaseWeapons.toMutableMap() + x[event.weaponType] = event.amount + _stepStates[playerIndex] = _stepStates[playerIndex].copy(purchaseWeapons = x) + } + } + } + + fun onStepCardStateChanged(playerIndex: Int): (StepCardEvent) -> Unit = { e -> + onStepCardStateChanged(e, playerIndex) + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt new file mode 100644 index 0000000..49a1ae5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt @@ -0,0 +1,61 @@ +package hu.mokegyesulet.it.dunestrat.ui.step + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import hu.mokegyesulet.it.dunestrat.model.Weapon + +@Composable +fun EnterStepsCard( + stepCardState: StepCardState, + onStepCardStateChanged: (StepCardEvent) -> Unit, +) { + Card { + Column { + Text("Player: ${stepCardState.playerId}") + Row { + Text("Lelép:") + TextField( + value = stepCardState.leaveFields, + onValueChange = { + onStepCardStateChanged(StepCardEvent.LeaveFieldsChanged(it)) + }, + ) + } + Row { + Text("Rálép:") + TextField( + value = stepCardState.enterFields, + onValueChange = { + onStepCardStateChanged(StepCardEvent.EnterFieldsChanged(it)) + }, + ) + } + Row { + Text("Fegyver:") + for (w in Weapon.entries) { + TextField( + value = stepCardState.purchaseWeapons[w].toString(), + onValueChange = { + onStepCardStateChanged( + StepCardEvent.PurchaseWeaponsChanged(w, it.toInt()), + ) + }, + ) + } + } + Row { + Text("Harvester:") + TextField( + value = stepCardState.buildHarvesters, + onValueChange = { + onStepCardStateChanged(StepCardEvent.BuildHarvestersChanged(it)) + }, + ) + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt new file mode 100644 index 0000000..cd26703 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt @@ -0,0 +1,22 @@ +package hu.mokegyesulet.it.dunestrat.ui.step + +import hu.mokegyesulet.it.dunestrat.model.Weapon + +data class StepCardState( + val playerId: String, + val leaveFields: String = "", + val enterFields: String = "", + val purchaseWeapons: Map = mapOf( + Weapon.LASGUN to 0, + Weapon.PISTOL to 0, + Weapon.CRYSKNIFE to 0, + Weapon.LEGION to 0, + ), + val buildHarvesters: String = "", +) +sealed class StepCardEvent { + class LeaveFieldsChanged(val leaveFields: String) : StepCardEvent() + class EnterFieldsChanged(val enterFields: String) : StepCardEvent() + class PurchaseWeaponsChanged(val weaponType: Weapon, val amount: Int) : StepCardEvent() + class BuildHarvestersChanged(val buildHarvesters: String) : StepCardEvent() +} From 251b307f1d8ef7932b008d2d5b42a7eb896122ca Mon Sep 17 00:00:00 2001 From: cstomi74 Date: Wed, 25 Feb 2026 19:13:50 +0100 Subject: [PATCH 130/162] =?UTF-8?q?A=20main=20menu=20alapvet=C5=91en=20sze?= =?UTF-8?q?rintem=20el=20van=20rendezve,=20mag=C3=A1n=20a=20kin=C3=A9zet?= =?UTF-8?q?=C3=A9n=20kell=20m=C3=A9g=20csiszolni=20b=C5=91ven,=20illetve?= =?UTF-8?q?=20a=20grid=20a=20k=C3=B6zep=C3=A9n=20csak=20egy=20placeholder,?= =?UTF-8?q?=20oda=20m=C3=A9g=20be=20kell=20=C3=BCgyeskedni=20az=20aktu?= =?UTF-8?q?=C3=A1lis=20game=20statet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/artifacts/composeApp_js.xml | 8 + .idea/artifacts/composeApp_jvm.xml | 8 + .idea/artifacts/composeApp_wasm_js.xml | 8 + .idea/gradle.xml | 18 ++ .../it/dunestrat/feature/mainmenu/MainMenu.kt | 156 +++++++++++++----- .../feature/mainmenu/MainMenuViewModel.kt | 12 ++ .../it/dunestrat/navigation/NavGraph.kt | 5 +- 7 files changed, 175 insertions(+), 40 deletions(-) create mode 100644 .idea/artifacts/composeApp_js.xml create mode 100644 .idea/artifacts/composeApp_jvm.xml create mode 100644 .idea/artifacts/composeApp_wasm_js.xml create mode 100644 .idea/gradle.xml diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml new file mode 100644 index 0000000..e52ad0e --- /dev/null +++ b/.idea/artifacts/composeApp_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml new file mode 100644 index 0000000..96d842a --- /dev/null +++ b/.idea/artifacts/composeApp_jvm.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml new file mode 100644 index 0000000..e3c9c59 --- /dev/null +++ b/.idea/artifacts/composeApp_wasm_js.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/composeApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..2fa8368 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index 0787087..c02a101 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -1,66 +1,148 @@ package hu.mokegyesulet.it.dunestrat.feature.mainmenu -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import dune_strat.composeapp.generated.resources.Res +import dune_strat.composeapp.generated.resources.grid +import org.jetbrains.compose.resources.painterResource +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainMenu( - onPlayerStepInput: () -> Unit, - onPlaceholder2: () -> Unit, - onTestData: () -> Unit, + onInputMoves: () -> Unit, + onStat: () -> Unit, ) { - val viewModel: MainMenuViewModel = viewModel { MainMenuViewModel() } - val loggedIn by viewModel.isLoggedIn.collectAsState(initial = false) - val deserts by viewModel.deserts.collectAsState(initial = emptyList()) - if (loggedIn) { + val viewModel = viewModel { MainMenuViewModel() } + val gameCount by viewModel.gameCount + val games by viewModel.games.collectAsState(emptyList()) + var showMenu by remember { mutableStateOf(false) } + var isExpanded by remember { mutableStateOf(false) } + var gameOpened by remember { mutableStateOf("") } + Row { Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceEvenly, + Modifier.padding(10.dp), ) { Button( - onClick = onPlayerStepInput, - ) { - Text(text = "Player step input") - } + onClick = { viewModel.onEvent(MainMenuViewModel.Event.CreateGame) }, + modifier = Modifier.width(240.dp) + .height(60.dp), - Button( - onClick = {}, ) { - Text(text = "Placeholder 2") + Text(text = "Új játék") } + Text(text = "Létrehozott játékok száma: $gameCount") + // Text(text = "Adatok: ${SupabaseRepository.getGames()}") - Button( - onClick = onTestData, + ExposedDropdownMenuBox( + expanded = isExpanded, + onExpandedChange = { isExpanded = it }, ) { - Text(text = "Test data") + TextField( + label = { Text("Játék kiválasztása") }, + value = gameOpened, + onValueChange = {}, + readOnly = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + modifier = Modifier.menuAnchor(), + + ) + ExposedDropdownMenu( + expanded = isExpanded, + onDismissRequest = { isExpanded = false }, + + ) { + DropdownMenuItem( + text = { Text(text = "Játék 1") }, + onClick = { + isExpanded = false + gameOpened = "Játék 1" + }, + ) + DropdownMenuItem( + text = { Text(text = "Játék 2") }, + onClick = { + isExpanded = false + gameOpened = "Játék 2" + }, + ) + DropdownMenuItem( + text = { Text(text = "Játék 3") }, + onClick = { + isExpanded = false + gameOpened = "Játék 3" + }, + ) + } } + } - Button( - onClick = viewModel::onLogout, + Column( + Modifier.weight(1f) + .fillMaxSize() + .padding(5.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Bottom, + + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { - Text(text = "Log out") + Image( + painter = painterResource(Res.drawable.grid), + contentDescription = null, + modifier = Modifier.background(Color.Yellow) + .size(width = 600.dp, height = 600.dp), + + ) } } - } else { + Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, + modifier = Modifier + .wrapContentWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.End, + ) { Button( - onClick = viewModel::onLogin, + onClick = onInputMoves, + modifier = Modifier.padding(10.dp) + .width(200.dp) + .height(50.dp), + ) { + Text(text = "Lépések felvétele") + } + Text( + text = "Statisztikák: ", + textAlign = TextAlign.Left, + modifier = Modifier.width(200.dp), + + ) + + Spacer( + modifier = Modifier.height(8.dp) + .weight(10f), + ) + + Button( + onClick = onStat, + modifier = Modifier.padding(10.dp), ) { - Text(text = "Log in") + Text(text = "STAT") } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt index 242159e..fe6648f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt @@ -1,5 +1,6 @@ package hu.mokegyesulet.it.dunestrat.feature.mainmenu +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.AuthStatus @@ -29,4 +30,15 @@ class MainMenuViewModel : ViewModel() { get() = SupabaseAuth.authStatus.map { it is AuthStatus.Authenticated } + + val gameCount = mutableStateOf(0) + val games = SupabaseRepository.getGames() + sealed class Event() { + data object CreateGame : Event() + } + fun onEvent(event: Event) { + when (event) { + is Event.CreateGame -> gameCount.value += 1 + } + } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt index 75dca91..de6a47f 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt @@ -22,9 +22,8 @@ fun NavGraph( ) { composable { MainMenu( - onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(32)) }, - onPlaceholder2 = {}, - onTestData = { navController.navigate(Screen.TestData) }, + onInputMoves = {}, + onStat = {}, ) } From 8a2a18b200d13fe2d7ac1459fb4efe5914c19aad Mon Sep 17 00:00:00 2001 From: cstomi74 Date: Sun, 1 Mar 2026 16:44:00 +0100 Subject: [PATCH 131/162] =?UTF-8?q?A=20statisztik=C3=A1kon=20k=C3=ADv?= =?UTF-8?q?=C3=BCl=20minden=20el=20van=20rendezve=20a=20k=C3=A9perny=C5=91?= =?UTF-8?q?n,=20=C3=A9s=20ebben=20az=20=C3=A1llapotban=20nem=20vinnyog=20s?= =?UTF-8?q?emmi=C3=A9rt=20az=20IntelliJ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index c02a101..b9e5584 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -40,7 +40,6 @@ fun MainMenu( Text(text = "Új játék") } Text(text = "Létrehozott játékok száma: $gameCount") - // Text(text = "Adatok: ${SupabaseRepository.getGames()}") ExposedDropdownMenuBox( expanded = isExpanded, @@ -55,7 +54,10 @@ fun MainMenu( ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded) }, colors = ExposedDropdownMenuDefaults.textFieldColors(), - modifier = Modifier.menuAnchor(), + // modifier = Modifier.menuAnchor(), + modifier = Modifier.menuAnchor( + ExposedDropdownMenuAnchorType.PrimaryNotEditable, + ), ) ExposedDropdownMenu( From 4b86432ef287c2b7b409f069b4147763d7c53b94 Mon Sep 17 00:00:00 2001 From: cstomi74 Date: Sun, 8 Mar 2026 16:51:07 +0100 Subject: [PATCH 132/162] =?UTF-8?q?Mam=C3=A1si=20machin=C3=A1ci=C3=B3=20ut?= =?UTF-8?q?=C3=A1n.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 33 ++++++------------- .../feature/mainmenu/MainMenuViewModel.kt | 25 +++++++++++++- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt index b9e5584..dfbda7c 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt @@ -54,7 +54,6 @@ fun MainMenu( ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded) }, colors = ExposedDropdownMenuDefaults.textFieldColors(), - // modifier = Modifier.menuAnchor(), modifier = Modifier.menuAnchor( ExposedDropdownMenuAnchorType.PrimaryNotEditable, ), @@ -62,30 +61,18 @@ fun MainMenu( ) ExposedDropdownMenu( expanded = isExpanded, - onDismissRequest = { isExpanded = false }, + onDismissRequest = { viewModel.onEvent(MainMenuViewModel.Event.ExpandMenu) }, ) { - DropdownMenuItem( - text = { Text(text = "Játék 1") }, - onClick = { - isExpanded = false - gameOpened = "Játék 1" - }, - ) - DropdownMenuItem( - text = { Text(text = "Játék 2") }, - onClick = { - isExpanded = false - gameOpened = "Játék 2" - }, - ) - DropdownMenuItem( - text = { Text(text = "Játék 3") }, - onClick = { - isExpanded = false - gameOpened = "Játék 3" - }, - ) + for (g in games) { + DropdownMenuItem( + text = { Text(text = g.name) }, + onClick = { + viewModel.onEvent(MainMenuViewModel.Event.ExpandMenu) + viewModel.onEvent(MainMenuViewModel.Event.SelectGame(g)) + }, + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt index fe6648f..10c24ff 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt @@ -6,8 +6,15 @@ import androidx.lifecycle.viewModelScope import hu.mokegyesulet.it.dunestrat.backend.AuthStatus import hu.mokegyesulet.it.dunestrat.backend.SupabaseAuth import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository +import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository.supabase +import hu.mokegyesulet.it.dunestrat.backend.entities.GameStateDatabaseEntity +import hu.mokegyesulet.it.dunestrat.model.Game +import io.github.jan.supabase.postgrest.from +import io.github.jan.supabase.realtime.selectAsFlow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class MainMenuViewModel : ViewModel() { @@ -32,13 +39,29 @@ class MainMenuViewModel : ViewModel() { } val gameCount = mutableStateOf(0) - val games = SupabaseRepository.getGames() + val games = SupabaseRepository.getGames().stateIn( + viewModelScope, + SharingStarted.Eagerly, + emptyList(), + ) + val isExpanded = mutableStateOf(false) + + val waterAmount = SupabaseRepository.getLatestGameStateByGameId().stateIn( + viewModelScope, + SharingStarted.Eagerly, + initialValue = emptyList(), + ) + sealed class Event() { data object CreateGame : Event() + data object ExpandMenu : Event() + data class SelectGame(val game: Game) : Event() } fun onEvent(event: Event) { when (event) { is Event.CreateGame -> gameCount.value += 1 + is Event.ExpandMenu -> TODO() + is Event.SelectGame -> TODO() } } } From dc32734c6864e32695fbadbdd66b895287970c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20G=C3=B6r=C3=B6mbey?= Date: Sun, 14 Jun 2026 17:53:43 +0200 Subject: [PATCH 133/162] =?UTF-8?q?Laci=20machin=C3=A1ci=C3=B3i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/gradle.xml | 2 +- .../it/dunestrat/feature/mainmenu/MainMenu.kt | 16 +++++++++------- .../feature/mainmenu/MainMenuViewModel.kt | 14 +++++--------- .../it/dunestrat/navigation/NavGraph.kt | 7 ++----- .../it/dunestrat/ui/step/StepCardState.kt | 2 +- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 2fa8368..cebe225 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -5,7 +5,7 @@ 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt index 92b11f7..e1ecf2d 100644 --- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt +++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/backend/SupabaseAuth.kt @@ -9,7 +9,8 @@ import kotlinx.coroutines.flow.map object SupabaseAuth : Auth { private val auth = SupabaseClientProvider.client.auth - override suspend fun logIn() = auth.signInWith(Google) + override suspend fun logIn() = + auth.signInWith(provider = Google, redirectUrl = "https://mok-it.github.io/dune-strat/") override val authStatus get(): Flow = auth.sessionStatus.map { s -> if (s is SessionStatus.Authenticated) { AuthStatus.Authenticated(