From 28ca21f7100f1c1cefe290ffd7a128aa80fddc49 Mon Sep 17 00:00:00 2001 From: isstuev Date: Tue, 8 Oct 2024 15:54:26 +0300 Subject: [PATCH 1/8] puzzle15 --- public/static/duck.jpg | Bin 0 -> 101074 bytes ui/games/Puzzle15.tsx | 183 ++++++++++++++++++ .../AppError/custom/AppErrorTxNotFound.tsx | 2 +- 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 public/static/duck.jpg create mode 100644 ui/games/Puzzle15.tsx diff --git a/public/static/duck.jpg b/public/static/duck.jpg new file mode 100644 index 0000000000000000000000000000000000000000..385fe338cd5dec97d311b15055319f5247e308b2 GIT binary patch literal 101074 zcmb5Vc|4oh`#*e(rS@7PC6-zv2qhwlR&9wORBWYak%Z7tTXmnYlUOSDSXv^K(vE~G zI_;oBVrhjgiRyIIRy*2hb#G@nzuSDi&-e3tJ+J4V=kjv9?sJa&oaDaFwZ6|kl7Ain z8vb}cJRlb?gHQx0I+0Bi%+ApvVh$lx&6C6 zvQc?wJ54M9JMRCJQ=(9_$Y9S~@b@4&Ju@2sWK=<3^=NkbCa(+f&`fYqAdlMQlm5ed zZ1M^J;WyU?%?u0i2FK7qJ}&A1;uHQa{(t5IhXC?Nzz@60{6CaUJqFGP0N&pAz~*=G zpZ51*|9&+G57d(_pV@4o)?&p-D8Z$L?5i{cgqCB-d@%F0SA zYOt+ps;X-GP#q1Jv7xD{vElaZ=12_69N}QOeY@>$TL&j+S65dvn>{{zc6wu6T%9)` zBBQLVtfs04-?|m!$%gATqKLIaztREn8$|W#z$MO<6fD6L|*(ZBLSEdqzBhy zs^~Zn)+y)6C3^x3g0AfiOy4*0y{Xc7yMmF^4$HacFZImksI=7+ zyY7XZF33NnJ8N+GP3u!pgXqOMtsUbVyd}SR#D&W4$E4lT03K6a(Z!p`atHg%gw~DhL zRoCuc$r{JUz#kWjG9-Ne@^OAwSLcRP)b`aRwwtNjIo0|7u9n0%H{6^a&Ku}*mpaY5 zY`Zcu+`Mv*2(11|v8>Ez8o!wMvri7AYYr5L2ya&$N;FtodzhHY?(1Q! zUQ*`7&xy}R6t-RD^X5r@J3N=IyQl^kY0^u#S#J9iYwuEP_N!OtbSskg@)ls$&EqgoiTaG(8$F{z+9d)_eI{(SlA-UGYEkfsk z$*J4ZT-v*Ln7#UGe*!7H+BIYR8@qesaOpD_qf$JR z*@;WVb&f42*|>838PWcnT9Q7N)tpz$yXuB*TQ2XZo4C0L)mlPkTF&CHIJVF7iX3|r z(AAtC&B^ihOxit@!67OKIKOQ@ai;uw3E5Os@9n3z{A*Kn(sRzKyWv_VOC@-t^t+j_ z=DwG%s6KYvaU8i^P0J2>R2LDY7i^MmeeX@9tG4Ih^(U~z(q+RklR{lB$wN68^RV(O z-XYgz?yyHLJ^$%Y5>8v4Ca(VCc$`ol>W{i1tcXU;{krg8^N7Q!6VHWI|IQ_nq)z>F z^|D#h*Y+OtFgw)sXp~>e9~Lma>CUE5EU&Sz=&*NabW>#1*$+pidOV z_!_U}WDjvK)XRG)VoDhmN!n(-gpm})H{12iFshw1EG7i3$#Ij8gQ$jzdQmSzqHtFZ zdh1df_+PAz_3vRxgM(#lgnBKDu0Z{Ja{j0&+WchGs`$E+HHC8D^y{klZTUk4t3IW5 zO4$3sO#eLb!KotgzNEU_*%vRq+f@q_PuH4 zv~piPh}QRQSK|-FMZE+P>vf>J_`LyjoAiFZa!m7#+dc)?oO_C|p2QeXzVB{)Re2;W zrtT*}U$WyaR`oNjnLC;@IoagXmDw%q>*kAEv06q!+%L9-Dd)tO`*#XI_;hcmnbscD zE^|nnHBr%EnsC!}OL;j;>o4l%W+H!$2l z)W$RBzHzN-r?nFglPZQfRvOMt{Wn8{eFqnxr zem*~7e3I|Kr&OG>y{BX0Sx8skFR#8{&uRJ{H8EWk5RM=In11?;i)GjB)?ds28hPMk zx>N8)pAt)2o5fg2ijV{6j9faG8DDC;mZ140mn>WHY>-v$ z&W46~`V55O+l&f@5%!~>1TFr(dZICII{zBJ$Za|*v>_-M7A%pRkCp~^Y+D- zt6%!3S?`maWiN=ND~-)Ae@*T{|4n?A{Xl;xknOTH{dQ$y`3yM}vA4iauLhk~-qJmC z1+VT)=(vSY+Xz_QI~=vJsy>PJ^P@&uX<-28Q$@MmDpvM~^F4xX(r=f_A%|$(Jq{1p zk?T!miNBMr9e+B%o7aQ!)%K^aWm#)~cDbVFR}<3Hda@w%gUfA!)jhK7r_{LztJ@kg zNBUQiJ#GyjIo?w9>x&Tb3I>1FR#f%SH1kn2rcRUh+1xg7K-G&3$Zgu^NF)#$X$<7b zoEJritb*fJs;j98W}q&;+vKMX+Q}-dn+y{hTlp;t(HIt?yQ*=0UI#siQ7m7GjARmY z0QEu-2&W3l9kf1BB(LJ=vw^{sc=EZnot+-l)b-C6&4j9DCMm)dExks_(1Gu)Uh+g^ zaN0w&vJpPzLo(ee(9<3Q^1$=$ArobU!g+1<1bKT#WDUpB6M1P$UCr_Mo-nGFpG^mC z)@0PjJXvqzl>0MGg9Wi7Y96_9s4{%a??g>tW{U8z&*g15w9a7!J0i-9Z^j)3NhfoI3T&AmTd5LK1$ezD~9p z?{6Ju%h`vZH^`3lI$W@j4&8qfP{IoCnVC;kn?hYzt{^G~KPm>jaU z^*ida{5Rp`z4(h2-LEpsv^yH+e(DINkJgZTke{|xIBs?LMD3Bnsl==sO4hX5^>-f` z7l(9AdoWgxO36>|k!!G(Kg`c~-nY0CHs4pFSC6^n@bdMk0M>5TfWi}KCM7kt;(N)~ z6H)U7?)s{Dab6>h6KcXIb+lt*@L3e2bHe5@3HCHKGZfjUOGDKx?2C3nsfN_AVZ18TLd8~c)a>Ws*G^;Y ziM6)f{@#3O7=t?G9aqkBwapA5EG9#sx%*N(w1d#J~5SX zRW>^_O%P9RyEL;Tr~}OI!YcGfI*J3QHzLF-TNh?A z%$1P9w({*goHtplJ3h5+KlB0#b(ce+%4^M&FitDkP!$jJ5DQLZmm(+pO!b1YvTiu5 z&cvis6zvP84)`0l-G939yTfP9L>;Lk-~O3&*?#T0BxtE~x&X`I#SlMLKcQE(;Mlic z)b9399umKHqAa{v9(zbrD?Wd9SR1hr^|)h&*0^MXuJP%>^R$P|XNa?&jxJqP#COBp z!eg(Sh?sbT)T_SUXKn~@(SHgy|k;YdIqa1pUIxdT^REuxj?@DZ2!Wei| zNMhA=C^9PK{x`S+5so9h@RY|%LeI=b?Ym&c2&?#Bq=ypjb^5EkyMYqZs^@a`f@$dC zcTwBkq&e8HCZlQ-HyqMlu+me_Y$Yz=YA+C&#AJKQ`3kI2uodCjy_NDsA9{Oz*y=b2e9$pbxo$phwX0ewH#low5z12a}O+a%(m}5sV7%TR)F{Jb zVJ1*7UXDyX?p+p8<)l4rYZcoX~Cb`g$e7*9eBEA~+D1-)G{@I~DZ0$p`)3{rRO9rJW z$$O^z`4SO~2yS6+6Ay$P@H-q^ne5q`FzO=u8;`6JZW+J9!pD-6q`P={0UjyP*DzSevwWK`T3klN6|<$N6JL z^?t(2p^&JHJ3O64O?5f&b0f&8pFLs)BMQ$b9BG_|Ijpe_nx8jlX3WIHbhA?mkKF|6f!hza2mF$^OV56(O*ubt^f z1>LwE@*@H$rnJ z`qo*ir)yTSXGp6{hvNC&(lZveQa=5$CxsVb_vKWOrjJU?Y zzFFjW7~1&Ry}@1FwJkHP;(O-Km)FOMf9|Kb-`G#++M5B7q#S>AVuzo?7kg3Xq5jXB zMpkCE%JnY3Z)^5S9&|L9d0^H&W=gTfaTEYZ2UArLdjRT>ii7lnyGbU`OkM+6kh%O# zw!DIj3;*QEqrF%jZyrV)?9ZO4%0NR}Xf-|>s`6FdU1Qy1eWjf2xcrUjT|PH#(<{tu zTs|EQ_jQ|klFW4e9dXI(i~(WQOnrS#5XKtZZ$H(2$H`ak^w~z9Q2zb)$LJJM0*zij zQ#vVb&Yq>0+(OLxRbUUPo_+37xy6fFcCq(#SX$zBHuc@axKT74pUCmVMCz+fm zVqF{o(t1^txos=Yt}-lbHWV}MmK~q%w4+raKk3A&EstHy>b^C5qdU|s!_5x0OUn`5 z-LosT(cWicxqoIt>pxGMO)Ky4;dk6HSdKsVyV0GP>RDg&`}k<-Jqzs`Ovrkg`prqG^ph=nla=Y*Gdon8JaoigRW!NC zYS1<;?B>qH6G#IQfdPJ|%dvAWC(~kNJ*~`#9pC%+T0B*p?SG%DonxDKxyes&$W28D z1D#P0nkuZZLQn_%V8!Mkc>jE}UiBL$ZG;QYCz}jAPzXCOmVaSLZV9bk$VeDf5ZhMW z$5&g_ui~m!1&NDjDX|t{QnHIVxwI&pCvM@-m3q~+Q?;;M6T$bgL#O9=>XfPJrP9LY z^(_YcSRKysE1dmQADY(b8;gu9f9V9j1}%u-*!&iAuL~EW7H=Ivo#9$*Ns2O;^}hkz81Q7OnH|wp~`&D0q{Vbb>0lG{w+XG(V)>!oG`?O?#BtF1X*7^k{j5 zTqFO<{_MFsM%6yUFh@>+R#Np_{0}qBlA7CRbL*Zrz1 zuT%zg2p)ra#zBLDMn%Zy$ftG$|2%i%AnH{}V3`HR>vISuAoG_{HPT(XZ|M7Ryk`2P zr=SD5v_278r^Wm2f$JA4x9NiZ*0jG24r#N{0^}#Rl|zHX2ParTGw&96wsNj4UN%`h zP#pf|NNmeN{FjY)qOM&%pIuPhSKs(*P0>FlDe%?>E4d^fL!|YQF$xf z4A-c@>hGm`ZIkOVLoihyBu-IdaE3S!Wy3M-3)CLno~;=T+9&nXsA={ga8_#oZSS3Aq)^;COjeAO!b0S#7CxxV~m z^+iPSsm7XeLV)xjAkcR%D#PYkY`gs==M2X!m zjtQQ%$`Gz;LA)zw1oI~ya;vCzaBE97|zSJbM)%7)_mRpzMTvayqN3I99qj9o<6ZB$1 z&VGxA;hdy#kI41dNi?AB-G)>vt*_ADrdmIPU$|gbkW8;lgoZ1ZB``SqF_(PqK<72C zjTc?ej?2${RASmfs5{Ywk4)2UI#@cjZ!HNSzO3)!pPl`+aMn6Tm)tNM6kS0}T2Z1V zvq!E!=}~(mil}SyGpos@j2z6^QFaCK#lB~`30tfyc&D+-ig4LuiQR=hTzhDhd(}`W zdNs=*d4qi?q2t_)2k#&>n;bF!?kWC&zq5hgS^8j2L);0}?7}Ck>EdVfd&h&bg0tq? zp~?HZVg|JX7)J9Z7{;Ice*jx;;WVcy`>S(tUB_=J%4Rdt;m3rh$IN$_9^kt!%Z=za z+$TJ%v=VH@lCCM-4lHXg+OJldJ**NZ8LjGT?_OxxkKS*wVcR{bY=xW(J~*22M0{JU zj;L`!vQw`=5@!e4vS~ATmw45(#-_lHL~gdP9_FS&)qTrY#xJ4Qo|J!Q&qB^1*Spf| zchqLtk!(pr%d5o~8^~w-Zn1o%PmfT#-=+wc9NX(3ug9d%#C}mHARXmz$@e*)u58&K zF_J{guy*;CVHjWZo8{TJff|pIXMd?rYS8KBpjw-bSsA4yN?zbY%gWhS1HaU!UxDSE zA5r#a-KyZGydXS(Di!a&G}Xo8W@0kl(7Q!H-o<#eI$k|Nn@NsRj(bL%y4HMrNBb!! z(Jf2^nbK+L2mhOyxw>49$7H^(+|DAsITGO6@U~a=1gdw`>5b2fz~XIE#cNx_{q=!_ z6Up}!U+`T~nVw4N`x7$+P7g?=>r>Ir&>w-pYg+tjv{Y<{Wzg=lV^BNUnWjxTa6DYa zu$pNMr$zyd)>O%QGZ1!Y3w3M;(f#};%hu6nF4W4=R-Y(E_l*-4@BOLQk%E(Gu#;=X z(}Z;$j((2VnR1?+#ZdS=pDSy$!rV2PtyBxoaV+dO?PF9>VW1QzC-#)s0>FYgTUXh< z-Jb17v&k=_;v>6@XGa|`$7?bqRwki3aD}(kiL9>7SU9;=k9NT~;IXpsVL~Qo9_ZCa z<(aTxEm)ZkZ(*|WHKe_rVb7OgPK@^h3H|-ib}TjZF7-dXZnK< zY(-amhhlXF;B*3rX^^W4YA^Ob6F_Lx#XKW%DPFcgZJ9}d9%@mxLD#f>l0gkUXz4M2~@7YK1`_H-YrSV z5}^;0yMhN4&g~*BvJ8TbABDY1Zt9#@j%;1VHn|M<-%qI3-a)T6Ygp4ZD|6XMs7t2| zZq$6A?$G__upYqsCA0i{MEc+LBMH+TS4O|=mfxQ78ohz;Lki;apC)Ha$Ch=?o0g9l zLWxFBzccF1Z?f^CWpGc-Kf5eB5c~Pfk<|8`O_C-vq*sNOvK*C%FIDR2lejZH7MX|R z7YgE}ipH8A5ZzbPUNbD~si{2^-gvsMghUiu=@64igh+yDx|+KEO>;p6YG=zOTXrDn z0e>!&9F~G!pX(66p!iiU;!=!n58Kyf1}-vQR%9Q0zWkHQw9~Lyl#`e~vG`kXQ}r8n zue7Vsj?;{dFKQpUje>S!YV4QMHRrvSSBGtk^eA<$!~LCp41b0}8u1oP{@}K(PBP&F za&`4Yhg|7$iT7i5ae&U-%_P=VBd~59!dHR!Xzr~lJ z?kVKW-DL$i=`~4R4jr-G4AY_qE$eH(_1BnE_>)}?-lxkF$`3fLlWkq!ux@{^>}d)` zbU!}UqkXyKdhozDhWO&#uRr@*$VXkB9DhQU+$eCNG!Bgpa3UL#`!e59$5v@pt(oH~ zw>=XM2p(uJCt27p-z;I4-Kew+o8@7WoG?oIL6~L7Fl{wbuq|4Xgq|suSbFIxW7aPt zyliC&Vl=C+A#p#pkx*PL=`+VqE?QJDYU^#lnk>oTK~)Lu{n;`kMe3o}zb&Jx!`r6| zjczZ9kT;DYE?c}kJdU0oi3&CIb>r_2{2j}X;)wHFrPM*OEgT=I3JK{7eS3`bm^n0u2)nBPW@)~2MXO%w|xX^y-%P%Nf@g&IGc zg2ki>>Cuc#Yae=-FivXQM5tGjs^%b)&1|b<6s^!z3rfVC@+KYz5o*B~%ZbP=+d9p1 zO{|-f4}Y2x8HF5*ZR4YonXqu>l2@)~$ZiEw08Hh=6$jCA`^+)LF=iBQvI;&LQ?*ki z{z8l_KF0}vl9o&g7h+Jspx@OMt7ei=c|0MY)?#Z`4r7_w6-fLPwF8b_@2(Fw-Pt&- zX6A3v;qODMQigl}&D4Pvx=$j*+u8!vsx-u?)arRN)n*k11TAkk!a!@LfV`q#s>_MG z^k~3FZK9~Di#ee73?JdE)s@z`geDHoiF>UjQP_MT>VlzmJQWk1KUT=^)0FipYM_Yr zL!xB{REHD3cI;;8i==9Xp$^E7ZQDbra`zB@m1>Br3>W@me_Jzy6UQ+}cixxRoqT!C z;CGDl?5-5$GH*Iw-&TjrN17flqy&9>yBWHz{!l+Q5pv4#+c8XO@ebSOu@^26 z3oh+CviiFL+?=dH>-1GP!I=-SaYnPzTXs1Q&0o81ynM+%o@^I%AxVk+-Q^@Sx3Ex8V-T z-uI#vwMSd295TAM761(~tO;{_bk<*uDToUWuY>vHCd}!J-m42xl>pxSfS;OawHy;4 zjjg(u&|OO^@M_yKb`h`~lhvdZ^lE!+C+0E^H6z(G4&N7<2=T%0!H0ziZ;R*o=o3ZD zXE0w^j)9S$5!UY*{`Ma4y61=cTUKwKtGV}O;T87g+YeuVL%m8h!Ct$0kdg0O`SawV zZwWbP&hMzt{xjgV5#f7ds^{SQ-<}YE`_ysp!b;0GI)>03SrtFzyUr#9gd(Sq>uR}&L z-(MS^+L>2Y)Y&a?7}K`pi!|_CKNgxUlQ}hrf7lmAyl>1%K!X%IF;rjQ%>+619g|vok=Q z@D=w4&Z>Jayj}SrYv>S}C_*n;=%Ck8Wro#7wWQWcp~~TWeQnB67%C@VHKvWeXlBC9 z<2%C%H>^fY1&F{V!V5%rQFY^tXJlls4RF>M$1NGGcQP&Joxfr0GknP}v6lF5?cD<| zZLskkEi+Xfr+3^uJMx~mHUoJY5+0W7U(UBv?4(|*B(f^lty*{|;w!dblGcmJA6Fqd zYsSCYEI;jMF>M{e>?YOpcDc4KdS|OP7S{ot*LE39cJv9MAe7i0eobBB?5>c+{TOzJ zwUCG^uGujRmc%9Bx5yPW${?*YQKZCrcQu3WM{{ThkP860G#VRJOvUQraDMhN&5EA&(V>%GQsB5>%DF7a8e3W-+IX zl8w6SyuLMQ2x&ymgnr%i9EL_)czGFU15)!7zHPZ3JV4+rqv3H->28U;r?HxG_Z3N< z_u+i#j1Qb38U?pjns9j?#hustYSJzy0wx0Hd`#c`FTr?|8fugWL`5BdgGw?XccRe2 zBg|gjLtd#iARZEV&MY2>4%C``1ji_dNphmz0ucM1P`&{wN|8* z-?hk@YGWlAaiWs-URylHU*y8d=%gkl4pWk>H)3BZFx{Te%n!U&&Qp_z^lNg6VpG%E zi1=oF&s#U*Y?jUdH05Sl7)D&~mbu?T#}Q}lhL7l2*^AkBvE|uL-;a(N<>v|yB@Es? zdV%=?U4LOO@9FHT82V3?qi6KZe>LkzJ*wv%ey8{A($A&uZry$R58yx0cMScb>jYY) z^E=6*zSrPnW_lW(+xIT%`F)sMY5p#Zj{BZ|$Y)sk@z{?mGn4-S;r{?x zx7-mfUw!D+us<_04wCulV+lbw=`~zqTLUYN<|X zu}CUDmb4X_T2wvu0aNMCv-hh&K(rlMzsb#DQ56Ooe0^-~F-O{RBHS1koSN7tP=awD zA~VVcaPUX7RFYYIfM#d<0jD=q;aXeL>O0CXCT9ZMU2>+z4K6l#sjh@bdsI&8X*dKI zN8}5KVE#(=Qa!_h&5b$V3*{WlQG>@C%+bOs;O2K4Y4wHf6N=@@`C8V-rZrZ2lU#f< z9E9>zv;6VorKqVY3VCv~l~7R^pLnwSBvXviS3!vQ;%3YW4A&1$neFGJ zHpBmTiX@UF?PQ9va_!Wvts_=>$c_Xav|*THPRn*}BZZzWxmZyjJT}7g5{IW0Wqx@)_`QKX3J{Tu1aFyD;Wg zGt&Op)vN%w)8#GA?t3lo_9t96lD-yW+&@#I z_|BgLWO$IcNho=+))EGF1xUebfN--?185+TaX_Is z%-XiLI;V?fZCl_hr_XIYiW@|n2{H;Ih77wpB72Ve{H6W+##Y;cQY-&mpMBT99GtWb z%d+|V*16_9y`!4l;xuewr(WjO`r$o$3u?~nO4Ay6eoOV;D6OJR++qB|fZZZ4V=Rb3 z!QXG6zM6u#ijOVjlorys#d7?G5ZzoMrr7_{Wt;VQ+>@sxdR+QF1$4#0$?K1=6j+H< zZYII;X{c8j{FuNV*5tF~h1S++#d_x$9YUq0bN8Ps=M~P$i7+WQQ_=bs4a7ha32wru z6(yEq%rOFqjEbHK#kMYOS4w}ObkD!uUJ6kMCHJ6MNAlWLXZFo zH|VWL?1!Z=SA(s$ADgw--T5}yGImbtd#3W)TU=SX6BQfC5EifZX!2JWnFt|Yj2aSU zYE!UaKu|B$YA_$sSWPL%#!8}KXf{uRwIqhgHzCj(OA0mmDjJZ=sl65u^@&NvB3sp3 zOle4!2*;`gJiKb62?1ms)ojR=SB#KrbCUt&6$9C~Fl5OTk(EC( z>Pzqn`I+f-Ds%2x6Y@oUa=`7%sINbgF5$Ns#GVV-D*HT=o{5gJT_A6?LvZn7*=d=&^R4k(=w8OneqgtD()yd1WzRRlS8tg5!cS`vc8GJXp!P zbcn4s&J3lV8{ufKJ6R&HIG-v^^(lLwyEMd|Bxmwn$I3tnm8@-?Kcp*SxoE(88jy50J%^xF*FK#)8kT0m)!MxFM^Na13bRB}_m5uEcAReU(Fvv%F>nS#%lGq~#G1(PpOtK?oid$X-r#K}J zxuN-ewb6DHLLv<|L(IWd&h;~SQnOWn$4RcOqPKZF(dG+pm{o0AQPZfH$aLgIT?~qb zzv&-vQ@cUpbrylZ7*eQJKVM&`dQ)%KPRAF{hn0okl{P{s9e&L{TaPsM=p@3OsFHsL z(*?E~*lRM^Y^zPii&nfTBbY-WF7PPZYOa!$Ru9YLbcd;mK`4#KFbs*zhB2SyaZ3}{ zBSRpi#P$$C0cH&JTR2FRDrbo^YsuARi9MV?0d{{5p2^M$WKK$vV0{EJ)}uAk&v$_= z@Yhk5cw|%rMx+Afop@3h%;TImhsa0v{@h;vK7<>$`f$g{yRX-K%PO1B&_BQXk|_#l zAlyBE&F)``xMVTw;*Dw(b!2hW_9O8ilm#0j zZD0DCUMw?T6XTy}x;7i1@2#w2QKd->3qeDQO;}Y{swhR$B*fTDHDMD`t}0gyj6sAz z0Y2sdOT*%S5M$t4QGc;!ZYW+32=90AO zwQG)dlk&P(U;jM*cr^q1z0}pD$>~t`Y2vvpulo~=BM)z(eHqCZcBNEK+PJlp5p$B_ z*vR_i>;q2?$A@bVkz*$Yc8`N80c=QnkM0|_iG0@eM=J@P9;<-?yh|SfSZAL^TQl@A zhb0V$DBJq-5v@tAja4xK!BFB%(q+7Ok$u6|Q@Bb2B-b;csFo2=i;%B{r~tGaB3=fB zNV5H^7O8LDC5gyN|g&*7OH=^y}95_R?wpOk|o<|J?jAa*+l~m;-`%qvh zw@F0>z(rP#spxTY}8y)6m=((OF54J_2o0zLpet6C0w93Y(R-**5$nLqLXymOAclGPm_OE+GKDPee zxOYVG7qQvdaEfesK;atX$pTaW4}OG#Z!$z~c&XMjcy&6#%FQ)V{6F6JalF zj8sqs`gOprf5}i0=i}~97PJ?MI!_wU{sx^O_4N%DW)L-C0YfFpP~BH>ouK@i;6IbMBp{!Y&_${o|G=Rw(!D}49FwMdU6#^C9ysvk&puxn^K2Q zmS0hGz}GktJIxHoM_7~e!^1)N=%V4KCW%-$FDx;&F!R&Rv>uxp8%!drB-qeWU87!f zncHiaMA(4FN*aMHhqg;)J{*@C)VaLSk(`JmlU}o?5eRVL2>eI7IdL$U}+wlGUR0H%U z%iE_zj;}u5zGZFrSHhEh8%}hwiFt^-S<-0nb?;IZ?S^WyDnuwJ)gseNgdTgVtF>@k z8nkM%IKDVUrOksPvo~FOMP+-yxoL|kwD96s@K+pX>>@wG?5&bbpv0awd8*8 z{QHfJq>R(2S{&mmBbQv3j}o;8k_`X;{!RC4(3!Ou_eA#~r=5=PrEdeEJzo@t5lm(J z#}?#S{D{#auA0fUTtN|`5yrB3U8SN@u8Ot{R#rvDJSU8j10z7B5l|=^63GsOZq^^d zJwUU(xhRt^%-rOl#2BXn05>1ubxzP?N-or@Uz%{CX&_lqrKycJXaJf3hr-z6rwT0F>dqc0~}YP%|9mR)Tt z2+Pn4I?0csPb>8=JYSyb zTRZkA&pqs#!c%W^wm+hO`c2y4>y7deacAXCy9a`#><{-uqMdYV2!AKWMrE7qBe4Q# z1(bNPdDJd6Dn}+lsU{v=ZzLFbmy!EdY=8h=FjEP0_dua!N>R;_8I4I`ONJg*6k}FN ze>c9x$}l~?J}SguW8V#<=l*~DU8Rb@{7m|&v|$l2{w-|>d?)#PZG~wm6XWid)JHY< zw?pVE*5?`9kBR81Rnia$-RoIx1wa@yXA&xMaVBpaN~$t#T6&6#z2hV;%Lar17o_8x$`o)GJv|NE&$P$5l&2kCs`E#m7dMZCcw%b z@QefH1|&$SOfG~X+UC^};jL#9q?~f^XYYu<;@;ZR2LnGUz02JHZX^9xu?v|vaczqf z?w9uLs3Yx1$<(U%F6fhQNiQ}Y*SmyjQ{Wh!QDJ&qfUdoghJ;y;F^Qa<*FdpKyURTs zpt-W`3s7_W_9YX1ejZ{oEW-FD%d1%E;m-TYebc#~9%3m8s}A2q`m31X{* zbF-n*77)#_-CU<0<8Y-mBxKsFEx5nGee-$Ud@NF#bbX97&$|%QNWUFi8!HndgZjf` zjyq4Ni)66%(5MkoIhVz)49N}_hVZ0a~w0Brj#HKBt8H2^|#u~YFjEA9{AGV z;|xYRmGFNPGZRA}rzidVJ1X9%K0w_|)IY%S)wc%KrmYru_r5;D~)HBk5Hl_c_Q<}Yfu~SV^LeSt6u*5!x@L<$rn|1T7Lv1IC(YmzzX}29<3+&Q(hnZ zOaEZCFVKUBB9BuoaXo4kq?&1;VaJZ*z(`3-c?4#6@$IT^ItCxD61hHVm2SXnqrB68 z_VBSQ3fa8PCyw`>@#K>$w(hdpROu>+5lo8CP4&)bpM9q0RoV1aCEgj{$aQs+1cd4H zdW`L1$cdn@_X35;lT77#abweF9ec%}%xnQzk zUW`u$n)P<^btPw{&AUJB)YZi3cRiFW& zH3O@x)? z`9fuxNTGt8PA*VvZtq#{%?IBLC{#;3}t+ z3l4JC1XYj+crV-L1!Nn8ft-X1-T`p{Cr%YS-07iho_G&&F-lFJs2N>T@K@#0jO?2w zE4MJt)2Dv=_QQAmn(Xc0erPAnd{no+_;ht^>6f2U04b|2u z9YoP(`iMiE;Y zA=nH=6x&i}cwAgPTMf$c)1)G(bWU4xfaIC`fJ}B@{m)G%e5KStR9BWx3(9HgM{hvm+My*mHBX5811W$sf2?>(vdR3 zi~U_X9>GmoMDZ3yEf$7YXXYE8X!o~&={J+orPS&tX*W}qZw#lBmjg_B!6ZM0MQI2k zzl=kRZ0G7nE=W;>fy|lqV5qU7a#5AHp)#8utQKt~=AwG!<)JnF+xlqf1;=IR{9V}8 zTXE}EY};Izr_ViFOH__sR|@LI?bToM_prt1Oa+yHA5?sQ|NKucralH8zOqI4+}@=x z7ebZ~%00x8Y=&mcs)U{3Zk2f%#gG92Xx8{Rdl;S^HfR!8ke--HpUB+SJmfgWB*Uvd3vNflp-d0*ULdtr=zwDFlFASq-ZQNPoWqWTha}LW7LBkq){5 z>&+_A&1f7{=AfK3SqsIXU`nV=-jqbYY7K@PV0i*Fw~nff-VAsuD$t}}Eb(M{jS3Wt zT?u5rF~u_Q|ywgANgx=L8mVWpa!E_oM`n04SLBTZ28IpX33k$pH3ffR)!4;B%RZ zjtZ4?R}@}6e2|?Pq1<3}X5`b~+nc_8t-H>C_U!RzhsBL7Y=FU&N1yn6*H|h3Pku^F zKHn#hwVpm4}HN={)9coNv;}7TPp=BR+HS)kR2Jyd6!g&q!%SU5cj!H zQT`S=DstK-kPCKSbRv<}Ki&>&Uxk#}l0ul4Z!3ji{uTYTfo%;r`UZ7nc3n!rD*=a^ zw95|XQMz!7_mb6u`8yeAF;n}~(25}@1Zw$N!Bfw_n;&;0C znI;(p__{#EIsUS803=t&Bp+x4_%gZ5W4elf$||KCQlnF<) zJpg!tav}@5K~tb&`X4eZ1@uA3ck@mLk_&p(3JO))7WDx@F@rYbjelybaJ=KH(X-!o z|0r>h3}=6LJZGMB^URui$^7THFLxln7RK>E7q6)bDTIM|Y8*-@@vE|Z17I=yaty7fCjO(?YoAQz-*Rrdv?8D|!U@5q&r-5ubF~;v$={0eX zSZl{r7LVACaIjh?m1QKOsQ~A0&du^b3@4#&{+)N3BO(pSe%j?*doSzKFT*Y2x{~{h z*})Q{pw{vRg;WqCG4tqq1){Q|yPlO}R*ENH6F#dsI-Wd=;7X*z26LeX;bQHpVTWeS zb>vi{n1PP$qvKpE1y=N+y$kB8KlxAb-u$`pOeo|%%}h#jcdkx0IF5L}a;~SgwZG=M zk0t#tXB)fs^uH4gPF%26pX^4xYpu}qVvHEZ*G`u+LCd5+^G=OoX4J@@s#Uhns9@6`%|64tOlWm#~?72nAk1AF-~ z{5BkS9=vZr+eIV$G9>gH*V8t8eWs#3!>eQfXi-KmCbz_QDr?kGtl9U`KH$Zzl(VXZ z6W37CLJfE2kgTf6{Ji^cHnw{T`K)t^k8r#^U*4zH)$P@PNzcX(^~5WQwgTm`$^ths zNP6ktEd~7GM}!E1q}Tslh5$7Rs9}|3^c=C8d5X0Wrg$W?B@Nc?h6gWXGn)ews<$ND zt&?c3nS!)sJm^(~6%R}&V>6M`8z=8u(+ZD{Vs!Kw@W;Z3>1wky^;iu#(ySMhB2ta~ zQNm#RR1?N=MZ;b4yrme%(Agi<_z|J_L?{qp3Dr}O0y&C^I{!iFz!GxSNGU#%=zGV% z!3DBlwnr=9FfSv|NR$VVX(%~roOH48L~*0-oQjdInmK|lPm`u=ZEeSa*0#taiRzxG z1}s)E#IS)Xg1{A5C}b=$gXn=37#|EJe~*~t6J|#sVHveH9|ZvciH{!f46yLr%e+`> z?4QeED>9mPh(3_N?_vKlzN;TAtVZX=NcTmw?|-V~G^*ogG@IuuZ^Z!vxhD>(gTx$1 z&+#cTDC9n;M@FP6rco~(o{CEjjhIJOd>W|Pnm1c~aLB&J{BXK%0;7~fCKO7w5cNF+gt#(Q56Ts!gV@&i-_ z&9Y*l24jW`({j0%6aUazoNB5%e{Vp>4=zC^SPK^&05E`Al`>K@9vE1qKsv}{TX0xd z?=$6bF5sq|aQ7*ZhXFfp1x*D-Rt0t>J}7Z0q)J`>6k7q4PqvMwBnj9kVG zyn!r|;F4K^1mWalWBCHOQ z`P`%)xz(o4m0J=EM%MXu&h`V53!<^J9|!D}$A{l%@+9IB=g;xtXb^+gpu6@7h!mDO zrCq8%&H6saZxSHsZ&XppE+qFq@)qu>Xun|e)z+a&I87)UlaG47>-;T?RT>BA(oM*- z6u&sLkw%qO(o+NB1cFCAjCpX7;XM`!>WCSEvABskjbXOY?Tir88rkq;gbp%fIm3WU;Q!q!H^b+XLTQcO4) zC06gJp!sr3GT}aFovDo$Xqm1HEerT?Bto0E+^tvORQx2++JsBesCKWLuz|3O&o5AW zG=fXv_0y>@g?rim{rGbSO5ue0oPQST$8w4U-OSAq)^+n5pBR9jn?}D$k-+B(_yDJJ z`d~zuKL^JD2j7hq!1YxPE7{?5LY7u3utZrJt*FuYJ5aiVQd^RwSFb(aCDX$fhi}xj zhF6bm$_Co+GZ}0Lj8ymjlz0C`lSPC7MzRsbXbx;3QXg$Iq+5 zF8=LaGGXo=Nayfx7JI5+e5Rqv>QL{Yz%YZ*rOXq?Vmnn2OM&U}zuT&g`XgF2)ml{@ z8}`QvqB1^yf$-V@48P9;Q>|HUF^9m!NceyjEyNz+h{kJeAjk~05B8Do9TGE?jfDol zkREv|0bSxX+(>o02fBnLAzng{-QTI>u_IXT!&sCJX*U&EU@@Xv41K={>h9}=WhcE- zho1UArBdM4OpYrkq@!?Pw;jz&h7A|UT8U@=ZzbjZ?>9*H`#V|S3Wp{{kTiK11c-!$ zHOSf`ygB|mkmvEW!nEahv$>gRA)Qu$Sl%6oVe!pm(}lEl^_0hJj*q!5GPys0^y>wV zYM%sT_cd{<9=ZFvec!yv-1^{{rsG}IB<+{V2BM?~y$iu5`ljjm zf)@lT0)bd%?d4t-vnEF&?Z244*QWtovRDwy0}u{qo*D(rGwcWNvtn_dl6MW*Bx`Vo zci@3QOks)DQ#fqzb}aizh4n~{`64TS+v|Bk4SzjX@OaKJZ}=rknouN=&Q&O{$iC2q z*Gm*khtrVSbw~_}?IU*mANLVfs=9 z&B5f9LQ;x2UN3oTtgIKL`xP|85W{8AN;LYo>lbV1SPcR`Sebc~E2G$A+NKgf%bDrd z;brAZMKiRsBW0&F?s@A{>;3FZu)0^sKhVM z+{k9&!ubxwaU{Nnsa*E+k|bJRNJzk>@y4UyE#{d}$6l(W^72Ao?rhQ?^;$J-dOFmD zTMcgKwa^0RY*tg7pN(~HMlk#Whdr@>D7J4=TdfK&Cq;-e3F!|jDRK`Aj2M)aSlF2JW2-yO%7myi?4{wB709B%z7WCSn&RvVDw+2+bt+f zqlng-plbcx3}uN;7DR%TY}Cmp5`_h2Ap7NQoxUhY`o<|Hk(if$m<9dYVg3IjkuanS zgdYOP=y4$&u^@I@!jOkpk`fIO3e3G5sACX6rr3OPU}h#ffIDboDp-xq^sC$h>DiMp zi%)eaoo&=JZ*N3PrQ>yDpGs8(`!+YLoV2Tk_I{6%Cg9skD;$`x%OyEKUdk>^e&`8l z9i6!p(CwYRH0MLa28kH4{wiv;zYdnh@TW)~*o7CSla^o#RsOpJskG|!%^lba`C(7`Q9>4+QM|A$<$iWZ==XC`4^De3lDy$idaXE!F5ee!nCU%zUPrsn5#X^e zG2o0bc%dRO+xs%8rF=Jq1dvxS2Th7&DR>HWqi7$`fqT@yRPCKSLs|x)w2ej&g9hO;1OD&>87;YbMhykF)I)?=5(fh ztdq>XNx4Qd-s~8iY##0cMqY^64{}=53=2zZ#wV%dzNx9lCFY~0{S*tLuzpWfh;*99 z$F2PvIp!E11Bq0|?a_+wGog*t3W0?=sN%pS;Cj-Vvi0ov2zk6m8$0V)&TYMUzT)f6 z9_XP9^YRo^Ka9$xrN``V3~%q$gieZ#i3}^JyVDK3Bu$#TknV0ry##nI20$<KI{6 zwmtd@jGcnXXs@fPx93**XO3m8x4v?OF}6>QMh}b}sTD6#oY1Rs-&$${>m5qa$0Dmf zT7M7~acI+-P3Q-#>_f({i+|?bk`nJPIe9!ZCzsuy6T5gV4BxXNq+~Gqccj3#f7MV0uh@i z{cc;(3n|p|ln%5@9vK4WW-oXTvX=#ozs5drNJ>fzz{MJl;hTVw2f|$AbRYjQ(WwcF zLUi^M-#*l4NtD*IWbl?M){Q1n140x0H;m<9?fC!c8VJ~1AeyZ;=LHV%UNY03@7kuf4!G_|jj}deAt7ne~Q!b#CTR=j`P0md0;! zTm^3Uxw#D^j_6BQ70BEV);w7p45MmG7P48C0CuecwFR z(JJf3JnlqOP351e>@x-{Fcpu#%~Y#cjNZMhf}iUlr^?TCFdwCS9N|85z0cCvgL)Gr zMVd*Te?>GR)h((og?Z|_5AN5EZe_-isdk=F@3q&8 z$S0x^S|=41)g;g zs#)kJjeyKUdH@3ri9(*zlW;8(P-w_z92`|J`Iqe(rHXw1lP00zeBDf#>ZgzI=iZ8M z-EO{kR zt=OdeFn2f3Df8Qu|8SW=g@DZ(A}G>VTFRCoxXG9K$fE1STI02#bkf8$Uq#LGf|NRH z?YMvL^F;n-iDR@z==gA=b*Nh4{KE{#&5pzb%RDd}zOG^A1DvCPnS|!Tv6J^(Zt8VA z^g8SKi>JGRLRu5%rR(OR+stnc@4qE9=aaIGT3nLCAIr|ejQ9ecrrhE*>oik0{6YjDlfAGV%0*>eDLREGV%_a6 zghH$F4uJy~bscx0=~gDgvr#jSqaVA6&82ZzXV)ztHL6W=CS;TbXK)<>JLVQ0|muhD{dlkV{WsHgWed}+-mu^=Z7`l1PZpQ7vDx|zR_tPI>U@K!K9VRHKPOX! z^{t)+MUtu{mk+a^j@PV#y24%5U6kuba7Cse@BMgqtfV%>e2B_O>wJUwj^HCU9{Zd7VE|A`pO{RYJjC0ShJs@}LHm2fI6qhF8P5lZY)xr+x z_IfF`hla81RZu6eM+fnDQtku&=)G4!%K^jyX)2sNQis?M(`9iJY}|n+B5CS8_jC%w z^Dl+hpDVX>YHfo9%^7FnDlV#DoG@?EYi;~swk49dmjVv{w#-q=s&TtAc+@pBhHgF)JARfwa9jqs{Izb!1T+5ZX`20R5$1-yP_RdDzcC_x(|& znj@g8Gm?h^<9OV}3SxF*`2k?CTA+)ab=CTZ<+9Y&0(f_A@;7`-BulsDD}-U!ERKdH z08vr&THzSU>Eq3k8_6N#JDfngME?{~2@cxYN{1wX53xzJ1+-G=M3NgZq=(p=I^-RJ zHD}T+4WtKm2`4p!jfxkfFTnxkqG@CWW~~s%50kp&iM-WD8-CH_O%TiR)ACk#`{9I}rZtv3qXh=W8{8w&A`D z8Mf`Zudz4JaH-?yE@wj?#s-Ro_>F~4l_b;d%F4&J;5Fij%nGHVX{<}NjpLLpZ1dV; z(AQcG(C+@fYIRZ!rmia9-iIKUY*FnP(#Lsfi*VVknKfW}t&C{o)kNV`tH{ts$!vs7({B?7%_L3QDnMIlLu zdER>Qcvs(nrfhmE@IU)?Ca)(-ARmbTH6@BJPjJYUK zPdjAUCDy`eLFE0i{{R_KewV5n3qS!yJisG*84>f2{7I!r8JU!Rt=K;81?K1(iHmkp zSw~yawB$ig6`~N$sT;gM^V=2R22(%9|7qIpjpXUyilnWEOibypotLt5+Y(19kLmdt zsAwS$bU{>vU!t6JYWTl-zKm0c?>&)?XD@n_U?k~Te8^Tu=Ci)HID0gwZe9`Tnu|B1 z!eyn?vGcCCblRdL<;c!#ZaJI5On4O9*wzCP6ow%sLLzm6(UvC8J5a>FAef6+Nytc7 z1@oUL#%1gIFmUtxqF$bwR<8wd4tQQw-GP#>CRX%=BvwW+HzqTu9}wppQU{mPIx}}5 zQg@0}tjvDEAKhM8gk+3J5ArHg2~)3mUb)l3Eu(bc;$5~$IoTB8XPVmn{X*sUtV!LT zBu<&(WxO+Qi=&|1sl~bE*UxZ-1=+CG(aNn(88%;RFD2%Li>-MNl0c~?`hLpcaJy!d z?5)rGcB5m9$g$b@XxHll%k}<(!qq!Z<>rdvAQF*2PjHKr1Gwm%y<6f@CHbBF?46%C=sV$E! zkw=7~a|PC#fW0oVCpu18f>>yLTy+xgAbg7NSDKx1brHe>DMuws3o5KufKAv4BnP|( z4Gr98PHJ(?pgYug@{H|(ZC>oH``60lzLCoPqv5N%1WtjlgX=AG<)DTbaHw)nI(x_a zgChULUGjHcJTMe7&J(J7k=YNiA=!Hu{sz7x%u_$v(*%^11y+W#_NW$rJH~KMC|+QD zn;ec5{Zr#LOQKVw6_kyV|0Gof$_NFj8|Wh=fTx`-?mjM5Qih=&aB*$#oMB7vK=2Fa zGa19fNY1QIDS4iQZk-eR>s+bP=}X}@35%w~K)E1?h%o1%O#(hwR<4>-cPd^%S$`rhnfwNdAdiID6DALG&RENp5yGDDMF&9RTg7E@9- zuVjv-m)5p3B?DDT9bvxyHj(pxgAz`q6H1O`&}%BJqrA^zRF;Q|2BwyCv!qSwtc?VR zdtU#3uX&pj@2I90fXV77cwF(^wGF=SL=4q+QnmD zuU>YKn3AA|@?N71F0gNmbQam$^TY1jva}<~Yu~*%N*E6tSoI^0(QHTmlxGtM^fk=l zYDlA@#6D=};!3zsp`y3)Tir6czx1b!-f;Eo@mrsbf+?M{EmqFBJJ5bj^~rD2lU{1nRAd=7`+X)t>Rv)tpy-;%K%G#`gV9cV`$3|daJYiKl;)dGnN zFk;wtJhdtGrNQVWUwy=FVntJ>ew5*I60%j<2O z4|f8@qYf&3Ts&%je43cqp}AsW!Ms8XzJvk#$MZsPrb(6i@04P|bYd{RFK@7I>rl98 z!KQ~Z0cE+h=6_(%-bSekF|VgT{FrT4L#0P`lJ=im@h<)No*=grEr6$M^oR-Ky2qus zePl&Up|p@YFP>^bTJO0FGpx+YG#ruol<@lbb+wfZ7_x<)FLEEF8T&HNZOfoWEMWg7 z)lr8HJ4sEYyUVMGN7eIGl`F9T{gyfSh-Xt%e`~mEFyhuar%dZQ7MUiOBgm61Svj(o zo$|J0I%Re(Ywh|OXA`EXV<{5U<(q&2JyZudlX$8j@4cUqBx0*$akHRAuNgy#Ln~v+ zIG$J`o^}+dS}7=NrZ(Bk3=Mm8d9$&~vW_PLa9iTw%dkCTKIM#~+Lcrpy`6ImN(3q{ zAD}IYSqoaGnXC^oTaC~8OV*O^`Z5N7-{vHIxJ|gc*NH!Uns~mJIVpF^KN7qd zSnX~1JJ33-<*(&3vqe;x;qI-v>A1ra7GoLdQ8#;g)uLD-!Gc|lM%q{QuR_AX))WdX zxZy%{06_^8BLVd!GqJ@#!ePz{0XIXO^OK!maP@cssPOL#WN?I3S2diuRu z{1|Gjf6``)s(i99`ZKjYvF&{=bhV)~bTJv0pn0S&GA>X3E`<$f$DstvplcQi$jb+L z>59pG$-^a&E=Et1CM4s`rOP9!6J$Ek>moZBn8$DXue|lfEw_zHE~VrfkuQUWz3f({ z6jZWGcRcJ*1#e28=^UM2kN)x_5V!5#(-_i;+ookO-&q@_Eb`j=c_zl<<**0~O7cob zADs@6NEto&Fa8bsf!J+){75f)$=5dre>FHta^@oUvuMCChIUiwED3iuzrkHfK2~TZ zyFb0;2@$mT;HWWQ(Hm)F<|A3&dzpgRz`p}c){U-O96)Y@zmHloq!|3VQG#0c$NL<~ ziwh3xE!`Mk*S1QfGs+`l01v%ZQhS{(=Iu>?z)YUm1#D(ODyPpOu@P^mUhnl!BcZ=| zdjt<&lA~%_3$}%$pH{-5)Y{+grCI;i!GLhPAD_PndtG~#5$)ID-}wB&G?lb z_md%#xCYLtUR!^JPNXqlHRrT4?5B{TTc}<1J`W*v?e{(>-uixP^w@wdUtfk6HN;kn zwlT)CAk(Re)Ci?40h$M9C6D?zY@GRIzqrE%D9lUx*`S4rL#lhYtN6BA6vBvtGovUw zCB+N>535m0rTqm-e3@$Ghxxwx4vqfFkYQ3dI@L6Y+?~zMrB`(^7*DO>8Q>ySB42xf zRF|{z1d>e?-U&Hrr4Gkmz+xxF=kpeNie|Ucb*$;shZf!mZL0{5CFjp}B~fsi78X8CHRSJ?D1;vPmZ==rMaL| z8P}aXZn2srH&jCPMOsB?omzTO-yb;2mJvRqJ$iBn5{?YOEZ2Quc#RCGGFVu{c$BgZ zbM&7tj2_8oMRo9nQtlJ5jYW1~pA8VR7N9i4dp^9Hc#`t58V=$4+8?z2MA7zVBVPCM zqtHx#4P`gm_p!jt0D`ckOR7f9G-aE7nQ1C;PD-@lR|5;-ymyifl?-Ne?z}(DafXoj!HP7YAPv4yeQ~;jwtGSm+awp{_1|2tw5nY7$<`h3KWP z71gV3R=va!_*kB54w_WO7UqYaTO4MdSO|D|o||+uy_FCNv{8Q^bkOm`NV=`>hJea4 z{!wrIqj<3iH7_+UY0k3C%uv^^t2jAn(FIQ_7iNI^E+SAE3G9*x$UiovpKzea&SIV zrRseHKiySnh#J7qKj~)F)mcj#X;D>~5 zFTMVp$`rZ!&R!T!tz2EmNc61^GL)ao=-wvOx@`HS?Ul7(!B4bdqgl5JPuF_Kj%_f+ z(q7BQsF!w={U&uCT|*e2Ckb6MH1~6qY<~jj)qs=hG4!*HJDxFNmdiaJ9OW-`YRQbT8FhM_ zRIz9H$#GotK%huO8sPpyJg16k9(oq_K}&1j(}fa)jXXHUjeaKT6eGN7jiAzs(R?Q8 zv7iqXl^}p9Sk;b;^7p$#W@D6{N_O?Bdh2nFiRJV_R8bUx_c8gzK#Y!_4aFoWSg9yO zovEwDzQ}TJTvr}8{E|(oqxI2W2T#v(KUxRQ-@F-%4kP)u)ZcF^r!6gi&KMSP@YkDb1D`OoS`6sTG2{R!!d1kSxy|%_ef+^N4`5~3O zxT^vl8vzQ-q|fwQmE(Hve>yWOl&P+hn&GyQe-kztmXv~5MbdTH<5-l?>zAN5 z_tWG6vy9Hv%Fpef^>ndg>p2XSyWFL=sE_d|!AT%8JDtw*%?v|(-_-{xTRwdymEpmy zp9@A8FC#E{>$eG>pMEh0%$ye@RW2oZgP9Sl>#?WRdMh*Al0aO|`b|Oz_GpXW=|(_j zi60%G>+~ab`hsm?>_j^)&ta?Hu%WtFCi)IU8L4B~!93}-#jKNcqn$8xsS@E=t>@_4 z$tx!1j$962ySj~7HX~5g9pv~MrxolVcF_JkA#l)peKvLcNL;!C{cM#fX|KCGQic)j z+!8Q*TU14?((iN<_awX#EM9|vrXtwxMXM?(vdp+edlL(FsM03RB}$Ner@dDL!efl1 zv=u+~si`t+w^k=T=Z*;?%Ti$~G-7b545k|4y&TE_PPoWN1#J}2zV6?Q1Rt5MOV<8O z*2gQ|h5ux0)+^({7u&Pr)(Q|9+ff~az5HX5p)Ri^u%f<^Joy*OCE%W-?0BQ`a`SIJnrtDIr1%@!ErwOqJ{5tJ8mTq5TlP)?U?v|caEuY(TSC5*lVwp-ysw}fUazaZ zG*9Mh?RlZhM?AJ63FL0HB^WTNA`R$heBs&HmIMM@M)hP3&hMin2aV@qI`rmbi5xQU zZ1B0b?l*-RLQ2|qggB@;Bee@JNiFY)OXUXm-`HlYx)y&i&A*E^8JnFLh|D;*FqF$z zxFdmyR)vOEg-MJ%{4p{-$`QOkv-0O%i760~E(*@$>5O zBZxw~8;fdM#6>RE?B=qxR}&0kt~j?ub{-!Ef7`0TUZn|>l2tvgG)bOMmPCgAA!I8U z@2uz$`+a4bF4KUzDUw-Lncvyc~-{0^E zpY|oru;r#IoyP1W8&lDvF}L zB<$$UguFY7o%^`y>hO`FAa>?q=zhnGTCZ6rE^m4oV<9W9sk;o*Lx5#2xsh- zLmD!ko!jO~3MoQ50|IE0 zYVCh1sj+2CIRA@2>B;CQECv+hj7)3ILB&G7fjS^Ye;9Z5$)uoi^Z`&=zQF!hYA9m< zS?blf^l6lU37dV19yE$w&xnpx9;iq|Nom%Q+1(KP0{ISNAm0ul)CYG9AkSp*6&iz? z#!+N((IL60G6+*+YYbT-!m#_1?66AmM{=`-N;pZpjZ0bCEAbAe5BLSkRTtd8yu3g1 zpH0e(1m=h7zQTjmDQ}tFq_pLkh=nZGqq6d@rTQVww-xnoAZCuPFc!I;#TZz`I zeZaxnsH^D2PBt|`Wh+g0x7s?5ojTZ0s2U)!0P`a#54OEBXghu44LrJ#7;6M$CWr(I>msU zV0wdG&eXp@=_hU2jKDmj356VULrwm$ZcuI3=eYb zE{<)Q(#{E5F%VepnDPEDRzGaF4USV;>iHTQY?bh-~}QJhUed zq4W@MNit*2TC%&26Q=;u@lk_fO3|DlkC)o*arI>A%1#O|j|<1+-F6GaqzJl+lED#@ z4(rqOax3%%4z3W^tf+;NQi5&0#`SFq;6)Zq1-4x2S-_e>0Oaz3W~U&5{dD>;buKzvb?r#i-H@UIW&3)Y#Kw3U*(E~d^09v}{dBBQ zu_q(brbPL@u}y}No2~Kvn8t-de0R?_TO(lwGR_pXqIOK_wh!H9Z0)*7|MwH~zMYtR z8DD+Qq$)FKs{|`9K1k_pbh=Aoc1Uy$xH7G`{d~Rp0l4y~+mCZ89&q%gad2c`|NS_&-vbwSTQJUJz1Kdz7oN5g#mi8n_lI9WTx{_$CZ$|80&-dU^T5-cwE-NQW5Ind%k zC!9I~W@KNhexJ~3! zDb+noM(MpbJs9EIP6>GayIj)*(Y1)Qtaxl zUC~idMLUpn2c)UO7@Oq2MZ3U**4!=>m7F!{%{vh>rua|TKiFO}s_Zdw4Q{@z8z{=D z+RFJ6P|YuGGh`;*k0GnJSvp>e@&YnxmR~IrWX9_$SP|d1FXMZRzIuGBY_oRTd#ti> zB60_si(cN4IViGp@(*V_+xX4vg+P)}0H(7KfNi-LunJ+7G+S^f8IxmdT=%bpe4^+Z zX)%tt12uJ7Lr0t>9_S8!)@Ofqhj&QUQD1T0r2Z(n6mwW_7 z%7|!w>M@DTNRQ(Jn?(aQHmEp>T%$h1aJEwB!8-!Lu|x#ClOc51OTkluN&`wyeMAHO zV*P2PzF9M4amm1Z@2}$K(^k<08m_>TYPPeK_3X=;jh&Nb3)S02fpOP%eedgGik=S; zcDx4_U}WrQidwF8v&36EgOO2TPLJ7bw>yx%a}aw1vs6itKo+TceNcM@UtUHEbu^Yq zF_5a6o;qxoP0xYa*0&9ke>EITw!V1ZyG6pHh4@B$*1$z)X&rqsGxzbr3mj1EL_IGy zSoaBR2m#J2u*_GgGSXbova^A_SLdhT6Q%`2Yzn}0fOiCl6}(>_Dh#B3 z@{zZISp`d!!=OdywJ)&U0qpjX(eeHIu%R(Jhm5WYVP#{0?zAC?!?}cB;GMH6!n8XC zA?yGtmU%yk?y(I-`MseLeP}mD+_Q(iF-iPE9rAMsNgaw)9Ugd$c2lScX1XqnKzFj? z$`o;VrjoTnYLGE~s;xee`{R=P4)h|5$y9AWb|dtLM$W0-RpFm4J}g1GkgYIKqjOe)h#U%Pc~M}Dn3yQpdU&voVoeq6M_DVK40NwB;BaA(uO8aZ{Ex>Bq)Ln9;ncYxw= z{V2!wi*6?0@U5W0@uIhm{_GgLhSQfwpV_Is;j(bMb&aoR!pj&Mc5FHS21(t@RQIdV zt{r#G_naMg0Z-Vh7A6X>pD<_oRW zWX1-ixit-gH?4t6e@kcE=D~F{=}OcljdvggahtF|w7C{PO!oWz!qLC-_k_xr&{_2L zGGa%$wM;iXr)s_Kfib&6n|?db&QQkht9yJNnil*Wc)6)~IuV+R?rMRxXMFBW;qJAY z_@wzVKtA`2;M)K|B3oyEfkK(wfyOdU!w+{c-Da4NB22KsJ>eai=6yjlbOWvHTow2* z4Wnn_?+tnLalmZ2iv5FOLPdJ3v|d|@xCmB#@wK$EnQ}m5%8fN|w0wZTi`S9E{`Qv+ zJJRf{5@Gu7x_;;VMeD2| zfoDG>>^fJsJy2RXVtysSBv))On(ZOI^su9)K$ina^v8vOHpT#E2ajE^m6>jC_w$>0 z^QpoEk96Umy`QBegf;XgBo~a!hRgiws*DgR8Vk(=oM11Lz+2@^I;o4t{le)+Y*h1U z{G^vQjXFHb$bi5{gNZW@D&ke~SAY$1G))D_c@pK}zE^t`Y;%!uSzT_l*ol8EeFw@@ zzJOl*D%c?wa+1GyRv%>5YhCLM%D2Xrs0so}4w{L4c!Qc1!U}bKKtk%jG9hP~!X!Dn zk?^EZWZ(`Yax4y;JF=V#OnAxDZxF*-0`viZhGX_&`(*a?gXGtExWj(4IhWC`qfc|m|Pn}C8EaOp%=0_lu2OPi;6L8iSEC6_lswU+6T!|>+Q3}2wZD$}ob~&SS>0E^KuOrF;8O3I zpqx^Wau(bKoI(1yUbMgnn#n~L7D^M(R7xK&UY#k)sN*kwZ@cG;UBVKN?9b_== zwu81lX+tL;7P$+($nQ@M=nCvRr}ql&Qs)En57kHwc-(9?UpF!tHLhk|xYDk!cgqD) zCH)K%o4*p58GK@)+#W8Fta}6R$U)JD46<6ACdNdkzaQWtm*Bvi{L3W_0BdyiV2H#& z0Y)7-ukPRRV~Pa>bwK0w?qeD*n##^PCLk%#rHC*CsPhHFdfy=qX|_N~j}xK|J7tyQT6HJh`gb%lqlAr`Mv&llUP&bo#5tx2(23 z;#_t;SGHSh-Ygva%)eG!>XTE+ikZCD6FNc z@%McAOcAzPw;%7m`lQw9bYfbr6S=|rfn%VrrQ=ejJho1o!WF~+X~rq$tnm0u+JmXe zby>n-c4kl5K}l!-_~FR~ogq&_8QZ*ZdpxFVt;sB1X}`6|hR+nM;hw&W*po4rVs9ON zw9O%cu=0yu_;j;AI#fY)!5=jiSPOo?oBGk=$5~XJ`Od;9^UMBF4Vt%2Jk^E2C;v(^ z3!cR!BL{hl$_KRgyA9d&%qXN&M4KrvA4(l9;{2|RPlAU=Az$29By3tH57w&JMstqY z8+96elhqs4Q8h}GdVw-J+aHPzuji~XTjMS*3eQw>szxQjrM#~P`n{&4cW>>Fyd_%J zGS;%u-CWOi9mNnx&jgRlfx^}!%pu1q)WW0NsHE`o-+c~|eVz3cs?{TZowD9-=Kn-S zOZyH}3+_O)XG%eWyNX{DEKjNVubOT&IX z^Oj`T(Y?2Reb=pNh}*oRwO(qp=xwXMR+Ugri3$e#BetlzDOVDaRaT{4X-p{YoL!Om zQ17(^Lp6IVxx!H2HqVKasWAij23y-5XfRAuI5KH~P&v8f=@IueTqM{I!k<>zPpG9( zD>`|id2v|&2c_jD<&rVAbo!`MperX54W||FP@iy5*c_o4Ovfmj&GmhiAu`BH-zo?$ zuaOIIPYJS0So3=^&FfREcKi6&_WABK?aazR<_+BlolwJC*~aKt{Xx$L-C#W8hAc-# zb$m4&(c7tW=r3`rY;-!jVz?^sbtL;@YBjoap3UN$`1WIW?DuaE=b1C9NpfXzW(`A% zu(Mqc@OmTw-&y?kF5HONz$rI^&i>iO9d<$cIM4a9{|9r3d$gfK9*Q8B@UGkLLfx zf?LK{k1fs@iO)?W?8H`MvK!%A?S0Fe*pvw=eVsiK4`0l=#UqP+1Zge`8bb`KS;``2 zRi_3SvJ_K8M+Bx}0snm@eI67wnaix-+aRigk*U7biP=8?65n^ll$KnwGD>0k$Y#yr zQEI|SHNm#^l9vD+2cC~y?_t^>)@=FR{?lTpu5xjuz@zuFm^Hsm{6W~R+w;^9loXW_ zU>k@$t}yRR2)nJEMl%=PA_oM_YF`9shqmwZVM?hPU{-*+#Lhv%5V64(`(bG(Z^UJR zZsBV#VID4FDs`AN!oRQdc(7$>ZI525g@-QkaVi7a340Hgf~##5VPndM-UhMrsFAvHrTX&>AiLd|hYc zv)OYpDW%Vm{}Ef81ZqhZL)E_PuU^M~!IrG-J(YuLkw(xAjZ4NP{{_s-rba36#WKg7 z`FqTT`N|`^UVkQv?3}-ehU-(C#pDHcqh(*mTx43OzWmWfGGFv)UuAAWN;han$)xKQnba!6jFRKp|E4b8CNWG+dyJ=d?xUv&6B> z*=^y&z5AUFI6~38?Es zpzYCg0|J}wK1nc@c7`n#&*J&^emW_Q{zEP$IVMrlY8}^g&0{W@jS43$$1M$A<)4QB zy;g~l4lw)=yO$f#gwTttKhF;%>Wv9+k8WFqNl3%8Qoi_@G}z2Gynyt(7jltL_vzaf zy-a_9{Wu}x;WJ^&s78$zJAikK5}dMOTlTAOhCd07aI)J@i2SA}NFKuazQhk>SA2q=a7WGj@6L%okn;3R?C3PI6vu^t4-@tJV z+XBe4F$`NmB-qL=ye&~TOxuCFtX$ka(@pEp=58N2B}iZ;)v2b2jN@V=uW+W77lOk z8$Goi@BFh}%WI)pWpn*dPKiC$TFn*RxB4x;w8C!a_IEmyCSYzxA1s}idCOtTbDoyG zwl}4#-BuDTs}1dV3xplj)4Y4jX_Fgl^!;nuu>T97@tW4`HH)(yU++n){}iN>l98~` zHNL%~qgjr5oGZdc$k<$VtwNR1_wu=nHOhcmzUCsC@^-j**O8~L&@+p~v|Qt7=95<+ zxkzbT)>M@XP|VBZBeq>A2veCCor*OU2O3fGAQ1>`a4NF%LLrvoa4QQaf#fF|yxCn{ zAX`$DMxDyo>i4)Pv!Lv>1eXwHL5iR}klSht_)w7JLMcKE62kIvPKDGEL=qJIk(sMR zK5-$d3JMteJH`P41@9%Q+p4w*{>AQ@BMPaJ?ts5`{C_N+2RzmP_s2i?8p+DIZr9#i zBb$(wy>lf|%H^7Mm6alNZLYmD%9f&VNu-c{U7HFiG74$gyZ_tw_y0U_uW{Ya{oM0D z@AE$Ib6!us7xqIUcfL8Y4v7TK4%VuxX1oCVzX+)^FA^Jm{i*pg?=56&c748olGt8%B;58 zr~7$thwx+fp6M;Q(uukPrRha8(yDH}yMzDHx0=rEB8O34o`-*4QO-uwY^cm>CiuGK zKc3*6tPOClAivu3KIliB2{@bd_%`VHF#kYCeD8`PU*nbU=#f{(eA-YTtGV14Y?rUY zQvzJGr5@0Vej1N;Zdv-s_ZV}xammW~g~`1$`4svsQ7|#OC$GC-wRm1T`$pX)6gB1+ z()lK5V$RN1`F4}t2B~l7`%+M&KkMxP`&;g}c;u7mm3#{XWlyNN7_V=+~|;ivqUp>hV8r3$NA!pJbB$%qOXqv7cw+S8LYjFGU*LqdA;iRF)nM zaA8DGnUf`t zESA@*>|Otu?W-LeUKGu(RF@8rO~IHSfj$r`=u%X=9=yF%kO1VjKtaWaipt_MQFV`a z@y#4*yy2pWATL+-p0*E&P@JU)>K}FIKqhhGZO61bj$~D5#vs)Bu*j`B!UsY(pY}Mz z>LHzRtdS34fNGCWH=%aIUI6aa;QDNcNhMDva4V9VvjCU?Tso{C1vynX~g-JL3zmN_x& zypE^RE66mzek=`1nkJY1*tv-5d)v@jU9=u}FLmeWx_G=&@z6(a-`j~w&WJqY;K*~6 zUFw=cEzu8$TO#z`&rhuCDNv203T|@h0C7AQDwrLl)R%liyhh@pD9c!Cxk1CpoWg67 zCSc1@*uW0R>7j+fyPOf6FTt!HECdKPOw!T>QTXcpSWfim#v6tKNHn*E_80CP%va4A zR#o0C$GYO)dykr<9>1yoQ}E^5U~n$agI|kA#tZREB>T3iGH&I^d16{OdQD^wxLZq# zJ&^KMo?EV6)n8~!mdSa}{ia5<8TdfSc7Q3p8WO)y}hI(iE903CN zGVUBpw|c0I^}>j*rw=;&M(4ytmgRKi2knh?@iYgCtn`o_shb-l8g+xfjqQ)z#m zo8XXZ-1%8-VEt=!$hoeZ>uNF~M;5^!{o>8C#dv0%KbQsRS0gW&k8JN%+XPvg9>vI1 zTO?nO>C4Nwa=&)qZe+Gw>(KO3TxZYExr7I}HT&V;xny!;$v6*mgP{(Gxy@BkI{LyQCm|#AQev@+ zvY`Hb3&i>N_R=59Z4eh(%@s@rqB(0d0y)~ReXQoq-7Rhll=SymUK>K*Cav%eR`YIdY_X&p?&da=4HOz!t-k^WH#M+DBYX~s!^ur~ z>1WE#xi>_<=aTanb@5RI3xjfx^G^Epx=5r2V_^V9?F#F(QHr&Kpevj`MTZR~7b`bz zlY2I<{evzaB;Ls>i!n*FIvROiv)9tKT+?$Qf)8~>C%PG0y@|MZeo|O~-WorXLy2g59$iH>K58f3NY$jY1 zRbC7EZA2N1(D>ftBS`s8x6`vUJmal1R%rUJIL4VufFCW>t!sfm3NRa=z_CCW+1GgI z5CV|jm&(APP=ZVW)Uen97aAaeTH6}^mPo>F7pz#h;2M-nIny|B`V%8C+ZZ-e0WSRQ z4C6y>Au9bZ<4BRug2f7mCI)I@0cYOW!Xa^7mXS;L36X4ZV?~Uj2T{5BX48mb5o+wy zUPRozkL@bNN$DQXGu%pMhE*e(Y;#`vjagj}RihL`il;n{pFaHQ(nq^*P~VIk4Agy( zdc%@YzVJ5bb7?nU%g@{-Q^`yowvqFPmwb6QTu(W(zq86D#4=1oZn9cJ+GVLhD89q_+yC zi@SV-wAy!5QAgjiv(wGJa$5{u5gGgy_qcwPtl<|vF7uV?iAe+opzG~Yv$6@Y0H?*$xq^@33*;Q%bP6Y zv-UpcF3h|ReVQwEXmKgP@AhmK@4G~yhxEJx_!ztp2|#p3-$Pt>z{ST)2C(&FtAe@8VayLPL*S4Y?bD33&6S=1w~IZ(8;p zW%TCFca2NeOBSlW5u7>c;SidGK%lzMn}YN!d7f)m70&=-6*C6KmJ`n5IrqbXJcHH5 z+jk!E3^JuaMOBA}z>ZVGIx{c`>d=BHLLW9f%7z}u(=8fqAN>U*=SaU^wfhIrihdjV zF)2H~BtK$6;WPi;Vn1{6HnkQn&qN1f0P@E`4V=YJ5Wr3{OqGFnW;_>Yko3OSBG~bF zzQ1HU>*H>KX5`@Y%0$;;mu;{k3S30k>?Y&Y*-Y^MT~jDoaA|ULn>p=sA(V`$Pj zpo&En%iPb4(JNx?g**C2j_964bta;jz`BTt>-n@P!HM-KHSW~`y)*kf>qcgB1%4P; zLnX;y7tdcyipv8|K#0+Mzj8@kDkL0jbB%`KOwMpa?r=qe^pHnG-4!ky6(p`kh1;qE zFHP|%;bU!>iA3DfbDqpIx_?-YxP5dAUt73TteG9&^PX6uly*jLwNZ+;v?A zLgTfU?@=QWfx`>1M_r@)Kb9fT|I+c_OU?h_i zz`)^vVEqItdTJJ8VTmnWVeyoRofv3$7@b6=os;XXsV)##F!D{rnN9NF9hU#u>$|7r z_|&rY=9UGtgg$&!P%v&|mi)BRfwM_+p+YGz_l@7EThM2XTTItW=rtD>B-`( zeL8>lq?BRey2rZv=`qrq+RG)qM@p@q$$S5xsDBWZ$$?_tRo*QmBGZem@(uPgazLD`yxPFPVmBu(jQ|&pJtQ-NANX9sVQd`-ojyki>G3cII z^H#P<`O6?JWjULVG74yKB&n1p8 z|3UYYj})#~?o)OispJ2=$J+atjvd~9&t*wLU-lT+D=c)$gN@O{YiqUW5l z@tQ3YAOAjB3d6tmO0dCAilpr^8bdJ9+X-+@KpuI5P#A!eVx_-=9nElXoqTp^BL89G z-bT95{;%Se>Ig+*2G`k>=`uI6rwQ1iik{q|KgVYnc4$uUlE7 zHVL*R8D*Ok5HyCOKF1hUn>M0jMlBfQK2)((NGpkgWDM0s!yTI?!(Px6U%KG4ESQN! zBhsbJF?vNVY&^O|sh(Dsa%!CmCBx%f#E8b89QrjD zh!hc}sFeJWNBh~j#Kl9ik~8rfG>8;To{Ckt@k;0|N1dWEQ-VD{b;Jh#PDv8&FU_iBtyhH21VfZBBBW z*C$P+aTy4Rrjd&)_e%z$5LDH_qO?DY5(@X2D~CDaVaDkC zJT60gVp__|2$NED;eZ=h ziAdX3pr7$FC>YS3)+@Hh_l2jHkBlPC6hQoxfsj_QR9(B=(qmpBzZyGIDNs?q}%k27FTG+ z?{aOayK@R_Tw6m7d?)=^DYW(`%L?jUXLtC2y*fzRe&T-1OGabDJx1Ykl4XD2okEj(+e~!Xg6=b&OYx*lHJzpc2v8U%K8^ zcgjm=PCEDDaK$H&hFaI*!N_!#Nmn#eT!}np)%!+`3lKJHHe$H&5Aq2Ms{WEZG`$JX zd}A%$4$XBwiT;ClPM;O;4jE2Et8BMK994wYBo6jsEwkFC*d& z2}=7F41zZ~V{f*v)i5wx>IdWJ4E#QBPnM2rMqyLlmn4LP~(wlZSew zdR&oFQ3&^jn7RlfodxOF!eoYMDy-r8ZziJh0& z07!usPIp2V&C;pW2H6on1t|ta?RNu6cVwR)`1Zhf>qy*M&3|ZMh3n0u56V1pdxy&p zQZ0ldUg*A*bbOnG}y~0riJmyPFw zg8Q)ffUGXjEER(vCOqS1YgfD(d5Ob;Nzj#qMvsPxkX=X;8O64v874I(HXi3)gNh{j zvg`BC_DW~UoAo@i$Sz)hoWX67)xV?ycp2?3b;_GJw%l-=C%{Z77RrYDekYN^^JOHseF2mJ0Vd_kAtf*Wu%gNm^sRT6af_t3lKb+BV0i~&lsIW zcQbPX6)JX3-bKW}kBx@pxF=ts6eztC3HZxnXn5FEys zuw+>XRurd6=1AM6@d#PRy-;}B%x;}2k)Mx8~tdF{@m`~y$Qw#lcHSR=83*Kmp-5Z1TqGt19uTm3@vK# z1IU3lV_>K0hWDF5-k_&1{-mvra5pinzCL71*qF*`__LxqMDsZycf;d-i)DzVthz+| zRlkmWzpc=x2M;|rPf4cbg2HM`zW&el(aR&lQ*IV!cQVSE{n?muOh=fq)$H@!M%7Fr z#3H<}<;r8_3|I_K1~8lI-3;e0Rw0dBiOEeEOPsnv-EF3$=3 zdaE#*!s(wt8j3PmQqZ0{|Dfsid)d$KRXd{V9g6`^ey(0%9&p^K#pRMYR83`0)#>5a zu5I8OUTGDa-CPqn)NThc*@}dO$oO-Gk9`VzOA>%eBlYbSld=nXFC&G{8CL`$grf9o zb)jce#uS!l0T|(7SUgbYlPf$A@=w;aBG%{YFa{M3;)#=<)|}~Os5iHE*u(0?(yr!| z3Cqx`&VIsw?Ay&wI%jQgLJ%c$QeT{EUl(r(RNe~hh&j!JajcEd^-=K=?OZRv&n|Z}rQEAwqN2f5QL}KQ ze0WMlrrXoX!1qRBcM(OGT+0nJOtj(u)%(2lvuM1+lv1J!DZ_jfgl;#q*_5X=Tl)vw zd#5zxw=K3#d1fo%{Z^SOCzd99YVw$pLMrYvZJnaveC>y7p68c))!foxig&wzqUN)1 z-=rZA*0Y9iJf!T2xwKh(@1h<*48t1QY7A6B6t3Ne9lOzaAZ_L zrPOa2QyC{CHU=VIj(RR-XL6xQV%L^VdLcYKS~aD?{8Gb_>(8SN1H{%5uKHJWSNhAN zzoxSLB~$P8v!qoh`$Y|oZ`m56XHuoJOcVY=uQf@BOPRGtO9xG%vm|?NPzi@H{Hj^~ zFt^J0ux(=Mn`U==@7m(8E8V%@?)-x!%KI~?M5QlQx4ZUcxz*|&$sU&2&F&AHkCD@_ z&BkAG1FD&fO{{gw$sC8vb;h`^{j>+`T)46O3vnaK7Kq&eQ2IJ}QDMO~9;v;f5j+@K z*sLqsgH%3RGpINp;Oow5*^hS~|Iwm3)nY9~dof^5hJp$#9Bh^K1VV{Jnn0#Nw89 zJ;jixdm@j^);i3%S-4STAfOc9T`E{uoJM2JF0n;ptSVSZU0t)Bgdk2QOqRxC+@2v! z)1-X(T@4H9MJs1B1%HWh)mD^BSTnFMR3to}o0{|#VK!knDJ(IcP&}{tD>75*@ZyHX zL(^-*l97uN9%4U0zG=4g^8;Djgzu&t8ReSrcfinY;pw}*-1uUxJ()Ag4;Wjpfqa*( zN1B{Qr33$nduJK_6|V*a_cT3#8GK$R<}h@ z7rSx=bJpMV$Qmi_Cv&b*Wh8vsQcAK&jFq;vNnn)L)2S#`5-jF2B%(b9h2q=|3>d=k zwz>@%g}YfSps~5l zO7+!OP3*O_Wn(ILulHnRmk9fb93h%2Xv-FR^(XQxtx7<_6OnPG8aV z#bl?tI8!m6I5$2!yt+#C=q)q(32vr_=yovdv0k&Euv>{yIo%o@^`r8$Zy%TXiarFc z^bz-o|9nkR%?x&n-Kln{2EqKOcuIwvXFpWLcI+~LT!`ruPjojomD5O#3iyNQ5wEjr z9Zmt6i?syI52 z1`;d#GuupP`$@h>$@}HC%R86-WTlN4l0<5?F9!RJ zRbM2jJFp(m#xpDLfP_|pT1ggL2Oq)$X(vND8Y{zfH|yWW?U(b6KsbxMOYgIBh3MMO$ZrvfyuqG#oBoLfes4 zqE3UwjlB}W3bHu)f(fEhM=`=ZERbeUE+Q@KEGV=Jja;AJ_EzKTzPGUFu2J)aSzPF+ ztC+^{+IO?MMOOEXRlg)-iR$#V(fEXX`(jrhh=>2oHoz0r*w?v$PBLtcTySj!N}tZ3 zI8uPTULMl*RbSTTVSaP>mfpJcgI)qnxmhkSQkg6M5KI!*U!6oDhe?s8d!HRL6hH7z-@pOMLdQVGr)3Q{Qkpd~$p z8daKjDx{Xxk4wbdXMG(`gYff1x)o|h1H?l&n5CKJ8YUZtBR!d-a(}_ZwtZu%HnYP zC9_mmYR#FV`r45^L2f-y8-z4_y-N0Z-FJC$S4}Dc{^>%i(Zuf;`CAGgO!|-C*jhz|4v*i)1cN#2;pOQYQ zwG6Ck*OinH|74o<%GY);?^@Kp*c{z~Z}o4PM|$sC{+Q$HI6I|qE}&PX^$XwZ#G;+( zigsP=7xjjNjvI$x8z&a8xX<|bw7qq?{gOsVt=7ee%^=XjKL#v3e%dIrrI1}}15nUJ6}q=9(>cM_%e0-FlgHZUzH(q zgqdI=ek&cktvm}ez(%2IpI_V*V|pPvfDnsiAn6wr&H;8BG~DG3xLxgJm~J1iX@~@t z3-Um{sVu_n>cgvuYealj=O=bRDNO_l*Ys|EHDYZHl3+X4<$ac*9Z{E%#~>inb9DaRp1@<{-dOu2(45HFiVEsO&tnEO#Ml&iy_vI z?w+mu<%v4mXPDCU%M109?qIb%1cBqZIX-ECxgjdz>#O2kU(x+J)1YD`>z$G^e5o{{ z34S9y-oxKa@bMSa)*gpLM(l*J2c}GLR5U`fsD3m&4m;!_N-l#t#KZKA{S{PnJzQxZ z1LpW5PU_(!S1JeOI|=Vg@KtN18H?4Dzgw>b4RbW~jNn{^weVI)Eg?Ip3X2(7tN>V+ zMvzeJG71}pGt{h_!-^)t+aLxO#0hT*a&oSLZWGuS93v4eCwvY^A^0mE7}YN|;qw@vN^B_^kIk_Cc9LYMbLrJIOPHLYRhN^C3val;fB@PfvkifyPU;2Q zO)S77b&iz|AlMoJS^+`$C=Gi>m8$_kjOibqQiJ z5^+rmecSSc@vF3NhGWs;=`l@RjVi~c-4=I_xy)*60&MdX9ep>A01%x8`Uj`NAV@6z z`?$%kz2fFS`g2Dc_p29RW?_?ZX}Qy_x&+uL3j}jM=34#tNlL-Op_8aEw9X|OI7UDY z9v)@*$toWRmF05M52(niDomo|Ij>$}sexTY>RAMFtpbaPM`3rd z^lL?R3GwNX^QUfzby5y_QC?FP8u3JyZ_LA_E3A9 z0pPR20jP)oc1nPXBi)6@2?Xnk^uzgF1?&AEbrSKlzh~FQyL-#nlkjFDcigGd4OFBz4bkfBdWZ^abZ3cN8T?oGaJ;TcAdubEY;NqM8UKKo*2^U~iem$$0@b@tgn| z8lcVk!&_%SRd{0~S;Al{xM)l5`_aIeANwb@9yHEW4^tJ=OuIq|Bm{D3VT=xpS+tVO zIstHkv!p;6fxruaRp2+JnF{X{iWPQ08N&X+nW<_B9GN&95k^cYrBn(4$@H8%A}xSk zCs6gQnPuy7a+v_anV%Rq?Bw@Z5A(15gFdY&O&LsGoJl{BDwrEwsrO#ezWwa|jknxu zwXGTVTL&J8ISw0Vq--Sw=-F7lQ!UPe3{xKoOkF};6)V55KX=1|VHf8Zjvi7mNWA}Q zlHuZG)r+PMdowORbGH@0l--=(1zrzhhphcsxAa$4=C&tTyOL(hQh((30a00f;dXJM-yF=m8WF=C5h#8@oKY6*s? zO-UIR06$^ZLqWz`9fdaufQYq9M>wV}Vvh|4uEz%XgIl1v0^qOUjyZOpN*6{@5Jb?N zN!J0KRSz9`f-wSOqk^a*Ich2xR!0s=4WO}%IxsyJ7J! zjPh36mdC$7`KDL*%kLn0Nma2&2^8mt;$%2+SyeVC87~{*a%|#Ckw_{oV}D&&JJE>q z_1qlcne{OYR~;pr8L3Y2og1}8J(21XxBGU;|EumrR5p*oe3l0BtEffLk&#w`^k3I~ znc~aphxz}YQ0o7anc@cLS_6;+=f>~ z-~={8T9ar70bWRR7AVD@78fq>DhG^d&^HOV7_Qf-TqdrZWMJS;Lo!ZcEeK~akezZT zInNs-*h})BuJGjE_nUv=6H7A~C~w|(+#w&M%eo$nWZOrf6!r*2g&k7BQQsYo;Y zIWKk;+S)nQIn&sCTJN}P@=S7LCuG;AOJZnAHPUi8@QPWHp1d8~m)+EpxApvM3`;do zz{~y?4AeL)Lglrp*`w}A@Tdu=SA5Su&80f!w@H@0vdfpHs>iHMCuw56C+M8if8(UJ zS=8#=_>#4Q$CkfKR_CMqwd}#piBVYZYD&rzSy{xle-J&s-u>y-zv$3|(Sv5v{4WE( zZ2s&B9>*YyLr3Y)g6g%U&c2fk`cE+?2ocr%97CXw3J)j12%K0h^G8YhToB1F8(@S; zc5O5+Bt{7WungxmMm+&w5)dsFJv-{5?z;dKjE*OY5vDH(C=0YuB#{lx;wvX`TCleb z<}1ew88e3yMCf7I6bNJ*!BJ>cczhF`62xZp))calE2IW?Ndr+Q_(OEG2q+o>sZhb9 zU5s&XsIaJ%8cvS_vtTc9kMM3mf*|3T)}{j}l317k*hI_ag{X%CL37$w=S)CwxvzWI zVr$GVtsX07&K`U-f6>P6i}%D!XXb`ZC=yBCAJZcmE2>hm)EZlRbVN=6y-QTxp|{7n@{ksh`FejaN> zB2Qw}KUMT}etf-=wyXFQ6J2S!E}D*LSdo9=uD$j^j7!sWe7>P!A-O|U+mN-q1v4xt zw8SZ%fmEjlCH}gFoEi+t&W)^if*IOCP(U5hjYzdyB{%cR6_q3DQ!1c<*pu+)*g`ZK zt4qTO=7(BFSkWXpfL>e_G5uD}{j|viG3lu$!zWM4O%K9+SnmX4Z-nTISAi4{7 zA3xD`qs9fEv6{#hEB`s2M$i+a79zk65$fW<94F~0Vi!7C-N-vwzdlT)!{Sn~to0@@ zGDYhZLv+ESz(8tu34g0h@4c_JUe~s- zJQ^UWsA1=gu@HDOO&}&v{_Fz7BGiG>KAJ#%QI{HcA`V^NScE?e{0R)QA|8ljFdomO zh5toc5DcqVNDsVK5mx8;YDUpO0=zVkAh@L_E!n^X8hf9Q^VgDSbA2xiZ(L@iTuhp8(fsJQ^*Czf?h)h6mX>UV_t^CXOF8CwZQI}r z@LCR?^ZIL6J1htoOK`Xi_*dxF`S)M*-M5}g9=^HUb8094)5=@s8ewVI8MmMiePvar zY{YZz*+UG;J@08`ec+R-wrX~e#=tp(y(Yz0r13)clamf2&$az2i}sG@qg{Ago&tqVzTL+HvC_#Ql3u`1PP=Ceg0mWN6g?_=^Xa#ey)mGjx3Ia1~G(<%$vq*)C)&W{Fj5u910g{GjMN|a9<3JPw4w^`vf;httSl~o(T22zL4>p|zCwzA_;?SE8ADjKlvfLLgl<8bvffEXgqC!%O0n;S&5(GhY3wT0( zB|CzE=bk+29PgindkbYqHxq4fm6Fq0KSO#ZdN(92Wn{;{<~xYK+&0>HVESFgmLn9i z`{FkL)FZG=py4Q+T8Ds!GB7gC^hZ?`P)Iasd1tLd{K$MmI>%5= z=&LfPu4!&a?v|86d`phW1YhV$v-3HgerlQ;|Df7lAg1m(z`KRkdf49OcLeBZ)!V=oOx1(;? z_T&kZwa&@(#OL(c$-i2=ubTfL;MxwNtm&8Am4xArxP^izPnx$5E%57qO@88 zaUVGLe}8byF52nO;qz>wyxUVXFhG}>qrQLpJ#bjL^JKN))k1(U59ZroWHQp^lcun7 zV;mw$6h0yddYpl1^(!KO;-zxy$$P(=Wf32UF@< zK!_20J>WvM$D{iY{w^XR3>a#0=<~v?K1#eiua~Y^pu@+_NsXx+^C8 z*VK?}z{bDvI%sry-E%5XtM}mUxm4-hJ4VYpJGLlHR*T=bTT%>T<=w8_2Ti9oLz3Rs z^d!lJR~T4d@9ofgl2yR4etmV9Z?*dKehBM`%!Pu-CN{!|NL@$^9hUy~@q>vIxNhu1t;LJEUA(0Uy-Q>BYkzPN2NI4S zo1UGZ)Y$r^Pfi#>FyMuNp+WMoF4#g2Vt;r_mmW0^g8$FxAQn3Tkf3No(RYL7S%<(X zH8ms;0;igq6Idw-LjbDWFpxF?k3Wv3tmGg8SR5k@7CJUy@ICOEg8$tMe+O>lAS{hQ z7$d7R93`06b}H{QF8NyZm4I5ONYV=bbkC`8hp8X=Kwc@~m60XBlILoR=je6`6JOc4 z#744M)9q=BNpbJmVaYXZ#dEK#n;ABYf}K-81mP^D8dz@WT6W8vk}o2_QjVYazgGnO zA|twj4g$PhJz+Sjt|Xitg2-8{xk)76d6Bjtr$E{DVwCv|7hXnzJB2*M0hzLS2L&XC|Yzn?twMRr9~ z>`npqorarn@3zgC)b7~1pFFHIZ@tOidTJ;2ACRBAJUPmk!0A-LMozNL@xQ1fW-1do zY@W#4%b{~sl*7rHN^CViiHt&C_MRWt(JfckEreJuBdHFApc8F&1WuW>SCdkll=1Qp zkDPiA%kM7gt^Yot=hT$Yc(}Gw+BAM4`OgcXfZ^IMTz73ptO2^Z7=;JHOF`4kS%;v< zp~2N6KLb|1o()`1(mb@({^I`q?d6B9`u1CIwTbh)z0b)~zF+Uw#TW&BzVOM{XiKhf z`Qv)Q_eloZ#b*Os($-%~*R?(NL+odgc@6ZqfK5B<%{JZl@!EBNZ|3euS7xiV+UWUR zR?lO(@;AVMlr-gIJjQylf3)=;_i|qf-|C3p zd36+Hy_+8*vHOtpJ$QH0<@bJN(11EqMTA@fe8a)V@)Bg@#0z3|j_F?ITs9yO={HFXd ziVbQ_t$49d%ayRJK>j*%_z&tyALU=^8}*c1#_dn?&-CuMJ{^hYk?+A@61j%?{H?4i zNKrp9)$xs}(#JVKim6e(B605Hujn5Kq7XWUg-ZJPnUKdhk>f)eoEWSW0x4}qJr*4u z$VyBo4uTQns0E1x7^BX~xCHZIk#m3dsC0jlN7+^rMuJV>UD++dd72iu9}IYm`wk%* zA__-uZ* zQl;7vlaj;(kb5guuX9b}xk%AMu-{&jTYgxUY+9Cn98(s&2V2h-!WB-_GEz<7$-O;s z4wEy;lM8nc2upY6i(-E zv*DkSY&@#GCGWDO$WrQt>d)wnopH8|3FP|a-ET#C1?BX8D$?>zNfVI zZw>wJKpk%RT_cmKN=+9^1DVU{yMk%L8Cj}A+Rzx5Ie?t5aw2fCjAZGi2*6nr;O+0= zKp_%xMioi{Hw>=isxyNCZz3HQ+=b|15KwLk01*fbYf5!Y2~rX)1nwOCN(Y98XcF`! zfN>GvopKmL6&3>Q1sEgY_<@iPD#n!xI+Y47<+voA)|_$u<)mr3{N{>RlY%&9Bj)4x zHKmRy-NmyKme)S1zX_^L&6AKYacI@E>ml-Bp2hjh`w3@qu8NlWE5A!u&Zey_mf(yV zvACZWI6&-ie#Tp^0~UE1U||0LaePK}5EDRKQyovcH1Jxmp#qjvAVbAMuAvo+7PB%F z-9N2g?{V^l>e;R{J7e-l3H+lVMeCnb0eWWR4N=#>Au38ufZ!YYEP2qHPlYGH68Mak-qHp{fsqb5}_MM|L+BT`( zFnX|l07be*Odx?#h_b?PK7&Z0XJqZCDs=VZ6i7(=xoy;pZn6g9drOSzC_a=TzF zZL!XFmjucyO;4V7eu2+1jVJo&sUa#Ve_gT);F6o91!lo;EeyV@n1dSS4TAI2|E{g5 z^yjw?d|3RF_rAuA+~`-k?442|^h53M(p6WW#}^T(G;A_i9n!7brNW!A!-Gryj2G%*aysU7Dj409q=c>EgyfSW;Se&^?(uRqR@~IBQ?Yb z{A3Lz2!)38f0fXo1_ZunU^V}V|3i^ZdUH{3>W{9UQ82wXVQ~#y)C* zXWe3$$nJ4zUFrshaJxT;pwH69B5Ng#FBO%*@hh@AepSbZP$`&a{+CODpsM4&p(r74{Ckgs)8l=77Wv|`O4um8fA@d1_8(>!?L9Ax5kBha-el&ZOvm-$IAD99v`(+K z1BD2v2hgqByRZe)$drP9=CBue7&YjY~<|O$`NH9pS1Uzd9VuRR2{@7I~*_J*usiEu5d{J`(9^HL9fF z(^Ps<#arQPlS8o4EaNE6&*QuC8>U;lhyM%z_~f%U-m$s zsXW;)Pk-~cm$iI#<19)31ruwWoIFu@)g_{~$oo>U+@+}EyQ0g2Y8~&5Q0i z3?K9KUjDfM=7x6h_nk*{7^tt;*u z$yM@7`x)zE3YZ?G@SmwefMQfAZwDD4?^i|(5x^eQ z@m9V3ae$=*mJhUykcdmmB5(j8>Nzj1$H75$?uonO8T-DGk+5kG(jWT0*z&lN9DVYI zo1~mG-gkOZ+ph{K-^d|9>uR_4+}Pe1 zEt}K3@TtLkO|98wd-kBZV%_1B*}FYHehyuUR?#aV`<%;JKX)AiTW6aYtiBA%XnqHk zvx*+*E_4q`OD{c1> zF*`P&gnAISHe|36c`@BTt#x>ANpeHhYVq%i?*;LN&qh0ZZ_ae^JUNO{3oT9h2Z{0J zrJ8DgNfE0Kdn`6ts(vu&<#NOH`gb6QnAyy`k32;xiPd8rpJ3FhIbRefS7RNm>l!oy z(-SFXkp@x74IKdp#{!2s=>|Yzg9IZ8z!Ib(w(xFKG_VVMNH7))9Us7G?I3x(Hk6Pd zDBUk0}!iY_@Jj#*6@PF7aK z$DFylWhAX}=&q1Z<2f^XU=e(nRMCwpE}=0Sxz8&~!(5O;{OTt&XF=W>y!X9T$q7jKO_0v@NfHu*~g{$1#R05DRgARcBfpg#jWA4Aq z3M&|HL|Qr>Xo8@_2x1tp1O{JobX_F`AZr?8`@0M}u+h(Icg^%I8QbWYJdu(%BCFnaieP0iI2{H-3Sb{vvu$fT&2(F5t}_c zsa?`-Qu>j+Yv0v12Gzn!ASv!y9RgV*E>lhbf~MMRc~I(%(6u%cwGK7Xdqq)#TyY4p zb&4Ujjxva5O2p9ZM^Ab-wQOrg_^*yNZtvFVugZ?FV#{8tgqm6mn(+Hpk}@e;zl|jw zZEqyhUg;UCmR(mech6pYwzJguUhGP6Zd4`Q_l>DLC$;awr$ zWqYf|_?in2xjY~$j)b8F%R!|435f9=P1FPp4DFf1=rUQsy~>(t3yyX_J}5qi>scIVNm7RJbs5w)@NK|?M9`4a%O>eW~+PVm9KTnJn`G4 zQBEBe%n`$aj4VZy!X*0tBk4N8+4{eCC~dXqQbmobO;CG>*50f3-kX{wCDg2y*s)g- zN^9@ds1lLd)UMil&sg98?eCu_A&*>-=U(@7KIgpWJ?~2wmqnKpvJKX!WGC;?Ck!!k ze@Gd!txxEv#ksD}jNFZ9xy&bqcibcFAhslZ4V=`y4v^KqVxa<5MPSGdoZ?-ejt0p- z&?1%rbf$n6L6xDRM5?bt@(EN-4K7i7k$#IXEYM?t=hwS7V_~s&7DA&pw)x7BO)0wV z7s=h;K99lfbct!cQ4MrJoF!XitSb1N+Ag|;U5FGMUf&2wbD@sRiz95XHT!*_TRRMb z3@@Dkj*4Vc5F9M$j9Z0(-wCp-unqLzY&bn6#O>z zJH8OZQ55}|RpVStuO^WTy^HG_^kTvB-a~12h(B`Lx3h6wkwG(Od?hK#a^Myd_5k#x z!pv@c2>wo?lL9MZSDZM{{A`v=J4xf#_DJ>p*P&K&jPRR=`O;HfgO>rP5mf@f5g35yo>jh3U=^vE{He5t2Bxr@_DOK>;FLMKtskqWNGXm6?MD6exnS%3wM!v zwI5$eJo!=~rq3|>4Y3^YXQCINB7Rp#95E=thyV}<@4QXHMFT3-BD5to#H#s$`s5-q z^0n@D2;Syb0FY-Q8UoVLNJ7Hl#E=lJe|>-8qHhA3xjGyG4uAX)JOpM3ZxH~65TlCo zeV=0G_nZcQDdpDccS2K2*p>U;O=XAMbw*UHo%1ZQma}K8X&YbvU@x4~=9th#BvOd@1F4;`kJ#bw2?F z2Fd(OBr1SX;2{Ny0QAD}U%!ADC>CDa0SG9BfRIWOu!VF`t1yzP6){^Arnmw(F>p&J zRJfP8d-Jf^pV})GKb`}bJ$%x_J7!98+IDF~&H*8N!2uzcGJS&MjcRA#MsIp=2E;M9 zetiH)Dv{eyNc7xj=4|?(b~g@V-8>oNzYe`MA*xxbuVgPL6>P#V4O&XFD_i@Lf z<+;{7(FEEY`9RSXJS|(W)=jr`#+n6NIm3pyVyCQH7E!+z*mm;vN2keAKvCJ2QsN~s z5Hf8)63EKMg;+9%yn6GdfuOJ;A%^LN;r44wa*XZI!dJK6RLK*ABJDr%iO7URaJ*u+ zOaT}u`S(FD3Sv4y`BXqj2_obR0nok|pnJ?;*XxX*#$mKeyGpuO9kQ*+vF+W*Vj`Q+ z&~p)|jWpN2qgtN*sKT40V^TvF4sd2hkC8-C+vdeH z^yt#)(p1c&m>cOVL4342PyWN51_`!hhz7A8@h5_RIXE!03zVop>QjFIVJ~41kW>(Y z?h(G}0PCyA5lljsBS3oj?baV2tb;&aFYY}IA@~Ho2i(rU9aH@8w~0X8G62Ei=Uw3a zt%tz!BhHt20!~SY<{SVYPxzLqHKfz-OA6yBPM@a zxFuD_HSJY2w*lWZF-h6n`xEo-3|q|i-MEHjv%L1P!q`W-;LGBbWdt(uz4_LhfJ`?4 zR7Ou#-RrTO1W`Gk7}h}p)&|AHgIe2@&Rp-YmD(HDB|B?3b6N;rBW`)2-2A{b-DKu1 zLZ!W1^bF^(PJz1nBsj5mr4x@Zz&4l}bY3v1b{n1svy^>^hp4@OZt1skYMS?wuOZ0E zCw@i+S&msqN5%cHrYX9wJUakv}%fK05tp-7o%U`W{icv}fyX zks5KToyo-l^1P1e@|g+)Y65SUfq+_Gd6kg7!=cObO0%Wq?A7alM6;C(G7ZhT`^vmZ zmHBuk$H!?30F6eVlUh|f*DJfJly|lNk_#68SI02*%&ifhCQw=nu50t6`*6on?4gf1 z!RU*7L{V6|JI}N&s@gLp=QB1>p5FYrDLwrDz?^r)2N6OQ36G%Kn@lWx#h;MCB4;Zo zlb-PF<0R3Wmm%9Slhnk9kM%E~RC@fz9C?OC>setx2Nb4>*(y&9#^2LLM)b!1I!@BBk@R=Rrx$8$GRI_t0E=1a4 z-03QPj>&nB6vE>PyU6D!Mf0@r^5 zz6yA~09X?Lb|L}*&5Q`W3#<_>!wQ+F)&LK!8bsg-er?CTtseae6x2bG3S=h#)>UFP zMW}VpImxS@2^(b}r>C~NmEAJfq`5P*Af?2bGu%XqLo$69?>q*dq3&$;%ZfI3*;}>cZ5s1=<7qDo<3F zoP9a70asp`XWz`_#4+C-1wOHexwSOirBJ|5jQ`7U+6A7nS*%WHH48ou6*Nz=!6m7m za=etLkX5Cz54?mYt?IH+@$~5^sAz5Hq!i`Ng?|EUJ=(e-HePC*qQJCss_|SFf&nD^ z(C+}}rB-zl!F;lKeMjk>!8-Ycn6S=eTzKCsfpSWgy;DIM|D#M+2^R8SLmd2{-s&aX zy8oCigl~#tXgf4A?TJi`Hn0X}r37*m#t& z3rjdwSbBP>c=-rC<$WdYI3~TQA&q?&ri^&v9ZaeC#{&uAm$n`2hjpK&o>X;1~#yV`BjfVKrI2H0K$!uA0q{@6t`1Avegv-)@KND z$O`~oC$OA)(WeFM$v}NiX!FEQhVZ8#kY9q-(sW;xlGHq%C$1Gft#usXsIlnPFfUSN zfjutGEX;Mw8BeNjnkRpZm`GwN{n2)_l2o=thl9=Q>3+)`K@8I4{Op#Mwc4bLJDPn!N2;?={4 zkG}l29@_Y0dbsc|X;1i)`Mc55Lf7X_ajdE~ck{C+stuXE`;`Ipa~F&i#yJ`dV%~0- zuF3w@XK>{6r4KikQePmsG_G8fEc=S%XCCA2Sr1UEC^aB7(()~7$Gt3LIiy45L#Vm) zL>BJ^$p_yE2Ds%SKbiT;AN@pd(hO~ZzGvBo%%#A0 z_Syo9CP{5~npxp)V;Bb%5iF6{!&zh)mVH4-~Dvopj$%Jig|v~b)%aS zKV_lIBWQ>6O{Rct)}$G!?k*+fWzf1TPDbZxhd=wVhfvBGZ@(KiPlIC<)8m=VQl!lb zkUQGn6nflEm%ieWtDaw9>$gIcr1gG=g$}0P=B142!_ZqCyfPGed^4fTTNtf9YGV;F zpt;6G#&Ss3_!`DufKLSatV8KNk1Xy*E4S~WvQR9nd3&$wG}d-0HXkbN!lnKL3W+5DcIxn0OC?h}7Fv&fq z7ADF!?4?Vf;97p@X-Sc=9^O|Y2vq-5fF9$&YR9m`mY6j5KE=Hgy2LN4rQkz9mK$KC&F_`Auw|Io zg_K71)6iW>n7xJ<#`!q(KM=p*MWFvvd6ToC{%f`VZr{iGMSKLfnxP**&!2bX5TGD4>;QO3cPuC0 zQD|}H&q8j67!!4f5k0DK9j3I%q~PoVs&DnRTxyaVV-{Cf~p zCuso(Dp|{bGQbV?eIWv@H%tn@?3jzOO(<=WGG5tS@0f)??O^^j@y0utTCtVSId2P^ zVQ7E3^#m0Sm8BZx3xQm99?rJVT%Y-Sap(-efrD6)H_gk~yNUuFTx)mW=t1hB9j{@@t4XcD)d zD_cB|@Gw^wNyKlc&mNIV%9%123K*xB*ya>2PA!$MUzk75#9|knX*HTCvuNKfz(p^3 zELuoS_vGl=V~wye#A#x8PEOK_b%WN2}phwe;+0VbodB zuV>8=Hw=!=vq!iK--5mc;T=T0#P%HvNbni7H7nQnqYFCQX-jWxnr=|0 zmTISJQ?F(c91N^7P?+UM;t;-1;E>xdSO~%%^2s>bzWmhDA^UyZxY%q*tDcj%c@97&sKSl5%HhUyx`8@j18eF8+ z39hbBsuS*Id-(UhbjZdnquPjUrkT(@{+e3|8Bm zR-+k*^EWwQ_oYo*97cReen7=0cGxHzUcdL42|JyYFQG_0ZKR$#R`|Nbj}^e6}v9?j~zGF(<_)J#%<~5 zTx==6w@w+I;9J~n>zkPC4oy7XlLenk-W(}!mu{eg?wNx}7qSm<?M6k|d>* zEqT47)lC!dd?g=fvApVcdU9;}cw1oF3%K?N-}K^f=&qMn)Xxt7;xp=-U&9z~1hy~Q zM389b7pdRV+RBp`JhAQ37l6Pd*f;HFa0DuMiYaU1_M}#w#6s@Pd*JZy4pFoWfLhvD zWxv9B!TrO1_BLQ^5}BQriqjR;42x85R+;+4r~NjjI?>Lk7p3-lDt=#xRUzVe$@F~z z^|L!7>-NzKjZA9pac?K~XcUxwC7^sJS?#{j{u)A+NFF*TyByF5X9%8i*!MsEqhLx* zAF8+RT*LCm_au7ti-v_+dv;7@fNHc7?kBcv6Fgadno;C{9&=1{t<4o%Rxf@~_#X)S zb2;0@D@Jg%izf8DoWb|^%OgE0&mF{g`!d!cWnm0dK97SBLpLva{sYwh4U!knND+GEr;;M@80FA#2`RtMaf z9Uf?7SDpQj6$R`woA<{0+oAkY+HVtwd1B;WY8kN$nthan_YSkybp~S99K!*hlft`L zVD5$_2Cy%PsFtaE8WBqR7Swx>!CH*t)exA3kpHPo!)tw8?bo&?fH8Q!wTud(YoQ4z z3m~%mD|$7OY8NNkO?>-;I4m<3JwLe`X*vz(!}I~?PJ7v2cq{^RlP7*O2hE%aA8ak( z(l9%hSKx~&3cn*k3{m*!-A5Glf}%Cm{=LTJFby#v$-qLGfUQ!GVSH^3BJ5G;118{} z{=+~^6tw?+_^|vwGcjQWIDz6`RgH`mComx_Q*fwc2q3GS0Ab|*n-FV40HAt77ni_I zFlq8M)bc^XG8jw{QUTVYy4L}`CV?^-K;18Wb$nmA zf#q7PR)ceOk6zEo9PLzH<`?mKRNnL}ps4-SU1|g&X$DtsUk6Kj9c=xf9=oj6+x(D} z5=fVcLhMXaPmftmCBD~Cr+0|CFjqv4ViZX@mOr2$*ll2sarCjDotZ=;`42aB^S*rT z-ERr|tyvlHfqxm@N~5#;-D5eZ?GCjv7=c>4Kyo4*pR9TxletHclgXi1)n`JZB)BHl zKRm&VLCs0&RJFyx?oLI`PHot!y}#X*heCJ9^n}kRXWH&>?&}PJX_^#S!O0pVs^K>l zvO5?CBxRr&r;{?hd)QCInl%i<|hmHLQ3Jx3Cq_ZipgR4`z4x3v~|CvEiFV#6c@7c&pUBum$bvC`Ymbutb zRO~LnfsLgk`4=q^(3Ouz^%6Q#=7_JG;&b{OwsXo-09%FR6n&&Pz675vTJada%=kO9 zIErIFkag)j(`Xer7KWs;EAwKrSu{I9w|so~xj!qTr#?$djN3qi>;rlCZ5^*4d8Ott zE`AqD9b3rcI~M?&)pIqUGei4b!6CI2?5)*PIYUN{rQ=E>+G&9L>@7#! z?63;ltV@UK6RO;wBYUP)Bx6W>tVZ@gz!3N31lZS>H6An6|o8(}v|u zJKiCu_%hSawcgn9diM8?cCB7nnmM0S?efLG4LLf7)_U~ygNq5td4_v@pDzRy%|%^G zDskNrf&G%Dz&u3!+v3A0o3O#`d5IzyYTH0|1vW9Z>rcb;2prPHrmI%W zqgJQj2sG4LRahtptJFfHfCggCJ$7QpCspjNPi!aC3*?J7`}F0Q`({;Gp7n-L8dB(e z6xJ)$B1qzsv3WM6p&gon=2RU|m2XsvL+e?A6EI|>**fMGM=N72nF|G&Rq1oYCzP5o z_u-L{cbd_Epy_s|p(^(BEYx$65r*kQ=jX-&KKH!c0T`fd)9S^Uqj);gM@X^lDry&MJV%fFt$N*WDA7K>>K}blxE6|t(5Cy3>9dtmY z0@OEvTh}Wf2Lp}(#ws)|X*lPb^VQ=Z`L-bvz=uHh2|)g|9;g3*rd9Pcge)RDgq=dk zkcsLp7@!+Wy>JZSBVZpAW+kQ0=L7=mzZ8tb^<{Wm25_Zar)AW~G?~imr^ob_y0If| z6WcfFa*_So)t29eIKgVwtgzG%#hYy|%{vQA)Ak2k3Fl*IDe3Jan?-+%cHFW`06YaeF>5tP$;$?Yn+*6h7@cS*NMi+a%}symkg z3O92nlRQ&UUh?wYonsyxS{Wthw4i_R%UWmIzBv#t&bO(LGD%~&pH*_5nmfQP$Oeci zHZn8JUNG<$4zF&sOq30>HzxT?n=^O}U0iu;chl?~ioJ6)wS`K(<|thb-RfLCiKn$t zy{Y_CHD;^*=X=)bjROy=aB+B}7ELx;a4!n=e!j76!`C&#fi!(d(y-t`Y~ZEm-WB53 zvuMUFa#0O!EOFuuXZ;jvR1}DA5(~5!>e5<+QKScZ@VhoTDxPoWF0T47kWoS|J2VyI zh?)L_y9!9=jVn7^=`tI%*0!;w963~ChG)^1Uj>uMZ)YM%`BvddEArIW;j_5)YT zi%y=QFiAQUPQ|2IZOI-ykM-K*b02eK~3PdQ(hkC({6BVUT;&2 zxX^`>laP_m;cSCO>Lmoj@5GSNH5T`qU1w*!8Ns$cP%VbiLe}DdPa;-cm}-q^Zb!erAnC#N za_|PDQ(b?_0k-{sqIwB2i+TDVh)-a1btC;pJJD3#+vj_%ZHE1VwMSQ&r~5X!^DS}5 zll;!U04)Kn+k5(1uxsRmMp8V^s6aT)NOz}wHU8in$C>Q$L-Hzn>#>EO?@mwK@#)oU z5>0Eg? z;_>`9I6uYRe8-wq?2lPEe|+F=A4Ph@axD96SVjC>WLiF&EeIRuca-L7!RFcpQ6Kf4 zqE@XHR40|?C@ATxc|sTYQD5R^pISHv-#n`w$T34{ARW&?Or1ZR;SvUpV5L-t_X3@0 z!jJ8q025)-4~t-V6k6e@)b(PVG?Q+mkuyK_oY6V_d=0fsyX^)myq zpk=#wo@X~22VG{^5rM`Zv?nw3$fEF?!SS1?_=GxM$;K@&9PgR*Jzx^9l9mb1`6`~- z0A^%vN35lHd6leiX?0iO(F|2L%VZz)(*iDvws^p(uARmHJ0frM;`3IfAfj~dt|mlfGq}OXO=PV!hM@PzsA)Mv~4^25P-Q}+#_@a6NY55 zYmo=XlTig5{xvdBtemz8&S)Px@e-}={EF+V9gGt6YC!F;6LvhHqyHH4C8AmmAPIE; z%WCrh38{{h+ko5y9835wF$R@C1(4%%0%8D&0)}4y{oo-`EfYK?OiZa=iVXC(c(PEF2$VJhFviQbDFWTX+>aP%7dZnHpM(Q$VAewR^Ms;%FFjqen+ z_2y2&=tWzS@y14)5u5M9sqiMANxM74WU6Jt7gj!t{|-ZF()s0j#_hi$6A}62@_qQ? zm*MXRcUHxW&{((HZSk*(TP=R3RI~cZEY+6+*mWs-s2Rkp1#XpCEFJ7+p*8O0D;D_W zuw#KPeiX>V$fY!6`HuUil@-wR^AF1R0DDDm9Q}VF?@BriDi+S`_!A>ve0m)k^{wRb;|$DwWY)*g^JWx6#5l-AJxx2#20<&i+Ngi=_xw-Z*xG14PMD3C zyP^CF9?@`IV`7ge*uK{zy;<6D`@p6cl2eytT^`t7>`jk5Zy;p2eg zY5K;ZTv$$nc*-8Dw88N)#8^~dVNmM)@aq23`3KrG0}&6bzyUqagy*@$7~iH)ljp`e z+=AbHfW$F=__T$m;99GC2^)p`9NRR4J%Y#a`NYDKh3CO6}EW zG7I4SGE(g8$Lae03Gq+@pk)0ljavT?G+Yd5$Q_FZnF_ckr+EJOVp>*ze9A>xsg4v$ zMF=`bBR$5hwqz@^#w%JTdkmjJ%vZl$4Z1vtDXRTDx*>S}GHV;v%>bp1VEQZhHYogt z>e`=^ue7vM;{lWMZ$7&K{!NEiO0fB#wYL{=F`tvt(cR>}Q|oi52I$MteJ`iV7r=Cl zcswv7?NPLHDVcZvOl7_dmsh&Hle*pUJ^y>Kuyb7ujSxz=Y;doBK6iTyb9bqy04{X5 zC<*_vK@O2~;12&SW%_iYEBOkM>XLjMuW{G84*yECZd~AK&YonWcDXAt@SX38wDe%t z&T{=BEN>)0LRN8wcH_{Ani_H{s&Xz##^65i&9(M!U;JCR`a^2U?ct0ziEzV+h@?qj zWAR(ouKKJ~j>Q~&W%FLVAjjdb6 z%BpV8=NmR!0ru-{0dak`cOzxnNq@GIzp|gs`Ac3QMlFJ9fY^q^-4;sbTxiP9g-HNq zLfy*52`$u=yZg}dG<%NOT?fu?0trK(WYe)3)*EBpwZe*88`lG_EiPoow)l(DqZj}y zHCY*C>)Nbu_I)xZcKryxX6olKyJ%@>xp<@b%UkTe;A!;U#>Hv1eXd$YaiWNC)5)

iN!7|8GsJ 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 F{|AkPDrW!y literal 0 HcmV?d00001 diff --git a/ui/games/Puzzle15.tsx b/ui/games/Puzzle15.tsx new file mode 100644 index 0000000000..54d372dac1 --- /dev/null +++ b/ui/games/Puzzle15.tsx @@ -0,0 +1,183 @@ +import { Grid, Box } from '@chakra-ui/react'; +import { motion } from 'framer-motion'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; + +const getPossibleMoves = (emptyIndex: number): Array => { + + const moves: Array = []; + const row = Math.floor(emptyIndex / 4); + const col = emptyIndex % 4; + + if (row > 0) { + // Move tile from above into the empty space + moves.push((row - 1) * 4 + col); + } + if (row < 3) { + // Move tile from below into the empty space + moves.push((row + 1) * 4 + col); + } + if (col > 0) { + // Move tile from the left into the empty space + moves.push(row * 4 + (col - 1)); + } + if (col < 3) { + // Move tile from the right into the empty space + moves.push(row * 4 + (col + 1)); + } + + return moves; +}; + +const shuffleBoard = (initialBoard: Array): Array => { + const board = initialBoard.slice(); // Create a copy of the board + let emptyIndex = board.indexOf(15); + let lastMoveIndex = -1; + + for (let i = 0; i < 100; i++) { + let possibleMoves = getPossibleMoves(emptyIndex); + + // Prevent immediate reversal of the last move + if (lastMoveIndex !== -1) { + possibleMoves = possibleMoves.filter(index => index !== lastMoveIndex); + } + + // Randomly select a tile to move into the empty space + const moveIndex = possibleMoves[Math.floor(Math.random() * possibleMoves.length)]; + + // Swap the selected tile with the empty space + [ board[emptyIndex], board[moveIndex] ] = [ board[moveIndex], board[emptyIndex] ]; + + // Update indices for the next iteration + lastMoveIndex = emptyIndex; + emptyIndex = moveIndex; + } + + return board; +}; + +const Puzzle15 = () => { + const [ tiles, setTiles ] = useState>(Array.from({ length: 16 }, (_, i) => i)); + const [ isWon, setIsWon ] = useState(false); + const [ image, setImage ] = useState(null); + const canvasRefs = useRef>([]); + + const initializeGame = useCallback(() => { + const newTiles = shuffleBoard(tiles); + setTiles(newTiles); + setIsWon(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + initializeGame(); + }, [ initializeGame ]); + + useEffect(() => { + const img = new Image(); + img.src = '/static/duck.jpg'; + img.onload = () => setImage(img); + }, []); + + useEffect(() => { + if (image) { + tiles.forEach((tile, index) => { + const canvas = canvasRefs.current[index]; + if (canvas) { + const ctx = canvas.getContext('2d'); + if (ctx) { + const tileSize = image.width / 4; + const srcX = (tile % 4) * tileSize; + const srcY = Math.floor(tile / 4) * tileSize; + ctx.drawImage( + image, + srcX, + srcY, + tileSize, + tileSize, + 0, + 0, + canvas.width, + canvas.height, + ); + } + } + }); + } + }, [ tiles, image ]); + + const isAdjacent = React.useCallback((index1: number, index2: number) => { + const row1 = Math.floor(index1 / 4); + const col1 = index1 % 4; + const row2 = Math.floor(index2 / 4); + const col2 = index2 % 4; + return Math.abs(row1 - row2) + Math.abs(col1 - col2) === 1; + }, []); + + const checkWinCondition = useCallback((currentTiles: Array) => { + setIsWon(currentTiles.every((tile, index) => tile === index)); + }, []); + + const moveTile = useCallback((index: number) => { + const emptyIndex = tiles.indexOf(15); + if (isAdjacent(index, emptyIndex)) { + const newTiles = [ ...tiles ]; + [ newTiles[index], newTiles[emptyIndex] ] = [ newTiles[emptyIndex], newTiles[index] ]; + setTiles(newTiles); + checkWinCondition(newTiles); + } + }, [ tiles, isAdjacent, checkWinCondition ]); + + const handleTileClick = useCallback((index: number) => () => { + if (!isWon) { + moveTile(index); + } + }, [ isWon, moveTile ]); + + return ( + <> + + { tiles.map((tile, index) => ( + + + (canvasRefs.current[index] = el) } + width="100" + height="100" + style={{ + display: tile !== 15 ? 'block' : 'none', + border: '1px solid gray', + }} + /> + + { tile !== 15 && tile + 1 } + + + + )) } + + { isWon &&

Congratulations! You solved the puzzle!

} + + ); +}; + +export default Puzzle15; diff --git a/ui/shared/AppError/custom/AppErrorTxNotFound.tsx b/ui/shared/AppError/custom/AppErrorTxNotFound.tsx index 64db950d20..9e439b46f2 100644 --- a/ui/shared/AppError/custom/AppErrorTxNotFound.tsx +++ b/ui/shared/AppError/custom/AppErrorTxNotFound.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import { Box, OrderedList, ListItem, useColorModeValue, Flex, chakra, Button } from '@chakra-ui/react'; +import { Box, OrderedList, ListItem, useColorModeValue, Flex, chakra, Button, Link } from '@chakra-ui/react'; import React from 'react'; import { route } from 'nextjs-routes'; From c498db8d3cbf65c0ca6985b1581fd4bc12edb6c2 Mon Sep 17 00:00:00 2001 From: isstuev Date: Thu, 10 Oct 2024 14:50:50 +0300 Subject: [PATCH 2/8] capybara --- pages/_app.tsx | 1 + public/static/capibara/capybaraSprite.png | Bin 0 -> 13928 bytes public/static/capibara/capybaraSpriteX2.png | Bin 0 -> 17659 bytes public/static/capibara/index.js | 2643 +++++++++++++++++++ ui/games/Capybara.css | 122 + ui/games/CapybaraRunner.tsx | 42 + ui/pages/BlockCountdown.tsx | 3 + 7 files changed, 2811 insertions(+) create mode 100644 public/static/capibara/capybaraSprite.png create mode 100644 public/static/capibara/capybaraSpriteX2.png create mode 100644 public/static/capibara/index.js create mode 100644 ui/games/Capybara.css create mode 100644 ui/games/CapybaraRunner.tsx diff --git a/pages/_app.tsx b/pages/_app.tsx index bc583e44e8..ed214702e9 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -28,6 +28,7 @@ import Layout from 'ui/shared/layout/Layout'; import Web3ModalProvider from 'ui/shared/Web3ModalProvider'; import 'lib/setLocale'; +import 'ui/games/Capybara.css'; // import 'focus-visible/dist/focus-visible'; type AppPropsWithLayout = AppProps & { diff --git a/public/static/capibara/capybaraSprite.png b/public/static/capibara/capybaraSprite.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d6a07ce08fbe68542018a0eb86b292375d1f84 GIT binary patch literal 13928 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 DrfJf@ literal 0 HcmV?d00001 diff --git a/public/static/capibara/capybaraSpriteX2.png b/public/static/capibara/capybaraSpriteX2.png new file mode 100644 index 0000000000000000000000000000000000000000..0455a2997ff85f92b88d11dc34f327bebd04837f GIT binary patch literal 17659 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 literal 0 HcmV?d00001 diff --git a/public/static/capibara/index.js b/public/static/capibara/index.js new file mode 100644 index 0000000000..4297ab20e8 --- /dev/null +++ b/public/static/capibara/index.js @@ -0,0 +1,2643 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// extract from chromium source code by @liuwayong +(function () { + 'use strict'; + /** + * T-Rex runner. + * @param {string} outerContainerId Outer containing element id. + * @param {Object} opt_config + * @constructor + * @export + */ + function Runner(outerContainerId, opt_config) { + // Singleton + if (Runner.instance_) { + return Runner.instance_; + } + Runner.instance_ = this; + + this.outerContainerEl = document.querySelector(outerContainerId); + this.containerEl = null; + this.snackbarEl = null; + this.detailsButton = this.outerContainerEl.querySelector('#details-button'); + + this.config = opt_config || Runner.config; + + this.dimensions = Runner.defaultDimensions; + + this.canvas = null; + this.canvasCtx = null; + + this.tRex = null; + + this.distanceMeter = null; + this.distanceRan = 0; + + this.highestScore = 0; + + this.time = 0; + this.runningTime = 0; + this.msPerFrame = 1000 / FPS; + this.currentSpeed = this.config.SPEED; + + this.obstacles = []; + + this.activated = false; // Whether the easter egg has been activated. + this.playing = false; // Whether the game is currently in play state. + this.crashed = false; + this.paused = false; + this.inverted = false; + this.invertTimer = 0; + this.resizeTimerId_ = null; + + this.playCount = 0; + + // Sound FX. + // this.audioBuffer = null; + // this.soundFx = {}; + + // Global web audio context for playing sounds. + this.audioContext = null; + + // Images. + this.images = {}; + this.imagesLoaded = 0; + + if (this.isDisabled()) { + this.setupDisabledRunner(); + } else { + this.loadImages(); + } + } + window['Runner'] = Runner; + + + /** + * Default game width. + * @const + */ + var DEFAULT_WIDTH = 600; + + /** + * Frames per second. + * @const + */ + var FPS = 60; + + /** @const */ + var IS_HIDPI = 2; + + /** @const */ + var IS_IOS = /iPad|iPhone|iPod/.test(window.navigator.platform); + + /** @const */ + var IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS; + + /** @const */ + var IS_TOUCH_ENABLED = 'ontouchstart' in window; + + /** + * Default game configuration. + * @enum {number} + */ + Runner.config = { + ACCELERATION: 0.001, + BG_CLOUD_SPEED: 0.2, + BOTTOM_PAD: 10, + CLEAR_TIME: 3000, + CLOUD_FREQUENCY: 0.5, + GAMEOVER_CLEAR_TIME: 750, + GAP_COEFFICIENT: 0.6, + GRAVITY: 0.6, + INITIAL_JUMP_VELOCITY: 12, + INVERT_FADE_DURATION: 12000, + INVERT_DISTANCE: 700, + MAX_CLOUDS: 6, + MAX_OBSTACLE_LENGTH: 3, + MAX_OBSTACLE_DUPLICATION: 2, + MAX_SPEED: 13, + MIN_JUMP_HEIGHT: 35, + MOBILE_SPEED_COEFFICIENT: 1.2, + RESOURCE_TEMPLATE_ID: 'audio-resources', + SPEED: 6, + SPEED_DROP_COEFFICIENT: 3 + }; + + + /** + * Default dimensions. + * @enum {string} + */ + Runner.defaultDimensions = { + WIDTH: DEFAULT_WIDTH, + HEIGHT: 150 + }; + + + /** + * CSS class names. + * @enum {string} + */ + Runner.classes = { + CANVAS: 'runner-canvas', + CONTAINER: 'runner-container', + CRASHED: 'crashed', + INVERTED: 'inverted', + SNACKBAR: 'snackbar', + SNACKBAR_SHOW: 'snackbar-show', + TOUCH_CONTROLLER: 'controller' + }; + + + /** + * Sprite definition layout of the spritesheet. + * @enum {Object} + */ + Runner.spriteDefinition = { + LDPI: { + CACTUS_LARGE: { x: 332, y: 2 }, + CACTUS_SMALL: { x: 228, y: 2 }, + CLOUD: { x: 86, y: 2 }, + HORIZON: { x: 2, y: 54 }, + MOON: { x: 484, y: 2 }, + RESTART: { x: 2, y: 2 }, + TEXT_SPRITE: { x: 655, y: 2 }, + TREX: { x: 848, y: 2 }, + STAR: { x: 645, y: 2 } + }, + HDPI: { + CACTUS_LARGE: { x: 652, y: 2 }, + CACTUS_SMALL: { x: 446, y: 2 }, + CLOUD: { x: 166, y: 2 }, + HORIZON: { x: 2, y: 104 }, + MOON: { x: 954, y: 2 }, + RESTART: { x: 2, y: 2 }, + TEXT_SPRITE: { x: 1292, y: 2 }, + TREX: { x: 1678, y: 2 }, + STAR: { x: 1276, y: 2 } + } + }; + + + /** + * Key code mapping. + * @enum {Object} + */ + Runner.keycodes = { + JUMP: { '38': 1, '32': 1 }, // Up, spacebar + DUCK: { '40': 1 }, // Down + RESTART: { '13': 1 } // Enter + }; + + + /** + * Runner event names. + * @enum {string} + */ + Runner.events = { + ANIM_END: 'webkitAnimationEnd', + CLICK: 'click', + KEYDOWN: 'keydown', + KEYUP: 'keyup', + MOUSEDOWN: 'mousedown', + MOUSEUP: 'mouseup', + RESIZE: 'resize', + TOUCHEND: 'touchend', + TOUCHSTART: 'touchstart', + VISIBILITY: 'visibilitychange', + BLUR: 'blur', + FOCUS: 'focus', + LOAD: 'load' + }; + + + Runner.prototype = { + /** + * Whether the easter egg has been disabled. CrOS enterprise enrolled devices. + * @return {boolean} + */ + isDisabled: function () { + // return loadTimeData && loadTimeData.valueExists('disabledEasterEgg'); + return false; + }, + + /** + * For disabled instances, set up a snackbar with the disabled message. + */ + setupDisabledRunner: function () { + this.containerEl = document.createElement('div'); + this.containerEl.className = Runner.classes.SNACKBAR; + this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg'); + this.outerContainerEl.appendChild(this.containerEl); + + // Show notification when the activation key is pressed. + document.addEventListener(Runner.events.KEYDOWN, function (e) { + if (Runner.keycodes.JUMP[e.keyCode]) { + this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW); + document.querySelector('.icon').classList.add('icon-disabled'); + } + }.bind(this)); + }, + + /** + * Setting individual settings for debugging. + * @param {string} setting + * @param {*} value + */ + updateConfigSetting: function (setting, value) { + if (setting in this.config && value != undefined) { + this.config[setting] = value; + + switch (setting) { + case 'GRAVITY': + case 'MIN_JUMP_HEIGHT': + case 'SPEED_DROP_COEFFICIENT': + this.tRex.config[setting] = value; + break; + case 'INITIAL_JUMP_VELOCITY': + this.tRex.setJumpVelocity(value); + break; + case 'SPEED': + this.setSpeed(value); + break; + } + } + }, + + /** + * Cache the appropriate image sprite from the page and get the sprite sheet + * definition. + */ + loadImages: function () { + if (IS_HIDPI) { + Runner.imageSprite = document.getElementById('offline-resources-2x'); + this.spriteDef = Runner.spriteDefinition.HDPI; + } else { + Runner.imageSprite = document.getElementById('offline-resources-2x'); + this.spriteDef = Runner.spriteDefinition.LDPI; + } + + if (Runner.imageSprite.complete) { + this.init(); + } else { + // If the images are not yet loaded, add a listener. + Runner.imageSprite.addEventListener(Runner.events.LOAD, + this.init.bind(this)); + } + }, + + /** + * Load and decode base 64 encoded sounds. + // */ + // loadSounds: function () { + // if (!IS_IOS) { + // this.audioContext = new AudioContext(); + + // var resourceTemplate = + // document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content; + + // for (var sound in Runner.sounds) { + // var soundSrc = + // resourceTemplate.getElementById(Runner.sounds[sound]).src; + // soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1); + // var buffer = decodeBase64ToArrayBuffer(soundSrc); + + // // Async, so no guarantee of order in array. + // this.audioContext.decodeAudioData(buffer, function (index, audioData) { + // this.soundFx[index] = audioData; + // }.bind(this, sound)); + // } + // } + // }, + + /** + * Sets the game speed. Adjust the speed accordingly if on a smaller screen. + * @param {number} opt_speed + */ + setSpeed: function (opt_speed) { + var speed = opt_speed || this.currentSpeed; + + // Reduce the speed on smaller mobile screens. + if (this.dimensions.WIDTH < DEFAULT_WIDTH) { + var mobileSpeed = speed * this.dimensions.WIDTH / DEFAULT_WIDTH * + this.config.MOBILE_SPEED_COEFFICIENT; + this.currentSpeed = mobileSpeed > speed ? speed : mobileSpeed; + } else if (opt_speed) { + this.currentSpeed = opt_speed; + } + }, + + /** + * Game initialiser. + */ + init: function () { + + this.adjustDimensions(); + this.setSpeed(); + + this.containerEl = document.createElement('div'); + this.containerEl.className = Runner.classes.CONTAINER; + + // Player canvas container. + this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH, + this.dimensions.HEIGHT, Runner.classes.PLAYER); + + this.canvasCtx = this.canvas.getContext('2d'); + this.canvasCtx.fillStyle = '#f7f7f7'; + this.canvasCtx.fill(); + Runner.updateCanvasScaling(this.canvas); + + // Horizon contains clouds, obstacles and the ground. + this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions, + this.config.GAP_COEFFICIENT); + + // Distance meter + this.distanceMeter = new DistanceMeter(this.canvas, + this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH); + + // Draw t-rex + this.tRex = new Trex(this.canvas, this.spriteDef.TREX); + + this.outerContainerEl.appendChild(this.containerEl); + + if (IS_MOBILE) { + this.createTouchController(); + } + + this.startListening(); + this.update(); + + window.addEventListener(Runner.events.RESIZE, + this.debounceResize.bind(this)); + }, + + /** + * Create the touch controller. A div that covers whole screen. + */ + createTouchController: function () { + this.touchController = document.createElement('div'); + this.touchController.className = Runner.classes.TOUCH_CONTROLLER; + this.outerContainerEl.appendChild(this.touchController); + }, + + /** + * Debounce the resize event. + */ + debounceResize: function () { + if (!this.resizeTimerId_) { + this.resizeTimerId_ = + setInterval(this.adjustDimensions.bind(this), 250); + } + }, + + /** + * Adjust game space dimensions on resize. + */ + adjustDimensions: function () { + clearInterval(this.resizeTimerId_); + this.resizeTimerId_ = null; + + var boxStyles = window.getComputedStyle(this.outerContainerEl); + var padding = Number(boxStyles.paddingLeft.substr(0, + boxStyles.paddingLeft.length - 2)); + + this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2; + + // Redraw the elements back onto the canvas. + if (this.canvas) { + this.canvas.width = this.dimensions.WIDTH; + this.canvas.height = this.dimensions.HEIGHT; + + Runner.updateCanvasScaling(this.canvas); + + this.distanceMeter.calcXPos(this.dimensions.WIDTH); + this.clearCanvas(); + this.horizon.update(0, 0, true); + this.tRex.update(0); + + // Outer container and distance meter. + if (this.playing || this.crashed || this.paused) { + this.containerEl.style.width = this.dimensions.WIDTH + 'px'; + this.containerEl.style.height = this.dimensions.HEIGHT + 'px'; + this.distanceMeter.update(0, Math.ceil(this.distanceRan)); + this.stop(); + } else { + this.tRex.draw(0, 0); + } + + // Game over panel. + if (this.crashed && this.gameOverPanel) { + this.gameOverPanel.updateDimensions(this.dimensions.WIDTH); + this.gameOverPanel.draw(); + } + } + }, + + /** + * Play the game intro. + * Canvas container width expands out to the full width. + */ + playIntro: function () { + if (!this.activated && !this.crashed) { + this.playingIntro = true; + this.tRex.playingIntro = true; + + // CSS animation definition. + var keyframes = '@-webkit-keyframes intro { ' + + 'from { width:' + Trex.config.WIDTH + 'px }' + + 'to { width: ' + this.dimensions.WIDTH + 'px }' + + '}'; + + // create a style sheet to put the keyframe rule in + // and then place the style sheet in the html head + var sheet = document.createElement('style'); + sheet.innerHTML = keyframes; + document.head.appendChild(sheet); + + this.containerEl.addEventListener(Runner.events.ANIM_END, + this.startGame.bind(this)); + + this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both'; + this.containerEl.style.width = this.dimensions.WIDTH + 'px'; + + // if (this.touchController) { + // this.outerContainerEl.appendChild(this.touchController); + // } + this.playing = true; + this.activated = true; + } else if (this.crashed) { + this.restart(); + } + }, + + + /** + * Update the game status to started. + */ + startGame: function () { + this.runningTime = 0; + this.playingIntro = false; + this.tRex.playingIntro = false; + this.containerEl.style.webkitAnimation = ''; + this.playCount++; + + // Handle tabbing off the page. Pause the current game. + document.addEventListener(Runner.events.VISIBILITY, + this.onVisibilityChange.bind(this)); + + window.addEventListener(Runner.events.BLUR, + this.onVisibilityChange.bind(this)); + + window.addEventListener(Runner.events.FOCUS, + this.onVisibilityChange.bind(this)); + }, + + clearCanvas: function () { + this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, + this.dimensions.HEIGHT); + }, + + /** + * Update the game frame and schedules the next one. + */ + update: function () { + this.updatePending = false; + + var now = getTimeStamp(); + var deltaTime = now - (this.time || now); + this.time = now; + + if (this.playing) { + this.clearCanvas(); + + if (this.tRex.jumping) { + this.tRex.updateJump(deltaTime); + } + + this.runningTime += deltaTime; + var hasObstacles = this.runningTime > this.config.CLEAR_TIME; + + // First jump triggers the intro. + if (this.tRex.jumpCount == 1 && !this.playingIntro) { + this.playIntro(); + } + + // The horizon doesn't move until the intro is over. + if (this.playingIntro) { + this.horizon.update(0, this.currentSpeed, hasObstacles); + } else { + deltaTime = !this.activated ? 0 : deltaTime; + this.horizon.update(deltaTime, this.currentSpeed, hasObstacles, + this.inverted); + } + + // Check for collisions. + var collision = hasObstacles && + checkForCollision(this.horizon.obstacles[0], this.tRex); + + if (!collision) { + this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame; + + if (this.currentSpeed < this.config.MAX_SPEED) { + this.currentSpeed += this.config.ACCELERATION; + } + } else { + this.gameOver(); + } + + // var playAchievementSound = this.distanceMeter.update(deltaTime, + // Math.ceil(this.distanceRan)); + + // if (playAchievementSound) { + // this.playSound(this.soundFx.SCORE); + // } + + // Night mode. + if (this.invertTimer > this.config.INVERT_FADE_DURATION) { + this.invertTimer = 0; + this.invertTrigger = false; + this.invert(); + } else if (this.invertTimer) { + this.invertTimer += deltaTime; + } else { + var actualDistance = + this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan)); + + if (actualDistance > 0) { + this.invertTrigger = !(actualDistance % + this.config.INVERT_DISTANCE); + + if (this.invertTrigger && this.invertTimer === 0) { + this.invertTimer += deltaTime; + this.invert(); + } + } + } + } + + if (this.playing || !this.activated) { + this.tRex.update(deltaTime); + this.scheduleNextUpdate(); + } + }, + + /** + * Event handler. + */ + handleEvent: function (e) { + return (function (evtType, events) { + switch (evtType) { + case events.KEYDOWN: + case events.TOUCHSTART: + case events.MOUSEDOWN: + this.onKeyDown(e); + break; + case events.KEYUP: + case events.TOUCHEND: + case events.MOUSEUP: + this.onKeyUp(e); + break; + } + }.bind(this))(e.type, Runner.events); + }, + + /** + * Bind relevant key / mouse / touch listeners. + */ + startListening: function () { + // Keys. + document.addEventListener(Runner.events.KEYDOWN, this); + document.addEventListener(Runner.events.KEYUP, this); + + if (IS_MOBILE) { + // Mobile only touch devices. + this.touchController.addEventListener(Runner.events.TOUCHSTART, this); + this.touchController.addEventListener(Runner.events.TOUCHEND, this); + this.containerEl.addEventListener(Runner.events.TOUCHSTART, this); + } else { + // Mouse. + document.addEventListener(Runner.events.MOUSEDOWN, this); + document.addEventListener(Runner.events.MOUSEUP, this); + } + }, + + /** + * Remove all listeners. + */ + stopListening: function () { + document.removeEventListener(Runner.events.KEYDOWN, this); + document.removeEventListener(Runner.events.KEYUP, this); + + if (IS_MOBILE) { + this.touchController.removeEventListener(Runner.events.TOUCHSTART, this); + this.touchController.removeEventListener(Runner.events.TOUCHEND, this); + this.containerEl.removeEventListener(Runner.events.TOUCHSTART, this); + } else { + document.removeEventListener(Runner.events.MOUSEDOWN, this); + document.removeEventListener(Runner.events.MOUSEUP, this); + } + }, + + /** + * Process keydown. + * @param {Event} e + */ + onKeyDown: function (e) { + // Prevent native page scrolling whilst tapping on mobile. + if (this.playing) { + e.preventDefault(); + } + + if (e.target != this.detailsButton) { + if (!this.crashed && (Runner.keycodes.JUMP[e.keyCode] || + e.type == Runner.events.TOUCHSTART)) { + if (!this.playing) { + // this.loadSounds(); + this.playing = true; + this.update(); + if (window.errorPageController) { + errorPageController.trackEasterEgg(); + } + } + // Play sound effect and jump on starting the game for the first time. + if (!this.tRex.jumping && !this.tRex.ducking) { + // this.playSound(this.soundFx.BUTTON_PRESS); + this.tRex.startJump(this.currentSpeed); + } + } + + if (this.crashed && e.type == Runner.events.TOUCHSTART && + e.currentTarget == this.containerEl) { + this.restart(); + } + } + + if (this.playing && !this.crashed && Runner.keycodes.DUCK[e.keyCode]) { + e.preventDefault(); + if (this.tRex.jumping) { + // Speed drop, activated only when jump key is not pressed. + this.tRex.setSpeedDrop(); + } + } + }, + + + /** + * Process key up. + * @param {Event} e + */ + onKeyUp: function (e) { + var keyCode = String(e.keyCode); + var isjumpKey = Runner.keycodes.JUMP[keyCode] || + e.type == Runner.events.TOUCHEND || + e.type == Runner.events.MOUSEDOWN; + + if (this.isRunning() && isjumpKey) { + this.tRex.endJump(); + } else if (Runner.keycodes.DUCK[keyCode]) { + this.tRex.speedDrop = false; + } else if (this.crashed) { + // Check that enough time has elapsed before allowing jump key to restart. + var deltaTime = getTimeStamp() - this.time; + + if (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) || + (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && + Runner.keycodes.JUMP[keyCode])) { + this.restart(); + } + } else if (this.paused && isjumpKey) { + // Reset the jump state + this.tRex.reset(); + this.play(); + } + }, + + /** + * Returns whether the event was a left click on canvas. + * On Windows right click is registered as a click. + * @param {Event} e + * @return {boolean} + */ + isLeftClickOnCanvas: function (e) { + return e.button != null && e.button < 2 && + e.type == Runner.events.MOUSEUP && e.target == this.canvas; + }, + + /** + * RequestAnimationFrame wrapper. + */ + scheduleNextUpdate: function () { + if (!this.updatePending) { + this.updatePending = true; + this.raqId = requestAnimationFrame(this.update.bind(this)); + } + }, + + /** + * Whether the game is running. + * @return {boolean} + */ + isRunning: function () { + return !!this.raqId; + }, + + /** + * Game over state. + */ + gameOver: function () { + // this.playSound(this.soundFx.HIT); + vibrate(200); + + this.stop(); + this.crashed = true; + this.distanceMeter.acheivement = false; + + this.tRex.update(100, Trex.status.CRASHED); + + // Game over panel. + if (!this.gameOverPanel) { + this.gameOverPanel = new GameOverPanel(this.canvas, + this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART, + this.dimensions); + } else { + this.gameOverPanel.draw(); + } + + // Update the high score. + if (this.distanceRan > this.highestScore) { + this.highestScore = Math.ceil(this.distanceRan); + this.distanceMeter.setHighScore(this.highestScore); + } + + // Reset the time clock. + this.time = getTimeStamp(); + }, + + stop: function () { + this.playing = false; + this.paused = true; + cancelAnimationFrame(this.raqId); + this.raqId = 0; + }, + + play: function () { + if (!this.crashed) { + this.playing = true; + this.paused = false; + this.tRex.update(0, Trex.status.RUNNING); + this.time = getTimeStamp(); + this.update(); + } + }, + + restart: function () { + if (!this.raqId) { + this.playCount++; + this.runningTime = 0; + this.playing = true; + this.crashed = false; + this.distanceRan = 0; + this.setSpeed(this.config.SPEED); + this.time = getTimeStamp(); + this.containerEl.classList.remove(Runner.classes.CRASHED); + this.clearCanvas(); + this.distanceMeter.reset(this.highestScore); + this.horizon.reset(); + this.tRex.reset(); + // this.playSound(this.soundFx.BUTTON_PRESS); + this.invert(true); + this.update(); + } + }, + + /** + * Pause the game if the tab is not in focus. + */ + onVisibilityChange: function (e) { + if (document.hidden || document.webkitHidden || e.type == 'blur' || + document.visibilityState != 'visible') { + this.stop(); + } else if (!this.crashed) { + this.tRex.reset(); + this.play(); + } + }, + + // /** + // * Play a sound. + // * @param {SoundBuffer} soundBuffer + // */ + // playSound: function (soundBuffer) { + // if (soundBuffer) { + // var sourceNode = this.audioContext.createBufferSource(); + // sourceNode.buffer = soundBuffer; + // sourceNode.connect(this.audioContext.destination); + // sourceNode.start(0); + // } + // }, + + /** + * Inverts the current page / canvas colors. + * @param {boolean} Whether to reset colors. + */ + invert: function (reset) { + if (reset) { + document.body.classList.toggle(Runner.classes.INVERTED, false); + this.invertTimer = 0; + this.inverted = false; + } else { + this.inverted = document.body.classList.toggle(Runner.classes.INVERTED, + this.invertTrigger); + } + } + }; + + + /** + * Updates the canvas size taking into + * account the backing store pixel ratio and + * the device pixel ratio. + * + * See article by Paul Lewis: + * http://www.html5rocks.com/en/tutorials/canvas/hidpi/ + * + * @param {HTMLCanvasElement} canvas + * @param {number} opt_width + * @param {number} opt_height + * @return {boolean} Whether the canvas was scaled. + */ + Runner.updateCanvasScaling = function (canvas, opt_width, opt_height) { + var context = canvas.getContext('2d'); + + // Query the various pixel ratios + var devicePixelRatio = Math.floor(window.devicePixelRatio) || 1; + var backingStoreRatio = Math.floor(context.webkitBackingStorePixelRatio) || 1; + var ratio = devicePixelRatio / backingStoreRatio; + + // Upscale the canvas if the two ratios don't match + if (devicePixelRatio !== backingStoreRatio) { + var oldWidth = opt_width || canvas.width; + var oldHeight = opt_height || canvas.height; + + canvas.width = oldWidth * ratio; + canvas.height = oldHeight * ratio; + + canvas.style.width = oldWidth + 'px'; + canvas.style.height = oldHeight + 'px'; + + // Scale the context to counter the fact that we've manually scaled + // our canvas element. + context.scale(ratio, ratio); + return true; + } else if (devicePixelRatio == 1) { + // Reset the canvas width / height. Fixes scaling bug when the page is + // zoomed and the devicePixelRatio changes accordingly. + canvas.style.width = canvas.width + 'px'; + canvas.style.height = canvas.height + 'px'; + } + return false; + }; + + + /** + * Get random number. + * @param {number} min + * @param {number} max + * @param {number} + */ + function getRandomNum(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + + /** + * Vibrate on mobile devices. + * @param {number} duration Duration of the vibration in milliseconds. + */ + function vibrate(duration) { + if (IS_MOBILE && window.navigator.vibrate) { + window.navigator.vibrate(duration); + } + } + + + /** + * Create canvas element. + * @param {HTMLElement} container Element to append canvas to. + * @param {number} width + * @param {number} height + * @param {string} opt_classname + * @return {HTMLCanvasElement} + */ + function createCanvas(container, width, height, opt_classname) { + var canvas = document.createElement('canvas'); + canvas.className = opt_classname ? Runner.classes.CANVAS + ' ' + + opt_classname : Runner.classes.CANVAS; + canvas.width = width; + canvas.height = height; + container.appendChild(canvas); + + return canvas; + } + + + /** + * Decodes the base 64 audio to ArrayBuffer used by Web Audio. + * @param {string} base64String + */ + function decodeBase64ToArrayBuffer(base64String) { + var len = (base64String.length / 4) * 3; + var str = atob(base64String); + var arrayBuffer = new ArrayBuffer(len); + var bytes = new Uint8Array(arrayBuffer); + + for (var i = 0; i < len; i++) { + bytes[i] = str.charCodeAt(i); + } + return bytes.buffer; + } + + + /** + * Return the current timestamp. + * @return {number} + */ + function getTimeStamp() { + return IS_IOS ? new Date().getTime() : performance.now(); + } + + + //****************************************************************************** + + + /** + * Game over panel. + * @param {!HTMLCanvasElement} canvas + * @param {Object} textImgPos + * @param {Object} restartImgPos + * @param {!Object} dimensions Canvas dimensions. + * @constructor + */ + function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.canvasDimensions = dimensions; + this.textImgPos = textImgPos; + this.restartImgPos = restartImgPos; + this.draw(); + }; + + + /** + * Dimensions used in the panel. + * @enum {number} + */ + GameOverPanel.dimensions = { + TEXT_X: 0, + TEXT_Y: 13, + TEXT_WIDTH: 191, + TEXT_HEIGHT: 11, + RESTART_WIDTH: 36, + RESTART_HEIGHT: 32 + }; + + + GameOverPanel.prototype = { + /** + * Update the panel dimensions. + * @param {number} width New canvas width. + * @param {number} opt_height Optional new canvas height. + */ + updateDimensions: function (width, opt_height) { + this.canvasDimensions.WIDTH = width; + if (opt_height) { + this.canvasDimensions.HEIGHT = opt_height; + } + }, + + /** + * Draw the panel. + */ + draw: function () { + var dimensions = GameOverPanel.dimensions; + + var centerX = this.canvasDimensions.WIDTH / 2; + + // Game over text. + var textSourceX = dimensions.TEXT_X; + var textSourceY = dimensions.TEXT_Y; + var textSourceWidth = dimensions.TEXT_WIDTH; + var textSourceHeight = dimensions.TEXT_HEIGHT; + + var textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2)); + var textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3); + var textTargetWidth = dimensions.TEXT_WIDTH; + var textTargetHeight = dimensions.TEXT_HEIGHT; + + var restartSourceWidth = dimensions.RESTART_WIDTH; + var restartSourceHeight = dimensions.RESTART_HEIGHT; + var restartTargetX = centerX - (dimensions.RESTART_WIDTH / 2); + var restartTargetY = this.canvasDimensions.HEIGHT / 2; + + if (IS_HIDPI) { + textSourceY *= 2; + textSourceX *= 2; + textSourceWidth *= 2; + textSourceHeight *= 2; + restartSourceWidth *= 2; + restartSourceHeight *= 2; + } + + textSourceX += this.textImgPos.x; + textSourceY += this.textImgPos.y; + + // Game over text from sprite. + this.canvasCtx.drawImage(Runner.imageSprite, + textSourceX, textSourceY, textSourceWidth, textSourceHeight, + textTargetX, textTargetY, textTargetWidth, textTargetHeight); + + // Restart button. + this.canvasCtx.drawImage(Runner.imageSprite, + this.restartImgPos.x, this.restartImgPos.y, + restartSourceWidth, restartSourceHeight, + restartTargetX, restartTargetY, dimensions.RESTART_WIDTH, + dimensions.RESTART_HEIGHT); + } + }; + + + //****************************************************************************** + + /** + * Check for a collision. + * @param {!Obstacle} obstacle + * @param {!Trex} tRex T-rex object. + * @param {HTMLCanvasContext} opt_canvasCtx Optional canvas context for drawing + * collision boxes. + * @return {Array} + */ + function checkForCollision(obstacle, tRex, opt_canvasCtx) { + var obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos; + + // Adjustments are made to the bounding box as there is a 1 pixel white + // border around the t-rex and obstacles. + var tRexBox = new CollisionBox( + tRex.xPos + 1, + tRex.yPos + 1, + tRex.config.WIDTH - 2, + tRex.config.HEIGHT - 2); + + var obstacleBox = new CollisionBox( + obstacle.xPos + 1, + obstacle.yPos + 1, + obstacle.typeConfig.width * obstacle.size - 2, + obstacle.typeConfig.height - 2); + + // Debug outer box + if (opt_canvasCtx) { + drawCollisionBoxes(opt_canvasCtx, tRexBox, obstacleBox); + } + + // Simple outer bounds check. + if (boxCompare(tRexBox, obstacleBox)) { + var collisionBoxes = obstacle.collisionBoxes; + var tRexCollisionBoxes = Trex.collisionBoxes.RUNNING; + + // Detailed axis aligned box check. + for (var t = 0; t < tRexCollisionBoxes.length; t++) { + for (var i = 0; i < collisionBoxes.length; i++) { + // Adjust the box to actual positions. + var adjTrexBox = + createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox); + var adjObstacleBox = + createAdjustedCollisionBox(collisionBoxes[i], obstacleBox); + var crashed = boxCompare(adjTrexBox, adjObstacleBox); + + // Draw boxes for debug. + if (opt_canvasCtx) { + drawCollisionBoxes(opt_canvasCtx, adjTrexBox, adjObstacleBox); + } + + if (crashed) { + return [adjTrexBox, adjObstacleBox]; + } + } + } + } + return false; + }; + + + /** + * Adjust the collision box. + * @param {!CollisionBox} box The original box. + * @param {!CollisionBox} adjustment Adjustment box. + * @return {CollisionBox} The adjusted collision box object. + */ + function createAdjustedCollisionBox(box, adjustment) { + return new CollisionBox( + box.x + adjustment.x, + box.y + adjustment.y, + box.width, + box.height); + }; + + + /** + * Draw the collision boxes for debug. + */ + function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) { + canvasCtx.save(); + canvasCtx.strokeStyle = '#f00'; + canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height); + + canvasCtx.strokeStyle = '#0f0'; + canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y, + obstacleBox.width, obstacleBox.height); + canvasCtx.restore(); + }; + + + /** + * Compare two collision boxes for a collision. + * @param {CollisionBox} tRexBox + * @param {CollisionBox} obstacleBox + * @return {boolean} Whether the boxes intersected. + */ + function boxCompare(tRexBox, obstacleBox) { + var crashed = false; + var tRexBoxX = tRexBox.x; + var tRexBoxY = tRexBox.y; + + var obstacleBoxX = obstacleBox.x; + var obstacleBoxY = obstacleBox.y; + + // Axis-Aligned Bounding Box method. + if (tRexBox.x < obstacleBoxX + obstacleBox.width && + tRexBox.x + tRexBox.width > obstacleBoxX && + tRexBox.y < obstacleBox.y + obstacleBox.height && + tRexBox.height + tRexBox.y > obstacleBox.y) { + crashed = true; + } + + return crashed; + }; + + + //****************************************************************************** + + /** + * Collision box object. + * @param {number} x X position. + * @param {number} y Y Position. + * @param {number} w Width. + * @param {number} h Height. + */ + function CollisionBox(x, y, w, h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + }; + + + //****************************************************************************** + + /** + * Obstacle. + * @param {HTMLCanvasCtx} canvasCtx + * @param {Obstacle.type} type + * @param {Object} spritePos Obstacle position in sprite. + * @param {Object} dimensions + * @param {number} gapCoefficient Mutipler in determining the gap. + * @param {number} speed + * @param {number} opt_xOffset + */ + function Obstacle(canvasCtx, type, spriteImgPos, dimensions, + gapCoefficient, speed, opt_xOffset) { + + this.canvasCtx = canvasCtx; + this.spritePos = spriteImgPos; + this.typeConfig = type; + this.gapCoefficient = gapCoefficient; + this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH); + this.dimensions = dimensions; + this.remove = false; + this.xPos = dimensions.WIDTH + (opt_xOffset || 0); + this.yPos = 0; + this.width = 0; + this.collisionBoxes = []; + this.gap = 0; + this.speedOffset = 0; + + // For animated obstacles. + this.currentFrame = 0; + this.timer = 0; + + this.init(speed); + }; + + /** + * Coefficient for calculating the maximum gap. + * @const + */ + Obstacle.MAX_GAP_COEFFICIENT = 1.5; + + /** + * Maximum obstacle grouping count. + * @const + */ + Obstacle.MAX_OBSTACLE_LENGTH = 3, + + + Obstacle.prototype = { + /** + * Initialise the DOM for the obstacle. + * @param {number} speed + */ + init: function (speed) { + this.cloneCollisionBoxes(); + + // Only allow sizing if we're at the right speed. + if (this.size > 1 && this.typeConfig.multipleSpeed > speed) { + this.size = 1; + } + + this.width = this.typeConfig.width * this.size; + + // Check if obstacle can be positioned at various heights. + if (Array.isArray(this.typeConfig.yPos)) { + var yPosConfig = IS_MOBILE ? this.typeConfig.yPosMobile : + this.typeConfig.yPos; + this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)]; + } else { + this.yPos = this.typeConfig.yPos; + } + + this.draw(); + + // Make collision box adjustments, + // Central box is adjusted to the size as one box. + // ____ ______ ________ + // _| |-| _| |-| _| |-| + // | |<->| | | |<--->| | | |<----->| | + // | | 1 | | | | 2 | | | | 3 | | + // |_|___|_| |_|_____|_| |_|_______|_| + // + if (this.size > 1) { + this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - + this.collisionBoxes[2].width; + this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width; + } + + // For obstacles that go at a different speed from the horizon. + if (this.typeConfig.speedOffset) { + this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset : + -this.typeConfig.speedOffset; + } + + this.gap = this.getGap(this.gapCoefficient, speed); + }, + + /** + * Draw and crop based on size. + */ + draw: function () { + var sourceWidth = this.typeConfig.width; + var sourceHeight = this.typeConfig.height; + + if (IS_HIDPI) { + sourceWidth = sourceWidth * 2; + sourceHeight = sourceHeight * 2; + } + + // X position in sprite. + var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) + + this.spritePos.x; + + // Animation frames. + if (this.currentFrame > 0) { + sourceX += sourceWidth * this.currentFrame; + } + + this.canvasCtx.drawImage(Runner.imageSprite, + sourceX, this.spritePos.y, + sourceWidth * this.size, sourceHeight, + this.xPos, this.yPos, + this.typeConfig.width * this.size, this.typeConfig.height); + }, + + /** + * Obstacle frame update. + * @param {number} deltaTime + * @param {number} speed + */ + update: function (deltaTime, speed) { + if (!this.remove) { + if (this.typeConfig.speedOffset) { + speed += this.speedOffset; + } + this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime); + + // Update frame + if (this.typeConfig.numFrames) { + this.timer += deltaTime; + if (this.timer >= this.typeConfig.frameRate) { + this.currentFrame = + this.currentFrame == this.typeConfig.numFrames - 1 ? + 0 : this.currentFrame + 1; + this.timer = 0; + } + } + this.draw(); + + if (!this.isVisible()) { + this.remove = true; + } + } + }, + + /** + * Calculate a random gap size. + * - Minimum gap gets wider as speed increses + * @param {number} gapCoefficient + * @param {number} speed + * @return {number} The gap size. + */ + getGap: function (gapCoefficient, speed) { + var minGap = Math.round(this.width * speed + + this.typeConfig.minGap * gapCoefficient); + var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT); + return getRandomNum(minGap, maxGap); + }, + + /** + * Check if obstacle is visible. + * @return {boolean} Whether the obstacle is in the game area. + */ + isVisible: function () { + return this.xPos + this.width > 0; + }, + + /** + * Make a copy of the collision boxes, since these will change based on + * obstacle type and size. + */ + cloneCollisionBoxes: function () { + var collisionBoxes = this.typeConfig.collisionBoxes; + + for (var i = collisionBoxes.length - 1; i >= 0; i--) { + this.collisionBoxes[i] = new CollisionBox(collisionBoxes[i].x, + collisionBoxes[i].y, collisionBoxes[i].width, + collisionBoxes[i].height); + } + } + }; + + + /** + * Obstacle definitions. + * minGap: minimum pixel space betweeen obstacles. + * multipleSpeed: Speed at which multiples are allowed. + * speedOffset: speed faster / slower than the horizon. + * minSpeed: Minimum speed which the obstacle can make an appearance. + */ + Obstacle.types = [ + { + type: 'CACTUS_SMALL', + width: 17, + height: 35, + yPos: 105, + multipleSpeed: 4, + minGap: 120, + minSpeed: 0, + collisionBoxes: [ + new CollisionBox(0, 7, 5, 27), + new CollisionBox(4, 0, 6, 34), + new CollisionBox(10, 4, 7, 14) + ] + }, + { + type: 'CACTUS_LARGE', + width: 25, + height: 50, + yPos: 90, + multipleSpeed: 7, + minGap: 120, + minSpeed: 0, + collisionBoxes: [ + new CollisionBox(0, 12, 7, 38), + new CollisionBox(8, 0, 7, 49), + new CollisionBox(13, 10, 10, 38) + ] + }, + ]; + + + //****************************************************************************** + /** + * T-rex game character. + * @param {HTMLCanvas} canvas + * @param {Object} spritePos Positioning within image sprite. + * @constructor + */ + function Trex(canvas, spritePos) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.spritePos = spritePos; + this.xPos = 0; + this.yPos = 0; + // Position when on the ground. + this.groundYPos = 0; + this.currentFrame = 0; + this.currentAnimFrames = []; + this.blinkDelay = 0; + this.blinkCount = 0; + this.animStartTime = 0; + this.timer = 0; + this.msPerFrame = 1000 / FPS; + this.config = Trex.config; + // Current status. + this.status = Trex.status.WAITING; + + this.jumping = false; + this.jumpVelocity = 0; + this.reachedMinHeight = false; + this.speedDrop = false; + this.jumpCount = 0; + this.jumpspotX = 0; + + this.init(); + }; + + + /** + * T-rex player config. + * @enum {number} + */ + Trex.config = { + DROP_VELOCITY: -5, + GRAVITY: 0.6, + HEIGHT: 47, + INIITAL_JUMP_VELOCITY: -10, + INTRO_DURATION: 1500, + MAX_JUMP_HEIGHT: 30, + MIN_JUMP_HEIGHT: 30, + SPEED_DROP_COEFFICIENT: 3, + SPRITE_WIDTH: 262, + START_X_POS: 50, + WIDTH: 42, + WIDTH_RUNNING: 50 + }; + + + /** + * Used in collision detection. + * @type {Array} + */ + Trex.collisionBoxes = { + RUNNING: [ + new CollisionBox(22, 0, 17, 16), + new CollisionBox(1, 18, 30, 9), + new CollisionBox(10, 35, 14, 8), + new CollisionBox(1, 24, 29, 5), + new CollisionBox(5, 30, 21, 4), + new CollisionBox(9, 34, 15, 4) + ] + }; + + + /** + * Animation states. + * @enum {string} + */ + Trex.status = { + CRASHED: 'CRASHED', + JUMPING: 'JUMPING', + RUNNING: 'RUNNING', + WAITING: 'WAITING' + }; + + /** + * Blinking coefficient. + * @const + */ + Trex.BLINK_TIMING = 500; + + + /** + * Animation config for different states. + * @enum {Object} + */ + Trex.animFrames = { + WAITING: { + frames: [43.5, 0], + msPerFrame: 1000 / 3 + }, + RUNNING: { + frames: [88, 140], + msPerFrame: 1000 / 12 + }, + CRASHED: { + frames: [190, 234], + msPerFrame: 1000 / 60 + }, + JUMPING: { + frames: [88], + msPerFrame: 1000 / 60 + }, + }; + + + Trex.prototype = { + /** + * T-rex player initaliser. + * Sets the t-rex to blink at random intervals. + */ + init: function () { + this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT - + Runner.config.BOTTOM_PAD; + this.yPos = this.groundYPos; + this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT; + + this.draw(0, 0); + this.update(0, Trex.status.WAITING); + }, + + /** + * Setter for the jump velocity. + * The approriate drop velocity is also set. + */ + setJumpVelocity: function (setting) { + this.config.INIITAL_JUMP_VELOCITY = -setting; + this.config.DROP_VELOCITY = -setting / 2; + }, + + /** + * Set the animation status. + * @param {!number} deltaTime + * @param {Trex.status} status Optional status to switch to. + */ + update: function (deltaTime, opt_status) { + this.timer += deltaTime; + + // Update the status. + if (opt_status) { + this.status = opt_status; + this.currentFrame = 0; + this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; + this.currentAnimFrames = Trex.animFrames[opt_status].frames; + + if (opt_status == Trex.status.WAITING) { + this.animStartTime = getTimeStamp(); + this.setBlinkDelay(); + } + } + + // Game intro animation, T-rex moves in from the left. + if (this.playingIntro && this.xPos < this.config.START_X_POS) { + this.xPos += Math.round((this.config.START_X_POS / + this.config.INTRO_DURATION) * deltaTime); + } + + if (this.status == Trex.status.WAITING) { + this.blink(getTimeStamp()); + } else { + this.draw(this.currentAnimFrames[this.currentFrame], 0); + } + + // Update the frame position. + if (this.timer >= this.msPerFrame) { + this.currentFrame = this.currentFrame == + this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; + this.timer = 0; + } + + // Speed drop becomes duck if the down key is still being pressed. + if (this.speedDrop && this.yPos == this.groundYPos) { + this.speedDrop = false; + } + }, + + /** + * Draw the t-rex to a particular position. + * @param {number} x + * @param {number} y + */ + draw: function (x, y) { + var sourceX = x; + var sourceY = y; + var sourceWidth = this.status === Trex.status.RUNNING || this.status === Trex.status.JUMPING ? + this.config.WIDTH_RUNNING : this.config.WIDTH; + var sourceHeight = this.config.HEIGHT; + + if (IS_HIDPI) { + sourceX *= 2; + sourceY *= 2; + sourceWidth *= 2; + sourceHeight *= 2; + } + + // Adjustments for sprite sheet position. + sourceX += this.spritePos.x; + sourceY += this.spritePos.y; + + // Standing / running + this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, + sourceWidth, sourceHeight, + this.xPos, this.yPos, + this.config.WIDTH, this.config.HEIGHT); + }, + + /** + * Sets a random time for the blink to happen. + */ + setBlinkDelay: function () { + this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING); + }, + + /** + * Make t-rex blink at random intervals. + * @param {number} time Current time in milliseconds. + */ + blink: function (time) { + var deltaTime = time - this.animStartTime; + + if (deltaTime >= this.blinkDelay) { + this.draw(this.currentAnimFrames[this.currentFrame], 0); + + if (this.currentFrame == 1) { + // Set new random delay to blink. + this.setBlinkDelay(); + this.animStartTime = time; + this.blinkCount++; + } + } + }, + + /** + * Initialise a jump. + * @param {number} speed + */ + startJump: function (speed) { + if (!this.jumping) { + this.update(0, Trex.status.JUMPING); + // Tweak the jump velocity based on the speed. + this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10); + this.jumping = true; + this.reachedMinHeight = false; + this.speedDrop = false; + } + }, + + /** + * Jump is complete, falling down. + */ + endJump: function () { + if (this.reachedMinHeight && + this.jumpVelocity < this.config.DROP_VELOCITY) { + this.jumpVelocity = this.config.DROP_VELOCITY; + } + }, + + /** + * Update frame for a jump. + * @param {number} deltaTime + * @param {number} speed + */ + updateJump: function (deltaTime, speed) { + var msPerFrame = Trex.animFrames[this.status].msPerFrame; + var framesElapsed = deltaTime / msPerFrame; + + // Speed drop makes Trex fall faster. + if (this.speedDrop) { + this.yPos += Math.round(this.jumpVelocity * + this.config.SPEED_DROP_COEFFICIENT * framesElapsed); + } else { + this.yPos += Math.round(this.jumpVelocity * framesElapsed); + } + + this.jumpVelocity += this.config.GRAVITY * framesElapsed; + + // Minimum height has been reached. + if (this.yPos < this.minJumpHeight || this.speedDrop) { + this.reachedMinHeight = true; + } + + // Reached max height + if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) { + this.endJump(); + } + + // Back down at ground level. Jump completed. + if (this.yPos > this.groundYPos) { + this.reset(); + this.jumpCount++; + } + + this.update(deltaTime); + }, + + /** + * Set the speed drop. Immediately cancels the current jump. + */ + setSpeedDrop: function () { + this.speedDrop = true; + this.jumpVelocity = 1; + }, + + /** + * @param {boolean} isDucking. + */ + setDuck: function (isDucking) { + if (isDucking && this.status != Trex.status.DUCKING) { + this.update(0, Trex.status.DUCKING); + this.ducking = true; + } else if (this.status == Trex.status.DUCKING) { + this.update(0, Trex.status.RUNNING); + this.ducking = false; + } + }, + + /** + * Reset the t-rex to running at start of game. + */ + reset: function () { + this.yPos = this.groundYPos; + this.jumpVelocity = 0; + this.jumping = false; + this.ducking = false; + this.update(0, Trex.status.RUNNING); + this.midair = false; + this.speedDrop = false; + this.jumpCount = 0; + } + }; + + + //****************************************************************************** + + /** + * Handles displaying the distance meter. + * @param {!HTMLCanvasElement} canvas + * @param {Object} spritePos Image position in sprite. + * @param {number} canvasWidth + * @constructor + */ + function DistanceMeter(canvas, spritePos, canvasWidth) { + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.image = Runner.imageSprite; + this.spritePos = spritePos; + this.x = 0; + this.y = 5; + + this.currentDistance = 0; + this.maxScore = 0; + this.highScore = 0; + this.container = null; + + this.digits = []; + this.acheivement = false; + this.defaultString = ''; + this.flashTimer = 0; + this.flashIterations = 0; + this.invertTrigger = false; + + this.config = DistanceMeter.config; + this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS; + this.init(canvasWidth); + }; + + + /** + * @enum {number} + */ + DistanceMeter.dimensions = { + WIDTH: 10, + HEIGHT: 12, + DEST_WIDTH: 11 + }; + + + /** + * Y positioning of the digits in the sprite sheet. + * X position is always 0. + * @type {Array} + */ + DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120]; + + + /** + * Distance meter config. + * @enum {number} + */ + DistanceMeter.config = { + // Number of digits. + MAX_DISTANCE_UNITS: 5, + + // Distance that causes achievement animation. + ACHIEVEMENT_DISTANCE: 100, + + // Used for conversion from pixel distance to a scaled unit. + COEFFICIENT: 0.025, + + // Flash duration in milliseconds. + FLASH_DURATION: 1000 / 4, + + // Flash iterations for achievement animation. + FLASH_ITERATIONS: 3 + }; + + + DistanceMeter.prototype = { + /** + * Initialise the distance meter to '00000'. + * @param {number} width Canvas width in px. + */ + init: function (width) { + var maxDistanceStr = ''; + + this.calcXPos(width); + this.maxScore = this.maxScoreUnits; + for (var i = 0; i < this.maxScoreUnits; i++) { + this.draw(i, 0); + this.defaultString += '0'; + maxDistanceStr += '9'; + } + + this.maxScore = parseInt(maxDistanceStr); + }, + + /** + * Calculate the xPos in the canvas. + * @param {number} canvasWidth + */ + calcXPos: function (canvasWidth) { + this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH * + (this.maxScoreUnits + 1)); + }, + + /** + * Draw a digit to canvas. + * @param {number} digitPos Position of the digit. + * @param {number} value Digit value 0-9. + * @param {boolean} opt_highScore Whether drawing the high score. + */ + draw: function (digitPos, value, opt_highScore) { + var sourceWidth = DistanceMeter.dimensions.WIDTH; + var sourceHeight = DistanceMeter.dimensions.HEIGHT; + var sourceX = DistanceMeter.dimensions.WIDTH * value; + var sourceY = 0; + + var targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH; + var targetY = this.y; + var targetWidth = DistanceMeter.dimensions.WIDTH; + var targetHeight = DistanceMeter.dimensions.HEIGHT; + + // For high DPI we 2x source values. + if (IS_HIDPI) { + sourceWidth *= 2; + sourceHeight *= 2; + sourceX *= 2; + } + + sourceX += this.spritePos.x; + sourceY += this.spritePos.y; + + this.canvasCtx.save(); + + if (opt_highScore) { + // Left of the current score. + var highScoreX = this.x - (this.maxScoreUnits * 2) * + DistanceMeter.dimensions.WIDTH; + this.canvasCtx.translate(highScoreX, this.y); + } else { + this.canvasCtx.translate(this.x, this.y); + } + + this.canvasCtx.drawImage(this.image, sourceX, sourceY, + sourceWidth, sourceHeight, + targetX, targetY, + targetWidth, targetHeight + ); + + this.canvasCtx.restore(); + }, + + /** + * Covert pixel distance to a 'real' distance. + * @param {number} distance Pixel distance ran. + * @return {number} The 'real' distance ran. + */ + getActualDistance: function (distance) { + return distance ? Math.round(distance * this.config.COEFFICIENT) : 0; + }, + + /** + * Update the distance meter. + * @param {number} distance + * @param {number} deltaTime + * @return {boolean} Whether the acheivement sound fx should be played. + */ + update: function (deltaTime, distance) { + var paint = true; + // var playSound = false; + + if (!this.acheivement) { + distance = this.getActualDistance(distance); + // Score has gone beyond the initial digit count. + if (distance > this.maxScore && this.maxScoreUnits == + this.config.MAX_DISTANCE_UNITS) { + this.maxScoreUnits++; + this.maxScore = parseInt(this.maxScore + '9'); + } else { + this.distance = 0; + } + + if (distance > 0) { + // Acheivement unlocked + if (distance % this.config.ACHIEVEMENT_DISTANCE == 0) { + // Flash score and play sound. + this.acheivement = true; + this.flashTimer = 0; + // playSound = true; + } + + // Create a string representation of the distance with leading 0. + var distanceStr = (this.defaultString + + distance).substr(-this.maxScoreUnits); + this.digits = distanceStr.split(''); + } else { + this.digits = this.defaultString.split(''); + } + } else { + // Control flashing of the score on reaching acheivement. + if (this.flashIterations <= this.config.FLASH_ITERATIONS) { + this.flashTimer += deltaTime; + + if (this.flashTimer < this.config.FLASH_DURATION) { + paint = false; + } else if (this.flashTimer > + this.config.FLASH_DURATION * 2) { + this.flashTimer = 0; + this.flashIterations++; + } + } else { + this.acheivement = false; + this.flashIterations = 0; + this.flashTimer = 0; + } + } + + // Draw the digits if not flashing. + if (paint) { + for (var i = this.digits.length - 1; i >= 0; i--) { + this.draw(i, parseInt(this.digits[i])); + } + } + + this.drawHighScore(); + return false; + }, + + /** + * Draw the high score. + */ + drawHighScore: function () { + this.canvasCtx.save(); + this.canvasCtx.globalAlpha = .8; + for (var i = this.highScore.length - 1; i >= 0; i--) { + this.draw(i, parseInt(this.highScore[i], 10), true); + } + this.canvasCtx.restore(); + }, + + /** + * Set the highscore as a array string. + * Position of char in the sprite: H - 10, I - 11. + * @param {number} distance Distance ran in pixels. + */ + setHighScore: function (distance) { + distance = this.getActualDistance(distance); + var highScoreStr = (this.defaultString + + distance).substr(-this.maxScoreUnits); + + this.highScore = ['10', '11', ''].concat(highScoreStr.split('')); + }, + + /** + * Reset the distance meter back to '00000'. + */ + reset: function () { + this.update(0); + this.acheivement = false; + } + }; + + + //****************************************************************************** + + /** + * Cloud background item. + * Similar to an obstacle object but without collision boxes. + * @param {HTMLCanvasElement} canvas Canvas element. + * @param {Object} spritePos Position of image in sprite. + * @param {number} containerWidth + */ + function Cloud(canvas, spritePos, containerWidth) { + this.canvas = canvas; + this.canvasCtx = this.canvas.getContext('2d'); + this.spritePos = spritePos; + this.containerWidth = containerWidth; + this.xPos = containerWidth; + this.yPos = 0; + this.remove = false; + this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP, + Cloud.config.MAX_CLOUD_GAP); + + this.init(); + }; + + + /** + * Cloud object config. + * @enum {number} + */ + Cloud.config = { + HEIGHT: 14, + MAX_CLOUD_GAP: 400, + MAX_SKY_LEVEL: 30, + MIN_CLOUD_GAP: 100, + MIN_SKY_LEVEL: 71, + WIDTH: 46 + }; + + + Cloud.prototype = { + /** + * Initialise the cloud. Sets the Cloud height. + */ + init: function () { + this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, + Cloud.config.MIN_SKY_LEVEL); + this.draw(); + }, + + /** + * Draw the cloud. + */ + draw: function () { + this.canvasCtx.save(); + var sourceWidth = Cloud.config.WIDTH; + var sourceHeight = Cloud.config.HEIGHT; + + if (IS_HIDPI) { + sourceWidth = sourceWidth * 2; + sourceHeight = sourceHeight * 2; + } + + this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x, + this.spritePos.y, + sourceWidth, sourceHeight, + this.xPos, this.yPos, + Cloud.config.WIDTH, Cloud.config.HEIGHT); + + this.canvasCtx.restore(); + }, + + /** + * Update the cloud position. + * @param {number} speed + */ + update: function (speed) { + if (!this.remove) { + this.xPos -= Math.ceil(speed); + this.draw(); + + // Mark as removeable if no longer in the canvas. + if (!this.isVisible()) { + this.remove = true; + } + } + }, + + /** + * Check if the cloud is visible on the stage. + * @return {boolean} + */ + isVisible: function () { + return this.xPos + Cloud.config.WIDTH > 0; + } + }; + + + //****************************************************************************** + + /** + * Nightmode shows a moon and stars on the horizon. + */ + function NightMode(canvas, spritePos, containerWidth) { + this.spritePos = spritePos; + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.xPos = containerWidth - 50; + this.yPos = 30; + this.currentPhase = 0; + this.opacity = 0; + this.containerWidth = containerWidth; + this.stars = []; + this.drawStars = false; + this.placeStars(); + }; + + /** + * @enum {number} + */ + NightMode.config = { + FADE_SPEED: 0.035, + HEIGHT: 40, + MOON_SPEED: 0.25, + NUM_STARS: 2, + STAR_SIZE: 9, + STAR_SPEED: 0.3, + STAR_MAX_Y: 70, + WIDTH: 20 + }; + + NightMode.phases = [140, 120, 100, 60, 40, 20, 0]; + + NightMode.prototype = { + /** + * Update moving moon, changing phases. + * @param {boolean} activated Whether night mode is activated. + * @param {number} delta + */ + update: function (activated, delta) { + // Moon phase. + if (activated && this.opacity == 0) { + this.currentPhase++; + + if (this.currentPhase >= NightMode.phases.length) { + this.currentPhase = 0; + } + } + + // Fade in / out. + if (activated && (this.opacity < 1 || this.opacity == 0)) { + this.opacity += NightMode.config.FADE_SPEED; + } else if (this.opacity > 0) { + this.opacity -= NightMode.config.FADE_SPEED; + } + + // Set moon positioning. + if (this.opacity > 0) { + this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED); + + // Update stars. + if (this.drawStars) { + for (var i = 0; i < NightMode.config.NUM_STARS; i++) { + this.stars[i].x = this.updateXPos(this.stars[i].x, + NightMode.config.STAR_SPEED); + } + } + this.draw(); + } else { + this.opacity = 0; + this.placeStars(); + } + this.drawStars = true; + }, + + updateXPos: function (currentPos, speed) { + if (currentPos < -NightMode.config.WIDTH) { + currentPos = this.containerWidth; + } else { + currentPos -= speed; + } + return currentPos; + }, + + draw: function () { + var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 : + NightMode.config.WIDTH; + var moonSourceHeight = NightMode.config.HEIGHT; + var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase]; + var moonOutputWidth = moonSourceWidth; + var starSize = NightMode.config.STAR_SIZE; + var starSourceX = Runner.spriteDefinition.LDPI.STAR.x; + + if (IS_HIDPI) { + moonSourceWidth *= 2; + moonSourceHeight *= 2; + moonSourceX = this.spritePos.x + + (NightMode.phases[this.currentPhase] * 2); + starSize *= 2; + starSourceX = Runner.spriteDefinition.HDPI.STAR.x; + } + + this.canvasCtx.save(); + this.canvasCtx.globalAlpha = this.opacity; + + // Stars. + if (this.drawStars) { + for (var i = 0; i < NightMode.config.NUM_STARS; i++) { + this.canvasCtx.drawImage(Runner.imageSprite, + starSourceX, this.stars[i].sourceY, starSize, starSize, + Math.round(this.stars[i].x), this.stars[i].y, + NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE); + } + } + + // Moon. + this.canvasCtx.drawImage(Runner.imageSprite, moonSourceX, + this.spritePos.y, moonSourceWidth, moonSourceHeight, + Math.round(this.xPos), this.yPos, + moonOutputWidth, NightMode.config.HEIGHT); + + this.canvasCtx.globalAlpha = 1; + this.canvasCtx.restore(); + }, + + // Do star placement. + placeStars: function () { + var segmentSize = Math.round(this.containerWidth / + NightMode.config.NUM_STARS); + + for (var i = 0; i < NightMode.config.NUM_STARS; i++) { + this.stars[i] = {}; + this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1)); + this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y); + + if (IS_HIDPI) { + this.stars[i].sourceY = Runner.spriteDefinition.HDPI.STAR.y + + NightMode.config.STAR_SIZE * 2 * i; + } else { + this.stars[i].sourceY = Runner.spriteDefinition.LDPI.STAR.y + + NightMode.config.STAR_SIZE * i; + } + } + }, + + reset: function () { + this.currentPhase = 0; + this.opacity = 0; + this.update(false); + } + + }; + + + //****************************************************************************** + + /** + * Horizon Line. + * Consists of two connecting lines. Randomly assigns a flat / bumpy horizon. + * @param {HTMLCanvasElement} canvas + * @param {Object} spritePos Horizon position in sprite. + * @constructor + */ + function HorizonLine(canvas, spritePos) { + this.spritePos = spritePos; + this.canvas = canvas; + this.canvasCtx = canvas.getContext('2d'); + this.sourceDimensions = {}; + this.dimensions = HorizonLine.dimensions; + this.sourceXPos = [this.spritePos.x, this.spritePos.x + + this.dimensions.WIDTH]; + this.xPos = []; + this.yPos = 0; + this.bumpThreshold = 0.5; + + this.setSourceDimensions(); + this.draw(); + }; + + + /** + * Horizon line dimensions. + * @enum {number} + */ + HorizonLine.dimensions = { + WIDTH: 600, + HEIGHT: 12, + YPOS: 127 + }; + + + HorizonLine.prototype = { + /** + * Set the source dimensions of the horizon line. + */ + setSourceDimensions: function () { + + for (var dimension in HorizonLine.dimensions) { + if (IS_HIDPI) { + if (dimension != 'YPOS') { + this.sourceDimensions[dimension] = + HorizonLine.dimensions[dimension] * 2; + } + } else { + this.sourceDimensions[dimension] = + HorizonLine.dimensions[dimension]; + } + this.dimensions[dimension] = HorizonLine.dimensions[dimension]; + } + + this.xPos = [0, HorizonLine.dimensions.WIDTH]; + this.yPos = HorizonLine.dimensions.YPOS; + }, + + /** + * Return the crop x position of a type. + */ + getRandomType: function () { + return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0; + }, + + /** + * Draw the horizon line. + */ + draw: function () { + this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[0], + this.spritePos.y, + this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, + this.xPos[0], this.yPos, + this.dimensions.WIDTH, this.dimensions.HEIGHT); + + this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[1], + this.spritePos.y, + this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, + this.xPos[1], this.yPos, + this.dimensions.WIDTH, this.dimensions.HEIGHT); + }, + + /** + * Update the x position of an indivdual piece of the line. + * @param {number} pos Line position. + * @param {number} increment + */ + updateXPos: function (pos, increment) { + var line1 = pos; + var line2 = pos == 0 ? 1 : 0; + + this.xPos[line1] -= increment; + this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH; + + if (this.xPos[line1] <= -this.dimensions.WIDTH) { + this.xPos[line1] += this.dimensions.WIDTH * 2; + this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH; + this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x; + } + }, + + /** + * Update the horizon line. + * @param {number} deltaTime + * @param {number} speed + */ + update: function (deltaTime, speed) { + var increment = Math.floor(speed * (FPS / 1000) * deltaTime); + + if (this.xPos[0] <= 0) { + this.updateXPos(0, increment); + } else { + this.updateXPos(1, increment); + } + this.draw(); + }, + + /** + * Reset horizon to the starting position. + */ + reset: function () { + this.xPos[0] = 0; + this.xPos[1] = HorizonLine.dimensions.WIDTH; + } + }; + + + //****************************************************************************** + + /** + * Horizon background class. + * @param {HTMLCanvasElement} canvas + * @param {Object} spritePos Sprite positioning. + * @param {Object} dimensions Canvas dimensions. + * @param {number} gapCoefficient + * @constructor + */ + function Horizon(canvas, spritePos, dimensions, gapCoefficient) { + this.canvas = canvas; + this.canvasCtx = this.canvas.getContext('2d'); + this.config = Horizon.config; + this.dimensions = dimensions; + this.gapCoefficient = gapCoefficient; + this.obstacles = []; + this.obstacleHistory = []; + this.horizonOffsets = [0, 0]; + this.cloudFrequency = this.config.CLOUD_FREQUENCY; + this.spritePos = spritePos; + this.nightMode = null; + + // Cloud + this.clouds = []; + this.cloudSpeed = this.config.BG_CLOUD_SPEED; + + // Horizon + this.horizonLine = null; + this.init(); + }; + + + /** + * Horizon config. + * @enum {number} + */ + Horizon.config = { + BG_CLOUD_SPEED: 0.2, + BUMPY_THRESHOLD: .3, + CLOUD_FREQUENCY: .5, + HORIZON_HEIGHT: 16, + MAX_CLOUDS: 6 + }; + + + Horizon.prototype = { + /** + * Initialise the horizon. Just add the line and a cloud. No obstacles. + */ + init: function () { + this.addCloud(); + this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON); + this.nightMode = new NightMode(this.canvas, this.spritePos.MOON, + this.dimensions.WIDTH); + }, + + /** + * @param {number} deltaTime + * @param {number} currentSpeed + * @param {boolean} updateObstacles Used as an override to prevent + * the obstacles from being updated / added. This happens in the + * ease in section. + * @param {boolean} showNightMode Night mode activated. + */ + update: function (deltaTime, currentSpeed, updateObstacles, showNightMode) { + this.runningTime += deltaTime; + this.horizonLine.update(deltaTime, currentSpeed); + this.nightMode.update(showNightMode); + this.updateClouds(deltaTime, currentSpeed); + + if (updateObstacles) { + this.updateObstacles(deltaTime, currentSpeed); + } + }, + + /** + * Update the cloud positions. + * @param {number} deltaTime + * @param {number} currentSpeed + */ + updateClouds: function (deltaTime, speed) { + var cloudSpeed = this.cloudSpeed / 1000 * deltaTime * speed; + var numClouds = this.clouds.length; + + if (numClouds) { + for (var i = numClouds - 1; i >= 0; i--) { + this.clouds[i].update(cloudSpeed); + } + + var lastCloud = this.clouds[numClouds - 1]; + + // Check for adding a new cloud. + if (numClouds < this.config.MAX_CLOUDS && + (this.dimensions.WIDTH - lastCloud.xPos) > lastCloud.cloudGap && + this.cloudFrequency > Math.random()) { + this.addCloud(); + } + + // Remove expired clouds. + this.clouds = this.clouds.filter(function (obj) { + return !obj.remove; + }); + } else { + this.addCloud(); + } + }, + + /** + * Update the obstacle positions. + * @param {number} deltaTime + * @param {number} currentSpeed + */ + updateObstacles: function (deltaTime, currentSpeed) { + // Obstacles, move to Horizon layer. + var updatedObstacles = this.obstacles.slice(0); + + for (var i = 0; i < this.obstacles.length; i++) { + var obstacle = this.obstacles[i]; + obstacle.update(deltaTime, currentSpeed); + + // Clean up existing obstacles. + if (obstacle.remove) { + updatedObstacles.shift(); + } + } + this.obstacles = updatedObstacles; + + if (this.obstacles.length > 0) { + var lastObstacle = this.obstacles[this.obstacles.length - 1]; + + if (lastObstacle && !lastObstacle.followingObstacleCreated && + lastObstacle.isVisible() && + (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) < + this.dimensions.WIDTH) { + this.addNewObstacle(currentSpeed); + lastObstacle.followingObstacleCreated = true; + } + } else { + // Create new obstacles. + this.addNewObstacle(currentSpeed); + } + }, + + removeFirstObstacle: function () { + this.obstacles.shift(); + }, + + /** + * Add a new obstacle. + * @param {number} currentSpeed + */ + addNewObstacle: function (currentSpeed) { + var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1); + var obstacleType = Obstacle.types[obstacleTypeIndex]; + + // Check for multiples of the same type of obstacle. + // Also check obstacle is available at current speed. + if (this.duplicateObstacleCheck(obstacleType.type) || + currentSpeed < obstacleType.minSpeed) { + this.addNewObstacle(currentSpeed); + } else { + var obstacleSpritePos = this.spritePos[obstacleType.type]; + + this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType, + obstacleSpritePos, this.dimensions, + this.gapCoefficient, currentSpeed, obstacleType.width)); + + this.obstacleHistory.unshift(obstacleType.type); + + if (this.obstacleHistory.length > 1) { + this.obstacleHistory.splice(Runner.config.MAX_OBSTACLE_DUPLICATION); + } + } + }, + + /** + * Returns whether the previous two obstacles are the same as the next one. + * Maximum duplication is set in config value MAX_OBSTACLE_DUPLICATION. + * @return {boolean} + */ + duplicateObstacleCheck: function (nextObstacleType) { + var duplicateCount = 0; + + for (var i = 0; i < this.obstacleHistory.length; i++) { + duplicateCount = this.obstacleHistory[i] == nextObstacleType ? + duplicateCount + 1 : 0; + } + return duplicateCount >= Runner.config.MAX_OBSTACLE_DUPLICATION; + }, + + /** + * Reset the horizon layer. + * Remove existing obstacles and reposition the horizon line. + */ + reset: function () { + this.obstacles = []; + this.horizonLine.reset(); + this.nightMode.reset(); + }, + + /** + * Update the canvas width and scaling. + * @param {number} width Canvas width. + * @param {number} height Canvas height. + */ + resize: function (width, height) { + this.canvas.width = width; + this.canvas.height = height; + }, + + /** + * Add a new cloud to the horizon. + */ + addCloud: function () { + this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD, + this.dimensions.WIDTH)); + } + }; +})(); + +new Runner('.interstitial-wrapper'); diff --git a/ui/games/Capybara.css b/ui/games/Capybara.css new file mode 100644 index 0000000000..379a59416a --- /dev/null +++ b/ui/games/Capybara.css @@ -0,0 +1,122 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + + html, body { + padding: 0; + margin: 0; + width: 100%; + height: 100%; +} + +.icon { + -webkit-user-select: none; + user-select: none; + display: inline-block; +} + +.hidden { + display: none; +} + + +/* Offline page */ + +.offline .interstitial-wrapper { + color: #2b2b2b; + font-size: 1em; + line-height: 1.55; + margin: 0 auto; + max-width: 600px; + padding-top: 100px; + width: 100%; +} + +.offline .runner-container { + height: 150px; + max-width: 600px; + overflow: hidden; + position: absolute; + top: 35px; + width: 44px; +} + +.offline .runner-canvas { + height: 150px; + max-width: 600px; + opacity: 1; + overflow: hidden; + position: absolute; + top: 0; + z-index: 2; +} + +.offline .controller { + background: rgba(247, 247, 247, .1); + height: 100vh; + left: 0; + position: absolute; + top: 0; + width: 100vw; + z-index: 1; +} + +#offline-resources { + display: none; +} + +@media (max-width: 420px) { + .suggested-left > #control-buttons, .suggested-right > #control-buttons { + float: none; + } + .snackbar { + left: 0; + bottom: 0; + width: 100%; + border-radius: 0; + } +} + +@media (max-height: 350px) { + h1 { + margin: 0 0 15px; + } + .interstitial-wrapper { + margin-top: 5%; + } + .nav-wrapper { + margin-top: 30px; + } +} + +@media (min-width: 600px) and (max-width: 736px) and (orientation: landscape) { + .offline .interstitial-wrapper { + margin-left: 0; + margin-right: 0; + } +} + +@media (min-width: 420px) and (max-width: 736px) and (min-height: 240px) and (max-height: 420px) and (orientation:landscape) { + .interstitial-wrapper { + margin-bottom: 100px; + } +} + +@media (min-height: 240px) and (orientation: landscape) { + .offline .interstitial-wrapper { + margin-bottom: 90px; + } +} + +@media (max-height: 320px) and (orientation: landscape) { + .offline .runner-container { + top: 10px; + } +} + +@media (max-width: 240px) { + .interstitial-wrapper { + overflow: inherit; + padding: 0 8px; + } +} \ No newline at end of file diff --git a/ui/games/CapybaraRunner.tsx b/ui/games/CapybaraRunner.tsx new file mode 100644 index 0000000000..163692fd3c --- /dev/null +++ b/ui/games/CapybaraRunner.tsx @@ -0,0 +1,42 @@ +/* eslint-disable max-len */ +import { Box } from '@chakra-ui/react'; +import Script from 'next/script'; +import React from 'react'; + +const CapybaraRunner = () => { + React.useEffect(() => { + const preventDefaultKeys = (e: KeyboardEvent) => { + if (e.code === 'Space' || e.code === 'ArrowUp' || e.code === 'ArrowDown') { + e.preventDefault(); + } + }; + + window.addEventListener('keydown', preventDefaultKeys); + + return () => { + window.removeEventListener('keydown', preventDefaultKeys); + }; + }, []); + + return ( + <> +