From a97eb0969ce2c7c108c13429ba1d3e4039ab99ed Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 29 Jan 2024 20:52:13 -0800 Subject: [PATCH 01/24] Image --- images/CraftTracker.jpeg | Bin 0 -> 154632 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/CraftTracker.jpeg diff --git a/images/CraftTracker.jpeg b/images/CraftTracker.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..2ed2ad7b9d4aa03feb6f5cc757d5e834f1b914cd GIT binary patch literal 154632 zcmbTdcT`hL8$KEcy-9~qq$Knr30;cRkkF)e6bKzcuYw-Blz<`hDkUIY2oS0sItf)k zkuDuYih#=D=sDlz`;~jwy?@<%-(A+8wP(+oXZFnOcb@mz|5^FZHh|R_j)Vg!C;$M8 z+YRuab%6eD>i=ex|0DI^Gv)s_Adm|9U!$b_?*-L=jS@&rO+|IPQ&Ii5kpES}?NgLg zz}sB^pY1G03`rO!3LmYqX4o|{AUaxbX!mA|5-NRe;LJX>C`l|bo302w;8%w z0hAQC6{Gqe)w#|6^!7f0ijA6G1gcBJVd+jQ8p;Vv%&n#a>varsSuK2nD0+k?(KB%K z@Phcn#3dx9q?MFaRMph)>l+xtjSxsbZmTLl0=?bTzdau`Qys!+RpCY z{=wnV=P$<>msj6^{Jj3_=I{UFqPWHRzwLhl`+wlNZG_vVqykdW-nKD-f-?H{0J2e0 zi$H1EbuDS#LpelYiFBNLxz!!R^k7A+Z(JT>3k=*4r5&-0|3dp8$o}5}OZxwX?EeJz zf8kmKFaarUUmlPR@Br{{+(FE$vp>Py*&~%oQ9bY=1HzHeH>CCE>!yWC_>Dgv_b#1^ ztyyIPC4OnAGma#A7=ex;l(f8U>B>D;9(_bMj}-!UzNux2E)YOy7Y^DJxYzire6LiC z3_B37OxZG?R%%M!W9x5NV;*yp?g(rbz*avGg3cQYaD8VlbAFMjR1~UH8+Mb5FJ^87 z1la?FkYDC4q$cP!a0Q#GY~%Y-SO+Sb&;nr|5|Iv{pcCMl*CjQ-TZURfpv|BhTPN;F z*CtgKdN-S^D<41@?H8*rb^R(%_%{ZZ@G*&a0u@XFQ6fl&Xgh0t3%%TXVcs|#A-;8N zGGpyKzVFRMNwiZ=aRGKQu}%&7G|UU+Ru-^j`l|2SJG_Ihj{>uwA9>4QUPH<~w>eJK zl-XW^n9Y8OUWYfuZv*Oo@qex)c7fqEcpW=t@BNO%NNQ*A@x*16uwH zP)WlmLH)vtf?SIe8+LRFYwz_j!kzh7EFKrG)?-(OWG0`NtKtRpgYf7 zU$B`C789MSY1YXT->#*zvqIdYs|lmjG}3;*%#Y&;613D4EumEKep)0fY3uD}R`;(* z6$j2a5p#EvOM5~TE$6V=@NZy-L?KFA@5T2*w-VlGzY+~;g%f+rpWt3d-CZKJ*{S98 z#8hA$eY`#8%^%Xi>Gpk+*DIw_Nae;7#&v891bxP9#Gbw>m@z!rPHq0|n##LLso+eR z`-g!rP|~HbP{(Bd#P)$A%N;AaeN@#KE1Z+agysm+-wu-IDZgb@8DGLc%CvD)1$g2# z?sB~LZx|<+)>k)4^dz*Kw$%I4dChnNR)5Sbs?P&oKD(3>|BxG`rrE6>D`_LxDr<-& zZL!c#Xj`xtnR1-sdF;Ha&Kto->9ECfkpy<(5EK=?5QO5m3V2RBFR%VRG45#VB4a4b z)>U?gWMk5or0Rlw7DU@I+>~N1;JHg0DMELgxVcb!yHyp86s|pFh!x2Dp}9=7daug| z216_2$HgJQeaTg;fg3-@d0w7N(k^Eb~KzKjLxj$R2$D>Y4tV2D4#~Vgh?PKFEdc(4_ z#5DDKo}%D|DdwtDx@Ixj$4s*B?f4imvbV>Qd2Xc~Jd@Bd7dAw>0z7w9p|RQPXhqR! znV-nwaHglvTAPNo%Ei2-&4`Jrvkl(;a=sEn zGtdbA);KvbAf|@cZ4&o>Dd?W@)JJ0d+J_-OrqGGYeoi|rTZayaj~eKw1LX{sZ8a#Pq=% z>uOMA%WEliFJ8^KCwwwI9F|3Ek7(j^#e696dZwnVr*75h2eAvv5DD|o7-y4E&U*FT zM07#x?%TdAx$ie(-gz?YJX`iu1=4p!^)aPRrp4nY&u0U)zewN$%Fue37qZso43`MU zMe0}c&s>NMFYrl=+t}>j zsTxK^(BBMv*9vb&xvqGl`XWy3)k}86*q@OV)8qFyViEQce!toov9v-8%t_o871J3( zFPMS_zgq~(&dYwzqsdWE`NkdL>5-GlFANPVUI*4>W_5~NTB#qr-!C!4bMs{%bblE0 z{nIO5DfdiKIJ+KmW+YkmR!0T5|N7bmU-r(l|%Jws|a;q|g~ z4V`2514;3z$$WNr)1y=49V>sb*ICFLED-C={|x0vS<%T3LNbz{)+Q&=-_Ap9S$Q%p zH{NGkGR{SvIW}|a4F@#35J{YAP2g23TXC`gC{ZkitDI|dI>i26<147IQ-FQuy-wPt zwi=v6dY{GPSu5Z)#1nCmYC&h?o_rmmzUee9DK{MuP2JiUcDXs-Q1rjFmar&A=`#yXZAgHbSgl0P_F$p+p;Ue65>pRd%7&j)YUj|xn`zTmFxcz! zkQBB8Af}niCm>I<{`*QHPprI1KfH5L$dez>EXxvg*8foM!yi#6xnqktNRvX@Ioo_J zQYjm<7a*$U^pC9=j@kIo(z^%dGI?iJiByYWGlmg=y-!TLS*&Hs#eS%G>zUhUp5aBOeC{n${oelGIpIKDr&)2 z*nbFqsrcNQY0w&$?M3lG$B^FkwrBkKxKaz*PmA|w>TG^i9si1*nPGN|I5cKS5|o0J z@;(rZmdFEv!@o5tC?yT_E_L>q`#SMIy_ybX9QKl_fNx9(2VUy4*+*yh#14;MQt*eC z3ozku!Mc2fYpFj5|Ma$h9!Pq0{C^_SrB&2RY`k zGFz>>Z>DTEEOto_$$`_|&I>1nU3=dTM5rA2u1LsC zwJw#En0f5R9n+(AuJSUcT6S6tVvM>F?%usnG$k?-7Tx~o3WzzAkVrj45Px{LKXJg` zI3Gl!IPe6FJeg8{By@v4j8^N8X#%wb_qC5^eO`MVhxfd!47DP?Cxvxb0@4bu4iOso z>uz=214RchIg>;%OdVp}(6TVknP31tAYe!PCS1yUcka&my`nw~uYbM8irbvkA$J~ymsq4qsRx)hYkjx>{pjkI z`QIBeZ3}wQkX$%dQQ#j^UbeL77YU(>A1j~UpDiuO?MM2im&#Q$Nmn|-+0Eu`ZPPa| z+w2qGkoN8l*(4!-6Z9714{0z#m&*z+(w@KfFX6g?i%;z*y{RkJ zxl|k0b!?E@e*m7jSc)JcOIz!zJ(?d7%)JseTke(TVK@H65ZWXfdMbf`K7eW4!ZR%k zy1e2Zz^40C`@$7U@*A>)=skz7w9&P}-zK&%l6z5{8=v%H#0hU(%up*tRBsz%{%3?> zbB@kUtn7Uj4SK5p15U?q@TOfK&P=_e^#I;js5 zh)P2InAGcoNLu(CCN%o5rX93#n{A%&gOkaRP9X<5PvzFx zLSsvQ{j!Y`VAW@0k#Shc&uNhrf@$5+=YAOAMwsGO3tICNeLfRwDg*2s<>fD@S2d?h zqP=yDXUQrpFFy7@IcMgK2ox}Fcog9fwArFuj6&`#q7-;r_Y3lO_OLgZR5Ns*xrui_QooF%hrAc8NSG<{to(W6%&cWm1k z>qKBIt9(goNH6p@t80yyb;V&mexDSv78UrWahUt1V9SF)Us{x2;E|7we+g_ESgBm_ z!IB>N8s`o~?ew0l>98mU=vZ{VA^Q{=zC3*Ku%{o#Z>k&c6lf@LNMFFGR;I?UuUe{r z&xaha@Es^sWP}jjjQ0$tu30xAY0AQt>f`d+AIc3GnQE%ErViTJL>t9p? ze$qW!{%(Yh436aAm>EaqIP6`nap5dGR7mp}kb=!#!5lQtDLE;Qd&7kDnyv10%6zbL za_mCv9RX*Hq4qP=B>hQ##qFXfsz12UkpU%LOP#_=jNV;uY;FfFL6Zm9^*Y*1gszqQ zubj&lNhih~@H#uSjs8qf1d$SpY+p;6tt(ozZWxk^Wvo{OG zg+)4@nA$^S^X9HApAz(?`uZx5&*+aTCzNx9L>_&35KDx)lj%|mFtrtv%Fu%GAXmn) z^=YvdVpRKda04WF9?d*Eq_{dZAh*Jwm@p>*#|JVNnvH7cEHiHn850b}@I9*cyOyoF zplgYIqv3qR&u@xy;JRT*(3%cOv?~kkT0j;Ro=5Qx7{x%k()b$<9tM4hzf1t!Vdx6Q zSqZo4p`m$LxIs96-5|d;@LpA_M=zm=_E$t7YZ}oJXrhpf-n5Jai|}ySIO^_dW5S zw(AqLXMao2!k{5oh#Q}{qq9gc)?6e*W9EYRZ%Bam{kLC7mP%)4g4 zDZDAz9F*X}aipi~M*}lb{ncsEc49+(VV0>fkhv(9eUKX{ftk6-AkC@!>&=N~pmp4`xv)05&i-r55-Ztpf9V% zB?=p6bvlhQPUBS39&08IJ1Hii&)3Xm`wS&js{MG`64W$%BK-u5#X)l?!jA{aX}%=>XpZmnvh_o5K87N_1P{jgDm}TW*=EzWdq59(J}a zl!KQv-^`x`eI0JXC+A?|itT9)#!PXntvv@Ry@>iEG;cwGGb8t@7kab8!Xa#!7syx1HVd zCxhlqPAj6X=F2@5gn?ZGm78~H>Sn*Q`yHo0_LE|#VdiVW-B+`Gp#%CG)zfgKrtG)r zf^x;9__!InuM5#luK5XJPwoZX3E#MKoU&h_U}RSXH@SN$9x6SQa*!I{P}4$vAx|jO zSJ4=Cq5M+rkC@z`rk|3+uhZx@SV9JweqpE_&0PH4Rz=dT+6Vn@ZHJ=7TuFO?r)ST> zOPpMIOLh5SW3&fAuf&v)M87c<{uKj!`Go1c{OeHI{FRYBQxF@M^IbO(ld1wxlZ`~) zdcf?~(iVVJTJ)?dzKILxkZFNw26DsqSpl@PpgA>x(t;Q*^F+|PFG@x#WFYqfrz70- zV-8wQ@rf(S+FoRCZdqUjZphMcrc-CX4@rvM%sDE@KDnzn`>^v$97e>v4Csv^Wn|6( zE7>S$sa_PLgmZBk&#cKJec$yjI3{sa&_1&n%z8fMP~InXJ3;bZ50{1iPXUkR-KO7} z>xtTq3=J`1bp&1*wmI)N7)TYKAT_1>fpg8N12^WnF%L^GW0#RcM&+oaj(V1>0zr`Q z6@f+cDpT8+k5DXrSaxNSDEsF{Ibtjf+ZL;?l}6YsO|6 zvJ>*hAWO`I?!1#FE*IH#mcG(l#nvJxayi{+$-oL6Z2Xh9ji(w?{zEgPG}xWEwTn+_ ziPJX(-gVUL4NF1uT9;!(Nwi>yO*qK_H4TCM0Snr=?1#}M_!dK%LJ$1+NE0-)9tx1HvCM&;nEB#&roT+!uVAPd`@5F3 zyc8EWmgp$0NQ~1)@1tE0Z=bYq!8fG9UQC7ynk>~G-Wj#UgQfqmjaiCPwT^yY$vW;i zGa7?rvw`ZBi^$sS?Slnb!m8F>>#Y4d!>rFbL3d&Up=5-1xg9-)q|CR7tEiCbgnV z{usA-jWY^GP$58zR!)#Y&Eq^f=XbOk3*KSjM0fV#$e4Xy&b7$k>-rYUjfhjHY zvp4gpxsrxaFX;0hvo|9gcOso$iASnJt%@xDnEz`rq4Fb$|KfMI$K{+Qga<8o&P5bm?!F*eL{Qu zX~>Yc)&PU-qp{_B)|ied4tB>!u1MYELwm3Et2M{*5sU{q_V%ZzA+_9#bD zIqU2t;!5F>t3LBiuZ(eUjpW0QEjo9;G;1u0 z-a_$}yh9$D{rfi!lZaDwpl`;WPCFwY^Q{X&buJnRR@uH`mj}J|dc}BkY=!jA%9{-7 z808`jrXD;dJT~F{0{LuO4zc8>(C^|P+53s;jx^rLMD@c2=V4SuH4e&M>Nh2 z^!YX3q62!e?#a|4$eR)~c!y`iJd&Xa~6&~Im+qW28q8s&m^)9X&G#|{ye;J!i?oJB_(ZOEE)0PS{iX4v6zAkWC* z2Yh>$LC(r!r0lhqGF?zA{$5Cy*syRKOW4tQa?%P(QqQavZQrdGJDTom(3(q=8ha(E3kRMBeK9r9w#eAy|%JAIVjRr*PxNvmS=u_)oztso(s5Rx6m-zJtWpN-i<-s|Okv(X_ zTtr!PoQAh&F1Ndykjls45{vjWn_t6yUUF}DjWn*RK^DF+^5Ii; zbsI^3%K`k-C{xLAh1>Q|Vssa8ina=m4Lew#b0?o>gU)@ft>PW~+D#PubR=~bT7XZ- zO@45cPy1X29+QqHzm*W)Zv(H6s#`4iZSsrP)hf0=LbTSD+>;G*bC~(u1}ThbT>(#w z>)yt3Vuj+lN8Mxu6iUn5~f3y>P^b##Wiv{W&>DrSe*lda@Iqp2(0Ox0u^c6HI0xiIC^s< z%+N>dz0j)1^5Tbk$kJmk!<=uEOtP;?m4#PFM8NX=b%V-XvCYnaR}sw0BX8LgXWdq^H@AO8{1DyCo`t+Zw=@RKmh_l zkiUNjR8u@DULBaeU{Q^lhk7fTOamXajW*+>V_B zk_g>n!Cg?buUB_z&Q^HUzIcC2t!PtXG4L$3y91W?qMsHjNK;s4h;^eu%sZJ$^p4wK z-m(KshPFAb00zV;nsGu$J&;nDB%xGhxxl_#HgK0~5eCRn*PQdr9y=Mu9()UbCypdh zxp834*d*(k#4v^hIS8V;`&yOYw^Km&aA(pea9`6$OCyDwvj5c6prEz58fw4@mhmz_ zcgbD)ROj}gA{F#DC6M(8ITy8-djc((EFAA7ORGz0(QH+8W6Czr#52+q4J+(vae>>c zsIb)(L&A8+q(z8Ef?6H5SGa>lGmVRkES;l8L8gk~M(9GC!*-mI`J3Z)6Fc^-I5sX% z8KGI>m{GFEITY@Pz{~-H zF<+DYpMn}Sq&!-|og)s|v)9rFjDaB`qgpY{AASE`N^PwUv5)4Ll_{oJ)nIxdZIOcq z@hZ$IgkoWTu&Pl`<(IA?YqL$$N7NYLyYyU63TFVJF`Av{>cdBUKy^QrlT==L{6g$s zvY%gbVoaL12Rg)(sHKL5w-etbbtx@%N}JV1Ksui`spyMN5LCRf&Aq24W_ct3y>d%> zB9L5u#9(RPDH$IioBMkKuUog)J{~6p52=c8(Dnk|fO6vE_yD>krryfpo*3C@@v)bD z%7Hiv$s4em#muuHgt~IHgvXazc$5-T$c9U^^Va4VA--kGwWc4tt70`{PPY*=5z_oo zxS|>)mvf?3XX|qQ`(CuL6|o77@)Rj~H!36h;u66P3(=!8#eRZX(A^N1akw`Ne;>ke zBQkth{aWVk;I80cKhX`1o2d3cZKrdRTQ1>wv;^dZQbOOXyZ55+--rP-5mlPr-tJ#o zUD5VyPUB%#>vuyu$O82c^kQ>k^iF8^%cCFZZ%}>li8EnhQh;TQmSHP{Kd~)af&TA0AY&)EW}KQB9jzq!;aV)i?OG~rQ`v4H z&y2WqzUNqP(tJ&PzZmm(k#opvBowf&d<+o2kquY%pw~r zqcY(p?wn^kU9Rvb9*p=erXIPNZ)J$0s39n2c3yl#%TVH#J6u=2K&3|&EjMMspX#I1 zgJygr|HGtjH`FIsF^CB}QPr4rwnwK6PLpXLz3x$vUDE}xIL+j>giOYX&AfYBs*bi~ zlBas>q2MkhyI13t76M9dPBsYE;txcF@+D>K9k^TLi(oO$>5RY8{vON zV}#q3#RY=YfxR;F=SmBGFkRfH6k!bt~F4=O=&6 z?pD7e6Is!;`00@1@2LS${09mI(`#6kQ}O(+cRu>3H%x|gWHT}Yect9|m#OuFvj~be z)l*=i;Z1WmoY-P^;pJ=98{NcOYnneB?X^=)YR`^laiC#D=>s*q*1mLIIi7Z@C#&QZ-mZm(B zE38^8bDrdyJ;g6yvs`KzR8dD1WQ8&qT{FV#^2dfe%LQet8*!QDpXXGaBbkZZv&|-u ztA|ZWs*Gq5Mcy@9Dm!18UtqGg{)5@*vkwmFo+zvCAIcN2!b3wD9t%3XpJYouEhLns zx815Nis@VgzxI{UlWzWX?UghrDpIA~QrpXRns_Yoee%y7je?xbKcYBaD78z3Ljelg zZDEe^&eVdTM#Z(*sNE4}G4vA5Ol!7TDe3@lxvT9_BoT| zb{J=<>IXx9UbnFCE{rWE84x_x^xP3^DiGF_+Pa6LdD3TIpIah!wBDJ-h2^f1UN=12 zJ>FbE%_Len{BHAv=Pu~*J}iUaZqyC*{*G)fA|d2cTdplO{vj?JEuDVQYo@jwJavqJ zr%!PuxZVpX#l$AOJSh_OJMcsA_ZF%mgUd`EygPB=gP9bSAKq7qUtWc<`;3}Oe$kF*RnRc5`kGN+eld|*s>7vfP@0C<=` z)i%s-_v@A4QI$lGb*0oRpHQXB{TB0m>8govzQ#T~uQ^`9=1E9Sd5GHf?mw@vew<2E z1GT~mzQq%Jq$qh9y&zzk3F9UY{a9y(^WVdl`YB-hW7pk8Ds)ShA8R>a=};4~Y5<>i ziIa*TRpCZ1ag(E9;fWnlPd_&GC;@fX*1s^QPv9J-TemYaNPZ^h0M%yzo$2smqt810 z#IIsRh4K!2(7#DPOVjwONx;i8!(gGf9i$&$ogvqSme!)U`IgESc|!2G-c# z9?kzU29hv^2k3p%dgh+94;a$rBTJW$vAi?QL{6v=&#TV^Ft1(SsdZJXBsvTq3#KI3=EqV>ICL%e`r^ zvVUW_7TQ@)Nb^)a@7Z;>_utugO~I6{=nWmb{3z3mv{CL!l?e&qId7mpl;s1e%7Vjo zM+Wju#SJ~d*Ewx1E8k5$cXGlIVrd~zo6;xM^j$-2rJO#HQyJP4Fs1rg=u*9Qa^{?c ziH#bh6p>x)>zE6+Xf1TaII|fEN9-Skkfon}n{U-aYt$m^0x-E@$^2fvZ@;B>h}dCo zM>=^}eP|9bw7uL>8Z=`4nLH&DrO>vLczIYh<#2#OCG-Jtyde(;wi>#YKCsEM8uHb^ zFQ8#3t-aDU%oXAE!!TFF%Mj%WfyE3uKX7h&2;!j86WJ>qI1-+AwREk9l3d1lMhR%($?@{Um-Mrgva6H8Sr`+_RuHZB#&Hlyg zfV5|Cq?;gCbpJ?fg#!CvO>XWGYfN_e^VLJDgg#Xn(S|f=8lqST>J$!=u4<||;1|Z? zUlf&Lte;dCax<1VoG9IZb5Uu!zL$o9m$RJNzdjE(p8h190OW7Dbb^aLI~AxHO6K){ zU@|09GJW)!-vuyhuH#<^hoA;~bM50LVy1Nkq=jY~zLHjlo}Or5sKc#%7DrwVb~C1L z$}2G};-+Cc$31AWK!89>|M@U7W6hk}zrMdfW_G2P4tLD!&E1S*C(ErP*_E0D+bh08 z*qQ@ZMGGwWr^TjmFlK|gr)NFClhO=M z>F4gg)4Cx9Wf+_mDtvpE_mk>X>uL4%`+3y$tpZsxN?aEu9u>!8Vx! z4!SNT+4H$cEtyS@BX196AH+{GA!Hq=t%7z>f|O1LKkPZIYz`SvI4;~(l!e?Y(;5MI zZln$~zuZ+LtF=PboLHc5Tbkug#>OEkA1T-!yVf0*R)hEsq0&9L45}G{BBm(<0=aQA zZoE%-8=RIh9M7jN7g{ZBlxtZgew)jd?CIz3IKCr4|7mJH`IJ)C&ZEy3!^FC8cY#CT z98dRnE*brtWSqUPmUqRkYDsbud5or2yW(&%hE?!+(^+BO#un64f@&PRF{@cGu_RW> zQ%3GeTDAvLM0X!zbbz^6?dyk=_c1?KhjS=ZO5*=&MC>$-#ry8X?&<6l#SwR>91tgyU_b+spK@YJDXZN1Z>vE5|JI%psGtqUR02WogASK?CWc4JcJ zXmLg7YasERu3)btyIOLK)pX(uS;5mTO0M?xd74hjxL^G0XL2nPuG+kIYd(L#D3?RI zAK8fth93?>JLJ|(0rph?6^a4mQo>xEhwJ_+)MzH*)6{$vwDuW}!D*5ecjlOM(V?z`q#FRS;{}vTiVHVhR~s=8}ut-Jq?vy3HyVsynG4S<9SOvr<;ZL9@noWyFx8H?6>6B@{o&54M^#>y`bLe9Ftg5?G zCFaF+xwQ6QIWQD^0n?ELz)i?~nD%0~+}ZzxpU@{|qjN0&5USvCr;>Z*#1g}$M*UQK z4@lm@)mQH2=7)&oDSVr1C_@fW!;#yUF(9q5Q8h;1EvE*#L-+|jW5UIR%B?0imaKl$ ztM+%IrE+AbO5rY77FDM+8Js-}$u-h=3;0wB3H<@=F(&M`jk%S9L$dz#Z(AYfC)n&` za_2qh1af|qEfn*n+$7P)(nm@2`#m{F}qNv$#yQ($T131~-}%|7MqmybZdOY*QEfkaUgs&SUPTzHwEor4uY}oTR z#KBQezzc6^lCB>VqK^tV6K*&!-_Kj%?P>h#wtk2dJWH_SS9Rx_aa?lY`Q;Ktyr!{M zvpsOqF~OUT^Q2_VeH%+;$Guto`%ItbheK%;F?>D0L_Vl0`GggO3DeAVVf`X0-KOS# z*z|nPdG-)ouGA?0R8t$`u=M*Y!p_7mP+qj?zUx4l3ZZg)aG2|lD0N_nv<;ReJZVf` z{5Iaey@p&xyZ1ZS@ug0y)Jl|uvnS_me>3B4+@?%emt2SHDyj>NN(=EfY0a zM@m-ny~KyLN+HLYp}Ov|A%vtuKV=~T@>$?vhqb?3E}MN*P_D_rI@UHzdzbPv!<|Nd znXKmG)BW^Sl5AUw3J+?wqXf^&gF_ZU6ryuoRsM**GetiM^Ez-Y@qk z{*$Q|bQDqOjQ=EpUcAvi)c9GE41h{hnn7PhhGj1~kD-Op@I%D@+g?8d4h`q7W2P+s zK^#*vFGzOSZtuYQw=yJ;Ao8$QeokH<65}G4IWOGP@B12)r)Yp+5#{1YtxsdFZ}jx1V3PW&tJu&3f~z<27Y99HWI8OIMD@h3+FiM zUgGs!do}+3GRih9*;QFnX5Rs)$$a|r<4aWO@!HkA&wE>OKU7}W%mNn}DgyxMWDl!9 z3(GIHqmyq5Uu$`S`N-3gW^yj-{F#Zv1dWb4`Ih2fR17gkVX!kzAyvR6hgXoz+%Q~5 z^-qn04|frwJgO(JrMVr}_|^08^Zx!PXZ4QEIi_&Kp@1yw-xNVdMx;`;AM;>7-ftze zHZ|T)s}fOVj}4?mtaBQC{YBdNHjYx?Qh0ereU@90$Z4 z`UyR|9z{iX5f~Lm#sIq1SB)}}9JruUo)h8Dj`rSPvbTaar>y~kz~eln+?w?<96IpC zXB0&x_=;EaJ*4J`N@mW?D=v%`B)lc#vf;h5Mx04jR;&oiF8zMLw)axMVRk%GrZU^ z=7*3>ub!9>POAA*j0e3>_hzVkLP50WN^K`*lEkqGzJ1*Nkri)Eag zRB!FXal+NVhxFR@`;wBoV=PJ=S!&2uem**+^mlpO2zNdUfB6U589-i1D_l?$H zi=y_fBzoYvkNR&@QbgcVm*qy5Oq@xH)A_VBw!P*2FdgA$(k`m(hO!73#UYvabe1wk zjurGiHbedc2z2nw+4Aa(d+t!Fk~ix`vpFf?nKoy?^xCPIEapu~+(p?y`zQWXa!aD2wfckl%gSDxf31Z0 zjCaMEV%!=1Ar`D|1D?(P*8>1({8C7pLqYo*f;8kou5M(1sHnAd%Swo%=eD{-8y+OEru(t z{f|v93ir_?KjuO-+z#_gDF~Fca?|ii12;7(OipS@?C{>}*~-xIzPk2X>VvMpF$+LI zr{v}m42DT-I#hS>0NYglUP`j-3(E@n?Am5b{Jz1e=2+`kD|vEKtfsC`b$8_}d8f_` z6>}@XHzrWqIS4ZB@=kcCOIfe~bGY>cu<{#J>*qf?&K6V3yU0y1&qBJO6N#2T1N;jf z22%Op%#|@XJCqQA5m+R*o+**38mH{Gq7@5KWD;;gfvRWaa*UKJipz+d4G}TyFxIB3 z4AepH+)v0KtlRsZNwn(e@d&UPIW~t`4v$sj@-mQNbvfAMFNS{B8F_8_K`Ydmi<)%i zVie2tTDH?gkIw#|4bLGHRgp5_yB)-S&YQ0Ar!48#nH96P4$CH?|QYjRv}*hQ)wH_`47D3~`Fb zjpIa@wBzsNPs14>O?~D5GW(;uOI0OwR;?J)GzTh4@drNT{I{o^lPRuTQrG2tAWDvl&xa2XOSTTWrH!jw4b$@8lnGRHo zy>aJL+qc_V(5Qt9?885)Q5TzOS=JiwLu6I0%*47>mUQd;V%y{l*y%B@H*2w-Q6HR07^u={X`NGj>&+lZgiDlwXJaXh3 zM9Vjk0nGglvIx2In_7^~mR|N)syEZKGdHu%jrD$FUy>p!^rs{m-O6TgIhCs~op(5S z=*%N>4HQSjOJdOzVaiE_K1>-_f%)e@aZ9kgNTGgrdv*ASbZYYi?VrfHj-16Q{y2x$T=`|S zvUy@}FpSQF$)?4Vl_%_=44gnOOZb;l(){eFtZp4%HJZ--ZKB-BuzJqL`0^&<=5QBa zB7HBv8h$_)7942ot6%(~atq1GoM`qkAaly<(u{eG;kCfx`k=*ZN-i z6+5#5D(->Rgd2E$SiB-SnyU*f*WCn{uNf#13s~7YNFuIgPMomZnVXwWK;S=o!0d7U zK*@O%iMYonD{G(7gC7xvEf=e@Zrp@iZ*@;ME5p0dQ&lPI8c23R2eac!XZZdXnX+F< z`N2Nkv#zevHHE`io_TM#5N~;%Jc_?|IE!e?(3?qS1ENu#IWvCO9#_|kx-zrTFVqWy zz-CRBiR3Lo+e9y&=*?FseI6M}vmq{n=O`j72fT@6jQ_tXWj% zkozv2g^{<{v;QGj`=KtDnU+Qi8`v?64xGm>vMW=$!B`#zRJa1k; zr^lRwwf20fQMQgZv0qRBzkM#+u=p(g>#r!o`{~)IH3gbqW0d*rX!OJRx)RA4G6$1{ z9bDQafu{USl5;7_knS}&!I0|uz8T4(rngk39GbcxE4Q0t@Ipp<;?7JMUA&^W4NBGO zq(=R<-1pw^%kKp@BQ=O!j>{?`Cq+ym6?r0I=R}yJvo8y+rOk&A8TlO1VnPFc9{mIV zF7y)Kq)DPJ#)B*^I_Wb;dhM_c4nq(sH^;aQ&)u0l9&zm%-W|k2<#_qH;9&jQ&LMYo zJj4aJB~!p1}rIt~600L4H$ zzdltfhH|QNv^0RJBZ+Odc4sLyu{E`=y_J=(mv*q+-hmCxywYyFm_rGf7-(86UwhVE{ z8pz~1Q74%sm<`u)8O~cd$vN%KAtc+fBt#&{sNoP05!Mw9LBkEas5RVHE^6hkVdDq}`b0M8I4UjSkr`e|WEfkW)N;ax;TkQ|d)X z*p2pp1XCf~C}b>v76X+zT4?;Tf5jx?x8w08qSfgc&2YS8;X%Pcsr{&3&xI$25rX zvgD}*5z8JAJ-d^P(&ci{2%IF1i5N!%u464HnUTB$^f(`Tj=q%T7Zw8%Vht;^Ads^G zCianl=b<^l0AS;$LFTlBRZGu;}Xv~bj>;+sY`I{h~xb)(jX4Z#5;2KnN(W{UF3^uVsR~uI?oPm`A zl=sOwrOzWjY2+}Gwi!5i_W3~14)^3Df>$SjxE$>qkU<$Wahq<;sL4p49oE6tVGv1G z%A6-Z{XHn)~>3nENvKJvm_b0Jp9b>VVo_MS)B!NExs*@QNb*}-z9VzGuxnQful zsz}Z9qrBJ%k%mzH#ot~Es2y>(nZUg>x+wsZKk`BN|KA~h+?ybIAD}Ay9Qu( zrqw5OgPpE{cqXYktCc^6l0?v3yh3TxF?%#KPZP}ccOrOYW!)fB0+N#e9b+uOjsE~E z5F!ccOrqPmB#o{tk$&5??;aU$tz({Zc-FEEun)L-V>{&eRA*|OyJsguWft{CyNG1B zx}M0brNNs^mPw|voHfj`k-R7+^6M(a%!HLI<&%O~4fKj6h1wK`{{T@#rrN20D&i{{ zomf1Ke|03JBA=8ZnMuG{4iMmqmo&Blc&+Y2&f+Duu=`9=+y{sSniFdv6F16NbMI0< zZ0=mEbAe7NyRj#9Sg^dllFOS`j_b~ar_zp(Drtf}g#?i0Ndco9G6qQx!fhp)ue0+b zmf{&&OJHsmD6O6d?_W(9x5~w1D{XHpm1lVjZbJe|BW}Vutme9-GLD^QuuN{F)8w=^ z*2c}Zv9`B)8r~L9v!Tq2z+@II6f}d718yL?ur5kM#AQi!3u}A3fXpOFqqetMBaTat zHOO3y76oI-#H8_@0;fP5Eu%48#<+qgbqQL1D%uu++AhFEGfOJsL>Mkf!31QUc&UF1 z6d`A8l@`@)rfAw>Ev!ou3!90{M>;GLOmj3$vN{!!!B$YqowxR8)2(i?QQhBzALO}96; zmk~~N9Z8*~2qqx{!p|8+8#b$mpO6OHlLXq>)M;&RZOlQgH2WVRZS9m^8LybEY_SL7 zfVUE^K5XqSe)AK68*SZ^-pd?f<4$%qW#e0`tJo|Bk#_RdH4`@SpEEa7tChgP1QBwL zwL(ojQ4O(qBuQlvZ@7rdJPG8y(u9?sIODhS*^zSK{Ga$zj z+FnTwrlFRLTf4QhjU{!tRX$vRyMV}CZB+w}$`8y&2HSUJ)9PN7+gu6bcd)k8Eqv`p z;?fnGE31j+`C=v#Mp<`*u(2V5#_hyX3VlXGEbT1*%GNfv7c8R5;#dfsp!+c*ONIeh zsggG(1{4&}0kKudBSO6L8;NdUk<40mpT2EAYjbetDACMwj(okPm^6T?Rv60eTK0Zs zGx(WyQEeEG##`+=%vldQ;yZZW(q-Qya$BrkYh*D2mkJ0cpO}KxQ20?)`jBZ$aU4+J zS=$tb3&`}xO;J|#nWeaN{nJSxZ;-GGLHU|ff(`-_k1?3o?MuXbxg*=E1xtNOYshXO zmE(zb;sudg4i(SLxhV@Ng?JeSoRGeZ#_le{ ztlo4#X?V1GfkAO^E#8?Va>Nb5l*b~4-MLN+1( zmP?6KP2OXla1o?=VaY}+s<<+9bRo@OLP&Op+#(+&TSzA?psas3Mlrx_ zdkzK#G}?DXN=hX0s|I!}GDp0lC>V7oYiDs89)leG)vKE5OlG>0!pIC}CYCkx1H5s& zYzxMBDfVJQgV3IH#W=}G&~DWQI4nRVgDCRAz$_Q|xa-$B!RNaZN$y7J6k(LA`DQ|^ zAdW}dgNHvU>M{Yz&Oz!j4l-%GqewM-2E<6glmhWbgCi@(Nhf{+hR5*?bDp^a9MwLd zHm_ntxMgIS(6g(i-#O%#G+=&KV~;>aFu;c602p_q-9$=O(GxcIBSmIkFKQ^rJDy+%BM1ucFb*;sh3iqzstMXr@#w-czRb zb}=%NPDkBkTq)|EzF|RX3F?^^Nfs+0lD-;P6Dj! zwpnCBBV|BlKQVUO^ec=E@z*1UmEDZRc&*W#rGcwK zR}4Nv2~f(wI)qT+L2ZX&&N0h!0Nck*8dFa95|VF1VvIye;#f9@*_2d~3cFxr5(vVT z86SsAe$UJ*NinGVjtc{kzD5s{r|)ef006-~GI8ofH3YXJ4$i`~vPTky5jlAj$r7#; z$yLGtsCq~M=ww35*iDDw{}t+fL> zvbcCz97xNC0I>v)Hw8S5j`<8tV&vE;r0$8-xsp<#5Tuz5*6fOUssK($AfDp{=dU$z zlGviV?T>4$j++GTVwIQd z!P%Bw%Id{RxF>M~jOVUD<(>~*Q5kZY4{tEYpqR@RkxW6vPS|h@WmJ|J+m5-w9_7ky z=xr2|(6bPk-sTWS+2QjUC6Qg&BJ#mW+6L8g&m4kB-W5=HRu>|@%JNHY)4(HWw=~-) zS<1*1yy`#za(QLs3}c1#J1Un(ZCTivgFI=4LllZeA($lPM$T89U=TPw40===Q>5m} zk$ja772ub9CLo!vRzNM-74Gsn0zShr)Z~g&Add*5Siigyr#F2WG!hU zuu@hnszV&Ih8WHXZhmUopHk?`(?@^d#q%s9wr9C{BX+nl!Fwr!#Ue*}10+DdnYmaZ z$eUEMlaH9uB__|-_-y5KKZ!?6VOX#Nz}U zDvyyaDXkB(hs2GUtnV%&)ShTy)LsZ?i_EfNvh&UIfkulmG;W?-l{|o|sxYn^vZ))I zN?fL1nQaU+U0N(KED+|(+TU}DZQAT@a_TGp)0Xe~a&4Y)G6CwYlfvN2SQ$R1TEZrlK;Ak&S}q?=N@G*aqjTZXXvEYU?Pq?6o4 z_O9&me2A+qP07cZ@~&44fDTl)Q23DFzOINa4ZM)q17jF@5y=-e#Xi?+6gzDrfixye zx|Ls){{Rp;I~0PmBzPlG-L}ikB zY&BK3o&k3>Zz|r#9vH3>0GXF|`4tRp$Z@#kRIb6K9o3CW3-)Kz>}?{N;TXXjtqgNL z&Hmx`k%p5yQa?9nAe`k85*4-^Cz2ytbe7CR_P?|<*)7G~_T;Ska3}2O9yu65h|I{P zHj{<`=W*N5NdJUY65qbdq}O|gaBhh7$CA|=SD=_ueTlYA*jjd%GOTIrE~TeS$x9; z!d*_y<&jhU8o_eqk~U{#K3S48ge-Q1BKGDllHqDYn>KH}Ud7K!KFJ+$U2-)VPs z+(P*=AqkU{k1@9{Mw8K+!b@UUA)Y&cv)*Y-s4bc#&l0uD-brx&T0+x>!DLB6Qb1wy zuw^3zG8uOR-dy=m+S#U`ZES7DtbuMd3uvRd@?zMB2(0Apa1S6pPCzwkZ7U!jBE@HW zG-BS_*xbFebA6}Ed4OrR+)Upy6^UCLa1JC6cV{%}Ni6_t`Im1#qVY=`MyVC-);8_- zxLKA17>SDsHh~nbQUaor^g_I+89rs(vcXrQqH8zr*Kzt zTN_w_y9vyVHl5J{n*LR?c;?gYVQA7wniOxd-88}cxY9xMsgDlahapKIlbRY&Mh&*^ z%_wA^Iis=?>8~Y~h-<0tE+M;H=uz_=VqP3<`E$L4ZB+#2hz&NK(NU65QogMLxxCYY z#b-IPwRzy0`puwA(9C?vV<3gx0EL1h5ZEB`wT|H_CiE}8w%3|0t2Fh4NH;`|d% zY3@pPcRq6=S-}U*5{I5fY%3N}&yCs656ihh{vpBO4kP(0ZR|!usM@NKDL~l?wR(au zN6U=h=hKe$6qVhHjCqI0jq>en=_VB>3sCu`yRq>+0DxpwWqr<) z84Cf^AAdZ2r-9eg8K9`ay-NtOyAtgbfk+GU93TMXYCFqAKj2iwKY( zB@`iS$-<{BqwpBubmt!Q$26~UCiwut2pDZE7z%#z+@$B`$T{`t+;fy;eOUu!Rd!_& z#Gom}s0TY$j~G1UVOGV(bGY&elIRF^)5V^`LQbZp?@&MMn;hM>DWuKr*&Ult&o{ zt0^tUer^e%a&}ih-f-U0#_{F1i-4jwjZe>vM5sZav+uNO!6piJ9LS? zii9(mI{en3}t z8>4L*1Rt1lzyfVMqUlApXkeJ7o+pwp3#*B-A(tDr?`|-D=-O3)8@l8Qx_wP0O>W49 z$IH#Uk(O28aL*G=lNM7Oh8a6Y&Qx*hz#_CIRdnT{ZaAJPfVW#)c6&w+kSG@m8C)w! zISN2-;jxlHB!fapXpWVx#mL6#V1_l3aTEqiLc%jCR5@ZnAxL5u1m%ZAyK0?jE4v*u z>AR+p-DozbAiP+nvbNMGL|HBFCOhm1Dw0hgb|j|OlxznC#8?#rBd4Ax>pVY6#|*A? zx5DoN)nT`ZCX!8h(i>^lPl|a!S&B?mrVl7jKWd@?3NZwgz{*u^qCUAcq?#(jZ*g;F zsNPz^GFZs#roFzR9y`5EY>+UPSDVdLSy2geAzCwqB!a8X+k2F+C#j}ki%+}rwE;YK z^GdN>+sw%#IhyD+KW2n&k(11Yf?IFJ?3WSS zNjem`RD_0H#U!lwU=TX(j0VXSqMCb_uP3RfZl`5=ZRU|}?KKJPg`Kvng%KSS$a$6J zcgZb;Q6SrdUFUHNnr%CyNAAZ!jx@Kru+vvi-jdkwyqMib_IV|UBuyBGHS*%~g0g@@ zkdB>9pDxtfo3kOeCuCQ-TZWodTY)Tkee71TPc`khx3*|m?ifzxw@Ch6FN92CmGH+V zu2Ht6w9~U3mUmJ^6Pe1{n57cDpJ7L|ZK{6PZx~ix?~tpKP9$vQn1hW@ghp2^#T|lN zL3L?-Y-64L`?pKTZKIhsh~g^a?32~6w}FWs$qo| z*`<@tkom?#8+mAqVqj+BiTTrDIc7FY+r7FLqw_x8_Co9kmN{Yw&T|(KfhM?C{TwO&S)5L|DnGBq>Av;xNb>(}CuV>~{jg`u?M`oAN88tUk zC852JG?~r5mo$byDo~O!Vx*##9$pt{$0RYXNRn6*%?qZW*E%(=#q?96N}gVx_P%I= zaKtLO^BI6h7^zLb9DJzQnz$=yT#`wlSAiPv+a;@7+h5wj7`(eknWK(UplnYyqkP5# z5WxNAAW&pF^GuwXZZEB5xp&iEJBGHAZ3Hgzs98C3u|Dm_MqSLIfx#IfnsaYJD9H`f zm$5^42BUEs>S=2=)QqOO+o4gL}Bc1;W0-Jv2$QW zwZPt!+@yzdNR1H4k&4Iz606G`W06Q{HtdT+4br})=6!d ztWn$L!v`t=2c005?hQ2T$!B=tg&NXZE33nN=Gm>_j@Lz)5WyU0po3MNDYsSS8A+DI-RJZUB%nqy+#0wo%U{@!LUi zt%yEW`xufaqaY;DJi9!%f1E#*fpA-ud|-l#X<3piZ9MRxOPwKYEhW2vO{c8rt^%Jk ze$9@Ip|_UguD}L!kZCr`mp3!O?*waeYZP}VC%tgO4MfI~sq*(St2BjJ?8f2%KQYY) zlS=n3NpCVi6yX3jH%%kNjU};yNzr2R*@6{Js&vlQGQdVjIGI5=b27E9q~<~9m=-rn zW&2&YnmK2MZRKc!pEK4|hIS-lDo890`#&+J)4B&YZ4_cVok-0Uwk_bckuD`r=gUBs z3{TFcFs#f-4UNHYq-c^_B9z_rAAb}r6xPzo0Jv)vnEkb%CS}~J*I}D_w0?YwqyUA7 z3U-21if>W~{{XaXo)0CXmI>}5-*F|o;kG{CGB=z#KqQhG0(sdiBwTbkQ}|e$vbpm| zn!(t{cE0wAqZ2a%NXsDrE;7TvOmooJB&Obn@^qlvvm!ZC3$%?oaEcsrxE$rV18Bxh z2VSR)z}vF8)MsUP4Yc61p(1VA`F`*xoPrPg!>Q|}B$}`^p5T%gkILoPDf1&PVQdnE z9f>^hKDfXnR5y2XNGrRNi8CT8MPJ@wNgH|3JmWlLIL`usD6Vc+Im4C$Yl(c8h`eH0)iAhRGas0od8$3g zIOG~>HtdF=-sEsIVYl2jmm}g-C(Pb*a;?vB0LUHic=>2)r)Eo5b~kNwoBOm41(o#5 zVlzzE)>GU7sxSjE=W*Nt83kK7%||pjR81)0>(z%m5un;8rwDH|Ow|%a#BUQ?%BYQm z1g`@dcMNfilgC_=oLrMz4mf)0``7!2zLBVg*w*)xNU^*@o)TU_yHR6&u>=A*I0OIy zGw9-N-miHf5*zlDrCZSugN z!#gg;WIzcj#fVXoa7ITYj&sg>){a!(>>W9+(<7Tqx(CgYHXGmfV=9h{uNo1_$sF;H zeqOZW$$bUMKFn~_B@QN2I;1LNjeOt=palZ;Cya7ACoRt+sm52e{{SM}lV1xhjWSqc zV;N?A#*Meja;VHzy|UN@ z4hDUVXroPN-80+Y^o z571(&GD%sT5vJSJ86|)pFz+hAW|CszR005C6pnBgC5|}+{an@6%^X^HW8;oU6<1NZ%_TE!9TR!V*IC7{KXTr_HJzN^Px0@~zd8l!?SD z%Q^&UkV*2Mh4Hw6tBh^Irbk9m)#QC)sfV$7K!5SzW@g!7_wtW1ded4AL-Q zM3_*yJe|NCWaq9oZ%xlv0~pG8JtxD`!!C_s8Mar{%xqw^XngD2#$P@ytq>^`!=1_) zgZri+ZTXg`Eo_)Ba(BL8l+XOtZJ)JTgbJ2;O`Kzl*yv%Q_FWek(B<*kk1TCFz$@8?(IC#-ZOG-C6x})$Tk5R5c%5V$Atuslr1@UMl|ai>X*%==ZY4a?rte z?eeYD8U-ag*eBV2~IV1Osz62SV{J`)r1;=b1PoL z?2|qi$cnp;U>TKKMhr*@?SR$F+?%vblTg&9EuuQX4fUmzLiy&qxfZh|i7KVA5X6Ki zV;dW1EQ+cI)aQ;Y3gYJ7C9otc4GzmVP3`T$MqROqcVpyCFbgQp1&aRwdl}T|1n`2_ zQ{4T&Nt7eYb#HGRYzs#uE#$0DfVdyKXxIrcy9>Brti>kmz{gmg)>kvhd?Q&Uz16R6 z(`>RvTlaa9a|C;^yGyBTY{>i0Ex|p2(vbxB4>^lbiw$dae{~2$9r=dcOGZPxZIOt5 z>$O-gV}|)nXRKHik{SG%L^@-^b8~1K?A*&7GF(EpLGGSWw`^+d$zaMym$(dowW?}J z2&RH@AD+t$5l?=SeTAfo;s9c2+)Tu#CL4G2hQY|_1x(`aa1>i4cF%cjE|y$8R;@L* zqSkRwA1Lt!d74KmB9jCuQUVRcl{J~9W|oZ6x3>FT+;(=$du8QA4Z1-DkTkOtc9u7} z^JK;z1!p~1_kbJ~1$B&5S7MAC)Xjgi$9p50?rfrGk~>+1+4CWbcb+`305Qk4_>Fe* z2qX?BDK}zdxpy`mIi=dR?G?<{wq|>4m$^}IZqd7WDu5Dv(j*K^DakuVO5(StXsa;> zqOQ)cUo1AMC7k!4Y`=y$-ZZ-Z09Mi{2oc&e4AP(^t0)5wF(6Wykr&w`yGi1bS<>C4 zR6288K^ik8tg1hD>#=rjNd9zB&fMYX3VosbuGMw)B~AD@^^09HY&5e&=X6! zn{q2yt8;N+&_F>V$~fn>`!&=P%{HYhK~7OvDhw+w0+PWabJGq~z1_%yyST|9wz-l^ zFS6RKgsSQ7Z)GGf82r_iMk(e4xl67(s2jdzP;s0jArgG4!iccNRhY3SB+Og8xo#`9NhO+SZ&KZ!XR(Z|cGFu#p+qpq zBmit;X4=RAK&$w@7X6Ga^JPk1$1?Ti*lG|4TukpnEz7mIGt5;a5e;gGNq z3>GNCN{ubXe9p-um5xCqh*U9IREY7r027c(YGH-Ar zBYL!t7C&~zWRf^H1Lc+5>%bgjA90Ee6Ki5s+7&|F6%7{BfRZI34oa5CIb3nj&}k^Q zu;j|^n+!uR-xy+CGblTAA^Hti+YK^;FV>L0Az)xXpy;n#DG5O02~YhkOu%7Qf;QmpF{h%{{Vwz zzp^}C`wM(hy1vo0yQk9s0BT)#OtzBZ81*Zd(0FgcQk7|0f4w6R2LKQ&$dyScG=8PQ z*cuhEvz29cq5T%Og8Uyd7nj2RA9)Y)K9O$Be;t*Zt5VIQ?A}wA$wj(oQ{Zn0V;}H} z_UR>!aS5b)!X(6)_i#C~Z^!ai0L!FF zAA!6b5Bi@AXb|!L04(&2c*h+&B_7_P45M@>Q0AhPyT^N)++ZB;q0w{%cb`duYr6U7{Yj0 z!{8n;G>dir0NE(|#a`hIhbd1=OYR!p1n^_wpTfQ$Bpl_mYi1+=03465moA#o<)^KYfXK_*Wv#Ff)oWF5b)Zl z7}_*TP@ha}r7F{svt`30%Ha9cCHE6Af_xtUA}$@YN~^PDm!!~7vmIL|+ZGm_@BJQq!J#Y;hp zDUsRbGpsDEIJrY3vB3?FM>R1v6kh2CdkgpWe#`3Q+1HS{SDoOw(jA!_^nr=;&(t0v$a-&p$AQw_rehIji}y+i0xi!VkgE(1a0dq? z1F7b&F-__vO3ad0v5nejrhScUVu(h@Yne=^?=~2cepMaDP~)Mkl5loK$|)Lqwcr|{ z*Fi?TW&sNXUNKu26F2FD-n)w~Wd^&6cI#(cgh z(HUEHg`vk8mIXiiEn3U*o!GhPEzIZfPJpM23m`iYY=fRUe6>p{#?MD`J*6Ik{{X^g zqcZ;h$E~O!6d$`Dhy9u!RgH&o{jE6Ye?MRFa94(4~yRft;PMdX^=-_HOyEcC!w|1^&gjuALY7x#Mw!Mfx z+m=9!aGOIkaIDP#05=6#42|2yaH%xvTAt^%DDxIe=$b>>vxQPMd2q4z2sm9Npc~K(_Lye<& zoy!u%9Z==EkVag>E;W~_}IW(KpfNZo*Yo?8I8Rr1(w1;Oi+MAEgu zhEMGAG}iOaYV*TjvBK8cOg74rLQ`yFWn!|b$XLk3YbvL?H4%$w)0>ywD&N@Ld0_pP z##ti0jpF{whC^j5O~i=|zZ;t{ZJhmZNZX~OACYy?G>ApwTgLETY@5y+F9UlE=NL20HuVx zvPs4zQ-Eq?B%Y&A>g5>axnXl_0#9!O%{s$(2BiQIkh3E|k+?o&phg=kaf8){+PkSM z8NprLwx;rUn44?co4GXTpwnT!o(sqonolj%O|%>UA;TF6DIyP)KGx^=xx9!Obsn8~ z@mxylHOmLNaXqYysHD;&+r-XLg+>o-%mxU|h)5tZnw2>BM9GfdHaF@ z8G@W*G6=xNM*soFG0;|1No;>8&Ao{hMv07(qJ{)3NFU}7q$%mh&m;kn^L7-Y7bZei zb|X*$k<8ZYTHDSehm$AnZCw8V60UQ`2671*sYxvmi6T4X-JSBHH=nSBhGgm-0h|^m zoNwZy@E1wY@u^JkjEM`q)i-5Vv#VfAXBwY>q4gko`a(ZVqQg(|yEIwZ9C1d-^{{Vwbb#bfz0Kq?g zHq3;RAMJzU6)MLp+3?4Mj!)LUex|kM{dYf4;fgMYw1e_LsWHj7T}%B{_B-K@M-AP*aRntSAKFwire+g*@{OCC^lTyEc!9+G}f@b&_cm zd1YdA<+4Upfy-m%&Os!Q03ea;T(gDRX!(k=ZIvU00pt%U-XJZm9bQ5$V^$a-5=h6( zj1B=jfr3;O-OOWnl7S>}?qp9Pok(LEr~}JoQO+}i^Kzq)ag(E0Ryqk+dTxYA2v-j2 zBD#f)1o_F!;3|-rAo0gR$0$ zc&eus-Oh??NsLKjiQ{Pg(G>GrmWgD7Ld9|eBxtx`t&*)7QZu(C5J1r;-I3Q6{sJ@OA1^f{YLBkNyp!@zvh7`yhO7d88&SH};9Tv9v)7z>!8YO&PX?7{JRp=aZcG z#d;ZJJ+nt%KBY*(PFo+)^~a37kG&>J<3Bbr(~iFN@53*Q`X4`EGmRyyAWw-9Mt^xK z;BmFs5PP0~8r~Tio4Q9L;yQ75Y@B>dvyhSJ#v2$aSOR(K1zO>WyGxf-Iq^0go3!O& zAIG-Gt<*qgyOWHcO42+xiB)xuf_zh#q~zph%j0-kl$tDnHnQ>$9Dn+&c<}XS_DmlY zU~i<%*YTF{N1Ch{pS&=0{zYu^DzSRNYl(34S4_>H#Idmi-YTv}F(waF+wiM~S2(*k z9RC38BqJ@%=kYW{1l_5~%8>X|$9$juy>7CqlUL=?boh=gr!!~aSNs=KSUQJ+zh^sT zl-&q*KM-oGYA)6vKV&u`cLM4&*>k%jfsEJ0=9GS~Ia5DF;i~SudD;BXYrA&3tk&&& zYik6zQox>V%qX&!Qs|rlS~&2fgCfU`qU4ja2~AU6k8^Hq8KVWGiDJ5*Id0POJ5g(Q zGrh{9;IV8b_|;gts47*C)fo|4NiB9XP*I09%*o=mwu?`vMPzKkyB z2147uV9eFBg6c$7q{rGOpXV*QC8eMGXO!&>MrOP~@Uj^sWFgi;F;y5)9!&oLtHU)G z)t!p=H*Rh&)(KT?HK#2-={;<*-m^8K6xyxfF<(QT?X$&v9uS%XxcsW@OU!2xN+Mc8X2Kn<}WS z8m{1ea6#RIB$kL1Yhv5Ve#LIqYPVU_)h{m_%)FA{&hw4PV6B4W#25fa+z3F&BKLMK zpHj3FqT0zUcN%oi+#j|@Wp=ML-{~?JB1B)5Gd94na;j2JNC1T;&AAC(*p2UGQnwd2 zvBPTsYioD4kSv;dBP2p5D&VGfx~Ai|3`qxWYXKEhPb4-@k|b#q?=)y4S)zt~6=O2F zIA9DLRCL_iPEB;C*2a)(_7i=~@lLCCB(_qdP4=tHXR(>2fgV_c1>3NG@!NJ+0YE=1 zoMr6%$ZfZFSh#!ZTbwq&%DzC%{}6R(#SA z&AsA=QmvGcCnaZH`3K7+9ORM67#Qv=pINi{Mp052P9&B#Dguy7Fv%NvJmc=<5uA4E zQ+F3*2}!|`k{`+~vT0P|gGN|Mg#(;p2MRHqT(6ehO-8ceO8Ig@ z8c2+89k&D^Wx8@WIp{O;^{9u$h?H-t8m#EwI&PVl&A^n9?9xV(7fFGJJ(0}FSUV{k0vL>st^vn9;@J^la7xs?$gQt8p@NTcKCx&#*Yg4tj zx`OWdNMxQna3V$_nIe#a*dH%i-kldyVk%;No3xM4SN{NmhyMWJlCaKg=l!TG<8_2h zy?hm}Ttr6Q!~-3xaxg(2qPKV&i>e#N#Vtz_{{X?mfAC28SgL;2dUpUEw}3oM0o+&& zAFzM2X{-%aw?U+{3B{1QS~mSz2{v_y@UacO)L;y6n14K_rcWc~ur9o`PgziJ&nQt>B>_1iBE=oT<|AH*y*3++O9?X4hI`xXItC5?AB6-#nO zDPSr=UqdP8l&MnowKiw$sC9VOKyr+sf$YHZTypmwy$;CA6)35#n9|3eu5c*~Zh6Po zwQB{X$)Cc{^7{V(f`^iywm||CfV?s#VaG*g_&T5L~w2SO9WNPmOr#k8g`3g<}9=*$c!DB1Cn!- zo_3t&xHJ*U?_$VVA%#p)D<3gu^W$Yik>W%f7toWF$j0R(lY(i*qiRVzCQBLfq>?qc zcDT0L3cko>nLvDpNY^M3;z76X0s!5d)j@S|+xDcmyI3SxzEssJp;}v#Rl2{hik*|_VhmhS$2v)jMV{#CM0YZXf1mN?G5=}Fj zRMgwK5=IzC>v3x=@4oGAlI4|@FwDsuN}!y!KqT!96a_q2JBo{E8-$^;&uSNI_DIC> z%je4T1`jYZ5b(-@x099x#&Ngi04Y5tniaU8;VZY@JfgM1AU zVJI(_`G^B6fC0k%z>uQ3>U>E1jI~Xuxu4m;{2K$R$NvBfXYuMIEOFd7?J0IrAu)a5 z@gdO(Ad{2dKSN&jNhFd??#^RUbW(x+1$c*6xP+F;3^B%dC$Q;Xe}?vK{Npi%)$Vx? zsp2d6r(wCH4oVZasPEJA{cGqj{>3d(^KzwEnYPb>f8e3NwFkxD*`wgkfxLC_wqFg; ztZTm(ZG0uH$Kt&*eOA!eTxq(6)A>=yGCD}ITf}#qfC?TbBvO$0v(jPeM#wEAA4{{Xy^N+dCaxm9Or5U>H8 z<@;LAEk{1uD#q`vV5hL5S~{{RirTb))r zDW-zf`Z;3XIz{uu^OKd0;w!nZ#z00v6qktj*!z;Fxph1;z)`2|J+8;q{{Zk?J!VM- zUk`jz{{Vu9Xx2LfMWBv&PseaFouKv&;v&M;e;!lEc&f33-yccV3*P5@xB2`hE zMgwd|&cyW4=kB9vV!5@`e$8=i@-2<^pqKkTS-0Fp=E>&< z`=O=WlVfOil(9JtR}|KUQIoydOx|1#=(1ZYJX@z}Ci`Wklk>bm7(O<%f&sXO-WO&O zagd`l-HVLVb|_w23zeB|);p`1V=Zl{EwhVzSec}Bb(k_PL6SvST#yldQhCJ+ZLN-y z;LU9cM|Wo}t<=dHOLJ)wf^L!6z#($z9T5tGBad-12IMfH(iIsojLtAg+`Fe<+1p=RKAk(qsou)b$zux3_9g*>E-+D*<8?>D zjD^4%=vyc!dzwWi?8_HW*jmVK-+4wdT55iMtd_}VZ1Xgnc~62eV=TvVzuFLIC3B>k zO6aEt(74mwKrWirBQ5({wcPfm!L97$+~r(mc*_&DBMBKhar_K~9%l0b!(f&zH1BZIx8scFV&@- zNh9G5F>T5?F$F5%51f3!oC=4;h$fcKSm$kCYbmZ3QPm42oKr!#i-2Pw6w0{XNlXIE zfLl10lhK+}cez_+OK%k7%TT$r`yHdr_Ico8U?Y_SPGdj{%0#9A0M&q1NjrF~TX1ht zL0#N>Tie}Q&to;dpjcfs&DHd)Zxh|^f1XJdFx#VFEzStoW60d366x8yq6h#gLDjyV z-p&{;<@t#YD7;mg%tEpw%9&-tHrPUjQbEVv!D`eg%{O*QWd*s?Y-57j_f(Gc zVrgzJF(Zap&y@>&xGZxVWUNGOC3gZqA)<#?RDwIFwuKDZjlHXBuF%Ts70uM@0U@OM zun_D;OsK}jEt0E@>U1YqyCddzQutW}tcxb}WuMFe3j(J*d2hTrk~8%c&k3s^%A{;% zW-6fWM^K3pl5luXK+hcdgV&HM8@ss@f^On4`J_)Sa8PXzlw9ym4nbUf2`3zmn7A~q z*^to0$sd}cVyFA6+++dJ3=Vp6)PdHaHl5K?n_3QHF_w$U3;Wo@#!P5gT=1j}mN-23 z10eHA9GfIX!YZLxI|DE*Rb;{1aB;>)-VmtcoSrB%a!J{XBS?zTD>xxRLmaMtP&m#Q zbose%aC^`+)7Xb>QLg29)T1efPU`1HC&-#US@Fx1YnaiL#0Jh4PUYZW4%opRyKzxbd$&Hz zD0iG%KfV6|_%p5Ce!(BKxF;n3**c_|I62+m?+1E)E619>MvuSBb;VTqen<7u<{W~0 z72G+%tST$T*!Ew+%j{Hr*pEn+m2T*&@R{{Z0bol1CrV?WvBO+^wzeew0K z=Y^!&u5I+We7uf_22<9(3>voQkC_SN5c?zJ*?8{BK68!@x9Y$Z~82}N_L0YJ{syU^Lmr_=W9mgAX9HQHV zAVzY#cH^*B+%uELI~tQ2W8~iERpc!LNs>rZXyGUKtuVm^o(K1_xM%MJQqy}f;f$1( zlhpS=*!CZ^e%Aj0vm9PrMStOccr|5bI~RBM{ey6%WS@~n0l{I{ttBp0oVL{Hm40j+ zjQg+5{{UyWzF1=z7+AvNsT)A=UNn-O+4R&jqZ@V*u>*|c;GS>|ON&O5l8Dr9Mo7tC zNj%n(i(8WBxn0ll{Qm%ggfy%BWq#9PnE-E$n(=8&yToB{TXy6mf!D4-yZzEf*CxG+ zN938+M)A@}_)0k-jpqpA7_6)eQW%v$NRxm@I5FoVs2-q`#W#AEJUiW{JY2To#&rnR_+_xhD z6GYOFv#jP7h{CZVx=N*^Jqt~YD;6!nl73=vdBROSi>DUegsxUd5;!ek-45r13FHMv z*yCuPO`~B5&f7`O^MU}|n%)ZWRy|57D_DP(a3XO6S}fNu9#+nOG)cKkjU1mMHV@3C z0&%+l0x~Iil_ajw0!XY~%!LuHjFH3UtH~pPs}X5cwqx%*ZerNk*j7qyZgRGktj)Wb z+1Rzq#T>B|f(4o=B2CNZZWC|bim$kCOz?6@IAbZbu?tvPZgKt~x%(ZAra5Gh!wF<) zqlil|ZB)w12GW6h){X_o%!2`AHt8dv`;>Mq8ad8Ke@#5y^bWUw$1+;A(D-EDy z?r?bNU0HnFYTwr9(oOVY9l6O+vCQ)15O>jH_ix zDiFRw^_IM`|HH>uRkLPMB9JDr;P)h>G{+n}ga=QhZxfdcB)Iu`b zn7frAn5A&1<;{L*jX5U>*i+dFImyuZ#qQ>h`jw`l%msO71?_^{%TSUQwxeS6x)RCW}H(oC|8tx~p8cE#! z&;I~}Vs*ne{1sQ><*Uq>hV$V+jiVP(qg%o9X)!*<3`-I6leC-xgIPDtf|K<8&MZt) zuSu^n{VZm;g7V&5Td1PBx44C%o(ZE_p`JHVuN$ctWnf4k@If`>YZIzZW{>4x{tAiz z00dZ*PLstSv`_4T;rZ^Y&8*sojXnwKF)X**S!VNYFT6AuAKe^pdlkbWT*6A9Eq&Yu zIxVU-ckW&n5Tfl@WBGi&w79mA>H3TpR(FEtBXuRs-Ot(di-wXorcIa|85nNbW7?}I z#~=ae$~>>g?P@buUkT00r0TGL#I)(^mUb|2wUj%Eo#J6SNq~VLD=L2V2*{B0xw%a= z?95{xQ#9O^jyYk`Ls?pF)1sc~qI;AI%M@lPn|?|22Ji?4&ek~vX&R_qUge9uAuM6h z)g)WjnEktCQW@k@ktB~WI1Z{vl&Ni?G%Cv2L!CXRk!`5xg^)!wLsN~d*;+u?7qIE@ zOK{Q3prr7_G{-2VcbD}HK`Ksje)i?Y(s~slviYte5eT&fL2CkBB5Co@F}Qg=!y2>5 zv8R_bgzgH&so-t|5v6Tw8ItJ&%cv#YyV$`MfQlA*BiJw$soREjlh0-Y5vC8!SOs&) zyK)=-I_T+_vnyIV#XdVomp}KVje>hyo%o5=0~-a9z~l?j$0PDx|!ZF5mOXW86)z@FR_8a zlne%904P0dUpDP^Wel;4d2eNv-p1xhQtt9pMg8HH3xq5pWin%SHi8h~{Hh5%JCh){ znl`ZyFj@0)3XihOF54y&#Vg1OO~F`#fdOZjZX{uAIj*Q1ySW@|Z7swx*j!I-13>Y{ zZv5%*1V_qgF${_kokJbV8?gwW001PX2Ymr1_AcB>Z1Gw{b9W`I3#Qz>$|Z)@&5OK~ z7?dM(mw1$pFfcKNBXzUIYDPGS$Fcp za6uXO7Er+>xceX47~5(SLV(+Sl&obuxC+Qbg%K2zM$!Wi26IYDwPaLXneyy}Fi@-( zWXpMuLdXbU*d%&m_;G=bFVcfd>q3OV^pjFL_|;}vm=wx*GaYV1s4i2}5WK}0b{nQ+`R1 zgYd5rzu!Bbp=A#Es*lt1Kdu1490I%!c*(5fuNHfCU8ix5=NUOPMk*#sT(jjz@|XVr z2Vvp!xm?BQ<|D>vD`GGE-foH4>_(T}^vNzMTEVP2L39MfmbaXuwsCrV4q{s|Mp z4FeA>ZH;9?FvjA~wUt#lBLs{A-`J5}l%Uq9$5zd!EfJ&t00^nxU3|w1gvf$1GKMRV zM&1gM*VAzrBxbObns!D@9XYMZo5F4kS%zqo$B3DCOCtT!TdC{Q2b}HT_soZiT(`N# z>v|lP_T?2^vH)dP8&|sp85lWGpyQ5x^Hmu09at=6T=i$Je#F{pPy1?q$#yLm0`vBk z@H9>wHCNuho^JzT$zOZmKn>j^b@SA)8&TONBb-(0T68G0EO$&5B{}zk!isD zdJ%N#$Iy+N9fotz9+jY~bRq}m1Cf@-ae>x@DQwU41pfeng^6YTvVUn!-dtW!#;tZ( z<#keyKHnnYxfuKG!?`S@JmS8p4Z10_@=WSUW2AgCS%iY-DDD_CnM&M7B0?F7bwVF; z+HyAlOJf9%-0nB3j~ks`T(5ifjke5%Rf2hn#%1O477j-X_le*Z=yv?X(I~jFbI7u@ zO6Ze9mb;9|xn)_TTztS1sD+3pqaRW;!7^zXDI1YV9HbRD62Me`;Z-gg=V;2bXK`J` zBV+{_+73q~iF8H9@@!uF1d#m25#fph3tBP|g)zAmN}RYThvoh(HctSzOcG5U^s1?8 z(4{qsvLTuUx=3PE6{N@n@ws(m3G;2ncAdN&s|@6Gag18KE!60TOKo({#U^odvw1KQ zipd;njmahs(!VmC5}bpO#1L?lF3x$qY-LYs8BvV0!y#|AnHEMs@*TvqfN~BMz$o~_ zp}{%KxUP)nQ?j*>H}O(UEws`sjIp!~<_0D}{{Su`Ae^1VCnte)$;(t}*B{o_(v+ zl|^eaTRv;lI7im6^vlG&)2`*TxQGKBkTwf&cwx|kx1p}w9wMe5ccjiNZVwNOgN;S* zpCkVO!At%lN#Y;bm*Mf&&{|onrp6 zl&y6>Zzszkgw5KH$Kc$aG}F<|%iLO+=ZQtUwpSU6WoaZ*wx@B7M;_u^<-u1Cv<3#g z0xd#GNuPS@-fqlvUNTwQ-rj9p@n*qnrj|Q(ws90h!f&$RyTKnN;#U$jOtrqi&|G;$Vs zr1B#~TS+u|+3jy_p)sq)5Oo3fut;Wke8=A!lqi};^w2KW+a2;Mf_+{{Vuy{2f&$B$MH9j^Z$| zJAfJsFej6YkT~o5RL4vl>~Z3MjZ5Z#s0?fCk_U6%yjeTDn?HpQDUdnj3}o|3T`dWc zQqcZ>{{Y~szwk$Y4*t=4cE9oK_Hfbk{{Rhqe$%Zd)OC*v+C?3vkMTn>sB;Knv0G|e zP!yDHNm*1#LHX<1!DH@~DvwU%q3tNk_mAB_jGceN+W!E9JV}4y?+|Djj)meq0!b{q zOQh=pdpqqe=_VHMZFdpa%j8Y5{G<#bUUTv*FSRzEta=c%wObh134eX&O%%GMMj59| zITkZB@{ z5bihGn34jdq3|?yEeZ97nhSt^3ei2EoBr1b`QG)71_%?zJYjiqc3VhTX*0 zanA%2MP%XT)I5|4EyGEGb{+11O|HKtdo5u7*;s`J_ZdBolXrOpkjY~O&9t%Gv~m5e zLea+~#Uyd;E5RE#`J@n87?Ln6X(rW>+#1-*)FJ-Pn&K#~A+V7x7|8ciU7@>{HS*-T zQhdb$P`ZG2xd$g{T+;dN9cp`U%rZ(Z2Q18i%IkU=;k1VdJKFa3zD zvRq8j`EkhWEtSf`%Vi^^r3_%WQisfDJn}M1H&Oyub}T)s$}eQQdwD@wG}XUtGTkq2 zOflq2QDk5kuH-SP$aWb7Fx&%f1t^W#wljp=Z*ty$+M$j!rrle#%n1;-1Z-P+u~1Mf zh<66apj^sM=7pWL!$jMny9+Qk)15G-#m(>Tl}Z_;UEg_Ch+%~Yg;GYZFJo_ z%k41Ekv!J$eY)m-T32}|m*t2Xd6ir@l^>hDsK`A*2DDJRy}Qn}d zlDz=$k`H_V#&KK@X{L|mnYAqljBz25tm<9Ilu?L0O!#`UEIY%Ur}C3 z9JAq3e7`Ko8fH_Iy88=VbC7yxs_{zpnqX+YpPkQ|c0 z;OA*!fz%Goky$jJO1xDj~k`=PgpSp4}{5*^w@t5_` z@uMen%94mB2+*+dt*u*Uo})6UkyJA5U@+U2a8Jwu?M=-WbpKm+*m;%58{1& zS(L`5Lt^-LHRsDRuNd;9^^6w|W%V%duGY3Rm;MOR{{RHnQds`Weh`!QizyTMgI=9M zLzOsd;y66ke_pNJzx7`)li1Ra{s{N~00i5BFPHl%d_F9Is!1irsVu-S!5hudZs)&q zOY7D9n!XjwTDK`)PxvFJ{1akT+W!Euf5W9v0{pXTNJtxq{{WYs+mGk%P_b37bj+6u z<&b|4zNIOD;EX@;OsSAZFYJx*)=X`E*Q%tyiR9R{{Vs- z{{X=&b$9b_{{UmJhQejtiJ-gHVU(ax{jBJ^`;_uk=;9M5$Yej#d>b{-J5)b$9w zGhwYRuKH42LF5_YMvf@~C|Cd_f0alFcOd4Y7mmeFHg7|0EgUQ*%#rxJci=w|*-JFy zTfs9E7-X2l%#G$8D=7eAHaSvAKrlcRn zjGPfq+n{MZ&s*@$iQ-F#iYXy`=u~-eNpS)_;sz#636!f6tOgvDz{b{B~Xi=U8QYHCL-Q77k2b_bN-h|yp?-_HYbbWXK z00qh-3y;}_-o^E(jshJ9}h~r`ka72FIvevVCp-bb~aL{i=$0>pXuhO9G(H`UQV22m{EN@rVzx!j*-SmP|g;@Yj9?BET}xk<)O*U+g#+M-!o z?An|VrQDa38&$H7dtk6g?7%z{$+BcwO0B$d62+JW3cv>1BBwOfk#UoInL(noTcnxd z-EyW4I$2G%wahWBsw6SWHwP;r0|YT*rArg{h&5{#n@;FfH&a<$$fbyp8gnEzP%_+k zqBchIl^DvvBk-UJR!k5BW}-4l>KAvpUCqU$NF#>YJ9#JjdDE_A5Xo@}w_z&@HmHkZ z#$?(^!QJwnqEchNl^##L_UlxZ*4}H<75wO}!$GpyO~%2o95XG zU<#LDf%CbF23&Fm2}#L}uHjo2w^KN4TPA5NwHudGSsE!LQcA33JLE9^;n?Tq063+p zVA6UrcC^%*=0(+^5F(@L&|O>1Y!OvnIWD4jfKM&L>|)7~vPeKUYC!$n$!(I|=2v;F zZ!Rt(mr)QyH`yW6Q*vx7p**(AA;4|HnD9_A1S$^iL*_k-8_podC=Cj;hI707_{TZG z>yG3a^P<~VXY#2UG_f7YmNG6;#D+E*11uPQ(a$*Ur@d5VXP}!)aXwUp34(-b&<$&?@UZ)HVHExLLNj-*t98{i!5_*PC2N^xadQe)DZpZ)w zr2`g|S5^TBQb@<+oYK{3kO)*0o-^OQ4k^2^Ftw`)?(O6Zppcjc8TIQ*JkXM{l%kf$ z#J?H*DwA2>L}OP4SQW=kxW#+eSvM4}a#WH|=ip!MP4F7m#aA~(+QlJROP5=GoP4<% z{7)y=yJtpqKFFh0Q(o#nD!fViB<_;o;?u%A5@Xv54&aO^zyqhv$4cXt1vjz0aWk>y z*PpTs<((t2a2(|u%W@Y40l*kJBN_g+A5wh{zOqZFe#ckROBQThkheLF(BpR*{Be?V z(vPV%t%K_)(O(b$0Aq`oX19`UF&&kG`JoCIp#vEI0BMK(2=iM}!uw5L!g!rDKU2N| ze$7AF6WGkUdZ||cVm+*IHjm^@Z7Ok!X&9>RTOXHy@Lzj7tuMzvv#o)R8reJrs5-+x zmdP_mb#4aj*}*vj01)ip5yvUr(MVW*YifTe-xe&Db=i@YW){!}^CVR>E*s{{dRBNST=sO{{X?cf3$zWKiSv*3V-m!#Gf6!WoO`h55=Ak*FG9}Q(e@5 z(ez6#dt8dz*G-R5nEbZz%lq45D;KZpj46X>sApI+bQE9kI8eL8EM|4d{N)7H5583&RY!RLhq0KI9dJ0M{S#ExJe&2ua zPX7Re-?uM-IwuNCVUIqA>VQafl(^p=&yAMkSl^2R!&FN)2&9WJHYiQ)b_E|`PT9&4kF3HUDd2<*c)x#gsE$#nD61WxVe(%K|k5!M|(@D z^CJ-vl#_^%0!}_+*(YlyZjxFdsJ3U9xV(nw+0CcI(ZC^P)D~IZDHt5?mv-YUj3ixz zmddu}Rw@R60TPY$WT|0qF)XrN$t1dAj7*I!!%8iu*jiYX$R$;PSrZ9~2+rhalQxsl ziZDssr+YQDGZxYm%WA3ids{ZPku=+fCj)HGHtty6zj#Sd0~~ytn@1NNQyk#QEvJe! zpUjR)(q{<_ZLgz)8!Tc82{>1rsxufuw$@x7q~)Qu-PtjVPg2d9YlMpEHlcfG3Ov^{ zL3II1A!yNj){v@_<~e0Xj9}%^oug@F3G=3jEtzbtwMgNb7-x!R)Gy+ACAxt?Ke_+~ zKf1)BTXAfd@}Pyy1!)v~u6(SQRe2gl;vo=i&M~y98394S#?zg|3?9CAkw@~^wNluG zM6Lh=U9lbP(#L>sRh!cUgdk_@PMcC@6KeJz<=Izig#^1Q6=Z3+KzKZieB62|>zdXw zR@AvkM2Snf0I<10ca7HzlaQqD$bgm22ZCI zN^Nd5o7jR)#zMd`^P4+-uijw#o`hiI9=R2wcUKclJ2RZtJe9YfF~pJ~Wl(dw1R>?^k$`9M1QjB+^V=}M|`i$2RJl$~fMe|JCdXC5E+ zD*daNV4wJE;&|!@{31Rc{{TOQc(r~b^sJ^oy{P>U>+FnYxXv?KHkPdPCamsA1_(a9 za5$p(VvBc3{&4>Q;O2)3`yPBTQrioBRaOjKqj78o0m$U!0nbt^zX5kv$oO74{scd+ z$MA6~w*LT~nPUeJ@&e6}a@hx`LIKYor>9m)ADA@OO|6%NmceBNef7=7%mB7aHAj$;k|qNQgUpWO}!2`!~x#YN0BWI zOA$beDME@yLE2RH9-MdPl5K2jLE2|~{{RI40FDWN;G!R~6mv=hPy1K+5EKJ>%J_!R zfO+8VA6oNsdTw}MXHHw6af0PoRWCL9pX?PM++U@6a7s3cKTw698WUZKLXrs|OjHbQ zTx8aS&)|NC`H_FYQRQR%bAHn9T2mB%6*V-KqzLg6vqykA&u(xD!5I71^)ws$yidh5 z=(m|p$LA=-Q7CB)tmO!3ok&0=K&Fx z85Y%5kcxAAZr z=4`G(tlYhKCKTOr+m&M*3mE@2|ABpT{@`xDLBRHu$BPSLqAaZClQ&!aY zhxUg50D_Hv#lNzLjNtfv`$K$q@aMx17~NcB_K&%zifX4s>GY5C&Jz*J9#4cwy9a6gV}wds=L?ta+n3>E{G^3VH(hpay%+2po(~l~iRmUwtp<;qJ|+JE!FYe*lRC}4%4xs0FT;z* z!Q2+#Qh|;JNBYZU{_y<0tJK2F4^4a1)aG)7m67@5@dy43MgIT@GBMuL7#d+0n)^>@}7+rqs{z$*zg#jO4x>X#E+&lEsxd&_^`0N;2F0#!*QfB@W9yD2Ve9@UXaUlFF~@&oZg)t2@p zwP=Jg+clgyjyYKm$>#ES+!+Z_xhKp*OZ=qr)@`*ro|1N{bL4Ln!!n3nLZqmn40#93 zSlN#B=N&_GHuUH^R~+=Ic)fwBIQbvi=lmPt{{ROaoejJt`woA>LB1e6>5KMn3w#9q zp?p(w<~^R>me!se@kQ%$W8qfYQk;@95_t;jk#>Xwz#*u zw}R@{Xs#`;B3Z5Hg~X7|i~&|w91uYS5yv&>&Jv8Ac4+K`<0z?LLdH12S?y( z(pl%nrqw6WT>cN5#& z+Y2Krte2|s&ZZev4Y-X6LnEuLx!pG1sTn}$3{1A(<+tq-PR@8>hf=wL?KNm6v59S# z=Ir^#BOK+2n!h^;%CTTnDcu?3MGR?5P1y=*wvs2ev%QAF)+23d_g1L|irHDU)!0T* z?I4$O3=<-^AQl^bY@M~FTT1V8B_2{eM^i{4v%fRxaNJIzC$>u@TYWkvP-PcVF77un z`M5C>8~v;eB-3|AMMdcMFJ4UEY;rq6{{SADeLl?=AOwmP0Sf0R&kBsl>9i~St&ojz zNi7MKEvcOa%-12Uip+t~YV9;pc}r~2qq9P&!M@`%$%D34@7|?N^(4l)wu%TKzm{D- z=TMUIVV)>~j4E7PTQrfCSk5r}-A>Rq%9~GFDjRLxfvC~UmP1gL9@XwK6cMz?SGO`a zEM#<1v9~#9E}M7#sq(`v-Lez` zmAJ+gNZ{_kJv#HB&b);gO#WB)eq>BWOoMcx5C`)d+k$tJHkBKXPMH228gYY8caGQ4-l&OQ1ZWhghfaFe};%8>3yzzV9t z8NhG=*x=xdl6QB{-5!`4C1qnqlhK%3v}LU)E@Lc7C#~`rRtcZoXZ#tzxc6+zjz0X`m7$Y4q$Ef0}4c^94R%xHj zkNh0qTz<(P3BsXuKNKG-00iRRKk3Fh*Q=L*82C;(U$}w%AD7qNE77SNy-&>NV#0ug%ku_SXpcBlz+x1yKu#AtW99^8K9s2moAT8O|_$&rAbQrn-xj$3u+q8JFx%z_H$pBnz}}%Vcw&MleSm>Z4L9 z?83*Hq#%=2Z{Q^726oZkh>QV2*Z@aPhYp1zIpRu?L*z1x5kR7NSaA%Eyf1 z%Qnob>!2r{XK7YcncSlCVvkO`nM%x&Wj z=NCCls5r(k#s)L%T0!(@Ru+t;{qx8)sS&(q;dqq*^ZZYyL?cq(U+*;=bvZo=OFTkI-;2$`#>|Z@kR(w%g=m%Y3~b>Y_9GHos<$PB7UsH7NB9`NOZ+E>YcNP}r#~s1I zJ!=_BUd>sK@AxA>{2S@}M}NUmd_$*vM)7Lu{{RDjWGyREQLp@8)wHG>-;V@O5t~l% zjg+bd)Qqgk*UyV+PI;Br$u|ha?s3({QeV5H@gM3(`xt)2U$bZI3-C)q_zC+kd^gfQ zANU(bV|l1g0t>$rc*9VURcF<8y+wzgdv`9y23eTxM2<8G8X_uutKl=J8@tp#xcdJ9 zjX~37mEwO7U+{AnxVZhQeiTOxG0h&I@R6<>7Px3`SheBLlE5}33arbK^5b?!4SgOB zb7l5_)~6N~r;M6?EPo}R5p)@^4A*icz)LucL0ag%Q2yyyU8uZQ9;X^ zKMC{o{kz*ZGKJrT1wg{K4l<-1em#0tJkyI(*q2SsdpEKDkN*I{0YBj6y$(QHVGgUKYdTHjdWM;*TTdFx z4EFP(DDk>7vbvH8Y=d4+N^_wmsx3~+b)#N4qHR()3Jhed$Qei(QV8|zDb7>1iIjQo z$+6;JAHQe6g#Q4v*TXG$;Wx%F3tIT6!dm2cDCFDfdNsm>(WMmdr z42;U!QLN}mNit&PPgrh$3ZL*$zxXDn#Qy-;L&pzsa}KNHe}Q`R=HI}-5^Uj;&f4LN z7hNw`NQ(${0Gre^lu4BXBjH{(`V2K?S{7%qgvLU=d1blzi+2vFk=n<5Y;S}*oNp8| z%QQ}rmEN}z%2j|v=j~0y|VIR(M@$TQg^#DO}q^RPZUD(?StE0wZ4q-Ur%r%SDrT}T&!yeAxSEH z(UHv#X>3(agy?3y7846x{{UyA#iZ%82uJ`pkjpv-W{fV}ISQnFq-5n@$Vt76t##*H z-QBcS#?(!56wf}(n^V1Gl^jpE%OOaTF`W5fakvBWfYV!<(AwTu%jZWG$VQGk>$|uX zSfKOSGdiLwvKPX}yORWMA(g?PM6tmrE zH=;YK)o-OlkL}UHCA>`b%ja(1%FdzurP|NQ0B1BxJiu8?nWKi~S_?a2b#kwNDoq+B z0!`qVVL9CAdm#ilCkHG@teR=ri7P7;#b92+=7vZu?c+0EHjE*L+T6xf7AbyY48sz~ z^8!c(NXF8no99*tLeAGdSOkN*7ESDVLKt~@Vh<-g4cI+$Naka;~%anqdCJ|qn_xb!Gl2LAw&K`arO!sq4zzl)MRhbJPg4eTL=C_D!8 znC_2hWr7eCU=T1mXXf_g^c65ycQ2nyGD&w#8Sv#Mz>ZjvaB>?2`k$NE89fbZLR}F9 zmia&`>fo%d(WwNJ&~zOzI`{Xirta(wH0;b>S~g2*BYdkmhE1>!lmc^-7_Kpno|(^D z(JMC2id_$yJbdJ($`c+}JKcHTf-`^s!2JI3KU=9Q#QNN&Ul5W%yC3*7c^Cc|tM;yC zZOs1wwQCaCz$5+EAK zDuyqbCEijJ;x>oMAxI^-&Ix54a(O>rVy8p%8l4FVbYN5+fxmKDVTDxgJ3%9!{ZH_c zIIPUnoL!Lw$c*wcv^hbw!@O%0drFl7xj4Y=2P@QMRK-~d`{>~OM5yq*k|_Yj$K@c# zIlv3fKAPbvm^83z6-=hFZEX! zeG-fOnfEw0>tT6hf2OQgdWMm6VXA3Yml}SdW3pR2yH+tx5ddQhI}iS~e~h)bk2B%U zBCDC@wOnc`x2BsmT2jMR=7T=@_#yjE>AovAz7_Gr(CJ<$3Lvx8Bw+=Hw8H}mPYEf{ zLx9Bbjs<^x_$Ty>$~dzPPbT6zsu;zhZTw%NKCdj!p;7xRN5$Ow{{YNgp@S(6fDU+W z!yl!8S<j0FK1s^lt^`qxoCBQ3>(6gqb7p+bI-WP;o$@TP?ek)0 z^8>PmF@iF@o-^|ta(M01t}?ca?ZVV=W_ezN6rb>#_((?)M_(6da_`%PBx*L>aC6gv zTsYt0v+OBUQo-_{?SbI#JSXA2jkwV5G7nMvi1@DyqDS3e8v2wMk+b6-Qu#o{XBpRu8&d-p%lXTly2@Xvs}H==km!CoKI zJSpK%4QY_*+6RX;4L&U^M$zqHQ5-N^+eWI9qAGwDKo!#5V;e-v4?$CWr&tCCGn&Zl zj9T1U3U;=q@frUB2OPhd`(bzz&|1SK#D54Z=e2Wms4fFu++_>}513BIP#M)v%BO-& zeJ%yRsjus~$AzI4ik(Qa`H1mvhVAZcZWbvRypo%ljm4n2TWI1tq6xJOd1EYliv}ze zlRqMXHC5G*ZN}5nW96?Nco2y0+Vp{8L~a2Nz$&rix^T|S7l11kj$Zn8DPrU7YrlKB~YeRfKC1N3crQ5OUB;(xrRvM?S$4#y&0Pt#zy4|YX>H36DJE`SeNLNV^heuUq zV89X4Mx^OJc)d*1Acj2=SX=+tIBk=ARBea^)WVDXvDemQ( zJwc^yFzEpdj*x;1qK6@Xn3Mjseq|sE?UWtQPDv=}S9mWP+Rh2A?=E3|K4|QJbYZ$- zHo=UMwoux2sN3=-hMn;LuaSXA^WnI9p z1UJo`6}oe2U6luCV?G&ft!-9Ty_N3ey#CELHdi9@0}q+|@JSwFZ{3M7!IwC5yb=K0 zZtSVIu+wgq?*S=s1;RxY-=DQ@t*$0uis9n~%PfFIp;kSmFh1_%NXT-(k#l1*-9>P3 zZ0@7ek#lQw5l~BR3ZJ;l<=vtvK`D~m=mE03%`L>ub1L1KE=sNY?^M$5L@~m|eU0NpQ=R1Rky*m-LmOuWwvK;k-p#WopxqD>tMo1ON)t!&!AsRMK*@>e8gi^4zSc;MSAQR4VM;XZ!fQ*%6B))8T zA&iluD@i61$UGM3o;r@8P(vck^m

{COJ-5CoBeVH+pgi{1oqFVzg1QX*Wf=V+^W_eB@J> z87CgO$oClOQy8YM&UX5oG5?cioa^Z#t-dOzyP~?cvnEL24BXX z_1yh0EB-A{k^O-(j2=HO4Mk3evNvGQU=Ob(pIXch=HLDfX_XW9LHKB{`%Iq`8Ajkr zoZF^yM?u)1OxLTIM(q6m0FHly1NbnMvJie^9rnE9W;+hhGE-^iCm202YuBmJ{LZIA zgoZ{_6P9NHyqh-{=s{u5?x)w2-m^L77TwVl(YasTq=nwvx1AgRC$a`9Q6ZlKu$B9eLsZuo=2mGifulp1Mtd8VEFy;PT`9K7sc-k zlwtDc{wG#|A7B2yde_DHjUQ{mSjn&Df0I7f1lplx(cyd&uFn3o`7?4%b)tB#7cf#&AOpLFvtYcTY0J<+!-wu(ENK zUX3$!scIIl*YgP8j5B9ZY=X-2B|SRZZxxDn}t{ zW(XNj%nKIWGln>PWD&*=4r|#^O?z1Gc+#Dgh8Cmc7L`$iVFR9bN;1R~w0#ELcPcZS zRT!zc?s(^kCwWwp6wa)s6S<$c=ZtL_^#1^44{8rn2Mug_$A{W9@vnrQSk>c?#kyY5 z@)TJB)GbaAd=s92waJTV(P!4-BAp&r231%I=_ zKF0XR1d*PZ&lDXZHZhTozM0Jce-GdAayFX3@Y?l9bswOveAi%B8ah+V^#W)dO{ z?j#8h%a4?F)KO~e?2aNaskw(YgJWN`OJiiLZqh>}cGmJt@icKHyVR)4A!bsBT!>h=4h;+X?R#b^3NLj9Tvu#BRnIw|=`E%bR2MS6Fn<~(Xwai(* z7TvADb8o0fu|pzH3tP^T&J${e#y;>a3uhR{bC5yJsJC`!3h+;ImB+(GmS+;_(~#@u zN|9O3F2ba54jBs#+r~~*kf7rk6*J{#s}mKoGS9+&TUFIG{Ub{7-leQ-dakJ*hMi@m zX>#6b8m!kc!jW6sL$h>}F_t6>p^oA(xUPE6FpH~D6?nmW??d}7{{VtC{{X?bniqkt zJSY28e%@Xax72)N3q`MdH~pP;bX)x|!VWe)wxi-``B^T(K_qq=WPq?TlOWk2hgMZF zbuBF^uAR;bu6UVg@E_R?m6ff9wY`;vv=(-Dmg#M0X>Ah0Yi%5A6(f!{!m6r(0;m80 z1$03uN0u8JMlpi-e&XV(TzigtVv>(7?Wwxf&7t*01p<1jAHzrd94X^33;56W*wA#} z4Qu*VsW*fE71`*T>siXWrIm)Lmv>ht+Tzd@f-`PVXyrOxy;lfAijKWqO0;Emt#Tc3~L z@Jerq@L5~UeeoamVey8o0r;cvS5dxZju^`-O{{ozNO2X)nb809TLMBlf-c1^aJ&67ZkL{{Y#?$GTUKd;#LiG?UJ= zlt-)hcJIqaxYG4&LVU|h+j8M$f=~!JBXY8{a@C~Z?zSBqLaM!-^pW)F`eWL$I+{{J zL6oB6$Xx~iInH-;?Np_2dF(=pR*CaR?T00tU$W=N_r8uPZ4==>w$sNtESBz~##4t@ z1QEdM2(L>mPI%ZZn*K%;icNBEf0^?|Bihe8L1AYj}i@0GY!k zM8p&%a;E1h4lz+Fr0fz&J0XFDqT>{d4VoC{{Qaj>;giv*x7 zxr8w+-ez0O&&;d2j#Ppc<0T$}2X$lQy@e&RI{5Cp{wSrvMY+lpIjL7jQ{{Tp32Mq4T6pS2>2SN`y z>A*Z1wC=8BB-*kk5fJjZkf6aai4j;4f#}?l6a&Y4xO_+=tbn-Ti$)k>L~Of=&&oK@ zzg+rbzBSS)sV5b7IasRh+@*3hd90=oN$02X_xvikoV6x0<`IM}Wb+*iVTz){i2I2f zx?tlVbUkzJnne}bU4cW$*ipA}a~nQe?ven&;}{s{xZ;2?wQS35RSJ}7!!eiu(rp1w z4hN@CP&(t5-QCS-D?1-Fc;IapK^{4f@VHQcGmeKRJZGjxLF-XbS!#WbQtes{{_sEG z&O%4*Tl-ZhIsX8Rsb=lp!ny~gcr*Sq{zvF}WB&kZn)LjS><4HkgVb;jTIX`vGvBa< zNH`qvo|FaN$MZ%100%nC(SFH436vpao8qG*0KrH=L2 zz+4cZ_Qrkx01C6G3jOAJr^E1z@yFoHZY{Us_l8N_Hxys(%Q23>%Dx}O2W}C?{{V&l zO#3Vx+oSg%>NECJ_Bb%OCf$FFfs`5kRsJStxOZC{UH<@jUzJ4mxkCbXRj|)=5h69JX^W( ze6r@<;x88I9PWi;UFpD$lxL9X%1t->hOb4NtqP9Mg) zL}4}-U=F8h4n0nD`d1D&_)gtRi9A$h8^oK)V81SPrulfGFd-m&w^@5=l&=wBc1^1v(O5kZ$5;q z4HeV#{{S+>e!#!6Qw^>AFMJYg0oURFpWCPWv-YL!>US+*=q{g$O#cAEK0jhMEY15Y z{1RqxRf~8E#h8QJJ3Xno(%6XLXsufOO45J9I)7nlOBp|8{{Vq*SYfw(Ke6Y4Ke_Bb zDvxU;Wrm=yr{Zp0AzoKz9rNAXYlv-aQHRhKNe{+wE9no^p61P@M~I3jtaDv zw3!i3f(tMLNK$YbJB?T?W6K+(wr3t; zG7l_zSX?FFG`=FG2_sIkjx2o&DCDdH&8m6mteKp>xsx-1XTs_HG zRCvzzWma#x;Ikcr9_&?>jp;P3#I_gF%c@MK8;fh$nqRZ(G3;Asia8^WM!DQk!z8jd z%o}$1B$jo=!8F=tvrX)C&lJW7nrL7)_jeFlEP8nROi`mi*86_YGrX*5F&ct$Smc!= zsxh{RnY+6lXX0N9T51sov$DLH+Cy=BBU{-pgtplK0C0=6l8)}Xqy%I;b;V6eS6ZP) zE=>7X$Nm71%e;wTfi7BbBv^LLIu42923kl4V_nOc`bDW?rjjeJ|+`Cyvm z1T7B0$Q6rAGZ5ecPp{q{gkbTUR~I6=3Q4P;A$MUtgR;z|d1v=zs9@j$jAJ}zx#pB! zsJO*RUMK3u{1SWq3hnzs{{X=%zBfbTuZEX$c&Fi4itg=f{xJL-)h`}D4S43pIahS{ z>5{g(j8X0oULYlR6>#Xxho%)3oN8=&c$_Z2(x1eCtrzSW`+WY+zwl9?fLagj1^YMn zzCRH7ZuV8uJX4@uA++&N!@X&3;JCZc^_x@vs_F%}iL#}ll_ZWxo=DvtxoXjjUC{9L z+~vzX&$P>R7{@p#sKrICW{cH{HRfrSFtIlvGMQi?SCbY>~Z@^ z-N1_^u>2m^3~0&<$Xd!p+k?R;BZ5G$UnovEh&TOibJX{wnf`7Uv9vJROtDXEdXX*U zQR&c`ip_;GG<)2QfC$t!-d-$?$2IL%l8o8(blOW%63SR0M~2qk;ueWuYulSLCs5*sFj*=$5G8U{DvjGNrf zz56-5)7^}6S|ge6rI{nYyF_x+M-(G&Pyhz(sa5$#)4)4Qf6QV+3dIey7KsG5vPV2} zM_`jc*5=!Mfws8cBkeyjt_d5sJ4jp`ySUPCV@K@B^T$2Kw71J0v6}BYqn=q|VI!Gh z+6b3(uipLM;{1`#Y^`k#6czO<+q{=CnQb)dD|@*k5Za49LMD5e9!O+I^Hv4-wxkkFXgf3*j$@0MFCjtAfLPt2m;zn>!atULG z-V-E~X_rRgQTA!AR9K`=%9AwWV5$gZ0}Sjp5%YcFy5`=(jIS?K=BZaEHId^ff>>~= z$;vUqbIAuMJqMsQ%>CSc=9Rn61V4Nu9f!D5k0aY1aMr2joT}*MXWmdohoS@DD zIqlTo4teL+z~^ftC^h5bkB;A2S^D*E#-_9W>RNN6p?gl1Ew9%xVV`1%EJh zCyWet>Fe!LR!u9j>vDy9&`0;1{{RMbDE|P4BmJk`V6*?c8Db zu4Mb0`ddfa_0Mcqt(HG|uj{GsoN$t%51ITN&XB1_^3``nAP|_xm^k@J1pA+I8Cv;@@LuLllEV6{--`c`y>i8pR=z6*%!5cjG3fT##jFU z6u&lm65PSKw#y8H<8DSqM{4-ob`?~UV3pmB-?1w=AuxB2NWeVT9$lA3V<7GJm*!<& zn{l!HGXCAL!w2nW@#@*2Xvp}T_m4b)f7?F;th_(LL;I)0 z;-fW+la|NC_Xb%cRYaMiQMJ?-WRnDw&IrPm7$?;6U$HcuM7kb*pz14})~6X`vC2f= zYLAc>NdZ#WCmncv)2Z0nEzx*efFm_eIAMrG_!6%{PAEk3+qP>)?eNF}*;=hsp)BY0yd@bR`^w6zA z$6g?eSI@^pewRWk>g&+xKm(?KI%Nw$2s7LP_|taiSMMdGLeeckTTq~OmvEphF5yWu z^+9QO3Snt?0s-LDO{a83)LXL}cN4+D_3KE#b|a`Lh2%)po!L+k=4bWx{Q%qT58bX*I=^u-`#v0$kr+LnFYqOq0n1q-(ql#KQP4a;%uh zI~KM{MHdM3GdonXdzF?u1AFBN7F%brWtJPYw{49k56!fY#HR%LV;SdcN=dy98c|j} z$HZP6w7<8wf;l0&A7%{AaLWzE2#n1nk0Pnd6=@S70)AyJgU1Zuq&`k#f0WwQ*Z)fGfGN=hOZ-iC_*$7a;e9Y(N0n{Hptv7R$ta(}SpNu>oBq=QM!8yZ# zpcOIgIbf-czrsMl0OWE`1#>&Ub}K?-%%#l@j}N=Ddud%=3M#+KP73rL5Bcj{t~PdC zmeOyTAL#M_00vd+7O{TEf3>ZSK)g@dGS>2Q=99T3@b!#w@Ob0OamV;qL#HXLv&^d% z98=}k{?KkUNFj{7I*}Uy${Z3p8k%&ibQLG<8&NrH?M^vnGTO+pINAz>jojn89=)rw zG}kSfGK`dukU!v{*HTOS5Ps9ekVzu^9M)Z;kw}hGw&dV|a&zs;_O8so-LDmH<;Qe? zocdhaE5kBJYXp)a$MzqwD4rtmfDjcM5)dRDWQ7YU3_!^3tnV#PdOIT9SWa1^7P3R+ zTtL?U0A@(%calbPIZ#gR;xz+y(3||G4Y{a|r5;mg#Wi+81ixsvmgX4k{M{<-T|jmD z1UPVMBz zrHOP&-3q@|Spi7(dk*_0nQ!X75OEO92s)9gcwEqARVVG0uUxGWS zjE=W2dG@U-lIqUdLmZC2U965Gc^rmT`Bj7 zl{}C)5O;3N0NX|m<6PuMR)`1tW||}W6I?(7@AF|I5amo;)-_YLm6bP;0+K+^Go9rX zAwesjGqbA~7ziEr1`bq^SPWr-?g-9!9W#?$(N;(DqiwsgypuXctG$>x9(y8{MImEF z7#x#=2>|j5!Q|FSHtc{*HaIN`BLe&4-t0F3Ko|slI&=8d)9OrOqAu-{?%f{tLL}Ip zTBr(63BWv!+z>jBxuTy@w4RJYs&W-Z!r0uHn{Ei?k`7KXI`+j}q@qlteNe^P(T7s# zr~+ppbB?5j2hjdii*38JA>+!8-6N19g=Q*Za#^rI+!$vBHyd-)I2grEH)NO*Ki>$; z5qR*d&VXZR01SHZjAMdpXEe402VMQ$qM1;ZNP>XUs)4wJj;A1c>#)v}L6 zAA~$C-}_X4RrA%qh|kdStNn7X$o{^-!6(+a(z9&$o4QC^V4agk^H2W(2RWfXvHt+W zDYux!UlSX5Bm=q_h6x=9P*0&X>E*4ex*re5%{nySsr(qs?vKF=uIJ2_CoI8$&O&jF z4mk8|XCl3-iKdf3CaF6;N2-#1#onwC9;{wm0q9BX*Qowjw`Fsjjo>(G5DA>O1OkP@ z$qX~t0NrqMe-V_VNq1O1LYi!l_!1{Z>i@W4C9Hu zI(!+7Dr`R)JTO?}Ehcr_4uj}NPs+YC#1D6a@n7M8lRnb}D^aojsB$9xnluLrA^?m6 zKH#tMpE%bYU;Iz=XSXHHu8c`x91oOo4sw2OeJkfG(v#iGP`LVhXZd+-_a=d~tGm=5*roY(l1AX2A z!YA1HJkhs{mCuInA+`w~$Ym_53$ikn3^9;P9>X7W^u~DN9!_dWvzXKn!o?Cb zC;*LBMi`aZA;V_}0h{I>cMo5iR~EHpDZZzWc&0HL?J)vl!h-4!dIlh$LG9D&ij&g9 zyE~q9q1_9A7kF0dcvQCWW}9%WxlpK%{@Oy>_QM?U)7rT4w~ZZH_8E0aN}5Rj0BX+$ zNV9lH!xBbD*YNd|q3eKcq9gRhcoA}y?$6S&+^&RIk<#G*0DJTmUeX2oId=|yTmJd~ z0P9sgGmRfjkez~f6d*JJ|+6Tmsk6Lu{=^i8au8X=m%_GCa zsFK%BaB(HkD=o@o4=xf+-i8*Ei@dix@Yrr`WPdLH1Msq3TnpRVcuY}59s(nY+G8eK zDTZ7YV6TE05?W4BD6c|{IaS$?KNn7dvp&@D$B=BrycX9IJogdZMP(2+=FL2&X*}5? zkg$z%zy$m3NmIFNbu|}ec=WN6sVBOaW;& zqPr0DqUJVGyFyAM>4N37`ZKXsT)!TSbXNCqywb&MZ{*#1me6W{XOS=0&WwiMn^?F= zUPa&XZTW!Me(y~*lIF3cI*@QmGFCFBic8yw*lD3O)PrrdpE8X+Y3f;i$#BaojEw4D z6q9PPK*0H&yR)CUW{v4qq?@Frs9jszK{V3`n@+Wo&S+A7HZ-@M{h8kp#9cC{bgJc5 zPU$g%aq{Hh9XX$Kx0*3dG1SGD%T0*EXJ#5pREEuEfF@_2Kpk347A&D+69QFLL$Zyd zU{lXDtZ5XRvF4iZhLY;hFXn*X$M!&~UUr7{p5{jn3v5CpMF0#H!g*jER6F~dmO9^wn!i{;{$=y86N)tjdK!zdSCEn&ZjPu`#S#E)@<7c{4RYKL{&Qn&7vqg zegeG=D)xyUU3fK7Gy5deHPa|nc@EYj76ax4_0O+S>(JLiprc|Oed`#Hs;bP)^M+o3~n2aYU=hG0^VI(nPs-Wv@s+PbNkr-$K@x?VpxF%_Z_%W zb_^V3;8uSDFXCS?u!ia>H9;itS{q9v1X50@s=MP^O~54rBTNL2!-KbTa5<}tl!@Bc z^BteoD6k12iv*pNk4g}T50S*%T;1Flx_PCa!i98z|SX;KqEe!)^qroF`90| z6^Js%8PT$c;elp$+UKDp^;QH9zQ9sRw`5aE?gM|8l9L!sfsWIFg!6^qbRc&gfDY)+ zP)k%o_!%nY$O<;+0m%=NI%m`me`;wru?IV=h|RMALdWEg6eBP^pO~M=sXYm;&a6Mg zkD9z}-dax4Fk)~v?csV8j=eLV_z9@2oObAaeoOp#AKt(G8Noj9*l+fvw_*#$`&-=3 z!x+Gq!X6k={{W)ReEnHoq>s^Ztub|9r{vG;{2Y=!D-`b9J+`cq7MPFb>;4XCK;N;4 zf!|HM-QBfkcd)to280V8~R_$?6y z1N>p|x_|&2oqJE@_3B1H;3B>!#5?~0s#v>!EBueL!6vC|o4z1^Jl#sk&!7Ro#{1hCM`(FOg>*pkFpB6RA5XJ*3&F0Jy26zOp2fhztEB%h}QCom; zYsmP##V@V9pA!=~jZ!l5eWjvzUom0%rBX{EQ=Uj5dUWhb{cQ4PmD4%vsEmpcoWzij z@uLR94=r4L-Eu;Mo_hKNROH@_=ZsU;o^9e#fV_yHk0_{&l1|Kw6S;HGr>GSteji12 zeDUHLC$W`oZKP2=Qk#gR+5*N^x2eG!Pu=gwB=cODiYlcoS@u~|jI4f|TYtgAzwlUH zCMY7Z{jz*mmez5ltQOz!h4@^-9BQbj7Bi;pz*S&2^}*u2nBd}~-Os$k&Q?)O?Ck#l z;N$Q3EIYrJ{{Xf&u^e%!W|RIEkAoz3A+lF5q#W>bj<_D8hliK*7yIsv>9p_oILH1A z6*5i#02zy`Z;cMg55j)}&aJj2B$PTX@4V}b1Ct{3ttsJUFSJXYRjOxKY5xER7ykgk zU)RWz>-M$0Xys7oTj4i>MoR*r8*Yz`?*tK!I63B;!cO`~#n-IPs?Yuo8vg)-wcHy< zy#1~91>LaSLl?rI1VG5AYLYRd=LZgQameGUn%Q8a^inBQttE4QPxvqA{1tNOt0(Pc zuK6BQIWc@X@Pr~qz>@+&rKrIGxX#=u1FmY`7AsW$00U_Ic_W#-{{Vve{{X>Qf(Ewz zuKZrt42djgJ|B2lQN}j8`LwVw2qY81=cpAv!ae^05BLSBSH7&GKj7fc_$x}5+522; z!Y0U8xcGPB7TUya5tkZaUD;ryXM#IX>?7as{{VmrHM+4I{tgZQ0D`zAn}6Z0H90qf zEKJ`H{3?9dkg=7*uA7h*QM+dqBN zM2XT>m3GmnUNcbDC{4w}% z;*D#=o+X=5@h5_Ge-Y{eFBx81!*M0;g{{kwi;#-mVF6KBY< zNuQF}(JVJI9XQ6fHq$%HdvOHUyJb5|LnO-hCPjiaEZEuQfE0B<&pDMUbTKkimWi|d z_u(AN6`ABXT*DQt=LpIAw3*F|cd)UzNY;5RW+E#%EcL%S8{s;mBE&ZRezx)J&Ovbu=T+!5w^Uud`@VY?(_o!K)OUsGbT%?0bc@Z8F5 zBC%Of<9M$fq~0;*$fqle70i*bX5YZvz!8SHSoH!g5D7RWsD=ss?}>WyHUy}^~yStL;q$#AT}fKY*W z1B@1I@Ns}Tn!%>GCQ@%g1GfvXp(_z1%i-TUiR1nSo=!T7LW{E_ARMS>3{GW|LHn@B zsY1UmKVE%nSSPtvIv85mcC|a#X=MgHkjS_{HhITWkFI&EG^Hy_KQrcE9Bv6P2&HgK z<*=iXoO)nn^*q#6_#Mx%$(wa!bNkQ#0E0NKA&2%P{i$!3>L;4__O6!NOEhrOKp^m+ zhe&*N!CZOltus! zPXoSs^rtU*GEz~DPv-;v4ru`X#U2oR1`+&4LcpkE!^%QOJ%|J!Vr#bnQ`y&xXTkBs zMG7=i^FM(0*d$Q0sfC93kfsLz0H1?_j4>OJCzkvx=;bTDPt00RMp|64k|4+dI}sgs zZUmCd00fK`U~$lz&TZ^x80)EIw2q2d!pR}t7e6mTtaIOx27PjRaajDG-L80 z{{X;^d_RcQ65(uT_+RADufa?AR%iO1$$=7JfPAt40HBWFpXFcUmSnl{Rqg!G^Jmf1 zrp#%#6O3dLjyl(Yh*x?R;?uh-M4?@9z<}$<+>!HkuB^Ez)`NZ5=4Vzds~^G-{1hHG zPumOQ@0ScJ(tKIf=IZAL}|cNqW! zxb^3bf}4w4vQ*XdI8PA5>?Hf2C}>$Ah%8w=k^lpe-EupRQBro&sU@-Cz9Q13mNT8K zUAD8HA0s))9^Sv+HI-F$W9hQGstq1Rd7#{+j1@NdOaUs{4#PZc>5ehiw`%6(k@lE; zd{k1oT|7L-M%ooxNW+#3`@&b|Zgbaq3 zc3fcf1d>7O6lbBTgk9`r@zkE|^easf-UJbcF%&H_qasC^n*b;z1Gq6D@yKp6GHs@} zIVFs8tH}rRI=v@NlFmuj$z(u=G`5Y@31c8_jT^637$opd-pEpJ5qG)i(5VToRK0aJ z6HRb-n`e1mb0f_ph_NJuxBAA(EVv9*9D-y33;;rsi+4R(N)f%?h~?5}6K)eb0AzR) z?nRe?pJ-5BS0O$|#?qm=A1N8Er6(_Og*nB=#hCXxE!1Eu%QRkV3B->A5z7#sSpWh< zu`E@bfN_Pa?;gcoGFO)6X3||e$nGHh+!8D&5sQf2EMxs!VL%RKY;E~+4?)qUmZ{j4 zRu4^!mbx3ti-{IDl|f{JK)aSA<&HjLS&$Xn!z6MDR0Q*^^H^NeB=K(5m0K4Ffu&wA)y;%CA<}-xn zmf|1xC;tGXv-=77Hv-M@E8s~5wjYAN6x+bZ8U~%tKMMZF;d%-gEgSYn{L^^pLUQpJ zF_f45yqD(Aj_XeZGLig8C#!e&^&hQ!9Er7K^4L(PN|!qzu+svCET;;jyO!DjA6_`g zJo{3_CG6+^7x^D?;dOXEJInCzcUB+zQa_t{H7C?zTc$C~cX1g+ymyvKU7k|FtXLjl zKv{_dr_HqhVUzrEnbcofntk8q&-&Ql`aa8tuq*zZL;nCtMKaUQw)V+)bsDAAP|X>) zgj`$69MQIX`1dP`13MLE$VE`0u)Dlz?9a5PG;udV)+p^|TdN1WxQ;lV?beHNBu-vW z^=lc<0+)8!UAM@;E)*qT+DlUD^*leuvP)|Ws$A?_C6aZ&RyOx8;pRZN%YPQeEDlgP zJdoANQ=u}I^ga~$g$u=Vb8Jfy9wVHzhG|&rKp3=B$iY2bq9iN>~eCe-2TVE z;M46v;P?UiT0+ItC&rx}_L&Tr-JwJe;&zJd%J(>NF7rRES+y_$aOO;7a}l_o^UZpd zJ`_q;Rwz%aM-*mBSOnXK`GTqFbDzibtVd_L@<;8ztVcKOjr%~w732_^ejHx9+)E+y zpeoCZu*bj3dgB$>hK=uG$8>)W@?6DlI$0R+rqxP|GcrXzyz>`CDrk8 zo4NAcBP!IUpB0UzrMyKno1Z=#OS@R3doMBPSf!FZ_{WxGLA7(1Vb0Jws_hvEHbDHTo}}Yob@L;AvmV)%*gLr)=P`nuI===^#-`}`qmfY-BLUZ$D(Awewbwyten!)?ioOC+MFABO}! z6n`rOVne({$+|JPyDs6h^=kuqzeB!Btp7Bwoas9c6%OL2jb&!OXm6mmqk#`>|csxdL5BL;6V-54m7Zo`3+ zNXd5mR;Z^mwg7ekh4Ng=1hQp`0PX$Q$;sgGI$&o97_91a7c^z@4GhGeDdHiHWCm7N zk+x(Kow0$`latOk%8XNbkkea`GAf3Rkbnmca_8=6jyTE3PMmwy%1vw|4_A*CvM-j! z)CYVij|T&dqM(50)IY`|uj!HRGy@2x@_^>^C_4glzV_n)8N7v<2 zQL7XBpZ@@ZX1*@?^WhJ{pO2c)g>}1#tnWTFTbun3<=RabQ1Y}bI0lm6ru;K5@$+M$ z9c%b|$NvBmv)%yWOdcaBn^AY>UC&<|jKpDa9@c}g{jmHS{j+`_d_#L(AH;-sJH%6k zjySa`lxer}9B0U%pvgaV@z)tO_I@4xAoy#F;S45aHA=g-k7m!K%ean}ckFR~>+XG- z9MQ`f$rGfhAPlUG9as^NMmpxdp($c1SBJHwB`CeD?tMaWnq02u^O64m2Q)?`{f7K3 z`+~B3P_zNcm+KMW4w)xzGsbaVrV1SRi2Tojyh8KE^o2S>bJSWc#Ss0HONSy-1r%TaYB1%&}8)H6#6@HT`USBQ3HqNAn7eptvW^d03N%&QImj9R_;- zwHj&jYq9f_nsTv5YiNAOm4uS4yhgj(3j@#=>ykhxj1lRKR?ac#%v9RY<~1!LrV*+c z0ae_}7Y=q37?7tPyn;P(Q79(P>UfyPLyo)fe3LtdS9J}wWGdTB0C)iMM@;db!lRKj zq3F}cIBd*Gd)-OkylL zugqi0;F5F5?mfpjW#m(s&_Q4d?#-UEQucIumTw)%qJrRe|snDPu=>|`xSA= zG^qNW9DWliZ5!L5cDQtsIoU`t6(l;~oPrsMAauzYQ^C$utZ5w1akE`dOZac_+fng{ ziF9uX=sHqf_=8#0;?s0%StpB1Yn!R$3j!IP)-_)G#}IOw*C5Bw4H_QSIm2IJsPvnh2uO#VFZ^G?iKClVDprqbn= zls?>rTntxC@*K`?)Tw?aYWht0YNEZaF)zI1HGlXdZ|!vvbcf&V88T04GFWF<_ zFTi{4BTM+1;2m$ldX~Ej>Kaw1sw0WD3wW7fki^kU$&K#01g;BbX=BeWGbzX5Z1E7X zXVCDkj9fFvlxK?JoKLibH_@%n47<=mx=6Oj-cz@k5@fx%P{pyy;Ko!1QTH%Ta(OC2 zyC|Pnv$~OzcibVdvbK;kQQDztOC(X+`5?wd<@gp%u%dhvzIvt#DRb}4X3CAx~bA_=x|FisNcNI4Q~2p3n$s)v{>Zx z?qzsOGs`n0Hdk_~ECPEFGlt;R#+9}(o-du~az^RHsg z=_Bzv*qHk+LX;7pj_{HY3mKW1U z`GK`td323F;oy#W7TPPz8<=g9ONo4<;B6@3fnDrYLZE`@1coBNjPq#Q9ajC{=6~0} z3$;ET!L+~S)j$0u6egNqv#FZp=1a*UjqYtO9!TW-NR_Z4AQ2cFQGgEE1Ym{d$D6XT z^%VM;`hywloJV5{$>j-@$#5f%L+9L1pvQ)3OPMx}#Xw&(f;S4t-VV;ki1@zM^s8Bn zwlGBmFoktpQKXhRJhm$Cp?a|18IL%G0&oW2da+4d&ATg~4}3gnxGyAo*!;?dGcGb1 z0C@iZJu4!ttz+fC+YeKnzh*CukUF%BbKoo3wk(QwF9#qu%asQ_@#|ffcGA$w9}+*0 z+nAuY`xFpf-KE4V(P@z(5!`KPzIkMkm75`*D=U$`w6{0X-dr!0b@q530dM>BaI8@ylHx>7hc6~P zmjG^H0OgA57`M4GO-jsBad#>ie39aKWdxxCHm)5(vKM(HI1acCl5x8j9f07E z&ZbQ_bWc1<8Z)rO2w1`@BeEFKDPA*xPdO(f@ru$i<|v9p+S0KB!xnw++gc({Ib8e4 zf_*)4RML7e#!f8fwXLgXD*1>MoFk)o<8M$)=cg;d09-+%zUMR@6rQQG=5HFx+in!d z0;pjiP&ahRz`(%i+upFQ?Gx*=yL)8W{TBZK!2&giw9nb=_SUrYH(=a@Tom*&pQ)}=Vs)cw|Ph&p5?OzAv%KY0*7a7QDaIxi#8*YUj!ReU`~ zDl&0vvpmIK4d{Js@Duj;)_xV-*=X9vsW*vy7=y`|M%5)hZ_|hOtu7mt184#!`ABhq z3CRAE@XP4~#9S#}o(ndp>fv%^6YK?170uqC0Fg}BT(P7=ZSU_qywsHA@~G$yvRAnBiFq``b~IxwN=;RWh}QD zePBy3{1V^x^^F-<_IUAA9r<4?P|_GL-WV9WJajnstRK>I!@sqPUvn^)QI1-uoNlxK z00guBzAl8SpR-qtt=Mk>s&eTvntNoks+mk8a+!hN9@TjlFYIfJ){01`9tGhTu{{RO4{{X=>fdu?5 z@ta_d55+xqZv>D(*Y?S;w0P;xT3=JopNIKUgT2cU{{X?ipYTlR_UGXzisT8mYRU0W zUm$Ji(&SO~+4+rlf0XX7Pbd5k5B>?8Ctrh}BvZ7mSK^O|LHUnA^7lvAW;fyfRpw{E z{2TB800hg){{V|e;_dEp<16MSaU9x()em%+i8oM4| zZNgEZMau`d{E1)aXHI#nO^lJ<$0%!sb|PD0jgrHL!N@yH064(!)7pHG-bd!OAy*Am z)0!~#8@Qymg;Fb6p4vTzSlLx%k|M>M0~>OuzE8?dPX@W_NkTlYsnJiJ*;F<@5BQnq z_`UGML$^0EUHFH`T2_Yvw{>|{?(d?tSqVA8%%?o|9lHKu;*Swehj=F$nb3ayIJUk? zv-6)9GDzmW9_BQwJ1$4~CeQ1K!hSCJ*WeLnXQcR|=1V9MVEZlHao(;Ka521eJM z!0TV*RUfA}iTpL4)~AKW{_Zkr)?FW={t{0ht&nDw<%YybDM|Hq-T9uuYx_$4T!==R zrk$i-?H@mxZ6dplqYQ;W#^c)=In92Bg8il*B;jP7lAHekw^!(WH4Nh)O>)$%*`U9) z_lwcNx$woCrqFglJA=mDjIqE!DeK1-_IMB4k;ZXR`ql%PcjjN@dRT5E$47ax-~2S& z-GyuK3%U#v?GB|Jn8@pmxaxDk?_Z&1zqK0?ImKeKwSV!i@(gbg*2l-#RB!J-Q~j$y zVULa9@K9Yhz#cyM7@h}$KLj?Z;SUtqc+13cXm>HoDbud)8r-eCp_OB4w;Zc0I^Z6o z`;)?d94Yws!?Dk>)V-f4OW$+*x9Fe!qmyJ}g&pPS<00x)> zO0WDa77K&9xsU8PJnSDfN!!Wv{YH3cxm{?_>(|BjsBKS2=2M&g2sQh3!qK+4;yrjpd?{{UU-*>h6-Pb%?`{1E5%__u+?9v%2+d*vxeTHoR# zA#8lB^Rywcw`McI!Q-0O*}+EYSNRc7kLc2xe7YYv>i+=nNKf1IL6$*r@Y6)Ul)LOr zAL4G1v8W-HNAIPJ1E4&BGn`YQfU8=xWfj#PhCjr-jb*i#Cr8OI$)D4o!mk8tUIO?h z@PEPHAhlah5O_o3Plo(2u3IFDaF*A)c8g(Wbr6v-!!LjWnRnkTH&R|*JmwiDV1MGB?MWpV)+$>#=|c4qc=x#Hd_ zfc=J9QuVy&1%d{FeAjfuh#}7ARQXgA1~Q<1QR$*ty_tscX-uE-247z z;X@9d{VO7QeePrCAKK4Yv$*}8ely1R1MHvhfod-^MHn(hqCT5g<8NGpU9?`eMp2Eb zYJVqM{-0=rRr!3)?A{z(pJ=r3%}l7|M~8agYxQA`|oEE^^(T%swPzUP*5S zta^E~jtg0AW18mKOT~3>6I>WtD9BKtp_Np5U}eyqrABER8L>wkg5v7v+8KBKrsMlX z3f?@ftgKvwY?XMW^4eAT(pCzLsu<%I`L1Ira}!(cYl~e$bx2~myz=eWPxE5B)9v7d zg4=|J3q%z*DwI|%TnuodVp~gAR~E2p7ZFEoG8eM7OSIHxn%yNv-?>?!Qo)(bj+>hj z5i8^<%P5yb1k+qw*_%u4vN*jhG-BC|4QUk0apl`7EKF!q<|^^wRQ~`A+>YwbRFA1O=jv8u1=o zO6DmRJw(W4jZ8{}qoIBmRRr$g5m$E$+7xurQK zv?|DC8CUZg%x|2csUU-Zxjf?|^!#g6Lz>q2i&+3sxF4KD8I3cXjxs^dJcIS1=%dj2 zbH>ifCo*I?QlvM_xZ@qY$RCLHt}Ih&J0D+_rs|}BLLcx#1$lk}{B?~^;UC5Ob}B)@ z^E7*3{wBZ8f2H)To?!hY`LeUFPilV8>K5**LI4?9?oslLeKYEMSMhq1a=G9+!ZKDk zjYb#h6kw9f>yQBjvy64^T~N2deaYvY?a=#Y_6z%Dny=|>5u94uaI8N zd^h{q=<+_r2_PpUamObl_37Ibr8zyO#OYsjMq!kF*B~Y%> z6k^B#AwdIhBIV1g<0I9;PTL00&_ zXYx(|00qDQ0D>|>uiE&#_Qd^_{5Lx5@LS8Rd|~)ipjwF+S5*b4*K~Vf!pxu-p4|uU zHu;J-f*a_vykus*jcw1)xSxjkr15x7kK%vCX5&xY1Hj7~xs~E{VIY1IL}qL->N<1> z2D>p)(X@O`DssQGskzPm&Uc36_Obn*-DG7S@VmS;r3p|t)$NWrAom}ge>`~Z-~2=T z%KIu){{Ye?eBX(bIXpegH2V_&0McZCbN4o|mqaS%R}9%X2d+O4@vr%w&m`xb;xD4- zm*&s#<2{eoanihYl)pA%-RW_aE~pa&BWzq_AmcwpuRcEP{pX?L#|;-d*yZjtu#JUe zBsL3e#&B`Z9*k?!r0KY?ddANT;~sRhF|IU$Ge4JY85^)zLxOnQo}(Q**L6D16HQ9z zomPxoxuWdP)&BtaCyj$e@XyCF9k^c}7FAL5A^y&?&Nu^+w>?kGl1VlG?|#nwYoCYp zX~S(lGyNd?D^q{!T~4W6^FH~O+~5O@ZNSOrKjfPK07X=HqYH@ZmX0gt4 z0nf}b1`kT4p!7$X^=Y@Mi+QG&R$#01Dabe@JfHrxKWBHLolF#3JlkK=izA0nFm|`f z81av4nL-kFI9?VidMlqic)P=>Os*9PNu78zv;pFMbB+gvNDuyD1i;qLad_kbB zw#*R`nL&M}au|XzGhe3g&U;f2SyQa^KY=`P@M4B46|+1PJ^fzC&3+)YmI-B!P!>q! zDS~$rGJj4h_Dnu4SEUNn_GkI8#25@#Pl=Wj6B{(y!uXB+zX$lrWqcTX9jUn~l`2-s zW+xza80Y*e(8WXjf~>!~{{SX^KY{a4#N4uPbyh$6Qa_n!?JSnY*4E%a(naCVu+3p{ zWgE{iD&a`n32omt+~l%}1~9`P#hItQm{h-a`JeUu!t;ya{1s2M>BIj3NklSST#HET zVTv|kb#HGJF!^^VT11tSRScwCCm{ifkywDa93yuKe}Xzi{P+s@@t-PdRfu#A>sBMM*& zGT1efpS`K-r)P4Pn>05t6}Y-tUg0B**+~qq9@!Q#9CH?s`GX9rA|~DuNgEDFHGK>A zo<>#DnJq1)c`b#_rHnTA@t0Q9i|>qdT*wScAdMteWep>3VYkbxGLz-pzisxQ8fz&m zu9D$p7dN{V<0U+$l?=s%flddQ;kM)vvo)+Dw;XRRh!Wij-o1szF3iYeWrETu zX0e*#!dq$HWHQ{`$pDn2nD;W^B+3DGz);M)*J)uTI*AoED-?NcVUjBtZ|>g>3ad)D z5iD^Q7ZdCoS%g5I80UEsVVgC}BNAn~3*_ZU$;)xTP&3!^pvqBg-2~29sgMa{3el{)0*tUMyFur=;Qs(h+8+uWV$-`Q z$+K&aZKQB;Ol};_o}}^HJPv;0?^PJ9*&A|5=QWU@VUblDL@T`mM}U~fImZB=c|YF# zXf~v@N6cO}Onsq{Zv{&Yss;`YIq8lCabmV-*=1`~e?lMdK~&xFSN4I2Zq;9kdUJAA z5L-!)9B0`2bg%PQ>3rArt`Vs2rTMcw?r?@Dl1J_3{3j;`M%eO41fFxj>HdET{wrxy zr_Chw^*(-4{pFy}6H~QP46KAGB;fKFckBK|bkdwulC_&5!ceH2XWHMeY#vst@lsYm zeA_)5EP?Yv;%l~s9Y)*z%EBN!O_pi2A-{{U5d8>r{N7&t}h zll;%4%c}cGa>*XIh6L^DU#l-?%geJnnyRPGYmNlt=HjN!buK!q^8O-#dCAYGY7%|M zlTQ4S$CM~1_hng9Ig#U$71C8G>dF~iNWoGMP)AYeR~~4)t;{CjG~)I@ zj-U802mBDC@5F2458BuEH}Le+-!wNKAo#QJOG30)nsx!ExYqnVZaTLN^Vvo>5+(N|Z-i4^czgDY_(47T%co1E{8-TCxSnzLV`%Ye zmciOUa7NZrPI_SHy?#yOj#*Nt0A^I78{VAK`Nl~6tHg>`Yj6%>N{+L1=coO-Bm1<| zwQGBr(7oNcc)^ZGGa@O+0lM@cgZS6{)A0ukW--od&J?9?d3~4N`5)m8Z?7!EttzzT z@k{K!@5t6^Y}roR+e3_k1;VlP=sEV{yjgJWX)DT9W%o0#7%JSW+?U+{0LadnVj~MY z1=xmQr+*y?{=bh=T0@4ju3c!$?tkQM^$P9|(3jlEUr!FFE~X{PU4v+Bo|yptp1td? zmkwrL(J4`v+|Mf&mg70&%Li{pexm-smTbQdymD0U68O_{;BYZxW{0`wEBcNFf4V=j z=LLw%cnY;FL|T)!je4Kx@6ksun&B$+UGCc7PW{iiWwMlz1bqfi%bc8cHU5cGa%*Gy zGaG^AsmVP`ZD%8W=v$^bR*;;wvEok+$?-Cb)^ZVp2_I2`+OL`RGxl))W0TeNrjj=S z2PftXqi;;+mHD5-a7vX%^*kfRnmZwo`DACW8-s8M^{rskZSMYNb)z`O%<#QeOJ!!< zoE+yPZb3Z%04nydRoynEo~Og{CPhmfgp!`Er*q}+i5dyFb9oAF`LMxTmFjt~*?31b zsa+})S|j|W`bl_k9Jd|y+%z<8v*zC(+%2EO--uI4!F<02YVrab2aP^+)De!Jzm0yC zi%sQ~YDN8*`5%RN5tia(;=Wn!7rX5veLT_p)5eQEirOrzZy6CXY8JD`rxLV?;Mg1+%x33{t1D~F{{S=oySQn8!Zr#LT6;Ku=_rQkb88boU`v^F=q=>A zvRLgQmTA^evpZQ;zE$EV*c`AYDt=xg>r%3@$m-Viwh3~v+uTWQCZQg&Bmsf25U$Z8 z#~UK9)nf#1r)ey)w^d--`#&+BXX5B(7Entxa!f8Rk*yKHwd#`OEGabnQJ=ZX!8^yVyxX=fFx8-+TUZ; zr_{^0-mpY1?w#dJR=8~**<^+YHa6>@Gw)!GmQtz}e75`8aZ;lfsx%^!#9IFVXG+aw zvi1+Q-3X;NvdsgmcM=7Qx0xhz1ymrD5n|)Ej`4aB;dgtQ5=t4Qj_xL~lHLh!p=+5$ z_Bu=@gih?9Q3H3&xGTEuloosi+-oOhG?Y<7>eAB2=I2l-)833++q&Dgn$a0c#!O5Z z9}x({F&u{ZOMJm|sG88Bw+RgCrp$Fnu3(Ymg62OaIqco0{xaqKx>&K zChSKfQiC%#2uPJeDl2Sby@BU-YX+mCnLMWIRFVa^v}of2rZMU4ZW`qxRCajSAZ3iC zO48>c!xUU@sHrwIn|4Ra?z>f8owp+hwqO}PWL=LZ?4n@;EnU0k=9F5rQ3tt;^n`8fw9XAIrWJ&EUCB(BShZ>t_$ zpmVsPAfkgYV<0<;RseyJppr)!IT`N_IJ=_xA|v@^h}DtP%VUznzTxxbo4?%|=ni=` z1|v5z!~(y{xoHDBLWCYRsmEc1fyeToaf_20NnzxVltxH0mE2=HSRTIJD`@(<1H>*@N|7?`eGA6b>uwXSXa2>r1?X;0a5!^Ij0 z!G9Fn_;*C{POA>Ps>3#cXREx^!vesJtvpSPCPp)Z_2$0|<9`e|bB*yzdEGu|4Zdf! zUoF97XYFdg#P@;!00nRTz2Vvq#2p&pys#Wg;5`*U&wTxxuW|-Q74U!YEj}N(+Bkp3 zlDK05Q{RNY6HY()EBEcns$sbCH-n}?Np`vLKAq;oa99k8)E-y9P7mde{7VmqQL3dZ zKHhBUrNP)qu5N3dpYZ4Y3z7c-1qk?OCaph$yj}4A!8O5e9-?kO8flueSAS(Q2v`SF zQy*%Wl7s`trFxXS0N`9b2~kuREAwZ!fZ?1z8Cf2wXa4{P6Mw-_pklIk_u*aAynEN} zKL)gri+};;6?^BBe@fJtdJwPTa=Q{{X?ppYT&eR*3k6;H1(Iy7K%V zw}l|GS`@i{{Z0QZ}=&(!;cVr5>#d3SK$0VCg2Fk z7aejjf_rqVrSx#{`9I+PFZ_vIH%F`fzVeIz0Kvxp0Ps_Yw-5M9J`57C!2bY+M+n{q z(Tshg>%ixl(jP}Z510J8{9pMSf2)+bzw7Tg`_K3|>;4LbB9m+5kAzmGlZdT;4r$Yj z;B?;a+U++@zoIai{zhA0{^Vi}1yoIlR*E@Owv$70Ej| zj~K%B9Q*N3KS$mKzq?2AXH+sQ6jQo2^zZmM-~I}P8^&hwzru@%fa~>IU{xl71M|MK5$~1YMQxl$#AswXVV%N{2Y`200kw{G|MeBNY;K3!((cu zNp2+gVFlc%Tg{isW@(D!b8Nzlf`A@!Pwklp4dvWBm%}W<#?gpJ1`MJ>?q&aVMZ>v;V+_nDz5iEbfPp#ND z{3Krk?w264&+vXR)meZAE?2M0J$S6v5-Hs=!i`BiSU>z6#s2^WKL{E_I%{mTvIJnw{eDjY60;3fDydi(KI z@UcqvDsYv)A=3WfWnY=+1uQ{t7$$cFY7be$#&y-2$0{$>O`F8NeK7L&?DW$2H4O==rs9M`tX&R(gl0zAZTrJI{p&M?{%^uR-Se~`|cL(GdZv)}pABQO_P<<7d^|=>> zJQu@|bTX_erx)4F{Le!F0Ehg4!C(VEKW+a2h(t2-4~2X#;QYK}8fU3LrG1r5^D?Pt z8QbKKZl{O5D_yQ?7ngg>{J?*~eZSzZ%0i#pTgRX*+i-j>;jOvk@PD&8>M6%R%wzum zFMkF8M+Ces;QF5x55`~S2mTA){{RJhWhK9De~1_$D-+>=4DIwM2Tb~!*ZY4lQM;-5 zFY+`00EkV&4_RS9@@apVt^WW8=KlbKxsjPaZ4Vo^_6ojdgnTU6VgLi3GrmtAn4&yE znn(U#ehd7IUKDU`J+R-5zszC(0D|iO0Kr(U7z>};`^6VoZR##O4dFr@XBi-AL%u5i z0M;ze`Tqa~{zX*qyM!dI96i2l=(NB1FJJr>Gft5{)B8nQYO0O|(Q5wy3_L9|1CUrV zErOqJ4|DiyM~O2v@44ydk1DT&{3lipQKxU4J(t7(0PtRa_$Xe!h7Etje+)IC=!lZs zd^ohXd3VXMF!Egi`=_=!Ij^J3IQKKb%bF6`(EOW?K8YS1@swj6#+~neul$dG(ZArQ zpS2Iftus{cpNd!E^tykFygPAgG`cs0p3_y9-sv6}7NlH|8_Se)w4a-BASom3*NVCI zTr{xqe(P4~d?yQi1Uw_*o+726;ApRDP2F#~=N4Cu6}6E`5J!I2=_F9Bc2mU@xD!nz zo5(F5(z}6NhIA(=Yx8Q=YgMT!OLp#mbzrcxu(aWZj*2NI@>VtOWdbHql(UXWAY`_> z`#iVP$aejq<8slmvd^3oj7gEf$b-@|E}mFg>gHR8o+~+8-r=6fVwM+6HZH?4?m;1u z94;H~;dZx`fVjcEPZaS*y_@N4BsWn#)xe4wL@T`_Fp{f?SxAlEMp9T1)io5DA!A=qawN^k-U?&r69>!Goo z?<=3q9C~c4dYkTBeE5+a*=4762df*B^Xw2^K}iX}^^ zZN{o3QecT9On<3(qTC2WDJ(cF18Swss5X|YqiVML8GCI$-tby8+DS65me&n`$Vq0uDHV)&62_mn5u7xGXaYi| z+X}TuX2Yxsh#O$$~p1mhv`efG-YIZpQRewL;?IiZE=gT>cg*zKE%11asMdbjv%4 zXNEPJC{Nieo?-*Fq!AV$Gbt?CKrUP366MduYQKMbnTuhRyPGT6!O50&89EwI` zL$Pw|#AD5?1LeTLINqYz`J{P;)=1doF(3>KF)B_8Jx)2zaUaTZi*D#@vc~c`CUr4` zE6J5*Y@Mz5j|70Y=b;?%MMf@6w35*iY+zg|P+CA^J2yh9oE~~@;c$JAd{&A{k?lN( zB*cy+W-3IglI`0LNY3^Hf=gg@#sJQ9Ly|-{cX3!`ByA=coJggVgM~RDl70UGL!8zk zZMSwch>cwX2n?J?k}-7|+z1VmfEW-?2t9b>gGj|Sc1A#T1!ZE*5sE+%=0ky;@q>lu zvB)BtO}ike(4tXRLW6IZNJ0owG6)R7Z6hQQa5w`Pts_qRn95wEC$njzDDyJ{rv;$$ zg16lnAQ9`_dSbd165L+Z-shX@-Wf}?D>cdojdw5+ebMt02Ts4%x$9HWo~0~vryC>h z&)F~bSNIF@JK;^vv*V8nUh3W`kHoi{T=%hSbJ;-90Ti(Q>3&eym##VyUWP@IZ%uMR!k)H&U)H+m{ULaUF3vakovS*{WUQt@-|%i%>`ut2 z-aYu_a8#CUYfi}_cV$e;j;o0I)2p ztk|4_RP&e*(z~hsAoD2vRJHXtd6sAY06z^Y5BN6Y_7S<<%=;;Z{=CNi*1|TQr<+?LPwSU+`{M{1fOu*=%*+jnTP{ zV~SSsRh04oyKo6>L!RgB^sh!34pXw5%wzFR=dSo;{tcr2iuD;_nm>u(9qfmgk^v5* zqdbc02+F%yA4S1EG4EQsDxSvht%&V5J>~ zEJ68$4l}_7^OIcjuPfMUc!PG^{tbHlk4jsZzAF4|#1V)-eMZC<8wzuXKpjh90nZhb zt4hY|rFXG*KlnAn_C#R{x5dwmh_S}eA1?mIS@&a>{m)$B^c7y-Qb9Vk-qJJ|{taUN ziKTXye~TX;8fj+p+>a1L8WVsp6et6M>B*%E(~B^TO6zHABmV%wtG}^ah9zH%-ySXC zjunC9i&e3a=24tv$qV-3Eeh89Z_%(<27-mYb50aRr9JVT&PtFp;f(JiBcL2ohB3w7 z#H=ff~Nlf!3KT+e#C#W*0b^Z;h)71iGC>8G_M)yx)AW+iFEBTEUzNe zWw=L|?H2GPzHCEl1}6l^(Lv*U4Mk+2{T2Eo7qv)fXiyJN?tIL2!K03+nj7kDyL9rQyWw1k!N862r9 z0bC8IC0`qIaHJk_UTs=j$JAl59JcIvbNCwdSIU|h3v3J+4Z#B)tCOBWjiZsAkJC~Sgr^JD-%;m55BH?ef$r0mMC;Q8`P!z&m_W-7RMz#I%8W81jTT+>l+R8+aX z$q4Xu)Qyj_NK~IOl%e@fK3tM}jo9m+eX^Wd+#@QH7yb~rl*Xl45TOcu(q}mvw|5xi zF~)FdsJE&pk}n3&8yL(o0*A_N(fN+J;{id*#zuWV8l?x+O7~>r!SkF(%JMTNK~P-h z1CmE>oqa*?NvXZYOui4g-H{Pog1Z_pHn1vG9&_D)Gm}e_KuaHi??aZ6CrFH5WSb+- zKs=7P7&#frj+tx&i-jg8lhK}`;XevAbK1!55rPQ=#6p&cmdS;3SPz>nyN6zJj0}K>O|CTWFRWwUN!LBk$rozuERi#{{c`f};R6Gj%H~gK>8uv7SYn&m#8P8(D5) zk^F$W_X4q$AOKvIQotXZBw=SDJg>xSJkNGgJx#2lLwjzp6}LBAk~0uM8I%Ivan2Lv zl25ym#wYNwJ{a+}xPta)jYYY*kj%0z%DuI)$!xGy3^@T+PXPeSax=kM+1TNq#LwT~ z_#l15LGbVPk3*JM@vea3iU{{&!sCz+uO~d`HNFlW>4eg~&)?_0Y!CrC0D=xOGx=9b zgHpRRYU;*(w}hw!?Hu(TdiTvUIKee@#C|%;W%x(&9c6V94-4JO%yy1K@rEY=_vgMV zs)SQlCOf0~?J&8rw2w<`us557e`7d#o&|-NA^=oK%6!##*q{Oq-~yV7K2z6>6xO9^ zr>C9GEatq4TkVjf7p-x0 zBi!8FMdq;%6vU=f!5=7G6(otY?u_LNvoh_iEd+*LR7($+bEs+7p~P0R2HJu_G@md) z&e9MHnAtfebs~vx6iq$V)YIugTb7bvvRld|ibsZckSVxazGLKX5xL|Q%M7wf-LqIl zZd}w(;4G~rnZ~CqvtClkzjwTjpx%*7}{ezP*||{{XX_h@iQHYg?O`RbyBG09v?= zvnoiNL2#oi0|B<&Q`~gdHNI!b0Z!KPn1Ljlq$PK4`H03(UfnP@@zWLOpURx>_a}^P zA}pdrpkd>UP>>iAxMykXc^$`Yjxu|osZz^9!4VbNHW<{fKOij1Prgq8;|tpyjs+q| znAEB)Wz1|qz*JBQ@Kv+;{(Sbx197xCMgi$DFhD7SIF;Tr3SSetOFJ# z#D{RqYzSS%Fe~3U3i$UwQ>0>|5t~{OyR#l^yZ-D5l7@PEXvU7SPX z+kws(8MU?l01s;P_*SC!)u8s!{{S&Qo46N8iLml-M}Nrs)_IQX2K%kh3;;S;*3?p* zlAg!#tvDwuJ&H+oOrVlf2KjxypOLP23UQyhbW0Aso3Z+<{{RH;)D~?&;$k}@m@f3` z8A%)16uAq>r^?@2{s#JKW%XQg-bCNWvOm*LphYTlF!_?cuaosZOU#j#3>XkGjhN?; zt$zt&vg~b_H7EBo{VJtS5?8ne$QT*t00rZmR+7NiPu_$3nY!eQwZ=GOjG^T7@*dyh zRV*$dGP8^OnWa8kU-c@pFefY5uH+*buGLPoWAL-FTl!@1UVWZG;eaX0-~upyzS*z1 z@b?iMFB4(;`o6@%Nmf^}kE(d4XOWOd!A4j%bJK5H{{T_)nsay>H6G0`$og7Ido=E9 zcuP^9^5rCV$pjpn{Mg9n^y0l}Ht%z#Cl#^wcY>#Mf+kSJkT@e7amnKz_^xWu>gS zvxHqqXu7lc&;I~}g#J4C3sn7${7LYG zLGcEit9aJa;zx(>yhEed%r0+qSlT<;t^CjkMUle!fgOR^nc6`a`aHiX#nH8A9Uqza zwrApp3FrJm_(LnjMy4v0{o-98MSZ(}bA_Xw&S0gGDbc*da3L z03Ud;#{gHSQlBTreLWh@S`{>Bi|c*`hfzpjwuH>nymLf?UnVkwNT5jH7*ZIx-Hn(e z@y=6%aZ%r6(}j0*N2Gir{@n2ngW>Bqq-{%D)-60er&wJ(TYa8e+nB8(NeCF&Oy~@Z zNnD4<8B)2upEJ8htw$D>lV{MMw3qx7=i^W8t>bC@1@WuEdcT5vMPq2U2_5_f=FeGy zi(=zXxrDSA5R(~CIucb_Z73KuI=FZ!-5*1Te%3l4GtKaJSb+lGRGwE6xZIvfF~M!2 zKytV-j0M0OiN+3jRTtFgSa!Y=lYAitRJNMb+ZfQ>vzu`gjJrTXgYx4eB!YfWaywSg zqOYJvjNasF@R}eV-fL)!fHpKS$1IUDoToi8!*WJWI3#tc;nUc1!_-?AQ{gy-h^{pI z%qSjkTV{ZveECg=P;xVl4&BZ{Q&$W!wu4YsD9`Y;5Fr+>Ktpn@)`}U5Hr=ue;S>?m zJBC0wJqJM|6k3rpd@GS9cqX>CA7yyaCo8(k%XZqp3^6O8I1H61Zy8ZXv5BE(ABF4{ zt&BkO+M6%u+bwGQ$2#0?KxPO1iu43?wV^ z^NOcQC!uPWQ|J#0=)Yu;*{#f)b;D_kBsU&xkj)G;EJAV|#rwd(OhV%r!6buQL0Mc; zq|?;zG`F^qrjL9=2`&~%o-Il4uB}Y7Y-4irL{wl1g86u07?2BLY07Ck4aKd^E6aPh z8DwRY$tBxdeVJ{i*&(-kFebind$P^RtjmYNUW~ze)hJc zMmYm2GF!cMw|8((>g6dlHvnLP+W-PF{&PrYi2QTZf^f3-7SHNC7Fjl?lt+(^Ksyw=jD9F;OV64}Z{65;t$)MTB|)S4cbb4zh0!s-_b zG%VPHOJ%*D# z%mO6T<0QiShN@KjyZ*odw!G~*l#=V`|J+8?z_ws96~c|wRHi5*J2 zMIx^K;~?%NZY;r>ahqBeW4gVb)*<$5HjeJ;=F}luQr0f}nGgrS*vinZ1A?r?9AM&x z($$cpo`}kt{{Zb;f<(7(Zw1uX6I~+TNpU@*hjx(3v;`aEQ|1D=RM-M06=|zqP$AUP z;s`Gi3uV|ENN;T5)MwM~7)L8ikU*GhJjh8u#6t98jJZuWWE_r76WUrlmQ&r^+S$T1 z>#g>t>`IpRGcp)e3(TF!fc9p0;aqs8(sn+R zEu^JoWA*3$31u_i{2};&f=vCB;vm2{C3o1sdw;oIyeBzIz9K87kMlR_`A_QE&H_n% zN=NBk?V(o7VDK`bG5&gfmG%{Cx62KW^Ro+HQd%PhYj#k(}{(!hzIGjdgs(*C+w6gyIf{cFN7R;g# z+QZ{2mH?|aiXKpS96c;$FLTmJx=>Hh!;f8ev<@KPO1;LK{B{$$C|`x6F5ew3G7z$G1^my5Xr)r-i3Z5%*$`PL*Wf zslJD^cq`$qs@E{cmd&de;?fOiqNG*#1|4X}|b6YvD)i-SOk%{{X;^Z}xEbldfx@2|RnL_zP0iYecmrX=NEEi#8-OD|w4malc8jOf@=I^{I}7$>kv87`&EFc~l}T9zwyUCC zM5Ht125Y4`0Dt4uPEb-pN^4^EulyZv`zK6dGyc#203B`7*%C4*(B@*tk~WL%oM3V@ z$)y<4llY2MCDogDfADp$>`<~d_J_gm+78nQB$chS?GEvkFvOf4Npf%wfBMu~f_8Eg zs7m)wL!m$Lb{G5;w%8Frfj?<@j@*sD2GM4XiOv8ktCQQ)6)sp*?;4+hjHuR>J=J!8 z-~Rx?DL>$)-vB@0r+y0flkpqiEr*Zvy({65i>e;w(1uD`8nw?AvU)aG^6S>%|p8}}JoB%J)m01j*2oF`2y z?Wz1d^daJG%QW#z6N}9%xz2LCZMUntKOUu?K{1NhMnsoSr>j93w~3XchDTu;Cu)Gm z%5Yb?918MkwA;}C0A}!&VL2wwUr>Q9XSj+7vyx<48Yt2Q5x_;jNSw$EZVkkWn?`mX zNCm5wqq;oP}r!M(lSGqMP5lE zLI4dpMO&TrXC5YTwcF~C^#lE^zhm!$f3}ChT~Fah#%~V)0BG3UM)UZ0#M*dEUlsT! z^^^p@(=PUt8v?7oCd^W*?U5L9UoTG+DpeXx`@B1JXvS3bvHS~u-oNloUyFaRf5b^V zJL6kPd{6M#S~ADrFNvC7nrwVK;)`TEz5pXZ+`i87CiQ~%Yn77TR#L2>#_oqF*Tefd z%b9L_SZqWpNq0X#XYkz6#@6uL`EpBgmgOS0FEB@o_jw!#S0n}8%bdT?pw_KB`G0+W18C$#GY6s+Wp9^kMUN<0w8e8fu{bBOEvAp8#dC2b z%qbMnEv?=4!@r*blN@fg@Tg6{ag~IdM)86PCkxdC)$U|gZ8F+70L31m<;5(J5=3OP zw2~;?K5d~u20`-VuTt0+XE^mDZ7)xe(VE`I>IkgkSiaEk%V!%emx79lWOXsKgvJR+ z3ZQ{OdNt%*x6$k^#lPEhxFp&mmKnlFD_i}kys&1%e(&Uy#qeGLn5 z4(ZZ8%nxgBRx6P#w^Hc$Z)_os7j^~T_nv0aE(0H!VBqA`#_~-Xq}#FyY%HxU?cDzW zWtk*)dubO?p3$aRl!TqpWAecc-cJC9{oo(Ot0}m$oE`Kv?c=m&o9!2OkljHOz3Pj3 zH5qO#*u@--8$6dtWm6&`;iKEskkn+I(VA9PEQn3L+>K?6yL+pIdF?JO<%ojA33C<9 zg@UwuRJ?8SFI+GRTTzgkgJ_f7Y;z^TyfbEB)7;^oC>GZ491RUwX|7pqeqp}-Z5n?5?x&@7E-|_ z8+Ry(MnZwLV;CUju1U3IL+8GQnPHk4cf2~Sw12(1Gdq_ZUSchSKK`%Fr&_Tcfmj6-n@XsE50bQmf^#xarxVhO9;bc*DQ-w4gSm}HN<>CY?nO1s)_SB7 zTicH^3yD)LH<53;Y2$%_G6pWCUICo20hA!htBQ@H^eB?*KRO*x&4gUb3bR~YT*YjW z*napkj367^3jkFm%Htu}9$Z>aP%1LWZQ!_Zr)pNV_5wt_xw#PPP#xx0dy9RhCd;%1 zqeR9W?cipV?Iytaly5SiB~~Vn5a}P;=Y#=b`3;%Cjs0$^xqvUC6{0 zcFy7e_WQp01K%{H)`*!!Nar=)ZJ0Be5=hPt)Zd(gwC&Gw!v{SpShm&J;H@~XQ{`V9 z+%mlR03pfV4ofdQ?ZL-!?V9s(GJ3XsZc>v`x%-j-00d6(-M@=I4*Y&MGRqwHKN0ld zVIVu>Qzn>M!w$TVroRW{Pa7fOPJK(-)8?aD>#o+&{%-nDcnuF1aFl9LtI1l5cGCC0 zr|GYYd@-r$5|ejq;@z?$x{WtmN!O!e1E)jHe#OE*E%2Wk;p0~=#4dNdUX6X{`6I_( z4RLn>VkI2Hgq;-hl3TBl=i~;HB#q?lBW^*>eg6PDm7On=M^KTlH)r>75ypD;{8(3un$E2Ko!+YF`fvL^vkFx4d8BH8aQsVe58Z-Q zV09{ipXTI&`1j+!eXGk+o82Gk=hx`+@1dg(g*Z@5QYscP5V$+LjEwWwrhRjh(Ye%4 z$2Zp~-Reqo3HF$scB8g10ULk-9z73CQbd@U?<|@O(<}@03UlxBe$KVMsksdgaBwNtjrotv*3)hoU1Hu~ z`#@$%WLN~aUH}AS01IH_ob&2)aGR;BI$k!K9yG~g-5l}E!Z0O3GLnWn8MgC)2?P>G zK5TW(Wa&7)4ryZGp5`6qpF5|RptA+}$ps1E1IBpif!C=go@+T$Z&H4_7u4jnJ7TJ1 z*aINgt;ih?;&3o}lb>V7XK3=amZx1jQKCTriN5>vQMob3E#$T9Wr-mHz-1e?@<{=k48V@B>4a;m5;kn{7wp z{{V<=7_{IEn|}gZO027YIu$7-zA{eQI5y>Z=UD*pAq8ckIgE&l_uM5x7T0mvE}~&wDBkQYVnk? zlWQ_~lSe@)!$wFm;5YFxZQL*2$mAZC8Lc=`P;o!8euA;3PsAC@PVN5ylRp;qYsI&U z;qINHxr*#a^9dF13y@Ydk7pSHvfL4vx#$S_$2Vr}q5jiQs*~SC$@Se<)+?X3+`PAP zBblWJ2}I}vM;De_H&K$qa8@7@7#SNY8AkDJ^=aa!=gD#(hTbK(e~h07t>TGgxL=Dt z8%X5)cKdkCYt{iwe2?9h-V}eb2?Hj)+;wkdh37_bnDo^dv5q9JDXX4 z{4>PZc;$?SRs#mQC|0S5eCeA;ZZ%VpnL41Fy{@ z_&4Gmnu{i~W`8^TFI!|IW#~{+Hh+>}s>(wLQSVX3LbPoy&gxjjQP}*fxznVY6Kv7M z^NC@xg5pbShx-gbt3u2qb~_|#B3y<;<%c_$AuDfZtznMHL-}n@;F% ztYEv2=IO2Bx|(8Kzu6kn2_y3mn4MjMql^H=w%zKjx8@)Tr?F0L?0G+mtgS8Jy0%4# z%QWI>C0N?p)w*tw%F+|R%u+VSNCIR4g*@YDxd+1DK7uH3ucNei?e|L1+KkB1Dq=J- zsuP|A1w9AKI_>Apu9|meomJWBe`a6sX&=L%3j9v^ANzUud&XK^8n%hML@Wwx__CGfb*e%_JVKRJa86A5PqQ zS4vInYUdXxso`HAFYclEN#n^j3CPf-R8r#IRI@7QdBzxLBeBLh)zj)ab55eVKMrm$ zrMEKNT1#gzaJK3FvtZY^S1E*SO&;dNWV?mr?J}IH=y004x$D7MYGh9EnC@CB?ll`kT+Jw7R;M~dw%^2LDJFa&&dD^=}7{$q$OQ~K#G`e(_>mrE4 z>Cw##%RijX?CIn_)@NLhaLhn#5L7M{Vxm)n=vj(Oc_;GPNrY0yZm(n1SoshkXqzTr zOGwc=Zbb!FFvqHZuA?qvGGwuvPV=OWO+xzCQijV+^S;=UAa@SXDyo?k6U|e&f(-mG z$Vs3Ud!clV6o1;+7V=ImuBJC0UZZt!bt6X-!waDxDGe-u9m~ih1w%j|E+||8r6Q1@ zHOT=S72HT2cJA&0=m!|DJtVY8@~r+AOiK^~$i_j1+T;}oa00WO;O7Id$4-E$CQ$0 zX+U5|DqTvbIR5GO>Nv^#>Yr0+$CM6GxM>I;Zqm-DIbGQKfbPc_?iZY5w7R{Ej9i#E zk`>Y=F=c~e<^-r#H~_KdDW59OItZ{2Fa+D{|;^ z`sylMJF3867|d^g!G=aL+n>{=S33%S#AnN1JzS)vBm@vj0hZ4g#(VM4=fLS)7?bNV zWqPRofIr}YwxZ|Y-|Y@q0%Q1}rt&~jgQTzd{A>L5`dUq_%Li}oGpjyo6 zNN(-rwz#*3;@$8X38Pt&#{-bvJJ<52QN?+lMT>?zEUw`=^iBT&!ylD#Cktgj#e%F&EvjAZs0=y~VATK@O&Tj@1JHgtG@it4D< zdMQ4q`NiYc(Xx3JIp^FdO}b9)!7Y!+fA}dDfclP;@Yl?N^KWr`DOU#|?1-EWx#0d? zYx^q`h{EDwPNfN{MXRQr_dcuWJ4+RmW|^HlOdKWd?wZ>D&&T_p4kR)lC_rW^CEK?O zNZ=34uVdD@T5EItIfgySU9p41@v`g)o>(PD*p-R&>Bn+E{Zw)}BWUB=(&E2|+iZ&@ zYRXu!f0qrx0P&7;0Q%>Sd5qyyWP^f07$X<}b;Vz^^E+tb z+tsrx;kJ~&@VGt{L^JdJRPfATyA#)2u^7kW*Zr#G#?$@UK93DmPJ>rJ+b_d5aoOm4 zTu7>=Y#|M_W1lT^TBY|rv^{{Vu* zczPR;_$eRld2pe?yZFnhPTQ5Xv3V4;E6oPB=}!y7%$Ip!{Nx?^GR+B>p4u|nI)WL7RQyF77|jO6j0@j|ur zC1YxJW=^T$q*zRzREi&x%e4^v-!K^W;~D&DYBt>77`m6UJafcaFO}yaqhSdM<+gx1 z>Ca3P)3`p>!B(F=++D1F76TCHX1X8PxBMG9`$}8>&401a?T;prYaXfN{{R*IVbLtS zA*D`PU}hRL!dt6`2mMSSqcVaS*_B2zS$#~hR3%D3Sd#ZTAIINIpB`o4&kfh9mq-1! zyjp|o_HWL|*M2d(@ehhTL8^GK#Fw`^$A~q}MqNi#)UJT@?q`{cyD$TQasW|)0)PMj z0rU{f=*nu(L;icS&MD5gyEw0#=QSl#y-7CPyWHUa0JGT2%<6ZE1~7zUILQp&62RxitMsX!7G}=G5 z{)2dlEDOCnIhiuNoFMSWR@9VV|TDh=N?>4F%d-oGN>!pILQr#M$$d3D!Z}I{2{&n z0E_+bE<%x%e~rEnFEYuPkt0!y%8_)4`ZHr1M5ys?&F8TjxWca0q}3d9|&vS0r-XCtslkS4)H#o z`ewV~?Q+d8^xZ}m{uV|AWlkAgaLCH6q!0+WPm$?!dp zz`i5+U#?tgKMTGsolGsJk{MQ9GInyMv#c+Wn!Q=2O^weQ3>gb+L_1Q{A!~c&;xr`^($eY^RwK zq>g)t+SQfRs>*V3s#)0|<>kJ8%ntyZA2%`4M-Z!foX1tCX#zVtLH66*OSYL6ZEXx* zbZ#xcA~f>%l_5}!u>>hUe>jM#B;?OVH-$SQwEAuAw{`;GRFixWMHDi@XtuE2Z!t$a z0Iyxj$U(zx!N4I^YbeP|SC+$$kWHFU+DYeml1qDLm+v#lDo-0pJI2Qo#Sz>0kq6Hp zF{=d1saynFP3%O;3d?01ShKn{sk;VoIFP8GSrS$%0FFPM!C46Z09X|6-C9OTD>D6` znMxUKZqg)`VNq{s%{+GqX=)!?6r{{Y%PEcjbJ zzks}ND0_)?;TQZ;dgPcH-xB`-$Gf%%?;{w=$y3(7Tqb7WB?{jO&gjaPwDv!nZ`(uR zXT)!W-Uj%S;D3mI8PqflOZx&xg>NHSEVUH#Q*4%#1La7B0z(GCAmmrl!PBWvo`bq& zP@0u^FH`WS+`L<^Z1Zz*buz8J+X+g=81kjs^A2Qmjx^h`F@u%b03()Bac8|5DH!*c z_fuR)WbJJN+qC+eO=oH>AX!<1MC=vR?gS%5fx6&Mvc4mn=?#mL%s9Vb%tnggg z%O%wAlG?**ai;?DATbGCxM#o$7@j1NNne(u<25QZm??84^Ri3l*~@Wkw-HJ9(HJ|W zXE~2>Nn}|hB``6SIATv{<_#yKC%G4QH#U&XsF)3%&{?jaxsPP3B@EM*^6=6P`$p9u z2I_Kgp`ZM`DnS%t<~iMUTZop>6?1hBwxX_X5g?X8!EjubQ{}GZUF4$?#wtxametja zyGD;jj?oR2QO@!t+MVUrp%hb?(&A_(Y@D=>DKTJ_BoofZ*u}Ks?uJ8SZ)bCBV=kR{ zZ*2&MX>}{0vs{%E8Ks8NU*umgf;^-!SCez8FV?L!Ef^Y>(yhesm~LTx(D{(t&l=vv zr;I0-VvBc}7GW_$5%ZEVq$2}Dl5JTM+sYz}cw-kY8$63;Jo{0jwu)8UcQc!K$z)j3 z0La>`cW*m%gIBSaB#)L-KpQHjaAG3d*-1SfM6 zx|r5HNK2EGo>UB;xWUOCsl~Rg$Z$_#j8J)L#4@o^?5Z9$A%G+gx^sdL9-NA1M5!y8 z6zwQT`G0%7g+J^LoyHHfZ4ZS2(#4Tfk^8_TX%T~~fresKV*rjh9aE=zX<1n45mgLh z5yqgd3``Uj=x%BRa)3q zE4y@`d;%oEkIX`TWx*^|{pJK?jQubco13~h&2e)RF;`qCmK2cV0k)1*7QOv@3lvJ`%mWNQ-j1oU#;;V(wqEkDr6kw?4f6YyBGfB5{^^#5Aj6vwTCGVPvnZ zkDtqUQ-|^zj=oz#Laj~O(&yq;)|V8H;`$@{MgIVU zdNe&BT=-r4OF?}O>&s6bXr|OkausCLr{92a)W*M+ejUbqZ$pOohK>fkPG4%>mF=!3c5|g3uEOE+fzsy zFYK-3xjeP~=DnmwkvLW?Ma-M<2*&|)f<|%1PZjmGvA*7nTR*8jgxO-kaqO?(^go`< z%aLzwI^^7=UP{JeZX*UoM_?F_kdj?gs^lC;^8gE1B9gRQ^FP@dD)EXwdhz6!7V$-= z%PA=qo)>H`Ea6r{ALK=5Io$4hx{DPcp=ljWV?2_cnrFaiPXiI#B-}AkQh3# z!BRpg$T$vgGDUjXTy;3APD-VZ|3W8)NI76$lqT+l4NEqmh z8`SafxX)(Yiu#VDZETbO0B7FDEMvMdtWhzJFP91MO}~+z z7ddpL&&+;-e0TklekptngTng1i*(z&-v{`&!ImB(@TZKdY~EcfRX6r4a+WtPa^GkR zDUH&07%CVm0s#D@g8X3N9yP^P%QDSmTXW(nzD-8WdPn znN=K?C5gZtK4R%K_9jxch|OzQp^i6#%`S(ZZ*aE@Z120}MR??K840)nwLsw>O~8HX zt3Aw{yVT6oUg45J#pG3)CPBYC8G@gv|uNg}q z)8&g9SGXT$#h5Z+?5OAA}Qyl#t& zRFZ2qjiW%Wth@k5Ku!wp7#KOPKM<^}a3^-r{jPt(n&7v?Pur`Urg(!)w$k+j3Q2op*H3w~Z=9xfVtlqBwaV#L5~ZXA0r64-R(`#yu*NcWn9utgK|gb*9N}1aaG1i(vxI zsoS-{FA0^3+pJ;a1<{nu!)aa7pvfG8%-e44-X97ivbnbM?Qh;IOUtE3X^nsvC(ZKK zFE&dp zLPp}PxR9(XSxH2hO4=bJ7gsB0mTwi$*p$xcdus4n5E>_uHypb@1F*>oUpUDC)WN-i zZKj$m-QCXL+C)vMtZ;^k>0bFHyM{>4!Sc$*aUx2ie?mycNG#i^KBbrt+P-%wnj(^U zkgLOO_GOJq!y5Ul0VpJ7A!8)A;6WKsaGHCNrk>^5Ev~K`>{44@z2aU%;%z}7v6WWc zBNpnB{L00c7-9R;BHp`PfXby?U@AC5 zb8A-5mWs;AV+!*t29Z^bAl)n zN$y3E0di0l`NUgzQ``@y9S31q#t!LQ zEu;D>{{Vw};}7tE_KzExyuXTia)L%b<`SRbU*`wY6{zMoKS_RU>&~LLTc5voR$#IH z*-%$`C*}>!e+o`%^*&=rqXKIn#ZKIf+zb+W5Pj>8yHbVBCfwDT_|5+S1$Km6+k6mE zyS&KtrgZ=VvjTZ-U-ir6u+10aHO2J8+q_%D1_L*YiSc6J_M6H3U1h{q(1 zv2UAcKkVm^PJ8sKEz0!QbCmHEEe(u}!PxB#z%rm2B!Q8Tcs;+DQ0$>A%_D&LLkFMx zL;M1>JCE9aI`}p$u+9T`lTR4kpYxjF#i#61_V_h=7G6jDNLn4Xuqo{V4tjmiE6vep zeuqhZ>MWR^z;)qs)4eAY!`gWWrvx9)l7*e_K4mDg{K5YK;H*$8{{X>3{{U(u8Z2^; zi#mg(j&iaWWXW8DGmkUV?hc?=(qaDqhJII_dzk5eUC+*0ErO3ROfkymmX_~)k+Z1X zx!4Yj4>-n2$4+u`YDbFa%+f_O3P=?MiVS%!5tO)Pk%$=27+!e5J9~`gWiDbBMvik^ zfWr}ImQ{%(IE~KbOo4!MGstm*alr#5^FoVi=8&q>K4|frF^@Tf(yQRG+~tIv46ZOe zxdR#JuU=Z|I&$S+@$|WMN!~F(qaXM;ktaS0{iY<0IEZ+wK)_re&)9DJU~|*0f0%zx zEZ-fI=2>n(0XC->Ewt=;oLiS+b6S5_u9MTL{T9nQTquHY13p4Xc?>{4yymcV$g#7-V(GOdeuu(8@KGqFgZ5AHWNJY(_}fK~VZhENl=I(@sNnXm z&?$TL;O6u{SNb1KQRBIDEpPfW`JI$n*_qd50vK-;&ucEnyR|7Q7H>Bnd5fvZ&RLk{ zgB{(iIC8Y^kM+uea-ycs1M$K{qGGa2CW!e4IALIs?K~lDc2OVA>@splJhdX6uFrb` zWl<(^KL^lUe02CV8aOu+{9W+M$N(Wp{@SqHfC=hwalt!9A=Sl_)v!*LGMIITnam91PK-Dleci#co;G$kuVX7stq&^~K1p*k`7D!Bkp$xA*{&Yu z;t3MoOX$tBD1jx|#IWAPp&MUvp=ZZz`;}&(&nCWU8Y=f8pHW+zHh3oxL_{&Ij1$Ls z9N3NH3LIeoRbvXNKmovRR0ch*)~xvK(>j$_(q;y^tjB2-d*PKHDF}$Gt|n5W5slb# z4)!kDbz>OLFbea{^GRIrmFsmpbHyG$xwT90HaC({9MZ_fc`c#Cmdb#=xfo(w19l4J ziuv9osqFGBTmJy3bN>L+S?EH8a7iQmB)kjYF9&>E{g*x!_)Ev0AJY6w;m;EM9Ps*T zI(DOKv%=zS9?gryPcgVHxnzv1DZvCDYyN06zZJNj!`~HgdCo^(RNN}b**9jnpF{Lq zvV&MUa)y#Wnorxe{t36@ui2AV)jlG49@kXyXTdw;_Ef&fxxDZ|mC!A|q`O$OKQnoE z3Zxb)q@y-l{h0bN{VXyM7BDn&oZ_;EVv z;#_wLB+??^+NF_0Ee{9u(-xSib3Rc$TABmsvUiW4FfIL$GvrFW; z@z#X|){h&?Bu}=ZHc>$(Km@h{`9a4xHSc7Jr8N70naTeEF%W3}5^n8n{{Yc!Ea!>k zk5Frvq_;aKghY~U09=dwvoW|$#HF}T;4-0jcW*yY4M08j#3PY zag|uzj_?~2AYd467Z}@f7Mk3#ES9ic+#=e`Do7=^f>gP91;eY#K#>9fjJrb+ShAsI z0~rR5AfDvfrNpvDs9Zc#PbbGRC`!F!j7>qbUtKePm#!T>HuItQB5l{VA8$I zGw(3GS6Xz9XC2(`e$NaN#dJrN9heCWdmIT;L}rb@fSt@7)^1MvGWp_jmUaq|BrxgG zN+gOa%kgn)Q$AZV`7yf=Qv-kQ_B#2ByLn;38jU`moFDxF+ z$R?5*K@hB@tfZ=jB!Zv_%MX~9Sf|w8d#Ii{r#82C_NcdsCm`L!aTNC?JEJrJvrNhy z5(yu4aLbjgVauGp1CzquO= z5EZkxpYNVA)qUwlr_2U?goacMTlbEVNI+7gVDAHw_s>I}-*jTM6IWIMii~7rA0usV z-ucW=Zbxo2&lu_2gzWS&YREm--V z#_9^pU6KA=fHQ_U1FxYtA5mObg=J&wGN-CPq;L2)S56mS1;1%&cCY-Etuz3rPfa0z z#=p(4ra!%y;FpxYHhTG;CpTmI2)xoIh>%s;3X%%G(r`(^{HypbyOeB?pQ(gt7^|l_ zagfLu?qV`=*Nj#Yo!W;(ozk_NKNCOTv39|3J_tbU>Q_(I+!P^3S*=3-GI8|5^sn@T z=wC@vjwXHVc=?KOHA9=U6GtiH zBt*57W5y8mI4s0xBO{(V^!4=W9hiB0>7IA+a0LCMzh~A%mXG7tgIpJ5Bkmp}({Mew z+t#?V3G&fa-20pi{{V5Xk^a$;f2fE4xi9h+rE#0GkY`q$-m&NLQnW_-`a`6VHo(Oe(d-{Bidp21Ri>v@t*bcG;PbF`F?-? z`9G2Qrr<#uD?1>{NOq}(VNzHy3Z#;EWQ>9_k4)D*{w93aJ({@7iFZiw%N)Usfm$|K zMOEF9s2F6Cl>}gshU5;3E4vpNrtERrOYcFuCuM$2cJIjin_M zXna;aXYrdb`$^@&LR6MHQU@ogJf4S~f!~Vrt4^e9wcPqlz0NffmdEs+{{RN&+N}Qo z48LkASrirV=ZBE6Z=0Pq?4R;0{I~jdtKzUK*M9MacNseT@n`fie`MeiN6WbU;Bv%d z{YODx=49&Bp+9SflZ#!nK3=^lbrWtkeT?rkHf`?!WCZM#4r1yC;oN_Suk2r=-_n0D z;LLq2gNW-nVWiSc^goxpXz*(X#uzE%vifOPN$ih+f8eF@Ew}8$eGy+VZv1tnU>p!t zeBu~&=x{+F{d)fZXn13ZvhE;U5vFhsMFJdaovbF0Tx^wHAGQmo5dax_)Q-P+Nb;m9{wj%J4dU+Vk1gHy6$@1 z&9t4RHyNExs<$Py2!?ClUAv)90dPC|Sh@X8@P@HAT> zG5BJ?<&VVr9NcS|dqwYiNq^}~`>YHb!NF78Q`Gf6FHYAq?L$S^H0v#2M%67I!&B0A zJ7v7E)F6>cNfgmID(b+HK?D(AHwf`IdBORuJmV*->C}TwS-yu%v`GG7f8e7Z@JuV; zgQr~h+3*_2Q`Ei}K5sSu01a6xUFv@i8KhGzs;3I&@Cp%dQ za!61>lnteZ<8>+DRAmYCXmdAmGeZ@}oojUF7~z=~&|i_|nH9m9pSW=Ru>>}Dg(ql) ztgL8xH;a^9+7^u4+N?H4D4OR@HrHXoLdhC$!m$b$ct*$WxhD;htS zaG^lmrR&^d1F#jtRaoPKwlZh-+gP1}K@e)mABG?Ba3qqC*kPiISYvrUC+KauTc5In zwsJ@h&eC&$$L{AntK7-mx&HvJ=QYx=1Nd~ky>w;1R~l?h?RF_Ry?N+pDEhSQ7NSC zAvFk=No?&cZl;q|W_y=NZe@nzA2LA*jI?Dxd_zRXa2&_UV9T40r1Sym^4TmGl23OO zmy0#UfqSz&_Tn{3Rm=i0l2Js2XKEEfPcB-yS1&PxR(2~-ZUiwy6`XQSY9eHgD{Gre zkQBN!vOBgCc2_K=Nn9!FKnI_Z-ohbJvBTtDPbIuFr`V-Q;=E{8KuMe{urU@A5#~D+ z$IF61HK;?~CB5>R#dQhpG~0>H^IKnBTE?bWkjRA+E{~Fh_-7jI%F5cJ3QZ*;q}s?#xT{TSpeQ_tM+n zzK88*zM$%(;iWbMd@6wjOr%m`F6Dn`fenAvv@8A}oX z8*{+vyT29ZMoLHW$qO`IV@OJ?%e{=Hu_$852JCT?%G{0#Z1J2{af)wH875Ef8bG)> zK;vj3i7SKCu6=o7*0m$tivYNAe5n!Gw3ylhsR0-jAx2JHuOsH;)C1>3qa&epvqt&}(im1iOk*wJ)UZf&d}2fsBAS`g(F}NXfJbSgc}D!4fw_+(95AE_*Xzl6e4p-E;3vJ3E^D zPa_LjNAe~`JEJIAIZ&{!3jvUS9^EsV8ndybFL^~DGd85~pV2!<8vu->V|>g`8BaOsU&HvAskCP(YP2Y}lW!;Qp$d(h?8pH8y)#}_T6Sr` z=Br&la`tUO2#38V;!Op#bM`fDwQXP)Phmf26-d2EB$MPy46EXN%d? zf_`VLV% z^Ug6>4}})2^+YWoiJC`|K-=={9YF^qjOPP(J@_?4ZMSxEo*_G|od#MY17oC0cOEc( zIsWfZb3xaH_|7BXnwR~he`lcM_igd_!D?_o;P{(P&+{Yjt_;a?(QNyC7w`}Edw}8y zy`o%y-y*!U@5+9YLf*zU>;G!3*Ev5D zGPSJIe38iW8CgttW4EfYAd!KbasL39hU~x`@y4$M&XGB$)hppc9lTr~W0kezpEs{X8zQmCOdKnmgH+Nu`o&i5fEtp>jZQh*umg z=8q(i!VZ38U+ewj2HV{II)^MF7(;Y?@8c9!9$d)a#~DWRR6D6B8@L(9;N)?SOm#f< z8hW$oGU}3YS2*v2zc1|r@L9@8yM8YCW>wnzw-4>R6Tr#pc>E1`n7FM|KDxbVtiICj zzcc-(?SJFa+nnMQ0C)SMxINbFkE@{`Y7v$CB5JApETuQjxwZ-)#Dvo5&*VG)4uO(Y z)PH34ff=P+Ul6fz8JH<&u__9LAc9n64oC;5TK4j!($M)%Dsosj{Lkb{`6iA?Ac=DX zswa39i#2BQ1b_f@#(5xc!a;$OKli45h1V-A@?h8$vGV4XCnrp>9o=5 z;L_$kTk*BaHJ#s?a%PZQ&O$p07%|RR5)MfO1B`v)?O#8iNxZtUf6D&=Bkb^rdpn=& z0sA(#{{Y$7;4422-v>M~o^zA^iDD1yn*RWoe-bFUan?WnzDx6G+F)kBr32ZWIQ!jC zJXgh4!z7KZ%#!sg$r`+J!!$`e6U6rS7V}0Wg5ex(WOP3(DFlKJK^d*`ZXwKgV=|?g zWmQ}%!QSb#E=z6D{&7FxrC;z%)X=QFMf*d3&6*gx(`;>I*8FSmwln7+59}>9&aI+{ zW4oM4^p`|HYo$@PzdzvcpTr^J=s4U_3C_o7;qBHXXP&4mhGk`ejw=(&Y zSLl2x@ezbNqPodzJ(-8ews55cFqJSXZ69=)+>^I}&4N#VHmhkIQ}~(vsQ&=Ln5d`W zNA2l!17=@}ehi4Sux4y&8pN67*Ul&Lu3EiL75@1n`)96msyhV)MJuL$5dQ$d!PgN% z`w-eJ%vERjp`%6=I7W6ef`A-@o!A2%P65aySGkirIh=J>@wDyce-Iuaoivgk~e7Gg9c}JC(Dz1^aSlBESo(yB)2|^M$x&?v7RVVE$(fuCA!k0mC>Ya zDpIm6xo<2x$XZk^!44D@?jJQ3Iu0KaE5i!A%4CYk^!wI@M2U7Stt5_D;7R3%MPxEA z=^W?x(hP!+y~b_3G2PAB(X$J-Sm(5B)}9t4 zakA1;)tXl({&?)KV}b}StnP|gwcVRT9oxiYNnAX8(+=eVFkqm@5DmbZ2$Yk06ivpC z!7gGNecY4Xz3Nn~N<7dR|e<{D2%K-+PrM+KC2>h{+k=+_rX z4ZB=D*O-#*NGBgFF5txBKyxnaD-hH*(TyhdBYi?UdwV!yv7UIuQJ=M6Uoyiakusn$ z#>!Pj_H|&WtTH;|D${A*2&;dneVzzfLpz(o?F6@zy}QKBt0N*f0JEq#48ed_87Gnv zF!)fG`i7rxHlVh#M|pANTfM?v>x~NU2LfS$#zI-=1&bh-ns+yFP1ugo6=6 zSTxfGRE?Oa+;GFb7z2a+LyQbpofxQ}%A{uu$`#~^O0iHt7$Ya#XCnag=i4=$WRuhc z``e1D2LmihHWc6zGq(V9j11PMZktZcj|ikI2?CTt0f_BDK~R4j0(c$kWd+opNel89 zMFm5vZ++^LNrUwyagpD@wkhcpD9Oc>?DKLYZMh(Wg^&dUEZGMG?x^R3>+3CZq{?tp z)seERD&mYf5EwoTi}Q7BNxc!J}&9bGP_P2Muc<6`zF85Z>Bvo^L!fem*&r) zm+>TjMT904AD0*{l1t>AcCX>gLgjWdl#-IU98)^VthjOx)yJU(5B~sFaa7g`#LBGg zNhE#?{{X>k*%Mdz9^qOz-$c~a*<1pINp1+|q2!V54;B85{R=(nSd~-A_-y_+sSE9? z_!Pv4k{~0NC6LOpAKe8Y4B<$_51EJMU+7Gu%z3Nbk1(s{LKMb&e4_|LNw^W7Gk|l* z;EZ*vYRNG{tJtjPY7^%mM>uSNhvp?u&w?-pc=TcEQr(r!PA|l9pto?wWZE(VG<%%! z*KZ-Rdgr*Nbz$`Pk;VKMiAU`x`#ccEoc_=r32lV#DnH>T(*`&_@K5{-xN}`7GwiTa ze#XrI0B4vn_GpOPgA$YIK2cs&Q+BcXb|3gL8pkIY7$d(*vKz9w7UQ^b4j2sM-@R0R zAztyh{$`)>RtZ!78|(J0*&3{}{9n`~Ntwb0JD_2ZNy?ushfY~Q#sROWg0&Qx`Iiw+ zKU|UdoXaQfnkm^)Z2(?Dp}xhtExpFZjAV_c0AS^bC+X?OH1F*bN6PzDq>#&nEh0IZ zIVKD886Dcu)E zE2ez6<58KXn3UfmXAZ!I+}J+AebdG{^{+Cscd_+3boEw0sGs;YmIF7#Z`#4Qa-Kc# z*c((2y{6v&AH!ee_tUD9o-YGs{K1+{x~-qmea(`B+^*+c<&;ghsa%l`oQD;AcJ{>>KGxL@6P!ree2OE&mq z?}OZWWD*U3a(xAH5~1P}rq=%evPg{_fwuyxLH%ojy$Y0NX{#SYmSgh#ei1x0 z(`(GsCl!yiOGbKDj8bfC91h<@-mNy;Y{{-wc7Gjz@N_e`{3hSBge^FRIea(b4Txn_ zNl)4=<8>s1fJBN5ae#g6+{+}A(D<$|#l{#oqxm;7q;`;~^I(5DBFR&EDuRges-P#5 z8DAuU^Pas2ZC0DP`Glj*?;{fCHmtJ&~-D?P%d$oZ(JD$?L;l9h?!yYbm>0$ESl$w0ytf#%8y=Q-Dk@ zzF@(3`e&bE{{VcSYWd9Z-z~5H7x^D!hfmoz!F@7gfbol2U*ZlmK=Fh3XRDV;4%=Yl)o&o#W9N?PxCn<9zdT?o^(UYxf zdTxVZKBcN%Lu+jiIe8DwA;$`<)RWEvf@|(L2g2SW@h3T@mE`m#CpW#ib@Dzpjr>62 z?f}VZ=ULr&!Vp*0>ia8i{zu9lF!-N+;v0z|wrA6H)nc%~xidLe1!iJ8oMD24g~vJT zU+v$~&*-Vco)+cwarlZp(;m`pPU_F{KgO>f{*_!w;y!6C21`-V!A4pn_p{#n^gkJY zZ14Cd#)0uy`)}fZ!F@Wa1|8cC4CPL@o=8* zHifQlqK!Pml0Sr9T6Fy~oM_V4GYTWo!;#>@~+)Wk22v{j) zY5evG;Sb8&BP^kEH-Un|j^lx!HmsvPPIFk~-9jgd z3zm<|Sn}IUNa(?2D;t7X0KYiHe7IHz&JJA4(gBu6Fv*q^jOXtSql}a5&QB+$dFS%E z42t1doS54qlA~*mypT%%UHkgu6?OuJk8F|`e$3H~zD^DZ83eZ@o`bb)R6Zn3M2zhq zY)-+9k~C_^IphFF>;gtZ9)OXWxLW#^Q=te7tqQPL!(=01r#KntdG#au^6B+Cc^&L3 zl_aW0xZYDRHb8BxKkuKJahwiM;^Qm5O%-H2Ohq^uUfJba(7(_ z9|*COSrzbP1d^$@1apzcQVHw_t!YoFdru<+SXMUgy%kWYASB!5Rs*g8=bZ3!*XviL zQKc2J^FNNmY@MYUMqomN<}V{09=seLpM`n2gx&0YZdTW|Q9q?m_%{0u@Mrd%JgENw zg{`E3I&gG;e~_>9bLoohnPBhQUz__KZPAzK8o4hW55P!jLm4)ZPM>6a$rt0y7%Bq0&gE+y+JBKHr z&#ix>ze4#nM-~47Zl4{*U&N+<0C~;2L9ifp+A_vn;|r2F;PnUJ(~SQBp-tWDc#%qa zp=44SRnMA$U;sus9#3rWJ$s6-XKMnyUZr;dnTHJOtfEsWQW$avUU&l^KnSdAbT&yX z5y@-oD7CmU2aRRrT3FY1P89MFI5`;S@WoX)zK3oY;{Fdar|lQ}KH)&m$Ik_-wnSx* z{3QBu2OpQ@Ylk)e0CfAmGwX1F?(F{nX9#olXhu51xF@gO73M;2(LYb1zu6+hC!F)s z1RAg+C+5ea0C~@U%Cv%4xq{sP05iY%D?yPz;Gq8iwN0UsB)9(ng{^!hFBp+v1V%_B zDl?Wq2_$^%G0lBajp$hX*FUHJ(<>jD#1FHPA$0SANZL$Wz%s-sIA(5F9f#f(#?p$n zQ{(DE+U0x4jFQunJ1w-SG+6SOZfQ2CZNRd(nxv78tG66xh&2Ucu8fpm?s0x6!o@5T zyF)6L+#1G?N=Fih( z2`o&oiZRi18RoSi3c4TC?AE(qBn1ip41R2k*ZId^DU{>v>n#rx5R-+`i6j6q<7*yJ z?cJUaZ}Zewhlt5}BJQ*~`CqcR`H}ws1#h-x{gLkFRN7>@#ctk1044m)%5_U~MCBCq5i*q7uRomITecAu`sQq zNhC5kC_s%zPwQWMjLRtF=p`?T$MQ}=$CMg&dGD81;lvE4aIV~8?pvQb6(b3l$E;oKRV-y zs%jLYqCb+mnISh3p_bZ6okV9b#?B=Kv0P(tCvPVwJRQ7|UGr zM91tJdX~!VUZsd^Gq)uBZ3F$n@JGNt74X9*r%wroPZMR$J9YW&e~TVAc=N>mC}$Na zV(YbryX1m;CGt6=DSTy@di>bH#(rFP#eYXp!@`UsW$1rD)0FS+XD?D)Y2FuCU7-w_ z0FnaZ0CeqMl`UGE-1hRk&k=&eD)eO2k6WJ;{@7o!4}yPbohE+~=yB=(82GERox*n5*2cH zGXwG=V5cN7$tO7Z>_Uosus(uKrK-^W(f?=8wUnmC&UTc z{{P4c-P&%gW}QzgV-vDboyyUiQ7#oau! zTND$ZNaSWeKg)mtZhyP)Uq_Hhs&$j!Pt@}<2`qgT>VFe1vRqjC1}!zG7ZQD*QFS!1 z$kEQy1~6<3AqrR*`?-vO7!a-BE!p%k*$Xao3Wipmn*!-ty#xPVA-y^X? zG>$1Jv$?ce+iQ{_hVD6{w!1oCxF-@N}N}z1svNVN@g>mJxW47UfMl+9GX9pRs z8T>5%RtS)ZK@k{0AG|WgFpIfzNWeVc^*K4?-f8tUP*&7xvn-}YX!f*$8b(l#N%?RO z@aN8E<&q1#ENYSfbFsGLBRKs!&;)o28a2p`$VFJ=%gUpI#EyjE z<0Fh?(ts6@-$cuAI4|by3I;HC94HvT>D%%nC3~V|+E+r1hb~6-a!5yEy9$JkPjiwu z;PF=G@1ZpMh`=A6zEx038^nZhp2vZZJ-EkO=%nth6J1e(tFyhdkGY83l12h9PtD)k zAC3)ZttF|f5=|t0)#H$uyL4Qq(0(z6O5CL@#*RL*YJf} z-s-W9N&CvdZRB)ppKN12tNjaWT~8vM zxne6LAZ!%`u-+Am1CU7vpc&8S_*HVdT(p#(*#xmixzH7i#EtV13pO)>-;b1Y(<(T| zag^27%}z&K2_3Asf3SbU73WY~(Sv8{ z*m++IA3_t!&#yJS)t$;AKY0B|e{od=_b^+Z=4<}|1!(do{{Vu7{?(5$@<;qG3&04C zqFKOL)63i#XCyNaJD~NisEoByKRM2yHYP{roZeZ00}@Lr1(BJ7nq9jv0t2|BvabVy zhZy_7S2dKXS)Vm1CX*^k%E-+GixS5ODt3A0$`SX29sqBYgWR4DGeuOBFi9;D#Q2NN zyMW6gvp})117Iq85LgV3rw6IPJ#qkvrxl^S6zRLz`JctDaGz^OQgM{rA0@VcHx8g< zC!GHPI^e`AoUSqIeMSOqcIH2>FZebGOuN)R1Af-h+QIgTEdDX*Ya<6QArk0vGLAa} zt<%=Oi2jt`AMpJaK`*jb6dVYtoNm`tFPYLmi zX#~>2D`Uxt)jY__kPP7*r>9#$tGuf2UBtTM0^SjxJM_(Zwetf z${g#~W2avH{vxx$#Y+_gdztUK!`1EB{{US%R|^Oo1WG^g(O;s{lTlaL{t(U}{pmR#kgw(1 zYa_~#5UX@Jh~Q)99joK>9vf-8m`zt>`dfhfcBeT*FXAU@J1&3t2l8Y800%+dMSbxr z_CjbLH-Wql;)e5LhwY>OKlFTM7n~lOyA9`p0k1O)hjdphO&#_>N#cCZ3z@1|oD7tp z_0axM?$ZqS zD#sr(>|wkVE08v#u)rV&03L?8oKtr;r&RLq7La3(F&SjHj0qW#K-)*$23kITYz{V`i|B6 z?jBUqjY>~&p@>y#b3xqc1Sf8APtG#@f=3u8yFYr`Z)4WO&D6S*53`jrAC*DD!2su{ z_=?^WQQvdeqX6@J4np$QaHCOmY5rt$n>E@O($3g~Uq?zjo}=^FQrV`zHJ_ z{jGchb>UAMTBe=ii%VI@g1mEMZiiX$#mtI!{KSJTw1rd6wqg~){uT<$*-oS}5%zUf zhxI>!-$*?B!@Sa*6;~`h*ECzvubKQve%hb1r^X-ISK!f{1>`->}Dp{{U>gEB2`P*QER&xcGstcsE$^Y`{^PFFiT-HI&}GsnzVpGE5Kdd;_^Er|@b7dJN|-7T*mmdnb! zR1v_7aKn5L98D$4hWj{e>f8*m%7aMUp?pcS=9HbsJOIxX&Z!J=+{G&0+N2&z+3ApmSfhl6B}oOj4ho&|o>*;W zmED2Il_FC2a@|5_dpYH}kj7eep3+MsxdudlTWnz46_K`(Lm7Zyyhy)kXqB=s380w8-1()OVU9(X zHDWWk2aExnbDj@UJpgR`SDO!oAIq~!*BWDtVD9N27`#F-;1S7n_0D)0?b5PN&g4dI zXbfG%g~{3*4$8|T4w&SOX8`Bd=~U+4#&U{~_|YW{rNB}*v1M4vAQBe?1Jq})%hxBH zi*0H&lGt&Ra1o(a1zQaf!l>YtP!s=I(E1B1Jqah!DH z>6!-GTChTeHa0e*y04aoHdYHCUic$#8U8GeC?70i@Q;xqD6JWYHq{c7xq|LHSdO5B z$;V!MX1Y?lv6QCoq08%$#E{4XZ6yOMbpYUSLlb~T7eNlU#`TtH$p8ZdY&# zl8>Ag01kNV#(3wiwQ*u)YuNg{t~}KhkLiQ{4ZAzO3jWg>EtUAKrVb7>{{Rx9{(l<( z05SfV(tp}`JM~ih+3sSaYK;DmzEHywE>(99Gl8F9!oPv*)UsDIl%kqUn%I_QAjgyU za&fne{VNq&#UlyS<)>rtSN;o%`(^!}hUX=$wbCO-+FS`V{lg>Ir~C@P(hs3kS5e~M z_7btj$(%MVla%>_YSJDiragh_;X z5#Zp*^8k6lBc3@p>HMfV@QL$kV}8$QlK$2I0J9apm>A9bMff5#C3lt2iM0FzN$rvT zO?g?=U(_2%)jR(HXEwX9%>Mvn_{k7N2>$?>Bt#xi<}(h;&DTFtZk&;J16tZ^8>Z~p+=&PMWLRrsl?Kic;flGm6{ zSwO(xXOcLM1K9o949?tH~dlB4sTcX!~J~ACd@PYWagD0|3JXbH+&SIc6%Z zqG~)DRw@^vS;}>dUaS@k(&3 z6(#1bepBLGsy`j+t*hGW%Gycmw?Mtrvb4cub{ekZT3e=qp|0Q6wVxc4us zn(*~COCFPYFNyv~=2>SH;_}*&r-;9^JTFwRy3^-tONd?ui2cU})%^%PF~@Gkzs3&` zxDSc=Co-#*XY?fsk!@buKc2_k!#SmiqZ-s@d!Lm*@K=Kq{>R$Xa~y-?Ei{B2439Li z8RMWN1JeN4?_Wat6=ULUDO+d1$nmRVs@KeD#rJ3O2dFy+-pL$o9qU5d!wDwYD-#}g zC#cV+?!D{%%Z1|zb33bj=jnKOI*vCyE5`D!ZWSE@$tzAHO}IN>0l82zH#k1)+Za5U zk9jt>K9c~Fm-Xgk{0eaw?FaBmcG`^x#%~Eo(Sf`t{8Sbo^YzI5yH#Ljp@@QKt)Iy} zg14vnpX)W z#A!Vl`YfrdU7XeQ^2ZtmAyuu?d2Pe_o@|WVMfX4lXaSESIrZZPtn3PsTApp<+p{Dk zQbpL%i9)h@jsR3uUB|W;*pr?z39eM`+C5AZ7c#l?mbS`eFZ;unB&+?^!w@R zSIp%38=ehTlzP9+{{Wb;9kNVyE;`55X|(D~Uz<1!#*vyuQc9`lxe7DE1M~HKz@CqufYV*5yfH-dW_v2uD}YE02y||(^6}cn2I%!f)O|8-7 z-yeTxpM+nvH^UuU;V;II4a2YaXIY8b-gvg#YknQN+QuvWI_vMlJWO&3`B)OHtc=Qi z8g=DfpVhIN#hHx$7Pk8j2^1`SpFY>;Glo-OYe<;u@8vvwP-CaJ|KJu z)h=0lGx1MP(*9dkk;}zntVR)**6i@A%tSKtv4aw|^Le)p*TYNO;kBmvAMB5T{{Tqt z9Pu9kO9zdbr;SNoE${O`Hf;Pms4;?hVH=f2v5klbcw9Iez#Dfd=jP*}z!mfGr^`QP zv_GjMR-B-vOI6VJ&ja{qM{tZF07p83Hx9%Q%vn`XnF}G2iWS0@?mlYtDN{{cuV~`0 z6{+?&gLY|lmX^`W95)l`@uTUJKxb5r7FKEQA`1IoK5~Dn41Cqz$jBDAjj6KLtD*<_~ba+b6dIeLgk2)#cr0O z7^R#+dkj-F%(u4DY4+;*lG-e}jwjrArWi=2NCbSiNjidhVkM?ajNdqsC7ai#R4Xw3U& zaNm2%jf)}x0YX^jt-HeE7L&UyLj|N-g56wQ#|^=bTfIS^7%uK)&zTAXw8lhW%E*## zCA`$ZQl_(Qw{~n-d#h=_(<_5_Z$6_mhTcyy-uYAP@=3XfC5HBBurL=hFy2lBOroSq zie+V*7K(TTlWH26)1$Rnqn21L?e`F-368^-Vz5RS1SOsM``9BD5@8p6j!9bX8{aO< zJvIrXN0xZRYj(j-K?A8tPq@tV%q<>2P0Pt<(*!VyDO~$Ob{{V%fr8)Ud6df## zpUS_?Po}ihJj(?9rTMei%qQ%swdwgE(m)KwPR#E4a!vs`^y~is>aX+b5s6J6JxoR9 zi!g!+KshAvKx5N5{RihaS)B1VgG~Gu{{VvKCiwpVXGwll9!H2!u>deC`e7Ld z({VWe0A$ztSM(s7l<{`{@9KQUbyD-B*O~nD6?K&DE48F594`)C15;6Yk7a1glC#FkBAlRdj>BsVjorBJdB>=xmEDaX z?48a>Uuet>fT4k+$znr>BOf;h+~9TR+Nx1W=yl;6=G4Odo+XigYhT%U_hFazkMKDP z2+M!PQKycby5pXeMvn#c$oH1aN}`uV6pIxu`2sO#Mp@ zHT8OJb|Eqj*(V(H*18-T*e3cSDA>CKp#9<0j(<-d*QOM;W4hmR$=I5`Q8ppsi17mrqzH3WWRnY&_SesE22(#0E>Wr<@%w}~!f zaN{lW91sq2SOysdyB<$XM48~p{6O)-(8`bzwljvgS0(Wpvk|nN!*f2|=cqjJD+x)b zWOF&UvFBbPEU_UB%%Pa8vh9zoiaL6pxbFC?lWAQSLRWepH~72(CD9utVD4R&z}=si zlD!Ws$J40eHNjPLCf3K)<@{DDOIQ8#eLg~X+&%!ebm0M0HJFDnc8r`>%dl@8g z6oBk#&H%_i<6nq)v*2F`_^Xvm37Sxkx3lG%R%!gVGs`o2xCXgihvJv~7C*rHzLonb zYWLB>HP(*@iFEnpy@d;|;pZSiIxB5s%VBsKKEK626MY`|hr!Pk6>LpDY#VQqeHyo8 z?lNrJo-;M4c7Gao(#l@xjF_D+wj#@4=Zu{CM~*rIn=r z0ENHdTu7=FT>k)X*)Su&spr?VWkNT^#h#XX87zjo{_Fgi{=Tj^M;Y#8UQf_h=u~u? zKZ-LL^G;3oW_*e|4D|G_eAziJg)A7f ziMZt1!VmL3YxE4KD2%fBN-@&M_(kH*Lqjsov0l|`=P%8l75qc^LwBQGTwPeg@>?$D z2mu|*2j7fW7CR@BhcprVqn_|~B7<_Ev_5mxG^@LLOl=!3*9Dhv0OuWR!>N|kjg_tD zd`G{BpR%m1(fGytYySYjM7}wGz`wQk!mkm0EWLvN0K*!$!#LB&{{RlPc4qM3jxCo} z)D{sc1C+UkbF>#KH^yCu&W|K-;bO4WaJA!!r7KA-x;=kCGyRDA9Q_}-Q^n5$@sZA< zY)hJS>ZR{N+f{C<>81K~Kcw&2clP-B5&M07HH*gI3AIyW;*B*CJZbRrQn-ygN#SYO zxBAG}@5cKWq{)oyQS_WQoo4(ykA^!es^QA-bkF8H z_Wl0=f;d|E5)E(TC+xRpscK#rwu!Yr5qv0VA5+#mDHC{tCXE*= z(yrxB9OEs)%AfX)2W8a%08Tss`dVQ#2SA^eZjQF!_}wio;Dx4P7+TTAtJ4 z+e<`_4<;Ec8P+&1<+DY!B1b_Ha>`h?@f>ZL0g(KsI5<6#N~NxP5pHw5_Bz$m?8eIo zSr*Z*B9{6aYjFe4Et|`D&y|8h7T&R(0t@Cq0KnV2MuvsCu+wbr>}{Re8>nS#i#x}7 zW0{bu!5mVDEUUDCs|1~=mL-X;Y4rfSSnX~tTwaw-9d1o0?9V;ouEqqR3~xum0MdKpgl)Uh1xYjbk&L8^(hC{j6w29tX};y-vAM`vs(a=@MhDghX(=612a zFT2c71d+V9^E*Xz6I{n+_R*)>uPuOd-y}twC2~}cnF{$t9o#KQMouh&?Znp?47V1S zTAM-UUB~;|PqF;c`RyE%bL# z+s2X!r81D@Fm(k$~hg)kfQb$qaW^@?YFXEcX!31ZyK7pJPm};1${!1M^bm zN%HhL$J$)PZ5fi;>|BN>=KRrlEr4^$$Q)$+kXT3Jv(ldhU%LNaV1{Jo3XAK?|j_d#k9)x!tYWaQM zVaf@+kp%j&Ypx)2%aVbvdnb=D!;f8sr8u6$MrJ7(C#Peb28-#}Jx< z_4!+T$o`Z+;M=qR02aUYoQnXk_>-ePG6!8T`gZMK7P3BOO{%cC6FN~vT*N$TzsdGTJh`WP7cv$ zms>GP7EJsN{{VvC-L%j9Kf)#?!)f9P6cQ9UX*AFWJ(%P3#~H8nH|PzCmLfR1lm6++ zh_35K57hpB&gPYoUT0QQHq{C?w-^jmXQmGazqTv=LX)yKh2oIpNX%tf7=G+KF_HdI zR1ikyIOl2gttriJ$*9?4RVy1wox7K4*iH}@`H$#FxHRWG=z=neR=L7!^5rKp4`AljxyvZ+w!puyX6FDdlCW2 z$?J+7pEZ&5ZN5}NerC+W%TuyqjPA;jiBw0HBc^!`y9cj9(xz$N*&x3#9r@BS9uloaj z($QVAn5_IMWvPwIIe0aD7EBZL1K%WfuM3GVv}b}-X*b{IdYm+;{i#i^jQ;>XO=%+& zN>W5I%{1^A3+2iOm>Faw4x1IR&p0DI^X76>RMNTqNr2{5V7nv0{A5>j)~peHhH`~C&Ma3@ms?ZxA$*T(I!<{8Bm6J&XZr;1Qxqq$iFrJB75XJOyC#1TC{x!t z10P;T2EKDY{v|(|`=5l8alm34@;{pE)Da}$Iyo1N;s=bhs~Uj2w+6^8NEuZb+ta0d z&E4*Pvo@_`nAIXzW|B{{wBcUnMwZ`vq1)wr9sM!@%VUGb91Lq-NZJbS=Zg55$$;c6 zvqp=U7oLthp6{ONT+jSu)QmXew;?GNy)bYwI9 zDa5{8GPv8R+>l2&Bo`->_zL*GAi_rtPMVjQ{^Nemx*t^VwzU_sant_*0zW2yW*^$4 z;`i*8@n1yv!SG|m*E$!7v}=L(%TmnxKZq?@gC&*x1%k~Jh37ni6_u6MSbTz~Dpi&v z4(&DlYJX*Tli`O8_@TqqavmX~rlg-Nthu85#qWQ?pXgEl00i0p0D`3a6#oE%hIk4K zZ7S~9!M`2+J*P(gIQV6Mc8Pg#>WU@QG`2VsTRXE4m*zvms*=0EK*}?EIh`M8Ju1g@ z{8aJ7=W1+!(bz1ZtlqTNC^6O#=1bL{HFD5QvPRn* zddz^8$nzbtq#z&bMoXDsGmHc=*>zP?l-1Oe?tie^UT1>MuyMy_^HZlZnrYv1mHa5T zF!tDm5Dr#KOT9?Zu9ae9L9584Sd+B1Z!tDFSUfp_Q#|u52uAE-ob192nB&?P8M=*+#Qm$tK2(?<&SsWJkaw z0}OI02}LJ#N)$zK&|CSsoAGOM?_njro^Mgh?P)pKpS+P-+kec#m^lE@F&|I1d&?_T zvr8*GXui#N9mJqr&lFhmiK^VKQ#>UM-nt~%Iwl) z0Fo<^H*X3u6c!TGPRw4<%&Tm-GDMm(xq#c)S(YfbcOu>?BW8@M;ge|jBX$H5ktqSe zZH_YKB2?UumlJZKb)9w@daP>Bg99UxoM3f1&P{nWIv>ju_?bH^mJzcee0~St8#L~jf^BR}$}q^GgNMQ16;Zec z95Frf+l9|+zHfJwwLXk`h1@X`oVX$U;$2Qd0#lKmdvTs~)})C^$&3*!Y7552!(o6d zrQg0VeHW*-1nY2k%x}PwH9sRVjD*Hlj(YMsoN_+^n904!CNd;omR1fkhm*=bF+VQe zpPzoUagt9{bp>yD!POQ~Z!lzFu%jRjBuw|mAaRrV0p7JPm9#!=@v=$`;$nz!eqeVN zz!}fwjyh(r#5Kud>$29b6Z%E}0E2JvpA5fg*WH4CBk0J)t^mfFH~#=$zsSGZ4T*HI z*>$^5^JfPc*HmW~zpW4HRqjfH*i04P9AqA&sQ#YS{$bV3WTShZp35@#nrz2;@^ToL zXZbR8~}Z* z{eAtP*QtcX%0J!xPj3xTN}Mi!2$-X1P*U4(mSQI$9D*`F?-}`d9-mtO08-@o9-L~V zhrzW5Q*4-I9$F|}qiGMcU}JDm$8bRG2_qz)rW2B7i6l+Z4a|1}84H&}KsY%Z;B(iH zV_PKKvN>CA-JBnZWtZ&IIF)x1!o~q0ZNcm5jAZ(C=|q<+J&eBU$^QUbng0N@C8hoU z0PR=s2m31SO*2%3<1dHr^!-8vN4eAIw$&iHj%41fn~~xS0|p#&t(+gnd|~3soGZbY ztlopWrmntP{(<605dQ$H9v|ZKica&Sllk=gY=5cW?8W( z6qRN2v=0+L&{?dDu`1aFm>rpveb#Z7AI<&;eJ}W9kMV!%&TiEVVs=fvC2z_8ACdWA zfS*e~8{?|5;m#mxhCMeGcciVgpGWvUosZG$c1MX_8C*EXWg&qF9@YJwPOUo7RVhg{ z`mImxsuHJK3Us8JdaHSsSDa&!$Ee3Ws>*sMf0-}*6|#9I{{Vu8e$=bx!y5kp!r|4n zE2bV`v!Ec50VHiKFnX}>!TKyDmnr!!bsOTRe&^=AfB_?K+E|T(NYLUru$vYJlE&&nR^v72}u*JDH!xGo(?eWo^KIeu~oR&Ilk4mxKkyR#?mGlkaM7LBqk zrP^q~hYw>+}8%KLNZI`)M&?NBkxJ58627e`o5W`gZlNZz5_AX&;UF#QE`5T7A#! zesvkn0q!}?eKbB4eg%q)YjZY5^c~Iy4hOAK=P5WVp9_D%OC)H2V87aSM2#L#_(2h4 z5;0UpI*q0YVc)R##d%rsjI!lz{{Y~hPr*;y=E*bs`86`;OQKd;8MQ}gC5eT^cM48R zMx-3%vF)4@fOu;8`6#vKe^B7uV?v#e1o4cqNM$j-!y<0A^5bwA3y?PT zIT_%o(D!f=x2fm<01bS{_{H$35UnHpQ1I4V5r99oEXeQIJM&!7O=4qi=g?-_aQ-3x z0CoOM{{UdrVr{LM{{YXr6aN5wgI}i7+CGQyrefNuH|GBUHdb?xea&{sS{6Qbq@N;X zSx?GOTDpJ4XAU8!7ufzqzu@DU!u)yto8nNR$HB|_BX(kn8~b&~EDl0~6tfKBb6+=} zHtItDf06sgg*=Ncukt^eQo#kxf;0u^5Xl=kV(!uTh%h(+ffyWcRGf4*^Rh`hBlRhz zYn;}v7#?UAy5!jIUwh)bGYuA>R}s`Oz{m$MtQAQdMHb{hX8JDT*g_u4l;UwA}fNjNkw`7 zr>lM;aeZ^OIQ)|y8O&HAS^zoFVArJi2Yj` zq?hC9e~uqWKcilA;dVZzYnD@Wve~xdB)%TJvP-J_n$>kn_P5D(VkAZeGD{X6KOX!r zsm$?Iu#j~V(EdZkxzVQAZ@}@Ni60W}JTq==boWVhIb-<|e(ZVUa}k^l57hp3{(XHX zK9u=Shgof0qb8c3K1ro#@SmRNx!S|7SWDjQe1WU$x4OODw|Db2)5!k-Gd4(E4g&fC zivIu)_`{0xt~btWXL;ReRI4|4cl~USgvMtTE8SIknRdF2mUEe4d_I2gILQ2~!J#_% zX~wo5O*Poyz-P6x>{qwL%F<6qbLAh}WA=3L=f*{!#r_wxv(f%1$7*Aq?F<%n9x5Oz zsJfBJSgut|6uLY%#aUTY2^0Mn`YZh`^IizWQsK@is^@@qSCjaESE>GmeHVV4xb6bX z>2TK?S9LP!G`XAje^!1zc-z9d&x5royf3d<-{}{+U8s**gHpGOC1qKdsg5J%tD~+! zlPqM6$s)J!3s?I+!hBhg@xEP4Iml}&lw_}?+vI<_JQv1XJI9$dJoAQe5vzxiz0+EH zqsiVBxM*bEs7G&Y1a`JsowQ0Oo$#1}1A=4@4tHSXB8cY;xP4_xagFcL`smt8r_j)V zqAlK`Bv#9IZe+Z-XN2kVIFcqrZz-hQ2yh#n%Ax{BOAs9yHyukOfiA7?Zr1eJ6ZYkT z8Lnf3NG|4_lPkQtLlL(s08R_;1nwra0qzFzHNd#DH*e*@w<{gY)1oA52+JL*8?%5I z6v`f@m8diQBzYGKFpDH9hDYyP@$_NS<3B$6zkV? z?>h(S35~t;n(mFgBTheHi_4cveWoM);v$Hd7)66V@wa^Pm~_jT zX55zI*6!l!LSqP^1=}i&9$39h$VKtyC5|F?cfuvEE(C9R*0*e@ev+e5WKab3eh^34 zb6145c}O7+KP>L$D!kVkl4YcGuqgQOTio{!Qh);4&i*X-(Fi?>NNz8w1am8KnDp&v z_zo>4MQI|0KZ)z}2lQT$G_eWy9240o=ts|efm3NKWvP^>8{31>FcI!+$J1Mr}GAX(rXo$c<9MEF2GuJ0@dZ}Ouad`kQB)&8#!tV#l2JkMm_ zc2*w>8(#9I1-z>LUOaEWW%_1F8h86tf(waE0BR zpjg4s*js5&v!r@SwEyY2-H3I6aMltZ68-l5%k?l|pvfIrMhPv8cQZYsIeRQ-3>}h| zT<_iqFhRSS35)NE+D;@LfT)C+in{lF@uUn1XeHgct+_+5WSKSW#4*X)Ww1k!b*8L~ z*dXkK$lym%{Ckbxn}B zgNZid;09&C;Y}CTcw1V*Ci%;t)Yi6@s_Ovg9pCU*xzDjLm!!=!^R?_Gtu8xqUoVR_ zvgCDqydGY!zQ-1nr`^|gBK_2TD2S40QJ1rze}<)pRxbEs(<>+}qf*Wy<?r80`z6T1?i%>F5#pv4~CK-rB8%u>kz)&d|~5;n0a$P zd*9kUJM8d)BeqR(?S(bHK=#d*{-zc3&r-Ke&z8IHBPl}K186t@{{k@I^Rf8;uz!Po z8mL~Xe;Pii`%=N=!a&cHBMds$9&T8i8H9eOu|SIuwZLN1(y`>Zecj}F z-q=O6ZxF9{U*BIS{o!vBr4tuU1yIjA;_P;+q3DWv@#dW670z4V+?%1;u9{Ubrp%AkDN zt50Z#{K*q7*60SyTLyvwPz5A7bUXNqt{Mv2Q&w71on*-oD}6l1Rf$Gg#;Wl}xR!CM z)_3a-6<}hBuD%V(rto$fo}EIOWpmW~TFjGoCHsDcP<1i;xY&OPE;%Jez$%81bi2YV zkQ`N9HevA=Lh${Yhe`k6aJrYfdR5s(#Hiq5TQ)FRu_V0^CY~u(#;NyISCZp)X%ane z{FIg0ru=-O>p3DwwwLu|JuQMre}jWtuN4*pKLdhXjNl*sn^{mhO~pioM}r#Ro}a5@x}$ok0Mp=s>s>TlQ|>-akt!thG@L&txhc}V z0`z)5$$h1k&9`u)9=W23nhexa|0d907 zUt_JnrR1Pe56po#KY)3xA@=Aco+t68W7cJf3+hj%dy{v1P?^jwsz+jZ1l~t^H8#bU z?xO_nnq$yklK%ebK>5e!f$1hEQ+HA31d+ASEdfwuSEl5@3 zwpe8Y2(f`!=;SyBmR-RQ8_EYC5h~OB)JId^P@H6F+}9TwtlF>RfqRx0=gr=f!Jq)v zt4mz2W8L4c>UZ@=+``~rZg?)u2SMC{{Lrt{ssa$S_Zq!;-ZZIt1#JEvliiJhx>|yU z`A5#w)e&K*B4UXZffxE?@*yW9HLo!Icc=08J1InG6Odtrs%;u#pZp#P`WZ2!Z7L4{ zbTV2@{EzJ4qm_-=fX|r~cIkKmj|btl%O(O^Jm`rbBGLOf!n41h15t8wEPb5=(sf69 zMKmkqit4o#YX;t?g0^zHp{}1`me7M(DC2!8xlKeMTWNPLmetSOl&UHr$(2v09Sv?b&3DaNlHi(By=lS77EY9y zolR_JkwZ4!)p8LunWiMy!}J-bG{LbW)t4{DL=i(ci{R- zk2u<@xQU!nLoM%vuBJ{Rr7k;V5ae z<~tSncTkIYMTF78;_R}6B1Cx=;#)<~W$2WIfkB+EIjVFYLB$ zw8CVCj(i&TwLq;$>jFTum%!sJKOgy?w&r^v{$q%$^{#qjt8JFUYW8V!aer4`d0*@8nKG39e2ZdivE}O_FI?ox8Ao=6P}$)J=_B^$;bgEeTLzS zW`EoJ)+c2?L1cx>t-0?R219inL#WnG`b$HrS$wfc21*?RaoJSCw12byLog`W=Jc(9 zo!l=A^0&5IR%G)Exeofbw$@ecXkZ=rnlRw@Qp!X8W626-49UVVq>^aNfmu$Z2^;;H z=%U6_Rc%Iju(8#{%c3)h)xf%=#aF)xZ<%)=aY@mvCoCu>OlX?#PCADu79&i`m;d~i z#fwy_lLTT2T3IcgA~^)QD;&&ZKBm;nK<0HwNEVNk(%Q1j4$|&O=UVx;&c_L?Bh6A# zZ%AVjtJ=``9%aN3#bT2sJ_<_mOOp9P?Nj4a($VG}tZq@Zn&JE@TqoU@LQEKfUbbnFhDIcOpy+y++VqE>prHDmui7TcvNd0`sa?Ou z*?Xn`-aVbTP#R1@R)T5HMq#I&q-bM<@cwMVEed7a+hMs- z4dt5-z1En)cO=d4;sjniZXsoJ*b=R$Ml#!9nBB<)+PN_fM8cVPCwM%Ta6R(NjLAQ1 zYLV*WzbBs5b@Z+H_@sY+IwTsGAuiOT6w+0)q%ooR{ZoGCm>amoBFXLa*@6$TD5;I8 zIl+BEew2;~Ay|~)b{JMK^{Z%OfbHZoa?eZyf#~B16lG)UW|sQ#G#Z1_>IPMo{+B6J zGI_)#Lf-mofci`uzVcTqrqaLM{Nb_e`h`&=&@fuOCP`kfM8r(vzLJ1IF^b25Iz^=v zqcEab?K;HqLkRDrirM|=jx}Ya!xbx~bK|#!psh6?$!+7exts)&9qbT=Rruc~?P|5# zYH1LtyP*RA$fB-{>d6LPv-93u5>>R}mC-%RCA1|z7!@;P>d;QG!lDOk?x5EIo14A* ztdz^ONvG}McT$|!EdeF2@@jV5kBcNqUOCP&h532_5BWnJ7$L^J`eW#e`tc@K)f>(D z_E8zM^!)TBruu~Qd&Y&<8fTi8FpsyKI~Nzuw&@Y$wES^Qe;4-5AlrRDc+}|`7~jz`dQlNYj=R}@vepWhzoHyLSW%$4I)1*}>2BD4Q3ncF-}S$7!r(<} zWc;u6e)X$QE|2s$=K^%-o;cw_obb^P8qr8_4`tA3KJZnjI`VB+9doDgf478j;&Zv` zrAiJ|c);gFp|54p!XoK?`=0KEGz9C%*KKF8phtk>TgX;?dLjx7vC!%VcarPbAwe$TAp{!nke`oD!eWcoB=@O4n%`ClP-r^e$^aNc z9idoS^UQB--e>FZw++4i=^_Z6mP8lx-jzf?V&0a-cpUT;_W;a_x&5|hwZn8Xi6EFWrF`0yY2P8|4D#>&3LDk&dz`0xngBxh7$E{T+k4;OWja z>se%VHQp3wF)Hk7EhRvHHirhgb4OqH>;14I>zAK}Jmq*1SL4rHuxs9?pN+m~`hf3) z7AyH`nvCtcv8$^A?8?%49i|T!(lQJkgGc? zj|ARo;^9-Olh$^gfS$k$QvU zipbyC9x3FZ6py0d{}6CFf$G~&moPw6Gq2${0yb+|yn4(`ihamSu5isW&2~puoM*!K zn=F35aw1m0nn%Xx^PH=NZ=hT@IkI&%=GT+MVYmiZFItfEJyS&qjE^nR*A@qqZx1ZN zZ4CxlGLs!g-vQ(SISiw$`HT`Ay%lYL&wl4arQ%mxj6PHH(?C-Ne(iMbQx|xwX8q!q6*Dq=?)c zKBJSsr=VVR$*uf+8hB??9qnz(PZFHXiD6_4RJam|Du1+tG42puA#x&xrOxp_fk)+} zO@6GP?pZFDrETRAWas9i?Vu2t`GhpG0r2c=JRbh)4MT2W-4hOT5qcVbV9Qlc~nLA~l?Q9)+;D<;g8-~%5K)E!+jgQXKd;TJ|wlFy{!&V(+M z=|vLTp)A`V|8VzDyvVf7S|4dzTg0IOAwhnszU8*u+B=Ywc*Wkyib(OZVNP=Ng-?1+ ziB;Ur;SWphN#@2WGbd43l(7OE6dLB!L06q4{}#TPRtS(SLFOuZTWpemhjUOJqt$lx z`{ez6bdsxD;1;|TBo*DU2!yLC`e*)CryXGEW`(0QduVa$Rug=b}#;5;nfM>-z$r2_cHNs$`VmsUA!?`!=cx|Y|8#8l*t&t<&U+mi6eZyR6VMEsTo@CfW1~r~5pgr5ujZh~FAI08TJmSsjl3vYPs7 zE;WiE^a13t(D9tf<^ogid1ijhm#(~2irDe+D}zz2zqmMIphR||jv?AtZbCCmD)7T1 zw7=)zK2c|gAG5`=o2~?+h;`@|OFPZ?Zyxx7;Yt10#^&lWz_*HKo9fAqcW>qKe0!r~ z4`IP1gDRLwXo!`(Lul_@t!#@>%zeZ)*;K#K>I4X`hJO;M-7cLx=V_+z9|15hCnU=| zwrZUsDG=~OYI3xfuEzc^5HGfJuIQrLKNS|VZLIbU04lZKdAXNtr$Jp&F7sjRYx{B4 z;Bov$-B6oL34}mb2Pv4JiTGcLu|eh28Ugt7Px%8akcBapx$p@shl6GQnTkRxE@dn* zU6t3YY9~(OpmL_Y$4r480)^`)2RG!YqT1EPt)>%h>a z--Vew!hxxRnV#&lwn|zAD@a39K=-YIhYsM6y89!4 zH{dX9i#@z2v+*syHrMVJVYhs_9n<9!Tp54Wu_!Tm5n3}f=SkOnZxgyL4QnF1Y0Q z;uFmPnF%lBkUB=>@42ECy9$G|py=fV?aaN5;qTmY`{)TyaIj{T$dX*EOzhZ2fbPg{ zyvtCBqgbPoe2gAxt8eKgpUoPCekHhzs7^*;M?}vd#F;sdnwQ`_pY2EO|7HeAXx(YE zvX2J%?CquF8#VOP-};*I)=({Ghc%)xc$v(4xY5;S^GIp#1n&dC7u0uHX3aCLUf4We zy)Ye##y59c6crk}bpSi!* zln1lE=X7V*?m15^7e!3`xq=k|n#v_^vqmk1ZuGC5jhdp_(&YHBKZ>0ZqN~zm=Ot+{ z$aMX%zt}>$UuWg?Qdd6uIHn`X7$}9~tNrOmtV{${cS4@ z06k6%!dM@adt;5iO4Wa4eAT(!qYJ~gh!o$s@+wv>Yeu_h##p&guHJZ%<}2t3%oZE@ z%1ZZ5qoIvC(YllJfuiHe4p=8T;x;UC8r$NOF}^9DooDDT4HKR??`jf8j*8LPe(Ozz z7@1FWaNWqwC}40sCJ+{N^B6|5Gc?Ev;Ke{ZF?>Hd#Ln`hIDzm;zw0?9=Af*!bG?v^ z+ODS+7(w>pL}UD&gu_7i$5S25@FHg)Gb|x;Y=F-|V~v+|%aKWlxuKabknSLL<=gUm z%-rQQ1n_u5Z>R6@!;u3-GF2`~A9<-Xi7$5A)EVKUSCY7E+1wV5@tnojhovwM$dG`R zW+)!H*?T#~VK$%B5Nt`TEOj}=@1ySyM#j_*52UvQ*1LVob{SX2QArEX==2jy`sFer z#$;I&TJ+YNueJ_f*5!EN78+3{w-(=|QYqQuHztQ3Oqm;-c8Efk zgHsw5Pg*+J3ft5w@*||jR+I@TgU^f)bAq@xI)*CmKJ(hyM?*;r$a(>?BYS2cqHg~o zysJo7)gPZwJTbHbPd3{M4{H(P3c!J0aL!+W?>wW;53bRVA3pOaIlW8pJYHyHE?0DR zpHCN=PO-E$E67;l9P3GN_sg4~CijQ~CmW`7=dxJh>nb&Ar_ykEy!ip56h-rhd6D=3 z3gv2k@%|D`PE5}e?6J8-9oTrWP2k{h)y3H-W%?}+uosR5SDqwWZ5Hmit1*mA+LvU@ zMR!fs;^G*R(*7B~(vyQs6LnbSr}J$xf4p2PS8&fmBik5po=0bjzC&d zwe4T|fC!h^jd)9mpD2{(yBXt+zX9LK-|7}3C->)3XGV$MvdSF~kKrx8T0d%Pc4OB~ zKHt&2GSHlJXbP)GK-J%J;c26y^aO(NgD~Q@e2t6W%RKXEw;jt}t)t(HWc`?_Zw!zL ze>bT?k@GY8n}3K-ldh;wwfWGJ`T0mt$wu)v!E%c5FSn4(&8td72M;t&r?m=HRGjtR z)9Y6xJEYPHzqwZ3pG#1W37^AK2U5l&A+1_9_odUHCn(~qhHmw9`^kpITQrZ%?JC{> zT+Ue_@DzoIDD|t=JorAg4ACrcg2%0A-PpIU2^qZb=u7$^g3~ve!~H*s?42d7wXv-k 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 jy^NZPmCP>^N|Ke4BHfLu$9{4*4W#E%O8T0?J Date: Mon, 19 Feb 2024 07:36:29 +0000 Subject: [PATCH 02/24] Bump org.ajoberstar.grgit:grgit-gradle from 5.2.1 to 5.2.2 Bumps [org.ajoberstar.grgit:grgit-gradle](https://github.com/ajoberstar/grgit) from 5.2.1 to 5.2.2. - [Release notes](https://github.com/ajoberstar/grgit/releases) - [Commits](https://github.com/ajoberstar/grgit/compare/5.2.1...5.2.2) --- updated-dependencies: - dependency-name: org.ajoberstar.grgit:grgit-gradle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d9a8f1d..03b2d19 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '6.0.+', changing: true classpath "gradle.plugin.com.matthewprenger:CurseGradle:1.4.0" - classpath "org.ajoberstar.grgit:grgit-gradle:5.2.1" + classpath "org.ajoberstar.grgit:grgit-gradle:5.2.2" classpath 'org.parchmentmc:librarian:1.+' } } From be976d7ba6a2d39991eb0eb95afec7e0de7b1c64 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Sun, 17 Nov 2024 22:22:07 -0800 Subject: [PATCH 03/24] Almost working --- .../client/event/ClientEventHandler.java | 46 ++++-- .../client/screen/widget/SmallButton.java | 20 +-- .../sweetrpg/crafttracker/common/Screens.java | 4 + .../common/addon/jei/CTPlugin.java | 32 ++++- .../crafttracker/common/lib/Constants.java | 4 + .../common/manager/CraftingQueueManager.java | 133 ++++++++++++++++++ .../common/manager/ShoppingListManager.java | 4 + .../common/network/PacketHandler.java | 6 + .../network/packet/AddToQueuePacket.java | 30 +++- ...Packet.java => ToggleCraftListPacket.java} | 18 +-- ...ket.java => ToggleShoppingListPacket.java} | 18 +-- .../packet/UpdateCraftQueuePacket.java | 37 +++++ .../packet/UpdateShoppingListPacket.java | 41 ++++++ .../packet/data/DisplayCraftListData.java | 10 -- .../packet/data/DisplayShoppingListData.java | 10 -- .../packet/data/ToggleCraftListData.java | 7 + .../packet/data/ToggleShoppingListData.java | 8 ++ .../packet/data/UpdateCraftQueueData.java | 9 ++ .../packet/data/UpdateShoppingListData.java | 9 ++ .../common/storage/CraftingQueueData.java | 92 ++++++++++++ .../common/storage/CraftingQueueStorage.java | 92 ++++++++++++ .../common/storage/ShoppingListData.java | 7 + .../common/storage/ShoppingListStorage.java | 4 + 23 files changed, 573 insertions(+), 68 deletions(-) create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/manager/ShoppingListManager.java rename src/main/java/com/sweetrpg/crafttracker/common/network/packet/{DisplayCraftListPacket.java => ToggleCraftListPacket.java} (52%) rename src/main/java/com/sweetrpg/crafttracker/common/network/packet/{DisplayShoppingListPacket.java => ToggleShoppingListPacket.java} (51%) create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java delete mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayCraftListData.java delete mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayShoppingListData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java diff --git a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java index 8269078..bddf48c 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java @@ -4,10 +4,11 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.client.screen.widget.SmallButton; import com.sweetrpg.crafttracker.common.network.PacketHandler; import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import com.sweetrpg.crafttracker.common.network.packet.data.DisplayCraftListData; -import com.sweetrpg.crafttracker.common.network.packet.data.DisplayShoppingListData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; import com.sweetrpg.crafttracker.common.registry.ModKeyBindings; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -15,6 +16,7 @@ import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -26,6 +28,9 @@ public class ClientEventHandler { +// static boolean craftListDisplayed = true; +// static boolean shoppingListDisplayed = true; + // public static void onModelBakeEvent(final ModelBakeEvent event) { // Map modelRegistry = event.getModelRegistry(); // @@ -54,30 +59,40 @@ public class ClientEventHandler { // @SubscribeEvent public static void onKeyInput(final InputEvent.KeyInputEvent event) { - CraftTracker.LOGGER.debug("#onKeyInput: {}", event); + CraftTracker.LOGGER.trace("#onKeyInput: {}", event); - if(ModKeyBindings.ADD_TO_QUEUE_MAPPING.consumeClick()) { + if(ModKeyBindings.ADD_TO_QUEUE_MAPPING.matches(event.getKey(), event.getScanCode())) { + CraftTracker.LOGGER.debug("#onKeyInput: ADD_TO_QUEUE_MAPPING"); PacketHandler.send(PacketDistributor.SERVER.noArg(), new AddToQueueData("TODO")); } - else if(ModKeyBindings.TOGGLE_CRAFT_LIST_MAPPING.consumeClick()) { - PacketHandler.send(PacketDistributor.SERVER.noArg(), new DisplayCraftListData(true)); + else if(ModKeyBindings.TOGGLE_CRAFT_LIST_MAPPING.matches(event.getKey(), event.getScanCode())) { + CraftTracker.LOGGER.debug("#onKeyInput: TOGGLE_CRAFT_LIST_MAPPING"); +// craftListDisplayed = !craftListDisplayed; + PacketHandler.send(PacketDistributor.SERVER.noArg(), new ToggleCraftListData()); } - else if(ModKeyBindings.TOGGLE_SHOPPING_LIST_MAPPING.consumeClick()) { - PacketHandler.send(PacketDistributor.SERVER.noArg(), new DisplayShoppingListData(true)); + else if(ModKeyBindings.TOGGLE_SHOPPING_LIST_MAPPING.matches(event.getKey(), event.getScanCode())) { + CraftTracker.LOGGER.debug("#onKeyInput: TOGGLE_SHOPPING_LIST_MAPPING"); +// shoppingListDisplayed = !shoppingListDisplayed; + PacketHandler.send(PacketDistributor.SERVER.noArg(), new ToggleShoppingListData()); } } @SubscribeEvent public void onInputEvent(final MovementInputUpdateEvent event) { + CraftTracker.LOGGER.trace("#onInputEvent: {}", event); } @SubscribeEvent public void onScreenInit(final ScreenEvent.InitScreenEvent.Post event) { + CraftTracker.LOGGER.trace("#onScreenInit: {}", event); + Screen screen = event.getScreen(); if(screen instanceof InventoryScreen || screen instanceof CreativeModeInventoryScreen) { boolean creative = screen instanceof CreativeModeInventoryScreen; -// boolean dtLoaded = ModList.get().isLoaded("doggytalents"); +// CraftTracker.LOGGER.debug("#onScreenInit: creative {}", creative); + + // boolean dtLoaded = ModList.get().isLoaded("doggytalents"); Minecraft mc = Minecraft.getInstance(); int width = mc.getWindow().getGuiScaledWidth(); int height = mc.getWindow().getGuiScaledHeight(); @@ -89,18 +104,23 @@ public void onScreenInit(final ScreenEvent.InitScreenEvent.Post event) { int x = guiLeft + (creative ? 36 : sizeX / 2 - 10); int y = guiTop + (creative ? 7 : 48); -// event.addListener(new Button(x, y, screen, (btn) -> { -// PacketHandler.send(PacketDistributor.SERVER.noArg(), new OpenCatScreenData()); + event.addListener(new SmallButton(x, y, new TranslatableComponent("X"), (btn) -> { + CraftTracker.LOGGER.debug("#onScreenInit: SMALL BUTTON PRESSED {}", btn); + PacketHandler.send(PacketDistributor.SERVER.noArg(), new AddToQueueData("TODO")); //// btn.active = false; -// })); + })); } } @SubscribeEvent public void onScreenDrawForeground(final ScreenEvent.DrawScreenEvent event) { + CraftTracker.LOGGER.trace("#onScreenDrawForeground: {}", event); + Screen screen = event.getScreen(); if(screen instanceof InventoryScreen || screen instanceof CreativeModeInventoryScreen) { boolean creative = screen instanceof CreativeModeInventoryScreen; +// CraftTracker.LOGGER.debug("#onScreenInit: creative {}", creative); + // CatInventoryButton btn = null; // // //TODO just create a static variable in this class @@ -134,6 +154,8 @@ public void onScreenDrawForeground(final ScreenEvent.DrawScreenEvent event) { } public void drawSelectionBox(PoseStack matrixStackIn, Player player, float particleTicks, AABB boundingBox) { + CraftTracker.LOGGER.debug("#drawSelectionBox: {}, player: {}", matrixStackIn, player); + RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); // RenderSystem.disableAlphaTest(); diff --git a/src/main/java/com/sweetrpg/crafttracker/client/screen/widget/SmallButton.java b/src/main/java/com/sweetrpg/crafttracker/client/screen/widget/SmallButton.java index 670fc80..6238ffc 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/screen/widget/SmallButton.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/screen/widget/SmallButton.java @@ -19,18 +19,18 @@ public SmallButton(int x, int y, Component text, OnPress onPress) { @Override public void renderButton(PoseStack stack, int mouseX, int mouseY, float partialTicks) { - Minecraft mc = Minecraft.getInstance(); - Font font = mc.font; + Minecraft mc = Minecraft.getInstance(); + Font font = mc.font; RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha); RenderSystem.setShaderTexture(0, Resources.SMALL_WIDGETS); - int i = this.getYImage(this.isHoveredOrFocused()); - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - this.blit(stack, this.x, this.y, 0, i * 12, this.width, this.height); - this.renderBg(stack, mc, mouseX, mouseY); - int j = getFGColor(); - this.drawCenteredString(stack, font, this.getMessage(), this.x + this.width / 2, this.y + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0F) << 24); + int i = this.getYImage(this.isHoveredOrFocused()); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + this.blit(stack, this.x, this.y, 0, i * 12, this.width, this.height); + this.renderBg(stack, mc, mouseX, mouseY); + int j = getFGColor(); + this.drawCenteredString(stack, font, this.getMessage(), this.x + this.width / 2, this.y + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0F) << 24); } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/Screens.java b/src/main/java/com/sweetrpg/crafttracker/common/Screens.java index bc006cb..682161d 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/Screens.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/Screens.java @@ -43,5 +43,9 @@ public class Screens { // } // } + public static void updateCraftQueue(ServerPlayer player) { +// TODO: display if hidden + // redraw list of items + } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java index c1dd449..bdc41bf 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java @@ -1,15 +1,21 @@ package com.sweetrpg.crafttracker.common.addon.jei; +import com.sweetrpg.crafttracker.CraftTracker; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; +import mezz.jei.api.registration.IAdvancedRegistration; +import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.IRecipeRegistration; import mezz.jei.api.registration.ISubtypeRegistration; +import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.resources.ResourceLocation; @JeiPlugin public class CTPlugin implements IModPlugin { + public static IJeiRuntime jeiRuntime; + @Override public ResourceLocation getPluginUid() { return new ResourceLocation(ModIds.JEI_ID, "crafttracker"); @@ -17,6 +23,8 @@ public ResourceLocation getPluginUid() { @Override public void registerItemSubtypes(ISubtypeRegistration registration) { + CraftTracker.LOGGER.debug("CTPlugin#registerItemSubtypes: {}", registration); + // registration.registerSubtypeInterpreter(ModBlocks.CAT_TREE.get().asItem(), (stack, ctx) -> { // IColorMaterial colorMaterial = CatTreeUtil.getColorMaterial(stack); // @@ -29,7 +37,29 @@ public void registerItemSubtypes(ISubtypeRegistration registration) { @Override public void registerRecipes(IRecipeRegistration registration) { -// registration.addRecipes(CatTreeRecipeMaker.createCatTreeRecipes(), RecipeTypes.CRAFTING.getUid()); + CraftTracker.LOGGER.debug("CTPlugin#registerRecipes: {}", registration); + + // registration.addRecipes(CatTreeRecipeMaker.createCatTreeRecipes(), RecipeTypes.CRAFTING.getUid()); // registration.addRecipes(PetDoorRecipeMaker.createPetDoorRecipes(), RecipeTypes.CRAFTING.getUid()); } + + @Override + public void registerGuiHandlers(IGuiHandlerRegistration registration) { + CraftTracker.LOGGER.debug("CTPlugin#registerGuiHandlers: {}", registration); + + } + + @Override + public void registerAdvanced(IAdvancedRegistration registration) { + CraftTracker.LOGGER.debug("CTPlugin#registerAdvanced: {}", registration); + + registration.getJeiHelpers().getGuiHelper().createCraftingGridHelper(0); + } + + @Override + public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { + CraftTracker.LOGGER.debug("CTPlugin#onRuntimeAvailable: {}", jeiRuntime); + +CTPlugin.jeiRuntime = jeiRuntime; + } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java index 1bf5940..8e8f115 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java @@ -15,6 +15,10 @@ public class Constants { public static final ResourceLocation CHANNEL_NAME = Util.getResource("channel"); public static final String PROTOCOL_VERSION = Integer.toString(1); + // Storage + public static final String STORAGE_CRAFTING_QUEUE = "crafting_queue"; + public static final String STORAGE_SHOPPING_LIST = "shopping_list"; + // Language public static final String LOCALE_EN_US = "en_us"; public static final String LOCALE_EN_GB = "en_gb"; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java new file mode 100644 index 0000000..98aed97 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -0,0 +1,133 @@ +package com.sweetrpg.crafttracker.common.manager; + +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraftforge.common.crafting.conditions.ICondition; +import org.antlr.v4.misc.OrderedHashMap; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CraftingQueueManager { + + public static CraftingQueueManager INSTANCE = new CraftingQueueManager(); + + private Map endProducts = new OrderedHashMap<>(); + private Map intermediateProducts = new OrderedHashMap<>(); + private Map rawMaterials = new HashMap<>(); + + private CraftingQueueStorage storage; + + public CraftingQueueManager() { + this.storage = CraftingQueueStorage.get(Minecraft.getInstance().level); + } + + public List getEndProducts() { + return endProducts.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public List getIntermediates() { + return intermediateProducts.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public List getRawMaterials() { + return rawMaterials.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public void addProduct(ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("#addProduct: {}, quantity: {}", itemId, quantity); + + RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); + + rm.byKey(itemId).ifPresentOrElse(r -> { + this.endProducts.compute(itemId, (k, v) -> { + if(v == null) { + return quantity; + } + + return v + quantity; + }); + + var playerId = Minecraft.getInstance().player.getUUID(); + this.storage.getData(playerId).addItem(itemId, quantity); + }, + () -> { + // should not have gotten here, since #addProduct should have filtered out the item + // since it had to ingredients + CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", itemId); + }); + + computeAll(); + } + + public void removeProduct(ResourceLocation itemId, int quantity) { + + } + + public void computeAll() { + Map intermediateProducts = new OrderedHashMap<>(); + Map rawMaterials = new HashMap<>(); + + RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); + + this.endProducts.forEach((k, v) -> { + rm.byKey(k).ifPresentOrElse(r -> { + var ingredients = r.getIngredients(); + + }, + () -> { + // should not have gotten here, since #addProduct should have filtered out the item + // since it had to ingredients + CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", k); + }); + + }); + + this.intermediateProducts = intermediateProducts; + this.rawMaterials = rawMaterials; + } + + public void computeIntermediates() { + + } + + public class QueueItem { + private ResourceLocation itemId; + private int quantity; + + public QueueItem(ResourceLocation itemId, int quantity) { + this.itemId = itemId; + this.quantity = quantity; + } + + public ResourceLocation getItemId() { + return itemId; + } + + public void setItemId(ResourceLocation itemId) { + this.itemId = itemId; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/ShoppingListManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/ShoppingListManager.java new file mode 100644 index 0000000..88d15c3 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/ShoppingListManager.java @@ -0,0 +1,4 @@ +package com.sweetrpg.crafttracker.common.manager; + +public class ShoppingListManager { +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java index c6fc8eb..399f422 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java @@ -2,7 +2,11 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.network.packet.AddToQueuePacket; +import com.sweetrpg.crafttracker.common.network.packet.ToggleCraftListPacket; +import com.sweetrpg.crafttracker.common.network.packet.ToggleShoppingListPacket; import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; import net.minecraftforge.network.PacketDistributor; public final class PacketHandler { @@ -11,6 +15,8 @@ public final class PacketHandler { public static void init() { registerPacket(new AddToQueuePacket(), AddToQueueData.class); + registerPacket(new ToggleCraftListPacket(), ToggleCraftListData.class); + registerPacket(new ToggleShoppingListPacket(), ToggleShoppingListData.class); // registerPacket(new CatNamePacket(), CatNameData.class); // registerPacket(new CatObeyPacket(), CatObeyData.class); // registerPacket(new CatTalentPacket(), CatTalentData.class); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java index 76af680..d0282a6 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java @@ -1,8 +1,15 @@ package com.sweetrpg.crafttracker.common.network.packet; +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; import com.sweetrpg.crafttracker.common.network.IPacket; import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; +import mezz.jei.api.ingredients.IIngredientHelper; +import mezz.jei.api.ingredients.ITypedIngredient; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import net.minecraftforge.network.NetworkEvent.Context; import java.util.function.Supplier; @@ -22,14 +29,23 @@ public AddToQueueData decode(FriendlyByteBuf buf) { @Override public final void handle(AddToQueueData data, Supplier ctx) { + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: {}", data); + ctx.get().enqueueWork(() -> { -// Entity target = ctx.get().getSender().level.getEntity(data.entityId); -// -// if (!(target instanceof CatEntity)) { -// return; -// } -// -// this.handleCat((CatEntity) target, data, ctx); + CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() + .ifPresent(ingredient -> { + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient()); + + if(ingredient.getIngredient() instanceof ItemStack itemStack) { + ResourceLocation res = itemStack.getItem().getRegistryName(); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); + + CraftingQueueManager.INSTANCE.addProduct(res, 1); + + + } + }); }); ctx.get().setPacketHandled(true); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayCraftListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java similarity index 52% rename from src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayCraftListPacket.java rename to src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java index a768dc8..9101f5e 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayCraftListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java @@ -1,28 +1,28 @@ package com.sweetrpg.crafttracker.common.network.packet; import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import com.sweetrpg.crafttracker.common.network.packet.data.DisplayCraftListData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent.Context; import java.util.function.Supplier; -public class DisplayCraftListPacket implements IPacket { +public class ToggleCraftListPacket implements IPacket { @Override - public void encode(DisplayCraftListData data, FriendlyByteBuf buf) { - buf.writeBoolean(data.display); + public void encode(ToggleCraftListData data, FriendlyByteBuf buf) { + +// buf.writeBoolean(data.display); } @Override - public DisplayCraftListData decode(FriendlyByteBuf buf) { - boolean display = buf.readBoolean(); - return new DisplayCraftListData(display); + public ToggleCraftListData decode(FriendlyByteBuf buf) { +// boolean display = buf.readBoolean(); + return new ToggleCraftListData(); } @Override - public final void handle(DisplayCraftListData data, Supplier ctx) { + public final void handle(ToggleCraftListData data, Supplier ctx) { ctx.get().enqueueWork(() -> { // Entity target = ctx.get().getSender().level.getEntity(data.entityId); // diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayShoppingListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java similarity index 51% rename from src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayShoppingListPacket.java rename to src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java index e8b5d50..f7c4b47 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/DisplayShoppingListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java @@ -1,28 +1,28 @@ package com.sweetrpg.crafttracker.common.network.packet; import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.DisplayCraftListData; -import com.sweetrpg.crafttracker.common.network.packet.data.DisplayShoppingListData; +import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent.Context; import java.util.function.Supplier; -public class DisplayShoppingListPacket implements IPacket { +public class ToggleShoppingListPacket implements IPacket { @Override - public void encode(DisplayShoppingListData data, FriendlyByteBuf buf) { - buf.writeBoolean(data.display); + public void encode(ToggleShoppingListData data, FriendlyByteBuf buf) { + +// buf.writeBoolean(data.display); } @Override - public DisplayShoppingListData decode(FriendlyByteBuf buf) { - boolean display = buf.readBoolean(); - return new DisplayShoppingListData(display); + public ToggleShoppingListData decode(FriendlyByteBuf buf) { +// boolean display = buf.readBoolean(); + return new ToggleShoppingListData(); } @Override - public final void handle(DisplayShoppingListData data, Supplier ctx) { + public final void handle(ToggleShoppingListData data, Supplier ctx) { ctx.get().enqueueWork(() -> { // Entity target = ctx.get().getSender().level.getEntity(data.entityId); // diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java new file mode 100644 index 0000000..0257d13 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java @@ -0,0 +1,37 @@ +package com.sweetrpg.crafttracker.common.network.packet; + +import com.sweetrpg.crafttracker.common.Screens; +import com.sweetrpg.crafttracker.common.network.IPacket; +import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +public class UpdateCraftQueuePacket implements IPacket { + + @Override + public UpdateCraftQueueData decode(FriendlyByteBuf buf) { + return new UpdateCraftQueueData(); + } + + + @Override + public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { + + } + + @Override + public void handle(UpdateCraftQueueData data, Supplier ctx) { + ctx.get().enqueueWork(() -> { + if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { + ServerPlayer player = ctx.get().getSender(); + Screens.updateCraftQueue(player); + } + }); + + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java new file mode 100644 index 0000000..eac5b74 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java @@ -0,0 +1,41 @@ +package com.sweetrpg.crafttracker.common.network.packet; + +import com.sweetrpg.crafttracker.common.network.IPacket; +import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +public class UpdateShoppingListPacket implements IPacket { + + @Override + public UpdateCraftQueueData decode(FriendlyByteBuf buf) { + return new UpdateCraftQueueData(); + } + + + @Override + public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { + + } + + @Override + public void handle(UpdateCraftQueueData data, Supplier ctx) { + ctx.get().enqueueWork(() -> { + if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { + ServerPlayer player = ctx.get().getSender(); +// List cats = player.level.getEntitiesOfClass(CatEntity.class, player.getBoundingBox().inflate(12D, 12D, 12D), +// (cat) -> cat.canInteract(player) && PackCatTalent.hasInventory(cat) +// ); +// if (!cats.isEmpty()) { +// Screens.openCatInventoriesScreen(player, cats); +// } + } + }); + + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayCraftListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayCraftListData.java deleted file mode 100644 index 83af3fa..0000000 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayCraftListData.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class DisplayCraftListData { - - public boolean display; - - public DisplayCraftListData(boolean display) { - this.display = display; - } -} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayShoppingListData.java deleted file mode 100644 index 7d0a944..0000000 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/DisplayShoppingListData.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class DisplayShoppingListData { - - public boolean display; - - public DisplayShoppingListData(boolean display) { - this.display = display; - } -} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java new file mode 100644 index 0000000..e98b330 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java @@ -0,0 +1,7 @@ +package com.sweetrpg.crafttracker.common.network.packet.data; + +public class ToggleCraftListData { + + public ToggleCraftListData() { + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java new file mode 100644 index 0000000..91e2d88 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java @@ -0,0 +1,8 @@ +package com.sweetrpg.crafttracker.common.network.packet.data; + +public class ToggleShoppingListData { + + public ToggleShoppingListData() { + + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java new file mode 100644 index 0000000..f960dde --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java @@ -0,0 +1,9 @@ +package com.sweetrpg.crafttracker.common.network.packet.data; + +public class UpdateCraftQueueData { + + public UpdateCraftQueueData() { + super(); + } + +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java new file mode 100644 index 0000000..5eeee4c --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java @@ -0,0 +1,9 @@ +package com.sweetrpg.crafttracker.common.network.packet.data; + +public class UpdateShoppingListData { + + public UpdateShoppingListData() { + super(); + } + +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java new file mode 100644 index 0000000..1a99d42 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java @@ -0,0 +1,92 @@ +package com.sweetrpg.crafttracker.common.storage; + +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.util.NBTUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class CraftingQueueData { + + private final CraftingQueueStorage storage; + private final UUID uuid; + private @Nullable UUID ownerId; + private Vec3 position; + + private Map endProducts; + + protected CraftingQueueData(CraftingQueueStorage storage, UUID uuid) { + this.storage = storage; + this.uuid = uuid; + this.endProducts = new HashMap<>(); + } + + public void addItem(ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("CraftingQueueData#addItem: {}, quantity {}", itemId, quantity); + + this.endProducts.compute(itemId, (k, v) -> (v == null ? 0 : v) + quantity); + + this.storage.setDirty(); + } + + public void removeItem(ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("CraftingQueueData#removeItem: {}, quantity {}", itemId, quantity); + + this.endProducts.computeIfPresent(itemId, (k, v) -> { + if(v - quantity < 1) { + return null; + } + + return v - quantity; + }); + + this.storage.setDirty(); + } + + public void read(CompoundTag compound) { + CraftTracker.LOGGER.debug("CraftingQueueData#read"); + + this.ownerId = NBTUtil.getUniqueId(compound, "ownerId"); + this.position = NBTUtil.getVector3d(compound); + this.endProducts.clear(); + + if(compound.contains("products", Tag.TAG_COMPOUND)) { + ListTag products = compound.getList("products", Tag.TAG_COMPOUND); + for(Tag p : products) { + if(p instanceof CompoundTag product) { + String itemId = product.getString("item_id"); + ResourceLocation res = ResourceLocation.tryParse(itemId); + int quantity = product.getInt("quantity"); + this.endProducts.put(res, quantity); + } + } + } + } + + public CompoundTag write(CompoundTag compound) { + CraftTracker.LOGGER.debug("CraftingQueueData#write"); + + NBTUtil.putUniqueId(compound, "ownerId", this.ownerId); + NBTUtil.putVector3d(compound, this.position); + + ListTag list = new ListTag(); + for(Map.Entry product : this.endProducts.entrySet()) { + CompoundTag tag = new CompoundTag(); + + NBTUtil.putResourceLocation(tag, "item_id", product.getKey()); + tag.putInt("item_id", product.getValue()); + + list.add(tag); + } + compound.put("products", list); + + return compound; + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java new file mode 100644 index 0000000..4abc701 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java @@ -0,0 +1,92 @@ +package com.sweetrpg.crafttracker.common.storage; + +import com.google.common.collect.Maps; +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.lib.Constants; +import com.sweetrpg.crafttracker.common.util.NBTUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.storage.DimensionDataStorage; + +import java.util.Map; +import java.util.UUID; + +public class CraftingQueueStorage extends SavedData { + + private Map queueDataMap = Maps.newConcurrentMap(); + + public CraftingQueueStorage() {} + + public static CraftingQueueStorage get(Level world) { + if (!(world instanceof ServerLevel)) { + throw new RuntimeException("Tried to access crafting queue data from the client. This should not happen..."); + } + + ServerLevel overworld = world.getServer().getLevel(Level.OVERWORLD); + + DimensionDataStorage storage = overworld.getDataStorage(); + return storage.computeIfAbsent(CraftingQueueStorage::load, CraftingQueueStorage::new, Constants.STORAGE_CRAFTING_QUEUE); + } + + public CraftingQueueData getData(UUID uuid) { + return queueDataMap.computeIfAbsent(uuid, (k) -> { + CraftingQueueData data = new CraftingQueueData(this, uuid); + this.setDirty(); + return data; + }); + } + + public void removeData(UUID uuid) { + queueDataMap.remove(uuid); + this.setDirty(); + } + + public static CraftingQueueStorage load(CompoundTag nbt) { + CraftingQueueStorage store = new CraftingQueueStorage(); + store.queueDataMap.clear(); + + ListTag list = nbt.getList("queueData", Tag.TAG_COMPOUND); + + for (int i = 0; i < list.size(); ++i) { + CompoundTag queueDataCompound = list.getCompound(i); + + UUID uuid = NBTUtil.getUniqueId(queueDataCompound, "uuid"); + + CraftingQueueData queueData = new CraftingQueueData(store, uuid); + queueData.read(queueDataCompound); + + if (uuid == null) { + CraftTracker.LOGGER.info("Failed to load cat location data. Please report to mod author..."); + CraftTracker.LOGGER.info(queueData); + continue; + } + + store.queueDataMap.put(uuid, queueData); + } + + return store; + } + + @Override + public CompoundTag save(CompoundTag compound) { + ListTag list = new ListTag(); + + for (Map.Entry entry : this.queueDataMap.entrySet()) { + CompoundTag queueDataCompound = new CompoundTag(); + + CraftingQueueData queueData = entry.getValue(); + NBTUtil.putUniqueId(queueDataCompound, "uuid", entry.getKey()); + queueData.write(queueDataCompound); + + list.add(queueDataCompound); + } + + compound.put("queueData", list); + + return compound; + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java new file mode 100644 index 0000000..706eca1 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java @@ -0,0 +1,7 @@ +package com.sweetrpg.crafttracker.common.storage; + +import java.util.UUID; + +public class ShoppingListData { + +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java new file mode 100644 index 0000000..3af6b91 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java @@ -0,0 +1,4 @@ +package com.sweetrpg.crafttracker.common.storage; + +public class ShoppingListStorage { +} From 82ddec535fa893aeaa47a1cb84fb8943678d6e0e Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Mon, 18 Nov 2024 06:26:41 +0000 Subject: [PATCH 04/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index c5d54ec..7c1886b 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.9 +0.0.10 From 583a78e261e3a1ea4d387d3bf744b86f21bce316 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Mon, 18 Nov 2024 14:59:30 +0000 Subject: [PATCH 05/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 7c1886b..2cfabea 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.10 +0.0.11 From af85f082ada16da6191666b3cc3da4bca6b24a21 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 18 Nov 2024 07:01:31 -0800 Subject: [PATCH 06/24] Logging --- .../common/storage/CraftingQueueStorage.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java index 4abc701..52fbe1c 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java @@ -22,6 +22,8 @@ public class CraftingQueueStorage extends SavedData { public CraftingQueueStorage() {} public static CraftingQueueStorage get(Level world) { + CraftTracker.LOGGER.debug("#get: {}", world); + if (!(world instanceof ServerLevel)) { throw new RuntimeException("Tried to access crafting queue data from the client. This should not happen..."); } @@ -33,6 +35,8 @@ public static CraftingQueueStorage get(Level world) { } public CraftingQueueData getData(UUID uuid) { + CraftTracker.LOGGER.debug("#getData: {}", uuid); + return queueDataMap.computeIfAbsent(uuid, (k) -> { CraftingQueueData data = new CraftingQueueData(this, uuid); this.setDirty(); @@ -41,11 +45,15 @@ public CraftingQueueData getData(UUID uuid) { } public void removeData(UUID uuid) { + CraftTracker.LOGGER.debug("#removeData: {}", uuid); + queueDataMap.remove(uuid); this.setDirty(); } public static CraftingQueueStorage load(CompoundTag nbt) { + CraftTracker.LOGGER.debug("#load: {}", nbt); + CraftingQueueStorage store = new CraftingQueueStorage(); store.queueDataMap.clear(); @@ -73,6 +81,8 @@ public static CraftingQueueStorage load(CompoundTag nbt) { @Override public CompoundTag save(CompoundTag compound) { + CraftTracker.LOGGER.debug("#save: {}", compound); + ListTag list = new ListTag(); for (Map.Entry entry : this.queueDataMap.entrySet()) { From 2b84b9466e890ecc45a143eb9213f4acd69d7a11 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 18 Nov 2024 07:09:48 -0800 Subject: [PATCH 07/24] Make JEI a required dependency --- build.gradle | 4 ++-- src/main/resources/META-INF/mods.toml | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 03b2d19..3f5353a 100644 --- a/build.gradle +++ b/build.gradle @@ -202,7 +202,7 @@ curseforge { // } relations { optionalDependency 'configured' - optionalDependency 'jei' + requiredDependency 'jei' optionalDependency 'patchouli' } } @@ -222,7 +222,7 @@ modrinth { loaders = ["forge"] dependencies { optional.project "configured" - optional.project "jei" + required.project "jei" optional.project "patchouli" } } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 97d4aa9..0d6d93f 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -29,6 +29,13 @@ logoFile= "ct_logo.png" ordering="NONE" side="BOTH" +[[dependencies.crafttracker]] + modId="jei" + mandatory=true + versionRange="[9.7.2.281,)" + ordering="NONE" + side="BOTH" + [[dependencies.crafttracker]] modId="configured" mandatory=false @@ -36,6 +43,13 @@ logoFile= "ct_logo.png" ordering="NONE" side="BOTH" +[[dependencies.crafttracker]] + modId="patchouli" + mandatory=false + versionRange="[71.1,)" + ordering="NONE" + side="BOTH" + [modproperties.configured] catalogueImageIcon="configured_icon.png" configuredBackground="minecraft:textures/block/gray_wool.png" From 44157269b03e0b45bc93ec74d1c581153e82e715 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Mon, 18 Nov 2024 15:11:04 +0000 Subject: [PATCH 08/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 2cfabea..8cbf02c 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.11 +0.0.12 From 370450b4d07238df8b538c8048aea93f30f75626 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 18 Nov 2024 08:24:00 -0800 Subject: [PATCH 09/24] Updates --- .idea/compiler.xml | 6 - .idea/gradle.xml | 16 --- .idea/jarRepositories.xml | 50 ------- .idea/misc.xml | 13 -- .idea/modules.xml | 9 -- .idea/runConfigurations/client.xml | 18 --- .idea/runConfigurations/data.xml | 18 --- .idea/runConfigurations/server.xml | 18 --- .idea/uiDesigner.xml | 124 ------------------ .idea/vcs.xml | 6 - .../network/packet/AddToQueuePacket.java | 28 ++-- src/main/resources/META-INF/mods.toml | 2 +- 12 files changed, 19 insertions(+), 289 deletions(-) delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/runConfigurations/client.xml delete mode 100644 .idea/runConfigurations/data.xml delete mode 100644 .idea/runConfigurations/server.xml delete mode 100644 .idea/uiDesigner.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ce1c62c..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 4b58a0b..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 01ea885..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index d9ebdec..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/client.xml b/.idea/runConfigurations/client.xml deleted file mode 100644 index b4f9465..0000000 --- a/.idea/runConfigurations/client.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/.idea/runConfigurations/data.xml b/.idea/runConfigurations/data.xml deleted file mode 100644 index c66268e..0000000 --- a/.idea/runConfigurations/data.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/.idea/runConfigurations/server.xml b/.idea/runConfigurations/server.xml deleted file mode 100644 index 1bcff55..0000000 --- a/.idea/runConfigurations/server.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml deleted file mode 100644 index 2b63946..0000000 --- a/.idea/uiDesigner.xml +++ /dev/null @@ -1,124 +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 diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java index d0282a6..5c10610 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java @@ -10,8 +10,10 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent.Context; +import java.io.IOException; import java.util.function.Supplier; public class AddToQueuePacket implements IPacket { @@ -32,20 +34,26 @@ public final void handle(AddToQueueData data, Supplier ctx) { CraftTracker.LOGGER.debug("AddToQueuePacket#handle: {}", data); ctx.get().enqueueWork(() -> { - CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() - .ifPresent(ingredient -> { - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient()); + LogicalSide side = ctx.get().getDirection().getReceptionSide(); + if (side.isClient()) { - if(ingredient.getIngredient() instanceof ItemStack itemStack) { - ResourceLocation res = itemStack.getItem().getRegistryName(); - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); + } + else if (side.isServer()) { + CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() + .ifPresent(ingredient -> { + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient()); - CraftingQueueManager.INSTANCE.addProduct(res, 1); + if(ingredient.getIngredient() instanceof ItemStack itemStack) { + ResourceLocation res = itemStack.getItem().getRegistryName(); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); + CraftingQueueManager.INSTANCE.addProduct(res, 1); - } - }); + + } + }); + } }); ctx.get().setPacketHandled(true); diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 0d6d93f..6529dc9 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -46,7 +46,7 @@ logoFile= "ct_logo.png" [[dependencies.crafttracker]] modId="patchouli" mandatory=false - versionRange="[71.1,)" + versionRange="[1.18.2-71.1,)" ordering="NONE" side="BOTH" From 4bb1f1b20c72f084ba759d858a6f7a9d88364704 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 18 Nov 2024 08:31:28 -0800 Subject: [PATCH 10/24] Java version --- .java-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.java-version b/.java-version index e000eb8..6215fcb 100644 --- a/.java-version +++ b/.java-version @@ -1 +1 @@ -17.0.9 +17.0.13 From e961a3f5b295140a292a7fb3beccfd4d7fd768a5 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Mon, 18 Nov 2024 22:14:30 -0800 Subject: [PATCH 11/24] Lookup recipe using ID --- .../common/manager/CraftingQueueManager.java | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index 98aed97..9853308 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -1,16 +1,16 @@ package com.sweetrpg.crafttracker.common.manager; import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; -import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.RecipeManager; -import net.minecraftforge.common.crafting.conditions.ICondition; +import net.minecraft.world.item.crafting.CraftingRecipe; import org.antlr.v4.misc.OrderedHashMap; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; public class CraftingQueueManager { @@ -24,7 +24,7 @@ public class CraftingQueueManager { private CraftingQueueStorage storage; public CraftingQueueManager() { - this.storage = CraftingQueueStorage.get(Minecraft.getInstance().level); + this.storage = new CraftingQueueStorage(); } public List getEndProducts() { @@ -51,25 +51,27 @@ public List getRawMaterials() { public void addProduct(ResourceLocation itemId, int quantity) { CraftTracker.LOGGER.debug("#addProduct: {}, quantity: {}", itemId, quantity); - RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); - - rm.byKey(itemId).ifPresentOrElse(r -> { - this.endProducts.compute(itemId, (k, v) -> { - if(v == null) { - return quantity; - } - - return v + quantity; - }); - - var playerId = Minecraft.getInstance().player.getUUID(); - this.storage.getData(playerId).addItem(itemId, quantity); - }, - () -> { - // should not have gotten here, since #addProduct should have filtered out the item - // since it had to ingredients - CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", itemId); - }); + var rm = CTPlugin.jeiRuntime.getRecipeManager(); + + rm.createRecipeCategoryLookup().get() + .peek(c -> CraftTracker.LOGGER.debug("category: {}", c)) + .map(c -> c.getRecipeType()) + .peek(t -> CraftTracker.LOGGER.debug("type: {}", t)) + .flatMap(t -> rm.createRecipeLookup(t).get()) + .peek(r -> CraftTracker.LOGGER.debug("recipe: {}", r)) + .filter(r -> r instanceof CraftingRecipe) + .map(r -> CraftingRecipe.class.cast(r)) + .peek(r -> CraftTracker.LOGGER.debug("CraftingRecipe: {}", r.getId())) + .filter(cr -> cr.getId().equals(itemId)) + .peek(cr -> CraftTracker.LOGGER.debug("{}: {}", itemId, cr)) + .findFirst() + .ifPresentOrElse(r -> { + CraftTracker.LOGGER.debug("r: {}", r); + endProducts.compute(itemId, (k, v) -> v == null ? quantity : v + quantity); + }, + () -> { + CraftTracker.LOGGER.warn("No recipe found for {}", itemId); + }); computeAll(); } @@ -82,18 +84,21 @@ public void computeAll() { Map intermediateProducts = new OrderedHashMap<>(); Map rawMaterials = new HashMap<>(); - RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); + var rm = CTPlugin.jeiRuntime.getRecipeManager(); + +// RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); this.endProducts.forEach((k, v) -> { - rm.byKey(k).ifPresentOrElse(r -> { - var ingredients = r.getIngredients(); - - }, - () -> { - // should not have gotten here, since #addProduct should have filtered out the item - // since it had to ingredients - CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", k); - }); + +// rm.byKey(k).ifPresentOrElse(r -> { +// var ingredients = r.getIngredients(); +// +// }, +// () -> { +// // should not have gotten here, since #addProduct should have filtered out the item +// // since it had to ingredients +// CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", k); +// }); }); From e24519bacbe6d6bc3405338b1fe0123b46fed4e7 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Tue, 19 Nov 2024 06:17:53 +0000 Subject: [PATCH 12/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 8cbf02c..43b2961 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.12 +0.0.13 From 25f286eebaeedf1a47beaa9e44ae020f4251a2e6 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Tue, 19 Nov 2024 08:27:18 -0800 Subject: [PATCH 13/24] Update how crafting queue data is stored --- .../common/manager/CraftingQueueManager.java | 109 ++++++++++-------- .../common/model/CraftingQueueProduct.java | 43 +++++++ .../network/packet/AddToQueuePacket.java | 11 +- .../common/storage/CraftingQueueData.java | 107 ++++++++--------- .../common/storage/CraftingQueueStorage.java | 87 +++++++++----- .../crafttracker/common/util/RecipeUtil.java | 32 +++++ 6 files changed, 245 insertions(+), 144 deletions(-) create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java create mode 100644 src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index 9853308..4278367 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -2,85 +2,100 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; +import com.sweetrpg.crafttracker.common.util.RecipeUtil; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.level.Level; import org.antlr.v4.misc.OrderedHashMap; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; public class CraftingQueueManager { public static CraftingQueueManager INSTANCE = new CraftingQueueManager(); - private Map endProducts = new OrderedHashMap<>(); + private Map endProducts = new OrderedHashMap<>(); private Map intermediateProducts = new OrderedHashMap<>(); private Map rawMaterials = new HashMap<>(); - private CraftingQueueStorage storage; +// private CraftingQueueStorage storage; public CraftingQueueManager() { - this.storage = new CraftingQueueStorage(); +// this.storage = new CraftingQueueStorage(); } - public List getEndProducts() { - return endProducts.entrySet() - .stream() - .map((e) -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); - } +// public List getEndProducts() { +// return endProducts.entrySet() +// .stream() +// .map((e) -> new QueueItem(e.getKey(), e.getValue())) +// .collect(Collectors.toUnmodifiableList()); +// } +// +// public List getIntermediates() { +// return intermediateProducts.entrySet() +// .stream() +// .map((e) -> new QueueItem(e.getKey(), e.getValue())) +// .collect(Collectors.toUnmodifiableList()); +// } +// +// public List getRawMaterials() { +// return rawMaterials.entrySet() +// .stream() +// .map((e) -> new QueueItem(e.getKey(), e.getValue())) +// .collect(Collectors.toUnmodifiableList()); +// } - public List getIntermediates() { - return intermediateProducts.entrySet() - .stream() - .map((e) -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); - } + public void addProduct(Level level, ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("CraftingQueueManager#addProduct: {}, quantity: {}", itemId, quantity); - public List getRawMaterials() { - return rawMaterials.entrySet() - .stream() - .map((e) -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); - } + var recipes = RecipeUtil.getRecipesFor(itemId); - public void addProduct(ResourceLocation itemId, int quantity) { - CraftTracker.LOGGER.debug("#addProduct: {}, quantity: {}", itemId, quantity); + if(recipes.size() > 0) { + CraftTracker.LOGGER.debug("recipes: {}", recipes); - var rm = CTPlugin.jeiRuntime.getRecipeManager(); + var product = new CraftingQueueProduct(itemId, recipes, quantity); + endProducts.compute(itemId, (rl, p) -> p == null ? product : + new CraftingQueueProduct(p.getItemId(), p.getRecipes(), p.getQuantity() + quantity)); - rm.createRecipeCategoryLookup().get() - .peek(c -> CraftTracker.LOGGER.debug("category: {}", c)) - .map(c -> c.getRecipeType()) - .peek(t -> CraftTracker.LOGGER.debug("type: {}", t)) - .flatMap(t -> rm.createRecipeLookup(t).get()) - .peek(r -> CraftTracker.LOGGER.debug("recipe: {}", r)) - .filter(r -> r instanceof CraftingRecipe) - .map(r -> CraftingRecipe.class.cast(r)) - .peek(r -> CraftTracker.LOGGER.debug("CraftingRecipe: {}", r.getId())) - .filter(cr -> cr.getId().equals(itemId)) - .peek(cr -> CraftTracker.LOGGER.debug("{}: {}", itemId, cr)) - .findFirst() - .ifPresentOrElse(r -> { - CraftTracker.LOGGER.debug("r: {}", r); - endProducts.compute(itemId, (k, v) -> v == null ? quantity : v + quantity); - }, - () -> { - CraftTracker.LOGGER.warn("No recipe found for {}", itemId); - }); + CraftingQueueStorage.get(level).putData(itemId, quantity); - computeAll(); + computeAll(); + } + else { + CraftTracker.LOGGER.info("Not adding {} to queue, since there are no recipes for it.", itemId); + } } - public void removeProduct(ResourceLocation itemId, int quantity) { + public void removeProduct(Level level, ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("CraftingQueueManager#removeProduct: {}, quantity: {}", itemId, quantity); + + var product = this.endProducts.get(itemId); + if(product == null) { + CraftTracker.LOGGER.info("No product found in queue for {}", itemId); + return; + } + + // if we would remove more than what's left in the queue, remove it entirely + int newQuantity = product.getQuantity() - quantity; + if(newQuantity < 1) { + CraftTracker.LOGGER.info("Removing item from queue storage: {}", itemId); + CraftingQueueStorage.get(level).removeData(itemId); + } + else { + CraftTracker.LOGGER.info("Adjusting quantity of item in queue storage to {}: {}", quantity, itemId); + CraftingQueueStorage.get(level).putData(itemId, newQuantity); + } + computeAll(); } public void computeAll() { + CraftTracker.LOGGER.debug("CraftingQueueManager#computeAll"); + Map intermediateProducts = new OrderedHashMap<>(); Map rawMaterials = new HashMap<>(); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java new file mode 100644 index 0000000..8ef9998 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java @@ -0,0 +1,43 @@ +package com.sweetrpg.crafttracker.common.model; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.Recipe; + +import java.util.List; + +public class CraftingQueueProduct { + + ResourceLocation itemId; + List recipes; + int quantity; + + public CraftingQueueProduct(ResourceLocation itemId, List recipes, int quantity) { + this.itemId = itemId; + this.recipes = recipes; + this.quantity = quantity; + } + + public ResourceLocation getItemId() { + return itemId; + } + + public void setItemId(ResourceLocation itemId) { + this.itemId = itemId; + } + + public List getRecipes() { + return recipes; + } + + public void setRecipes(List recipes) { + this.recipes = recipes; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java index 5c10610..7ea1594 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java @@ -5,15 +5,12 @@ import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; import com.sweetrpg.crafttracker.common.network.IPacket; import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import mezz.jei.api.ingredients.IIngredientHelper; -import mezz.jei.api.ingredients.ITypedIngredient; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent.Context; -import java.io.IOException; import java.util.function.Supplier; public class AddToQueuePacket implements IPacket { @@ -35,10 +32,10 @@ public final void handle(AddToQueueData data, Supplier ctx) { ctx.get().enqueueWork(() -> { LogicalSide side = ctx.get().getDirection().getReceptionSide(); - if (side.isClient()) { + if(side.isClient()) { } - else if (side.isServer()) { + else if(side.isServer()) { CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() .ifPresent(ingredient -> { CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); @@ -48,9 +45,7 @@ else if (side.isServer()) { ResourceLocation res = itemStack.getItem().getRegistryName(); CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); - CraftingQueueManager.INSTANCE.addProduct(res, 1); - - + CraftingQueueManager.INSTANCE.addProduct(ctx.get().getSender().level, res, 1); } }); } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java index 1a99d42..da9bcfc 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java @@ -3,90 +3,75 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.util.NBTUtil; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.phys.Vec3; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +/** + * This class represents an entry in the crafting queue's end products list. + */ public class CraftingQueueData { private final CraftingQueueStorage storage; - private final UUID uuid; - private @Nullable UUID ownerId; - private Vec3 position; - - private Map endProducts; + private ResourceLocation itemId; + private int quantity; - protected CraftingQueueData(CraftingQueueStorage storage, UUID uuid) { + protected CraftingQueueData(CraftingQueueStorage storage) { this.storage = storage; - this.uuid = uuid; - this.endProducts = new HashMap<>(); } - public void addItem(ResourceLocation itemId, int quantity) { - CraftTracker.LOGGER.debug("CraftingQueueData#addItem: {}, quantity {}", itemId, quantity); - - this.endProducts.compute(itemId, (k, v) -> (v == null ? 0 : v) + quantity); - - this.storage.setDirty(); + protected CraftingQueueData(CraftingQueueStorage storage, ResourceLocation itemId, int quantity) { + this.storage = storage; + this.itemId = itemId; + this.quantity = quantity; } - public void removeItem(ResourceLocation itemId, int quantity) { - CraftTracker.LOGGER.debug("CraftingQueueData#removeItem: {}, quantity {}", itemId, quantity); - - this.endProducts.computeIfPresent(itemId, (k, v) -> { - if(v - quantity < 1) { - return null; - } - - return v - quantity; - }); - - this.storage.setDirty(); - } +// public void addItem(ResourceLocation itemId, int quantity) { +// CraftTracker.LOGGER.debug("CraftingQueueData#addItem: {}, quantity {}", itemId, quantity); +// +// this.endProducts.compute(itemId, (k, v) -> (v == null ? 0 : v) + quantity); +// +// this.storage.setDirty(); +// } +// +// public void removeItem(ResourceLocation itemId, int quantity) { +// CraftTracker.LOGGER.debug("CraftingQueueData#removeItem: {}, quantity {}", itemId, quantity); +// +// this.endProducts.computeIfPresent(itemId, (k, v) -> { +// if(v - quantity < 1) { +// return null; +// } +// +// return v - quantity; +// }); +// +// this.storage.setDirty(); +// } public void read(CompoundTag compound) { CraftTracker.LOGGER.debug("CraftingQueueData#read"); - this.ownerId = NBTUtil.getUniqueId(compound, "ownerId"); - this.position = NBTUtil.getVector3d(compound); - this.endProducts.clear(); - - if(compound.contains("products", Tag.TAG_COMPOUND)) { - ListTag products = compound.getList("products", Tag.TAG_COMPOUND); - for(Tag p : products) { - if(p instanceof CompoundTag product) { - String itemId = product.getString("item_id"); - ResourceLocation res = ResourceLocation.tryParse(itemId); - int quantity = product.getInt("quantity"); - this.endProducts.put(res, quantity); - } - } - } + this.itemId = NBTUtil.getResourceLocation(compound, Keys.ITEM_ID); + this.quantity = compound.getInt(Keys.QUANTITY); } public CompoundTag write(CompoundTag compound) { CraftTracker.LOGGER.debug("CraftingQueueData#write"); - NBTUtil.putUniqueId(compound, "ownerId", this.ownerId); - NBTUtil.putVector3d(compound, this.position); + NBTUtil.putResourceLocation(compound, Keys.ITEM_ID, this.itemId); + compound.putInt(Keys.QUANTITY, this.quantity); - ListTag list = new ListTag(); - for(Map.Entry product : this.endProducts.entrySet()) { - CompoundTag tag = new CompoundTag(); + return compound; + } - NBTUtil.putResourceLocation(tag, "item_id", product.getKey()); - tag.putInt("item_id", product.getValue()); + public ResourceLocation getItemId() { + return itemId; + } - list.add(tag); - } - compound.put("products", list); + public int getQuantity() { + return quantity; + } - return compound; + static class Keys { + static String ITEM_ID = "item_id"; + static String QUANTITY = "quantity"; } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java index 52fbe1c..8ea4a3f 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java @@ -7,22 +7,38 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.storage.DimensionDataStorage; +import net.minecraft.world.phys.Vec3; +import javax.annotation.Nullable; import java.util.Map; import java.util.UUID; +/** + * This class handles storage of all crafting queue information for a player. + * + * This includes: + * * Position and visibility of the craft list window + * * Position and visibility of the shopping list window + * * The list of end products in the queue by item ID and quantity + */ public class CraftingQueueStorage extends SavedData { - private Map queueDataMap = Maps.newConcurrentMap(); + private @Nullable UUID ownerId; + private Vec3 craftingQueuePosition; + private boolean craftingQueueVisible; + private Vec3 shoppingListPosition; + private boolean shoppingListVisible; + private Map queueData = Maps.newConcurrentMap(); public CraftingQueueStorage() {} public static CraftingQueueStorage get(Level world) { - CraftTracker.LOGGER.debug("#get: {}", world); + CraftTracker.LOGGER.debug("CraftingQueueStorage#get: {}", world); if (!(world instanceof ServerLevel)) { throw new RuntimeException("Tried to access crafting queue data from the client. This should not happen..."); @@ -34,46 +50,49 @@ public static CraftingQueueStorage get(Level world) { return storage.computeIfAbsent(CraftingQueueStorage::load, CraftingQueueStorage::new, Constants.STORAGE_CRAFTING_QUEUE); } - public CraftingQueueData getData(UUID uuid) { - CraftTracker.LOGGER.debug("#getData: {}", uuid); + public void putData(ResourceLocation itemId, int quantity) { + CraftTracker.LOGGER.debug("CraftingQueueStorage#putData: {}, quantity: {}", itemId, quantity); - return queueDataMap.computeIfAbsent(uuid, (k) -> { - CraftingQueueData data = new CraftingQueueData(this, uuid); - this.setDirty(); - return data; + this.queueData.compute(itemId, (k, data) -> { + if(data == null) { + return new CraftingQueueData(this, itemId, quantity); + } + + return new CraftingQueueData(this, itemId, data.getQuantity() + quantity); }); + + this.setDirty(); } - public void removeData(UUID uuid) { - CraftTracker.LOGGER.debug("#removeData: {}", uuid); + public void removeData(ResourceLocation itemId) { + CraftTracker.LOGGER.debug("CraftingQueueStorage#removeData: {}", itemId); + + this.queueData.remove(itemId); - queueDataMap.remove(uuid); this.setDirty(); } public static CraftingQueueStorage load(CompoundTag nbt) { - CraftTracker.LOGGER.debug("#load: {}", nbt); + CraftTracker.LOGGER.debug("CraftingQueueStorage#load: {}", nbt); CraftingQueueStorage store = new CraftingQueueStorage(); - store.queueDataMap.clear(); + store.queueData.clear(); + + store.ownerId = NBTUtil.getUniqueId(nbt, Keys.OWNER_ID); + store.craftingQueuePosition = NBTUtil.getVector3d(nbt); + store.craftingQueueVisible = nbt.getBoolean(Keys.CRAFTING_QUEUE_VISIBLE); + store.shoppingListPosition = NBTUtil.getVector3d(nbt); + store.shoppingListVisible = nbt.getBoolean(Keys.SHOPPING_LIST_VISIBLE); - ListTag list = nbt.getList("queueData", Tag.TAG_COMPOUND); + ListTag list = nbt.getList(Keys.QUEUE_DATA, Tag.TAG_COMPOUND); for (int i = 0; i < list.size(); ++i) { CompoundTag queueDataCompound = list.getCompound(i); - UUID uuid = NBTUtil.getUniqueId(queueDataCompound, "uuid"); - - CraftingQueueData queueData = new CraftingQueueData(store, uuid); + CraftingQueueData queueData = new CraftingQueueData(store); queueData.read(queueDataCompound); - if (uuid == null) { - CraftTracker.LOGGER.info("Failed to load cat location data. Please report to mod author..."); - CraftTracker.LOGGER.info(queueData); - continue; - } - - store.queueDataMap.put(uuid, queueData); + store.queueData.put(queueData.getItemId(), queueData); } return store; @@ -81,22 +100,34 @@ public static CraftingQueueStorage load(CompoundTag nbt) { @Override public CompoundTag save(CompoundTag compound) { - CraftTracker.LOGGER.debug("#save: {}", compound); + CraftTracker.LOGGER.debug("CraftingQueueStorage#save: {}", compound); + + NBTUtil.putUniqueId(compound, Keys.OWNER_ID, this.ownerId); + NBTUtil.putVector3d(compound, this.craftingQueuePosition); + compound.putBoolean(Keys.CRAFTING_QUEUE_VISIBLE, this.craftingQueueVisible); + NBTUtil.putVector3d(compound, this.shoppingListPosition); + compound.putBoolean(Keys.SHOPPING_LIST_VISIBLE, this.shoppingListVisible); ListTag list = new ListTag(); - for (Map.Entry entry : this.queueDataMap.entrySet()) { + for (Map.Entry entry : this.queueData.entrySet()) { CompoundTag queueDataCompound = new CompoundTag(); CraftingQueueData queueData = entry.getValue(); - NBTUtil.putUniqueId(queueDataCompound, "uuid", entry.getKey()); queueData.write(queueDataCompound); list.add(queueDataCompound); } - compound.put("queueData", list); + compound.put(Keys.QUEUE_DATA, list); return compound; } + + static class Keys { + static String OWNER_ID = "owner_id"; + static String CRAFTING_QUEUE_VISIBLE = "crafting_queue_visible"; + static String SHOPPING_LIST_VISIBLE = "shopping_list_visible"; + static String QUEUE_DATA = "queue_data"; + } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java new file mode 100644 index 0000000..58e6c00 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java @@ -0,0 +1,32 @@ +package com.sweetrpg.crafttracker.common.util; + +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.item.crafting.Recipe; + +import java.util.List; +import java.util.stream.Collectors; + +public class RecipeUtil { + + public static List getRecipesFor(ResourceLocation itemId) { + var rm = CTPlugin.jeiRuntime.getRecipeManager(); + + var recipes = rm.createRecipeCategoryLookup().get() + .peek(c -> CraftTracker.LOGGER.debug("category: {}", c)) + .map(c -> c.getRecipeType()) + .peek(t -> CraftTracker.LOGGER.debug("type: {}", t)) + .flatMap(t -> rm.createRecipeLookup(t).get()) + .peek(r -> CraftTracker.LOGGER.debug("recipe: {}", r)) + .filter(r -> r instanceof Recipe) + .map(r -> Recipe.class.cast(r)) + .peek(r -> CraftTracker.LOGGER.debug("Recipe: {}", r.getId())) + .filter(r -> r.getId().equals(itemId)) + .peek(r -> CraftTracker.LOGGER.debug("{}: {}", itemId, r)) + .collect(Collectors.toUnmodifiableList()); + + return recipes; + } +} From 78f55de80ad04b01958cffa82f15584c52f25b3a Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Tue, 19 Nov 2024 16:28:55 +0000 Subject: [PATCH 14/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 43b2961..9789c4c 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.13 +0.0.14 From 459b6372b6b1e57eca1a0971110ed1a00dd1a737 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Tue, 19 Nov 2024 09:00:39 -0800 Subject: [PATCH 15/24] Craft queue data display --- .../client/overlay/CraftQueueOverlay.java | 20 +++++++++++++++++++ .../crafttracker/common/lib/Constants.java | 1 + .../packet/UpdateCraftQueuePacket.java | 13 +++++++++--- .../packet/UpdateShoppingListPacket.java | 11 +++++----- .../packet/data/UpdateCraftQueueData.java | 14 +++++++++++-- .../crafttracker/data/CTLangProvider.java | 3 +++ 6 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java index 20a49f5..94684e9 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java @@ -24,6 +24,26 @@ public class CraftQueueOverlay { GuiComponent.drawCenteredString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE), (x + olWidth - 8) / 2, y + 6, 0xffffffff); + + // if products list is empty, display "empty" message + if(true) { // TODO + GuiComponent.drawCenteredString(poseStack, gui.getFont(), + new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY), + (x + olWidth - 8) / 2, y + 6, 0xffffffff); + return; + } + + // end products + // TODO: title + // TODO: items + + // intermediates + // TODO: title + // TODO: items + + // raw materials + // TODO: title + // TODO: items }; } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java index 8e8f115..123f472 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java @@ -26,6 +26,7 @@ public class Constants { // Translation keys public static final String TRANSLATION_KEY_GUI_CRAFTLIST_TITLE = "crafttracker.screen.craft_list.title"; + public static final String TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY = "crafttracker.screen.craft_list.empty_message"; public static final String TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE = "crafttracker.screen.shopping_list.title"; public static final String TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE = "key.categories.crafttracker"; public static final String TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE = "key.addToQueue"; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java index 0257d13..4879d28 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java @@ -4,29 +4,36 @@ import com.sweetrpg.crafttracker.common.network.IPacket; import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent; +import java.util.Map; import java.util.function.Supplier; public class UpdateCraftQueuePacket implements IPacket { @Override public UpdateCraftQueueData decode(FriendlyByteBuf buf) { - return new UpdateCraftQueueData(); + Map data = buf.readMap((fbb) -> fbb.readResourceLocation(), + (fbb) -> fbb.readInt()); + + return new UpdateCraftQueueData(data); } @Override public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { - + buf.writeMap(data.getEndProducts(), + (fbb, itemId) -> fbb.writeResourceLocation(itemId), + (fbb, i) -> fbb.writeInt(i)); } @Override public void handle(UpdateCraftQueueData data, Supplier ctx) { ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { + if(ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { ServerPlayer player = ctx.get().getSender(); Screens.updateCraftQueue(player); } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java index eac5b74..27b70b9 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java @@ -2,6 +2,7 @@ import com.sweetrpg.crafttracker.common.network.IPacket; import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; +import com.sweetrpg.crafttracker.common.network.packet.data.UpdateShoppingListData; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.fml.LogicalSide; @@ -9,21 +10,21 @@ import java.util.function.Supplier; -public class UpdateShoppingListPacket implements IPacket { +public class UpdateShoppingListPacket implements IPacket { @Override - public UpdateCraftQueueData decode(FriendlyByteBuf buf) { - return new UpdateCraftQueueData(); + public UpdateShoppingListData decode(FriendlyByteBuf buf) { + return new UpdateShoppingListData(); } @Override - public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { + public void encode(UpdateShoppingListData data, FriendlyByteBuf buf) { } @Override - public void handle(UpdateCraftQueueData data, Supplier ctx) { + public void handle(UpdateShoppingListData data, Supplier ctx) { ctx.get().enqueueWork(() -> { if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { ServerPlayer player = ctx.get().getSender(); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java index f960dde..a614174 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java @@ -1,9 +1,19 @@ package com.sweetrpg.crafttracker.common.network.packet.data; +import net.minecraft.resources.ResourceLocation; +import org.antlr.v4.misc.OrderedHashMap; + +import java.util.Map; + public class UpdateCraftQueueData { - public UpdateCraftQueueData() { - super(); + private Map endProducts = new OrderedHashMap<>(); + + public UpdateCraftQueueData(Map endProducts) { + this.endProducts = endProducts; } + public Map getEndProducts() { + return endProducts; + } } diff --git a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java index c227237..7eb162d 100644 --- a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java +++ b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java @@ -31,6 +31,7 @@ private void processENUS() { CraftTracker.LOGGER.info("Adding translations for en_us..."); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Craft List"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "The queue is empty."); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -42,6 +43,7 @@ private void processENGB() { CraftTracker.LOGGER.info("Adding translations for en_gb..."); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Craft List"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "The queue is empty."); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -53,6 +55,7 @@ private void processDEDE() { CraftTracker.LOGGER.info("Adding translations for de_de..."); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Bastelliste"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "Die Warteschlange ist leer."); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Einkaufsliste"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Handwerks-Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Zur Warteschlange hinzufügen"); From 33249019161a7101773a8e24599390a60492f1c7 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Tue, 19 Nov 2024 17:01:55 +0000 Subject: [PATCH 16/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 9789c4c..ceddfb2 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.14 +0.0.15 From 42cd0e1a2f6c074659f8a8382e90c7eea57fe615 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Wed, 20 Nov 2024 08:25:19 -0800 Subject: [PATCH 17/24] Switch to client-side --- .../assets/crafttracker/lang/de_de.json | 4 + .../assets/crafttracker/lang/en_gb.json | 4 + .../assets/crafttracker/lang/en_us.json | 4 + .../sweetrpg/crafttracker/CraftTracker.java | 4 +- .../client/event/ClientEventHandler.java | 77 +++-- .../client/overlay/CraftQueueOverlay.java | 61 +++- .../crafttracker/common/CommonSetup.java | 1 + .../sweetrpg/crafttracker/common/Screens.java | 8 +- .../common/addon/jei/CTPlugin.java | 2 +- .../common/config/ConfigHandler.java | 2 - .../common/event/EventHandler.java | 60 ++-- .../crafttracker/common/lib/Constants.java | 3 + .../common/manager/CraftingQueueManager.java | 128 +++++++-- .../common/network/PacketHandler.java | 23 +- .../network/packet/AddToQueuePacket.java | 106 ++++--- .../network/packet/ToggleCraftListPacket.java | 86 +++--- .../packet/ToggleShoppingListPacket.java | 86 +++--- .../packet/UpdateCraftQueuePacket.java | 99 ++++--- .../packet/UpdateShoppingListPacket.java | 84 +++--- .../network/packet/data/AddToQueueData.java | 24 +- .../packet/data/ToggleCraftListData.java | 14 +- .../packet/data/ToggleShoppingListData.java | 16 +- .../packet/data/UpdateCraftQueueData.java | 41 +-- .../packet/data/UpdateShoppingListData.java | 18 +- .../common/storage/CraftingQueueData.java | 138 ++++----- .../common/storage/CraftingQueueStorage.java | 272 +++++++++--------- .../common/storage/ShoppingListData.java | 14 +- .../common/storage/ShoppingListStorage.java | 8 +- .../crafttracker/data/CTLangProvider.java | 9 + 29 files changed, 796 insertions(+), 600 deletions(-) diff --git a/src/generated/resources/assets/crafttracker/lang/de_de.json b/src/generated/resources/assets/crafttracker/lang/de_de.json index cb22c3a..bf00aa6 100644 --- a/src/generated/resources/assets/crafttracker/lang/de_de.json +++ b/src/generated/resources/assets/crafttracker/lang/de_de.json @@ -1,4 +1,8 @@ { + "crafttracker.screen.craft_list.empty_message": "Die Warteschlange ist leer.", + "crafttracker.screen.craft_list.section.intermediates": "Zwischenprodukte", + "crafttracker.screen.craft_list.section.materials": "Materialien", + "crafttracker.screen.craft_list.section.products": "Produkte", "crafttracker.screen.craft_list.title": "Bastelliste", "crafttracker.screen.shopping_list.title": "Einkaufsliste", "key.addToQueue": "Zur Warteschlange hinzuf\u00FCgen", diff --git a/src/generated/resources/assets/crafttracker/lang/en_gb.json b/src/generated/resources/assets/crafttracker/lang/en_gb.json index a741026..d478315 100644 --- a/src/generated/resources/assets/crafttracker/lang/en_gb.json +++ b/src/generated/resources/assets/crafttracker/lang/en_gb.json @@ -1,4 +1,8 @@ { + "crafttracker.screen.craft_list.empty_message": "The queue is empty.", + "crafttracker.screen.craft_list.section.intermediates": "Intermediates", + "crafttracker.screen.craft_list.section.materials": "Materials", + "crafttracker.screen.craft_list.section.products": "Products", "crafttracker.screen.craft_list.title": "Craft List", "crafttracker.screen.shopping_list.title": "Shopping List", "key.addToQueue": "Add to Queue", diff --git a/src/generated/resources/assets/crafttracker/lang/en_us.json b/src/generated/resources/assets/crafttracker/lang/en_us.json index a741026..d478315 100644 --- a/src/generated/resources/assets/crafttracker/lang/en_us.json +++ b/src/generated/resources/assets/crafttracker/lang/en_us.json @@ -1,4 +1,8 @@ { + "crafttracker.screen.craft_list.empty_message": "The queue is empty.", + "crafttracker.screen.craft_list.section.intermediates": "Intermediates", + "crafttracker.screen.craft_list.section.materials": "Materials", + "crafttracker.screen.craft_list.section.products": "Products", "crafttracker.screen.craft_list.title": "Craft List", "crafttracker.screen.shopping_list.title": "Shopping List", "key.addToQueue": "Add to Queue", diff --git a/src/main/java/com/sweetrpg/crafttracker/CraftTracker.java b/src/main/java/com/sweetrpg/crafttracker/CraftTracker.java index ba7fc7a..8641476 100644 --- a/src/main/java/com/sweetrpg/crafttracker/CraftTracker.java +++ b/src/main/java/com/sweetrpg/crafttracker/CraftTracker.java @@ -36,10 +36,10 @@ public class CraftTracker { public static final Logger LOGGER = LogManager.getLogger(Constants.MOD_ID); - public static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder.named(com.sweetrpg.crafttracker.common.lib.Constants.CHANNEL_NAME) + public static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder.named(Constants.CHANNEL_NAME) .clientAcceptedVersions(Constants.PROTOCOL_VERSION::equals) .serverAcceptedVersions(Constants.PROTOCOL_VERSION::equals) - .networkProtocolVersion(com.sweetrpg.crafttracker.common.lib.Constants.PROTOCOL_VERSION::toString) + .networkProtocolVersion(Constants.PROTOCOL_VERSION::toString) .simpleChannel(); public CraftTracker() { diff --git a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java index bddf48c..c577d77 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java @@ -5,10 +5,8 @@ import com.mojang.blaze3d.vertex.*; import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.client.screen.widget.SmallButton; -import com.sweetrpg.crafttracker.common.network.PacketHandler; -import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; +import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; import com.sweetrpg.crafttracker.common.registry.ModKeyBindings; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -17,14 +15,15 @@ import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.client.event.MovementInputUpdateEvent; import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.network.PacketDistributor; public class ClientEventHandler { @@ -63,17 +62,33 @@ public static void onKeyInput(final InputEvent.KeyInputEvent event) { if(ModKeyBindings.ADD_TO_QUEUE_MAPPING.matches(event.getKey(), event.getScanCode())) { CraftTracker.LOGGER.debug("#onKeyInput: ADD_TO_QUEUE_MAPPING"); - PacketHandler.send(PacketDistributor.SERVER.noArg(), new AddToQueueData("TODO")); + + CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() + .ifPresent(ingredient -> { + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient()); + + if(ingredient.getIngredient() instanceof ItemStack itemStack) { + ResourceLocation res = itemStack.getItem().getRegistryName(); + CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); + + var player = Minecraft.getInstance().player; + CraftingQueueManager.INSTANCE.addProduct(player, res, 1); +// PacketHandler.sendToServer(new AddToQueueData(res, 1)); + } + }); + + } else if(ModKeyBindings.TOGGLE_CRAFT_LIST_MAPPING.matches(event.getKey(), event.getScanCode())) { CraftTracker.LOGGER.debug("#onKeyInput: TOGGLE_CRAFT_LIST_MAPPING"); // craftListDisplayed = !craftListDisplayed; - PacketHandler.send(PacketDistributor.SERVER.noArg(), new ToggleCraftListData()); +// PacketHandler.sendToServer(new ToggleCraftListData()); } else if(ModKeyBindings.TOGGLE_SHOPPING_LIST_MAPPING.matches(event.getKey(), event.getScanCode())) { CraftTracker.LOGGER.debug("#onKeyInput: TOGGLE_SHOPPING_LIST_MAPPING"); // shoppingListDisplayed = !shoppingListDisplayed; - PacketHandler.send(PacketDistributor.SERVER.noArg(), new ToggleShoppingListData()); +// PacketHandler.sendToServer(new ToggleShoppingListData()); } } @@ -87,29 +102,29 @@ public void onInputEvent(final MovementInputUpdateEvent event) { public void onScreenInit(final ScreenEvent.InitScreenEvent.Post event) { CraftTracker.LOGGER.trace("#onScreenInit: {}", event); - Screen screen = event.getScreen(); - if(screen instanceof InventoryScreen || screen instanceof CreativeModeInventoryScreen) { - boolean creative = screen instanceof CreativeModeInventoryScreen; -// CraftTracker.LOGGER.debug("#onScreenInit: creative {}", creative); - - // boolean dtLoaded = ModList.get().isLoaded("doggytalents"); - Minecraft mc = Minecraft.getInstance(); - int width = mc.getWindow().getGuiScaledWidth(); - int height = mc.getWindow().getGuiScaledHeight(); - int sizeX = creative ? 195 : 176; - int sizeY = creative ? 136 : 166; - int guiLeft = (width - sizeX) / 2 - ((creative) ? 15 : 0); - int guiTop = (height - sizeY) / 2 - ((!creative) ? 13 : 0); - - int x = guiLeft + (creative ? 36 : sizeX / 2 - 10); - int y = guiTop + (creative ? 7 : 48); - - event.addListener(new SmallButton(x, y, new TranslatableComponent("X"), (btn) -> { - CraftTracker.LOGGER.debug("#onScreenInit: SMALL BUTTON PRESSED {}", btn); - PacketHandler.send(PacketDistributor.SERVER.noArg(), new AddToQueueData("TODO")); -//// btn.active = false; - })); - } +// Screen screen = event.getScreen(); +// if(screen instanceof InventoryScreen || screen instanceof CreativeModeInventoryScreen) { +// boolean creative = screen instanceof CreativeModeInventoryScreen; +//// CraftTracker.LOGGER.debug("#onScreenInit: creative {}", creative); +// +// // boolean dtLoaded = ModList.get().isLoaded("doggytalents"); +// Minecraft mc = Minecraft.getInstance(); +// int width = mc.getWindow().getGuiScaledWidth(); +// int height = mc.getWindow().getGuiScaledHeight(); +// int sizeX = creative ? 195 : 176; +// int sizeY = creative ? 136 : 166; +// int guiLeft = (width - sizeX) / 2 - ((creative) ? 15 : 0); +// int guiTop = (height - sizeY) / 2 - ((!creative) ? 13 : 0); +// +// int x = guiLeft + (creative ? 36 : sizeX / 2 - 10); +// int y = guiTop + (creative ? 7 : 48); +// +// event.addListener(new SmallButton(x, y, new TranslatableComponent("X"), (btn) -> { +// CraftTracker.LOGGER.debug("#onScreenInit: SMALL BUTTON PRESSED {}", btn); +//// PacketHandler.send(PacketDistributor.SERVER.noArg(), new AddToQueueData("TODO")); +////// btn.active = false; +// })); +// } } @SubscribeEvent diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java index 94684e9..2a9d926 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java @@ -1,14 +1,29 @@ package com.sweetrpg.crafttracker.client.overlay; +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; import com.sweetrpg.crafttracker.common.config.ConfigHandler; import com.sweetrpg.crafttracker.common.lib.Constants; +import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; +import mezz.jei.api.constants.VanillaTypes; import net.minecraft.client.gui.GuiComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraftforge.client.gui.IIngameOverlay; +import net.minecraftforge.registries.ForgeRegistries; public class CraftQueueOverlay { + static int TEXT_COLOR = 0xffffffff; + static int SECTION_X_OFFSET = 4; + static int SECTION_TITLE_Y_OFFSET = 18; + static int ITEM_NAME_X_OFFSET = 22; + static int LINE_HEIGHT = 8; + static int MAX_STRING_LENGTH = 16; + + public static final IIngameOverlay CRAFT_QUEUE = (gui, poseStack, partialTicks, width, height) -> { + CraftTracker.LOGGER.trace("CRAFT_QUEUE"); + if(ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_HIDE_EMPTY.get() /* TODO: || user wants it to display */) { // return; } @@ -17,25 +32,53 @@ public class CraftQueueOverlay { var y = ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_Y.get(); var olWidth = Math.min((ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_X.get() + ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_WIDTH.get()), width - 10); var olHeight = Math.min((ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_Y.get() + ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_HEIGHT.get()), height - 10); + var backgroundColor = 0x5f5f5f5f; // TODO: get from config + var borderColor = 0x1f1f1f1f; // TODO: get from config - GuiComponent.fill(poseStack, x, y, olWidth, olHeight, 0x1f1f1f1f); - GuiComponent.fill(poseStack, x + 2, y + 2, olWidth - 2, olHeight - 2, 0x5f5f5f5f); + GuiComponent.fill(poseStack, x, y, olWidth, olHeight, borderColor); + GuiComponent.fill(poseStack, x + 2, y + 2, olWidth - 2, olHeight - 2, backgroundColor); GuiComponent.drawCenteredString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE), - (x + olWidth - 8) / 2, y + 6, 0xffffffff); + (x + olWidth - 8) / 2, y + 6, TEXT_COLOR); + + var mgr = CraftingQueueManager.INSTANCE; + var products = mgr.getEndProducts(); // if products list is empty, display "empty" message - if(true) { // TODO + if(products.isEmpty()) { // TODO GuiComponent.drawCenteredString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY), - (x + olWidth - 8) / 2, y + 6, 0xffffffff); + (x + olWidth - 8) / 2, (y + olHeight - 6) / 2, TEXT_COLOR); return; } // end products - // TODO: title - // TODO: items + + // title + GuiComponent.drawString(poseStack, gui.getFont(), + new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS), + x + SECTION_X_OFFSET, y + SECTION_TITLE_Y_OFFSET, TEXT_COLOR); + + int yPos = y + (LINE_HEIGHT * 2) + 2; + + // items + for(var p : products) { + var index = products.indexOf(p); + + var item = ForgeRegistries.ITEMS.getValue(p.getItemId()); +// var image = item.getDefaultInstance().getTooltipImage(); + + yPos += (index * LINE_HEIGHT); + + var stack = item.getDefaultInstance(); + stack.setCount(p.getQuantity()); + var drawable = CTPlugin.jeiRuntime.getJeiHelpers().getGuiHelper() + .createDrawableIngredient(VanillaTypes.ITEM_STACK, stack); + drawable.draw(poseStack, x + SECTION_X_OFFSET, yPos); +// GuiComponent.blit(poseStack, 2, index * 16, 0, 16, 16, image); + GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 2, TEXT_COLOR); + } // intermediates // TODO: title @@ -44,6 +87,10 @@ public class CraftQueueOverlay { // raw materials // TODO: title // TODO: items + + // fuel + // TODO: title + // TODO: items }; } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/CommonSetup.java b/src/main/java/com/sweetrpg/crafttracker/common/CommonSetup.java index 97ac3a8..2027294 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/CommonSetup.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/CommonSetup.java @@ -4,6 +4,7 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; public class CommonSetup { + public static void init(final FMLCommonSetupEvent event) { event.enqueueWork(() -> { PacketHandler.init(); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/Screens.java b/src/main/java/com/sweetrpg/crafttracker/common/Screens.java index 682161d..89ad51a 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/Screens.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/Screens.java @@ -1,5 +1,7 @@ package com.sweetrpg.crafttracker.common; +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.client.overlay.CraftQueueOverlay; import com.sweetrpg.crafttracker.common.registry.ModItems; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; @@ -44,7 +46,11 @@ public class Screens { // } public static void updateCraftQueue(ServerPlayer player) { -// TODO: display if hidden + CraftTracker.LOGGER.debug("Screens#updateCraftQueue: {}", player); + + // TODO: display if hidden + +// CraftQueueOverlay.CRAFT_QUEUE.render(); // redraw list of items } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java index bdc41bf..c2f4ed8 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java @@ -60,6 +60,6 @@ public void registerAdvanced(IAdvancedRegistration registration) { public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { CraftTracker.LOGGER.debug("CTPlugin#onRuntimeAvailable: {}", jeiRuntime); -CTPlugin.jeiRuntime = jeiRuntime; + CTPlugin.jeiRuntime = jeiRuntime; } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/config/ConfigHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/config/ConfigHandler.java index f2d42f9..40df25d 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/config/ConfigHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/config/ConfigHandler.java @@ -17,7 +17,6 @@ public class ConfigHandler { private static ForgeConfigSpec CONFIG_SERVER_SPEC; private static ForgeConfigSpec CONFIG_CLIENT_SPEC; - public static void init(IEventBus modEventBus) { Pair commonPair = new ForgeConfigSpec.Builder().configure(ServerConfig::new); CONFIG_SERVER_SPEC = commonPair.getRight(); @@ -81,7 +80,6 @@ public ClientConfig(ForgeConfigSpec.Builder builder) { public static class ServerConfig { - public ServerConfig(ForgeConfigSpec.Builder builder) { { builder.push("General"); diff --git a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java index 793221d..6c53d67 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java @@ -1,52 +1,58 @@ package com.sweetrpg.crafttracker.common.event; +import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.lib.Constants; import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.biome.Biome; -import net.minecraftforge.common.world.BiomeGenerationSettingsBuilder; import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.living.LootingLevelEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.event.world.BiomeLoadingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(modid = Constants.MOD_ID) public class EventHandler { - @SubscribeEvent - public void rightClickEntity(final PlayerInteractEvent.EntityInteract event) { - Level world = event.getWorld(); - - ItemStack stack = event.getItemStack(); - Entity target = event.getTarget(); - - } - - @SubscribeEvent - public static void onBiomeLoad(BiomeLoadingEvent event) { - BiomeGenerationSettingsBuilder builder = event.getGeneration(); - Biome.ClimateSettings climate = event.getClimate(); - - } - +// @SubscribeEvent +// public void rightClickEntity(final PlayerInteractEvent.EntityInteract event) { +// CraftTracker.LOGGER.debug("EventHandler#rightClickEntity: {}", event); +// +// Level world = event.getWorld(); +// +// ItemStack stack = event.getItemStack(); +// Entity target = event.getTarget(); +// +// } +// +// @SubscribeEvent +// public static void onBiomeLoad(BiomeLoadingEvent event) { +// CraftTracker.LOGGER.debug("EventHandler#onBiomeLoad: {}", event); +// +// BiomeGenerationSettingsBuilder builder = event.getGeneration(); +// Biome.ClimateSettings climate = event.getClimate(); +// +// } +// @SubscribeEvent public void onEntitySpawn(final EntityJoinWorldEvent event) { - Entity entity = event.getEntity(); + CraftTracker.LOGGER.debug("EventHandler#onEntitySpawn: {}", event); + Entity entity = event.getEntity(); +// if(entity instanceof ServerPlayer player) { +// CraftingQueueManager.get(player, entity.level); +// } } @SubscribeEvent public void playerLoggedIn(final PlayerLoggedInEvent event) { + CraftTracker.LOGGER.debug("EventHandler#playerLoggedIn: {}", event); +// CraftingQueueManager.get(event.getPlayer().level); } - @SubscribeEvent - public void onLootDrop(final LootingLevelEvent event) { +// @SubscribeEvent +// public void onLootDrop(final LootingLevelEvent event) { +// CraftTracker.LOGGER.debug("EventHandler#onLootDrop: {}", event); +// +// } - } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java index 123f472..3b4a0b9 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java @@ -27,6 +27,9 @@ public class Constants { // Translation keys public static final String TRANSLATION_KEY_GUI_CRAFTLIST_TITLE = "crafttracker.screen.craft_list.title"; public static final String TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY = "crafttracker.screen.craft_list.empty_message"; + public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS = "crafttracker.screen.craft_list.section.products"; + public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES = "crafttracker.screen.craft_list.section.intermediates"; + public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS = "crafttracker.screen.craft_list.section.materials"; public static final String TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE = "crafttracker.screen.shopping_list.title"; public static final String TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE = "key.categories.crafttracker"; public static final String TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE = "key.addToQueue"; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index 4278367..3b274dc 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -3,12 +3,12 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; -import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; import com.sweetrpg.crafttracker.common.util.RecipeUtil; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.Level; -import org.antlr.v4.misc.OrderedHashMap; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,38 +18,78 @@ public class CraftingQueueManager { public static CraftingQueueManager INSTANCE = new CraftingQueueManager(); - private Map endProducts = new OrderedHashMap<>(); - private Map intermediateProducts = new OrderedHashMap<>(); + private Map endProducts = new HashMap<>(); + private Map intermediateProducts = new HashMap<>(); private Map rawMaterials = new HashMap<>(); + private Map fuel = new HashMap<>(); + private ServerPlayer player; // private CraftingQueueStorage storage; - public CraftingQueueManager() { -// this.storage = new CraftingQueueStorage(); - } - -// public List getEndProducts() { -// return endProducts.entrySet() -// .stream() -// .map((e) -> new QueueItem(e.getKey(), e.getValue())) -// .collect(Collectors.toUnmodifiableList()); -// } +// public static CraftingQueueManager get(ServerPlayer player, Level level) { +// CraftTracker.LOGGER.debug("CraftingQueueManager#get: {}, level: {}", player, level); +// +// if(INSTANCE == null) { +// var storage = CraftingQueueStorage.get(level); +// INSTANCE = new CraftingQueueManager(player, storage); +// } // -// public List getIntermediates() { -// return intermediateProducts.entrySet() -// .stream() -// .map((e) -> new QueueItem(e.getKey(), e.getValue())) -// .collect(Collectors.toUnmodifiableList()); +// return INSTANCE; // } // -// public List getRawMaterials() { -// return rawMaterials.entrySet() -// .stream() -// .map((e) -> new QueueItem(e.getKey(), e.getValue())) -// .collect(Collectors.toUnmodifiableList()); +// CraftingQueueManager(ServerPlayer player, CraftingQueueStorage storage) { +// CraftTracker.LOGGER.debug("CraftingQueueManager: {}, level: {}", player, storage); +// +// this.player = player; +// +// storage.getAll().stream() +// .map((data) -> { +// var recipes = RecipeUtil.getRecipesFor(data.getItemId()); +// return new CraftingQueueProduct(data.getItemId(), recipes, data.getQuantity()); +// }) +// .forEach((p) -> this.endProducts.put(p.getItemId(), p)); +// + + public CraftingQueueManager() { + } + + public void load(Player player) { + + } + + //// this.storage = new CraftingQueueStorage(); // } - public void addProduct(Level level, ResourceLocation itemId, int quantity) { + public List getEndProducts() { + return endProducts.entrySet() + .stream() + .map((e) -> + new ProductItem(e.getKey(), e.getValue().getQuantity(), new ArrayList<>())) + .collect(Collectors.toUnmodifiableList()); + } + + public List getIntermediates() { + return intermediateProducts.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public List getRawMaterials() { + return rawMaterials.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public List getFuel() { + return fuel.entrySet() + .stream() + .map((e) -> new QueueItem(e.getKey(), e.getValue())) + .collect(Collectors.toUnmodifiableList()); + } + + public void addProduct(Player player, ResourceLocation itemId, int quantity) { CraftTracker.LOGGER.debug("CraftingQueueManager#addProduct: {}, quantity: {}", itemId, quantity); var recipes = RecipeUtil.getRecipesFor(itemId); @@ -61,16 +101,18 @@ public void addProduct(Level level, ResourceLocation itemId, int quantity) { endProducts.compute(itemId, (rl, p) -> p == null ? product : new CraftingQueueProduct(p.getItemId(), p.getRecipes(), p.getQuantity() + quantity)); - CraftingQueueStorage.get(level).putData(itemId, quantity); +// CraftingQueueStorage.get(level).putData(itemId, quantity); computeAll(); + +// PacketHandler.sendToPlayer(this.player, new UpdateCraftQueueData(this.getEndProducts())); } else { CraftTracker.LOGGER.info("Not adding {} to queue, since there are no recipes for it.", itemId); } } - public void removeProduct(Level level, ResourceLocation itemId, int quantity) { + public void removeProduct(Player player, ResourceLocation itemId, int quantity) { CraftTracker.LOGGER.debug("CraftingQueueManager#removeProduct: {}, quantity: {}", itemId, quantity); var product = this.endProducts.get(itemId); @@ -83,11 +125,11 @@ public void removeProduct(Level level, ResourceLocation itemId, int quantity) { int newQuantity = product.getQuantity() - quantity; if(newQuantity < 1) { CraftTracker.LOGGER.info("Removing item from queue storage: {}", itemId); - CraftingQueueStorage.get(level).removeData(itemId); +// CraftingQueueStorage.get(level).removeData(itemId); } else { CraftTracker.LOGGER.info("Adjusting quantity of item in queue storage to {}: {}", quantity, itemId); - CraftingQueueStorage.get(level).putData(itemId, newQuantity); +// CraftingQueueStorage.get(level).putData(itemId, newQuantity); } computeAll(); @@ -96,7 +138,7 @@ public void removeProduct(Level level, ResourceLocation itemId, int quantity) { public void computeAll() { CraftTracker.LOGGER.debug("CraftingQueueManager#computeAll"); - Map intermediateProducts = new OrderedHashMap<>(); + Map intermediateProducts = new HashMap<>(); Map rawMaterials = new HashMap<>(); var rm = CTPlugin.jeiRuntime.getRecipeManager(); @@ -125,6 +167,30 @@ public void computeIntermediates() { } + public static class ProductItem { + private ResourceLocation itemId; + private int quantity; + private List categories; + + public ProductItem(ResourceLocation itemId, int quantity, List categories) { + this.itemId = itemId; + this.quantity = quantity; + this.categories = categories; + } + + public ResourceLocation getItemId() { + return itemId; + } + + public int getQuantity() { + return quantity; + } + + public List getCategories() { + return categories; + } + } + public class QueueItem { private ResourceLocation itemId; private int quantity; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java index 399f422..3dac903 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/PacketHandler.java @@ -1,12 +1,7 @@ package com.sweetrpg.crafttracker.common.network; import com.sweetrpg.crafttracker.CraftTracker; -import com.sweetrpg.crafttracker.common.network.packet.AddToQueuePacket; -import com.sweetrpg.crafttracker.common.network.packet.ToggleCraftListPacket; -import com.sweetrpg.crafttracker.common.network.packet.ToggleShoppingListPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; +import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.PacketDistributor; public final class PacketHandler { @@ -14,9 +9,11 @@ public final class PacketHandler { private static int disc = 0; public static void init() { - registerPacket(new AddToQueuePacket(), AddToQueueData.class); - registerPacket(new ToggleCraftListPacket(), ToggleCraftListData.class); - registerPacket(new ToggleShoppingListPacket(), ToggleShoppingListData.class); +// registerPacket(new AddToQueuePacket(), AddToQueueData.class); +// registerPacket(new ToggleCraftListPacket(), ToggleCraftListData.class); +// registerPacket(new ToggleShoppingListPacket(), ToggleShoppingListData.class); +// registerPacket(new UpdateCraftQueuePacket(), UpdateCraftQueueData.class); +// registerPacket(new UpdateShoppingListPacket(), UpdateShoppingListData.class); // registerPacket(new CatNamePacket(), CatNameData.class); // registerPacket(new CatObeyPacket(), CatObeyData.class); // registerPacket(new CatTalentPacket(), CatTalentData.class); @@ -29,8 +26,12 @@ public static void init() { // registerPacket(new CatTexturePacket(), CatTextureData.class); } - public static void send(PacketDistributor.PacketTarget target, MSG message) { - CraftTracker.HANDLER.send(target, message); + public static void sendToServer(MSG message) { + CraftTracker.HANDLER.sendToServer(message); + } + + public static void sendToPlayer(ServerPlayer player, MSG message) { + CraftTracker.HANDLER.send(PacketDistributor.PLAYER.with(() -> player), message); } public static void registerPacket(IPacket packet, Class dataClass) { diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java index 7ea1594..429f412 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/AddToQueuePacket.java @@ -1,57 +1,49 @@ -package com.sweetrpg.crafttracker.common.network.packet; - -import com.sweetrpg.crafttracker.CraftTracker; -import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; -import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; -import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.network.NetworkEvent.Context; - -import java.util.function.Supplier; - -public class AddToQueuePacket implements IPacket { - - @Override - public void encode(AddToQueueData data, FriendlyByteBuf buf) { - buf.writeUtf(data.itemId); - } - - @Override - public AddToQueueData decode(FriendlyByteBuf buf) { - String itemId = buf.readUtf(); - return new AddToQueueData(itemId); - } - - @Override - public final void handle(AddToQueueData data, Supplier ctx) { - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: {}", data); - - ctx.get().enqueueWork(() -> { - LogicalSide side = ctx.get().getDirection().getReceptionSide(); - if(side.isClient()) { - - } - else if(side.isServer()) { - CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse() - .ifPresent(ingredient -> { - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType()); - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient()); - - if(ingredient.getIngredient() instanceof ItemStack itemStack) { - ResourceLocation res = itemStack.getItem().getRegistryName(); - CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res); - - CraftingQueueManager.INSTANCE.addProduct(ctx.get().getSender().level, res, 1); - } - }); - } - }); - - ctx.get().setPacketHandled(true); - } - -} +//package com.sweetrpg.crafttracker.common.network.packet; +// +//import com.sweetrpg.crafttracker.CraftTracker; +//import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +//import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; +//import com.sweetrpg.crafttracker.common.network.IPacket; +//import com.sweetrpg.crafttracker.common.network.packet.data.AddToQueueData; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraft.resources.ResourceLocation; +//import net.minecraft.world.item.ItemStack; +//import net.minecraftforge.fml.LogicalSide; +//import net.minecraftforge.network.NetworkEvent.Context; +// +//import java.util.function.Supplier; +// +//public class AddToQueuePacket implements IPacket { +// +// @Override +// public void encode(AddToQueueData data, FriendlyByteBuf buf) { +// buf.writeResourceLocation(data.itemId); +// buf.writeInt(data.quantity); +// } +// +// @Override +// public AddToQueueData decode(FriendlyByteBuf buf) { +// ResourceLocation itemId = buf.readResourceLocation(); +// int quantity = buf.readInt(); +// return new AddToQueueData(itemId, quantity); +// } +// +// @Override +// public final void handle(AddToQueueData data, Supplier ctx) { +// CraftTracker.LOGGER.debug("AddToQueuePacket#handle: {}", data); +// +// ctx.get().enqueueWork(() -> { +// LogicalSide side = ctx.get().getDirection().getReceptionSide(); +// if(side.isClient()) { +// +// } +// else if(side.isServer()) { +// var player = ctx.get().getSender(); +// CraftingQueueManager.get(player, player.level).addProduct(player.level, data.itemId, data.quantity); +// } +// }); +// +// ctx.get().setPacketHandled(true); +// } +// +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java index 9101f5e..fe3aff2 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleCraftListPacket.java @@ -1,39 +1,47 @@ -package com.sweetrpg.crafttracker.common.network.packet; - -import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraftforge.network.NetworkEvent.Context; - -import java.util.function.Supplier; - -public class ToggleCraftListPacket implements IPacket { - - @Override - public void encode(ToggleCraftListData data, FriendlyByteBuf buf) { - -// buf.writeBoolean(data.display); - } - - @Override - public ToggleCraftListData decode(FriendlyByteBuf buf) { -// boolean display = buf.readBoolean(); - return new ToggleCraftListData(); - } - - @Override - public final void handle(ToggleCraftListData data, Supplier ctx) { - ctx.get().enqueueWork(() -> { -// Entity target = ctx.get().getSender().level.getEntity(data.entityId); -// -// if (!(target instanceof CatEntity)) { -// return; -// } -// -// this.handleCat((CatEntity) target, data, ctx); - }); - - ctx.get().setPacketHandled(true); - } - -} +//package com.sweetrpg.crafttracker.common.network.packet; +// +//import com.sweetrpg.crafttracker.common.network.IPacket; +//import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraftforge.api.distmarker.Dist; +//import net.minecraftforge.fml.DistExecutor; +//import net.minecraftforge.network.NetworkEvent; +//import net.minecraftforge.network.NetworkEvent.Context; +// +//import java.util.function.Supplier; +// +//public class ToggleCraftListPacket implements IPacket { +// +// @Override +// public void encode(ToggleCraftListData data, FriendlyByteBuf buf) { +// +//// buf.writeBoolean(data.display); +// } +// +// @Override +// public ToggleCraftListData decode(FriendlyByteBuf buf) { +//// boolean display = buf.readBoolean(); +// return new ToggleCraftListData(); +// } +// +// @Override +// public final void handle(ToggleCraftListData data, Supplier ctx) { +// ctx.get().enqueueWork(() -> { +//// Entity target = ctx.get().getSender().level.getEntity(data.entityId); +//// +//// if (!(target instanceof CatEntity)) { +//// return; +//// } +//// +//// this.handleCat((CatEntity) target, data, ctx); +// DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> handleClientPacket(data, ctx)); +// }); +// +// ctx.get().setPacketHandled(true); +// } +// +// static void handleClientPacket(ToggleCraftListData data, Supplier ctx) { +// +// } +// +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java index f7c4b47..550e0a8 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/ToggleShoppingListPacket.java @@ -1,39 +1,47 @@ -package com.sweetrpg.crafttracker.common.network.packet; - -import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraftforge.network.NetworkEvent.Context; - -import java.util.function.Supplier; - -public class ToggleShoppingListPacket implements IPacket { - - @Override - public void encode(ToggleShoppingListData data, FriendlyByteBuf buf) { - -// buf.writeBoolean(data.display); - } - - @Override - public ToggleShoppingListData decode(FriendlyByteBuf buf) { -// boolean display = buf.readBoolean(); - return new ToggleShoppingListData(); - } - - @Override - public final void handle(ToggleShoppingListData data, Supplier ctx) { - ctx.get().enqueueWork(() -> { -// Entity target = ctx.get().getSender().level.getEntity(data.entityId); -// -// if (!(target instanceof CatEntity)) { -// return; -// } -// -// this.handleCat((CatEntity) target, data, ctx); - }); - - ctx.get().setPacketHandled(true); - } - -} +//package com.sweetrpg.crafttracker.common.network.packet; +// +//import com.sweetrpg.crafttracker.common.network.IPacket; +//import com.sweetrpg.crafttracker.common.network.packet.data.ToggleCraftListData; +//import com.sweetrpg.crafttracker.common.network.packet.data.ToggleShoppingListData; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraftforge.api.distmarker.Dist; +//import net.minecraftforge.fml.DistExecutor; +//import net.minecraftforge.network.NetworkEvent; +//import net.minecraftforge.network.NetworkEvent.Context; +// +//import java.util.function.Supplier; +// +//public class ToggleShoppingListPacket implements IPacket { +// +// @Override +// public void encode(ToggleShoppingListData data, FriendlyByteBuf buf) { +// +//// buf.writeBoolean(data.display); +// } +// +// @Override +// public ToggleShoppingListData decode(FriendlyByteBuf buf) { +//// boolean display = buf.readBoolean(); +// return new ToggleShoppingListData(); +// } +// +// @Override +// public final void handle(ToggleShoppingListData data, Supplier ctx) { +// ctx.get().enqueueWork(() -> { +//// Entity target = ctx.get().getSender().level.getEntity(data.entityId); +//// +//// if (!(target instanceof CatEntity)) { +//// return; +//// } +//// +//// this.handleCat((CatEntity) target, data, ctx); +// DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> handleClientPacket(data, ctx)); +// }); +// +// ctx.get().setPacketHandled(true); +// } +// +// static void handleClientPacket(ToggleShoppingListData data, Supplier ctx) { +// +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java index 4879d28..ad4116f 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateCraftQueuePacket.java @@ -1,44 +1,55 @@ -package com.sweetrpg.crafttracker.common.network.packet; - -import com.sweetrpg.crafttracker.common.Screens; -import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.network.NetworkEvent; - -import java.util.Map; -import java.util.function.Supplier; - -public class UpdateCraftQueuePacket implements IPacket { - - @Override - public UpdateCraftQueueData decode(FriendlyByteBuf buf) { - Map data = buf.readMap((fbb) -> fbb.readResourceLocation(), - (fbb) -> fbb.readInt()); - - return new UpdateCraftQueueData(data); - } - - - @Override - public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { - buf.writeMap(data.getEndProducts(), - (fbb, itemId) -> fbb.writeResourceLocation(itemId), - (fbb, i) -> fbb.writeInt(i)); - } - - @Override - public void handle(UpdateCraftQueueData data, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if(ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { - ServerPlayer player = ctx.get().getSender(); - Screens.updateCraftQueue(player); - } - }); - - ctx.get().setPacketHandled(true); - } -} +//package com.sweetrpg.crafttracker.common.network.packet; +// +//import com.sweetrpg.crafttracker.CraftTracker; +//import com.sweetrpg.crafttracker.common.Screens; +//import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; +//import com.sweetrpg.crafttracker.common.network.IPacket; +//import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraft.server.level.ServerPlayer; +//import net.minecraftforge.fml.LogicalSide; +//import net.minecraftforge.network.NetworkEvent; +// +//import java.util.List; +//import java.util.function.Supplier; +// +//public class UpdateCraftQueuePacket implements IPacket { +// +// @Override +// public UpdateCraftQueueData decode(FriendlyByteBuf buf) { +// List items = buf.readList((lb) -> { +// var itemId = lb.readResourceLocation(); +// var quantity = lb.readInt(); +// var categories = lb.readList(FriendlyByteBuf::readResourceLocation); +// +// return new CraftingQueueManager.ProductItem(itemId, quantity, categories); +// }); +// +// return new UpdateCraftQueueData(items); +// } +// +// @Override +// public void encode(UpdateCraftQueueData data, FriendlyByteBuf buf) { +// buf.writeCollection(data.getEndProducts(), +// (pb, item) -> { +// pb.writeResourceLocation(item.getItemId()); +// pb.writeInt(item.getQuantity()); +// pb.writeCollection(item.getCategories(), +// (cb, category) -> pb.writeResourceLocation(category)); +// }); +// } +// +// @Override +// public void handle(UpdateCraftQueueData data, Supplier ctx) { +// CraftTracker.LOGGER.debug("UpdateCraftQueuePacket#handle: {}, ctx: {}", data, ctx); +// +// ctx.get().enqueueWork(() -> { +// if(ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { +// ServerPlayer player = ctx.get().getSender(); +// Screens.updateCraftQueue(player); +// } +// }); +// +// ctx.get().setPacketHandled(true); +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java index 27b70b9..ed4fde0 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/UpdateShoppingListPacket.java @@ -1,42 +1,42 @@ -package com.sweetrpg.crafttracker.common.network.packet; - -import com.sweetrpg.crafttracker.common.network.IPacket; -import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; -import com.sweetrpg.crafttracker.common.network.packet.data.UpdateShoppingListData; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.network.NetworkEvent; - -import java.util.function.Supplier; - -public class UpdateShoppingListPacket implements IPacket { - - @Override - public UpdateShoppingListData decode(FriendlyByteBuf buf) { - return new UpdateShoppingListData(); - } - - - @Override - public void encode(UpdateShoppingListData data, FriendlyByteBuf buf) { - - } - - @Override - public void handle(UpdateShoppingListData data, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { - ServerPlayer player = ctx.get().getSender(); -// List cats = player.level.getEntitiesOfClass(CatEntity.class, player.getBoundingBox().inflate(12D, 12D, 12D), -// (cat) -> cat.canInteract(player) && PackCatTalent.hasInventory(cat) -// ); -// if (!cats.isEmpty()) { -// Screens.openCatInventoriesScreen(player, cats); -// } - } - }); - - ctx.get().setPacketHandled(true); - } -} +//package com.sweetrpg.crafttracker.common.network.packet; +// +//import com.sweetrpg.crafttracker.common.network.IPacket; +//import com.sweetrpg.crafttracker.common.network.packet.data.UpdateCraftQueueData; +//import com.sweetrpg.crafttracker.common.network.packet.data.UpdateShoppingListData; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraft.server.level.ServerPlayer; +//import net.minecraftforge.fml.LogicalSide; +//import net.minecraftforge.network.NetworkEvent; +// +//import java.util.function.Supplier; +// +//public class UpdateShoppingListPacket implements IPacket { +// +// @Override +// public UpdateShoppingListData decode(FriendlyByteBuf buf) { +// return new UpdateShoppingListData(); +// } +// +// +// @Override +// public void encode(UpdateShoppingListData data, FriendlyByteBuf buf) { +// +// } +// +// @Override +// public void handle(UpdateShoppingListData data, Supplier ctx) { +// ctx.get().enqueueWork(() -> { +// if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { +// ServerPlayer player = ctx.get().getSender(); +//// List cats = player.level.getEntitiesOfClass(CatEntity.class, player.getBoundingBox().inflate(12D, 12D, 12D), +//// (cat) -> cat.canInteract(player) && PackCatTalent.hasInventory(cat) +//// ); +//// if (!cats.isEmpty()) { +//// Screens.openCatInventoriesScreen(player, cats); +//// } +// } +// }); +// +// ctx.get().setPacketHandled(true); +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/AddToQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/AddToQueueData.java index 4aff2e3..840d7e5 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/AddToQueueData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/AddToQueueData.java @@ -1,10 +1,14 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class AddToQueueData { - - public String itemId; - - public AddToQueueData(String itemId) { - this.itemId = itemId; - } -} +//package com.sweetrpg.crafttracker.common.network.packet.data; +// +//import net.minecraft.resources.ResourceLocation; +// +//public class AddToQueueData { +// +// public ResourceLocation itemId; +// public int quantity; +// +// public AddToQueueData(ResourceLocation itemId, int quantity) { +// this.itemId = itemId; +// this.quantity = quantity; +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java index e98b330..d167074 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleCraftListData.java @@ -1,7 +1,7 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class ToggleCraftListData { - - public ToggleCraftListData() { - } -} +//package com.sweetrpg.crafttracker.common.network.packet.data; +// +//public class ToggleCraftListData { +// +// public ToggleCraftListData() { +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java index 91e2d88..10957b2 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/ToggleShoppingListData.java @@ -1,8 +1,8 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class ToggleShoppingListData { - - public ToggleShoppingListData() { - - } -} +//package com.sweetrpg.crafttracker.common.network.packet.data; +// +//public class ToggleShoppingListData { +// +// public ToggleShoppingListData() { +// +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java index a614174..3d4fa8d 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateCraftQueueData.java @@ -1,19 +1,22 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -import net.minecraft.resources.ResourceLocation; -import org.antlr.v4.misc.OrderedHashMap; - -import java.util.Map; - -public class UpdateCraftQueueData { - - private Map endProducts = new OrderedHashMap<>(); - - public UpdateCraftQueueData(Map endProducts) { - this.endProducts = endProducts; - } - - public Map getEndProducts() { - return endProducts; - } -} +//package com.sweetrpg.crafttracker.common.network.packet.data; +// +//import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; +//import net.minecraft.resources.ResourceLocation; +//import org.antlr.v4.misc.OrderedHashMap; +// +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Map; +// +//public class UpdateCraftQueueData { +// +// private List endProducts = new ArrayList<>(); +// +// public UpdateCraftQueueData(List endProducts) { +// this.endProducts = endProducts; +// } +// +// public List getEndProducts() { +// return endProducts; +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java index 5eeee4c..476bc99 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/network/packet/data/UpdateShoppingListData.java @@ -1,9 +1,9 @@ -package com.sweetrpg.crafttracker.common.network.packet.data; - -public class UpdateShoppingListData { - - public UpdateShoppingListData() { - super(); - } - -} +//package com.sweetrpg.crafttracker.common.network.packet.data; +// +//public class UpdateShoppingListData { +// +// public UpdateShoppingListData() { +// super(); +// } +// +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java index da9bcfc..1a94f76 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueData.java @@ -1,77 +1,77 @@ -package com.sweetrpg.crafttracker.common.storage; - -import com.sweetrpg.crafttracker.CraftTracker; -import com.sweetrpg.crafttracker.common.util.NBTUtil; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; - -/** - * This class represents an entry in the crafting queue's end products list. - */ -public class CraftingQueueData { - - private final CraftingQueueStorage storage; - private ResourceLocation itemId; - private int quantity; - - protected CraftingQueueData(CraftingQueueStorage storage) { - this.storage = storage; - } - - protected CraftingQueueData(CraftingQueueStorage storage, ResourceLocation itemId, int quantity) { - this.storage = storage; - this.itemId = itemId; - this.quantity = quantity; - } - -// public void addItem(ResourceLocation itemId, int quantity) { -// CraftTracker.LOGGER.debug("CraftingQueueData#addItem: {}, quantity {}", itemId, quantity); +//package com.sweetrpg.crafttracker.common.storage; // -// this.endProducts.compute(itemId, (k, v) -> (v == null ? 0 : v) + quantity); +//import com.sweetrpg.crafttracker.CraftTracker; +//import com.sweetrpg.crafttracker.common.util.NBTUtil; +//import net.minecraft.nbt.CompoundTag; +//import net.minecraft.resources.ResourceLocation; // -// this.storage.setDirty(); +///** +// * This class represents an entry in the crafting queue's end products list. +// */ +//public class CraftingQueueData { +// +// private final CraftingQueueStorage storage; +// private ResourceLocation itemId; +// private int quantity; +// +// protected CraftingQueueData(CraftingQueueStorage storage) { +// this.storage = storage; +// } +// +// protected CraftingQueueData(CraftingQueueStorage storage, ResourceLocation itemId, int quantity) { +// this.storage = storage; +// this.itemId = itemId; +// this.quantity = quantity; // } // -// public void removeItem(ResourceLocation itemId, int quantity) { -// CraftTracker.LOGGER.debug("CraftingQueueData#removeItem: {}, quantity {}", itemId, quantity); +//// public void addItem(ResourceLocation itemId, int quantity) { +//// CraftTracker.LOGGER.debug("CraftingQueueData#addItem: {}, quantity {}", itemId, quantity); +//// +//// this.endProducts.compute(itemId, (k, v) -> (v == null ? 0 : v) + quantity); +//// +//// this.storage.setDirty(); +//// } +//// +//// public void removeItem(ResourceLocation itemId, int quantity) { +//// CraftTracker.LOGGER.debug("CraftingQueueData#removeItem: {}, quantity {}", itemId, quantity); +//// +//// this.endProducts.computeIfPresent(itemId, (k, v) -> { +//// if(v - quantity < 1) { +//// return null; +//// } +//// +//// return v - quantity; +//// }); +//// +//// this.storage.setDirty(); +//// } +// +// public void read(CompoundTag compound) { +// CraftTracker.LOGGER.debug("CraftingQueueData#read"); +// +// this.itemId = NBTUtil.getResourceLocation(compound, Keys.ITEM_ID); +// this.quantity = compound.getInt(Keys.QUANTITY); +// } // -// this.endProducts.computeIfPresent(itemId, (k, v) -> { -// if(v - quantity < 1) { -// return null; -// } +// public CompoundTag write(CompoundTag compound) { +// CraftTracker.LOGGER.debug("CraftingQueueData#write"); // -// return v - quantity; -// }); +// NBTUtil.putResourceLocation(compound, Keys.ITEM_ID, this.itemId); +// compound.putInt(Keys.QUANTITY, this.quantity); +// +// return compound; +// } +// +// public ResourceLocation getItemId() { +// return itemId; +// } +// +// public int getQuantity() { +// return quantity; +// } // -// this.storage.setDirty(); +// static class Keys { +// static String ITEM_ID = "item_id"; +// static String QUANTITY = "quantity"; // } - - public void read(CompoundTag compound) { - CraftTracker.LOGGER.debug("CraftingQueueData#read"); - - this.itemId = NBTUtil.getResourceLocation(compound, Keys.ITEM_ID); - this.quantity = compound.getInt(Keys.QUANTITY); - } - - public CompoundTag write(CompoundTag compound) { - CraftTracker.LOGGER.debug("CraftingQueueData#write"); - - NBTUtil.putResourceLocation(compound, Keys.ITEM_ID, this.itemId); - compound.putInt(Keys.QUANTITY, this.quantity); - - return compound; - } - - public ResourceLocation getItemId() { - return itemId; - } - - public int getQuantity() { - return quantity; - } - - static class Keys { - static String ITEM_ID = "item_id"; - static String QUANTITY = "quantity"; - } -} +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java index 8ea4a3f..9c684cf 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java @@ -1,133 +1,139 @@ -package com.sweetrpg.crafttracker.common.storage; - -import com.google.common.collect.Maps; -import com.sweetrpg.crafttracker.CraftTracker; -import com.sweetrpg.crafttracker.common.lib.Constants; -import com.sweetrpg.crafttracker.common.util.NBTUtil; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.saveddata.SavedData; -import net.minecraft.world.level.storage.DimensionDataStorage; -import net.minecraft.world.phys.Vec3; - -import javax.annotation.Nullable; -import java.util.Map; -import java.util.UUID; - -/** - * This class handles storage of all crafting queue information for a player. - * - * This includes: - * * Position and visibility of the craft list window - * * Position and visibility of the shopping list window - * * The list of end products in the queue by item ID and quantity - */ -public class CraftingQueueStorage extends SavedData { - - private @Nullable UUID ownerId; - private Vec3 craftingQueuePosition; - private boolean craftingQueueVisible; - private Vec3 shoppingListPosition; - private boolean shoppingListVisible; - private Map queueData = Maps.newConcurrentMap(); - - public CraftingQueueStorage() {} - - public static CraftingQueueStorage get(Level world) { - CraftTracker.LOGGER.debug("CraftingQueueStorage#get: {}", world); - - if (!(world instanceof ServerLevel)) { - throw new RuntimeException("Tried to access crafting queue data from the client. This should not happen..."); - } - - ServerLevel overworld = world.getServer().getLevel(Level.OVERWORLD); - - DimensionDataStorage storage = overworld.getDataStorage(); - return storage.computeIfAbsent(CraftingQueueStorage::load, CraftingQueueStorage::new, Constants.STORAGE_CRAFTING_QUEUE); - } - - public void putData(ResourceLocation itemId, int quantity) { - CraftTracker.LOGGER.debug("CraftingQueueStorage#putData: {}, quantity: {}", itemId, quantity); - - this.queueData.compute(itemId, (k, data) -> { - if(data == null) { - return new CraftingQueueData(this, itemId, quantity); - } - - return new CraftingQueueData(this, itemId, data.getQuantity() + quantity); - }); - - this.setDirty(); - } - - public void removeData(ResourceLocation itemId) { - CraftTracker.LOGGER.debug("CraftingQueueStorage#removeData: {}", itemId); - - this.queueData.remove(itemId); - - this.setDirty(); - } - - public static CraftingQueueStorage load(CompoundTag nbt) { - CraftTracker.LOGGER.debug("CraftingQueueStorage#load: {}", nbt); - - CraftingQueueStorage store = new CraftingQueueStorage(); - store.queueData.clear(); - - store.ownerId = NBTUtil.getUniqueId(nbt, Keys.OWNER_ID); - store.craftingQueuePosition = NBTUtil.getVector3d(nbt); - store.craftingQueueVisible = nbt.getBoolean(Keys.CRAFTING_QUEUE_VISIBLE); - store.shoppingListPosition = NBTUtil.getVector3d(nbt); - store.shoppingListVisible = nbt.getBoolean(Keys.SHOPPING_LIST_VISIBLE); - - ListTag list = nbt.getList(Keys.QUEUE_DATA, Tag.TAG_COMPOUND); - - for (int i = 0; i < list.size(); ++i) { - CompoundTag queueDataCompound = list.getCompound(i); - - CraftingQueueData queueData = new CraftingQueueData(store); - queueData.read(queueDataCompound); - - store.queueData.put(queueData.getItemId(), queueData); - } - - return store; - } - - @Override - public CompoundTag save(CompoundTag compound) { - CraftTracker.LOGGER.debug("CraftingQueueStorage#save: {}", compound); - - NBTUtil.putUniqueId(compound, Keys.OWNER_ID, this.ownerId); - NBTUtil.putVector3d(compound, this.craftingQueuePosition); - compound.putBoolean(Keys.CRAFTING_QUEUE_VISIBLE, this.craftingQueueVisible); - NBTUtil.putVector3d(compound, this.shoppingListPosition); - compound.putBoolean(Keys.SHOPPING_LIST_VISIBLE, this.shoppingListVisible); - - ListTag list = new ListTag(); - - for (Map.Entry entry : this.queueData.entrySet()) { - CompoundTag queueDataCompound = new CompoundTag(); - - CraftingQueueData queueData = entry.getValue(); - queueData.write(queueDataCompound); - - list.add(queueDataCompound); - } - - compound.put(Keys.QUEUE_DATA, list); - - return compound; - } - - static class Keys { - static String OWNER_ID = "owner_id"; - static String CRAFTING_QUEUE_VISIBLE = "crafting_queue_visible"; - static String SHOPPING_LIST_VISIBLE = "shopping_list_visible"; - static String QUEUE_DATA = "queue_data"; - } -} +//package com.sweetrpg.crafttracker.common.storage; +// +//import com.google.common.collect.Maps; +//import com.sweetrpg.crafttracker.CraftTracker; +//import com.sweetrpg.crafttracker.common.lib.Constants; +//import com.sweetrpg.crafttracker.common.util.NBTUtil; +//import net.minecraft.nbt.CompoundTag; +//import net.minecraft.nbt.ListTag; +//import net.minecraft.nbt.Tag; +//import net.minecraft.resources.ResourceLocation; +//import net.minecraft.server.level.ServerLevel; +//import net.minecraft.world.level.Level; +//import net.minecraft.world.level.saveddata.SavedData; +//import net.minecraft.world.level.storage.DimensionDataStorage; +//import net.minecraft.world.phys.Vec3; +// +//import javax.annotation.Nullable; +//import java.util.Collection; +//import java.util.Collections; +//import java.util.Map; +//import java.util.UUID; +// +///** +// * This class handles storage of all crafting queue information for a player. +// * +// * This includes: +// * * Position and visibility of the craft list window +// * * Position and visibility of the shopping list window +// * * The list of end products in the queue by item ID and quantity +// */ +//public class CraftingQueueStorage extends SavedData { +// +// private @Nullable UUID ownerId; +// private Vec3 craftingQueuePosition; +// private boolean craftingQueueVisible; +// private Vec3 shoppingListPosition; +// private boolean shoppingListVisible; +// private Map queueData = Maps.newConcurrentMap(); +// +// public CraftingQueueStorage() {} +// +// public static CraftingQueueStorage get(Level world) { +// CraftTracker.LOGGER.debug("CraftingQueueStorage#get: {}", world); +// +// if (!(world instanceof ServerLevel)) { +// throw new RuntimeException("Tried to access crafting queue data from the client. This should not happen..."); +// } +// +// ServerLevel overworld = world.getServer().getLevel(Level.OVERWORLD); +// +// DimensionDataStorage storage = overworld.getDataStorage(); +// return storage.computeIfAbsent(CraftingQueueStorage::load, CraftingQueueStorage::new, Constants.STORAGE_CRAFTING_QUEUE); +// } +// +// public void putData(ResourceLocation itemId, int quantity) { +// CraftTracker.LOGGER.debug("CraftingQueueStorage#putData: {}, quantity: {}", itemId, quantity); +// +// this.queueData.compute(itemId, (k, data) -> { +// if(data == null) { +// return new CraftingQueueData(this, itemId, quantity); +// } +// +// return new CraftingQueueData(this, itemId, data.getQuantity() + quantity); +// }); +// +// this.setDirty(); +// } +// +// public void removeData(ResourceLocation itemId) { +// CraftTracker.LOGGER.debug("CraftingQueueStorage#removeData: {}", itemId); +// +// this.queueData.remove(itemId); +// +// this.setDirty(); +// } +// +// public Collection getAll() { +// return Collections.unmodifiableCollection(this.queueData.values()); +// } +// +// public static CraftingQueueStorage load(CompoundTag nbt) { +// CraftTracker.LOGGER.debug("CraftingQueueStorage#load: {}", nbt); +// +// CraftingQueueStorage store = new CraftingQueueStorage(); +// store.queueData.clear(); +// +// store.ownerId = NBTUtil.getUniqueId(nbt, Keys.OWNER_ID); +// store.craftingQueuePosition = NBTUtil.getVector3d(nbt); +// store.craftingQueueVisible = nbt.getBoolean(Keys.CRAFTING_QUEUE_VISIBLE); +// store.shoppingListPosition = NBTUtil.getVector3d(nbt); +// store.shoppingListVisible = nbt.getBoolean(Keys.SHOPPING_LIST_VISIBLE); +// +// ListTag list = nbt.getList(Keys.QUEUE_DATA, Tag.TAG_COMPOUND); +// +// for (int i = 0; i < list.size(); ++i) { +// CompoundTag queueDataCompound = list.getCompound(i); +// +// CraftingQueueData queueData = new CraftingQueueData(store); +// queueData.read(queueDataCompound); +// +// store.queueData.put(queueData.getItemId(), queueData); +// } +// +// return store; +// } +// +// @Override +// public CompoundTag save(CompoundTag compound) { +// CraftTracker.LOGGER.debug("CraftingQueueStorage#save: {}", compound); +// +// NBTUtil.putUniqueId(compound, Keys.OWNER_ID, this.ownerId); +// NBTUtil.putVector3d(compound, this.craftingQueuePosition); +// compound.putBoolean(Keys.CRAFTING_QUEUE_VISIBLE, this.craftingQueueVisible); +// NBTUtil.putVector3d(compound, this.shoppingListPosition); +// compound.putBoolean(Keys.SHOPPING_LIST_VISIBLE, this.shoppingListVisible); +// +// ListTag list = new ListTag(); +// +// for (Map.Entry entry : this.queueData.entrySet()) { +// CompoundTag queueDataCompound = new CompoundTag(); +// +// CraftingQueueData queueData = entry.getValue(); +// queueData.write(queueDataCompound); +// +// list.add(queueDataCompound); +// } +// +// compound.put(Keys.QUEUE_DATA, list); +// +// return compound; +// } +// +// static class Keys { +// static String OWNER_ID = "owner_id"; +// static String CRAFTING_QUEUE_VISIBLE = "crafting_queue_visible"; +// static String SHOPPING_LIST_VISIBLE = "shopping_list_visible"; +// static String QUEUE_DATA = "queue_data"; +// } +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java index 706eca1..64b795e 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListData.java @@ -1,7 +1,7 @@ -package com.sweetrpg.crafttracker.common.storage; - -import java.util.UUID; - -public class ShoppingListData { - -} +//package com.sweetrpg.crafttracker.common.storage; +// +//import java.util.UUID; +// +//public class ShoppingListData { +// +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java index 3af6b91..af4c908 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/ShoppingListStorage.java @@ -1,4 +1,4 @@ -package com.sweetrpg.crafttracker.common.storage; - -public class ShoppingListStorage { -} +//package com.sweetrpg.crafttracker.common.storage; +// +//public class ShoppingListStorage { +//} diff --git a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java index 7eb162d..2b0b8e1 100644 --- a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java +++ b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java @@ -32,6 +32,9 @@ private void processENUS() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Craft List"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "The queue is empty."); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Products"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Intermediates"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materials"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -44,6 +47,9 @@ private void processENGB() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Craft List"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "The queue is empty."); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Products"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Intermediates"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materials"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -56,6 +62,9 @@ private void processDEDE() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE, "Bastelliste"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY, "Die Warteschlange ist leer."); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Produkte"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Zwischenprodukte"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materialien"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Einkaufsliste"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Handwerks-Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Zur Warteschlange hinzufügen"); From e641259536afaea15d8f084cc6aec446c9e49f86 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Wed, 20 Nov 2024 16:26:40 +0000 Subject: [PATCH 18/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index ceddfb2..e3b86dd 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.15 +0.0.16 From f43c12e8f6b842bcff5b8d6d8fe371b64a77e9da Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Wed, 20 Nov 2024 21:33:56 -0800 Subject: [PATCH 19/24] Populate the other sections --- .../client/overlay/CraftQueueOverlay.java | 130 ++++++++++++++---- .../client/overlay/ShoppingListOverlay.java | 41 +++--- .../common/event/EventHandler.java | 5 +- .../crafttracker/common/lib/Constants.java | 1 + .../common/manager/CraftingQueueManager.java | 87 ++++++++---- .../common/model/CraftingQueueProduct.java | 10 ++ .../crafttracker/common/util/RecipeUtil.java | 10 +- .../crafttracker/data/CTLangProvider.java | 3 + 8 files changed, 209 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java index 2a9d926..a54c7e2 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java @@ -13,13 +13,16 @@ public class CraftQueueOverlay { + static int TITLE_COLOR = 0x99999999; + static int SECTION_COLOR = 0xcccccccc; static int TEXT_COLOR = 0xffffffff; + static int MESSAGE_COLOR = 0x66666666; static int SECTION_X_OFFSET = 4; - static int SECTION_TITLE_Y_OFFSET = 18; + static int SECTION_TITLE_Y_OFFSET = 20; static int ITEM_NAME_X_OFFSET = 22; - static int LINE_HEIGHT = 8; - static int MAX_STRING_LENGTH = 16; - + static int LINE_HEIGHT = 16; + static int TEXT_HEIGHT = 12; + static int MAX_STRING_LENGTH = 40; public static final IIngameOverlay CRAFT_QUEUE = (gui, poseStack, partialTicks, width, height) -> { CraftTracker.LOGGER.trace("CRAFT_QUEUE"); @@ -40,7 +43,7 @@ public class CraftQueueOverlay { GuiComponent.drawCenteredString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_TITLE), - (x + olWidth - 8) / 2, y + 6, TEXT_COLOR); + (x + olWidth - 8) / 2, y + 6, TITLE_COLOR); var mgr = CraftingQueueManager.INSTANCE; var products = mgr.getEndProducts(); @@ -49,48 +52,119 @@ public class CraftQueueOverlay { if(products.isEmpty()) { // TODO GuiComponent.drawCenteredString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_EMPTY), - (x + olWidth - 8) / 2, (y + olHeight - 6) / 2, TEXT_COLOR); + (x + olWidth - 8) / 2, (y + olHeight - 6) / 2, MESSAGE_COLOR); return; } - // end products + int yPos = y + SECTION_TITLE_Y_OFFSET; + CraftTracker.LOGGER.debug("yPos (initial): {}", yPos); + + // SECTION: end products // title GuiComponent.drawString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS), - x + SECTION_X_OFFSET, y + SECTION_TITLE_Y_OFFSET, TEXT_COLOR); - - int yPos = y + (LINE_HEIGHT * 2) + 2; + x + SECTION_X_OFFSET, yPos, SECTION_COLOR); + yPos += TEXT_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (after product title): {}", yPos); // items - for(var p : products) { - var index = products.indexOf(p); + for(int i = 0; i < products.size(); i++) { + var p = products.get(i); +// var index = products.indexOf(p); var item = ForgeRegistries.ITEMS.getValue(p.getItemId()); -// var image = item.getDefaultInstance().getTooltipImage(); - - yPos += (index * LINE_HEIGHT); - var stack = item.getDefaultInstance(); - stack.setCount(p.getQuantity()); + // TODO: count overlay on icon is amount produced by recipe + // stack.setCount(stack.getCount()); var drawable = CTPlugin.jeiRuntime.getJeiHelpers().getGuiHelper() .createDrawableIngredient(VanillaTypes.ITEM_STACK, stack); drawable.draw(poseStack, x + SECTION_X_OFFSET, yPos); -// GuiComponent.blit(poseStack, 2, index * 16, 0, 16, 16, image); - GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 2, TEXT_COLOR); + var text = String.format("%s (x%d)", item.getDescription().getString(MAX_STRING_LENGTH), p.getQuantity()); + GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); + + yPos += LINE_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (product item {}): {}", i, yPos); } - // intermediates - // TODO: title - // TODO: items + // SECTION: intermediates + if(!mgr.getIntermediates().isEmpty()) { + yPos += (TEXT_HEIGHT * 2); + CraftTracker.LOGGER.debug("yPos (before intermediates title): {}", yPos); + + // title + GuiComponent.drawString(poseStack, gui.getFont(), + new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES), + x + SECTION_X_OFFSET, yPos, SECTION_COLOR); + yPos += TEXT_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (after intermediates title): {}", yPos); + + // items + for(int i = 0; i < mgr.getIntermediates().size(); i++) { + var inter = mgr.getIntermediates().get(i); +// var index = mgr.getIntermediates().indexOf(i); + + var item = ForgeRegistries.ITEMS.getValue(inter.getItemId()); + var stack = item.getDefaultInstance(); + stack.setCount(inter.getQuantity()); + var drawable = CTPlugin.jeiRuntime.getJeiHelpers().getGuiHelper() + .createDrawableIngredient(VanillaTypes.ITEM_STACK, stack); + drawable.draw(poseStack, x + SECTION_X_OFFSET, yPos); + GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); + + yPos += LINE_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (intermediates item {}): {}", i, yPos); + } + } - // raw materials - // TODO: title - // TODO: items + // SECTION: raw materials + if(!mgr.getRawMaterials().isEmpty()) { + yPos += (TEXT_HEIGHT * 2); + CraftTracker.LOGGER.debug("yPos (before materials title): {}", yPos); + + // title + GuiComponent.drawString(poseStack, gui.getFont(), + new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS), + x + SECTION_X_OFFSET, yPos, SECTION_COLOR); + yPos += TEXT_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (after materials title): {}", yPos); + + // items + for(int i = 0; i < mgr.getRawMaterials().size(); i++) { + var m = mgr.getRawMaterials().get(i); +// var index = mgr.getRawMaterials().indexOf(m); + + var item = ForgeRegistries.ITEMS.getValue(m.getItemId()); + var stack = item.getDefaultInstance(); + stack.setCount(m.getQuantity()); + var drawable = CTPlugin.jeiRuntime.getJeiHelpers().getGuiHelper() + .createDrawableIngredient(VanillaTypes.ITEM_STACK, stack); + drawable.draw(poseStack, x + SECTION_X_OFFSET, yPos); + GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); + + yPos += LINE_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos (materials item {}): {}", i, yPos); + } + } + + // SECTION: fuel + + if(!mgr.getFuel().isEmpty()) { + yPos += (TEXT_HEIGHT * 2); + CraftTracker.LOGGER.debug("yPos (before fuel title): {}", yPos); - // fuel - // TODO: title - // TODO: items + // title + GuiComponent.drawString(poseStack, gui.getFont(), + new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL), + x + SECTION_X_OFFSET, yPos, SECTION_COLOR); + yPos += TEXT_HEIGHT + 2; + CraftTracker.LOGGER.debug("yPos: {}", yPos); + + // items + for(var f : mgr.getFuel()) { + + } + } }; } diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/ShoppingListOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/ShoppingListOverlay.java index 81ce04b..be188e7 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/ShoppingListOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/ShoppingListOverlay.java @@ -9,27 +9,28 @@ public class ShoppingListOverlay { public static final IIngameOverlay SHOPPING_LIST = (gui, poseStack, partialTicks, width, height) -> { - if(ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_HIDE_EMPTY.get() /* TODO: || user wants it to display */) { -// return; - } - var x = ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_X.get(); - if(x == 0) { - x = ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_X.get(); - } - var y = ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_Y.get(); - if(y == 0) { - y = ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_Y.get() + ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_HEIGHT.get() + 10; - } - var olWidth = Math.min((ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_X.get() + ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_WIDTH.get()), width - 10); - var olHeight = Math.min((ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_Y.get() + ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_HEIGHT.get()), height - 10); - - GuiComponent.fill(poseStack, x, y, olWidth, olHeight, 0x1f1f1f1f); - GuiComponent.fill(poseStack, x + 2, y + 2, olWidth - 2, olHeight - 2, 0x5f5f5f5f); - - GuiComponent.drawCenteredString(poseStack, gui.getFont(), - new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE), - (x + olWidth - 8) / 2, y + 6, 0xffffffff); +// if(ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_HIDE_EMPTY.get() /* TODO: || user wants it to display */) { +//// return; +// } +// +// var x = ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_X.get(); +// if(x == 0) { +// x = ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_X.get(); +// } +// var y = ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_Y.get(); +// if(y == 0) { +// y = ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_Y.get() + ConfigHandler.CLIENT.CRAFT_QUEUE_OVERLAY_HEIGHT.get() + 10; +// } +// var olWidth = Math.min((ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_X.get() + ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_WIDTH.get()), width - 10); +// var olHeight = Math.min((ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_Y.get() + ConfigHandler.CLIENT.SHOPPING_LIST_OVERLAY_HEIGHT.get()), height - 10); +// +// GuiComponent.fill(poseStack, x, y, olWidth, olHeight, 0x1f1f1f1f); +// GuiComponent.fill(poseStack, x + 2, y + 2, olWidth - 2, olHeight - 2, 0x5f5f5f5f); +// +// GuiComponent.drawCenteredString(poseStack, gui.getFont(), +// new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE), +// (x + olWidth - 8) / 2, y + 6, 0xffffffff); }; } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java index 6c53d67..efba3dd 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java @@ -2,6 +2,7 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.lib.Constants; +import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; import net.minecraft.world.entity.Entity; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; @@ -33,7 +34,7 @@ public class EventHandler { // @SubscribeEvent public void onEntitySpawn(final EntityJoinWorldEvent event) { - CraftTracker.LOGGER.debug("EventHandler#onEntitySpawn: {}", event); + CraftTracker.LOGGER.trace("EventHandler#onEntitySpawn: {}", event); Entity entity = event.getEntity(); @@ -46,7 +47,7 @@ public void onEntitySpawn(final EntityJoinWorldEvent event) { public void playerLoggedIn(final PlayerLoggedInEvent event) { CraftTracker.LOGGER.debug("EventHandler#playerLoggedIn: {}", event); -// CraftingQueueManager.get(event.getPlayer().level); + CraftingQueueManager.INSTANCE.load(event.getPlayer()); } // @SubscribeEvent diff --git a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java index 3b4a0b9..f2c96b8 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/lib/Constants.java @@ -30,6 +30,7 @@ public class Constants { public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS = "crafttracker.screen.craft_list.section.products"; public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES = "crafttracker.screen.craft_list.section.intermediates"; public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS = "crafttracker.screen.craft_list.section.materials"; + public static final String TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL = "crafttracker.screen.craft_list.section.fuel"; public static final String TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE = "crafttracker.screen.shopping_list.title"; public static final String TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE = "key.categories.crafttracker"; public static final String TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE = "key.addToQueue"; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index 3b274dc..cf0dc11 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -1,12 +1,13 @@ package com.sweetrpg.crafttracker.common.manager; import com.sweetrpg.crafttracker.CraftTracker; -import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; import com.sweetrpg.crafttracker.common.util.RecipeUtil; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; import java.util.ArrayList; import java.util.HashMap; @@ -57,9 +58,8 @@ public void load(Player player) { } - //// this.storage = new CraftingQueueStorage(); + /// / this.storage = new CraftingQueueStorage(); // } - public List getEndProducts() { return endProducts.entrySet() .stream() @@ -135,36 +135,77 @@ public void removeProduct(Player player, ResourceLocation itemId, int quantity) computeAll(); } + /** + * Compute all the intermediate items, raw materials, and fuel needed to make the recipes + */ public void computeAll() { CraftTracker.LOGGER.debug("CraftingQueueManager#computeAll"); - Map intermediateProducts = new HashMap<>(); - Map rawMaterials = new HashMap<>(); - - var rm = CTPlugin.jeiRuntime.getRecipeManager(); - -// RecipeManager rm = new RecipeManager(ICondition.IContext.EMPTY); + this.intermediateProducts.clear(); + this.rawMaterials.clear(); + this.fuel.clear(); this.endProducts.forEach((k, v) -> { - -// rm.byKey(k).ifPresentOrElse(r -> { -// var ingredients = r.getIngredients(); -// -// }, -// () -> { -// // should not have gotten here, since #addProduct should have filtered out the item -// // since it had to ingredients -// CraftTracker.LOGGER.warn("#computeAll: no recipe found for {}", k); -// }); - + this.computeProduct(v); }); - this.intermediateProducts = intermediateProducts; - this.rawMaterials = rawMaterials; } - public void computeIntermediates() { + public void computeProduct(CraftingQueueProduct product) { + CraftTracker.LOGGER.debug("CraftingQueueManager#computeProduct: {}", product); + + var index = Math.min(product.getIndex(), product.getRecipes().size()); + var recipe = product.getRecipes().get(index); + + this.computeRecipe(recipe, product.getQuantity()); + } + public void computeRecipe(Recipe recipe, int recipeQuantity) { + CraftTracker.LOGGER.debug("CraftingQueueManager#computeRecipe: {}", recipe); + + var ingredients = recipe.getIngredients(); + CraftTracker.LOGGER.debug("ingredients: {}", ingredients); + + ingredients.stream() + .filter((i) -> i instanceof Ingredient) + .map((i) -> Ingredient.class.cast(i)) + .forEach((i) -> { + CraftTracker.LOGGER.debug("i: {}", i); + + if(i instanceof Ingredient ingredient) { + CraftTracker.LOGGER.debug("ingredient: {}", ingredient); + + for(var item : ingredient.getItems()) { + CraftTracker.LOGGER.debug("item: {}", item); + var id = item.getItem().getRegistryName(); + CraftTracker.LOGGER.debug("id: {}", id); + var subRecipes = RecipeUtil.getRecipesFor(id); + CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes); + if(subRecipes.isEmpty()) { + // no recipes for this ingredient, so it's a raw material + this.rawMaterials.compute(id, (itemId, quantity) -> { + if(quantity == null) { + return item.getCount() * recipeQuantity; + } + + return quantity + (item.getCount() * recipeQuantity); + }); + } + else { + // intermediate + this.intermediateProducts.compute(id, (itemId, quantity) -> { + if(quantity == null) { + return item.getCount(); + } + + return quantity + item.getCount(); + }); + + this.computeRecipe(subRecipes.get(0), recipeQuantity); + } + } + } + }); } public static class ProductItem { diff --git a/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java index 8ef9998..6e81edd 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueProduct.java @@ -10,11 +10,13 @@ public class CraftingQueueProduct { ResourceLocation itemId; List recipes; int quantity; + int index; public CraftingQueueProduct(ResourceLocation itemId, List recipes, int quantity) { this.itemId = itemId; this.recipes = recipes; this.quantity = quantity; + this.index = 0; } public ResourceLocation getItemId() { @@ -40,4 +42,12 @@ public int getQuantity() { public void setQuantity(int quantity) { this.quantity = quantity; } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java index 58e6c00..77f2b65 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java @@ -15,16 +15,16 @@ public static List getRecipesFor(ResourceLocation itemId) { var rm = CTPlugin.jeiRuntime.getRecipeManager(); var recipes = rm.createRecipeCategoryLookup().get() - .peek(c -> CraftTracker.LOGGER.debug("category: {}", c)) +// .peek(c -> CraftTracker.LOGGER.debug("category: {}", c)) .map(c -> c.getRecipeType()) - .peek(t -> CraftTracker.LOGGER.debug("type: {}", t)) +// .peek(t -> CraftTracker.LOGGER.debug("type: {}", t)) .flatMap(t -> rm.createRecipeLookup(t).get()) - .peek(r -> CraftTracker.LOGGER.debug("recipe: {}", r)) +// .peek(r -> CraftTracker.LOGGER.debug("recipe: {}", r)) .filter(r -> r instanceof Recipe) .map(r -> Recipe.class.cast(r)) - .peek(r -> CraftTracker.LOGGER.debug("Recipe: {}", r.getId())) +// .peek(r -> CraftTracker.LOGGER.debug("Recipe: {}", r.getId())) .filter(r -> r.getId().equals(itemId)) - .peek(r -> CraftTracker.LOGGER.debug("{}: {}", itemId, r)) +// .peek(r -> CraftTracker.LOGGER.debug("{}: {}", itemId, r)) .collect(Collectors.toUnmodifiableList()); return recipes; diff --git a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java index 2b0b8e1..b1a5986 100644 --- a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java +++ b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java @@ -35,6 +35,7 @@ private void processENUS() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Products"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Intermediates"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materials"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL, "Fuel"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -50,6 +51,7 @@ private void processENGB() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Products"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Intermediates"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materials"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL, "Fuel"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Craft Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Add to Queue"); @@ -65,6 +67,7 @@ private void processDEDE() { add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS, "Produkte"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES, "Zwischenprodukte"); add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS, "Materialien"); + add(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL, "Kraftstoff"); add(Constants.TRANSLATION_KEY_GUI_SHOPPINGLIST_TITLE, "Einkaufsliste"); add(Constants.TRANSLATION_KEY_BINDINGS_CATEGORY_TITLE, "Handwerks-Tracker"); add(Constants.TRANSLATION_KEY_BINDINGS_ADDTOQUEUE_TITLE, "Zur Warteschlange hinzufügen"); From 70c092c8ae2b6b284b9be38bbeeaa5924ede694f Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Thu, 21 Nov 2024 05:35:17 +0000 Subject: [PATCH 20/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index e3b86dd..cd23180 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.16 +0.0.17 From 2f174fad5d75e6aa7b94094df15d56b54f3dd746 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Thu, 21 Nov 2024 20:51:48 -0800 Subject: [PATCH 21/24] Handle looping --- .../common/manager/CraftingQueueManager.java | 87 ++++++++++++------- .../crafttracker/common/util/RecipeUtil.java | 20 ++++- 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index cf0dc11..f0ef438 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -6,13 +6,12 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; +import net.minecraftforge.registries.ForgeRegistries; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class CraftingQueueManager { @@ -166,6 +165,25 @@ public void computeRecipe(Recipe recipe, int recipeQuantity) { var ingredients = recipe.getIngredients(); CraftTracker.LOGGER.debug("ingredients: {}", ingredients); + if(RecipeUtil.areIngredientsSame(ingredients)) { + CraftTracker.LOGGER.debug("ingredients are the same: {}", ingredients); +// if(ingredients.get(0) instanceof Ingredient ingredient) { +// var item = ingredient.getItems()[0]; +// var id = item.getItem().getRegistryName(); + var id = recipe.getId(); + CraftTracker.LOGGER.debug("id: {}", id); + if(this.intermediateProducts.containsKey(id)) { + CraftTracker.LOGGER.debug("intermediates has this ingredient already: {}", id); + var quantity = this.intermediateProducts.remove(id); + var item = ForgeRegistries.ITEMS.getValue(id); + this.intermediateProducts.remove(id); + this.updateRawMaterials(id, new ItemStack(item, quantity), recipeQuantity); + + return; + } +// } + } + ingredients.stream() .filter((i) -> i instanceof Ingredient) .map((i) -> Ingredient.class.cast(i)) @@ -175,39 +193,48 @@ public void computeRecipe(Recipe recipe, int recipeQuantity) { if(i instanceof Ingredient ingredient) { CraftTracker.LOGGER.debug("ingredient: {}", ingredient); - for(var item : ingredient.getItems()) { - CraftTracker.LOGGER.debug("item: {}", item); - var id = item.getItem().getRegistryName(); - CraftTracker.LOGGER.debug("id: {}", id); - var subRecipes = RecipeUtil.getRecipesFor(id); - CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes); - if(subRecipes.isEmpty()) { - // no recipes for this ingredient, so it's a raw material - this.rawMaterials.compute(id, (itemId, quantity) -> { - if(quantity == null) { - return item.getCount() * recipeQuantity; + Arrays.stream(ingredient.getItems()) + .findFirst() + .ifPresent((item) -> { + CraftTracker.LOGGER.debug("item: {}", item); + var id = item.getItem().getRegistryName(); + CraftTracker.LOGGER.debug("id: {}", id); + var subRecipes = RecipeUtil.getRecipesFor(id); + CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes); + + if(subRecipes.isEmpty()) { + CraftTracker.LOGGER.debug("subRecipes is empty; raw material"); + // no recipes for this ingredient, so it's a raw material + this.updateRawMaterials(id, item, recipeQuantity); } - - return quantity + (item.getCount() * recipeQuantity); - }); - } - else { - // intermediate - this.intermediateProducts.compute(id, (itemId, quantity) -> { - if(quantity == null) { - return item.getCount(); + else { + CraftTracker.LOGGER.debug("subRecipes has {} items; intermediate", subRecipes.size()); + // intermediate + this.intermediateProducts.compute(id, (itemId, quantity) -> { + if(quantity == null) { + return item.getCount(); + } + + return quantity + item.getCount(); + }); + + this.computeRecipe(subRecipes.get(0), recipeQuantity); } - - return quantity + item.getCount(); }); - - this.computeRecipe(subRecipes.get(0), recipeQuantity); - } - } } }); } + private void updateRawMaterials(ResourceLocation id, ItemStack item, int recipeQuantity) { + this.rawMaterials.compute(id, (itemId, quantity) -> { + if(quantity == null) { + return item.getCount() * recipeQuantity; + } + + return quantity + (item.getCount() * recipeQuantity); + }); + } + public static class ProductItem { private ResourceLocation itemId; private int quantity; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java index 77f2b65..d5a80ba 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java @@ -2,16 +2,21 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.addon.jei.CTPlugin; +import net.minecraft.core.NonNullList; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; +import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; public class RecipeUtil { public static List getRecipesFor(ResourceLocation itemId) { + CraftTracker.LOGGER.debug("RecipeUtil#getRecipesFor: {}", itemId); + var rm = CTPlugin.jeiRuntime.getRecipeManager(); var recipes = rm.createRecipeCategoryLookup().get() @@ -29,4 +34,17 @@ public static List getRecipesFor(ResourceLocation itemId) { return recipes; } + + public static boolean areIngredientsSame(NonNullList ingredients) { + CraftTracker.LOGGER.debug("RecipeUtil#areIngredientsSame: {}", ingredients); + + Set ing = ingredients.stream() + .map((i) -> Arrays.asList(i.getItems())) + .filter((l) -> !l.isEmpty()) + .map((l) -> l.get(0)) + .map((i) -> i.getItem().getRegistryName().toString()) + .collect(Collectors.toSet()); + + return ing.size() == 1; + } } From 08cd20ef626e8c6ec68f0bad67e30de4e7603fc0 Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Fri, 22 Nov 2024 04:53:27 +0000 Subject: [PATCH 22/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index cd23180..32786aa 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.17 +0.0.18 From f5b8019e3241f40814a93637a51c60cf1f4bd342 Mon Sep 17 00:00:00 2001 From: Paul Schifferer Date: Fri, 22 Nov 2024 17:27:07 -0800 Subject: [PATCH 23/24] Save and load the queue --- .../client/overlay/CraftQueueOverlay.java | 22 +- .../crafttracker/common/addon/Addon.java | 4 +- .../common/addon/jei/CTPlugin.java | 12 +- .../common/event/EventHandler.java | 2 +- .../common/manager/CraftingQueueManager.java | 66 ++++- .../common/storage/CraftingQueueStorage.java | 241 +++++++++--------- 6 files changed, 204 insertions(+), 143 deletions(-) diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java index a54c7e2..36605fe 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java @@ -57,7 +57,7 @@ public class CraftQueueOverlay { } int yPos = y + SECTION_TITLE_Y_OFFSET; - CraftTracker.LOGGER.debug("yPos (initial): {}", yPos); + CraftTracker.LOGGER.trace("yPos (initial): {}", yPos); // SECTION: end products @@ -66,7 +66,7 @@ public class CraftQueueOverlay { new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_PRODUCTS), x + SECTION_X_OFFSET, yPos, SECTION_COLOR); yPos += TEXT_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (after product title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (after product title): {}", yPos); // items for(int i = 0; i < products.size(); i++) { @@ -84,20 +84,20 @@ public class CraftQueueOverlay { GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); yPos += LINE_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (product item {}): {}", i, yPos); + CraftTracker.LOGGER.trace("yPos (product item {}): {}", i, yPos); } // SECTION: intermediates if(!mgr.getIntermediates().isEmpty()) { yPos += (TEXT_HEIGHT * 2); - CraftTracker.LOGGER.debug("yPos (before intermediates title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (before intermediates title): {}", yPos); // title GuiComponent.drawString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_INTERMEDIATES), x + SECTION_X_OFFSET, yPos, SECTION_COLOR); yPos += TEXT_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (after intermediates title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (after intermediates title): {}", yPos); // items for(int i = 0; i < mgr.getIntermediates().size(); i++) { @@ -113,21 +113,21 @@ public class CraftQueueOverlay { GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); yPos += LINE_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (intermediates item {}): {}", i, yPos); + CraftTracker.LOGGER.trace("yPos (intermediates item {}): {}", i, yPos); } } // SECTION: raw materials if(!mgr.getRawMaterials().isEmpty()) { yPos += (TEXT_HEIGHT * 2); - CraftTracker.LOGGER.debug("yPos (before materials title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (before materials title): {}", yPos); // title GuiComponent.drawString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_MATERIALS), x + SECTION_X_OFFSET, yPos, SECTION_COLOR); yPos += TEXT_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (after materials title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (after materials title): {}", yPos); // items for(int i = 0; i < mgr.getRawMaterials().size(); i++) { @@ -143,7 +143,7 @@ public class CraftQueueOverlay { GuiComponent.drawString(poseStack, gui.getFont(), item.getDescription().getString(MAX_STRING_LENGTH), x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); yPos += LINE_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos (materials item {}): {}", i, yPos); + CraftTracker.LOGGER.trace("yPos (materials item {}): {}", i, yPos); } } @@ -151,14 +151,14 @@ public class CraftQueueOverlay { if(!mgr.getFuel().isEmpty()) { yPos += (TEXT_HEIGHT * 2); - CraftTracker.LOGGER.debug("yPos (before fuel title): {}", yPos); + CraftTracker.LOGGER.trace("yPos (before fuel title): {}", yPos); // title GuiComponent.drawString(poseStack, gui.getFont(), new TranslatableComponent(Constants.TRANSLATION_KEY_GUI_CRAFTLIST_SECTION_FUEL), x + SECTION_X_OFFSET, yPos, SECTION_COLOR); yPos += TEXT_HEIGHT + 2; - CraftTracker.LOGGER.debug("yPos: {}", yPos); + CraftTracker.LOGGER.trace("yPos: {}", yPos); // items for(var f : mgr.getFuel()) { diff --git a/src/main/java/com/sweetrpg/crafttracker/common/addon/Addon.java b/src/main/java/com/sweetrpg/crafttracker/common/addon/Addon.java index b3b4fd9..4488e69 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/addon/Addon.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/addon/Addon.java @@ -9,7 +9,7 @@ public interface Addon { /** - * Called from the DT2 mod class constructor + * Called from the CT mod class constructor * * @throws RuntimeException You should re-throw any exceptions as {@link RuntimeException}, this are */ @@ -18,7 +18,7 @@ default void init() throws RuntimeException { } /** - * Called from the DT2 {@link InterModProcessEvent} + * Called from the CT {@link InterModProcessEvent} * * @throws RuntimeException You should re-throw any exceptions as {@link RuntimeException}, this are */ diff --git a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java index c2f4ed8..3e80fcf 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/addon/jei/CTPlugin.java @@ -1,6 +1,7 @@ package com.sweetrpg.crafttracker.common.addon.jei; import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.manager.CraftingQueueManager; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; @@ -9,7 +10,11 @@ import mezz.jei.api.registration.IRecipeRegistration; import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.runtime.IJeiRuntime; +import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; @JeiPlugin public class CTPlugin implements IModPlugin { @@ -53,7 +58,7 @@ public void registerGuiHandlers(IGuiHandlerRegistration registration) { public void registerAdvanced(IAdvancedRegistration registration) { CraftTracker.LOGGER.debug("CTPlugin#registerAdvanced: {}", registration); - registration.getJeiHelpers().getGuiHelper().createCraftingGridHelper(0); +// registration.getJeiHelpers().getGuiHelper().createCraftingGridHelper(0); } @Override @@ -61,5 +66,10 @@ public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { CraftTracker.LOGGER.debug("CTPlugin#onRuntimeAvailable: {}", jeiRuntime); CTPlugin.jeiRuntime = jeiRuntime; + + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { + Player player = Minecraft.getInstance().player; + CraftingQueueManager.INSTANCE.load(player); + }); } } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java index efba3dd..48a2c43 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/event/EventHandler.java @@ -47,7 +47,7 @@ public void onEntitySpawn(final EntityJoinWorldEvent event) { public void playerLoggedIn(final PlayerLoggedInEvent event) { CraftTracker.LOGGER.debug("EventHandler#playerLoggedIn: {}", event); - CraftingQueueManager.INSTANCE.load(event.getPlayer()); +// CraftingQueueManager.INSTANCE.load(event.getPlayer()); } // @SubscribeEvent diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index f0ef438..6cf1b1f 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -2,15 +2,26 @@ import com.sweetrpg.crafttracker.CraftTracker; import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; +import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; import com.sweetrpg.crafttracker.common.util.RecipeUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; +import net.minecraftforge.fml.loading.FMLPaths; import net.minecraftforge.registries.ForgeRegistries; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.*; import java.util.stream.Collectors; @@ -18,6 +29,8 @@ public class CraftingQueueManager { public static CraftingQueueManager INSTANCE = new CraftingQueueManager(); + public static final Path STORAGE_DIR = FMLPaths.GAMEDIR.get().resolve("craft_tracker"); + private Map endProducts = new HashMap<>(); private Map intermediateProducts = new HashMap<>(); private Map rawMaterials = new HashMap<>(); @@ -54,7 +67,55 @@ public CraftingQueueManager() { } public void load(Player player) { + CraftTracker.LOGGER.info("Loading crafting queue for {}", player); + + Path file = STORAGE_DIR.resolve("queue.nbt").toAbsolutePath(); + CraftTracker.LOGGER.debug("file: {}", file); + + try { +// Files.createDirectories(file); + try (InputStream in = Files.newInputStream(file, StandardOpenOption.READ)) { + var data = NbtIo.readCompressed(in); + var products = CraftingQueueStorage.load(data); + products.forEach((k, v) -> v.setRecipes(RecipeUtil.getRecipesFor(k))); + this.endProducts = products; + this.computeAll(); + } + } + catch (IOException e) { + CraftTracker.LOGGER.error("An error occurred while loading crafting queue [" + file + "]", e); + } + } + public void save(Player player) { + CraftTracker.LOGGER.info("Saving crafting queue for {}", player); + + Path file = STORAGE_DIR.resolve("queue.nbt").toAbsolutePath(); + CraftTracker.LOGGER.debug("file: {}", file); + + try { + Files.createDirectories(file); + } + catch(FileAlreadyExistsException e) { + // ignore + } + catch (IOException e) { + CraftTracker.LOGGER.error("An error occurred while creating directory for crafting queue [" + file + "]", e); + } + + try { + boolean overwritten = Files.deleteIfExists(file); + try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.CREATE)) { + var root = new CompoundTag(); + var storage = new CraftingQueueStorage(); + storage.putData(this.endProducts); + var data = storage.save(root); + NbtIo.writeCompressed(data, out); + } + } + catch (IOException e) { + CraftTracker.LOGGER.error("An error occurred while saving crafting queue [" + file + "]", e); + } } /// / this.storage = new CraftingQueueStorage(); @@ -109,6 +170,8 @@ public void addProduct(Player player, ResourceLocation itemId, int quantity) { else { CraftTracker.LOGGER.info("Not adding {} to queue, since there are no recipes for it.", itemId); } + + this.save(player); } public void removeProduct(Player player, ResourceLocation itemId, int quantity) { @@ -132,6 +195,8 @@ public void removeProduct(Player player, ResourceLocation itemId, int quantity) } computeAll(); + + this.save(player); } /** @@ -147,7 +212,6 @@ public void computeAll() { this.endProducts.forEach((k, v) -> { this.computeProduct(v); }); - } public void computeProduct(CraftingQueueProduct product) { diff --git a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java index 9c684cf..e4993b2 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/storage/CraftingQueueStorage.java @@ -1,44 +1,42 @@ -//package com.sweetrpg.crafttracker.common.storage; -// -//import com.google.common.collect.Maps; -//import com.sweetrpg.crafttracker.CraftTracker; -//import com.sweetrpg.crafttracker.common.lib.Constants; -//import com.sweetrpg.crafttracker.common.util.NBTUtil; -//import net.minecraft.nbt.CompoundTag; -//import net.minecraft.nbt.ListTag; -//import net.minecraft.nbt.Tag; -//import net.minecraft.resources.ResourceLocation; -//import net.minecraft.server.level.ServerLevel; -//import net.minecraft.world.level.Level; -//import net.minecraft.world.level.saveddata.SavedData; -//import net.minecraft.world.level.storage.DimensionDataStorage; -//import net.minecraft.world.phys.Vec3; -// -//import javax.annotation.Nullable; -//import java.util.Collection; -//import java.util.Collections; -//import java.util.Map; -//import java.util.UUID; -// -///** -// * This class handles storage of all crafting queue information for a player. -// * -// * This includes: -// * * Position and visibility of the craft list window -// * * Position and visibility of the shopping list window -// * * The list of end products in the queue by item ID and quantity -// */ -//public class CraftingQueueStorage extends SavedData { -// -// private @Nullable UUID ownerId; -// private Vec3 craftingQueuePosition; -// private boolean craftingQueueVisible; -// private Vec3 shoppingListPosition; -// private boolean shoppingListVisible; -// private Map queueData = Maps.newConcurrentMap(); -// -// public CraftingQueueStorage() {} -// +package com.sweetrpg.crafttracker.common.storage; + +import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; +import com.sweetrpg.crafttracker.common.util.NBTUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * This class handles storage of all crafting queue information for a player. + *

+ * This includes: + * * Position and visibility of the craft list window + * * Position and visibility of the shopping list window + * * The list of end products in the queue by item ID and quantity + */ +public class CraftingQueueStorage extends SavedData { + + private @Nullable UUID ownerId; + private Vec3 craftingQueuePosition; + private boolean craftingQueueVisible; + private Vec3 shoppingListPosition; + private boolean shoppingListVisible; + // private Map queueData = Maps.newConcurrentMap(); + private Map products = new HashMap<>(); + + public CraftingQueueStorage() { + } + // public static CraftingQueueStorage get(Level world) { // CraftTracker.LOGGER.debug("CraftingQueueStorage#get: {}", world); // @@ -51,89 +49,78 @@ // DimensionDataStorage storage = overworld.getDataStorage(); // return storage.computeIfAbsent(CraftingQueueStorage::load, CraftingQueueStorage::new, Constants.STORAGE_CRAFTING_QUEUE); // } -// -// public void putData(ResourceLocation itemId, int quantity) { -// CraftTracker.LOGGER.debug("CraftingQueueStorage#putData: {}, quantity: {}", itemId, quantity); -// -// this.queueData.compute(itemId, (k, data) -> { -// if(data == null) { -// return new CraftingQueueData(this, itemId, quantity); -// } -// -// return new CraftingQueueData(this, itemId, data.getQuantity() + quantity); -// }); -// -// this.setDirty(); -// } -// -// public void removeData(ResourceLocation itemId) { -// CraftTracker.LOGGER.debug("CraftingQueueStorage#removeData: {}", itemId); -// -// this.queueData.remove(itemId); -// -// this.setDirty(); -// } -// -// public Collection getAll() { -// return Collections.unmodifiableCollection(this.queueData.values()); -// } -// -// public static CraftingQueueStorage load(CompoundTag nbt) { -// CraftTracker.LOGGER.debug("CraftingQueueStorage#load: {}", nbt); -// -// CraftingQueueStorage store = new CraftingQueueStorage(); -// store.queueData.clear(); -// -// store.ownerId = NBTUtil.getUniqueId(nbt, Keys.OWNER_ID); -// store.craftingQueuePosition = NBTUtil.getVector3d(nbt); -// store.craftingQueueVisible = nbt.getBoolean(Keys.CRAFTING_QUEUE_VISIBLE); -// store.shoppingListPosition = NBTUtil.getVector3d(nbt); -// store.shoppingListVisible = nbt.getBoolean(Keys.SHOPPING_LIST_VISIBLE); -// -// ListTag list = nbt.getList(Keys.QUEUE_DATA, Tag.TAG_COMPOUND); -// -// for (int i = 0; i < list.size(); ++i) { -// CompoundTag queueDataCompound = list.getCompound(i); -// -// CraftingQueueData queueData = new CraftingQueueData(store); -// queueData.read(queueDataCompound); -// -// store.queueData.put(queueData.getItemId(), queueData); -// } -// -// return store; -// } -// -// @Override -// public CompoundTag save(CompoundTag compound) { -// CraftTracker.LOGGER.debug("CraftingQueueStorage#save: {}", compound); -// -// NBTUtil.putUniqueId(compound, Keys.OWNER_ID, this.ownerId); -// NBTUtil.putVector3d(compound, this.craftingQueuePosition); -// compound.putBoolean(Keys.CRAFTING_QUEUE_VISIBLE, this.craftingQueueVisible); -// NBTUtil.putVector3d(compound, this.shoppingListPosition); -// compound.putBoolean(Keys.SHOPPING_LIST_VISIBLE, this.shoppingListVisible); -// -// ListTag list = new ListTag(); -// -// for (Map.Entry entry : this.queueData.entrySet()) { -// CompoundTag queueDataCompound = new CompoundTag(); -// -// CraftingQueueData queueData = entry.getValue(); -// queueData.write(queueDataCompound); -// -// list.add(queueDataCompound); -// } -// -// compound.put(Keys.QUEUE_DATA, list); -// -// return compound; -// } -// -// static class Keys { -// static String OWNER_ID = "owner_id"; -// static String CRAFTING_QUEUE_VISIBLE = "crafting_queue_visible"; -// static String SHOPPING_LIST_VISIBLE = "shopping_list_visible"; -// static String QUEUE_DATA = "queue_data"; -// } -//} + + public void putData(Map products) { + CraftTracker.LOGGER.debug("CraftingQueueStorage#putData: {}", products); + + this.products = products; + + this.setDirty(); + } + + public static Map load(CompoundTag nbt) { + CraftTracker.LOGGER.debug("CraftingQueueStorage#load: {}", nbt); + + CraftingQueueStorage store = new CraftingQueueStorage(); + store.products.clear(); + + store.ownerId = NBTUtil.getUniqueId(nbt, Keys.OWNER_ID); + store.craftingQueuePosition = NBTUtil.getVector3d(nbt); + store.craftingQueueVisible = nbt.getBoolean(Keys.CRAFTING_QUEUE_VISIBLE); + store.shoppingListPosition = NBTUtil.getVector3d(nbt); + store.shoppingListVisible = nbt.getBoolean(Keys.SHOPPING_LIST_VISIBLE); + + ListTag list = nbt.getList(Keys.QUEUE_DATA, Tag.TAG_COMPOUND); + + for(int i = 0; i < list.size(); ++i) { + CompoundTag productData = list.getCompound(i); + + var itemId = NBTUtil.getResourceLocation(productData, Keys.ITEM_ID); + var quantity = productData.getInt(Keys.QUANTITY); + var index = productData.getInt(Keys.INDEX); + + var product = new CraftingQueueProduct(itemId, Arrays.asList(), quantity); + product.setIndex(index); + store.products.put(itemId, product); + } + + return store.products; + } + + @Override + public CompoundTag save(CompoundTag compound) { + CraftTracker.LOGGER.debug("CraftingQueueStorage#save: {}", compound); + + NBTUtil.putUniqueId(compound, Keys.OWNER_ID, this.ownerId); + NBTUtil.putVector3d(compound, this.craftingQueuePosition); + compound.putBoolean(Keys.CRAFTING_QUEUE_VISIBLE, this.craftingQueueVisible); + NBTUtil.putVector3d(compound, this.shoppingListPosition); + compound.putBoolean(Keys.SHOPPING_LIST_VISIBLE, this.shoppingListVisible); + + ListTag list = new ListTag(); + + this.products.forEach((k, v) -> { + CompoundTag productData = new CompoundTag(); + + NBTUtil.putResourceLocation(productData, Keys.ITEM_ID, v.getItemId()); + productData.putInt(Keys.QUANTITY, v.getQuantity()); + productData.putInt(Keys.INDEX, v.getIndex()); + + list.add(productData); + }); + + compound.put(Keys.QUEUE_DATA, list); + + return compound; + } + + static class Keys { + static String OWNER_ID = "owner_id"; + static String CRAFTING_QUEUE_VISIBLE = "crafting_queue_visible"; + static String SHOPPING_LIST_VISIBLE = "shopping_list_visible"; + static String QUEUE_DATA = "queue_data"; + static String ITEM_ID = "item_id"; + static String QUANTITY = "quantity"; + static String INDEX = "index"; + } +} From 13dc749fc0b5ee919b01beea289029da7961fddf Mon Sep 17 00:00:00 2001 From: SweetRPG CI Date: Sat, 23 Nov 2024 01:28:49 +0000 Subject: [PATCH 24/24] Update 1.18 VERSION file --- .release-info/1.18/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 32786aa..44517d5 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.0.18 +0.0.19