Iix7oI;jjj{MBZro_#=Rbdlh5ddMV{znHGAM6n z$-@p@9xu&N6hWG+p|LO)4vpaO=_wq4ejdqm1liptIxW%Cgg=}_YCMgnP94N6Z=b`7 zmrmm0Mgf2Gqbn$vI;i>l1So*wo$K&ddI$$%oF`2#=@h;C5X}-~tD@abIM@5Twqx7B zRi5PI<9z|*u$bHLeDi-$uVhSzn=XPuH)dzYtYM{E#M;IRa`_ww&}hFgjC$dzq2v&z zCT6JqaXYoVvW;fFXKHVts9FE^0k$Gy(5J@jkh%A_F5&sV_f0GuSVS}wLZjkiUp3sh zU%|>s1CzsfocsJ7K3T3{>75Tz+FGG>2QV@{hKczZOwOb*HI>Hra1ymf4{NJ=l*$FH zKG;O3+d_ywDLov=#6%Lwp%`dX@hDTpFTei|9)7q?Q|yPI3O93X5w+WYgZ<%I#KLLL zgL5$29pViP+q0?JoJ`7DbVb(&Om^n!6ld>=eS7SHB60>KvFM!s zIHtx}-MWqDNDTXrzlCNyhRtmcs+A5rKAJy|8v(4~^)Jm)aKbcQyEH!mB&gZxR04s3 zmzv(gW~PSw1o7^jUD8#<7ruG~$*>P?(%q4X e}nGUp&I_n0>Cl_z$^MKNDy*#PJRW=D2SY#tQniruac=TQHg9s8tL z{KIekGixLDG#0VbE}0~=pecknp;# o>)76{V`ry
3PMbWDP^msayLQD$38@6o5j^LD z0cCt}iiB_PUhB$ft22+ako$l1t^aY*i=ELWfubAQWRw*#k{f|!x3U9WAB2k*=5i~I zu_5{c*i9<^L-lX|LnHT#F6p>S?QFN3c(id9IgT5fdW|NC<|H}+A2maoqJW{C`IuW? zPtK9?Ni^x`8x5IWhxW%qvp F|H>x=_%5TD0SMpHZb)$$CUQzWHp&UufW_jv=nhpIjx&IVMK)@eelI_{Hbdpvdo zK&C}nmt$WAoO?QI((0r6spgi^t`^9=HoB~V_ ?p6vDxnSq0w4 zDqheiOE47G^%*_!u$#(KTnI;3bI`Nd|fXhP!g zQkc9RMqo8`nq{8fMYFO&zZWLMDQsBBvj?(x?&(R4($wW>)+*&DqQL}#r1|R4MxO&8 z>6F1)4g6SBl;;f?t<7Nij?VNiJW=d=zmY)loFOP`HmD8=l4?anZSJ=b3i&WR8fU1S zzzDag;TRH$2>p$h4!TVf(KJmk_;|mO`NY_LSnZm$qS>rsH@}L_t*daCu3^!)h2zm0 zMuIJbr&0*|A{Ytx5O9~UTgV|>s5u(N2vNOx14HPNx!qQdjON&M@H%hOX#oa-te4Oz zZ_!@_Y36)3Zo5H1s=H`ZGo-h{TPS4IkU#$V0{;Ft-oSJ!gs1k6V*gA6kCrbnMBhN6 zoM+I%0HALjoO$) fhrg{O9n)x$3mE;&irGbYub4Y33=;9Zmzn!3lI| zMu0^ogegWg0>Ez(kaJy=k-+%;UJNB-jDF&b)Z)xl%4T~-k9sq6#RCJH8Yb{|@+$=6 zBgC8ca4J^Ck(dhsPZxOx4J*|KS|lo#97cnT_WJ_Z%vPyMb+lU*V{ErsHz45h6AYRi zGOWvRx2M2FV-Yw74UG!J^4bn*4j2#H6oh6qYv+1wFAAx%n24?6JHLGvM~ ^PvNrK`wO<7ufh>fka{%sC}`+NMyZrI7h z_$211_ZgvWf^zTPIwggKD!x}s-czMxUCUfWthItui5`ONztrxa*z91Z?Z;+2fwG%9 z0d2X~xSE|H8tnkXT#tbvLD6^~Z_r^h$M2E(T>{OMq(cd03|?PY12U7(TXlJYJQpL7 zbneXxr|D>$q)OU1 @=N9 JLudgZaw-iPk>;Tvv!Z9 zzC)nK5@o#blpik~AHn;-{3Y&OUV@KfEW{%eiZ(XNRkX;wq@RHg&$p=c5~RE =or84Q{3{<**OuaW7_xRx(|wu&GZiL|6_*4O59>mAEu^9kS(p?;nFf1 z%sYB+Mmr j5+}!<;uIi~eU}ZW@Wm6joO< zxW9A KE9K%AiX zdA`mwl-`j^n@A~3zd>pB(9}iZDVn(^iaQ$!MB)g>7+F!n8r32LhoWYV*yt>(g-sN< zmrT2q82T9cGK)dleH*RJHG~^mi24H<4+pT1z%&WWqe2<$1V{27Wxl2=6D!TB`lc~G zaumtvs9B2vFpqgJ4N}z^2QHPt-X0*VDxMQheeQfP=)p7r*)2YzTd$*BrKE-PIPlsk z7G6qV|7RvJvOkVkI)sQ4$#@Wvkr0L#W0*RYL~6Q?=*T+C%)s4lwu$;tqoZ}Xo5-zy zf@#+d%>YoR3E1eyaIG_sif 1v zg<_&*lp4V(tWGCP^A%1E5eR}x$JeaYcn?9Qws=`XQJOU-p#n LDPS6YhGsn)VJWQZSYv|}-J3l`^Mkn0Bdgc*+>kF?jaq}Xua2F#7 z<`5d9DG9n!&6ZHvEup+yq@-1-NhF%Y`AMvo<|dpNX4?{u9C#k5j-JGOzj_zJRt6`> zLJR=1l&TI^x@p|)9z@SSNv0(b2&K4 si}}id2#_yiO!QDHMYKP{+<~F5 zF`4HTROmh(2O?2U(P2P~OjREWXZ74fV0tYqW)!C6kF?0tDY}=|Syab-#!mrIWY=^h zP-^rTbusX0X2JXu!{H#NVj+acz)G{l>rJ$HzQ(>%q;1l}AckS~W~G8C=cO>*0Qogw zR0d$N+MEdp>!8TLcs~F7@0|CO@T)g3Au`s)XI?prr;a>@JR_A}sDy64f{mN^P+Z$a zZL5afb^`6J8|CaK0oq}PonxXE=A`{d`wrsGb6 vqVl!#%-^-|RZ>hs1RSByjr|QEWj!K3(rn92$Ck-Bk *;_-H1wj9mrlLdD? z8pig{IvRCm&+R<2l}*e~?88^j{Sgv{Ls)&EKyZuzB`VzY;M-sN$N1W-{|KRu76>V2 zyrwp4t9uqE7YV3VWwR)EUDzEt2-o5ZC LKbFZ<%`as96608*J0c)bn)@#HgeRGxuFD}9UsB5bi%-C(qd$44Qy2Fn5I=3 zYTn0#tqZuj{weA$C&;jINxy-ySB(PlO3OEj!ZatEug|^omGd|6UbQEP?x8G3$I=Lg zBA6JN!KuS9;n2PlXp|UjY~91u@UwXNxo==7InL x-jQ8-%ouxl)w|1i~@ed4;)p097+$ZLO=X+CKUd zKdartiHXS_YJ=u#leIsG@mkX9snR5Q8Ol1#B2GJ1yGG|PP+N@Bcmg2?2-3D@Lk#R0 zK+atZ$GL4Hh(gPc5(T$Z+eMgQPci^VvVX=d*r6$}QJ55!)oIQ~0}PWpH56z)N{sZh z{O_aI%2a9&eWGmy6+6Wuoif2YbK=GG+dCO}Q#l;}ViGk*RqbX2Mf!n6G=;I@87!~f z##SbSH(vhxn3+5TKN;Dhrrck;j(yY5;^fiKpuwbWZR0*J-}?}ccJ5;I=^#pl0NQ1S z!1*TP1TvGYkl9X1%P@;T(D@P&X^NQLZZR=r-XS35ycNmFEP4r;t;cC1thScM$*T;s z#F--Zc+IvvZ{V{pV?=-A0>h+|_qQ5MNC{pbOq0cNWHQ?X#zVRkaHv;RJy!1UVx=3V zKWQUZ%D_#jmXApX{qVE@JWXzef+9cF Bxq(v(v!G#?_>Pzy?=*ehd+bQz4*I!v+Csd zA~ts(;`-fp3EU#4CKes_BNNtIpCB^qLt(3qV#b5)P8mb#DModA9wR$S2TUNu3?D;T znHUk+BCwKpIdjLK5EMCjEiYIb6|I>mk#tyQQ__CF8Ytw9Cae4Rz+^L3( hlTgr*J&p<=%tSwUpf!ATJ^uj21yvWz|)U$!%NC4@89}x;siNc&`03cwr z@)KoB;dgUhG|kmwfg!UZGlzc;Jx0A94-!n AT!`&-)F_m7h zrTce3{2sMqKfdzzpOR=NM^Vv)7iUj0r+I*tbw+9gaC>JJKfCz tBw)y}bs zdCK&4ziElgm-g96P3<`m+bEyuP&0MbM}H+Du #R~V%( z1A%Iu{nRiL3SfvD6Qd>+2~2^0r^Aj2G5v5I2qsZvJ9pc|$Sg^E N)w^M{z1kN|!d!#WH<#a%rr zaEPXs3;^koA>9TUM&PuPu%|YLhEvTfk$H5y<};K%o8&!~NC9V{unLd?qum6+B0+1T zNQ@fW7eMPz{gS>O&6wVpVFNPVk8+(RkV0Ik 4PuaX`b$GOvAMWbD!L);)^7_Bja;TY5wqE4sitT!pA&=_QPKu(>-7)Dv;bybV3 zc%z)NtpSn9xa$sHoV$XroO+124&TM$#1**8R}4hm>txAJwSEK$tEAmDW}rc2rmbXK z6?j_q?`xdEvU{DpM|8@!IRU{F=gA Du!^3qg-2W0sPHnsl1I%|{*zl9R}3-D5jJci31HE&%v87S$ANvP z&3CBOIvFN_%w5QERqgAX%KrJMF&aD035cocZ6KLIh^pj+By_zF4Wq>3| z4)El*RcS$o{=u|EAc?uu5SKBU4w++$K%-PNsGYP(=g6oYwZW+ko_EX~4~)L_ Lnu!`g$ls&?CGaL%mMRg}Ip%)==eI{-5J+`OlffR;+4Y=TsC`%#I!5 zz>K04Df!&iSEu1dBobk?M(f&bp~ZNfy2|k+p{fouOcF`QKtNpG5{{;C;Lm<{AI_dQ z2%nfD^Fc<2DS>akbQ0fs#f!A-JyZ%ybj~tEq}`-i05~She1;sqt_e6@XO)&|D@}9G zE4 dQFz+-XFI;uu>Eu8OFK;ROVJ6IM z)l2CnRisYx>y&1<$T+-AnkAKt@wu;agQBi=i#JToP{!M$HWhu$_9I9Qr^v4cYIXVz zF@^-nd5T$LjyU1iR1VqM#k2GG@JHV`fXSgCid!qFmbRVnm5dfe8tcWA$MK)NHimJ} zhsb4aqh9H2G3~w+Ez#MEp_F!Vz{V4(0?NSR^?^X>38)Ru9;CU_GA;8rYX!9GMfOud zEZ)Pj=N9ngZ@!L0CysKW9<&)^g{cwgIYzBRNzA|WMR*QAgX_71v2K6Nk2z|MkN#6u zLh>=xt)f<2!*+g^V@d*4o#91+0P?)}+*{{MS{CqDFmlL)P$I;MbWqQg(5gjn{O~z4 z%!5?mX(apyXgBY` WhRJiQ@9eo|5!HA$=h6zS41Fu@(c zWU7Ou 68K7tTpz>;nEFg3EzoV@Wue|=;;UwGqN=QlHS>dYUy{v0M3h7q6_%jdEv zZRIgFde9;;F<;EstbNF>3p3%@5%o^lWG`L)1wOd(7w8VPF>zp?7SNA-pHRB n%E1 zt!F7@mZ|Asd^nBiw41=(#nz4hY!Y-mc#;-6j^67J3q~_*y6!|$M3Cn+hA$9jvo7l8 zZ8TWhO|5WOF~ex|jo&(rFMaj1n3 r2+-h zh3C&4!_7O_kff$2X}T3JRwKhG6>~ liBK?#fHy%~`G!@WD{7B-msW1#yFdMBD7ZGUc;+xC z7{KmE4iB#C&5#9xI)YQroW|8_cd@jTA%mivFqtL-1P-;P*Dm19>=M5D`j{ ^oubqcn6>-Z%ITR)!)&!_2Wj*MoE2o5 zGOZezmhhU2Rv7f0rdHgd3EbuR8b-&%c `XUVt`YPjiXh_@R|z|CYC$(It6gX zdAnbK0*C#2UVr&(=cCaGGCN!7(orj_N=yV0jQEil9l~yT9iM*sE97=}>2n%ra#Fkb z4DLU=jGw;uZ}3+i{hz={6LU{5F!u;Bb8TSha+&69k)|Yo7hX6=MyKpe`D<5iksyVX z@>!Z3HKf hP-(3 qo{X7&vV1$k)sNxRA<)Qj=-yjcy$=@XRGM_)TXluXhwH&7-+8@oP~ zYZ;6zBnV6h@exW{q>cxftGIOM7r1o&m-yuRPjO}KXUO|jFnlm!UnlT~A_T3ATOaHq zSU8MoGlH4fIV>DF$O$)*ru|=C*`(&JlR-fOre_;<0 -twu`fqs#z%%}%6inON63@{77FW%#`5|j z28gPK$OJ=)a))Ll80_KfxqbNZ*Ph2`KldEsiHHSabp}2aYKMT)x|ph-V9v+=IJYov zFZq46s>IQUmuB>ovj@LdPfgprw}bfDBDLj140V{zixBy`iK8g*ESU)m@??pAE*_t@ z_sj%}is#6_&z=ucD@KRMQ7B{?`_r~&y(pA7;fr<=(No4m0IA71QgaC;7Qz@>3}cv} zrKb`IMFV868{6x7tX-iBZ!aS18zD$xoPLQ6<)rGZF2=`4jOZIzZkxu*As?RH!tcKk z!;ypgD1i!#UC69waO3I?+^ElEGjtZEY#UF y0F4hW!kWZ(_4O19y0WCd!4~>^6ekU4#itA`$OvjDQ)%#84BfYa7@u`3VH`jYi4* zi7L>->_XDOc>D9`uzzue0^ug@ZGzE4jT)l0D+!T&NUfa5K1QWyW=HA2(zw1=K#hPM z9FAdO$XR}3Qw?o`p>-_n)fkxgqQiJ}?*`nZ9fY*5M-8ndXHeZ)QFAK=EeJ;@5Dz=+ zVg`yjD&)#Ya)8OvNfOc`L#XQID5@Jl?A&W%>tO-w4>qxUa|J86wy^!E!I0OB+A>4n z`U2vfDFy;zj7-ns(3xj3nF!*8Tg!B^wC9{~FyJ?-P7f!sv9yfed6h}n^E7R=^Fn-U zbpw|#UBQ+5KI}x#p`2?o&ne)wr|CRjdk(qP+eikf0h<(nz&`dH!*&7KVU!r`W)NkR z6KAfVu~a6f#xWYJV|9H8I|N3n!&+GwN(b=f+o$m5ue^j4OiTm80P_uLM$62V-p*GM zPio~7lAbP3PL1Hm>=d@^9X#0Kxv&q1M&gKbK5Az+(&T~KVqgLR1|tfDnr%Ao_!#bA zy?_|=C?8E{Q;!AG(`e8?>h?gF8boc8Z!uqU_V^diOTy%IbJ+?rW}kg&Xku&<`9hw_ z69=hL^>^afxJl{zB!f E;c1M~R1K5aA*9HVFZ|Y<2o6tRyIez%VQGX8uH5rj zh%G{Wl%VnPVGayVHjn91Jx6h4b9sXrat)U&v)Bo}fHM6?quNHIbql}q<^r83^Cm_| zE;{IHehn+7Va{aOqN*L5vTbT}u#>?sBgQCwj7+m;G&Ma=C!4{;l_GldDbKz56n^)c zui@Oe6G$-ZmT8h!NTORxW+@zIzGy0Z)TrZ=!+2_j5tGY{8%z%b*uHcO)5!>PHKE{O z;Q(To>}_?~%oweO%NxVi`Xkh~mzlRYuQk=<6R6NP);XsTBhV7fsk9{#VIun6@!vXc zQ?MR^a+walN^>@pATToqPBIoZ)h?7Wj4YVD?0*WW^eCk+jb~0D!?UMP V^hO8Ix_*6FMwOy zMLeXijz)ucYCMS;wPR468ELC $TtrV0gV$5bs1gJ{%f{4aq7)jB2`@0yM9OneTjeUy; zu`oZ2DLO}e=dn_itp?`DQkY{XUFN`&un7hN%e!Uxe0u8UOe8=F8k?HMCm&wH#>1Pq zLO?cyFQQtYU9T`IY60=)ZG7wVGsstHN=qGkohTOUAmr;Zf63t9PKtJ4FXV+SIovLI zu)*xn+gd|_KJdZ)RlIwv0^i&Vc QCNpw5aXDQwQE$I=>t!6K!#*}Q zi2ybK&UOh8_4W=mX^5e|015fDobPboKoFYFp$VO38HVuS9CH~?x Q$a}g3%Z!5Mdn(Q_3<0#I=q_lq)pVdb7ip8VF)MP6rtt z#(URG*z6odtr9?!8lo@f^oYvY{T00Z<{4bLc!%LKo#mZ}@JGS~hB-i>hg b?Hg|TB&F`QXJ%ZD3{0 D9#01V4WRllJk)f{u6{BQ3)z3NYSl8eskb st#UzXy}^hmk(`3^r>Gdy&GEeS~IB zOFA^!v`#_#!tI@#b{{*|ApNe@`v7ShxSwgVws<@|F~?IRSdVn+$hBnh(RvYob$=Vz zHw!jtElVxsD%h?xQQ&Aj0()p|2-#xUk|rfqsse~}XUN*3$#ND0Q{Gj4;W-b^K6e~9 zZmyZLbZO}$(MEQg4zeu`?&9dYk6HDSC4n+OEt&(-a^T1!UVim6wiMH1yShSLn!YxH zk+9UNHMp3oJhN{Kr T0MNay2H@DhDCuzh1` zd&!_h(Ar`SL22qc?F=CF@|>r&g|l-T_{YCDg719&48rav{Zx}dM%0+;qc3UH>jYM& zr-yyBX?*)tH(r>fS!=El9Q9Mja5{<*qdl3aDib*Muluz;Qs?~>u>d}M><}X12yQdq zxVe?17PS%dyG^5|AwkY99`a)@6~k0gFYEoMi=!#p4n77NBXAsGRQ&`%V2#Zl#=)1r zinU6W{Ocm+?jq5DI?FT_e)~la>q|EZ3a3+hwV#x5F06WTC%f2!hnk_6_f&G!f_E;j z;|HIvGOW|{5Epi6mcD;u9pAmW$`G>1yuybwQ$v(sHyX?nf*ia_d){Jy3Ptr@MLE2& z&=S7=2Jlb*>@6HQFp9gkZs2FXxPrPXiC=!a%X>Un-(mDuri4&4%2nnYsWd+K+2`=5 ze{c-ndE1Y)>n5}JI{gPViIUXN%L}ASzC*P_4T+IS#}}rtL*I2LlSfujUZ3Nd>u+*j zhhEbDxC|PpI=pi3QS@p@EPWu%ggJbbN%IjD0i}p+|EbR+vF{|dDs{VWn!aq55u+S_ zqeDKQJNYFGi3dfBp`!snD#~t~tqOMQSsXk4EGmsIE uAK%4C7gtc|rYTuL z{N<1D q#IovWWW+spj-4v(pJY{q%nNoh~wK_YsICvH$5K z)D%5q7tb86SGfBWO%n4ztmZ4&KaxP2VQ!|Vm$;czkoRNpF#KfN<6<2=!5$qvQT~&M z{TtY^BNUFwu25)ebp}W706d`tmLJ_ll|E7aSC5Y6CW;hV&+{j~cwRhmR7FrY=vu%1 zs?}6m9j@&XlxK|aso^lA6+g1H)g4NJkKiQ8_^%(Cz>Bjf%doFy%GgxiLxK)5XSutX zr}Qg!Cs53VIuAd@o5zQc2z&9)Pp+fV8^N1j{0z>XS;SPTfyvPh-g ?kw$p lQ@(Yn+rsIm~=a0>qT2(_Ql=hg~Jq%YTK*n4H3=cOq`!J@AIpZ(LZRrOClz_50w zyoIBu&qyC_h4#Q$4Cz>q8Fn2FdiR&-(s*T_*)}^!(k3hGeUM|q$H^`xgSez6UOIg} zaxKsSmsf7!*l^2!RA;N=XZBo0sPz!VofUh$n~HgmND|<&!!#p3>|Yqd*f2xq;yQtB zU~VCSEc25OE-W$G4PcSc+~imotM{%mQtKc*G;WMhRgySLS}W9Gm3YvP8qdk!mB}c5 z!&r2sVq^+UkEB~m_L+PUYdeg}XzD@%KYb6AM~COXRvtsZeG0MT$0!UEXbNSGT2dw` z(iG9sU=1clFwJOeyI8 Ew}+0CyVnPhaGBHyP`wNFkWo#V4n9B oXdml9K&0e9p|gg+cnY-0W!{Hc99XHUfABw zG6*1O#yH-udFpw5mZtCGqb*8x#pZ87uuAs?fUOX4s{lja{S+V?qoJTi(WdpMz)5$g z2WIjiz0SobGn_^w7^g*GK=8tzqgNF$tQ<*4gCs^9p|9_}$0yR5nx3&iEjeP1(w9sR z>HCOITkD5^7uE}SRH)%N!_;3gk7+UU7eP4^sV|^zeR2u;)gso*AzXh{M49$A7N}u{ ziQ?jH7^Nz3;`k(v?2lu_zsgXzM_*IJ{20yQq=yM*6RD6SodDB$|KigOzWZJc+pQ6t z_{?Vv2=a$xihf35%nc^!%;oGAl1t!*=nD^zr!bR>lR*I_BKo-#ABs#O6)NjZdPR Jh@UYmXKdsaeCnW+vO4 zE#?7Q7+uO_F+*T9ZYmtWk;ycpE`0^t;e!m(P$Y=y;RNN_g(6K)fdCfC+_Kb`j7)J( zdekiDOIT$U!~cB z;5nzXLqu8qjUsxLnd;@_wFeI^N%K R+$l1F^|MOpg*AnLyX#;CiAsM zC&o(pM7yL)f5Wi|h6z?dLPW;uHK@}wBlz;MdAxOa8pjw3%IrCSXbipNM^K~*tnwrJ zc65o+XQi(pX1c_%K96GrcVmM=OJE2AU%+xAEeYF`dLzbkLhYS9WPTi7_@g~k _jm+%?`6=g6;UkFPB(+hceJkHqFry};*uNd#USmy-X&x3 z;g3_&^a@h|*FU|D`wv#_JBdv)LuSY{hLgXa*}+08icQ)_g~XcY0E__#G?RU!L-xC4 z2gg!)^Ql=pO|6P+-t?*La?gi2KU*cibTmf7s3@yQA5^9`H>f$b(8~4}1d#r4$jdOj ziaR%MU?eswu5nZ+sB-u^s49IFbzT>Z;Qk5i4^dn0UcYMLp{X0sNzmlP6zWN8@+2rm zGxO|t93QRa(P2JNr#kCB+a|A9HaC&KeH$(>nMujEcXoSv!$j&E=KXP8yj8%$l)l8e zfz{Ow+*)IT$H=Wxt{O8QW;z5}U-aR0yb%M{CZ;@y0+ij@#_bCiQ6kgj4_X{owa19@ z=2o7`qBEZlmNEoKFQq#SGZ)PHhz)uu_sYQuJil)Y3Hlyc0=vLQ3VR2pNH30y>C$Ft zr6NI@EmiSER>X29-Yaej)KoHprR60way5+EyI%st;k8KH!(&i%ym!_Jafs0h@Mvlp zcP`(+_I8#gjM*>kp4n9r hk)Hw+!Ctw@4-Mw`KwcQ+ &!>r9eAo%lW#2UO5#odhn!IQ@gmf+{H)KmeY%j% vxwhH?rT7Ra@$(im2#I3brF)jJ Zj9vTwi8N2W zlo&?-<;68T&FJRNqYN%=7SZAeO-{t#4q+d;E0<8E`GVWYj2$3Y8EGkAx0kb7i $Qo5IOGLO;#M+=;-vp- rra(TJbXSPk#L|0|3}#+i&V^3y}bzNQ$TO?+7gxrynFI#Be( z&ETx{%728#USSQk-T37F_u%V4YtWL)-(259fkY*JJ^aPZO| }NvWxPAd)WQx zeMd`ZV(iz4+WI9TeFubq=vd$oBo}?mg }9!^_7l0@B9tc?ms}D%qlCRmRVtwRYpZWzjhy|CX!ejijuKi zYG92)iTZT3W1uYqU>qR0-#1^T9a=Ktyo)r2a`4g+1vB>jN<+ipT`1_a0O`GVe~I#1 z4*MsLkbllX ~ zGlQ274dcS?P29-T>}wJF?MI8onk{60{u5NzRtSpTVxfr<11S-lCW$8(bo@kB^0$?3 zVgFQ+xk?f5TrT3uCLOvWErQ}`h0LGsA8gr=sC7h>jAJ-jt)f{fB04*V9>eNE=}<5- zr8<(X4h~NaGl{KZqu5{`!zeG{ICc{#BTZVU@ZSMNA+Vm53XB5#C^6E~8ka?4GSvpC z$Z)h+rYRl^;p(+(_{ER@3Ww5%kdBQo4DN$syr!0L<2iTY%jd;-C(_b1lz-T?oMIjC zVYAmpfBM?k#haHAO;6xgH+P7942@C=fBosYF`Zbz6Y#=Aua>{?D`Y= DamSB{;Q~WipjU^a8HHlq zGInW SJ}?rIwcQRLZ1YM_+9l%pKn#HS&8on`wqlg_op89gzKTF1314c2 zxeLeT5Mp$mPI+Uuzz{x(I3uI01ZH0{gdnp_hslog%SY6y@d6>o2ry4T^tgFo+6G|o zkYhSv2q^tx?dtj(zW;;o!L!@IvDs&sEHZMG;2PiMPaMt)2hWQq9rHCPVwG6q?CZmt z{`z0PBXyVvrBT>@$Rsg?JDZduPn6oA>%5B{bqXcqF8%_|!j5T=nMCWhKZ@FM#DRY5 z?@1|Fr4e$NEH3g`Tc m6lw|Z6SZ`75TBvV$U`HYNDz|By*PJuD|PeyLh zzhtr{d}e;cLikTsa+stUld)49tzl`oq`;_6U?@|Uc9_#QKSRp VlzN64Yz)>gKCae6V(TwpuBP0`j-0G7gc$$9UJ4HtUCMn*!~8Lr)m2& zFjyTeaX{E>Cs6wB2U=pFS)UBaD|OT|>xfLwz~v7@zpdOf=iI|eu8tk%R #XnCiMt(_uXSV&`?AYHh=VZX(q zC4Tu4bLJE{cF(@06&jCQ)jfcmudILY!LRW1zy52Cdx!A!+!-c}415HL@zDXofT3l1 z%x`JR>BpcL7~JYl$Ad=tuyf9V8rvN09{ve+FO#*A_!L6D2HN?DKz;@8>INrLB*ALx zIJ800?w(fIX=mSQ(m!uNJg)k)&J2`6?O*Hf4{W*ib=`om+P}Yly_U #JdQn`0@VFlXga~PwUfLtT8jT8m_Ol=1 z&V@^ivJT+L^l=2})Wsw5%K)%Ri@&PcwTas?HRY@J Esw@Q&WgC>(>{!JB)yox^<-{l3rHFuaJQ(E>6(p(mP{=bAv4> zZu_ikGhnosVvOlu7r>4-J=wP3rhnhwIo>1fs%&pkyQ>HcO<3U9rC;eVEN;`pZ8Ece z#N6n_{$T`|Z(X>$Wc>^$qYhQ1-$G-8VGprvy{nF!x31&epZ-WckcksBr!g@! N*skoLP_EK0b86Hu>aa78^`rJr>|4Li5{e{X5F7M2ERXjd?++Rc zK(Yfn)^-4#{sfYK;X8fc1cnbhPhdP~ld;_ rd zo3qpKachSm`f$1$TygRO-oge0KnBdTWJ^+Lph=5#QRf`HYroiC%VE2)j*T1vD(;|I zV`9fXbxTLTucdx vA=&>eN24`6My8{5MI%l}+Xp7@> Nf+lT0X8A#kwAGovEM9CBbzxp&?JrF+?s GHA6Ef4*evATM3T9dA#*9CrM5mLTc)${qm~=ik5E#kd}e!n9_fS zb6^p>1mOO|2MC}JR7zNgO=Ds703x9%?EeI?(BHxtBVi8|F cH_|I7mP&l}Gi zP=0g5gJP|X!A}CB;)HX~zynRuKeg*Ov0@>g9?=<7v~hfZ4)nW>gKe|bK($%6pKB`C z^2iaeVohH~&;v60Bso#~B ^^LbWkabWvyiR_Wi!GC*5JEq^_>cIu!w6IXS*)$2!tF`QOp zfPIP@F-H4+2KCPmfT}T)DOL+8*9!!!fMT6MNu!#~rYXtNs4ne#$NFKfj}hWlJvj$^ zVAx>V=ZHL}(cNp#>DU5mFc@OemxeE-A0MTI?l$4?Rgmz Px#07*qoM6N<$ Ef{GZ!)c^nh literal 0 HcmV?d00001 diff --git a/repo/js/营养袋吃药统计/main.js b/repo/js/营养袋吃药统计/main.js index 7d7e45e85..c2fd6f4c7 100644 --- a/repo/js/营养袋吃药统计/main.js +++ b/repo/js/营养袋吃药统计/main.js @@ -1,14 +1,36 @@ let userName = settings.userName || "默认账户"; -const recoveryFoodName = settings.recoveryFoodName || "回血药名字没填"; -const resurrectionFoodName = settings.resurrectionFoodName || "复活药名字没填"; +const mode = settings.runMode || "营养袋模式" +let recoveryFoodName = settings.recoveryFoodName || "回血药名字没填"; +let resurrectionFoodName = settings.resurrectionFoodName || "复活药名字没填"; const ocrRegion = { - x: 110, + x: 1422, + y: 586, + width: 300, + height: 40 + }; +const ocrRegion1 = { + x: 1420, + y: 687, + width: 300, + height: 40 + }; +const ocrRegion2 = { + x: 105, y: 242, - width: 124, - height: 32 + width: 140, + height: 40 }; const loadDelay = +settings.loadDelay || 800; const stepDelay = +settings.stepDelay || 500; +let refreshTime = parseFloat(settings.refreshTime) || 4.0; +if (isNaN(refreshTime) || refreshTime < 0 || refreshTime >= 24) { + refreshTime = 4.0; + log.warn(`刷新时间设置错误,使用默认值4.0`); +} +// 计算刷新时间的小时和分钟 +const refreshHour = Math.floor(refreshTime); +const refreshMinute = Math.floor((refreshTime - refreshHour) * 60); +log.info(`刷新时间为: ${refreshHour}:${String(refreshMinute).padStart(2, '0')}`); (async function () { // 检验账户名 async function getUserName() { @@ -21,6 +43,23 @@ const stepDelay = +settings.stepDelay || 500; return userName; } + async function close_join_world_popup_window() { + const game_region = captureGameRegion(); + const text_x = 762; + const text_y = 29; + const text_w = 210; + const text_h = 40; + const ocr_res = game_region.find(RecognitionObject.ocr(text_x, text_y, text_w, text_h)); + if (ocr_res) { + if (ocr_res.text.includes("进入世界申请")) { + log.info("检测到有人申请进入世界,拒绝申请"); + click(1051, 51);//选择珍贵物品 + await clickPNG('拒绝', 10); + } + } + game_region.dispose(); + } + async function close_expired_stuff_popup_window() { const game_region = captureGameRegion(); const text_x = 850; @@ -62,26 +101,24 @@ const stepDelay = +settings.stepDelay || 500; if (lines.length === 0) return result; - // 获取当前时间范围(当天4点至次日4点) + // 获取当前时间范围(根据自定义刷新时间) const now = new Date(); let startTime, endTime; - if (now.getHours() < 4) { - // 当前时间在4点前,时间范围为昨天4点至今天4点 - startTime = new Date(now); - startTime.setDate(now.getDate() - 1); - startTime.setHours(4, 0, 0, 0); + // 创建今天的刷新时间点 + const todayRefresh = new Date(now); + todayRefresh.setHours(refreshHour, refreshMinute, 0, 0); - endTime = new Date(now); - endTime.setHours(4, 0, 0, 0); + if (now < todayRefresh) { + // 当前时间在刷新时间前,时间范围为昨天刷新时间至今天刷新时间 + startTime = new Date(todayRefresh); + startTime.setDate(startTime.getDate() - 1); + endTime = new Date(todayRefresh); } else { - // 当前时间在4点后,时间范围为今天4点至明天4点 - startTime = new Date(now); - startTime.setHours(4, 0, 0, 0); - - endTime = new Date(now); - endTime.setDate(now.getDate() + 1); - endTime.setHours(4, 0, 0, 0); + // 当前时间在刷新时间后,时间范围为今天刷新时间至明天刷新时间 + startTime = new Date(todayRefresh); + endTime = new Date(todayRefresh); + endTime.setDate(endTime.getDate() + 1); } // 时间格式正则:匹配 "时间:YYYY/MM/DD HH:mm:ss" @@ -195,22 +232,23 @@ const stepDelay = +settings.stepDelay || 500; // 如果需要删除当天同名记录 if (deleteSameDayRecords) { - // 获取当前时间范围(当天4点至次日4点) + // 获取当前时间范围(根据自定义刷新时间) let startTime, endTime; - if (now.getHours() < 4) { - // 当前时间在4点前,时间范围为昨天4点至今天4点 - startTime = new Date(now); - startTime.setDate(now.getDate() - 1); - startTime.setHours(4, 0, 0, 0); - endTime = new Date(now); - endTime.setHours(4, 0, 0, 0); + + // 创建今天的刷新时间点 + const todayRefresh = new Date(now); + todayRefresh.setHours(refreshHour, refreshMinute, 0, 0); + + if (now < todayRefresh) { + // 当前时间在刷新时间前,时间范围为昨天刷新时间至今天刷新时间 + startTime = new Date(todayRefresh); + startTime.setDate(startTime.getDate() - 1); + endTime = new Date(todayRefresh); } else { - // 当前时间在4点后,时间范围为今天4点至明天4点 - startTime = new Date(now); - startTime.setHours(4, 0, 0, 0); - endTime = new Date(now); - endTime.setDate(now.getDate() + 1); - endTime.setHours(4, 0, 0, 0); + // 当前时间在刷新时间后,时间范围为今天刷新时间至明天刷新时间 + startTime = new Date(todayRefresh); + endTime = new Date(todayRefresh); + endTime.setDate(endTime.getDate() + 1); } // 创建药品匹配正则 @@ -275,7 +313,6 @@ const stepDelay = +settings.stepDelay || 500; if (!res || !res.text) { continue; } - const numberMatch = res.text.match(pattern); if (numberMatch) { const number = parseInt(numberMatch[1] || numberMatch[0]); @@ -296,6 +333,55 @@ const stepDelay = +settings.stepDelay || 500; return null; } + async function recognizeFoodItemByOCR(ocrRegion, pattern) { + let captureRegion = null; + try { + const ocrRo = RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height); + captureRegion = captureGameRegion(); + const resList = captureRegion.findMulti(ocrRo); + + if (!resList || resList.length === 0) { + log.warn("OCR未识别到任何文本"); + return { name: null, count: null }; + } + + for (const res of resList) { + if (!res || !res.text) { + continue; + } + const match = res.text.match(pattern); + if (match) { + let name = null; + let count = null; + + if (match[1]) { + name = match[1].trim(); + } + + if (match[2]) { + count = parseInt(match[2]); + if (isNaN(count)) { + count = null; + } + } + + if (name || count) { + return { name, count }; + } + } + } + } + catch (error) { + log.error(`OCR识别时发生异常: ${error.message}`); + } + finally { + if (captureRegion) { + captureRegion.dispose(); + } + } + return { name: null, count: null }; +} + async function findAndClick(target, doClick = true, maxAttempts = 60) { for (let i = 0; i < maxAttempts; i++) { const rg = captureGameRegion(); @@ -308,72 +394,155 @@ const stepDelay = +settings.stepDelay || 500; return false; } - async function clickPNG(png, maxAttempts = 20) { + async function clickPNG(png, maxAttempts = 20, doClick=true) { // log.info(`调试-点击目标${png},重试次数${maxAttempts}`); const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/${png}.png`)); pngRo.Threshold = 0.95; pngRo.InitTemplate(); - return await findAndClick(pngRo, true, maxAttempts); + return await findAndClick(pngRo, doClick, maxAttempts); } async function main() { - // 设置分辨率和缩放 - setGameMetrics(1920, 1080, 1); - await genshin.returnMainUi(); - keyPress("B");//打开背包 - await sleep(1000); - await close_expired_stuff_popup_window() - await sleep(loadDelay); - click(863, 51);//选择食物 - await sleep(loadDelay); - await clickPNG('筛选1', 1); - await clickPNG('筛选2', 1); - await clickPNG('重置'); - await sleep(stepDelay); - await clickPNG('搜索'); - await sleep(loadDelay); - log.info(`搜索${recoveryFoodName}`) - inputText(recoveryFoodName); - await clickPNG('确认筛选'); - await sleep(stepDelay); - let recoveryNumber=await recognizeNumberByOCR(ocrRegion,/\d+/) //识别回血药数量 - // 处理回血药识别结果 - if (recoveryNumber === null) { - recoveryNumber = 0; - notification.send(`未识别到回血药数量(数量小于10识别不到),设置数量为0,药品名:${recoveryFoodName}`) - await sleep(5000); - click(863, 51);//选择食物 + let recoveryNumber = 0; + let resurrectionNumber = 0; + // 设置分辨率和缩放 + setGameMetrics(1920, 1080, 1); + await genshin.returnMainUi(); + keyPress("B");//打开背包 await sleep(1000); + // 关闭弹窗 + await close_expired_stuff_popup_window(); + await close_join_world_popup_window(); + await sleep(loadDelay); + // 打开界面 + let maxRetries = 5; // 最大重试次数 + let retryCount = 0; + let successClick = false; + // 根据模式选择点击的位置 + let clickX, clickY; + if (mode === "营养袋模式") { + clickX = 1051; // 选择小道具 + clickY = 51; + } else if (mode === "筛选模式") { + clickX = 863; // 选择食物 + clickY = 51; + } + while (retryCount < maxRetries && !successClick) { + retryCount++; + await close_join_world_popup_window(); + click(clickX, clickY); + await sleep(loadDelay); + // 检查是否进入了申请界面(通过查找"拒绝"按钮) + if (await clickPNG('拒绝', 3)) { // 找到拒绝按钮,说明在申请界面 + log.info("检测到进入世界申请,已拒绝,重新尝试点击分类标签"); + await sleep(stepDelay); + continue; // 继续下一次循环 + } + if (mode === "营养袋模式") { + if (await clickPNG('营养袋', 1, false)) { // 只检查不点击 + successClick = true; + log.info("成功进入小道具界面"); + break; + } + } else if (mode === "筛选模式") { + if (await clickPNG('筛选1', 1, false)||await clickPNG('筛选2', 1, false)) { // 只检查不点击 + successClick = true; + log.info("成功进入食物界面"); + break; + } + } + log.warn(`尝试点击分类标签失败,第${retryCount}次重试`); + await sleep(stepDelay); + } + if (!successClick) { + log.error("多次尝试点击分类标签失败,脚本终止"); + return { recoveryNumber, resurrectionNumber}; + } + if (mode === "营养袋模式") { + // 营养袋模式 + await clickPNG('营养袋', 1); + await sleep(loadDelay); + const pattern = /(.+?)\s*[(\(](\d+)[份\s]*[)\)]/; + // 识别回血药 + let result = await recognizeFoodItemByOCR(ocrRegion, pattern); + if (result.name && result.count !== null) { + log.info(`识别到: ${result.name}, 份数: ${result.count}`); + } else { + log.warn("未识别到有效的回血药信息"); + } + recoveryNumber = result.count; // 识别回血药数量 + recoveryFoodName = result.name || '未识别到回血药名称'; // 如果识别失败,使用settings中的名字 + // 处理回血药识别结果 + if (recoveryNumber === null) { + recoveryNumber = 0; + notification.send(`未识别到回血药数量,设置数量为0,药品名:${recoveryFoodName}`); + } + // 识别复活药 + result = await recognizeFoodItemByOCR(ocrRegion1, pattern); + if (result.name && result.count !== null) { + log.info(`识别到: ${result.name}, 份数: ${result.count}`); + } else { + log.warn("未识别到有效的复活药信息"); + } + resurrectionNumber = result.count; // 识别复活药数量 + resurrectionFoodName = result.name || '未识别到复活药名称'; // 如果识别失败,使用settings中的名字 + // 处理复活药识别结果 + if (resurrectionNumber === null) { + resurrectionNumber = 0; + notification.send(`未识别到复活药数量,设置数量为0,药品名:${resurrectionFoodName}`); + } + } else if (mode === "筛选模式") { + // 食物筛选模式 + // 先识别回血药 + await clickPNG('筛选1', 1); + await clickPNG('筛选2', 1); + await clickPNG('重置'); + await sleep(stepDelay); + await clickPNG('搜索'); + await sleep(loadDelay); + log.info(`搜索${recoveryFoodName}`); + inputText(recoveryFoodName); + await clickPNG('确认筛选'); + await sleep(loadDelay); + recoveryNumber = await recognizeNumberByOCR(ocrRegion2, /\d+/); // 识别回血药数量 + // 处理回血药识别结果 + if (recoveryNumber === null) { + recoveryNumber = 0; + notification.send(`未识别到回血药数量,设置数量为0,药品名:${recoveryFoodName}`); + await sleep(5000); + click(863, 51); // 选择食物 + await sleep(1000); + } + // 重置筛选,识别复活药 + await clickPNG('筛选1', 1); + await clickPNG('筛选2', 1); + await clickPNG('重置'); + await sleep(stepDelay); + await clickPNG('搜索'); + await sleep(loadDelay); + log.info(`搜索${resurrectionFoodName}`); + inputText(resurrectionFoodName); + await clickPNG('确认筛选'); + await sleep(loadDelay); + resurrectionNumber = await recognizeNumberByOCR(ocrRegion2, /\d+/); // 识别复活药数量 + // 处理复活药识别结果 + if (resurrectionNumber === null) { + resurrectionNumber = 0; + notification.send(`未识别到复活药数量,设置数量为0,药品名:${resurrectionFoodName}`); + await sleep(5000); + click(863, 51); // 选择食物 + await sleep(1000); + } + // 重置筛选 + await clickPNG('筛选1', 1); + await clickPNG('筛选2', 1); + await clickPNG('重置'); + await sleep(stepDelay); + await clickPNG('确认筛选'); + } + await genshin.returnMainUi(); + return { recoveryNumber, resurrectionNumber }; } - await sleep(loadDelay); - await clickPNG('筛选1', 1); - await clickPNG('筛选2', 1); - await clickPNG('重置'); - await sleep(stepDelay); - await clickPNG('搜索'); - await sleep(loadDelay); - log.info(`搜索${resurrectionFoodName}`) - inputText(resurrectionFoodName); - await clickPNG('确认筛选'); - await sleep(stepDelay); - let resurrectionNumber=await recognizeNumberByOCR(ocrRegion,/\d+/) //识别复活药数量 - // 处理复活药识别结果 - if (resurrectionNumber === null) { - resurrectionNumber = 0; - notification.send(`未识别到复活药数量(数量小于10识别不到),设置数量为0,药品名:${resurrectionFoodName}`) - await sleep(5000); - click(863, 51);//选择食物 - await sleep(1000); - } - await clickPNG('筛选1', 1); - await clickPNG('筛选2', 1); - await clickPNG('重置'); - await sleep(stepDelay); - await clickPNG('确认筛选'); - await genshin.returnMainUi(); - return { recoveryNumber, resurrectionNumber }; - } - // 主执行流程 userName = await getUserName(); const recordPath = `assets/${userName}.txt`; diff --git a/repo/js/营养袋吃药统计/manifest.json b/repo/js/营养袋吃药统计/manifest.json index 89b694383..8c9a69c45 100644 --- a/repo/js/营养袋吃药统计/manifest.json +++ b/repo/js/营养袋吃药统计/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "吃药统计", - "version": "1.6.1", + "version": "1.7", "bgi_version": "0.51", "description": "用于统计指定两个食物的消耗,推荐锄地前后使用", "authors": [ diff --git a/repo/js/营养袋吃药统计/settings.json b/repo/js/营养袋吃药统计/settings.json index 9ecfcf192..7be6e7a4a 100644 --- a/repo/js/营养袋吃药统计/settings.json +++ b/repo/js/营养袋吃药统计/settings.json @@ -1,9 +1,13 @@ [ { - "name": "userName", - "type": "input-text", - "label": "账户名称\n用于多账户运行时区分不同账户", - "default": "默认账户" + "name": "runMode", + "type": "select", + "label": "运行模式", + "options": [ + "营养袋模式", + "筛选模式" + ], + "default": "营养袋模式" }, { "name": "initSelect", @@ -11,7 +15,7 @@ "label": "重新初始化药品数量", "default": false }, - { + { "name": "recoveryFoodName", "type": "input-text", "label": "回血药名称", @@ -23,16 +27,28 @@ "label": "复活药名称", "default": "" }, + { + "name": "userName", + "type": "input-text", + "label": "账户名称\n用于多账户运行时区分不同账户", + "default": "默认账户" + }, + { + "name": "refreshTime", + "type": "input-text", + "label": "每日刷新时间\n用来定义当日刷新时间(0-24之间,支持一位小数)", + "default": "4.0" + }, { "name": "loadDelay", "type": "input-text", - "label": "加载等待延迟\n用于界面打开和切换的等待\n默认800,单位毫秒", + "label": "加载等待延迟\n用于界面打开和切换的等待(默认800,单位毫秒)", "default": "800" }, { "name": "stepDelay", "type": "input-text", - "label": "操作间隔延迟\n用于OCR前和输入药名前的短暂等待\n默认500,单位毫秒", + "label": "操作间隔延迟\n用于OCR前和输入药名前的短暂等待(默认500,单位毫秒)", "default": "500" } ]