p0vOX
z>bKmx697x{Pq6`4c(^
zlN+AK88skw_SGvsc0h-c$Y2$D6=FX!BqPRvDx+gc7*K9~z>uDg?=alZKp?&j
zL^^1DS5D3qrCjy*lh4IhKXmL4!gNIvSeS%V>aa7Wlwi~2)z_qk9>x92hWf3
zt<4UNmQ+!@Jvfl`>8YZ8nQNeJ@1=d(8pu5cG@?aPE|*vcOmu10TNDn@{esnd92M#)
zJ5gc4rKxOlYj91S7pg=qLn%U}OqdCIGIMv4Ia#ljiRlyHTLy)IauEug5~{cPf%@%_BX%l
zlKZuwKir`-LZiu|3cKRYGCMyXumgYn5&!8RFR7g<%3A4Luf1PM>fh%sjr;`qZU3aN
z#l|Nmu_Rn$8?oJgp_h&BF;yDxnort*-2CBgiTQ*Pz>CU!;nlJ)?ePY$l9pT}N@
z>y^Y?%Pgp6RXtc}*ZYtq`4>|&4n7=5BTp-;3XH#&&UMPzl^Q0unf6+gwW#BqAnU8N
z2umf2{t8}2_b)TVg0KoT|&sw#48++w?&w)EuPcm}li!1c}L)m#R2@|a474NzOXgtZl>^^VqU!w*
zWk_V2-`pco7Wc1Qfl`3*Z+Z;U^z}XC9Cg4w$Wh@DAz
zUVqmP(prPle?aX(
zR_hRJVIJG?i9-buxWLB`nhT7oY&oP-W23WgpF|efG!-uln^x#tGkRolD9`w&Am_!?
zQ$1Q)BF!iFpF11HyYO1H)LkssI#O!?hvCe-ntOQtW9jed5hh|qMKe6dyOdUQuB@q9
zd%A{2#02Ha`uR@JZChc>l`O9CA)RX!-cq0N{r;>6&Ol++5#6a!f7Pyym;GvHt!|CD
zrFT?u8C$n?dIua#cHg<9B-4fU{On~!xbV%&saSPBzo4xiQ|)&Q(k+9dB;*2B9z}VX
zs#q0Cd3pJzQ)~}(5wx#-$`CE8BJQ=_ewv8_zLg+%mhZuu7gkW?=Ent5fezHACwcE?QCK9_)^3yBP9FVe%;ps^0brF}3J
zHL>uOrSr&pS@R%EE0O?a6?8}vJV;MQxHpn_j)*g$l*7IZsIDYLYPDDTI|W~KPvOse
zlx-YO*};l>3^&Lns<)&upuEX~`F@qPn;o^%X&?NTP;#`VbFMcE?svT#ID@O98P2`W
z8Vb%a66v3jnByIvr74vhUv(-d{<^#zEP5_?%E`n3Np!
zx!i|?U)vCFkOgrN@R_hTF6}7;iWn<;3D0cB2g>$Tf1)R$N6Wh^ZAf-=1#=YV=m7@Q
z?x=rxt!j-I+N+bI@^oJ{(n6G+H^85&@&n1E-HhLWlREB3Etv>;mIy5R{1WHm(%SmJkCrkNoUfe5c5{|E)6
zex3nQ{BXn-Q9>obv;3Pj(N}(zOFOP%wUiWJBBXk^v#+x10%{pYd>WAo7w(KB)Q>
zX@;foJ)<)W5d7r2X6`CmR=3ix6GzLsXa!5sl*uNfeK@c4bD(7v!Siu^I+64ipn67H
zm!vp1KsV<5O6cy!2OBrMs0Ac6X@U6XIWL8XTaQnSkz~&?o)G_s33NGitv>&q9xu
zvIpY^EXRkr=ypYJ4Cvzh_@hhw=0hnq?`|MG%d!phlZi$IFS6eO6cx){@RNJH4XF$)
z7_Fzc#6?-MAvWA(j=Q%sPf|NDbeaKS2^8h%HMjw>@eTt9)ROU*xWa?SG4Z*2mwpvJ
zS~zl)=;?u%<%d6|eQjbuLHCxrNzxB%>~Zf-B0PmP2Vu{V3pKN~qhE@cQjR}reBwpz
zIWf{Ru&9fok4Gu4&C}A0jAN3r)#!0i=?Z43N`RCP9{Y+bQ&|&Y(aRQ57!d3o;{&3_
zwthQk^A3aX3_%CzClZa~;hz{0sxmx3yFbrZasl>G%O^rVQD>Zp;*7!}JM#vuftWva
zA+HEz=jwSblEmC3{EUnNiO;94hlisF5>jha;74ehC|3v8EG{W0z
zWa*h;8&PWVDY4N*rj6e98Z0n#Q)c*B@{8I_ZB#9|!OQJ5{Zyg>Twe!d)z^Kwgvpyr
z0`HO$UX*M-J=s`aproP51U7IrdOA}&gc*uUdzZR0*uMQJWgyyOSOT)%Rs5Rm%fvv!Q-U=X%^gxrdfN+gi)tD`Be)dP(i3c|V3
zUM0X}Ym}*?2|>>p&;#pO^wL$CzL){Y^Hs7yaS&cr#aC!=_`C-tP^}H=&U?SVsizvy
z!oC88)mg^}mnpW|1cNNR7$mm9*nRjvbSic&dR!QmpvU
zr7|t6uU}~*>KXco1{8JY&D%oQ8cnu%hy&YDwL0zw{K_RS!}#YLk&4XM>StGvocM)H
zLLShGrsIbI@ydznJ9kTT7wS`wM4vh3Xe__d+f~Ra*BZxPQ9khDYsaa
z18ZIbjBG5t!S@&1#vQH33g&}-9Y-ea({@IE_FKGadKRS2zt;G7n7}o!OpW=mD}J32
zpxlp$*`yGyYwqRG-B_^nUdpX^!wuVI4L9k%#@Gl5-Hlt&%DERXfLtbyUL>DIk7g1%
zyt#IeRZEc9F4wI;`gQdR{)q(3Jdq~)Vtp7;hbNSZ^+u7kY%52c&LoY^<*j_}S@^8M
zxvL{C>z&%&5Bq8Z?Ue7hP|toa2t9J*x(C_X#Sl+X!!-ssu_x@QMBTho(dkS%MLbP-
zOXvI#$pssX=K?cD+&2(jy}9f6o}(q7y5SqY9odo)G;(`o!-u!`EVFd%I&|G9^<*4JA_+m+JRzh
z_u2xK3M2GXpT}Hr9I;uR^l0^QZu6h{#ouZ^|AR)h6;*m^?smArKT#ui
zL!vgNwu)}lX|8@Ah&umJ!1_I%8q%XIg#1X6Hw}G-2R16DzOBhuJ(EcnVk+$|7xd^&
z4=!(ub{Ve6daZi0mt8>KlR3NaTjJj>X`&kpJ1Lv1T7V_p)2>1?+};5FCQF%D$Z5TR
zbY)JKXg}Wfq2oe#KYg?O)|*e~@EAvV;7*gmcb!CdtH|xSjreodeE2Yk`ypo|_pQ8#
zonXf+;{TC`Gc^WQLRF~PRnbICK2BuSn9Y^6yc=-(~I{nzU`v(0s7U2K+=&Emms@$fow!TPX18$XSTclv_Gy6G}&3n
znCSz@DfdOuU%0rZaxy|uQ&v|6&Y@1c-*}
z9^f4l-i1Mw0)^7zS_Kvh*8{zeKfitFpH*1nSiqdp)^5NkgSsuZv;9nL=v&St<;|gz
zRulFX*2;J46`#Td?@tl*d6ptuyXU@&(#E{kprlp`gg^QOge1)i#+w2tt1FzPCHdBI
ztdxUv>u!zzoODNYz?y_kimG4BYvRH|nnyoU_buQJWn{5;*iAj1qN+CWXJXd
zkgY2Aw`jH5@Q2Xuf_#5ZV0hk~JUP5W^~MfFn=NZ-U^1`j9Ik=)d(gtHi4k6ve#%Ud
zCgw19pA9(@eS;#PDAMUcA;wH9(FDH0_KyF6zHcR>Oks+uP0On#%G+@iUAgwbEt&;R
z{j|*6O;7hF%v4QmS^Zck@%n?=Gw$i{+7N7lBHJh>;0N5s6k?}G*J>n>ha|7|>!sCg
zj^D(7`e$v11B%t92``!<^{}w*GPy^9mbyW^#+H&!F$?y4_Hk!2-LvTR`9;~o?a`6@
zEslM?_Ay?@hAM!CN1veh-bFB=O$K|oWaf$@yv1h4+U3dUO#1GBLcg7i^^?HHVnqC4
zvdwv8jaGPie*Qsn>+1<}N%}XGvIk#*ODktwcwkH(M_K2-fC_gJel}F=#mu#7%ch}L@aebvStMf|my2Jywi?d~)&}HlGfG=;Qcn=eWBh$0t9_?aR2GmSV!Pj?w
z)lWbEr{DN@;~Cd{)m(o7u(}SKkKWYWqgJ*8y3vQXwv=~I>xAQh4c8p3^f_C<`AOWR
z%w(0|bCSCnhK2-+_O}q~JW3-IZAb^mw>0pSs#Po63`YAE27NB)(eF4WqY980OYNYa
zNmR;}0GnNlPl$=6{%xjO-|pyepHd1+s89L2dtms&(Dlsg6Q8CoEbc;UHWR%`Aphea
zQd}Es&>~u1D-Q&w4_!}XESJ69uqi>TfIKp1nU^s$32UrE{oDrOu9YUwje^A81Z
z{;xj+fiJL^ikQ3zJ0%@|(p{loVB%ZhXQ}M$?5usFi7ScC>DP%E+-W5X0h;nnF*Jp3
zZTR(0bXkmmSTpT25cizbUd1xje1GFbe5bcdlF^|J@c)*i+rnZzWibsCcH<9ZXuJ(&W
z2C7QbNWpom#oU9hwePEt1>TwO27`JMHVF3P&bM&KHcfSnoO%
zgN}nS+ufGX6#KUOl;B&SqQOCS-gu>MbKJh)F6=03W>)MbRfJ||vwBIJZDnWMs;#53
zLZaXl?pgHx4z4{UH_2M5%3STzjb+CJ3cR%p1(uw#9u!r@#^?iWw8O9fQa!d#3Vlr=)f`Z4Fn&-T3B;#%B96ERM{snGN5
z>rxS4whLF)y?ZQYGe|J%Ds@wWYATh;{}@=^rFH#s+cTOw&2umQ5;3@mkP$<|%+gW6*8lfK8uCo0sZFoueM9~ujRLyEe|g3mV&Y9GuBh&Wpf79EgpEW)kDaL=l34#
zsPjyRIMNCWFM*oaDPBj=>7de9G$2^3y3fEl=+jl{xCnRu?!5!s@nsKAc3um!vA5x-
zU$LZx1deelB7tfrK(S8sVa9sc|A(J;z4H%vtHYFL0h%%I_(?Vfq}|p64n?-=F1j4s
z{JMZ#GJR`7_|i%LuWq+bxH~H}za1AiQj)grlr;a-ZfwgCokG6^q9)&c@=GIl?C@j_
z1FHWosP>p?BY;9gO6)X9R5(HP=T5n86_e~Ws#3=;YZz@)F5mPreB!!s2RClTHykR+
z#q-mzf+MVQB3Gsxo^ogyc!DkOfA|K{)L{K3+#(?Mz#XrznrKiDKGlY79sm
z5E90_T8M6wKSJO!ns@ftbOAmdRs4*5iw8liAq=1C;+bN<)qqe(D96#Vh)sq7Cv+Ea
z;f&0hghs-HFR6re^HkvYB@SCluh#`gVU|{Yqx8{Bzw)9Mq$m>?EC*fmVE%x
zw5~#UX6Bxs7)QinQi%)W@PTAX=F|Qs_^4sQk$|zJ@0Z=();U%`Q|D3k^LJvqu#o>?
ztK#f3iI7fJp=og;=aBP2dfWQthn|0n<6!;eQ05uXU=%x{tf~czTUB&@PAkmUQf)(>
zLmekw=CxYGHp`cIps!NSanPtp&zX1ss8tA<|Kll%eY%Vm8XotgtyPGXZa=ST{F)
zOY3)w@!8~KC+AdVAwK!8tvf$MXqA_KrB{(uOMb_NgC@rrkixBE3!i0!uk63l>(3*V
z(_n)DbejZEW>1@aFTdj8b^&NME9!ONK~An=@#6fl!p!LIjVc^I`x74FR-GVZl-ZJ{e0b)8DP&m3L18Bi{`Sx
zv<>cwdK_GcbxdmQ>Ji|{;Bz-Rh90g?##c6Ms{2B1Ia++1Cq{Jd#XRYmt<8S@!rI=q
z!Qlz={1f4w-`Fkmg3SVszVS@;@E4niH%jaN%!9eEB@Vd}$Id5inUQ8djVupZp?%MCBLA_@azC})YoIpotc)bHC7T_EKw|S=i;$
z2=_Qadm^i~0^7~pixIn0&QYhRn?d&Skfzm17otm*&|{kpS^S;gtBFIO@B^J6GwnKQ
zvaTh_rRP5qO}+efg^DP>c00;<_dtx56z3+YxaYQnM}0Y;a)hW#1j}>=uO61jrROZKO+9^sB}aLj8!yH&AZ|9_&ZfCGr;!Mi|UbLR!(n&(Ysy
zzvh9hmQU_I+HM-ETU02>vUJKl(BYs|s8`X>(=Sdx++~Dkr(ZL%p{Q~-nsXJj9)pnZ
z_x&;;<$40Mtq^)E{t0C!&iUX#F58k2d}tgkop9J@44Cj3@$Bmd3CgRJ$9Q8-gqVu6
zJG&_~_FY3g;+Q_AE$F1CI9o-!I7kFp$i8Wig`ijD*M^IO4a6=1YQ?VZVH3RmhRvAeo8@Lh
zu9UAwnjebmmveDs#>e$OOLeNDex{H_iAKQn
zv$KPfH{qm3W1cz?vMqj)O0@PEQu6BcOd+L~YsdHw1t1r6)c8kS)x8B+=vVjwxkT86y=LAK%Tid=~tp}
z7?1F9HlV#dQdBHy&tsm#gPMQr5ly%+4}q-ueZNi>1G-)1(1y#`)NFu#u7DWX5Qqr1
zT;OVX&B1^+gQ&LWbF}rIg{XJYCV(cN{UGp)W#ESBLF_G`rhj0zZEL~T=0L=
zDDXLE+-{yY`p83~0UbZ?mj*9XlHYpu*Zolp=$CMRKy2tYdR}}xYBFNkxx8{wo=dT$
zFTd_Ip$;xnIpZ-fGPWx%+@|TzdH~oZ7yMMq$(UQy(ARUmIn3a!H;C9<3(wIWAH1?qtLDqBVgGXkFl_
z-{OXUq5$GXnXUlm^CJ=(BjX-v0?58$hg);R>HAMJp43^^ZW^HOY^s$=^ZQ<0P_rd!
z+o$xU>hy+4k_3@-$3gk3ux=5|Bs1>50vwzMms#)q@I&J_tL(v#1j$?gJY`~_&e
zGs-)b&Ijhy(kDKwvPYdn-{<)!(yq?7o#2q+#`qdc|F}(B7-5z6w7`s#HVg_OG9TuP
zC@<3rJ=yRP?gF81;zA$3IYou=N}8b)VNXG+rHz!mO~fX;5+JAC*
zmey}#7O@FyO(qbv=o`TBAGtUo>O%_jR#43SC-zb}{3{29_rP34*!0}PR&>VuZ&PE4
zE_&LNV@%=r0-5uA&8k~2
zaigX2T-BowWK}hwG|B1*SpyE1iPMcfVZL
zX{709e&cbJ&hn8>sbPqq3+&D`HIA_v#hPp>$QR_g6@n)Q$9$=RY~
zZ@I7$93gdO4W%|6w+y1HH6H4inHmkoBxPaZk91Ewe{oP|egVZN*4AhkZ7
z>C2*s+|2Gb@<_AZ{b==0?mv)Zo6zt3*ier7wX~#{63I`VdR!)~##i6YXnnmZ-&>56
z>dah^*nc<{`{Q)3xc&5*vGXH+cg>vr7C>ZWe+p|N+*9Ne^aKG=^vmtrECdcFwf=H2
z2`-&`V?jtx!X~&}?MKfkK8F0HZ%{qa4u71>X1tU(c@%|7d2b-~7&rJeeYca!{JLzm
z8Atha=BO9Un*oyv%EJ+abJ2Mvbp4lz2=h!jSnNCc;nshk1bA6skkc2B@S4t@dKgLR
z|2Rum&PnlT_N9xpFVx<&S9;k-%w|gp%+1T`S3lY4Ze!-K@eaIeguG|!(m2BXmK^QU
z4^d>^V2tWz`xFhq;T-*U#WMli-Q_614oZZ~lX&ImZW8Ri=Z$TZ8le?GQL>llv1O-@
z#{0_DH}AjD3lXu~1K3Xwg%MPFfBR9RPGkX
z5hT>d{1);5#B~b|YSgPPa7zHuVrc-z)bp_zsdBKh8P}b>&|!V?;?=cX7B)qZd=uK|
zO;m83jl!%*Mx9g22)D@A*Fbeg+v*nq%Js_U~$49Jxyxf#}bqLwQnqUpLh(rEuV^_Z4Q-Ib(lo`;qD!3!3W-UIZ%>dUUcDm-~YT?(w;H1QVBdcsB)K`4yGmf)J%@O-HA)(lW2
zj{U$GM%&TxR3F;DG-XDn!wWl*
zgQjc&*Tp{Cv(QTrWucjmy{K+HRwE$zTtC9Y+(FfPuS5FLBHux!D;z&%S$`7XKv&-j+xORMj#~jJp*cD>PP#=s}cC&_yz>a1U>Dc;YbN
z#361@t{KdnM**FG2pnKJ($AYlSXPa4Elq+6hc$*qDl%H?cQc?{bI0j2crS-l%ojYa
z9+Nl-#=3dO=*~pVMaHzzRJc%7C%l)fUfv*C9NY_>rHPgn3(TCDI|bLy@z@{+mSqG`
zXy@m*t82EQ9)TMIu<|i8m*=h=GgjD(zS8@Fdoa`LzG*Q
z=&SX412)w){kZz$$N;l!RSWI_l+1M+j|k1eE1OU6Jl2$DboLG%xUSK%dQRM6;P^du
zxXZo{MUAp7C4_##dyCOa!R5}dG{WG?@lpo#q#zDn`bzluOrGisE(}_j(rXZXc&r=j
z{ysf0N~5M`MsK{l1nelOo~4EuGIAl8znW5X3>0c2pQTZgwc+EBehSX!hk5tp5{*E681EAbM`{QgT)s2QfWiDnm}KdUpU
zqQ8B;3tV6PEYo_d<+V)9bz(3mg^!boRroes+edI4aIrRTptE`%3bl&L#?uMc$Dq)B4fNA&J=m(U?0iurdQv(r}WlHo?c(hz$2D}i|Pnn|7
zY@VU05!PVV6(D5A)TL&?n^(Kw7-}F5&|XOpR_pwp-9_>nI@CqlPpAY}y0X-3f%ao@
zCCx|i-?Q)-`-a%1O*Vrt!h5g^Kgc!Cy-M48nE_qsBKeBpy{4(50Mp=;SU8ZFT2Lp@
z%!Q^pWt85N?jji`en6Am?&zT^3R)U1={809x06fYwuI4XcyI#YJt6)c4n(DlBNHL;
z!d9d6*5EBNt@uPX8=5d1!h0sQ7LjcGScfJ>pvJ8SI#<13qo&qZ(S$)TNiJJ
zV|1nI0}0qO*-@RM+U(~~FH774E{L{RgR}5={!0*bD&J4^M>7emBJLtF7jP+@u@vkip^i^z`jrtHWmGP>&CL`XkdtKspgHIPV7DPf%
zrq(LoBE?j_i{_|dyKCqnHnw%irh6>
z7=@m3&wJf$R)(5J|G4Jf6p>ren|;wtUATucJs%e)e9AzCi~e3@P~n+$sgCXyW2*Uq
zLXP&?o1BEK;AeO+?^0>l
z5D$0QPOGRG2~OqG<^_AbT*1xOsbA7W(_>;f3fx
z7-JN+pX5Z^UA2C`Q0T(-k2eRj?-i+5qa*op0?wwypVd1yE}xq#VYj$u9qM|bBS*@m
zVYh8VdrmCeDsw4_P`$?$l#WlZ<>1XMS|VhkXR|nc_xH`6uU8uzJ|P^UK9-Od)S>_J
z>YIanNvrHz&pkr#Q4>`+>HDU;uJ&2jUX_5s*SHgBY&>egs}1=T^_zDoEDxHzOm`nt
z6Bm)6+UZ>uti49s51uk_^Bxl0=BYDxx!0Ecwp#>EK7}nzGPXlxr=&CHtEJ|lEylZc
zhfQZWduD{D1ileJdokX#d&7x|I+t^Nx1OzbZzwj~@OCIu)BUv9Ta(;O`@*@|8>5)E71Go{d4EfH@w+j
zii$Zc45p0EETIcZC>xC?fY%?rJ`kPT>p|8dnscoNmkonAsG)i|;}<95Foo-L2qO-gWcqQK5;Xks|5({ZG>U_%<2xf4TWakY3|4yZ3{E
zRZ27qd03?0ExF;F>v31X8&kEqJnLHpl-9zul#g
zg9VPymyRT~JNZV@oA)QWkn1|FUX1zrJ-${Kl5jaYYG{kWp@h8kGom4KM*81we+=_I
zeg2hyjNws_mD!c{Y0STE~tXtEE=728z?
zKd!W~tGnoO!L7VL`6gvLIhRvv^x3A6n|m{J*W9olAhquGd(&OLZr{W4*?kn>jBO&m
zedFx+u9jUG?Rq73Rjr~rd)*;?g54`EKliWcA}UgPMoK{zQN!i;w#_3Fwo`H!fk4K!
zd<3s*Hf%`b9J=3;fQ}XDJrd)I+)htJ17_cy9L3FKkPY1EH;4i1>~N#y
zC~>{^!$3*hrJHXF@7GrJ>V_h#eJ=U?9yJKjiZvf`ZQU)aDv%M39vuR{kK=3$k>qm{
zH9|S@Q500(E7(jA&1)5*M~7!A@^3R%@dJIG$otqfH<-?|(B(Oio~TZdO{NLz^BslD
zim$fqrQn^=9p6&93^T#I%Nsptg3%*%4^qIb9F6bOlBux4Ws8Q_a=mEsE)UA4Lr|ed
zSwXUe1$umS0Yv&ZwjB58^1wPk&9y=0qqxn$bW;;DQewk=UH9`)nHX(79^pR0a|OR-
zNFLeoX~_NZO6EQOqk`(R^|c81h3pa%JXX>g2+A-OpjlKFOl1N$Xa!%u(DAgb@Q6^=
z_ls|&=Qn|k4~`~;bqhz@5W!A*Xa;yoIalwF4Hr!?9^r1mEu%5BoP=f4LE;TbZT^6m
z%_#Do5{lyP(ZseoZqz22^4`5Mf;nkWO-i8w|#&M39jup*JX?zWDPWjZp!KEPFd>
ziEmkkG5F*WvijOa_gk$uEJZ(HzFMlW#9?e0O>Mzh!>V)UO
zeM4}3HzUcG<=%vB_&{Zy2&11vgoS3w)ct6x#qlp0Ver^6s&vaIq?yPKZb3XadKava
zr7foEEkIF(Nu2!f*x5Qk=~bFOctiS4L9`eq1L6KmWx3agG!eK!9Dg6+LBsDMft95Y
z?r|zq+LYJ?a}mk!ra=1-j*L)Za3qdw{6O-U>}rA@c-5FWk0-eFJW?wDDY2QrZP_z6
ze1ieC25@QBo&$OB4E=;=bz<`fcf%6pSb+dN&t&Dt^nd{3Fg?Xs7(odvj4`0c{y-3N
zml5tJDrCLPfV@Y}ppuhF#gJOuiCF
z3Eh0UBrLiAt0Br?;EUk#FPFbM1M2WVP)j#JNDE^aD@1bt`boBCKHv?K-_|}x=-VR=3OWFq((w*pFKm*?!
z@{GV)s|}swv~npx4j59BIe4l)^bI#aZS+|F=z*v)u?Bh2D4s~(OFfy%JTKve>)EGi
zcZ9hN&q^U{-T~BF;b&A-Y#=#n5}rB2ma{7y%pwktX7ngI)A5G7ZTqUkJP~j3#QZ2c
zdTxYly@lA9*k&wa4E~P+kdq!hK9Md>B=15~HaU(9BCRE {
// layer display
const name = "The Factory";
const color = "grey";
+ const FACTORY_COMPONENTS = {
+ cursor: {
+ imageSrc: cursor,
+ name: "Cursor",
+ description: "Use this to move.",
+ tick: 0,
+ directionIn: FactoryDirections.None,
+ directionOut: FactoryDirections.None,
+ consumption: {},
+ consumptionStock: {},
+ production: {},
+ productionStock: {}
+ },
+ conveyor: {
+ directionIn: FactoryDirections.Any,
+ directionOut: FactoryDirections.Any,
+ imageSrc: conveyor,
+ name: "Conveyor",
+ description: "Moves 1 item per tick.",
+ tick: 1,
+ consumption: {},
+ consumptionStock: {},
+ production: {},
+ productionStock: {}
+ },
+ square: {
+ directionIn: FactoryDirections.Any,
+ directionOut: FactoryDirections.None,
+ imageSrc: square,
+ name: "???",
+ description: "Produces 1 square every 1 tick.",
+ tick: 1,
+ production: {
+ square: 1
+ },
+ productionStock: {
+ square: Infinity
+ },
+ consumption: {},
+ consumptionStock: {},
+ onProduce(times) {
+ }
+ }
+ } as Record;
+
+ type FactoryCompNames = keyof typeof FACTORY_COMPONENTS;
+ type BuildableCompName = Exclude;
+
+ interface FactoryComponent {
+ type: BuildableCompName;
+ ticksDone: number;
+ // current consumption stock
+ consumptionStock: Record;
+
+ // current production stock
+ productionStock: Record;
+ }
+ interface FactoryComponentDeclaration {
+ tick: number;
+ directionIn: FactoryDirection;
+ directionOut: FactoryDirection;
+ imageSrc: string;
+ name: string;
+ description: string;
+
+ // amount it consumes
+ consumption: Record;
+ // maximum stock of consumption
+ consumptionStock: Record;
+ // amount it produces
+ production: Record;
+ // maximum stock of production
+ productionStock: Record;
+
+ // on produce, do something
+ onProduce?: (times: number) => void;
+ // can it produce? (in addtion to the stock check)
+ canProduce?: ComputedRef;
+ }
+
+ interface FactoryInternal {
+ sprite: Sprite;
+ canProduce: ComputedRef;
+ }
+
// mouse positions
const mouseCoords = reactive({
x: 0,
@@ -88,36 +159,124 @@ const factory = createLayer(id, () => {
});
const isMouseHoverShown = ref(false);
const whatIsHovered = ref("");
- const compSelected = ref("");
- const components: Ref = persistent(
+ const compSelected = ref("cursor");
+
+ const componentData: Ref<(FactoryComponent | null)[][]> = persistent(
Array(blockAmts.h)
.fill(undefined)
- .map(() => Array(blockAmts.w).fill(undefined))
+ .map(() => Array(blockAmts.w).fill(null))
);
+ const components: (FactoryInternal | null)[][] = Array(blockAmts.h)
+ .fill(undefined)
+ .map(() => Array(blockAmts.w).fill(null));
+ window.components = components;
// pixi
const app = new Application(size);
const graphicContainer = new Graphics();
const spriteContainer = new Container();
- let blockWidth = 0;
- let blockHeight = 0;
- app.stage.addChild(graphicContainer, spriteContainer);
+ const movingBlocks = new Container();
+ const blockWidth = ref(app.screen.width / blockAmts.w);
+ const blockHeight = ref(app.screen.height / blockAmts.h);
- globalBus.on("update", () => {
- blockWidth = app.screen.width / blockAmts.w;
- blockHeight = app.screen.height / blockAmts.h;
+ graphicContainer.zIndex = 1;
+ app.stage.addChild(graphicContainer, spriteContainer, movingBlocks);
+ app.stage.sortableChildren = true;
+
+ globalBus.on("update", diff => {
+ blockWidth.value = app.screen.width / blockAmts.w;
+ blockHeight.value = app.screen.height / blockAmts.h;
+
+ // will change soon:tm:
+ const tick = diff;
+ for (const y of components.keys()) {
+ for (const x of components[y].keys()) {
+ const data = componentData.value[y][x];
+ const compData = components[y][x];
+ //console.log(compData, data)
+ if (data === null || compData === null) continue;
+ const factoryData = FACTORY_COMPONENTS[data.type];
+ if (data.ticksDone >= factoryData.tick) {
+ console.log(compData.canProduce);
+ if (!compData.canProduce.value) continue;
+ const cyclesDone = Math.floor(data.ticksDone / factoryData.tick);
+ console.log("produce", data.ticksDone, factoryData.tick);
+ factoryData.onProduce?.(cyclesDone);
+ for (const [key, val] of Object.entries(factoryData.consumption)) {
+ data.consumptionStock[key] -= val;
+ }
+ for (const [key, val] of Object.entries(factoryData.production)) {
+ data.productionStock[key] -= val;
+ }
+ data.ticksDone -= cyclesDone * factoryData.tick;
+ } else {
+ data.ticksDone += tick;
+ }
+ }
+ }
});
+ globalBus.on("onLoad", async () => {
+ for (const y of componentData.value.keys()) {
+ for (const x of componentData.value[y].keys()) {
+ changeFactoryComponent(y, x);
+ }
+ }
+ });
+
+ async function changeFactoryComponent(y: number, x: number) {
+ const comp = componentData.value[y][x];
+ if (comp === null) return;
+ const data = FACTORY_COMPONENTS[comp.type];
+ const sprite = await positionSprite(
+ FACTORY_COMPONENTS[comp.type].imageSrc,
+ y * blockHeight.value,
+ x * blockWidth.value,
+ blockWidth.value,
+ blockHeight.value
+ );
+ components[y][x] = {
+ sprite,
+ canProduce: computed(() => {
+ if (!(data.canProduce?.value ?? true)) return false;
+ for (const [key, res] of Object.entries(comp.productionStock)) {
+ // if the current stock + production is more than you can handle
+ if (res + data.production[key] > data.productionStock[key]) return false;
+ }
+ for (const [key, res] of Object.entries(comp.consumptionStock)) {
+ // make sure you have enough to produce
+ if (res < data.consumptionStock[key]) return false;
+ }
+ return true;
+ })
+ };
+ spriteContainer.addChild(sprite);
+ }
+
+ function removeFactoryComponent(y: number, x: number) {
+ const comp = components[y][x];
+ if (comp === null) return;
+ comp.sprite.destroy();
+ componentData.value[y][x] = null;
+ components[y][x] = null;
+ spriteContainer.removeChild(comp.sprite);
+ }
+
// draw graphics
function updateGraphics() {
graphicContainer.clear();
- if (isMouseHoverShown.value) {
- graphicContainer.beginFill(0x808080);
+ if (isMouseHoverShown.value && compSelected.value !== "cursor") {
+ const { x, y } = {
+ x: roundDownTo(mouseCoords.x, blockWidth.value) / blockWidth.value,
+ y: roundDownTo(mouseCoords.y, blockHeight.value) / blockHeight.value
+ };
+ console.log(isMouseHoverShown.value, x, y);
+ graphicContainer.beginFill(components[y][x] !== null ? 0xff0000 : 0xffff00);
graphicContainer.drawRect(
- roundDownTo(mouseCoords.x, blockWidth),
- roundDownTo(mouseCoords.y, blockHeight),
- blockWidth,
- blockHeight
+ roundDownTo(mouseCoords.x, blockWidth.value),
+ roundDownTo(mouseCoords.y, blockHeight.value),
+ blockWidth.value,
+ blockHeight.value
);
}
}
@@ -131,24 +290,31 @@ const factory = createLayer(id, () => {
mouseCoords.y = y;
}
async function onClick(e: MouseEvent) {
- if (compSelected.value === "") {
- console.warn("You haven't hovered over anything, trickster!");
- return;
+ const coords = getRelativeCoords(e);
+ const x = roundDownTo(coords.x, blockWidth.value) / blockWidth.value;
+ const y = roundDownTo(coords.y, blockHeight.value) / blockHeight.value;
+ if (e.button === 0) {
+ // you shouldn't be putting down a mouse
+ if (compSelected.value === "cursor") return;
+ // should not already be placed
+ if (components[y][x] !== null) return;
+
+ const basicData = FACTORY_COMPONENTS[compSelected.value];
+ componentData.value[y][x] = {
+ type: compSelected.value,
+ ticksDone: 0,
+ consumptionStock: Object.fromEntries(
+ Object.entries(basicData.consumptionStock).map(i => [i[0], 0])
+ ),
+ productionStock: Object.fromEntries(
+ Object.entries(basicData.productionStock).map(i => [i[0], 0])
+ )
+ };
+ await changeFactoryComponent(y, x);
+ } else if (e.button === 2) {
+ // right click
+ removeFactoryComponent(y, x);
}
- let { x, y } = getRelativeCoords(e);
- x = roundDownTo(x, blockWidth) / blockWidth;
- y = roundDownTo(y, blockHeight) / blockHeight;
- const basicData = structuredClone(
- FACTORY_COMPONENTS[compSelected.value]
- ) as FactoryComponent;
- basicData.type = compSelected.value;
- const sheet = await Assets.load(basicData.imageSrc);
- basicData.sprite = new Sprite(sheet);
- basicData.sprite.x = x;
- basicData.sprite.y = y;
- basicData.sprite.width = blockWidth;
- basicData.sprite.height = blockHeight;
- spriteContainer.addChild(basicData.sprite);
}
function onMouseEnter() {
isMouseHoverShown.value = true;
@@ -160,6 +326,7 @@ const factory = createLayer(id, () => {
player.tabs.splice(0, Infinity, "main");
}
function onComponentHover(name: FactoryCompNames | "") {
+ if (compSelected.value !== "cursor") return;
whatIsHovered.value = name;
}
function onCompClick(name: FactoryCompNames) {
@@ -171,6 +338,7 @@ const factory = createLayer(id, () => {
color,
minWidth: 700,
minimizable: false,
+ componentData,
display: jsx(() => (
-
+ |
e.preventDefault()}
/>
|
- )),
- components
+ ))
};
});
export default factory;
diff --git a/src/data/layers/styles/factory.css b/src/data/layers/styles/factory.css
index a47a172..7300361 100644
--- a/src/data/layers/styles/factory.css
+++ b/src/data/layers/styles/factory.css
@@ -7,6 +7,7 @@
.factoryDisp {
border-left: 1px solid white;
border-bottom: 1px solid white;
+ border-right: 1px solid white;
}
.info {
border-bottom: 1px solid white;
diff --git a/src/util/save.ts b/src/util/save.ts
index 8ddddad..2de5092 100644
--- a/src/util/save.ts
+++ b/src/util/save.ts
@@ -137,7 +137,8 @@ setInterval(() => {
window.onbeforeunload = () => {
if (
player.autosave &&
- (layers as any).main.days[(layers as any).main.day.value - 1].opened.value
+ ((layers as any).main.days[(layers as any).main.day.value - 1].opened.value ||
+ import.meta.env.DEV)
) {
save();
}