From 93b4877c2d4b42d7ba090fc2dabeed4ac3f932a4 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Fri, 24 Jul 2026 16:01:13 -0600 Subject: [PATCH] **Update game icons assets** Refresh game icons in both source (PSD) and exported (PNG) formats to reflect updated design. --- .../civilization-cities-steampunk.png | Bin 0 -> 139493 bytes assets/images/game-icons.png | Bin 339240 -> 342958 bytes assets/images/game-icons.psd | Bin 873712 -> 883213 bytes data/totalannihilation-artwork.json | 99 ++ data/totalannihilation-rules.json | 276 ++++ src/games/totalannihilation/TAAI.js | 533 ++++++ src/games/totalannihilation/TAArt.js | 561 +++++++ src/games/totalannihilation/TAFx.js | 229 +++ src/games/totalannihilation/TALogic.js | 1451 +++++++++++++++++ src/games/totalannihilation/TAMapGen.js | 295 ++++ src/games/totalannihilation/TANav.js | 441 +++++ src/games/totalannihilation/TARules.js | 307 ++++ src/games/totalannihilation/TAWorldView.js | 475 ++++++ 13 files changed, 4667 insertions(+) create mode 100644 assets/images/civilization/civilization-cities-steampunk.png create mode 100644 data/totalannihilation-artwork.json create mode 100644 data/totalannihilation-rules.json create mode 100644 src/games/totalannihilation/TAAI.js create mode 100644 src/games/totalannihilation/TAArt.js create mode 100644 src/games/totalannihilation/TAFx.js create mode 100644 src/games/totalannihilation/TALogic.js create mode 100644 src/games/totalannihilation/TAMapGen.js create mode 100644 src/games/totalannihilation/TANav.js create mode 100644 src/games/totalannihilation/TARules.js create mode 100644 src/games/totalannihilation/TAWorldView.js diff --git a/assets/images/civilization/civilization-cities-steampunk.png b/assets/images/civilization/civilization-cities-steampunk.png new file mode 100644 index 0000000000000000000000000000000000000000..c3dc646613ac2db94311882716717b151243b671 GIT binary patch literal 139493 zcmce-`faAFngxO90q-hU|4S*UGa z^l{J_WNg`|)G)H$g<1ZAA*v8a-6`Dfvz;%xA?G8EC8`7>02&hG*Ey+W*t7jwS|1Pi zn_O+Nk{*+2in%||yVC_i^1@>?}{`@$!7Vy{(jryL^;} zu~MBifkprKRxJBxQ{FK%-OHm*xS!8rorwCIhhh%+I+~|lQO9R(tpPUgkTKrQ`Ota# zvScAvM}d`BtHYanpz(~D@odFZ&VkJ#b12-7-=n2-*6TUB+n`pxhp+g{(~`%<+MJ`k zhxUJWx3`{JP{c(?S^vTr4@ZT)5d8tYWI~{X6v#p^qQ*n!S$xXH#YLUYs!vb(!&A=4 zFMRtzgXw3?Mvwj-k7i0%r*^8LCd&BEPU(yH!Fe|iRx}jb)qKC~mC~cW+hVj$O^4H1 zp1`Yq@q*dplfFzd*(6@2hGHg$&s~My+MipONA5O)y

4nAU%FJ~s#sT`x+4Hy>*! zn{TAtZJK-S3Nfh~yl9;7%;lC0xzqwx z_+VkCtJ{xDiLFWu?^o5%S~fN~YZli9tTz6X3$#Xvb+doGl)BT=BgL$qCgTe@mHj8K zx+)DW!-|Sa+_M~9)y-Xcx~=~2YqCuY>5B)}Y6e5>XW`Q9cN+dZH&SG~lmF_hTs&V} zNDDKjZDM;J#UQSK_TPnwiK?4iP+Vqf4;S~UM_)HR3Km_A-2A}oumr91E?tAb)3aSlXZH55GhD-#x%mZVl`n{u!9zumOra#`duA=obm?A|M|kz0`3 zB^>_N?PU{FPU`D3!7^d0{_^a%cbQ9?i=ED_j{_3A+~}yi7+Sl0YXH4|;u2g;^LNns zeU5c$oNG3ij-CbwT5^qT#Z+5rLx`@Yqr@p4vP(KsRA8aF+e*YS!eo-^H#BOjZD`#+ ze1&&@<2oF^ryEsOr^7|(r2LeE z8skZo3v+I+@G7;_E2~va@sPggc@%!_^4R!W$v%ab8Kz{z`gpI&+`x2k`0=H;T_>xk zu-5;R&N>I@%W@^wQHB{(H$GkDa|yg6H}Q9b}H< ze&{Vg4;ulX4?j=CK;zM3vYMrq|00FXeQdr|L5J7#=8yN_bb#UQ%YNhZ^|wx{|2-1w z?6I#6y?&ISs9W(ZK|<5cdx!h3@$_mO`FZc!((-~#zVGVITZQ;}8DoPxp^nNc_=pG} z*2WfxA?3RJyHs#YK9n}5q}c>;>2Zz|X6s+hRMHO)%nwY++fTc&cJE(=dn!wG46dY% zH71T_*&A0)dI{8i=CEyKO_{eO#9Q!mpzvJA>pV<#U)H>L^K`UKALc(P`bLaPXr}Lq ze}gbVS=v|`{neXozDpZcuhycYHi<%ikk9v)EX&_rS!pp7(`qIhHu;rkLRy_G#7*kX zfVd-9g`8IyT%B&jt`>A%weiJl3}U}d;wf~L(M@7H9UG@svv&_&g1 zp+mBtOueGd!2&1)&;!m}ZDdF7XveDB7yPq_Y`Dl@f(|d72(9w$y-i|I?~c>&Q|6E`bq@fA_TEU=(A$=T!IEvl+){I4E{Gz`{S&UxSvxuHGpU*1+qnyUqpq-mcU z-~BGmR&2`+C%pH+d_xn`oXbIVW`Cnw^Y&5d(ZC5M7+~)OJP{hVr)@pyh+X>cq$p4RR%1DhxM>oh-uppSe1%W)b zj`qX;6EU&@fg|1)<_0ouq*#mj-S}PMHr;S*31oCsG>PaiW2zI6N)V8Q4ao_u8rTqp z{E%zyyuIf_-o^7Un6UYtK!OmLm`H1kMv;Rr<^2$^>9tUym(cNu&B1dz{^3xs>-XdPRVrG<|?m3n)v1G3UU?eDsx_b%4_jdo{VXv$fi0 z>71;l;dBfSU1Ta45s~aB9oWOC?K$o9Xor%YaOl~+pWI}vVc@3*gV22_8lZUjKx!dT z#O>u#QljlN>3U6v9<2te0>{$gK6%5FJABGxd(pytD+|AQSoACz0#!s0bA5`!mN-Mz zz>6Rv9QFlOpsGAgfo%K%1o*L4kHWPiA>@18eV*aAc@-nm{dU~JC*-vGF%SQg2g~z< zrW=4eiO^>B2!FjwTIt<=SN@6>Tpe)#r@q@pclbYJ0u=lf+#ZR7T>B$nuK=)Umm~0Bj74N`|mH8{jv&Tw_UN2%^N3u(r1!x?c`@; zc%u+6_2C0gdrrqUGY2VgJnD_+jVoUH9Pl?prtS@1ffNtgl+mToNgfAH8ul&!?#jwG zBWIU8GG}=#*zb_(+;~3ZMQb?8AxU~ePLz%nw|``qxnE=WSa1FnRd;pC;4kLyg5yMf zO&926OI~IGgxSz*%ALxSwjak~fJU=MDIy>4c_!clBjILU5=r_vX9QWGsWPUYv0 z*56ZB9^19L*v(cyh1;;_zuaEU5YNZzKIxgyGBW}LgI04mH^!D-#0vAt)kYg8Hiqps zmN=IDbK+4pdUe^xc(O|q{ijJ6$D8GEcO$G5KQ`coUnL7mwCY^_(W~@0u+yhb!iNsj zuWVt5!ljH1sZm?TgG#fvjVV6Yr77FhTC3$*Q!eXJvK!i7JWcaVGLM$e8Xr=o_grqS zQ|Q=U!rAe)UDt1L8Qt&VrZT^pOs$Fzz6j{=eRw|Rt#j(Tz%WT*+D?-DU$eiRZ2!v8 z?Qy8pWYyqDX=aQOcaVUG)^$P?NIPr=<{XOkLXpLLu^OQKX6+o2yD@eBkcKCzc>QyW zfRW2ZE7toeW=MfZPgPH#fM1|ICqu0)*$9P|7i$XTIuF0pKrnvyBQvGlq1)`!jqAFu ztu3lqfs=Hf)DoRi8ACxj8eYD9;4u{mBeEH9`hMknx#c=|C0z~;>g=BrP0Ez1NtPAM zf#w6ZjdM@4D${4?IBtN}h=x#UC&NWFp zS8p)viYR7g=t{gL)aJn1(DOt4D5PcZP7?RorBb7&^9=Q!pCU|l@U0J>e0B&yN*u>} zKzuk>+|+#S&);x_@c07o z?+Nd-FtymSA|-{BlECcDSAnsC&4s)>LFZq#e$bXxAo&BZYEDJi3FMo&794o&B4+L2 z8R22~g!H%0E7UVhemqRtk&+@vCm)|#VPB4=HRratjJaRMLqA_TfxSA(=h~1+1o^I3 zeC#`{=b3txvGjmD2E)XTdUnB#Rm7fsb&_CJU9=O@U^b z7pA@z_3pQBmM(tn#J+%8M}a97rmYOmGHduhZ_JLtP|?&-rXH6Dj1k(c>Y#*1&h-RRZ&9Iwgq3d^nP!yh(aj_-BkY<|+}u+(A`EMK8PN@H|L{M;5STJz7RQBhHs3dGL;D)W#o zNX$E@ySFHS_2S!%_mPHX; z=(eD!kWlt5p^=B&1e;RqFSdv6W^#B#5sj-Bm8?9Ibu;P9tfrM;W7C;mW=%5-+sgeV z*hyZ`n0juL2a(tQ)@t2`KN!C(l%p`o;xk9=C3C8yzaJI$y}S(lN)`V7fiD!W%>S9c zMS$ZTebZI-&ChW*C)0tTrDF6n1`BN>D_{`!@F(KX4UHUj?CwXH6;Y+8>j$%stcaKR zY7uiFHKNE>&gGG<;bee-bEpPtvDbX*{aHH8S~@uWRbjY6TY8CYy!g~(2FJLsOkMbz zYi0%n_Ecz)m-}5p{;7eVGy^73#&;}a9T6$j3{O&>swKYo+wpK|?X%yd5RLqOX+qvr zp(^F4m)^GBY=LK=>&XdI>rl;R^D)a(*(dABWx$s3%|)g9$0^H_tx&Fb8jG57yLz1U zPf_5oTY!(>6I&lQIRPQTUC1FEvqG3_DU{Suh3zU1h(Os8q7#h6QSS>kOcK{-MR4^G zIb!?R0L5+ofTrbO|Ab^hHdd>Uk<|;|l8J~g zhVb84zp8PWhPw#EA0$Snk%$;uus^^DXnf|$`_n}=KFkP$`O+eRQh`0<{u#n}($tC2 z1AJ<_YHA>xk64o(GlfnNezK>3y!uE``m|!YrxOf^e}3dv0Sb5tL)p{v`;{zc zMC8Yp06`gcD7i}73^oJrRYB_H(NHX4cVX8LNWBh_=?3nU#&HxHin7gCeawtg!NLDv z#mvQ_Hefv*Rn>~eeq&3g%UxkOo|KrBCJf}+AcbHG(ZKZfet z)?>_d0pL{BD4@v4co@xZ>2SP2CyOKu7S?*1rI=5zxZ6P?OyAL-ObSEP zjJ(T!Kkwx1BdI2mNF|{0Kq=^N)VjiGq;4%S<4GkPe8YS4u?R&G7HcVm#HKYHY2}<$un}b_^Bmvr0Fc z(r&gk;~$QS+1(zo(3Ja(Hp?O{aaVOoqG_jwSayB-ZhT%={HwF~+NxRC(eQPA9Aeq? z_^yTz@KbSKYpL#8B&j?ZLXONMj~*pkpdzND@J_BLM;+ygN(c-r#X$I;%nT4eaNM+H z_(dH5>*V2)C8AzbkWBhG<4^wB)1(dwikfEoM}9?7p$OF$h|1A%!VinF^`~gPYMAX} z5P`OD2JgLKIDUZsTy?d<%b~;PUE1U0S>M9)vb1@Vc1?eh$+1O1_T`JY@$X)9+ikY% zAXanv!&aDH{EoBLmNx&{x95HbeelbnEAx;f zKmKW-zuhfmmnWH=rS7_s*KK>W#uWwOU9v_r4(h*B6@IwZEX^{h0X%G_xUl?%x7*lK zhr??~NRJ#!WJpHvG?z-_j2+SJvcK9rNc5Bsbg&Gp+!R^Y-^3-&{v+M<7{12J8}^l7 zx{Logp25VVQR}-N&cLI;K7P5@_3V~%+ESxUawE4xJL&oo0`aJE(uu-UMZ{VH(bDdN zKqDYo7DJGY>Dm#e>DGy`EPS8dKCOA@qaqH+WcG-^HUOV z>wLR)@p30`x7Qhet=(|{oIK44@Y=mGtJSfp^E{@M+~eJU(lmOC`Fz;*>HaVfy5!H8 zZpWb}OW@RV>tmC*jtd*&{CODt6UnmOi9QsEndB~@wwb}-&qWTKd*N7@aL&sYR~A%| zq?XLm)LdDu7FdPPpGKmoO^CzwkR%OUA&IZdv6}M9R?H?)6MLei8HWFsZDg{UVj*67DdZORzJpb+K62yY<4Eyo#}RTk;_(Y>0OiQ?evffCM82b8PvODeIteZ!SqzB8;4rF%ZKh zRE(-DTOE*&_&S(hx&;RDskmjex}wfYm_Pt{{37ZZ%*zTXY-Tc}UH7oI+*Q z$%b2m2{$uO^~KOi26PRLa*P|;+s8(6MU^JzNh+k7I7x8@l1ljGe+DL1LdgN3~j zErIZ`Ik4EKou%B0#c1KfeV!t4if;kD;*A!Qx?X2{%C+OYL5%_(($o_j=|1@5$hYYe z$9ZloxBkR!B~ZOJ8*ZfSInwE&rtCJ+%Fz2c1Gi|uYYZ?1tA0vu@H_5p#df=DO&p=1 zl*k$uQC#U}n@8Mk!7~qcJ^T76Y;tS$xOz$Bh4CYY*+R}N)FGaeLu<427qYa(_A$fB zp9I&hmu9?1LW!_U9RY`Kk zk&pLS)YzKY2?JQdf`LM#5fD%q9t)X4xxs6$0H7)IV-RsC;GggLhTrAN{_yng-{Yps>OKpCkp$*S0k9Ef2(8cWJ{y$6D+a&Q@k0| zzf;AxyxdKhyKq>XRMEuk(mY|)ZxM5RmON=W`76B(0F)RpgU~DZ)mz|Ip1?e6SVeAh z?Vl);<`|xhq3}8gQ<8MOup@aAOF>#S8DgGW5YO<3MbDkK;%70SWU8EubzM9|$|pg=fTf^dCk z%c#aKKGaCKR#C*66n&gT$ge)3mnml!@F~6-<>0%IOx|{%^0=Aj*_!_thSR0eng4k9 z+nhI%O?Bp4mUEZmwNE2&z&kc}07SJM5t&by1j(8iu7fbiV1?NFwknzKK=P=ApiAwA z1-WCVv?36lF#9=@p~AV~#e62)3#fk6)z(ppLCMd(AsosC7gtEdw zWZ#SKXJ(T@uYfJjcCPJdE_u^;s2Vd-WxT6FVH>}|hX_zyv9t?xa@Emr51eWILJ_6A zp(QIc1-_pbz5&@PDtR0vX-`<32?wf5zayNGNMVz$P9Ns*u4T=>M_^-l`6&zI^Qdv`-fdaMMZQlmcUgd7Ft9sPjmIBmctj{ zm}*w9))@!xpMC5+T}|}Kr_rouK|m`I=GZir5gSW-%_Q^SSFj50&{^)6+h}dgXF75TqQTz^a|2ssdx_|;EETs)h48MRn`^BtR4K^L3)jFfwV3cZfJJNGR2j2$b-PC;rqU10(L?@{U4r@|6bR$pE?aEScqF@t zF_ozd{U#0DFK9x4Y675ZTs&%qp5l|pmy8#U&c81HTql&{V%00q%7y&lqyBKdFl!8&KYP%w4vIAVKeriQC$_h5b`NjP zzE)|mh?^d4!WNw#25MOz+>m?kZ(7KouJBn5P4MfSo_>BM015=oxPBWBMZ~{HGjX8x zX{|DDtu=6O?>~6l>~L74+YC5eeBAU~OYpzk&=Klz8|1lwnZ!M4hjS_WQ@NZEG_tw9;H#g*Y*p>3u2y@W)9c^I*H^3~99Jx(` ze)gu$rttYCcMfav)ul^n`-?zF8FOt#fZC^I+hAw_6zS?=sx^Qx#$*_D(k7e;6D@SI zfD^B>gi$Voz-2#UY#;%(FeQCGJBNN?u^8{0JgPV~8nw1~MRN2CNuuaX;EoSNaBom_ zciwW6uU3TUsBoaDFsj^rOJ#F2a?nw9^ngfGhtlW6c))9`oW4Kpsd{bI zqpF*f_yc`e)XR_4gd73^o1UoiH3OmO7jx%PD5wAP*RHB6Om9LV9=SDH=V~dl`OYqc-}!qNU>Hsj}C>ow6d}KL;%e5 z^Vt34^146armWy6VH#L(NBxXyd32@aro0B152l$tQv*&&NV0y%TCN@5ig1wxwl9}U z3c@J?hR7us?4#1H*2i4zi+2t$<3{2^NL8;3-<7-|p2N~$Wfy`hWa+AwN&|}ReT(kDiFg~s@LQ+xbNQxSc2_$y|y>3)pEHxvaT~2 z51OyV2WUE|xjinJC6k_IoWgz~I+6Qqu@Vw4tsjYhI_QbbueKZvArKQ|88qBE>(KDD z56<6XB#W}U>KXRoMjtzYy^q-UhNXnRI%71?DOanO!xD_|2@6ZvYD61aGeVx1zZm9YPpAtxI#C@aN{LdC(=}rs0ujB`HG!f zy*{(BiYA3d9(wtHm|(TkW+CMYs!FKq^@Ko&=@MbpgSf=scb1Pv&XNj~E{{raYaAlk zG$FtEzgRfGGY{0MTa3fUQpP~O{4iETOPJo*J+$d)t*H%Y9X_a*{Vz(OIsiFuHNfX^w=9&vx8GDke%io*b^z9)+@DcFci#TpT&w zI@&U+Pb^|ck%8c=US}(t15=CbSKJE%$``O)QB`L6afJFY>k*j>5HkbZ2Y^peQ^Nps zS}7;^V>JRoIkEac#kCnwn(RPr9xqHpLJrjIB1I?~Plfc>?lqaCpg87l>0l^0>ad=R zX)%Ln)vzB;sdXwtZ9mf!!9XA@%PLLcGPCm~^nwNX6mmSXQo-$`T**9rxeOEmA(;fd z=}6AcU=0S)kMfC|=QK6THANn-@6=8_DWSWZ;Dc(kj$rL(OkjNU-DRou*v; z{tE>HSqIFe7Xqn2o@MaZh|ZzB@6&$(Bj6j@+5Kh9xdCi_AdYuoC}j$feizt+U@g{u zPkk}^3nnBOatV|39%JX%p^VGL+9z?c+2b}yYyR7r1=())4 zN;mY*PMMoDuG=Q@$`)$)n^Z+cjpj^7Grft2lm3%a>;MOX-j}G0`$So@f*(YKyQZBI zC(F1Su<@q8#f=yq~Gio!J;o6Fni%3O7GC-f!6MKa*T)Z zndLc@UO9|EZO&WmcBX##{oCv%!;;$(LIKINsoXpZw?6JwCh5#NciZwc7|*{P-T--? z2lP$u3gMm&L8)dD_P?o#Wa>$7(~AnNf4&GVvA4o|MwIT)?&DU* z+UhQ+p}^O0C=9U4%t7Ys0CPoaVxO92Sxqnzg}j1?dVSQ3kB?1ws+i^swiIAt0;NmGZgZ3nv6f8G)QwG6#vT{=%=ga-PX>UIA z13$hmN^?-J#Po&?MY`JfCFZ_)PxIkKPw@Y$hh*?{%{SN{I|rGyC0Mn6`45@{_A8>5 zHff^Bs+0`F8nzXs7WJB~CtrAM5!cq%h`%DOI7G&K`$^f5a&`?Wjf<2eeq4dKV2+We zDO8%cA|2r@(bU z{~RaZ5GF&F6kU^y<7A4_!SlgKNK%z`y5OvQ))!zsS&N6y$N7{hWKB$D_XJ9X@P?T| zhPYS;>*L}@1<7%2LrmdAf@Hsmm~44H5k?kxxo))V*ZFT`5lgZp!29N6Q3(de`pMe( z9OjY?*U-@Li?5twa2NH!2mLF-2!0qM;Q0L=Z6Kj+bA~JW?>h;jqY=sr!guymh7@NJ zT0F$UKSPOM{8wkH*)}|OYmWEw?8cM9@yI#nE1fDEtpP66i(TWp7J~eJw@Jy8P1+4R zJ-k>bAC)$3m1An0UUyHUd!BsbmSSTLI z%)m_-%wFDs8XhrRVDTca+2%{QZljU`>0HmP@XCX-wzH5w>xnbn6MV`9 zyy#uvY#MXVAbtAq$2B5JL#*$H)R#9(x$OEpFc|0vQw%&u$xu+}LN~~j z;J=^6DsXi04KB$eV!kN&kz%8iYXU^92locWkL`Zs8$G*I|NY;arH}bw!eiBa;zxkN;A-YQI*K{UIhMsQa?G%8lY<|- zRb2eH(?cC(VG0>16ZrDay}9$0M0$lN|Ljy|epKC)MuLw!yqMa{vha zj!A@>nP&8R0rRhfUoSLbM;p733QTDe{PvzSv?AJmFZO`{)YsNQm~Np815^@4e#CaO|;CU0sP=}J*N$XYe?krkE(NR3voL8-}Y?db!Wcc zSvGzGQh8a6hTk1MqMyUX0pVzhz6HKlL8pIi40T*gR17{4R6QF9q%t_ zgvXkt7c81i0;2=d&REQL{?Qa9YT|{Z#_s%kzovp6Ig*WS`d!PMOh{L1=PSXdU`lHfd79(4XBF4k zvab5R>F=&!A;-&P5dKokn>gF-usbCdI4nKD`_?+alk0*n3edO3q`6jDn~!GZ@x6?9 z*W@UMKCc4i#)+R{RSs(K%XYoc!M zz-Fd8Kqsn>e3(kAgAWfQk`%%Lq#B?C9%+L8jWJ|bxPndb{}>VsVg5@B70?(yO-vM^ znsAXNUofU36iE77o;ZDyteN&aNmyG^$Hsu6!5UF3xpRh9Hl?V50EED;*9; zv`%T34Ad+)0H#Q0&`XzFE|_)}45UcS0doi%{vCC{@EUX+j^(*-Zs(;jX>{5J_|L)y zDrECqXY<_aGcj%Jeb1WVebvnR85N4{uo3PpLt`N72=TZ1shgR=eu@I+xIeYQAgJ|{ zrOogdS+hD0cK@kcC0wr@Wc@8sx>v2QY4w9)>B%zFTC>M!Hd9&c%mWPaMR)tUiHXv(e?oVum9TbLr0P(`vVd zZRdKH7R{%prqH5HpVGDTjh7m@9pftrk9&C}j6~ZUu}!CR*S9;3Hn7(Mn8@7#*qvF5 zE{;L?P8D}#7Qs_`;hjKRI z1HS3c5rn5^;W~S@Y0nZRMk(^x{=%)6Nc8xpKkA}>wqAEuC3zih)A}cys=tT56I^cX z`Dm+G{}eBQeE3buYMYz!NsD%+dhbfc%8_|o6_ehpWc`bk3bjm=cv4gKzEUBRBu}-E z#dP?Li>c#{11>z*FI4Bq!~H!1DUKzuKl{p6wV6Oi{<*bUe?8!IZ21 zKEW*sA5yw){ z{G$^Y7e(3k(Rih^@|#XR)4Xu2+1!8ObBrYXUx7W(i!O;`>Y5T)pu`d(fWPT^W~hZs zgNB9+OtQ!uim96jHMAcPN*|mFJ5+@Mi#DGE)3g{Nd^e7fAZ6%j8p}#(=1h1*OcsdO z15XtZ02|^Lay2tB-PaTF`s-?BAYov%?+d}WO%s}*s7jC?v3hT9o*?k^IfOh!e`?qF zpx5cl=5sit)z|R&->6QL^KpXyv+iZbY2am0hr{!~@t&C2C2dBHO(*^a_sQ^*iTYMs z&9=LXrPtRct9ivkW5lXqRafHl3HuV^@xJ;Wd_PGuyWz6T9q--lIo|}^5Jf)jrFV|7 z;(}fXF#`YIVE@$>&viir3)59mK{Hc5Rf3=mkF@!r&(W^(#N0xuk+j0Y6*U8OY2|K{ zJYito%XR?;6vr_D@d~jL-o%(C2%mPafna{Pt&9|LSi9s46o|#zCd0DM_CGbPOHIrR9JqSA(C2{_7|V>9ER==FG*&CX*pIO{103i zJOX_Y(PAnn!pY;ndCYU8MX_Mcdogle}9Cn@OapQACX|_7qkPaYpQ(WC-`1wm%i{OCkp^AE{2>9A9bxbHBx!wCXDN3*TBq%SX0`9N8il+nHaaB+BE zzm)!tr@l~+(#bkLI%Z%De6#GBo;0yM7Nv{Zk$sF9^;vPp+3fm*^b{N_GJ9&96y_pv zI0bL}!(#_et(CA}GGmq?H0DiJ@Zfw)E&!Z(&d-!CmGqV2IrAT0SQLv$R_9|jzLb%* z1&n*uN$C>5lP|y-cy$8vl91~K(cQLiYKorve+4^){?wKTfX0fJQ9z`qeRirYgUg=wGDPB=!Cp-Bk;?=Fxl z4;5OoqXD5$l0f#zlz9M9s!chXAIKUBu%lSrfFn(z6Vrb*1RNGkGJz;OmG#JmhUTtv z13vKV=tx8n>YeQo{^+c35%_1Q2*&a4BxZSdo@5?wZ|8sDWY!=_ypbe(>N1;B-HKru zkfs$%zbVRuc55q)0$d$3adG1Nzh33-17Q$<`+$nzDZOB^VmJaZ80qwR=`wNvAqfQ# z-oHDHhZ6Lp*=Y!a(XfKm>lJhx4faI~5+)(_RHi@-qD@pn3J-Qg{6$46oajm^{wn?e zU2q(8QC?GAa@X_ar1)oa@vapJlZ||XI7&2Wng@d*X=DaF0m7&{wjZxa?|HruMM|Cl z{uQN4mV;DE=`_pIEf{M-t}QIww%do-TY%asB=;Bk`Saz& zqdB#n)E6ifBSKFMLw!OdEV__iyL@$4CYR#WTi;fYz%B60^(t+NfOP8q{pLDkI`3%u zJ@Sn#P7*;6@;R6at(0J$C~HA6R@PKZf_RTb3o4+Rsf43PAqxfS1lM?z^aj=j<}tmx z_rOxU=!1ZXp-sZ+Q4arB|0(AS8cZJ_PaN$mTdJp6OQcx8|Y}f0c}2DbfgMY z1SQk0C<}O_gFoJuR+6+|^@GKvlFTSVmVQH>k1AkreVM8rS*G-dN9~cY35&^s6AIX) zf7=rZK#X`rDo80S1WGHxL>&$QUCFk< z=Ez^%74EN>6lN%HnlIm5Nr7VtwkLW>U0p8g4TAy-@teN)Q=>}g2j*%_xR$Xg{}?Y9 zu#ljeQRJ|TF@!|}O|ypIMv5x73hM_f=#RpkHgmjgH=Frxa|_m( zZ#tvmxcHoG*%wQX`GmM2XoJ@x?86CSHJUJ!xQ2#Y$n}ZnSwtrysqY0HC~y!{{tb&f zU!<1=(hy@-RHrbpi1Q*Nk3u@P6g|$du}m+2;nX*_g9c(Z3G(ta$;3c;;#_ZB z=}a3fl|B z66agkA@JN6=F%ZVn4X!PNc#EQy{@y7q%cn1*V9;a^0*DIZy)$0w!PeH-*%BTUv2wK zA)B#)vlyzEEoP~rl$%X&&hYoqj3lIGyubc~d2)r`bZBk(6^+L#oHZE$1WZ-F2O9*kIc{Le5nZn7kLcG_NHZz~hB{y#Ft4$=p`35FCqRv0bF7?=?0qLj%L zOY{?gl`qOSh*$mBMu5VSai*C>0q6i%4P}|aTl4p52FJHQz|dL6CMhpV;Xp5r?_uQV zQK0S4Sw==xHP|L+L}xQn$%XF=y9z&wg*VJVwH8FAP7BEahIr~8d?+;=Jj;l|Hg-za$uS0H}Xy~Pfdvgrdj?%5Q z*NlV(SU~qoKH4soVB5_YkUD(fPYQSz#IV1PJ18vz5dhzfEZdd#u(j2-aSGXU%C*HL z#F*XIO@@8*|dtN0`V5eg9f^r`st5m9@eJl#Ds54=YnIw-TgW#{eBPJS{g7SRNjU=vGS z@em;y&&Y6C8U(-xQE>P$0JStE9}d1D5Q&A^M-^VP92!C#W|{?(B_Kb(Z_oTji+GV^ z*LN={(SEwI{K~t66K)z2V`(O>`Gp{!a$Nap|5s%00>e1YOkb!5@6(aJ25t zv)q1I6#`r;L7&gR48n%d%qfc762;i%go~{koBk=?6+4liMwmn-?7Saf;*#cXr^{5R zo4@|)CR8m6tFX2=4njvojr9%0l*rucWzOU8y58$Mbl`V>l2G5%rR5fEpxeQp(kA~{ zlhzmxoi}ScRj*dtL$UWN)nOa>IXKaqj#vQV=G& zCHr6p(y`_L16_16oy|{Z?lvsV!2|}vLvohltw0e}PDB|E)=^a6m%)*r$uvY*9CYJQ zSmo0eR*m$42pV=d8trbvYZtC#)`G|d3S#Qy@FkRj#%i1foSbbys8ueD>py9sAokS= z0`Db4?EX4WeSWyq&zp0)ts@YYC?I0AK-vR+krWTU5trbvDN)##ugDB99As81gD8F{ zhXxeN?%n_)z5g-;vI3P6f}J4}hieNh7%$0CIS@g>xdO$HRV}Fiw(WOxav^9sbaH4C zhw%M(S$EqXW{H-G>8T_h(SXDeNt6BrP^unBeOFYxhvp6Ksr7Rsksvt?hu3TdFzTmPBj$$a$Fu+HDmDv6WEC8X42d*}Z-ct_7eadP zn}38Z)Blpy5=$U#h%FQ9jo3tD#T_jZ;Wd1jHIHke7$ugZe*mW5CSYJW_6&yWe>qTr z59mr1$e?YeHK(0m=FSVE%XEe8DMKR-<{uH_8QGCkJVI(JzY@n_=JXes@k5?<^;z4x zqf+P|J535ZkQubUKHABB7mD6YD?EK2`H&Gszg5|xwkrKbLM91UHFF~yF9`)vbw(p4 zLs(ZplFv&GmH57kv7p0mC7fU|f`e5=mO-FUmu(18~`2PeAb5RSbADTPe6G z>KKJTn{mx2f1spvWE+awkSa)5WDzM)?(v2Y(dRRN8>AiwgbDvYM8jHwCTQ^TSFQ>H$v%&BEhZ_e#+$Eu3ojd zjFSJztERXzGQ%=x(Fiwlv!?hVT2f-yhql)|{vQBcK%&2*=l$bVE7rVjtK|ze?v~|u zJTHsmP$0j5`P28cubk8R59O*ZS9PPMt}gO-@7cRPX*1k#3BlP|IuC3?r!`Ozo(H=~0zB*hnY{M+e5p!Y=Se1=0xI9G8KyBH95Q$Eg$J zkccXjMP*eRetDz}VRQm}u38RFb!gnY~OjT^q5wB|aSvk$21c1eP&6+ySQ4^v#7HzVH3=^3iZ$VpFgdQ~LL7Z1S zjupvW!KsD7G*5x!q-Gxk@NW)+#UO9fp7ereg5VnXnFbwClGlK@4(Z`Rc;bm)L%CEI zkR2n>VfX2V%{3%~n?1)0mGk*2Cl2?AfANcFL08GTwNFwF<_$NLIag5>+D~th+q6+=!_@3-9o8<3Xx9^lns{Oxg%RO+{9e2(&9A}6`%Sfe8Q08a}UOkd2 z2j@0%f>|U6#K|#M72>iDbwL525kAj=jI3Uy)dHmczO0%wl0$m#RB;}0E{O!4>Jy+K zh%4A01JhTuo~{@#Z5zg%TEfG5cWA7Npbn@*&*?}l&{;PkP;5>}aP-75 z{Q7X3Y&1nT2=gm4f(nvXfPjSLCZN3`{vyoiptvTz_iC{KA$1)8sL@?U2@oM6f4YQW zk{$tNs1}Nl8y!W4o`-bNAbZQeNZ%+NKQRV+Ay0t|DLa)s4GNhIXlN%#22P?)GQlS? z^mE8E1OZ0VrgV7YsqDJuaDbK|8>$_*HB})xVQM;UoW%0nB%d$%mVL`b4FE)r@8mtZ zvp&W#&51~+qoWZK@FZX=qhJ(9p%Gb=XB%{_MfFH_E#2C2ky$m*2*p~aIX@U=CN0_8 z`WZ36%^v8>foxa6$JyXk&;l5qh zF1>x(ikW9HI~)L-cd&TLLVoYAEB%RRa;2f`3k+5JC=%X%WW9-0m6p*7l{}e*w<9>T z+uq4%d7)%1ox2zP)-i&5NtAtyP<|Ir& zWwuHgc;uOW7%I8oM%%!X6bPcR2t>l*3n+AHLB#RZ(Rh>%7pnMgf389|1R}W#p)i6% zvQ?frJ((HL%$d;=f%eu26p#(JBt_^-%5({e zA-%|nK|>>`Rt+$-$S_L=-L;T}G8mc=NPsWiMY(v7XQ)rETe|pyFu_;K2X5KZbI%P| zF8Si$e63tD?PK5g{*P!@^5NryGsiNy&9kPqvCZ)ymrsqYEEEb;Y}>X-#Dc6~R&gv# z=(MU;6^&Qod#vJ zHIF@Wh*l*u7iLNV+t9h!z3>vNld4 zFETnP(yyOHR7!?G9lVbvn{CNdpTcAb4uLpvlGFjBJt>yU^co}|heUAdl95vtV5m_N z&m))@>GGH+7kfkYJVO+he1EFalrRMSrF zAyaV#$Z9UmQ=2X)RSdEug#afg(H<1>NPy;qkbMh6XkX$u&q)&^yPCsisX_sL#qm^? z<|~aAEgCI(@;67A`nZBzmY;a{eea**Fp`g9MM^*f#)1>WIT$IpjK#jCiexGmH2`2a zewuA)dnHc(sLOHfnSu%pjR}}JwS#8tlKr~LUflu7qgkCKbc$#($uQ#jm#=aE13UbZ z#7><$Oa5n%G4`V}iZ7nbz(~4`#wHIzG~%R|xKON|3jaz1kR~=NdaThQL~e!BsjxN4 zGcnCOOU3M8v`h$o<(li((-7y{wQCs1Hm_Q`Y?+|=eCu`7oh*pr=PX0}6jM%b5?!r< zRBn)oNJ`NNoh6JG2ex<0JJ2D z;qVa|nVtivb7H--vY9Rd*w=LcP^yY^7>KbOL&M8#<7#krJnp@=@PR4L&c)>MfASc9 zmRI)9!Uo-U3ZTFP1`me?2T|$c%$S`3hfSR$zg?(E6=45kU)_=a`pFwQv?vY7xq5w7 z*4B6Myqq~;q0*)JLt&X;Sdj00%ad})Jtt&#dR)bZ_RmhpgU5Hv0sv+@lkpVFWlOzU zerFEOqz9X|l`g=cb=l(P{%8N>t*^RB*}_!p2mk!P4YQs(6N~e_046M$cBqEIKy4WA z?YMc+wb~6hwN~5ja=D_!53H4`H``d}uFCs%O#tv_n>y4M?sLdv*48KJFS|}%M(z+$qAUyQI5fB(w^coTei6BTY2jk+2 zjp)6w<7=7zBRGMxQ0)kw)h*bBdNCuZ?TlP~`J&vsw#piqCrib`16Z~(U><;6i@_4TC6b}|Rd!VwP+B$2x+ zNvWeA07UlRw`YET;<3jcFwO3E{#I<%dTM*CCV@ztim9xBQFtGi-3N~J^uV$t2g=VLJ zrrzjC14h+om$c=Eld#z8Qp5E#Q((y$7XP_iHgTJ&tYwu@0M61nP}$4?oWF%r8LYNJ zchH~XMK!ZPhebaYLmhD@TM-U+W$w@iRxu0qT&tGk%1TbIt(COrn}t2QMqHdoI2<3$ z?rs?IdZVpX-?UhED_9x~H14Avc+)%I@?^kgvpgF1Ies@341U<435%|?>BT%3~x}W-j*`oXEaz)(Z4wI0K%&*)4oOL%)kl8Tq|m`yaBJYRXGr zyDpdi__yTrv)AP2Wh9!*kek@ zy5PjU0*2Wrr~_k9`{9K-84p;cTx-bXo2&BsAN!2{?(zC0k%&`6LvlGdeykmzk2cR~ z8Q7}%{I7n`+n@5k^QluBw*S%Zd)xH)zx@f{-?F9O@O9@McO5rby+81t?|FKXe`X7< z$z)ORP6tf|PJ%hkPWZleeUAkW|8DLR^Yl)u^>(+z_M;r38( zC>Tiio#F%Wd*S()6ktZ9LEMFgTuGLtWAlmwXTTsJvb)6=8q;(X)7y4D7uU({w91Ij zA|nx>lxi*6N@gTo?)a^yZrSg3%~H97vdB(=5Nc-I?bdKl$=t zGg@H(o%#WrCMD!U!>%yvOtI5E$+BO ztbY7lTqOsfy~A$33P=A`uVenHkNy4^wCxLbaI9Ug{#37=eM3F7HJ8kkEy)IqhuHD+ zJ8B##iAy%YqBRTJmdi1ckXn@mRC8jiE$zg>W^?0=8(q665C1w3%Ra?{l9j4^${S^blCPe=A?Ge{=+F*(uc-=OiAX@IbubJtPkXPf{kdVERVV&+!7>w3I0!tK z2ba`lXNz7{`j)oDMg}q(bKv}dote8D@-JpH;_TyR0JKmRI%J27fP-&T8nU*W)cju% zhRR0$2s^{~HWjYn=+bJfsTKXLE(MKxldV?g*CSr%&+eO>JZUoe(XhuVZhU@%fIRHl zPHEWjP|W2d5q7B~p)wso=)eNQ;&I4S0Dp$l$SZs8t8(Q|I`=@Q-Tlr$C~7}`?DlWl zd8$_`AMpBvZ^Pv{*=g240Kju!t=d31F(ClC7gS3oHoa&(hE{7SV?_ZWODsnyhGkzIH3T z+#YC|sEWM6SBnn-VBz3FnLn^k;-jP37q8ZDtR}Nk8L)5*j4(PTj$lkIF1HqX+TeT` z>6;vhVlO@71(1sY43Lp=ubnQBlv`#=RYWeqIsMtQSL9QlIxC<3>J8b>*R<9k67q?I z>ZI^gG3=eRTbjyY)KZ-soTD2|tptE-8n$sNL#4!UH_qd`R>}zluH1{`_28;tiZ95KyB!5g;6G1OfsRkSdZ;r3ml zLGAk|ksx{U`QUuvXw42M*21s3e>2XT&*hX3#oTOG*zk&`Z*t|~U?GA9YBjJ}uGMVd z3^ugDm}v#xw+g_i9k$_x=U$dvCM(-JREey~k!d$zcHvkOhr}T360&wzSxIc7BhH%*|>Wdunw}^6)qQa9Fib zJl&$0YXzNQz?6~44vHTONC%*Jr_z@|G@`}O)t)uxa{E3!5|94?2||-8R?bjp4_mCO z^;&Rpa_SvF^n*V*YGZk`%I5M{vAIYuylUh-mOZvUXxUHpci;E1J(dT zK;syQM8)ECV*w0B!kq3FbaOUzkMX1*IJj@0>EC|v)3ty3Gk^S{TElYRsp+O`u9xy0Nq zX?=gLT$g+q#-t6$8*)mn+7q9R`8o6L)oSe!Lwy8w6nWuA;zfJ-NK_8(j>)09pxB%) zMcWL|xnRVjFb)**Fu|a7ElySX$`oppS=A8!_(TE-JOHD^p+bg?`|vYk00%JkKCfB4 z_?}4^*qKonbQl@8N^v?>7;1i3SSG?&Ie%?KlBuFz;IVi>{9qf5?l2TcRq0Z>A}gC| z+1$?QFeIj^*doNa;jCe(H>zel@^84^?w_zb+>$Bxdmmy&0Sn)pJasPxIoDFF46C*SgI0UxlE-mNePri6lURbG0u}5unmsUtq z49IK@)f(?SxJ&#@(@f9cJx8=xo8jFqSpMCIcFT0c4FKuTG#uB}+OA2rkd!f_EUju; zN~Jm+PFN=hM&VduU~AJ8BN7LoZ1?zH7TL3~S5r?fUEh}L$)XG#V4IZCQQk1zEdtQKse(%Ny@M z0hV8sCr&*qPd#x;bxP{Znq0ZDG<34~A8Q5pdm)>dg)!PysMaP5rE+L_ap`@l8!0Cq zCiP18x?Qilx7BLx-zm2xm)v@4H0Zj=?y#9WcEiMiKC407*3qt*TCHKum&zlRded%( zL#3J~^*WkxvW#@HPJ!WmEB70Jc6$6`c!R3VvA(h))lyOGZrp*eRH`ja+q7FPWyx2U zGpY^H2l!=FhG5~?!5Oi@0f*e23Yb>SekXfiS~X1vm~h7!31s&Gi|Z)hb!jmrZ7}=_ z2p$0wf#vd6PSUVXOheR5dHi=xE?(V~#p|m&6NJyQwz4H|fbAg2fFHlMHnwn|0Tw$p z?c#7UI!J#3JHcN6wgCWQwF8`4HM+qTWCFFFrU1}2rR9vv!4vmNWHc^&X2T$rAx#^3 zjgEv(xJS7RK0{BV7i9FbeH=K%dWV5(l~9r%e+L504#rZg8O-6iVOBLA;;;e;)SJC_ zr}y4YZ}3YqyLNr-t~>8^eY3rOT>^kuZ1jQB#?rqpM5Qp2?@K{FQDc$IufB4^D{^%cl?76=*^L;7Txw~2_ zMK&vagQ0Hr0J9c>1#bpJ!8mX`)waqwwyj>V1QU_~dURpaJMC7)B*r8E@GJc4y!mC@zLtt5URrsRLxEq(O z(d0PaTe(V#_K;D)G4m3!(9mgPM*&c^lbQe#G4<@)(lY{sGy%|(@Qp|?IIa#PjD7dm zZ7|?q=q`=}h7+k2<;yd{ zEGpQrxC7Fv6lCezMJX>|lxi_A?Pguuh6lBxl<>^g*LK8BVn<+8tzf_H)K-@u4KA4c zMk*_T_^8BY=f#En?wPDo?waA~`jW(+m%A3RMJH8bpbh8um6tb%Q6e}W4te5aIs(dM zuBt$hErLF`OMwW5ZUT0S*Dy*pkaB8CuD|?(EM7UQX_BSoHFcJgqcM5;$_mbX@Kyw& zug-u>x6^KzaTktxy#C*)H(GHp!d1g;NjaRJR}>A(mYP8hO_ExP;M&bnR5@0AKE2%-8L(` zc8yCg0ASNGNia4d)q!2EZ|0PgF_p)$yeAHg%JBo!k{BDqk-*nH;e+jE&)KN z)%m(uY%lj(b#a4@vvo}O!K5|jk}NbdXLDn5QSmo&Pm@Mp0 zT=M$t-z`1&-j9Fsb07c2C%!cJtIv7&-S^#5%I81CFsubkgGkq4HiDskX2=)xWFXco`95WaTuziUJC5>56{mZocae`-dE-I=>7LRXsYGE1lDSF2fe)2;kdYy zEodqwU2Ljw%g}j?G93j%>6)Cskr%h!tf9Mu)1z{Ixg;x@szfGY5{o!h7-eK+M47!G z7b4;`4JQD?n6M_};tMyWSt!WL&260s7UGmG!=!SXgIl@5I?hH8mzhd(F8gr!4`>7& zHAS8(a5g!~ft_F5YQpHcVH8|?Q5ll9X&>@Hd_fBtz@$mt&S!-Pi2a53>7@@K(@7jt*p+H6xo@=1k!*IqzIS@IYq+u%*pzxA)vG`&ECy_k!K&{Dw+| z-|EZM@8jc=J(<<0Q>ckWxSOf8T)lQfE}XwCOH0d=+)m3&FP#@RSl-CUm~^ndg{DQa zFv?N8Xozw;>XdsACDgDoe^1BIlwp{-2Efm*8Hz+Hn8W|7_*9N?LPP9q8(T}4Ra%z! z%mI(OZ||&(#-d>GRCsIZ8-x7QD6?Gi5xDto(~I6q#WM-pIdtZDhm*Dq=0f$za6U#V1)t>ljMu`X?#Flslj z-i7Ht6ZX9C3UHF7G+?Z$RLB;#u$@(HEh*u-&MfAoRII7)LC`k9z1pp8u7hVYRWg#> zT+>Q%o~cAAFx0YVr93NH!Gs(5I?hFyfivl}n&D=rx-bJB@4jcjw zx6HBczC2w%oCUD`{-QNABd|_V@z>Ecd1P%mEO#nnjDEe57 z7WMhbVsVF^V9RX)ujQ4bj%xOq0W2~*Qrg~>E32DQsI`Wzq5$|KF`ukv*-G4%+mG&- znYrEKHFago58%g1ogCT)#!OMT3(1!J_b=a+^NUH1qOriw!lF*ousXkoyVnKqBQWQA z@BoFBSpk&e|w}**7KJ&5E;Gl%T_`AeKeq zY;`Qdu9dL9e0HW3m^xumBh#mRc1}E$W?5Rbij=Eul}sjJVrNLXT5~$gy*Z{Iy6pyw zS^T3TBS*i{e!uPjzZ1K^~H^~TI4<$}Zj&U-LMFk-v#(f;ki{QiG%F_%~Rdi>;lr$UzAX9{5d zxZ5?X{TYYZI8NOJ7ROD-JzCgTZ+BokOu9!t7*aAOGNXlK^)G{9!<4Ydb4jV7OEo3k8Vk zEn4)%0W{oW7T>V_=-P5p*0#1K1@N<;%*i&GPqop}9%MQKwj8E1Wt@X9KzUQ1UoHTA z^ub^|GUjv0!k8bQ(ISVZTrlp2h7?)zFdKGiKG@{K8YF_!!=A{-hADxtad2kXVSC@3(nZ_@GyWm2g~p>R~kc|bPvE?8O|n^&gz7~LMAPZd`9JX z+hDMRP92UCMiQ*iYKG(JHKkLlQM?K74d%pYZUB7QTmk>xmq6H$&xK!puf*USSdH(a z2WZx`KA?s7ELQ>A%1u=d?ZP0Fc`?o728bBP`5hmR%D#Q`5{V}yo!XQfoEker?wJqC zM9>8GJdlLPp$dpM^Bpysh@1&$q%maXQ#DVi*ci1$& zR47&@S12p+OzrGQE}xh9$e0;`Aec_4Pt+ToQ$O}2KkWEhw)ii-oNmulvpaY^i)FLn z&RX4m@rNS+)YR|AoEAeXIoNmawhq&B?A)Lot5j;@=bf(skl@)V*Uf5nFV=3?cvu_; z>jnV6aThOKT#--^E|G(F0F zDx)g)jY9>6?Tm@ zYT7n@WPU<+O(%5C8OcjB&5V1@f>lv9&SA@-$K%n|QF-8!4Um~)E{`)-l`Qt>>P}y- zmTj_J!96yH!^SEg9G_bP0L5oMKIapgP7mzJ#7sz41PA!<5!e={uUTmP^&4q9x02O% z)_iMN+{f|iOr208q17CAtuPwEG80p$sl8rt zjkZ-9K$P>*9$2AG#UGT34^02J-xvN%#V~*L`LFgr`?VLdANj9e?q;`5A1zfY5A+Sw z2XWv2&uZ25!1bGz@BjG^e&pP5|MnOE+TK=5W1l-~NTaan$4M?bWPR-;vA~ z@^)|Fq{U{-03@F(R@)Xz@A}*}?WN_x^};}Kf*bRCc{uAPX%p6Br!`PVmjin38aCO= zH)S1;G~{<{NZkci?{y3$00&v9_F!l%Fd&8&X{O3;ZPChBTiVCZ-r4~!z1i-Na5Nxy z-*Z^*J#k15EKF;GVGf28GAD{p$PP`+ZS|?WK)W`bVF6`LeT^&q@t@ zz*-gx>@YNa?b@Q0n_#o}m4*{dr*||eQpn{%w$pMXP?uhfd(Zm$SX)ZO3!AC7l@>tiS7?QcG*5fdwbbP0x;*nmu%B0d}*!#2Pyj4=y= z$&Lv4$Ql_U=d6&Sq^!WGb~O#-hSMjLG{NcYg)zS4U_@Cxi_3gqviyuLms?zv3{FHf z+H&RMMM-b0$icZWog%qwBEmvm4ec|{w7s^Xs+X2PTmp%4DP_~rNv})P+Lv7+vnpGz zWE$90i?(sHli{wY$iiq?47*!O9TN=yKyKW`c?<_}MmqBKvlry*+KybhxdDe?*KqmN z)VRzaID+%zl{y?HjED^InM-iMR5PsBYpS&{u(N)|^*#P}tbOuhYlJ_}1-nZ7sUahNrg{1@9U5r&#tWBs zz~NagHvsZ0no<+?p)6)r zXHo4qo`nj5sX|*`S}Do1=aZ^y5S<(cu*E*5>zdv>d~{x-6O&Tzi0YH@vv#a*jp_`% zao7dF+LUlKqU@Y$=!(fN*ZVjl2``*_Td)gK>e=LzXVSPMW=Yo!`O5jKEX*Zz=oDL( zqp))?T;G<<+jUv5S)|%`$a=1-lU2=(BKL*^h6ZX+22upj%crr105ByORg#}ZyDxPD z8~|m`o2X)+uHU>#;PZbqO11CUH8J{=Kl{_~`)k9)UN->XsVCp!8w{-Puk37n*xap; z9>d~0jTSDNTcSWo9Mh}i^CD_QSp#4NQ?ogoQZFQ>yt5{?>;?{-UyFuVbi+$+8Vp8Y z+W+|Ap=0kqdg$n1(@p=)-}%_!m;Uvq(m(#s{`lYg)Q|nI4}9=nee~*wfA;eNYZmEW8G{7|cWrMTp1FhAvYf>qsWq#DA zQ>iqTJOm3yyWRaaCX?$A4jwwX_sHSHubX}OtFJfQdAHT*4R$x09ecmo{6$du1}k={ zgvXYkOuD4fsIh=ubDAEjA){`)Sb?UgsO1IoxslAu*{kakkB8*MZM)>YyAO!X2Nqxt zh|4yRHumxCx$AQJT26;HG3PG)S$0Fij%(SD6dP>FF-x}A(MoG840d>WQg+RZN(9W5 zJ^dWn>GbquVZR;E1BYVq$Y|Un(-ToSbz)9Nd{#L$Kc=jq21a}1#+vk74Sm1sYZ+yj z>|9_5V|+9SK;Tw~PD4^I6=CpmlFXHrjcQdlQxP)6fDW5wm=eM{EY(|*+|ElAtdv4V z%uTb;*kbO>Xuu>p<(AysswfL^dkonVwn?#6)9Ur<@eyTw%)hdYj;eSg0lT(Es-{xE ztCynH?wLB>Ui;PB=U%U8zOlIY#Aa%9?9(rtHLWJM6aZ0CQ77jGBXZ>Uoe~1e-Lq?2 zqZ4%KU9HG=sJ!bgZ(qhe!$QpUVn>7gKATCSF4tjXnabD()lBDWnhKzz*-E-8Tcxhd z0Q?`n{U|#eV8~Wva^GRu>_p@u*fK}=l7&*=U!|EXL#f|Pe#k^QhMGXB0jEi+-9f9R z^2q|WI%rklXu2B3Ve!`E5AT(Ugh&6IIb}bb+JXHzLw>9FVY5JZad|_S-a38mvV8r; z3z`qMXjQ*c=e;L)vH*bvNzCk(9026Xr3=z179<5HRqGlO1Q4aV<*xZzty!R&B%SNU z^}Mvoc`)9lD&4VI@ba}SRTvx}jp^Vde;};Iq1WJ8x3)5pOD9z`Ary`cRSt2^OzoPU zNmVnfQ~=QHl1lA}shWq)Ngpfa@=yL<_r2d|l;1SS<%@9AMb*5Aqw$u?&3}dc<8{_M zz>buqor;dpW~&;D*BW5>tQ80ZLweUaz@`SEe)-y}RLUh8@lYd!lC=)$QNYH;j)AU> z2hHM?vYv6)<)kZJS=%nj*Ds|6*HS?q)jtW+KXauZpIvQBwhkLm84mJdnze_0^;lZ6 zdu~GR#GR&cCW%PQW03^*(u#FT)&|AdRyKB|-m|FAe+K~M!g5-kP1>d2x8r;^ zC1mNzc-SjdoE4kfEqll8a{kJyY-0WP?}@_Z^;KQ)SXg9l7`D_0I~a&bu4|DZ)}*6F z#GHr-pEkHPH^FYv`4_=xLJW6M(=TR#T6Q{BvKd)h-;mYSb$RLRxp=PDdt)wN`R71# z|HpT}_0(UAG{5GK=E=t&d*ak%kNsG!R9b9hlD`yo8P10uxE&X@t!$se6-GSl9k&nj zYYe9|+`$4Mr$a?49NvgdopTOa^32(^=KNGwLPd9a%*8vp&DQ(zw-3FNXMF2ho=Tj0 z>`iZd{k+LS(i{^;b0>UwjC&-zwQd*_{a zzi2QZBcVyLbUYGrk4UHC6fYx3*b{aG95}RBJ4iOb!s~DxY%Ahmp!Gse7Bek5cVk-} z4a10ijxc4&y;{{!^Jc1`!a`n`L))>K=IP$bV>5H@1$&*JP3Sme7JXd>px{ghAB;}K zZK&`_0S1Xu)Gk=nuzTp#R|+OMkMl9*AI?-@p&dnVc+MD6aWbTd%WpA;8VPvIqj%qP z?>vC6$;yhrSLwBtO7&aqeucSvvfN6`(3U+av#rA^U%Gr*&Yiy?GqbZ|Zq_)LKsK^v zHQ+R^bW%mI^_SMGnrfjk9@ilZM*gqgby)5{6csoATmmbnkji$xA--@_s^Cl&!zJTm zQMv!bAt{(WlInRR+qTP6(<7bOUcFZo>0zxvlc^BwS;+5E2iUQBBnbynh7)wN?!szQ z<{7gOq*80hVz#En*l%Uwp=h|i*-&js1r}^gJun}K;|+=#|7{1;W>(McW@k`XE^TCF zaeZ4hk{LO7b5j=6WjMdC9GURS^2M`q7S)zq-Prv60iH02#2nQq- z3`!gOn+6LW^A826>gB53%r)iN3u}@}roeVXICB(4+L67mHM7%G^6c{$B$M2L9jVK% zuvLyu1DLwZQY_**I$)4n+Y*V7$n4al_K{ah!yz<(+w17D!}g}k1FXE;R>e$%!zpYQmG@2+YZ?t?YjL_<*S135Yetq>&(H>Y zE|zOLIPA`SV=_0D0BLY3vyRWs;C#c6RJrmEtUv8R z-sF>wQcvrU@<1_L*%H3KB_)uC750+?h@?wR0M;rFhYR+~Dn6Xim`PV zG+-a-!{`e+l#D4SrVKQ?jkDhY`K21J3RZP`!vaD^0SW+rxT{p!Y+?VqjgnlueD$5Z zW@GZieRn(Vy88|lD|yWTz?&a`#Q3hKpPFm8n!nU8=TA60nP2lbOdn-o2P~`hg|-?^ zRZogzQ{rH21ON=L+FAJwq^3?5zgz7t$#Ai;(Tf=zw4M_OZ5WOp^soc%>bj*@`QS?z z&V2Om`<$m9d&Dli?srv+<)0taa66l&pA33zZ9j}K9Z7N{srfFK%PO-Y03-I{ymI!0 z)A>{Bi+$V#2Km}B{JWUx$WGfzp_Czu>vfo`%$r(;uUNX}pLyxR*~+(L&0mez*_Y0e z7pJjHaGl~PR_bHhxsu$zJBWkVRu()r7SOgB8g>Ez3ny9Oi$?=+=(teHvdHoMQ(|eA zWjth)Msi)AdG=+wd1F(iLzbZ;7QS~K%+T=I!E|g2`qYCHz;<=2oi z4HCslsQOm`drn0CTG-PEWA=Munu_@Bm(I(@%d0B9(P|Fl%EcRCI5i3Q-9uxKy<$4& zzImVAG3%ET`$7t0s03-Xn)HG*JZ!Q#;RMTCohiBx(ZPOA7dDKuiG&jv$wnau~#>E%(ORlb?#)oY`U{OwrQQ3@J6AG2} zD=T1bd(>}_+bou!3V2<=VwlW-C-twt%j?CnXSLqe496XTp=5#5{=K{KyB%lTsSI{& zYfFk-D_Z19XU@8`G#n0R8CYS+6CsCu|GVzO`kPf)XIIE6=~`Dl`IQwpw#zT?esoS| z;~oGoGMA7Z2}+*ORZdngX!v!nR>A*sdPhjgL4u$&cXD;fbQQljsx=l@t%Auo2Ra7S68n*PcV0>L*^Sehw@{Tv|lkb1qVE{Xa zI!)Fx47)yhDm6`I!Az+wzxyk{D_2$us^36RL=eXaQ?XnT_;8h;otc@E96-TRswkaG zUh**Hv#@_NV=?*4%h$k&dvX;ZcMZ-o4N$h#Fv}W%7YhN&HraiV$z;UR0F-h7yufA} zhWT$T4h?(V_ko|jx87*~w@$lhaTx4S7zUJB#csC@BRm@2;>E|mvr~%tFbG>$@1 z1mQQ5Rar?_0W1NMZm#HfXA90rI#33czH^5NI-*#3wvIDxL=2QL70jZIUszGBYaKZpw(Y zB{4@w%+;(ou|^c18kwAusK*eebhU~?`C7gsH@8YM!1r~URsHNewxhZ{TBt~3 z!w4Z$4RNC-djbPpTP=uzPh@vW0^pTtv<^N$i;x))2-uGGtfqILyT0_g zb^d?;!g}|?yaVujM>~DffWk>HFJ)xcm|KvNftc({ zc;)y!^Ja$p*`K{2&zxR_VN8qD;ZB}vm;B*{@7R6b$0=ut2o2;i$ zEP}1!;qU?z`}^8ny96b&eriB1MvhSa3&2fNVCk3gk{f2P-_?H;LHG{CJoPl z)xaUrv8T7w+NIC~Q@XjHm6eSwj9W|c#hldPAgVCf?4NdlS==$_k+R4RN)F$9Qc9c&Uv6n_MLZagW@{jy|Jr#m>RG&ZNlqM?khuUu z{~Z|vizet`lxOy?`=n^`ifw#O+tju71QCw7*;TUzdDsF$P-5Zw|XY| zga3I^t}PeU_%>Kmf)%cHZVk0!0T%wecJrpZbo#7zJ#=(x-LNBtRR|*u32NB3g z6@TaP0hB~SG7|C)3rcaG^39(1f$snS?!DtdIeO<^V)lh){P3OP2jF9hXL4>S=#I+jkRThlZ=EmC))Ls(DY%0JGDj zK;CETYbAbLH2`c?~7ILDLo#M&k~3SfE%2PyPYXMZ#jJR>o1($^7_4npvPHf%LoM;nz-m&phG3FJ1>lEf`eFaK+HUZ zI*dh!p=e})PL~z3Y#-`_aRK_nc=m|0FI-$)OP{;3RC^`Q`H4?`X7QmD(~Cd!?uUAh zJ+S9%{5zk=z2U^AW#upBV{_QlY_TP!d=V_fslbHJi(o~ow()Ex)fpd()VSTAA-Znh z83$}dGiB^f*VUJ=uf8JTj(_M^zt;tLHrox0#mYr}cl z?8hAq*fbh&@9mTF)Ds6}X40$cX8}O!HtIUUlWc{3)C{*%;+Y}b*;qn$CqgnY>6Wo6 zr#M44nTWz+2h5s_j*pmRcG@9_cKcx*x&VtsHAW*W^Z^5Ky4_&V4wa@QqwcoZ&Qq7A z?E)NARSOYG*9;xL0|$<;z4Y=DjQ*yqt>(2zli}?!(6z(d!)M!25tKu-QOUq5`JEbeD{e$7QE!l7oj2$?=65HFy^DutknQyApLYlb0Smv(7K zKJm#vksDX8iwR)L<@L(^t}&U}J1ZA2U6GA+PFgUE(P&Hyyjr=GD#tO@OVv5{gHz7g z%Q^8j(Y;`m{pIDfEN3g4recJrQfm&C8x6~lX<-k8(TMCMx1^lUDTw4GLb6Iumt0O3 zRTc2#pI_F>c1F6!Mq+Yc*93lzOBBF}M%e20$^6|X<$*^Yk?B1Pl9-wklhY@o)4S!s zy$?yl8kCFIZb}cN6Ss12u9c@ z#+2uvuLYt6(OhXVU4Xc6Iw|t%)ocA?t#{Q7hs7c?i!Y*@Dg%dKg}b8oTsx90@GsM8`bCtECG zp8*iMTZ=02v(|UXjiyt|*lVU+tRN&u#<3TZ06aj$zfrCDf8q^ZnT?1L^sH`W@w1## zfb$!8TCR|+_dHSrDYd&C3dn=__nD|erXp^c#P7CBTD%?D1OTE~(5`@% zbuI&ruE#G9vhsW2H!K!aknCd-U*4#Q1?v-`0%p(w5^0wJ-qT=*i_zB+<@LTU?$<}{jZ6T{-<UKjmyjYFf-Vk5XN!{77?-aCFY_2zEh^hre$Vq1c#Ut^SpBBLQMAVae#gL z#oY@6IY(t?dP>4Rzxdoveg9A-tg_D3i=em+<(SFzI1M0IsK||tw4A@WEe%{$8uJoR zG)?BE8yWe;XRnCifI;Wf-A))A|k;? z8_puNwJiZKq=?%xEMUdgGq3rj(>sdP*_%wqKLGP-;qNb9+>p+x87c0`h zelnV7Drcop$OGr6<(`Bg?>y|6H_inlVDAmJ1#YQ!u+GM09?lJ61-MTmDSiB$u4Ld4 zR?8hR+&-<&&td;BU%saG8L>n{*0KPKn>ny@yEwrb9rmHE;s02EK`)prx8!pdGx|Ah z0@zs$t7_Lf+1KrXlZympDuI0(Pk^ztWaZ{fxq1UE6^=ZUO3Dr#!Om7va_O{$dPQ0A zcjWft)AD_9KL%ze8a8JQOsd+_t_ZW{fbdyNV1!_e132w&XDE3~P{Qa}!0m!Va44{~ z=(&`}-?rZXY+Z)SN1Q$JNK{$SXn4c#Yz@?#v@)8 z`?-1fs< z1Yipr07v3*D@Z%MYgEd$wmN)e1dc)-K?v%*-Yf>}$wD_$lHrd2w=CC3cM-5wt zJ3Ncfi!iqFCw8f+gk! zJ3W&c)zd&Ni^Qgii~XSd|=L z>H=q+I_8CPTk4Ib1sD4+yUl;?(Ko&My^lTm__quMOjdipY<9{HWwM zWvu55=Pt^N&wg35$&{1^Hti@Vrq(6aE5RvNRpi7E*v3d2fu@7SZiR-lSv&>9_Q1Wf z^1c7`L3!xjIWVM-j0bEI1EZqTP;nopc_`})plRlaSpchgm$g?enV1~akhS(=11%Kc z02U7ImrC0tC*E+M>^X1zgX5#SDD&F9uqP z0%@wjbpV1GPOkOL%7&?Z!4}6X&NgdsLT--^%&GP}vWe#^8-u?lV)j>G4p7c6z#2iP z$J~{2W?Re+Mi_E%;MmJD);!k)aINad<80f|s0r(G*oMZzIbYizNMLkKrT}&pW=6yl z0uv37iJOX@uoD4%F;f6Mx4D%~IDde0*v&JW4F!|@jBbaaB2(r;2lgDGuWji4W8vi7 zE;?PDCpcUS2}O6P1F~H1N}AI>0XUlWAZ%?}bKHFFqn|hfnK3SOge|ji`ph z(cN*`T3=HD!XjjyUWj`UbZTGyYYPBA`-LyHoKDv@m(#t-KX-~dzmVL?&N9^MHag-0 z;#&znaUbK??=!iiQm!-&d5{w!Y=9?|gTEU_4W+6@IzXs)Wcc zIHHz0--yqR!)}E$?W%#Q;g?k6JJf8M`~;L6?FI4j5s34hx_t7ISX3tn8h{Gh*+m2+lUZ5LNxCF)$Jj z$eqXb$lfFS#WOOhxyeGgCf71GEhejgsj3Lppsx;KI_%V{{07#K>snN80@bC{^j9Xm zHd(DT{zIeH{`4acKX&rrM^3)-lmK(GR;;fpC>1dBslD?ug7u{F4TF`Gav7;&omW;@ zq~EA&dn5bTCn$l6b)a5A!snF7A37jUKek`ueuvz1e4pHR;!asgW)(PYr?+K&>6%;y z2snHGx>UjFtj)ZXvuR1LuSiQHN@gw4i`x2H(VnZd_4D=`03QH}RwwMl=v~@gX9XBP zH01->9BPJ8kAX&cDq_PPQHI*4mF###tffokZd(9X++7Z(2}NVr}U9McXAoWU43njs0wEDYTE(7FRB?#bBz~_AKLR!A`wN>47wg)kKLhwpQ zI|Lv@rq6ap8ho0#25ScJ_c7c>uhS*l_)KNo5ugv8MO*coZSzMz^!@wyj|HdV?twZf zFO?{9hS}@CzFicT(H5T-??=_kY72YO)#zAwbVB-^Rl%Hk->xctt#VP@>&$MS9GQ*E z9dUpc09yvZy!e@ZoQq^0_5l9q*)5AalXFSO7uWPD742Xahl9h|bKf3S6erLE~lDtU77R}nyNWizjp_dD5&%DbmF*JLAI6n8iw(TS-+$~&PG z2%R`LJ=hY;Nl(PRGPgIPwHYlfHZjSn1@ z2X3F#yd7m8tvHZfGtfRRSkUj4K{+j%r3>&LB}MQ1C;buz`r-wq5xl%q#^o5QE5TqK z`BtCNV!ai+6jl661)b*qHaQ-aM;}}m>zjYi}DsZ~lx>-B;(x-h^%y?q-P zHTI4pcDLDPQ|)L!HA7VExw=foeX5u>9IXp5)E#KSN~Q^LU~*_hI0cd>XGZ18Qzzun zhi;RD`(~xqt|&8Szj(V}mI@I4=1x^sx5~1`@ygia>~>b1Jura*E}qMQJ@CusMp~9` ztjS0KXXRGvr4APDpk%3O*q+B)3~nSvq=@?Qxunz>fOXfTP+@ccOlP~KN@2XTgIW$o z#DI};X@S}{K35r_q0{cmbv#pTU{S63J-Enw$9x*r<3K51xF#5b2bXdj?0|Bh6u9C1 z_L<2L3;`pLP8fZk){s=;^x1MmQ5eJF9L_n%*Yb0)fDI^fIJpcCVhiu9q2YQt!;E;XsS1w+am7D9@858jPB+Ir&{9C*clcH|63V{kB{@b4iD9?bNJV zsKqW2?s>Jv;!&$COa|qiJNCedc))^0>V*OvT0;eN>RpPdwDCP%Np5V)mGc+m*)M)s zHUMts_8pK@k3J|T-*C5_diY*>;!StUefJ%gqsNZO8bIS$Ubradudc}C{2`eINZLwg zRgZpPVGFPacFUzv%x%sbh7pFTl8KOA#sNV3t-36w%d%7I%hEC z)Ufc;fpf+B0~U@}0UD~6lB9Mrlz#=Ga49$#1wbj$4hBPTf(|{0{a*JQMuQ%C^ik`j ztRRE z1mM7C?EtXb%i;u)i~HcrLQXhvkUYCx&ZfKaaJj(KeZx9uc44rPCz2UM%KhwBhcfb22+OBLM)6PNgb0mbWD|s7N}w zA?Kd|8b03*u>^xM8irFP5d;Y;woLNrudK_5{$yEFm5xkLjY&Kf!TD{A(H+i)P(2Sy z69c$~D0l{fv$(b`wQ^q4>9m|H*kuurr4*XRo|{3i@biH#s36&^6lKO%muR1|>nSPX z929VGj7CB2Rjp^pk(sEZwo=1R1yW4BuZ?Q8SlCRVsU1lkxBuOnMf>z?3IJ|Dewzs! zJK64bM}FpKpB%aW>FJ;I1sxN`Oi}hsCnPW^X+v#AsAe^weZrf?!RNfK$SLiZ>WT8Qi`Z&I!ykUlxEreUig>kkY70~LP(>!uU4F#Vv~3qlVp`AptaDO6vvrbMUPYp4xjpEeD0 zwWbC-5{+ovr5~G82LmO0p-wtQneq*WW7}$U9RNca0G2PDUYF0F-BJdW&lRy2CkT}Jd?%N+8N`TWa!)pKOm#@l4{^vRQ z<7aOIV09!1V998W!#tc4Si`;*>pcx`KlJ1Yal`s+7l6*V=afIu2-e|!yQB=8kx#Bm z^2SBEcIlG5c;=#9zH~zCKlZGeE4EPyQTMY`CL8>yOHTUyh!MhWY*Q^Z*VamnNP>-gMUJIcAT zHIB0iD3|QV@6?OW09bJ(G^1LaA@2rJnw}UHTYOxs9=|wUPK|(A44rB44fP>Pb<^c9 zUCg}u+-lw^0ia|l!UK@YZl(b4nh#EvMP{t2(EDfVO4Qz03FYMG25kcvc3nIGPE($) z9GmcprhKdML zpr?+#+bp-W0NB7e<6Y|E?0SGo2skbMVU&$zVmuMhe0l|Y$D%nG!L`v-fH=TC^z|xC zfte-}J{f>Wu>H9#xZ zP}sPCYTEW&hS7Zrrq9M!t!KY>T@j$Ao)M$6Ep}nSM?tG`nWN#Ha8|z(<#xa>t2JEm zVoB0qhExON=usWW(;wW%c@SWsyadBo^WXC|=nuy6O>=Mi?%5yo2IPOo`8&wsiqmJ7 zWFwgua|>v{k`imD@)z3BWcF^La+Sx0p`sVcX7OA=fZNF(tw62T>MDpc=yrqsUi-he z+@@cfo{8T5)YEs(-g|QItE%b#t-Nej>pIqNK&D6i37ZjsBlhE$O${)SGo29?!kEpV z5*wpB9N2MuE{MzI0@Ai?O#rKH8QzbL#l+?J3ok|%AgGidjtJ&Zk&w?XhZlCqUdpXX z5PR&E@zJo1Oqk`#2M@@J+h*nPZTl2pT!3-rNKmSgaq1osJO!edIj7@wJl zN1~vUH*pS9rIrNT1{kJIeCC#HWD2sLtwvyE z;!I`#Kla`OT(YaW5?)nxE9dm;)ytu~U$;6)t%L$dAPbDKFxcP#cm^<@u|4B)#{R(` z8ym)$vHio?n3=)GgF#@Dgd~Jgt8>3j?{)5eImfD3Io_(eRrk)`yX61#@6Q+vLM`Dw z-`Af)zplP@&pCUqea_lz6$ofFYtrt;Dt9b)!^glLpG6Xo#iq#B69;zw+zON1xP_4d zrN&fqS-t|_^QKw*{^N9~%G|(Jd>3RH4r#R}BtEzM+)VM8< zGJc*C6Z1S+qL~w`m)-Du0AGN5C5)~?>ZwviZgxg%#{ z;N^uJKJkZti^+s4dK5)ki#(Tn62uF;kjjeAh)vozH?g`nkGa`djGe!L*_jyulv8s{ zIC*{=S0)y*PV+Y~ydAr5+9kxuQj-8((?W?qhYQzc2^^+ib5jDN_m$a9U`J1g;uaMO zbrtDc7RAh_*bm>dZCGTY*-xH@t@v@ujW*cib;CT9e(p43Qdr23FwJ*>z8mWv_-L)i zLiD-e7;L^MtS+yBDAulE!?bL^j9j((EeXaOD=V&SI%`k~14D7hDFUCKPOcWw>uO=h zO8}`-lXhOT=?`)ZYcyMulyypQRQw|D(xIl3a5Rbn9Ju#A;-|ulzHfu}%x90U;_1mO zX46e^M&&kP77SuBAny8MM!%3V8w~;IJhyyu$hPl2d?iSGXCYa_+2tZsdN2JQE(}Gi z7>>Eb2{1r=zhZKT3E0|J5w2DZ0|das!7#-Ki+Fmp>`rV}X`fLAoF+h+q<@$1p|{sZ zzN*oB3)1&DkXTEgGvXHoQ`()vxMyNI154&J@Tua3=`Cpi>>7?Crg1IO#qg@2_#>3b?+$E&rO0n%+=qG@|f2O=Li$=MGHToPj+Ir^M<5(#) zkqz&Fe`o~T<4z$6`q{jUS4f>rqcZf15_@KgisY7D6|!zeu}Hv*@@6N6uiI~mF!cbT zcsIohFD%|LR#&%7*9`#t!5_S{?`Pk$`-g0Hb7WxH`Tka`_NL@!*`jGy6zT?+*-ed; zCsk7MP@B>3Ae6hbmTBLCagVO0{rJhP5|$HXq*8hIrj02U~rvka|`Hf`9gh*c?j zxTjbYgDM?vQ)7`DRxxZJ-Ks*A%Vr8vVYV6377?hR4I8GweLq3BM`5l#6%4v>zj^=8 z@5;{e#U2)`>E|pK^FxYaxzwQO&KmHbVCVC6!4u&7Cp#s0leEn+NzEia2<@Bcl|%(U zd`q0ZFCk(=$bCJO)a|USM;kSfmFoj0Q8Fou-w7M|IFzPH-a-kqI-rKQ<&m-!`pnNOc@vk{WdQRP5uvS4vzYK_^$ zd2FT%m|RTa!g5}?1>9=JlBitK!~#xHl`B+L0VkoLM|{ke7Z%zUyX8E*E(5VnD}2!! z7^e&j(zDutzHSQoy$)QWAjnmJoqMW%9t!@Q6aXXWigZy$(6wpLDpz zHO_=G1xaSUv(j6&NuZ4OH_y+G-T=1Iul}%&ka}HuviY8#e=i&f;irD)ZFtqIZbOI1 zf(_a~Yg$&8JvT2Bkke$Gl%O8+d;enxkboO@Bd$h_`A@c%~6$9aD95wxg z5Wpr<6x3Dv?<{?Xt*tC-6hoNLp|XlwHicwj4Kvdd=;?6d=3914`(BPh3ERW2PG6$} zjKB@OmsTZ1@uwiFl_skL8x+6~A3RQpzzVfxm1pII(iw0O$aNz~Yn8S3a+^zXe;DN$-)xgBY+fs11fg!eqD8bM|`p9@mRbs!)RjoI>FUOu-mMHm~%Ivk46A?WZWl zWC?h|!V>DrwF`KkAde#x-I~p zlNyUN1&lmeEOV`x;WnEPVfVzK6VJ|+ajqJ`sk{R%#o&zZ-y7^cND13% z;nqcpErmQzoSDYX-9rdm<7fz@1 zh1Xm+0MOZG{Aa^ZAEupX1hI*+ZS0?jjCW5Az3!sYRkJKK-JC;tD^t!-Fw5|Gk@X57$=S&(odg0^0dWb zd5y(teYaV$u9}2g!+uw0bVPb65XDfZ>a=DxAx+6lx3!6w+{3%MnL%fmDp*Ry<&ugZ zEpA;`;i2lHq7qmlu*cUr#e5NKi>t8EhUw`DN;SxLo!p+I(7Q^eN|>5m6~P%31&b~E zd9T+Cug56~l&b~qKNd}Tvm(I3=}?5YQzD>2YXU~2B1d!FBQE%nwInFOm?<#pLbNkU z88QDHTirH`DG z7~@Svu}N<;uTeW&q_Q4Cm6E*IBP2pL!?}5|Qv#yX@W883g%>x_9dW}iZCy6FEMZz- zU6fp0^k>@UDmw>a^mp+)Zi}MOI-IfDtUp7$=k;gDPM#hg8~e5l@NYGocEx0tdtgce zwHmZ)MPx-)MG^8Ni)^wTEeCJD0^uW2ZdLPm{j?s!80hX0f+yeK^IqZayj5ty=L^zY z1cbp|rK){;aSNB{H?X*wla?k{m}8O~vzfE$6fRz!pgk@b?K2u$nV2# z`*z}i*WFJb$A!&88_eCYcL)Q0F{BB!TpYiI&wu7&%uh~Y+fX;+R0S_4GRRvzw7=76 zY^}>ZakH3{es^|qyLrzD`uh7YesO|;K~>@z3$|3tH3AhWSegwvp<-^t_TC8Yf9+n} zy}OSg9TF}9A0TvPPunY31+&kayA9E#Y* zdCp3+Thj?h69DJ$;ZoR$&4Mi|dcL`A;Labrl=;dOtUJPfboF%Mwp(|@>U1FziJ-5qpTNK%3@T#o-P0#JA>2LC zEK|H*+Qhlhb6BI~>4P$(vi^oOXx zA%Kyj+)*{)p`UNqJi%7Z+xiF*tfUW|Zpyxjgx z84PAC&8AV2)6s7;KKR>TT}~J9_TE9;iHF|Wso#57uW9#95sR+DEMDP+_&Q@Du>j=? zVoNn}g(kMiOkmoS_u`#DJd8IzFpNMTAlE5!WNvQzv76a(u%Kdk zIV}|z2Q6-sig^{ePi-`Gsrb19umtQlBl$?`3R2* zS}ftrt%q-Y^IrVHe|a@tdux~UnscCFK@>j0dV$|6&&HpfFV=A4;(`EGRxLEBYGp13 z%SLl0o6T40SA*7x2O(#V9BuC15yI~NZuD``iu%#FEsBwCI|u~1acyoL>0Fsr2rm)% z`8l)AbnDskC%-m+{`9x){r7OwZ;3*u-Mvemz3Dpn%NeKQbpYqX;N0`bWkPb zBQV=b`ytGC(v$>QIEbCeGF43kfW*g~5Blj{b0zq$`@5ji=kjqSlOA0Z(7M8Qnjf1m z(Zf`7B{vf&tS#f}#2iLX97pfaAV%(ch?02-9tw2%7w*BiU6euM{8|FBco$xE?|sNo z#dKkEk={=Ux9r=2A9=$qR3(|j6Tn8nmEw&^35O}*`W+UGoxj`$d1i|ky__DKz~scN zw1N%yL@+SegYm1YNUpC7~1Mbr$LgQ8L8UngK&DD(AEQve5F1LgC@dbwOlf4ix^cI|vJ*6IIm4wwBkv`apn zsiH=;ijZhEvt0lQ&r94z)vCEEt(GoZyDH{)7qYdmt&`iYn|N-ffUC<}*tctlz(GwE zB$=%qjYSB|xCMZFm9|qLy9P3;)BM8y0+Xlt-?__$Z``!HF6%~XHbvB^iw&l{gaIl= zN$ip`GPa5>Dxii5eMO|Vmhcc}ZmUiFFsls3Bk9iRw zG(P0eFyd#zF@b0T04(#{&x3V4#!W|Lx&~quC zkeqmD%Pr|_a?(4G*{X0ha-ylyKNuE~F@9HUkOmQ&gX{(FBw8YDl&Dx}-#Vl%c{N{8 zEGF|G&7=MNwJ(xD@VMK$!g0f68C;G^Yy!Ss2j%l-zd&H9;|+9)U2RkTVqQ-EH? za9;<)AwR0%-flj(=L*?`T%jbDwKzxd7cjRgHX9`hN6_EYZOw&jHT&)>lXJhdww~TV zyYgp*z!#b27ykJjS4AKEtw&}p4(GqKd#%5qwv4NVf(4~a+01t~gI!?^c1MJ$#n%yH zo?&8Dsk)H+_%4zcE+3b1mjF+s)#sz+PnCb5NdTY*w_hWVpQ$?|uk0^@!&1kxR z>`M=yn1ANkQ{Q#<_Wc~A$B(FI&YbuJC4>h}X7iuW_uJ&-E@sI)tX={{+=J?Z;-jjU zNlX;X`#OBMeP0;eF$XTKF_T_FKo4MBw3m`w7X|c)bW`Xii%2N5-RWk!h~Qv2i z#R4L4$H^migav>oe?#u`$q$QHX`?m$) zQ8aNa)N56#lH`_1Zy(jOo;i~{dCuYlU=|Za9GNc2J>Tx(4(#l75QuTLWn(E&EfMHr z6~AglYqPGY8spCq(;_w{5oJ6UpJ@owpMwV9@wXi#Ym|_3Xy_%D3K1hBz9Y3-T%suH zcZLyk1mLpM`li67(DzPfo0ynhm+FZ2#Dddqdp}iJZ#sWz?6YIzXJ0fq;6%3k-^<0q z4_Ax%^UPOa8^N#ya*4oIhtDY|8F%gO5QaCiv{Ur|`BaBRn^x!Mk(^(^sbeRxy10xV zdE46%?;FJQ^nwtsn54*YAeYT!c5V?VDh^yukC1IA=2ws;5Xn9Re80{b@R~uD6TM3& z>iP)81^iw#o0k>P@@aY2tmP`l+{0N7G z$WtYKX<|_raS@3bf<7j@ci8Ub95PhpA$(Wz&?0NY! z-&p_jcQW56M@Nmx$qQ3+^H+YcWwq^ESCuETn$=)Ej3Onz)slhk4i8+kPq@27t2ZQx zbJAuqorU&NPftirqBK>-!om_Z8O*xevUlq>?t*ZOPe7F35BIneju7C-+y%8|Mv3;= zu7MZ~nhQ7D?XykI5K^9rDtik79h8b9%gd)qRl66@ty*y{Z$;kZ!JNGpy*J&2d-lcg zhMjHCLqa*q775~RE4=hOmt?lF?%xzcZAYRm*0;4JA#P2O$aftWmrf6FRisfCA|(L z=qs>L)UMvJv`3Dgoy6A4JZ#}O3KlPXH1{m9#W$@TAurx=|9;_-&8=h!m9lwMF0|f*OIg9Q|F3xtg|^t$e&(*LEK~cH(14jvo8}fx+ua z0{oLddaU{WUwib})yvsmOm658CKn3prlv01>WOs8SkAK3lwt5DD%EO+$!e3kTa&3x z0%8?Fwt;m7gNDs(Z<+0CRk7(S`D}IK1+E{59nsUkzcTUvj{;PG%}XD&f0i77?kjH@4FzBoQVgv3sF|DkQrc^#>?O&;+@fhQsbM zJobR@a#~APMR~?*alXFMa6I(L-=BHv(XSo^uVq3 z9_mzbaj!R5aI6|hQBaN#9S%yE1Txdb2F9oYom*j>f$CAGpabr^#ncWrGU!Le>R5~TZtsp zT$0uaMEl{k+i%5=Z3EI$$W?uj0xY*o>Q*PNT%`HPY*Nryg=}@<$`TG7n-@U9`~ijL zmG7Jfdt!LUd*6fCz3Cxvm!2iuhn=_HE!7tjiTMhLC0s)Rms~GBa8EB@y|)WZN^o4m zbH!+;WW_&QE9b?$jMrBtmzR^uvZ{(E1(U(J(_~M#Riu(xMU<;H=D@J8MayIp(Hq+f zR_2k2pJGhgZ$3bk=vp!elOF9zqirU?p<7% zIkhl1^{y?!w_W8DE(1>85%BPYpAi<@XvD*~KFjfBADwPXV;WfLyW z)E*c{<8BM>iTKZD9T;BQ2!Nb4#^uZ&!e{PV=q?C;Ta>(4&=m2;<`dTjiAKeoLN zFVB{RYV-$)m+nMjEgY?Fyel=w$4uj1N#ir$f)rHb*B7Q6lC6K78S z^4W7^%ir($otT=ap1*MJOT|)dPo-4)Sh-wIH5)algqTD{Fdb^sO!J*ZvDQ}YWNBya z<*w;$9Agt{*|6V(*zk^alA`bR*n}>`K-Pk=5=dsR4+iz&U;uXx_%KLdnN{gJJyxoE z3dq#xEmHDZSx@1~r;p;9C!aviz#xA4eeb~jeS6S9G>D$wZq%sKXObC{^xOob_H_E& z_RI8KK9`G66X9cW9j%dl`}bmCuwRlV|C8_Y)0D8gdgBlW21*cBn$u=olPjInMg~9f z>OQGJDdR&SFvEBGj9o+Qbx-Rpk;=4NUb%Bbt%z3vcYwI4c)7H!$|;ayY2OVsD6!11 zrEz(FU96}T2UW|IB+c}0gB=|RL?Vb$K~$&Wg1c6_yW?nB9de(`r;BW`rxIXhZFr;E zNa`)^4;z|x$J*M;-)*j~e$R!(wTTO(POsxZtKIR_Db=``P)$aG&>WwJv8p3GpR&{8 z<=F(9fo`gFt>XSy<=>}@I7nccwKjaF8q8WjoLkqaGz~<;FhzP%AyA-GY;X~9XXd_E zDWg`*BNp}wfixht#8gz#>vHRw*orfW_sqJAGxJ$t&Yv8gq0i_*u}J$mxr};#3oAxI zP6c>fIeZ@M?D3$(Yn9FgwbGOhrAwTlR&U4eK0=kRx{lfSdgq+_fBB zR6GQ_y2Kws4fG-&4^hDsk{F>j4Cy|Ix-GD16{Hghk+0T7qrxH%tHUt|?%f|3rBr5= zb?)4OXkR~Uz90)4HRm?7Pv$E12cAB7=$%K8pIH2t=J#a*0C;@mxzU-X$+E9ftAE(- zc9aa0SuYgJMunn`Kc*NCuUWSSs&IEIdZ53%<&XOev#Zf^hng)J|Mj z>NRaiHw;duFZvPs!%rW7zhbey(eAL$c->B8D_=J1e8J-~8KJn@u)13Cb;IQA=`tb% zQP=}!s{U(m2l?*FV)(rQ!)~)4ayeXY&zH&%eB+68k3I3V^ZzV5d!>!%j~re$6#Zu? zq1{D+^Vu?02%{I5aQf;7t}Nwney)Ixt%9`eISftuRYMe8&?e4N)$aEbV4@&x zqRp-u%xYESnAj|wO+!vvxXN!Dlw2shvA9Q(c_9{C+e4SC>0+Ubi_>ca1iZMqso~J& zP2^Gu#NCE?4XkDA(we4GrKnIqvan;L(Y9xG(vC=!3`}pT(v`rc5e9pnD|~F{5c$yr zeTOn7llfFpByYJBBv+wor`$e*<%+e;e~yh$D1n5dUFhiUMt6Uov?w+;76hScX+B2? zeigZ56-8B-YYt|*cloSXpuhj*wFC~0uVD1TJZljUV6^_Z#ir~(eSYj$PMtZKc&X=i zYI3rsYPAnGn$24&5Pqs&uQwW06*buzl>i!R0Ps6wzjbjr6tfZw%_#+03Q(NPwl>#A zPl4~0xn-7+m0X)~@{e?eL`wF-Tf%tXd+wKBbS6h~V&zU5mi4tz@>^cp6a%UASLSj4 z(iJRHb?NqcgiXD&IFCvpCle)+wZA0`rOFX{>6)l28h-x_m)C8-Uu;@eSmcNm7y|^Nnm(G>nh9I<9m=^Z?U$wi99I5|xfKL$Ch_~QHcHr7 z-@x*EMo5b6Xcpdxh2*e z+1x}rTOtr!gvZp7_GqU~htERAjNK%sDPCH~R!aJdi8A|T81rkXtA$Ga=c)R5`{Coq zj(sO{`?3K5JRW}bROvHMpZ>$SjqKe;we~2*1mV3{hn?6|M#^mqg_^Zc{K0lYK?oSq~ziEPg zwccucVraMTu{&?uUbyGZoAhwZFhZSW42?K3ywhdedTY1->U)Qc`|jIT+OaGA3pQu) zO<()U*b|2jUVd3i0Dm{*l^lA z+sGiACOb}@x+ZmJ&w^v|kBixZ$A6lSO@)h{RV5TBU?M zB@VWQ<)dp3E@1&57TmpW0MVdZT5e1fq*?N{tm;DQ;iFKtHQma#g}XJmsB0yiA^xUZked!*9}W>yVpiFA40xaH;@*fr8GEkxyF2`#Nzuvu;YOL02) zA3c5Q{U=VJTD_k0J2f?JT)lQ>{le8NzuYp6`|I`E*DKYUkxr$hcVDV`t6d0BoFujl z^<&#$pD1{;!)LRuit&o2R)A5b_r1O>{^;C-cH6!#{Or&F1oq!C0I z1pJ~Q!URl@%L=#4j!gm-Q}c6F$>va{b)@C;Xj1T%QzPbYPzFCgO8|CsipjcivcV@# z#avP3uQ!rer0L(&R9W`=OkxJ(p>@pH0!(6M=R*GHCVgg*g13XsvA83qTA}q?79hYN za*5W!+-g!(1i5|G=?fzejiSH58-oMA=pX>ad!`dhXhPQykXYO^W>!`MR}gvAZ#|mcv@AA_hmC?6hV>xHvsdu8Rl^m~|2O zaat9T)n=~E8UeM9jf8;oUbe068iFGd!@y84f~^udJ=~o^ARyor2fh~jsSpSp+i2ji zSpv}X{h4UVB0z&71)YvM1_(f}Ckea}z&>^A0v1*XD4HprbZtX5O8b`qM9={*&HLB$Bk2G&`=W&|IbqxH_LC&^wK}=}BRa=No2mEoI{6dai~=`h2xi zfy$QTCRJ>TCnsl-$>m@RgpqcHak=7zyMG((G>q zUN=k28&s@pfm^Lxtp@#lI)Mihx>)dt0LY%*UAVkf$3nr3-kl?I3aTs076E|Y(puU* z&mDgLbBB%|ZG5M5`mzE5|1iFGWPECJIr}roT=6Fk9-02HFxy^hvDkmffmkIJuo?ry z4vcJ%7~2N=>K;q~#pJ5;AJ$h|@A~X#kF32^&-nG{Ppg0Pjboo!OXcr6eqr`EgTcT% z3|+a`V+$X5SY1YEuY$fIud#bihtV6^SuizXUo2$pZ!#;6zx(o+p8CE5l$XOeefD(g ze0V_W;j8Fu!S1 z1#LowyU-kVc>R1wUZ%j=pk&Y0c>^j1d+vSa9&@>GXA4*QjFnUqCQ2>=7h9t?CCEoR zA_!6wVUx6frW<@m;F~yo;`i_de7-U9p^8eKQnf2{B4bT<0t%q zz(h}9pD`GVVpnerL%rSDK0GKAu}o@X0wD`{&8?)6qD06twp_)s`ddpkiSW|G>?$hj zSCN@nz})$1QKic;>4|>NXs{l>fovv4)ucsQog8*MW~svFL~Ew`q`fF4Q8oeU@9|)e zs^`L58o5GE*z?So_OZL9(;==dn zEqw8O7AIF~LJnqOBGyXCRGI%l?~Q=d#Bx?zf74Y%y2lt0&MhS*nNKaJP&7HvLFj*p z{yV{@Mp_-kLPf4`Qo0>esTvMUq(NIzI6s+OQNS6?mNOrLNt?{s;Le8z0UUPpW8kOI zdSXt5)6_!HOlvKbfL=dpBpbr0Knta)hT0gX-$yKpMCT|HlNbIlCPA0iZ=KO!)$75x-pqbSKc$Xu=UW# z|MH2y|MXu!zjZyHb9`b>?(IMEXU{kO_@j?cwj^7%Xx)R^34 zr>cp2x~Z%YmMs$~`7D!zqoqZOf;Jf|;qfs%3##zZag72myXLa8n-HteK7MDC7mJ3} zD{N+>NlOvSqXq&#*gXNHs*p|OG|Iv=U>R5byr@Hw`$2Bk6zfvjqD=*8Ed&FzdMA3L1Ei(@AV?Hyo9G7zlhPvjOcIpyzTCh zI#u4`po`|sgy>)gUVHC$A)@uub91Hb?25ynIdxD#=i}rqx!bSG>Cu4;Nj!aVQ|6Pe za#RZPOk`nIKCjCmq&+^OXBp!DAaL6V`^}rBV<_woAm21g`g{&dPzb{cvodh-pgxOU1US+j^aCZvC`Rt7sInG+z)q-afw~T!i&x32Q6s7@u50 z)bGLWT_fo2>JVb~CjEbhUK0ksm)0B0Xmg9BPv+bqyqt+umDZyhJKPOyW65^62b$H3 z^`cGyqXH{;2pF1hEjXC4_DfI5Er5}=GC~x`G+NgIs@R>aDw3@BS}M~swk^F2brZJy z+p%U2A>P{|EygS$C!QbNMyi?G(!`2^j)))Ku>cmAlQ=(oy5AaJ+-0SeC<;GCqMdUU!4Bb|9s>-K9>1G z9B0Q)6;GT!`BxUZd3PWf_;`0$tP~6eg!SvAKyG(=sOnY40H;A2naM3p3MK(2{L_Xj zm8-0U3-$4{O4+tplUB;PRqg~@CyabRX0113tQn8lvTQIZRx zH|%SBb>vw)zeUNdD%Qa(S*q>|O{&gRtmNq3v4SAGLsFvTdvTA;E+iEp-ps@r9)9!^o~Md#x!4kJ0(W8b#GH8d&%YkS!!fE92?S2epsO=L zpJ(NcBbb_XStq=vLfu_dojbuDq+FdBawQRx;x)&DNEs^WSv<(O%0zyu#%+Dd{V;^`w0Y{Gjnq0Xm;%EGKgb0|jP*S>FgW2K6a=MJMi6vy& zI7zJ$&{fm*TJ4XT&DO0et1DkvSy`%G?>Xmj`pntpH;b^hvwpK6)#hGemMzTN(6 z#^K9J%oKD{u1x03$P}w0Rom;cAw>Ji$!ei2!AsRPtN$@+uuj1HvFR$bvbV?DK0PQ*Calus7AVZwPdWFJZq^6cLkK zIeKnQz#v;7dmMJTo(YF(J;gfV3q@dc`N7g8D=W)LY$QZ=P`BDBwphetfk}>LOPk-s z8WLSWC+^=JprWNIvg36ttsPpw+~!`^jOI$Z{?)B=eeY8T4*$w?&mWxqUgz%>0|5SE zeCgp6#-IPmGt+0VF`+ zBh>EqIz>rrYcq*dzKU{1L)2>(MZu-j4MOonsyz6}p(&o+>>9@+H4bSpWs#Pas1mZ5 z1NV+IW8EP1KfSO?iM>W4pp0rQL&?dFc&r}-L)}!}xrGTH2(Z*>i-LhqNIQ`Tw=LG- z_1NSZfL-92-5jSRAU4DFOsrWzXk)=_wY<;jcE0|^*!Xkfz6V}|ZmQ-6I|7K(yW|!zKALCYlQt6?q66Xg(!2G$go|+eS_WrU%UGidG)+|? zx7u-+iEeUYV|ofK4?eMy!ry%PA|C$2QL%#+7t3T0^Xn;Wrn4gbeCKW3P|@3`OS8!$ zt)r^6hjQB)Yen$$u#pW{@>M8UDKw~Br1hoPFuSGVFFyGUzVPT7F;6Pwa;T~*wsl7l z4~L{hw7)Y1AFY*Ksf5jZ1&S*mfuA`J3}jR`hA{Nc60o^_X=Uj{OG}IS>pjQcYD`aF zI1=sj->EpPZ>zRU>#X4dt3y=ibc;hwm$=W~X{d;MncZ)KpWY?!LnrNj?yM*ms^SuP z%eFXy9t~G#S5PsWxV%I_ZX<;dC0}nm4ikYSpUpt8-%2r{eND%rJFFq2vIKW2kD+(~ zyL#z;(kt}2+A`tccoYpv_I0%(Cn_wI!{CDD!u5uV5XA}?fw_pogu8p_-}M?j7lBVv zN3>C52D*bTjPwLW2Y~^KL4`yilOb>yfbNf@r1)q}+VSdrtUS0Ex840J(TC`?(F&>O z2=Hnc84SY1R9FH;tSrZV19lTt`fdaBOBuO#2zPfQ-W5fvVZ&S^e==LH-u>jkBR_rQ z=#hVs+5fK@uQ&j3W4!XmXTI?D)`vdz`3qk-aQeZT#W~zkY=3XIIg;EH9_IUVHtpe^ zRaeD{jytYcBc4frn!$H*CQ;U6r#_aFW%5io;6|;UrvQ?XcB@oL!`QVoxr=SKtjJZl z-M50J%{0=fBwD4kIGAmx4~^apmFI(zo-4j(=C19M7r_}KB8e5L;8spZrUO|4{(c6RsT{(El4 zV1G9}ejhqJqVy|_pvNXcIc(+4*CQc9NgTj;(L3y=Ksyw3;m6)`3*Pdk{pble28;rQ3l!8!QASdVChdTqdeUAp&5m zLD4K`5D(Zf98{z#)XWa7Q?)sps$#vM%5(Jv?P3lTpem7Xf%pn5Uuh7?;43Bst*a%@*L|vB64- ziWRqvbQ9RA$tem0Y?k(oQ6i5yTf(tXH67D)iNjcb-@yj z!3cK2;SHjLD*O&g@a)LS%=~_jS>|Vp_Byw3`e=Wv6~3vZidwV7YGbf(5vNFYb>*s_ zuNluJRB7Ahj+B7cCMR`+QIEtIx6Lfqj`UX{mto*qMOVZp!aP;hcyI@ib;V(JhbbOZ z#knx8+9|$JF`#G&JIo@b%#BL9s*Y1vGuX8)jHsKy4_YE0%{-X#`3#oIRFt@Va&3^; zl$m6q@=&?add)M3j-7k4=j#RlxG}EVxN_x+@zMYB>E-FQ?7J1S@gBE7_(-Fvl~__$ zdh*$HM?{jvR*_{fpgH=lZPbZK+z!I{PNcjim#R3}w$ci*-fckJ6mm9Gmqn!6$e zXEy&@E7XM4)g5L*7zJ*(f>gPQgI|0S^Oq*+JyM0=*Dv6Vy8$**MIm5Xn<{Lrnq1Ye zaSZb>M1j*{qj?Bo&yIfd_QXVwzDD!K%>6=v1%m1XLP8XjTVmPFlEExp%##uDxUiOG z%i5$E@e~_;H6^Y6GII1>1_2kA^$k;5$PSGU+&O?>ed~abxL7*clN-N=J zsU~ds^kxFfYYF;&H?3DUtudeQ2GG>l(%ew%%~7gIA4=!*4=t{&Tv*>&f5n{$jg6hI zUAcDde>j}>y%xLsBUYf;;dHn2(iC8f5Iu4-^P>>{llALx?SMmB)sdwNQMFOv0|K=nh(7r)R6w8Uzf13kw7! z$`IwxY@I-o(}O^)8;Uz%Twcmuol9i@bFNao^_c^Qo;rH$`1fTE+yDSK#&sWG`08V= z5B%9@o=ugC?`m4CZ}Ip%M?DUkZk0=ZtF+T`xx|coM(-BcgoYduDmJ?WZI&G|3>$4| zxAYhKbGPq~!{xS$+^L1bhE^B92X0B@yI(#wW@fjUp3@g}6N+!)%;hz#ZseiXnpv~O z{NLR^?`w~pJ^jB=pBuj}#MPI2jE$XZJ$c~pm&PU*_D(EqeV|ZM6Rd9M^ZMz1c`(=$ z7cKutI1HYMzK9D&&4jg_A&Id_#aod8SGwiKrTKNNC$lKi|1^~w2sx<)r}e=aX-phd z=~ungYP(GG+)%R5Qj*zs%O2c)-`!|g+g$*x^w+A_#Yytc{WoLx&h6MS+>hNm1`v;R zh{2AzUKWd3R>8AyvY<7?cG8*{fmzTuI3O*DEZV{?hQ3xA4?jME^GO1ogcOR!5(Rf) z+h9L#9vK#sAHJqY5cpu*XTA<$j!N5&xXI5yUv4yizFe!_Pf7o&VzKy&OZ@+IT)ccf zIWcwd_nmIXtrgw!xsAHDQq`HVsWd1xs4LolanpB+0r=bQQ)=e z1lU&X)O1Z@?e#`)8U3S0EfqlE;RI%4^b!JT%`w6P9xu;%10Ofj` zf@>Bl$pXts3ji=%tdi_S?w@|KKRlEC6vct*WF7oFY}RvOy@{n%jefQPug8H%h&E(` z@9b&g@u5KnM)HSC>i~OMCa()S>y{Ps_0x;Pgn5t>3N*JyvWwXNh%)x z!tS!)GJ1CG14l+rY+UD8eO<=X0#i__cOyJr`6_IF|bjy~+^ z?Z8khfP!YB!0CXW=75P+wHj4)h6ROERTL!o$abShfUHrMxvW!yF4F{aD;}Txuv;U~ zhmWdCbG3+UCWA9q7HPdKVQ~94^pEUBrcgw^u3~j%1y`o#pi#x{FquV+O05(T@USYO zg5`8kh-zH5ahoVt@^#jrpo0CaKk+KO@rQ4R&&^j%O+0pX3#0RSAq*}i)6y@VNvBa( zs|W`D*gnuBNBb@|H(|~Ib6aRlmrEtJUT^$yt)<^FwX*tu);BhO;7)`tT|U3+4SC=ng1DQKcdmd=w^{V_ z8?~|!{zk$I&2bufY7P!Hg>JuH1cVqkM1wZWWonoyn{Z;WAc~sv(=(#V$Us23Gy0Ct)`=JxD}c`2bQqVmW6|IKn93#$p-v1bS! z0S8=l0;lWqC}-0ISQ@A|+D?j0UUhMo69JC8ZbID^f-gdVmkOBGWFA-M6Set7?&HO3 z{k8+gkALX7Lx)N)#k|}A05`_VZv5RBAJPBIfBo2Bl?~fIlf(I|Zm)aFthfEp505Y4 z{B!~fTXnI6W^eIMzd}_Rx7uW&q9FVM#m32?jtXt|k>MT;5B8y>t51$wS>2_<7M)E3 z6kOsPZRZHqbgxhn;VP2j0pw>iX%}OOTBapTF65EPS5Yd}P-^DU-5(`1AIIGUO7`p; z5?1knlQXz*ZJEtk>Mob_5wqQTr`6~G^}{DmzhpWUFX!>p!Q-2cJbmch>*?Yxsbc+0 zF1NQSN{<}$Z5H%&1jLrNLKRY`Vv@Ua7Iv!Fs>o2{-_h5JJMOv#Ihwdcp)5z|d=kUN zO+J=owMajGPS{Q`fW@K8WlT*>U}AO^Paht~)ro2J4UeF~;!#aqd?2{5n=-qY`!rir zMLMn0*4GoGYA)h~jerQ-I9CV+k$j-fz@l;I=*w+$OHln@Cq$sHm;B)(1g&R(9+U zm=SDcP)KYNU}KdcEu|x)*`$Ym?%FkY1w7P+x1rEyu{afj zf4*vDi(*%^gnY3gr!Et>$kXICA7A z<$Bxz05`_VcKp{r`9$e={`jvxb$ViPkJIh^Ijdq`EY}SEnbBEXTqvTazYjn1;7u6X z8Ap>gvaN21sp*5;X@SS1!|gF4LRCh#Tt}F8{y?W6zHkhR%O{-!Y!tMmHbsj+O;vtg ztx1;wEBzHJDza2@^2q~}y6Y`Nt{^fwR-$XG|1E89p;XO*+ZkD2c40Lo$G&Q#p(=Lk zJ|XFa)#11C3SN zO;%~uDz(g1z178?kNqT;5@`Zhb0Q<0DdbS9l&Q+*?tzBbH*?#b*`$MVIO=RRZ%|_4 zd)Lhb*5+ohOx4}-6JtU+lS^hk7h<33!9TLph zn2K9fFNu_M7HgEe_)eMC`Pc?}S7!i0w*z^x05`CaDe)-~yiPkhDDij3qME~QJ=oN= zHyb9?Z>+2=tzYlCcqzxZbLUzUQ&*0dt=6{`%C+|tD)mYBdAPcg$8@R=K9n<~Dc5Su z^pr!n%85gZg_#TjZ1l6$hK{-Qyx1?FU&vt1(Tf#FAL;~72?vNu zsGSNEovQr#^|I)G_-N6&2~F4uAaL?;(7Icp^*Wbeg-{1#z5UV|F}u8FOw6ntsp)tN z#hv#ZIDG8N(c>px@)&Ug0NfZa`|;(+p3{HhV}JYEiph2x#o4zjCeu;1RBC22Ib51d zQ-E+#GH^&Mn5E&wU{^mPo+vs)QK}J{{Nq5mX+edmHZ}(m?|7RXy3Hwsu`*SC^+p{* z3eJ&^2*RD+;x&*j)kL6(-RAhT!R2D|Ayo@bFKnJLELs?A8x|Zrzl!ryo2aU_G6knk z+wAuHX_Not!4sq3bD!=H`Z#d-Xzh`2KKrGuV(oQn75%*pi!<45TEzH-iar8yP7zqj zRqHTlj`=vAb@Z97?=~4?x>KS`o)i4Gz8)0HRpDoF;!V{JIkmA_Ez-8fhD1y*tq`zN zOGPnj@_X9`L@f7gwewoBi)tMMh`#O!LSBd5>E3lqA9nRdFx(YFSJY1cP!sYND-SZm zpBd&p7p+5DaZEO}$hDIpCNT^Ovb2^ent{b+abZ1Od|R=q{mAs(!t?WU^Z%xV!T#~M zc=1eW?DF}~7j%99Qm*#9Q|V?ZrM8W@#uf^w7*?7KE8Z~TMz6<=h}|Sd_aY8e(}c^z zR>Dl;)oH#CO=mDVlg6d#6{Kk$uqOqZ%9ZkYnFnSQ>6N@3&9j3dxBjsh5bKY)DRDE{ zFumYT>1HQTqk(+3h^1sf0Opa=^Qc-qDET`uG899Q{!GNJpv&8~4sSM^LR99YdIwef z`?_uLD|PW2$x-s>w#bU6V{$b$IlGbmiCk5E@M}*#`^s!4-hQKDM~&?^**Ba}Ly23$gi)-B zppWR%Q^jU;Q8ITpVWv%5t!t8C%9Xkhv*hPtv_00D*hf{V-QtCZlE2mF!Q^5JGs{Wj zij@{6nQz)0_8+gq^h-xipBX=L{P@4w!2aEgBcrElk3DznbJy0>!xQ<|Z!I*OoAzi7 zMGB1-nwPm`S;(2Mx@9*7Xp<-luBP&Yvs`jy?`ApOa$c@=Y837H|FL{;N5G9Ay7v}J z-W>@0+FlL;`lp@cTU&;-hw(9Uqh2Mzmxq(vPzea^hvDgznoh&TLQ=GDa+S6kAOlZ>5>|)+!TMHV>f+qye@tX@yDwgy zd~#-f;l}!SVVpg8p)oyk^`mZ|dqg#q-%mDdYOZBP(`Ck3uvlEWQy##NI&ECA{6kz@AIJ3)q>mHcIxCcz@ksAy2ovX ztP7IQ72zQO#|(cTt&eDLA42`x;0Z<0*VTc2BmD?^-3TLx8Uc!-;h3}$vpztv+LBHK zh2$k7qF4a3G1I3$Mt#* z-5JoA%bz=f4!lJI83?1Er3)3vi&br53EF2q>8da8`$HI!)|v|l2K7Am!P#w zHLKZj#4s6eJaqc>n~$CyKX`h4{KirL_b`r~I9t6qyY_La2!D7sQU7vLcU6b_;@E%F zkN`77Ys;{5`yUJZ#9}cxoo*D%s>lL!CEt{rVu;@VU_6NXAGi&7?(1!{!d*^bjx+Tm z>@-Uis8(uXUcSgC^S@Vn?|9La1=|KoDgc*CV@*ZyX5X10F4-|@?JoWC%ZpP0G&5u4j} zQ_*mJQa4%iyl>ee_rUpeEEF2z2Dy?bLZf}`v;pye1O40%NN|dk^df`~*d@@a7m&$h zpjn)V$3kd1L+}Ly@OoUL`02KD3n%0kg9&y4WL<(-Zx1?qx)JK?$M%sO*t=&t_V3+> zfp`FWV-}Q(6?vXZmzL${(=1nQF^O!rwWVRW+f5Hbpuprp#TG!WZpKETzMd|Yf3MnV z?SJ~vkuN{<{Gpc(_`3lBZj4v#IC1J!gv0Mee@_HKQwxe=7Qg!b zs1JP+A3CVg1$KJcj4OP4Fc$QI4)kfuzYR$@~>E( zwl`^J`&Tzqb0et&g_eR<0{Cp+#VVk@achM1F7tl(1f?U$&R28_yY1CrZ>dPGucN9G zxN}92Xc8Fcj>BZN6F_K*EODz*LxC##&72BzxEI-$6*Ze*yZ|=luA!D$hf-LBno0<7 z$mOEAK!wB-JxASQmuo5qfxTQ_MX_nZ*|{n@ySqdRcsW}xENtZdy3)XXUw`_*A3yW_ z(U*kT|F0Q00Kkp$iXX>LohlwVd*;K6)3MEFQ$9>JTyAk?1LvmJ+I`r4F_8#%67qN2 z&4`5~6uceqm>sa2tq6r&1QwhG8X98z$#;Z)sw6@oZqh1_Qw#7gf;rq zFBa0^SR!kSsg!`&>Z8XmPKzE7P;CX;0Nz?9pCy4G#%0 zVD-mBzJNx46T7=zFscQtu4hDxq(Sqq(g!;UVDRZ;rDcX{Qjj)S*)c2~9%aKuYp33z zs{fn$YV+=5z5a`jKlAL1Cf)mDj~f8s#`u95hmN117-9YpmGk@UHsw!Sjk>m)$YFYg z{i4k9czm#1U1G=S;OgG)f!oP0ZXWnO?AF!^pWBA6&JF?uF_=si6mvOB7-D5RZ?#+B zr&~;SpBO*=#Obr=uKWGnzm;+D&{6%-Z#?xxCSQJYx~Tq+&F)(0jz_U$xDOrCC}N#a zbaX^%&LRkSy<)(`GOjL{5%gyCQ+)$0^(eXq@g z9o-&ytSk)%cnHXQom3J~B3BD3tgWu2!*7SjAAqZ~8}M~tej^X%|FZWT@OD*I`s?&_ zZoluoUXu6Hlb0R{=?N)>6w-ju0wjc96+uBAyN-^dqce_m97Siw`HzFxP-)VYnvjI_ z@_N0!oZkLx?*|M>fQShp;jZ8BCimTY?!D)nz4uz*x7tP^FrqdF)5kYKYc+smW}!b- zfc|6-IEJBaevxrO7CESo@~~p@6sW7KghW*W0bvYm1e^l}(`!!~zv#^t&(0N!Yk&99 zBl{kE;=Rx8|7)ZS0LpS&$rDfi*?;=Q?VW$#@zTc_QJ#aW@CT^S^;AxUY_-?Cm_#lY+sb35T;T{Ncqu=*{cUlhvSmKm#P`ps+4TEZJy?B=q`26$DNZ+K#l* zW=&+P>O@5tNZx)yRiR2I`wpa`tt$)JqK5iF0tM$yP!{R_BEHYie*QZSr{LKR8$yjE zpsKzBL`jAt-N|iz>B2>}=WhBVDxoL;{LCpq5Xu0cET^qJ{larEB6Po*6U7yhto+<| zY>kxsXeFZHqjWz34w(YS`5=ih{S3(p&*kVYfbAmBeGUP_k{5Tq@U3mzUpS#HIm>eF z^5j#0%K!G^$8PUQj)Lo3lPgIQ#T zZGFXo=Q_2IcI2Ij&+Od&!yS9~zOQ#EJso8G_Gfc1?|JcC9>-3#J?=~UdW(g910@(J z*^nlszLpOy6#^_68G0Yw z!EpbpS7jYUdqB4JX-~Vby$ykz<-%YZ$J+F0jtD97RYZ=K0j6^$Q1f}v5eO6q`+;mb zLUthx@C?X2KosZBV734|_H{ygzXpeUiqJB46r>CvTwaD9orU7gLtP)qsOF^qcmG4* zKH2x<-AWk%l;r~<&%f}j|LnHsUvd~YR}}f_wUsr$3x{G(k?a76Kp-59s?kvR8!X4P zY~Qv0^Dph(_JP=_=Tw(JJn)eF%ilflP)91ezNcufHh^!Jki9q7Rzht>7y_aI1GFyO zftm_rb4~U5e+)*}R!~sjkUs69AXL}YLQ&VjcS-XevO!IWGC@V+!C^V52nXOmZyp}m z-tqL#_SEu2$>QZtZF^}?M^E=D;p#nIW$&Jy{reB>+Qf;{ESD931Ds5&K>Cq~7d!!~ zLlTTiknMmqs8kqeK*o<89}+xwd;X7P_)|7%1jxe*jH4Y1ha^Ma=6i zI=^g7Ym1+Fap!`kx9xa#&%S+S!~Z^#m-oK3EuILi<0N5qkH$V-1jPjeZe2wmj-)K= z1tRtTlWIi-1pq01ZCr`Jo{Q9M+hC7y>>fQUenvjt7HNV0W*k@99YX zzmjQAfAF!#Z-4ah#|BRJH6ITt1Awx8AmyQleqlfHds1{@KI53jpGIDTd-gGZlZRd(z`wl$jy6*crtNVj2&p!K%zjyzx--Y6# zMHcW^7$EM|T!6gcA$yl;Z-3VH5D40|%{OWHOL{kuehw3w0?^Twgu{axJbOe1M^xyJ zjXBk{I|i~3^`^7)e*f?vFL~(E$M>J?>pY%P1^{I#OIc1U`SUZ+`ai$#cb)(Ji{E`{ zFsIM$8Z7)TFH8Qk8IvGZSpl7cIoR9L2Nr1^OYG{D^`g+nau%G4m z>s8a5yJPqMpS`^IVCiIES6Pltw!QegyZ^whp9MpqS$S8y)nJtY(jTH|`tYX?4YIaR z_XSZA_zAuVojn;^>N^gB;`s67AX-%e{dvnj*jIe1Xu9VhR$TSyQ_pNa+1GUKy4u`@d z;jgPuEPgzvY9n^@iP(ZeYVHovuTG1&Evu4 zM7kjmY5y$*LU17EL4U~s&2gbEt-ahakl&gu7Uw_k$it66`qUHeo7TO@Tgm{SEM+On zX)nKh@X@EUMfJ+gzSNbS{n;JSSj28_s)g!A943w(3ANRUBExa_=%&3=Gu^KoInr@T zm2;Klt>xvHw|9kNfzN>;EHZrV2LU;#$D?r=J#IWSwTytO#^IndLBjgGlSSk2JeFJb zyT_jV@srOyd)kA)G5{z`S<3Q(kozBewDe#1{`Non2Qzb0S?l7#g12ulryB>ll0QqO z^Ru?^e0j@`-FtR-c6OBG{HK|`_}p{;zI{7)9X_(}y88NIGaMoDvy>5df(_R^emKiL z-d>cx<*@SD`yYJdi}(Hd{?j7C?^vV^0LoI9vV5TAiDzHXfBL__xbFu){n>hr3?VdQkJrmr7WkVEIVge{AjPsvLYr6)|X4FcHXR+Ge#Zn-)$W~ zdf4&)&J!VJ08o~)l%*_XIW1-G+_^LHzc+aS&YCm#?xLzMRt+nbsEGbcU0vOqj`>?; zRCB}d+?-#ulwQZ{?=9 z`yK=mi5b(U9+O*tV(a)2fFJxDPWwHRG5{z`S;|tDvYax~&`|p(wmx?3SmiHYJNMkx zLVs`9M{l}*>x9?8F3aL(%QQjNb?8avpj0e@z;V~S-EWQ^H+KHWQKQC(LZNr`6tuQZ zh_k*o`dF^zWJ?(Ul%*_XDN9-27czGIc($cwHm zk0h!;@#;%U&OTfAJ%205u@DN%An+{6q5!_@%=^eKS5GC!EMC00?$zTj-+Wc{imlh& zZM*)x!BFJ!3og9i=1o^@;@|XJuak;cEbJQU`eV4qVtE&{>vSiM8!Ln@nFm)B` zA{;~&;c9YBzEI#V-*j08d7dkj?(ls0VlWbAf;K1VUeXt8+VV^~#2>BZrrT z1$)-_zH;aCwdbv@emD2wy^=BjC`(z&@`03Vwq6lA+2>G}H%dGnld7vL-!j%-Uth(H zYKSLZ|5|l*H4_QRAHU}6tKN`!=lihScWuy2n~x_FjceDe37eMlunP=uj0a)C2i~(F zF1pa(UxFijg60g19191d)jwK?+_-k4Xl zMX`$5oo_hU_nTbT1>f_)ww;z>AUMj@^@K;-Jh40k1RiRmGVJdwfxvUXa-6W}?8SkS zW`0%)Dhm^pl@X2sKP0n|&14{#PJ!z<5R?_`Xy>+e#gdqjPkkDHkSUl97`cI(hA3Ql zJnqYhlGDoQjUF{}Ttj{B*PnXw@w0w)-+kP%d}d6wbmoXS`!AXP&T%LD{K`_6vYcG` z@NG9$Sw?Z0UdqgJbakd{X|q53>AM!)b>}UK58ZHO@aoGiIIVQz`$c3~X0~qLRQXQF z6^jKil}t%*_5Qe)rb!$tw7&i|!?3R>2HketYrgS~f1-MP)0J0BuIF(c8vjhL2&QcX zUB_kwo)1d`2MW(XUbmn-s{_YzV44=$`gwRL9A1OR%fPT-3IzkajR1lJ7s3*Q?+f6w z0^~|M4K=Lau)gB;=izaIbJCe&qC#|_F<`*RYCN7(W^CHH_I(=$IISl8lTSRgS1oD( zp~%V|@rqdEv3$lf^lQL#i0kKV%RtKV{*ndr7ar62{-vxuZ%qsz?^vUI(RnMEoWJs% zGv3W_l;!;;AHMAd?$e*Vv*GTK-8P@|%ft z426SJ-szaK{Gag1vN?_s-|mXbL>VntC{(KQ$9$qun^?@Q;B z*V3uv8ppDE7LB@utp32heUr%RB~@(@I2KB}4j#Uj%@wA&zW+smAv2zT6VLG*&Rw>=cJac+Z>rZtBVbw{{47^ADl4LaUkR8&L_Yg7}r954)WTy z>l$3!{-KLXow)mn(FSm9!y)C|Xe@$%GhllJlrRugIH(E>)EiJzH6((lUknQj(?+&0 z)S)hrNFyPh=Rh+Z1PdN$y2%)pbxt%C{L1Bg&|!T7ETjfw*ksyXoU z1F(JPA?VNOn=P#IdOt}MPIeDT4LzeHjx+F<+6E8h3hLP7 ze-&jpo-%LV9Okl%FMNYR{_a11CLULqe?t&=YbYSHF-3$*1)vH45qJT*26NDxEkQ;z zAsmz-9tnaVNW%-++!dMruG?<9=8BfgV9(6{?#?gYxcSnh8`rI-3|7si$K^Abn^iw0g&#Sg9cePo^H9|R6j2}vfxvLWS<#?;E;+uaSs;o$IEYTb_XD|H>7Q9a zno(O{y`^=+xG7ydsV6i%_i#XhjB3KR1B1}klZ8k$0%9OMDHf7m_}J~&zt&&i(#tL- zl?3moyTYMB!{2n{-$71W0I=)nN5lX9jZaPf$(Qa3r@N0V`t|c||Bw)EY}0DV>2@872brc-;kZu7DGGOO^A-dXdT{klMqIue`lT%SXdB+Xxe|+b28$N&MHD_PH`BJ)0 z=gyfG3Wa1E7P|eGt9X}G{u^@~ZJrX8P zo&*yojDu_{36`nRN1;%F&fa8L*VJ#=E;~chEl;R?Xc_iDB1eYCr&;?PO8|ur$!s1B zWcP&aGx#8e9f-k403otIh#U*V8|Mm$cE~d5piu^z@WoV_V4Xt?0$ymP6jkhdox`l{e*G;ywqibw7lMxkJni~m+w?re6Z?d9%fo9li z1~O_#zM$72-fwYTw|T+*1!b4+`$P)GV)W47J>MSa?)(($W7+~%5ttCqc+)>}+YM7B zmbs*+vf^I~C3Rgco0bnAIs!X)?tw#x+aM(R@cTbJ4!j`3l=00lZEO=XBqAWmGW7Kh zKzd*hG))5pijYhu&tqBUQ}{H8C{VL3+aSLsi(D1f9!GpeHSJ}9gWanXhf(F4_j|H}jLy``I%j}R}OUk7of z0818)gUXnE3o?Fz@f?ULJOpF`a;gn#u_#qVWQdU0M3%{T@ zxAyk*&wcUW;MY{KVoE4p*~@t5<-P|kjN_gc~hmRdIMjp(nzd;&d7Lv(@C;R$7$l`fUB$LUCYN;f7t}ROv zd(4||c)oWH8gl~v!9_GrfIv2v7Y`md80_uqueNP#i6}~6&bwUP@|eeP;g>AO?RPw{ z?};a$@K5A*mF3u_Kb0#SZ0kFaD;R`r(d}M~+4Q)OEN&6Ny2~>4&1ya~n(@6ex_SmE zqX1VxsK3vbRA}$)fyW+s91b1n0+I(%iAtI+pHs-BmuFJxx}&}SK@-n5zgJQXnK1XD zz<%dw$8eI;YOxZyJ_Dv{Lr>m@lIboZ?^RV*F-XWiy8S?s7+*-I@{r3GpeAY zxt0(ShM(KhBX#uU3OwUXXFO|GSXQP~%ibdmVdhN9)rHECKqD|BPijIrAoZSU-Gofh z;9@ejswpmggYm8P2tDQM5c^q2h$eHoeb{upa`&*4DE$M;sk&|eM-aPjgUmC>)W2c& ztog%T+gZj^9`Dn#ANtJmsmsyO4JMt+gj30MB_jLLf*=GX6r3ccC<^8Yyrib)(xsJib2<-}rqe7_nx!@3C&w z*$n8K3KHsX2ibU0RiQ7L0R(`QtqW-A!9iVt2HYiXeI$B4X%$qO;KxD7UM-jjvPL+!Ljulnwpy4(o3XB^5s0(E6$oe;gS`MFr+RMaQ~?^UO$p zrUbkDRp1$qHiPhqLBNSe$~<^PYBNS7p>TDQ5gLk1?3R}5@O>=9RYhgD&STkWw&%Rb z7)+f!x$NqDkEEp8wYu%o#g|nr;<>3kmd|;f_hD4v6si@JkJcQC^+ zAz#RgsB9V#9A1ypA`lLRW(uNw(Zy?4%s_t6`44u#~)baKl1oYnz2gC(>u?V^3AnZTb28VjHP!!@2j8&3KA2j|Z3}jR^F1XHd z4)i12-@ES+4D@t^=;64hQ)rwDln4}b8&X9TWJMw6Vxj6-aDGKBdQ~u>)RuDTDcOOZ zrE%T`J(pUg2+W(3{gQd3CzVq>agxp7v~Kwax?FJGs->5@wmDfxkk!`FkH*>L*Dn}* z*IypT@q8m$kBPAOO1T{}Y~r^EVhDvRSX8@=Ya(#>AQ%YZy-z}oVnIU~*}Ni9<4ssT zvH3Y1YhO~(n6nUM9O||gL=i+;0Zx?Y20%ypbI>!8f|ARE=F4#AlxApbr~wP@fMGh+ z_>*t|sV;~}EHuOvh=?93Cxn4ij^+!Ha}iO6Br2kJc=((cdn68MJ4{MW3P>m!1T;k* z&jEiw9EniY(K?~EwN%n>RXAq;y45SrI@*1kI&~`3(cXSzE>~nz-Rj43FJI1~p}!0jzN|>%*JA?Qlg}4xdeTMcOzV)* zNvuZz9S50YjgktHSWtjT%`xiqd2(kjSU6e7HOE1T&?QA@Z8nK>)Yp8uA;xrxl3 zv!Kbco%3<18Iq+NR76x7gt(FDo==fG2@5c|s5yp(P(X%!Neh&a8m0@S{hDUJjGukW zSls37&y8Gh;hIx(Exk`={krp5eDH@3cP;AZ>}~An>tB>i4$}OaNKhVQ*)Fm$H)2{= zqPMS~*Gd}XH4}Cp?1Z!<;XqdbX#;EcWY@O3I*kZ1THhrkQjb#)>Nn&!NrgC42o zkp&@4K)H%zNL=qE+a67(X)-oJib;J4?z$a-L73bVIY}=2x-n^nbdQ@p|O^E z9yE)%`H_v=CPZ+JJ2(e|$nJxI0B!ywNCHUs_rabN7)$`qCl1kY0CXc|&eU=UPtf(()i#tx7hJ*y2^+|nI5;+)42blhC-1?*J{3mP1tEb5B3INH z9P|lVNw39$oS|x3<@{-*X3w87JutE+G6dTX@LKW^8vlT8J9m-NKhp4n_Y6oxB&slYj-^n7lms!a zgau?-AR#3BS}I>c_8^QcTyXbrqo7+3-hYs?mg5+ z*qT#Nb>?ed_~@$P4T*aq;SiXX%c#1^_9e4$q-y|FAqI{drNEFCBrw%HGB}Oqdnn3K zg&;}mo<41|KYa8Uzh@u=`?l=_bD$p_(?n%$0+|U4GR&p3Cc`=NroyBNV$jX1$)sDZ-29FPY0>PN7udYQ_o{&R%7#?IhTTbk?z|INxAN?18fUtbhiTcxOp`-uE4;v9@ZVZCYDHglan$nHMm5SQONvhU=~X9=_IC z9YIj20BQUa6dB-LByP3^CBp<=#P<-;hkZ2Cjsb%MY5W|{3morcIs<)04_@wd;L#^v zU{Jpxcp&)!41{HldJrUz!S9w3)B)`3%)+6*4DtpCBs@oA_-(a_fPmx-__RwP$rT_? zhJuPjs}D#;0ckko6TRgPmHjz-X}OQ*IMCON*Q8Ak$6^~LS^4U#_pPzMW>j7^12X=0 z0s(4osu0`XHJEs-`@D7QmSggQ9!*X;0MOghI~HfgIM;Fi;QL;e%zG6g$4=Hs(8zTr z$&B|S*R)pQ;){@lZzAHai3-qE6NcJ`dWeRIBJgNuDxe0$#X)kRmy~bl78Gz|5#wMX zdh5%$X#7nWR;z$4($7~7^!dw#R7O`Cv_1xl0#%?e}~8vCP&pLL-zE)(}2GD0g{GTj6M#lX?dYuTa& z7p7HzYo=&^amj*tqVj|OSZkA#m>Pdj54^NeQukSYzub1iR(r4bjd zg2tY)G#KB~0Fzp(ARbbnrmhiuWPO^ZLtU6BiEYRT4b&HxF`sRG}VkvKF~#livCvHn?8^-mgF>4YV8y@$f!SiIwPXliK zgm5#N9GJ+e_Z{v5-LSziilCzU=am42Z~;frc!$v#7jRBw5X3iB1!;2N=!66nf&(4h z{h+Wz9^gVg2W^LsKuiHZTmgv?8gJ6~AHhgA>&UC zJlSxFc$;vZWwIqSL#S);IngyX8vn|OKysB(#rVv^1@jOhcs!mWO*GYQo2aoBqA0vZ zFdQ|rR5#2Y6ip8f^q0WrMHpEVeCDfnUwhQP6Y+R_#=9NJQw{(W)Y1_pprj;GWS)BJ zX-5o3gSO*mNu8(d0+%hQQ!*t(BAuQ{QwIhyePvXE=20!s+*}Wl7`e|wu3sO9;=p9L zD1tZ{vTGC$u<7_{7=SWF7oT_Z7ioz|bxa~)K|nx%PGdZ{rBd;~hK6LNh6+ev{qdP> z`Yl=F2Tep#e#@pGf2~ZNIx~hW{vI6T1dB$RArG3s$wC0pJp#RDU>Bbp)H&wK=MUHA z3mR>nF>mht*|-@0&9dx}`AUP4rkF_je3N=%kXajS%N%m&!4Nx);mWK><1gUR)j=LK zT;%DZ!8k4>O`SH4CVZ`0zAU_G_F11>Hh)gthy?TPnnXmX3UMnNqWt;C>za=ytClZb zc_P36WXbiLFP5&k{KAHh-Mo46OX>aKT*4_6V^c zHQ{9L5Lacj=V353lhN=kd84y+kYC}l) zfshSSRwxU6sHv`cxgv@!H2(;dSUez%((~!1j^n&m&TN*{u_DfC0#KqdqDh71t3kNC zKmVq#r9|p#tKYS*J>>wvpPzmTNq{bEn$|dL`jjT$RL_zfGr%{OhE&j z8#s_QW75qP!P?Vkjmt4Z;UBVkaJ3>ve$dAe;0n~=1bO0 z{Nqg*Plhktes*#GwAw!&k86G_i80p2H>{rjq4i5Ht!zwS4hd7*UU`5XUJ1w+0Su`EDr*~QSmF6+o`qy81G_qlRL_u1agt9m*w+IUVTo<5 zinQp3+frxs%;ESL$0;%JN z9s7IWP{$y6%uvNoH6Cx+6kI?eAV6)Hhw=4cK%)$M4=16$w@AH$N!5q$yhW=A5U_xV zgWb_H2w}7{Rh2Y11g(aH<1RvXx=7N9XiotHS${|tpd!G7M8cFr>3BmG4iyS_zh#(P zeAnT~xg`*QASmMVnIuE13E-8^fHnxoX@Mpm1~?yzssn8)7mf^SZ_=rkUA$iL)ZDw? z()yGG02eHtIWow5XS0@ivjFZU-uLEo4CE`x){Z1=6W1Y(tqtM=_2@uTOOFiC@pQQ-%IASfY-hCx!7P)fOxlPxuUB3LI7p(O7gR ztAsv8&PnhbCjb_&u%7?apN{O7kv0wt<_Gq7n*)FIjeHw1<=DeG9m6ckp3wf_$3t$o zZtL((mt6RUc=y)JHdNnm_2r|l-Fn50s3Kn-2`Fd&QlS5-3HM>EF*qy&tBKzsyvAB_UeE7kcQ>HlxQg+iR}kM5j6VUl(Z4ctjI{8qX7t#xW33zBFNz*g<~<0 zEPGn4#W27Zh%Bjk?H_4H_6;n7^5l{39qN*g<~in8ORx6Sgw!yNcj3UzE*YX zEjNwDIVzE|R2#o>PrnI$1qUL;W$Alw${!g>=GXOhcP{wwZ8uGLH)DB90e}Uwp z%tW)0om9vdW}t$M z3IMXpb0``^M5oZMUzviAAc1);8H}`oWJngMC=hoSq*6IF2skluQh5j_a)=OVj)7&< z7?9`K0g?6RbDq5lXKvlnxznef+`IMON-o)dOS)+Mpsl@U0p1T2A@amxq40${v(Dn@ z&z-}qT)E<}21P3t%_Fh*dN~sRl&V`f*4Cl9f~T3=r1Yl>LBYrh2K0Ou4t8dfyL!z} za;Q+Ev2ef#riUy*5{nZEQb)ihk+`5{L0YvSYvRJ>L|O(YN)obvfmD1zx?tdVdej(@ zMX1{@%_I&5<@uGd@I4o;UwtFT`1?3iSh}h{cd+07pL_4$-SfaxUB75gA$oWha^31> zw_SP8!XK_(F#Bz{Deg~Z%(TIL5$Epyck?^%skrWlzW#wXD6E>Qih0#htfTz8|%i5 z9(mL@y6>edU$I)*cd+C8s7EhrZfqR!m+x=9@WR)&={7A>c<{MycxgxbP?o-+K&eoG z=bza{Dy^ad$IWYRYiAGaKm29ly)Bt8t<`54k9uo}(KPg-I)36_Ct%#Led;+Zus+1lu*)Q5i*tb_5fXAnLVLl0OmjnsfhR-&e}cyE$)-Mp>7zY?lpCUXU}SD6 zG7IBJRl;z@d0{m4oNdAcMTZ4rkt7Np#6k*?ox&IaK_&4H6L2C9m6Wr@Wv>V{u{?{w zfOZu``$57ARdEGU0|N+NRgg&|B0OKzmuP=v80hU`v{I36Y-+e;SYzFHB!O!spp=wC zdJISP4=H6;0>L+o|Jw7`#B%A(f0P_H;JWtr-pzQP5&&TKjH$JY=FEz5o_mSQD=SSg z9@0T3hCAL669kvuAfMK9lNxaq2Z%VJHOmLnBS;UG{$K`CsE^9u0oq?eAg*1HGKjPe zP{4_hE9x|iKw3I-IEju76v5DnkVVFyFIf=5$zd2ebtjVWz>quIqwKLV!158n-%=Y> zu8}!=J(9!)XU~~Yw`kV%i6{I1ooG4dtXX4ox;1a0r>8Lzlr}~K$|{7kbAzG4bvS9i z9SVofz*#)&XrEoXa@pdG)+~S1h8K&^nmIhixw~vb`w_CZn{bR}1Hl#=K?pKB)CF8n zGbz}%y9Zf;LEQMA&%U%{5aFB2aZJf~-6~Q#L>7O+aFKbHkQsW=fD2KPWi&D(WrZY; zConEe24!(Urx7@&avFXHK|@6tkYs|4ATgaO;38E=qhk72T|@1?BtZ}r6?4z89^RM9 zYQ5Qv`n&)A^+WEvxqq6beq=D6UE4d5I%-^X@%%+qXU{+TX29L-}s{Ejj*)BN>prT*Vfks5yXdB*Bq`Ea_k@~M;#5SkA{Cj>jW6yG6Gmp zM#XQ_vaXRMnvr<3P*o9y0l5Ps*#P*M=zmFfut=B28snx|S!7 ztiriAgL>``^%S8iZBTGWT)ZS#h&qUXR#H+? zDybt`-=m3LAHMmjmTR|M{MP;98K`M&Y&@xZkyKSx@tIudTj{(ujFV(EhwOiEJb(Qg zT5*pU*+LU=H>_RLh}XZmr{Ka*AMJ#Ujs_le^P^AhfgSt%pis=ud+FJyC+**Vhx2qK4x+lvz(e8p#17;5{A=&~+^P4k)e;m4XAqgd&7F zkA5d%*lmLvs(iA!4-XIS>IM}FAMqHIyfKc%;#uO&#iffjWWtzwXlaT=LxhJ01)w@2 zz!OJu@WK%tY7ubciYfv>oq7s$9*1KqL6wAa3_)a7G>CS>#Pbp$7*rq=iqoe~ zJL-GO7R?zcbM9o$)i0?~_~kgNOMM(oV(bZ;>o1xxP$KR>;>u+yd#8!wB=kb#C_vE` zF#=?v*G3hJx=IKJQkq9MYasC)@?JmbBMlP7VsY9svS|1;tGF5s0mn-SEI6uQNam;i zN;w({Gw5iTNV4HzHZOXvH98`~7b*hc`DlPHLsmIwC+@%Z=FEn9HTf!_Qh{r&0qriEuHf9<{d>J68CDabhu zobTe4k>1cIb@775Q&*n5at+yY~;3C*rk{`t`F~M?f}}f|p<33uY+~1)NV& z(f#a~?zn13roZ>o*<}A4ng-pt_0oACzHuux`hv(eHVvzv_Fj(Tf&~lSz&K}4ZM_=7 z>czyXR#0_N3z-RH8e;!Vw!?k(7-ZEnQB_roGaOT|cF=f{<~oEO54F~1VC?W}7}XF1 z(N!7Rp%1~8V;Q9HJ=yV(R^JeYPFOHWgd+wph%d$!j&}DW%$;}?@|gnoNXP^PcyR=F zb!fCnq9IyVn#3DNbHjM3ueBVKH-I=A=t>;ln2hUQk-@<8BC&lDnf~YzHE`yX5wLr2 zJN)+Hoz!DEx-JO5Z9)O(T3gNolSch{@Hq)>4+CWLp93i!$X+rm>Kf8Vq8bfO0znDc zS(5BD*wLFt!(Rh)r;dPqdk@hz#KYd5pB-D5-&9=eT)bA#(8J9e)_K+Z$0Yo%^MrrC_2RzuYYaHyfa2DJZpMIDVsi9WSG&w3bz(*ZZSgP7V2^* z{hv*;%tf3G94QAIiud3|A#py`>p18PkO@c-R#XuI0R(OGQ`8LV2BKaipmAoBszKdB ztP}(}S~(O?!U5ggt&!fsP#a93Dh;A5&oRh&U<=J}81a zuTtjz4%y4%Qn9!aPpx^`ys2}KcKw&Fn!B=f#IRGgkx(!e|9w%n50EfQs%Z75@}*&< z1ct`adIp=iI=ib;NiV6as{Rq?9GVfe4LNVd{ztDJ-NsRJ;~#=M!@c<69gl)`%#dM9{W?xb%qPAnskV zQMg5c9t(ZQ3UUP%K^{=oFiFT6%&9c7D;|rItXkkh6(9y9w38QU1QHW0C>8Rs51C=_ zV1~{E%P>Y%1o->oLHRmF;y>3-=WX{pKbrLQ5AF&h5}^%fTz@xtZONiV!{*PMH^y@8 z`4tt3pN0a;`u_fb2p&7tbG@^V!;qa&QA@>!uFm%LsewM)8{vEZaQBjYdhm%od-pYw zQo3+73Qxbdmm2g)JOSZY1ym&xB-0r($w5f=_d-`!2LgjZ7&&@0oH=(6m}&_Q9%@Gu zEe6`&Ozp{f{FOCq&tv^M`7~x8IVI|Pqup{U{nbNXxG34 zyIzK;A9w_8Bm%?h5-@f07}$HD0|xv0X#AI~g|6;yXg_=il6Xx8{5%CBg(7g4wmz3k z{{sTZoUYmWl^3pAPwv?@S8S-ld-jQ3dT{txKYu5h9D9+aY3pyfcGG+21z5OX{u}gF zDyxo?JzZfO;(yhlwTg%2z~KCJI!!y=t=n+Xm4>F>@yWaI98IoSp=*zpzmX6rt=T74 zHN^8;LgGfkIF?$30Gigg%u%l`@v;)aM*4Kw_&y<6wma0hlY|0DZa{!AetyVBuA!kM zOq=u%pvl4f(0FPHBvb?{gbb3rF9Qi;R}tq*AdKTjTlhj`6^9EaER1Q5&WE2N=2Zq+D`v)~oi6*gw(L{_2*&?+E~~EK@`SFmu`B1#ik$ zoIbuKII>#$Tv%i%dYLhKeBHFk6BXC8mdmWaC@gZDEJOdS1HxDjjeuQDqvF*_@+ef| z4Ocw@r;kY8J*3Qw)P<5P(wyc(79XV5%dmq8)G zK>}@u#5v&r=bev;^VX;Wmw*e4*RNd8o-=p)oL8@Z_SA`i8O_mu2uaM9@5NZ02>I0m zzc*ah&5Ildy5mm3=T*c-N^>veS?O0 zSxjMXCq|XzhA^)rcajES#HB|1eajM0_k%ys(F+-5bA(wEm$4EIKr|YmiCx6q7({kp zAzSP0%iw&nXl$Ceo{MNeh)Xw>&T&4&#Km}mt!Qb!VdJ`$$Gkfs5mAzR3>_668U$nn zFVCAhzZs4EheV$LaiSvniR3_k)BZ#4@?bj08)%L4CH>RWrcHm}gzj#*ZYy{31?%6C z?1}h0!nu|Z;2GNS?a<-&L~=02CzEMZaDAZl9s&{`Nasq>gJY0LR6u1_B~8>N?o$)* zN87=}uyfmX=SL;>S5(V#>bAh8JU zE8sp$LOYrV5-Rh`iV6^vAlS^%+9l;$EzJp-w{Qy0J0Oi)da@>D5zrE&9ti~@R^J3K zA83P=!9u#ALBS&R+F4N3eF!Mf$g?7K*-mJbV9lH;%$ih#EWd!tPou79VlWPM_v3F0 zP;z{lo5K1wJn*Mouzg<#ac4(cnulMj74vu8bk)V{9m@@*Q`t__u!|$A<*Q~*Zr;Ke z`71qBzy6kMx4b9guc~iMX(`!$rhxj8l zg6UG@P8>WW9K?zO?b@i}d>PhMjpNACJ|4t7K{_>hLsMXAYhDC#?P;Cb1d^z!`vTp% zx1&1+fB54L*xQzcVGWgJr$=ybUYIx!Luk}XHVG#WwI2%xkbEE!sA4n_9JE2EM$3jt zt^!Z;67YJ8C4+{Z2TM37oT2vOIh^O|67e(*g*V8CN5s|$t_9WcFsUGc>HtGFw1RvF zA9$`={2m_X4JSynl`#R7y#u+BQ7lZKHER}^E}&+_?;`giE|VMy3tn!^{M8$lo$!hD zANTxlgd~W!AlgAP(r?&OvnsE@Pw2m4+IV^M2@Sd*V!C&ZF`gigLZQfw6 zSV5Y&_lbIj8oI`C|W(#TYw||Dxx?Zs-vWK&8* z71?Y6jq9rFs_LJha`+lD;FjM!{Dk-FcP(AESa2PiB-?Y67@UG&u*SAb#8Wn1Wa+$4 zm4b@KoiCAit4WfDVzypbM(S`KO=bF%Iff*6k_0TDjxS3~h(Z!E7DF3;)K^C7MlcGA zx8Zy#7FEhDiEB3^prTNdWb+}~B#Iay(khy=0>`B#rUX{>rVF(9I4dhG8lP*cqk*oQ zwp{e33pT8JQ#}9Xt(PU%Et%c?`qwyFvB*C4qAY?G4v&k4Lw}A$BA4?VH*wG2!_@~4 zckzXS%Gfm76t73sr<4lXaqF@0w@Ng?*Va^o)=!%}iO$`B`oc#gyv=7gmKj9=-TJw^ zZdNQ--qDQ=sFcssJ=@#*bI7iX5VexXD)Z2lccHDT4~n@06iP$QRfwWTiU>PjcoCj{ z`Z?%m@5FVPgiq!CH!PY~2Z@+Old(u*==k~o%xtZPS<^;99F;0Z;>olE2!Xny zKp+@G0~Cf>I0(b*t6*4tHO(C$&0YTV+#$I4zaD_jv;qC914(?4bRp6{RzX7~^FAEd zy&LpG4$ufg2!B7yBO6rHU?4yw28rm35E==RJ$~MT257Dc0Be-MH;a@dnrL)LVxn&w z)a6aKq@6W=5=@;mW^O#BY)lXIZ&r1~%cax*YU;)~oMyVD1nvDhci!B2hLP z3e+OAA|Zh=@#sb*)Wsqc@@N-2J0YPU2n>?sJfD6ySy#jhLALs(Jzlf|1zsdsurVg1 z{zfoo^3ZLBsHceR3>ik9P4Z6mcML$%WT2lk_OVUiToZw%fOAKHrl1GaoJlK2h+ZKj z=w!Zfuk0^E$O(%!i4Y9}9EOh=hVxINKr|MTpo+w$k-+-AKpPs-xfzgYK9a4KXz8ly zad5CV3u#;*#cT$4@9&_^jS^vb)QvY?|5Mbr5fZi}^R6?c!;t|MBGC{90L#xk_n5d^s2=D!qt!oVAQD76UKtk2!XMQ5@RPx+Mk^q4Qe5=o)zKhh zBXg^W;6NfOC1rqQ(>Ga>aS#F^2Exe3JUEmgER>W1dcN;yJ#k*w8lybTM(s`+kW)#= zDxM~-1SM%``$)nxeOd#L6FLq>Q=r|gf;i}k#0Z@4jjsp@|B43aT-&f0>8e&UvL<@< zbGy44$96`uw)&Q(9KZSH&p-O(AASFw_m4gaf(VlXq(#E)fYr#TP5@SYZG?C`~}x)s&%W+32Vt`)nZ`<(kBuvBw!-^8E)upFEdbG+UGnl7|eO1*} zFs3mpX2^gRA(3iclAL=cy!J4KQZk%UHojxS06B%lVBVFnF9VT*=opaC1e z#~=w0Nea*0RUZxPzV)h$Zn|dEdU|ckm!8dJ277PPP3QXy<}Z9Lw#wUDJ&8eUr1C&f zE=RBw+kfy#WbeVw5UD99WlaFDc$`?4wy&}rciqIv6WjjUo{y%f?0WVozRSWNPs_LA|IozB&+`gTsdwP08j|>x-z>sEe zkPIR|0a1sb&reCp<2{8ZhKD}Thl;59i2{m}VF;5tP3Ls`=2SW7I(5$5Yo9xOpP~q3 z4_d$9rJ38e>(=>)z1P}dZK}2)6l2iLmMA-1yXv~y=rN;f(*!g5o1e3X4Yg;p0MEll z(kVc|?RLvPLPi>+pj|Fa00F@J279He>84C>gL)fM1ij6HFj6tjPCVr3(SUAHOt%<- zbZ9uF(bQmsAlkyVOMu3u<+2!hL?n_SmuS*QbW5C)j@~1-VTkrvqD`V9k3?(BOIkLU z^_0sc2ZPH4yzdTwFto$z(gvKGGrYEv89aS%j==c%S%SPC-*@)KwR+|ArJDxg(eFxJ z>zd|;Dx$4?XNsd^2PhE$UMIhV-}-2W>jx6?=n-5LbXlNy7Xz5k&R6f4yw^Sn^Fxy# zbsLzu(&jH8#B=yA6nHP!%1oe`9V0#GaoCLC$!m-=V{UeB=%~G| zsm6zcL2xkXDrH1a|t<)1&nA2X3J)DNO{1zHvD(zaR4f z2Hic{l3=2#I3(vDWfGY3IVgzMLMDPB7XkjckZ%7s|CP)Vh%lg-zkHR3<6g9iOB-U2 zL6B>FHzl_3VLRlO=ru8@iVqHu%LG6%39}PW%&JkB??H>w8#y|4`XZHzrM+x}W({ZGn*Z@PYeG+*tW z!NCRy2nPHw4XWM$S*X-T^R*5r<)TmoTwi5@-*kfO7~j+Jl-D zm}0unqC&MNIi}$R1DS5a8+566Bs|VvA0LdrtXOMn(V!b57@AXaxA*Pa_dSOF(|`Tv zZ&g+GHg*E$KmWV`46ha&Xnt@N(GV17IVA5V>`-*HvBs!~b~)@k9ng8T*3>sLh2Yt9 z7emcX>la`8f;;57o2~ZS8QdLk>z*MP(ps}Q3}rAdV1(pkhz~Dv52HP^RsL@$O%8|X zn=PM2DDHml|LzxXb@z{==#{YYw{5;~nJMCTUQ^+v1ff|12@ zqAwb?45DLTA?oBr_3(>YjoyL($UT4fr(bc)Prdvlw|wjIQ!A~mk$w96M^B!8xzVW) z?b|-gcc(UX{_OeJox5t5nI?Z<5bx&<GD!7ziZ-Ed%> zriV3p&)e^$-~Qd7r4PODA-enU2xYScDi`yT7IHZ89tJIyEc)6LY06}pG8N8X5O>2+ zn6|QaXf&Ht;_rcEjBlByy$26bhMnE)YF-MDBe5ur4ktb3QZZ32WIoKi=dPH~^&xh= zada}Flb}$n40T(b@9CPk)~|zyZrYtL)Zg0a7{C9^|MeY0JlM&r%`Rk zC#;L_=lhMmom)4%-BE{W{1zN7ST+8v7%G+7Mw`4w#r1V@tkB!{vu%K24pS@IJ{FfT z-%m04F7rAKGC|wFEhSFerP_C(gQ86aPJO$m^sIl2a$I-@mjzZx8Mfkp> z=rGF2WFYLOnA@Hm5DV!1-@4ca%nAs&HPj;tp*(hBnFfbq)YrU}W|TR9*dd$U&=i;7;#%WN5kyodzrUK zfB{tQwXb_)^V#07XYxWj3s#`c&j|y8HAYqJmH{7a8A}NA6%{|ABhgw=WRzx)QGbYm zQZ`q(x!vl#0Pn>Y(x_GI4;8A79bjP&r$cAELv8jS2(^Q`jmG_K7?PBXd+5?)mX4lX zp&R#((yQ;?E&1Sdx*%; zVcb9srrjQhq9*(|7>cAi_|kRJiNegb)a;3Fgt;jiRbZBT_R=D)t!|L7ncoo)`F?Br zX!zp?w+2JkZ;R4h2M2bbl3AXYtVXuo7b#j4^R$@p+o4uPj1Sf_6%Wyt(Kt;G1mv^u zcZV%YdB@=!4*iGqh1V<9alWr(BSR_1MOOG1-}36~-}lZpeze{+?tk>`vWVZTtYw+? zH^+}0+Vf*wX1dbD&-Vo~sv}XIDzzpV5L&UClJ^DEGMM$RWNUmMI_v~mQZ`d+WpYUwOr`XVxGZR!(gC~)Qw5QV75 zE?I~|q7I3V%;k(hfA*ziI=@in_cLjIHA_nXG`zPsW5{j$m~@N}57C{^y_KGG>tTw8 z{SrP7v*WpLW`c^vB9$wpNV%AM(Mk@cQ(>^vM(2~oV0z?rFMG)|)_?y&UJSzjcdgy` zI(>oN^>XQ_@I6G;!1_Hdm#}Il@0lBUKK}7awSF(w5c&942m1UibnlDPRW}Rxq-|P2 zv__D+s}_o)%=&ct1ch>l#8r8JbOsHjQkm}=gS{@veN*}O&TJhOVW&ir_Y4D^*mRhP67ajW~)o-T#beX5@H7cJWBN@E%6zHDc1pM!~ig<_37Y*SEZ+C+h%$4dd-u023OUZ0;GpARjgGDVP= z=jeKuHhO-FPE6B_Ui&JVWSfX#ML2(b%`Y;^?d&S4jgr(cpd&3H=}L!mtE^QzG%^sE zXhp5jQI=QNy&LNr5r``}#IExDV3vhpC_nR-w>-dW{HG7U{TE*LgY5IO3ILva^Yw#Y z`^UfA^}m1jx1W0Ydk6RIoC@mB{Vv74pKsWtS9AW!IbLw+?jwgT9}43gt&UXs$K5?L zi;L8#RcPIakQSdJe;_E~yRzYC7U3kb(;&Bn%jH})9G-VvUCaH+?D9qx@Ad8D%cnm7 z^-G^Ub9YvF)u~^!dUygZpW;3 z&Hp#o=pCwb%-7_a{f92DRsW>c?5se(5^X-nQlaRj(J`g9OjGRI0u&7aoB#>h)uX6T z?b76s52Z9{k#V8$2>GaJ!1GFGlDFJ-^Yxx&z;y#Y|K%&Q4y$hsF?)E^v$!bj)slPmO)6F03 zwA(vct+s;=gn}Mfg#9gh>Yo(OpqF^9P^V+(mg(|hj>bl!6byMOS8URm^Q)B3*F*}` zU}KLVFvu@r1Sy+gDq z_RS=}@z-B`L_((*^4)vpG9?;IhVgi|a3vJPh~)eOC?JXwWS|rexJc=>B-|MK5Sp^`1^J{f`BgI_y zuho7}L01jdA7BdMQ-s)2B)Y!3PWg=uGHPY&b=tO6Gg3#q7HIiIpI@t9D{JgjEq3IA2wA}dnYvHH(qtVqv)$8gd=e)^kq{M-f|{mvD7?2~^%-}=YLX?~TRvQwiL+YIErC7;b_ z2u^8!VO5-2ZBw1J#Ex5(O8Go7S!BS&fTo(K%-XVm&w)sQ0YsC}WuDGmS*Bwr&y&aF zrc@$Ml}eo*VuR`p6d{{}kTq3(L#x$U-ya!Zo5|dd*_{Z zgj7VH~@Zl&C%3(Ezm&q102L|}CzPb*jE$fMfPzOc_t)kZ^XJ^;7U zK{0B@2A<`bJVTq$-TY#PL0^v=)jDM}d79(@_J@LWrKVD$=b(Jc6cMp_SffNpr?Sa5 zT+$Fu`AkR^w9t;;H>Cam_U`DT-=RdrP1~m@DI80Xr{5%hw?uvhQxU%B9*HnIWPSVS z0O0l0H^~B81KTY=3;f)0{!XPYX^h}z2L*p@t=H+izFMgRdi)k`N(+C#*Ru)$6va_l zxp0zl+1%2t2lo!$cJqz8uJ(S(>v0W*b?ZYxO?j))X$=(WZM&;p#wwEZW%mp_4A5EbZ4WooO_-`eOJ|D>1J;nMgxDdlL~(I1Fv)fe0` zLu1SwQFzf(yfipGKzch*)%97nHd#6_8RQM}vBOZP+J-?_BGGsxI<$L_aR607s=s=E zWB&a7YW?W>h4t9vo}XE&Sud)#%@eTPd#7TQWE78R1;V+iI1GzA%_dct5rUD(9BJK4 z?qHZMvhxbF=R;l`j56eLuu*jV@<=rJD!*HQtzupeN7es8H|8_n`Tpv*CZ5b<3z=zOYVB6Y0%+z;`O0~UwX)&etFrjNDvO6(ZNncQRHG`YS~lgR_Upu7wM_*oS?Ips`owo z`UijPCC|I#?Q=_OZwP5W-mSi$0LwClLvjxeUL-rN-90DrcR!1Um_Z%9gID<8ZVgoPq=Y#32 zu2<=?vl#~cqBC!+q>+qxko-7 z=eoV*iVX06Yckm}>er?4lXsPEfP?L>gT8qwM_)a$#6Zj??T2t~sJjp?0kWhv0DFa^ zJrofl@{Eo$ht;EMy&?5P09+A1Z^>kWG``=E8%G^OkL}n31H0MtS42C&aQmp(RVmE` zwC(UxA|4bVGcg*JiGmng4BLcSiHNIDV?l*-%U7vc%u+dD6cCkA+4-AI^0_QZ_}F%` zpX>Cb9T=xafD?!S>ZWj(ph}DTfzRA&Hsm~^U{Qdz6ro!Qze|0-OMsOrwMReumN$Lh zbkVa40JiVm+w;0ziZ>WGRjc<_-C?F&4##~~r~Svpa&@%TY$#}7KtYf0vTQA7J|O6m z1rtJu4agZeCxnXk?;NA?h)SK}DjW3%jV8Q&STrdBEHhH?sk-Iyh5rfa;YIZ8Zr8!< zj|}{Ba>VnzwUr9FjV5V~!mVbGQC5ute2@}hudq3<%%L{rcNc5uIOs)u1CHO&-St<$ z;#K#3S81u$Y>xXwv7H{D--DFJddaYSK6VTmA1Y=UPqWrCqeQgMlsmTA5Q+`MezzEg zEVEWrmR1=g9NCp%M+1#sV^19)`p&^fA zezn_f-%m0u+3vysdxF76UL63)s+Gv>w)pQhouxQ8Ck^nS-#r=V-{w}mwIu%LaHYKWqw-_ice9PQTlj-_n}pzQ?qGW zDYOWh0K0*@{{53XAQFaQ*Cjyldqi&ro zg2o0KZKO43ET}d{ngX%VIHP(*diXX=?=#LohmBjVT%o`H(s}yymoMQA+|_NjzPz}& z`~k)%bYMJ2qhSvXvopfkgG*DTwuH}2yC&1dP+5-j5+W6*{3fI_jKlrD_1VLRZ~mcL z5C1){YNfKn<5XUgO*0s8)R-OSZ_Q@1$y%+>#V3c+b`y_e-5Qo|#rPrZWVS^ti!{?;QhNmkA z8^%8ru)o6mJ7ldjH%)EdB4T1|c?O#dBwHq*b#I6|ic1vgtfvbG+4_X}+(T2|yJo1~tkc#dsR z^7Q+5Bo5~fM9snLSz-r{o_!Q&!>%G)0Udt_J2XV#d<={s{Dgu?NNyiLeqIz;*Vl8j zGQUVs2Gr$^RVoznd^S1^>?=ZH01L1`0@8RbKs3&ry2AUiLM_xa)LV2k@1*l3g&G#0gKnMC4h&rs~D^dW7sfTT(JYe(qcl&gwqy^h- ztB~RxWjg_l3j7Re=g?j#6BFAlYSgP@H;rCPfAgDvI>ugT`{Bcf|5fdXXB7Y}&d&!O zPUcUlGR`|Xto1vOtNq@v(Q9gZM?-QUX0t77u>*xNU&Lz*d58m%o_v=qGO3yr=rx*M z+P8ZPjio|Tbb)?P%;nZKJBOqw?Curi!99ESiz3@cKKiEN9W%j0sX^C&sn>H4*D7uO z%4(TPd_aQKl>Tw_BO@${0v0$e^p$7}V{?-*w3)35u=QmMyArmRMM+TDqe|D~2*kEV~rQWau~=I)%%3B!LhlL^u1#89t?4(R-uuoTNJ%8 z2~Jo0;`mTY0OMon9#0ktv|jB~&*hiBWvj;E zOcz!T1m9o>R74)PPm&MLVh*`Y+sGXVP&68n_AelBKRXwoagPV0Pc9h>!gifpnneSt z2(y$RGsqO?a!Ys)Ux@ZtY|Nw{f={$z+A3(cs3*_Q)8i*DX>PUe+BXHkAs4eoCv8i5 z88m59BcVtnKxIdXR)~i55+gL|*aVj1Qxu3(wQe)pt?%4)@VdKy6nnm~oc645WV8@7 zFpSoP(az-%9$lvM7cbJ%;sUKLFH$+XO7&8iw(s0cciwZiXmn@{ipDT_ot?(apogZ1 z`~r9!swG8q7W%l+hY#VG%~S~NK!Xi;sjJX>og@#-mW~`u91yODgNd;NnH(RaGT&>k z`4gwkGZ?9o&gZ1T_wm@#lXULMC#X}(O0lM^RU)Nbr9!btSJ#(%*7 z6J3wRm*zSBX?er*84FR7= zVVn2De!urnz0sI%ce|^6F8wuXM2vZfKjg^?SJ_B?I{4({LQ`)q_{v*8Z=nO-|3H~J>@JL#AIa8(r?;{2dq2{E*jQ+xWmY%q@A+3L- z!wD)w?}N_;QqCi(81>u%YRAUNV#1;I>b#dNsdsvOL!qW4M8~ph*FtFPbI`XJ%CyR) zWTDuhM38|5uc^sowP7I&?4|Ls7$MREdHVnJt$F&!r5vp``jlbf2`O@1~JqwY|RP0O0xq z2c};0%GcNx-IeUyTdiI{;&!~p(eJ%-VLdwth6iwecDYDk&PbXf+=>uamLDDh8`63K z)F#u|&?+yX43b|&fd?ClQ|A|$fl7yjuu>G1 z;6S2qWQ73%989f)P_OA^kn&sX4D?t)f5+^l^Re4+d(M9vt$@dmJ<)c0JiqDm1;5ql zv?%U2rQPXiErz+GL93;XRIfVt0;!wrfFKYdT@hgo-Q%Xc+mjN)N`<{L=5u&3zOhv3 zJ&tq%3=z^nNF_jpYakY&QAX=cKJ=^kmVm`*Bt+XsW29Sc`u2%gI(K!Q5UK=&gOCPv z|7C(;#4l_O1x1#E@V+h~H{^kR0Y7=X9%1i3zaL!+5^{qR!agPwF-k;(bYyFU21KdM zD^Z*v1DRMnE((Ll6Jo|Zri7s}2`y*j(1b}e*`Qz z&TknC+fe|dOHen#pR?RDr04pnwU!h)*-DhAXcf4XE`I)x70o?&-(7b+s{rvg-ubhR zpvxI(wAcd!g22HsMTA;gwos-E7w72WV!xpF5 z`|V&>4g)7lr)NlEAF8(-&7R~-q40?m0GPKP2-8^1%?<%=VjarYI(B;|8}u#XL+t1~ zG6Aqrtw^pkn=cCEhZHQ<9h&eC2A2R1T?Uw_QHir5LeDiKIBvg>BJmg{`7HV1g#2C_ zOhskjOu(zF$4*_4X`+Q{jcWXR3Y8k=@_Cs*5DNMw0)h7lMI%xew#@e!-3|k6_+yND zBD~Lje%KD5lgF;0=#1?c|Naqvrrlejau?KoTWShi0Cv1q7*PxLJUD+u0dP)h^#*NZ za}r_bbh=8r)p|1n>O1+*o37P9TV`K{O=>le``%z6S3tDs4n=jop~m+R#}Ae60D*|m zpvwnEZ7#O$7q2c#<-drfv5iK=ZE<;B#xNh^dyXg;#Bq?q!g+6~4e?X_{>2ZlpMPh!w zhjtMp+EaUah}y0I71>UN*dNuaB?|c6Oh`O*x~fww5uzxY_YvOzis7P17n)KOx>zJS zylaRQ)Q!}tG!%DJmCw=EfJzZQgO(>k4aGybmT4FEA)ShD21qGermQUrFfpRsV!zn7 z96~M-vB3E9^^Fax@m;t&w@4SxUwrd=Cif=4?)=lAfAdfMi$$Z?+?h-Eb-~+FXpAYCuckkTmSX^rVk;mtL&UJ^9ANKqF`EI34 zLlG~H4aLc1Mu5Xq(qN<9mZB9Is|zdh*^!Ra;o#dZ3 zI{j+7*bwD2pTi#D0kjWb(^c)>YdBIcTjb_lW}6}muppL%0wFMWqlX@M40JL}UBeiZ zdUqN6p^B&mqBBObP9Em_K4xlg@Dl@Jd9PEKmg({HtK!@t_7n5jbtC|m_?@UEFO=$H zuyCn*#-`(ErymITx>B7C|a~y6k>*g z+#^EUU>=!Pp9*Yz>TO&9A_%!rhO@bHjTCQaLGuJA8_n(?ujh)4&40JLo|7WIQ|D)?m0qBvQm3(? zLuxZH3}vIx;B_{Ip>0okX(;Hl>l?@pJHxTU=_O+kdhU^fGO%g;ke7B%1O+toH)nmo z>E_wlHxUiwpGR)gXx5~S3Xvn!e}MJj?15PHTQ$iGhaD!x{SJzUTo}qjL1w7o5bq1` zcOn&+bRfWJDVrwMXwi`U8~!Y z`kU17AnlylLNT^KVa@IsOR%$#v%Oib7?iGdWX*Erh6qyOx!};zuMb&XsDwf@1Q7-R z(6i^RGEghgmTglMPsS+5fT!PN`^IN;iDR_Ri@NncrU z&>Dlll|q%lW}T+u9@;sTps2^9*yRkleI8Ny1My4ovDq0W+8%Y$Kr}@Dc#6&x zO(8~b{zyRrdBOL=*C7Q0Z3=%hCTRlfi-@i@suE>hTUi$IEM7}zrC6Ha&+w6dX}_;I z0Qkw*zc!dm#(vdowSLp=wC`QdSJV_=ZXn@BMr%;j>u-+gL@Nj^Jd6mwaG~*Fg$AP@ zJxHN&jC=|k>N*zMq-x(q3mfdP*r=`BkT>m>y<;2={jQfH(NSYJ8E=UIDh|4lxVljdDj+(%6Cd+<|4U z=KZg3J$Q?*Cf@t0KYMfy&-jw(-L5Be^_5Dqy1$sqC;4zGbrZD+Er;M+Nqd6 zW_*9FY5Kn!&iRjc-GBQnI~;rk`2ZsMA${IpVhG<2@V;Xzn`(CoNgfj;d50q!p?nKD zQjISl!nDH?pBTcCkO#*93o2PvhZN&Eq&N?iwFySp1-1n$@1ep*!O?5b$+HV|aWOB9 z$*b7T1%q9X1BOA0==St2v@BV|Nct2FxuoSxRZU^$NCP>XCWXTesUAja5-Pz_@m#Hx z(VHd(V_{v95Hdpf$j4=~C3ZM~XzZI^01o2beBP6T!vad6?{xkV2($3-8M~k zKX*nVZZST0KHgum+mwAm_`A=ab9Qc-&Yw9;YfH-%4tQy1Vt_`N-J!y~UMbNk8(Pa7 zCVygx>cu>DiW@SwU^?liLAH4q7$X`jszbUBT6~_ypi#kVR_o*bvxU||w4GTivuq7(8S8ANt-g^cGRDIx|_7ou_JaX@0#-kDN=> zEVKS>u}W%aKq7nXx(yoG$+6ufT5l;br4REQLYhOsABHX=FOFh!0LKc#>kg+dX{3;Z z_@P#Z0nV1;h_o)p7yxdcN>MO5ARPni3rpnVy-0dg8TCE8mj?TwacufN)^NE#dwfLM@ynAv6gS+ZM}*&ePfuh81+3O#k^(#%aa z-t@u;AAG^s|9{V~DFC>6?@aP@fBuK>KXc;AALC`cj}e{sz?KNL_+s|?p?%uZB0h?J zIQn)j(-gC%2&+fNRB!=bm};dW6}%M_BNH5w^C+`p%@u3k?SCv(+A<)-5lE0X5Rx1+ zUyuYF>F3{g_^`yBKk#dxHPw#w_dc)tBZ@<$wlbK~DKhD!$asQ6>~O<_%m{{^ ze9e88|s%(6Y zk+j3=jhWrnyYIa9@Rn=2Z_o7g+?#LO->O!QF=qNRzuTEG`0{Tb3ekAn&(7Ck^vnjC zjc?ow=}uMl7t^wkLqzBpIX_I&a`Q6-Jc*1a8v)BOe!ko7?8RC-H(Q7hlAIh2(9~dv zjc}Z{jKS16KVL&#(XdBw` zx`=H+#m+-R2_MD63I`-iiI{j*W-x3RamJAzLpelL zGek8mSMAu!lg#`OUYA1CYCX2Px;ny6amTZ<&*@CsfBf{>(6LLilxHJ~5F?^H^%jOI zRcUhDH0|EKi*`(nf{h9@LK?)yd+%s6fMUl14Tnt4mhndD`?~S4T_J9%bZMnbivae# zUk*=z)>;~E+ciTEK6pQ~wk{o?P191HXhoZ%`CdxM0xlyQOAXAQ&zjfgqcWd|b!M9A zLkBAYYlacz6_zP+P!(+Sp+KimEaIV=>4a2Xqpkvbw|#4zUh?2!J}X{w>d@*y4!HO6-y1cMXmzFkUjs)}`AYz01o|7le(As*A(#-6$Ele3?`@-k>-un;HFgq1Q zth|AMU2jsZNqq?-J{yHH{q>igpu)x)?btF-@mP!sg%V}*MPeI6>nrPY@$6F)$w)Fg zjWM8=@)(F|RVp&~-lKb{n&Hp1Ft1iC)yZeF$M@fJ=T>&OPnBxj-)D!$Si%q|FVer3 zo!W~>gR)itafr4Ihutz&Gs2$-so5*(8aq9P?(C4E80ZT{N#!#NWjuu4RhjI^HP)B_hLiec3M~Zf#*t6Sh`7Et7=qeQp6dat8;=D?!OxaA1 zitOmxMFy2j9$Wq-nE=^*e*r8Y*B%S|s14B2w>3);0hmhqXzNf|(h|6bSC?~4zM>-P zwYE{DX1PQYi6D6_TSP4GckvhBdzXt0zU#Wdpr@XrEbo<%&pb%2-)d23`LaZR#dbjp zhR@yEbPtM2gvKF-|HK*1=ju{GjI=071&|i{|5ax9upxM%W-7w=V0b9bpDiw*69bhz z=t8oX(C8%XgR?6oNpbkqzH~~2qEX(12&G2HDK{DA35TuWo}Ioxv{5KNpTi9uF5Pw5+OzRQIU<4#oz=# z)93GRWxxTjgZDszl<3v7F{v>_P&E(5k|}XcJ!V?5u$S&SFi80t)XkF)=QYq@XF z^wp_XZ(rZY`8Lv7??6yj$C5$1ao3<+HXLdw+%?!*FQv=O*qe0wt}ruwW_k{^2RUeV zAvW;-(+_ppokRC0fksTarA6JgK_e>e5CW=gNcj{yD7!*8}h{1SVSoq2~z@n3T2d1N|555IU99o`;eXQ4~lWPJN}+PPzjI&7d93tjr` zm%hWmj%a$%Ua8!lU09@*hD8a!8wermBH#tl6*mnf;}l_IKbVXOBVWjr`Fz%hO}nHD z=kiVJvGI>;b{i$a1}H+S*E`a-3Pr&c2IME6V8g9?rB);si_i@>-$3qgOlmREB?89* z1__7t;H{(dvOC5ov$P=5he8we2AJ{Qm;7y$e=fVUOod`yfW6c^u$_a}ga=(nh9M4b zaBzUmuduU`IRg!;9j;Y_%sMRB=$%0Nw-LQ)<5ze1rP&^v8M<(yxHY@@+n8t^c3WyCH9P-@* zdS9Y$BK*T^>@(^z>;~i1?>RihtQXqs5->$*wR!>QFv3PAAK%*jKa@MjU8It&2c=&9_#RPv)^g!wHr z4r2T>uw^J3RV=&k6X(?AvvTuI`)JR;9g=SJvK@i#!=I>GL)sa(1&X%Gu?gxrJVG7@ z5@8vm3|aJvB$H9AKj;h%d{;&EngW1YySvJ3M5Ss&@~HVrSAjm#aw=`}^*??N-knH|%?`(`vjP&aG)WK3Ax=u0qq`3bUki z-JmLiStknkEa+7`soF5;$usNhj16|0ZkirtBi?qZckW46rfqd^Ym#pZ_x3DDmx-g3S2$xETB*b zW^;L?%7&dUI|Gdhl=LKdqR4r7X|BGeIj*D<(J%Yl?wqzc-SWEq`+V1O?|bcz0>L-0 z>Lkcl4YQNoI%@aLqidna&yBh1W67up+koY7+9JA`bdUTae zoLi+MyHm7f>jdq%`3~B7_z10KO2Vv%M@E_PjL=uVb)4F{CF->5blZWgbkCu!l43|Q z8;u10G(I^&`)_|9)m#B`va`r%GSsV8q<(O@bXqM?vi^41s-)ok_YYhSrc z$4;z^<3!PIqwAo274zvkY_R#vGRwSl<_x8?1#$2Q$)j!vH6gplLUi|SQ&LRk;B$k# zHljH=T$tikWwwgRbH|@rrG^@%y$7!+UnEB1;Ymz@l(ayM*Kg*)^;A>5G9zI1*C;Y2M$3 zjdu6zqO=%}CrOVEkm2%DrqQP>41OC7s0ziBfEl-dYm$3@wa}8(*;L3$K_+`8gFiDn zOo>vpo6O=FEHW5II2;=NsA!*>o}%gL5lEoRnxU}I$?plLrSRHAKy9sJNCXZP$!gXp zrC?Z>mt1~(h!^_nJ?!Wc?BMi%Pr~!l3BOeRD;lTI-P0xXqn$@Azzo;gh{re zy9a_2LAv$!DM}_>qF@L$K#;UjvHIOYaR5`r6C`&$MR_ems~xB4k{}{7713!|Sfvr4 zMS)I%df8P5c0JK|fsFx>g-w!{UTDDZ7iN%DrAvf?MyW%m*XwL&R8huEF}Miv+C%Ev ziz$D+RtEddEBte-nF@X9+b8Ja`76{_oi@Nm%3QG|0_tWEru#gkK-JRe6uphhbIa0> z*~OW1w5d@lFmUgZKM?rzul(vSz8QOeO`XW#@K6bY9teFPuaYjd?MguvL0M&I}1R#SWFumAdA9{uod z|5f{6dbWG+z0bw${CP&L^l-%I@J@~oNR%Sr{{MtvY&K@gKgt&lE<1mh#V9kTi4k|{ zUfH6CZ8nYCK%R}u_p3WY7l5*@w(Z`CsYMu`#a!SaTGt%{4zHt7U zwf+1cFQZ<2%ibM3q`fkr8Xs^g{ZG~!%?Uo%)l?+t4*A?uXweD;Jj`DC$xU{EhOMox zu|b4`2Eg$#7#L*sUS<|l>Y*yWDf7DXhLeWkK}y8KluE=XHNcFH=^uk)YBd{_V~3il z_voX4_+|Q|Kl%zgiIx~{OIoCCNl#Rn%m0S}6JG!n= zUQa|t;Ml@!5a*UwC4B=2gr5IH`*w<4tJ~|P>B%vQ2fX$OSJcBWp!E6|B2 zkI~6TA0wmPkjm@fRD@y-MrN1SsKR$7lHfBF4v^AovLWtDM}cT>AnL;BvBrQ4bLX*l z=n8o3Y?|iz&LJcWThy0xV~W;<)QhkKK$smf-1}0LXi7L79VS@@p=VF8(4$XfDG`fE z6*)wYrYDDJl=rOfunCHPXp-)I=oPeM&pz@bhAF`RJ-THZjZE($|L_*Nyp*P1HAh?7 zK6q7&#xQV<&j$+CR{8ubr1QcUp@_+MP;?dWLd87VB_TY7XqmQ|4&rBU{V;p`;LBfg zqpFzyqnJyJV}`0+LN~~$7Nk`V=k)4&nO4#zT4$inCSCH}O^h`*8R!_Q)UK2}7G>HB zWyniyr%$36JZp8XyR>J3Z9^nZRmDSB)|m`=qvVPWQHMbZj2ZM&(5d2&C8RSZ91Mse;>^re zniw4_4oI}tt1Y*2ipKQFN zlP9H~6_jReYT&D?t!2K-0AR7|q)Nk{ML#nfV&-VFqv%S6WF!@oZV6QVqL&_pUvMOF zw9RTMaV_`m`@Lqi&pcRbwBF4C{x@$rcwL~?sNb6{)sBVYsn;_z^p&f%1C@FcSWK8U z^z?yZHHi2y7_9PVM=vb^0WMUVjRzF=%KTh#2IrPb)MIC=@_`PA{4yGIaxlaP8#zZ+ z+D4%umS?B2lxvE5pFOS3E(kOo_PU260ot~0lo{nXjg1U2DCkh7%4frZ4nS85Ps>G< z*3vae8=z(bUiIk^zK8b=VfaiYXIBJ=gJP@@b?I&zThy&cwufw;aTttDky$1?ei83L zcv7|7O|kz~-di|J&1D0XzGd+=nGgwM-j&DLQ+rV1Z_vHh#pvf>eUNsKN7$LQ`LlItu2`3v2oyRZ zJ?iDpA9V6r>^2#Q_C-<{`EHV}`}%=8$I zu!9V%U5YbtYVh6Y@|xzm3av5tXnR7^2j4DaNb5D|`t8GFJHkqbw04Epj{&~ZPMOsS z#7&_mzi^IbuN0(HK^1)sm0}S;15BUj&H(t=3weUjRgI09UA zG0ir`elW#kYImqlyBQSd46xA>jFdR)oO<3c8KD%Z(IL`;F&ZCA()o**Xk=tWy0JoP zM>>`yMGCcS2Epi#IezREe5gay-T&>H0)S$ru3+{voSQ0ne$#Fd!GIOEwdo3amQ@-H z>x?vII7_2rsZD=ukX#;rUopBNH+5+z8}Z?Ih&s&{p_O_ds>vcFBm))#X;vU!_>8D9%hUHpM3T=j>E-A!;%fXxhVXGL(;k2%4nD_g=l)kasZS`Xg)7YjM zy+F0r44~Uz>9#+j>AFL6s0oBWq1(`GbYuoM@>($XQD)R(%^|D_dGiimiZg3@%2t~+ zGMJ$2c1=+z7N@4+B!}*m@R_SqpeIky)6q*gsUGfGc6ueqZ-~J=SJQO{0ESdm*BL-y zjI0(8)6D*zbltvb+R7{p(!IDSUVo1!Cv+LZiTrwqzdw=;iPS9QcY(^?KA(WAtMe(uOGSA;10?EF!51raQS+5;Z# z0Nb0B{$Bk{9$njJm}p(Znd&+;)p8~VjP*Z_CrNd~^ zYS|v+8+S81+?b~jJFC% zT3A|T$AbG+qom%a=|Erdr|2v}ODBdeh1ss;3uPIh3W1du-?5Dze$ySacS}@+XAlC8 zLv)At!iBjFj9i17LvqI`!b8rqgY+TW4zI@!$73H>7+~z!vzHWikjAEWQXm>5otZ4c z?J(Ht(*v3SZlG%ELi=V}wC2&q2jdNeLZl`D1P1w>nYl%J?3<6$*$Z>Dyt+o{pa8Q~ z8PE(l8+CJW$&8W`lk7jg|c zxzeD|U24(QE^LCA9aV{7J!rHWxMvZSrqm1+4K1& z&5a1}Ab)lN^$a$G&?w=Pm-LWydtF@Ipa!4GVsMx&x0gK8I0n(#-A@b#w0?sIp&Zz& zQIQD>(jw3*=~?!u>vAqF{x%g0Q)d1mQL98MuLp#!0Hlzra*DPNKP#^#3X%i<;4Rk_ z0MzRZ<7w?X_$=iAF<%?9pD5PDB4CVSuGTUe-M-1j^<-*%Y8h-$bvRZAhX!tQYtH%f z+J;oXqGub77{+`c>J}y>EmSVs`J+b~xlmMC=GuI@)gUz#d$J%jlBrwEy3E!Gybj}6 zjz4ws^bhka_uqSOjM?l5v)RlGdPeU{$#~=eUDuCivqff*2njNZul6Vc5D@iBr7FU< z00KHQS+wZjxe6_2f&7_D6d*%&+w~5;7OB13S2fjmCp)G;?3va^pOo`+h1RRSd3LqH z2WEXDVfPuY=IY`EaQPtvY1ko-%_z%{<`s^FzsbnG(C>D?)K^_o{0x4EbT_G%k$`5u z06cbgA$F9>cz~vcBNStFFPzb`3!h>hovM{OmDq5HeVXiV2wMG&TCd-`jkb=)sZ&^` zCy$<{N6u|Xo(vPQVCWn&X^@#TJ6&7~=O!aZ&K(1!g3Q#>4~*$=6)7fCD3yp%f(>|z z*<6f4m}UehsE0+3Ex?!W=0h`d{q?&fRJ*viCKou#hJ+C*UEZkC*}1eGa(5_Fy^J~o zFs@#wD-Ie)Lf7n}9TIbb$`^Xmjjp5(pkE#hd1Z7cY65cn+>jy>ktZoOvPAD4;oYdu zzNAHd<_p;>4YE-~>*q#R1X!ff-f2NO&UR)_dz^we!QA?~V!AxHMMHs?#sc2=g=4Y4 z4_yZi_vjT@gXK<-jDp=y?ZdEb8ICPSL@G`)Co) zqTHmprDZz)Qv8Ms^70q>iuH@hlWlD7# z92}(Ej~u334swWjdTM75oO)#%81`9*(TeO zu7u(@>Rm~tLW~KK4gjtpwm+C3ar;fXW#$GVf#~QN8BWr3j_jf9c8}AxsbQ)yXuHZb z0>zn8s!?v~GC8udr1M&MQz?5SaL~;pu1jB=YtuVFdO}*g5v?6zdzfdNgcKDDodKxO z*@l!Mh(^F_QLC62S@d=b#i||phszDR)bdh4#>}4Y8KMe^c(@gV0t^I_W`%lCwd^#h zS*sxFLPLW|{yYq@tTunnrbL_p+yZ>Y`+R3$Zx_}}Y>OH+Ix;L#D~IZEUQ+;YaBFHG zM%Me;$VPZ^MZFSepFhlCNU9R8LZf?Gap{jK4z*~hjx&39j-ETPGeuh`VqgCIuYIfJ z_4_{$l#9F$oZjq8oz~M8@~MU$1<;X4*X%D8ji9=gS&73JpIK}I>G22Oz{cV; zE~hhqP&m*mdSD@_f~s{~V(ib6JwptN+~nqU=%SLrOS&{D_0rB`9%HC4RIki8wN)5gCVDc%n;f| z_!B9DI$vaj)4KeDP+TTcxB#m+2SQ6RC&H(YALZY_Wi(1YhavgnY^hDVw+_+Gdq!z` zB5C)AyFB6?5ivey4w`13=|#Hn;?V9?Lt$R+7L2r)I-IM=Eb$}G*( z#{2@!&t7GAQYS~JPA+yp<+eeyY!FAoUUBwV<1nu|B0(4|gfr1(&v^FO1v>dRe@-ha zdFnAY;(sPi1I2pPdQX7BP~1m1>|=vI#H`g}qJxFcLQT>_h%%v_vtBKWg5jgzd7Nf1 zUZ%;(F}m&68|lc6*D)B_PY>REgl;&voA&M9#Z0$APoBI;OPLZ~cjHauizF$FwpZ99 zX2gDW%+NAes~b#E?0)_rvtYl5POvsDu|dY{BLIiC2<9-^Y1yC!DF%q`kYbM+mjcRe zi@{1o)CcjI5mUh#iH1W|V(?iu?Ku|SfdBZ(tLvZpv%mRb_iK+F4SO{0w(qys3(M<8 zv>)%>w^szJ(55@aHfPIFlv3K+vGEcxgVya>tucbJJ41xBWa@+uX8dUB7e}!cD>HTfI&1@nvg#$ed})r>3ROCP;fhQ&E&2y3-Ddz04bj%5PuiUkS?x8O6lO4>w<`<|7)Z2> z)GcSl>7z3ML#w{LW=DFB!r4)4Y=2ZZm@>V)Pgp*$dl7|Y2;nSb>U3ePD&gOFn31FaTtK70UlsOmi?=v!}PX_zPRd%p3_ZclOP`m!D;4r&W)kzZ%9H710p> zxwKs1HE=V!Hl^iiBU=^#2aR_)$ADWERl^BJ3EM|QG@1(1b<+d1y0SqR82x|cv5U0K z%vYx2@uBwv8^V;17ZC-QCk1w%v z=(Xwrh!F8Iyz6(N5-ORiFv&Ic9uik-pCxZU-G)D6@Kzxe1?`ow3>(y6OC zX_17SI3g=Zl>rPOnuM7T*WEBfqk};KYKBY`R3&nQG{q!?l6tv9#nnYxo4riOkDa8a zPG6QhJU~5E9&rW`B^cf|LroXoN%D~4bkiz30Su!m@H5Y^<^L~j?*S(FeO-s1>AlZv zUu**wy%&-IDOOP=RY*~yC|Z)OVoA2_IJRTwC6P-U`$fr1p38f3?8vfYS&fz`i4w(P zQ6xbUBme>+7Fe|1#r8I{v(tOeJ$JzOy*LgnO7Q604-~MwGyh+?_uPBWJy*vdlWk7z ziM5>E2eDu4`Sz%A*3f5{qPlO>t6BY-xkOR32$^zCvnI@VX$g&-1u@h)S@VTN25anY zk#ob=I=@7QUIC2Bra}I_rrA<x;3}GP4yykHh_Dd!5f0{`lkl8sryke`Lm`1Z9Y(x!TR_`%DXc|6^+uVC_mLorB1@Y)92jxesThR@hgLB% z7x2IgSWUq4V%dZzE~N0n*c@gBIB~8EwOFhYsT0icu2UF@^=Z?!cIvh|EcF~}*f`K3 zK*y;W<@vY(eUlr5T^?*5@?oIUg?@32Uh#aZg{IgFyQasthfUZOFd*V~X!ij-Yb3Zt ztRlXMW2y~iEEj4TND@>NE^`g2I2dDwSKfO>95sKJD%7GaOm+77f{d za+kh-^eTRIC5gpMMeQPi0?#kIGstIP>4YF&MUljLQ~WDasOUYgH|uit#eZdUsAb|x zR-|g-@iSL2K0b+zIfz>CC|r>UM*F<*+kl{>Esj>CH1yJ{+{=qaR#VQZ3Q6)6_GMJSR)-YJilf_!=7cQ+N zKeN1+m^nYY`k(vv+2x;!vG+VNcCj3A*+Z#J@q)3{SSZ#ErK<$XymL`TqxbCXeAnNFZksA|MOr#cXJNJMSb z-r?W)#Ups|zAfku+qC~WOLZ;*6eKpX-7U*Boz1G^KpvX|of6@6bT}o#vcMOx!pPa* zK8upfI7PCftA(vwZ3qXT5pi^774=e444)HDTidZy8IGFcRNh-zO$rbYr);w7d;T#tvaI1+zM}81Grz@S@4I0e9{I@I;bbaO)_|lawu6#X zNhrk$d5n6(uJ-vVlxhk%_}S?w*^ZabW!png(A+8@<9WBs^OiLus)FK+;`r(Y8-D)} z|DZY8<2ThP4J(l#!3Jxk6)_-u@4OhZQn{+n!~JH|Ta<^&R_a=&p->KW-qqHl7q1lZ7UQ5Kl{(b3(lB_5W;EKWBf-CZiA!u6&m0sGe3 zQE+9gtegOrPMC%2?eEdd3IQlHk@L|UzW=S~apvNR4uT>Pk0immj`?XJok*zR znTh@0`2?O>Xkp1net=siHBzG2Z!9BV5im&=O$)oja@w`yHMVrrJJa(Vuwy|@);*&w zrWEBV^Gpd)SS$$WvA7WG@T-h=&}GsQypBp1g-jatKo@KhL~iR5yI`ud+XM{_Eu)QW z=*IS4Ti^}&5eUk;^7YbdJ#Y-nH>s$a=FoMBOx2ik0tGNwtOrVVh7zU&srO z99o;p`>N65c`DQqS=zUAd(Xjr`xG5K{@w2?LCIQ5I3=RH@SP`~D!%vLy9eXB%5PHh znrcy0%i?9O8)aiE+9(LOp`r$vMzfe%M?N-(E=xu(ibY$xJ#BiTp>{7DQD#_KTa%Ru#?2RNE<8TO%hCOa@W z+pJO=a$Xu{E$j}5GRi)3&!K}yc5eEgW;SOE1y7}Jh^<#G&x>*W<5IJ&mCwk(Rx4}W zd2%K7)t~Y4{>hiv$0zN+&?8K_GtFMEv}JI~?54rzXj0bZ>$QferbT>qbw*_RswTL( zt#HNYLABk*kW5@&f`{7oOKUon&t)-P>*W_GN%>rM3s9O@2gbPNaxl9R!lr>9^a=W9 zAMqCXb9AE{ot>=f`rtMA(c_PZ5wL0<-=X{v0iqJ!8^tLwqU4&<05SE&c>(9-0$yE= zV|6{F^+jA04qB5C`}6<(S-gC5PIddK-iT^JhrQf)co4l_lMO{3yQqS3f{1TULS> zKNa~CBwgv800Gw|){%r3ktmR9!OlJLu6-H+OrE6_}O)cBV ze+w8awJeCPC6!;n3=-Q|W2+08x^xab-X@ORupK3nL%S1VYwK8;zN!pw60mByl*;5X zmERCR@Zzyks{hb3+H@dLDw&pZ-X2>_+f)|-WvCaGq{>ne%V{hrHg!Gw!wL>oo}|~ zsUvopG~N94Oa)1}1lZJc+8*oSW{H#uY;BzWC_vB7nE`U9R3}5Hbhfl}i0z0Rlbg@x z6#y6I{?cxGfZ~D96|k0f;OG*xRdGcQ zhTZ4Kwr!iSVX#lZ-^frOhDJsZ>gjIJ5a<+NB{tk6`_FkF2{{Ynr!U~rN)CCeUrCwl zM6wt;ucR*aV+m8!%ZTTyh!qW53Jh4A*x_wq$ZOJ!fhtVufV!sIzjU^s*-;8ZUA(e@ zC!T%{-f$2H?>H>MlwD=hhq{9Z_w-;Yno%%GyTWOK^ za8B-DQ&nhNDs4`#0ZGgIZ@n2qyY|5A^&lp}+se|ijuvmod42bu{(o_O#boMb(}_zl zt^4!@%mM=37>Iao3OcQaTn_6yO-92|$YFdRzx9sWZg#)@$PLELUH0EGRSVvC-g}Rs zQArz}mge~Ojh(pT(2#1z+f+W)E=bFw`rMI#6ZN#9`1m}^sZ~Kk2HdzgfUvL4#9zxqg_~pxE^CIHu6@%K8&(*|+U#w=}iBVsOynu@`>~9>5M2NyC#mB%y|N&HMyfsw?}y~6k4Ik)#aHLoSIk?kXw+b z(5M0@bP%;%8dF!MR9^V-eeaR4_iA~M17d3RG9n#e70Tj$XD3a=;?zlPsj2{Nixr)H zJ?QQ2Ku=dlvpI2z@cd4j0uy!;c_lKv?eJds!x8Pj> z1itgFAE3802#>?0>zxn>ok_$|j>mMWTeFl`CJ2WY9XPNDSEd&*J+q*Img(-AtlN4b zB_^_@60lsy-CKHb|9uCrOKggPGu`ETdn4_3E}N`hQliWC9C<@^c){UkY^iOjMm{xF zG7^liJ&hto&1ywwWpp@g2YY<(|710ptbDd`r0e-N+iP*?x0NepjZ!J!&8*G%r5ORh z=a3gr)Vp;%KJ?B*_?3^|gZ1SlYz~;Pqg#NO?0LXNO_!$5Um>?)L&Pq2--OF+MVz0D zDF=YWKDN)g`~lV1=bG)Xi5V5 z2((D{-4J$ZSI3C6j)&#ZpvJ=q-?|C4vTDV!EA0; z4ts5e-y4p&eqLhbTONAgextEc_;fO{93b}}72ydfeR zVbB%}qtZYtip)FZA~R_g@jC=5d$e`RDG?d<^jR0?Oy{gbaO_#Z#+K6qQ#{llBR2WaHOtif3unv&%KH`ekhZAm63)7+7u zM~RR=k4^rhenCq~J^WmSa(fD0WWe`eHWU3otI@c-S+9Ni$f13Ej~v)1zoqd&LBfA= zS|5>s@8Pv%>L!<2oSuLHIs;}lSVFMb-jd$OPxX>W?DvwX)Y9VOiaJhjAcS2*UAXJu zC^m_KWU8D%fask4u;fK(7K&XMa>!$;BK-11H1M%ZO~DVDvRCF(S_?H8+wFlol&s5A z3!GU4ir=uNSvN4PTbB3WbJtXxKf$)1fieFD*bvde? zz7%O;hejL6wNyZb??>&0DEs#_MI_|erWay5Mwt7vuwF!yX*_mx7#b=v>OR}f=7((h7ON%0d!xi?YRc*wxDBR1F{mWQ0xDa4$T6J*CygIyF>wz{-6!p`U9GJk0-NAz@%ov=Fws7JFpx5 z;_TQRvbq|_;^MNl`_${zFTK@$cRD>Or`4uIV4nKnGlo*3gik*5ZXDjV30<=1G}a`h zW!ciiVK4HY%syCE|H3JElk?p@@H#nexvF*RWT`4^YSI)c!66B29A|uZ)TgOe3NVFS zHf3lJb-J~kieJn;WW~{PvKbqBs6@B*;1O)xzEh%mpBUe?4*4Pff@fwW8b?Q0H+qLg zv6Lucb}^>E|E_!XV?&p_Jt0!wpA-GuE~hwj8*+(tBooQDV2c3tggE%i;`o{7rcCpJ zoqgKkx4N{X=ZD;qk{IuNlY_s+pj+D0F3BfgB+ksp-ksYd%IsIL@3yqZod>;c6|OR} z>&eWFr3(YYT^Q^OtAsHJ1UY2Q=2q79eTD~m!4c*Js$B1Ml^HjVJ?8Y%qBs{L!eake z8raa)A-2+@=Q9%YslGnTCUSP(R2fjfwT;7L*a5(dD^tzcd>+|!4rj+MV{Q5}GU9mt z@~c0>Y}tck-J-UY(!>k0z9iL=w-EA}krzh@LwiJXx7&i)S^^6a$>wTYSg5=%2c&iO z^}-tp2@tcZUP!=c5hv}`QOV5WY#wU&@3WhPX>mF!DnEMJ6(o^4g>9%1$jm{;?8VVl zE7m1I^a&V@`i4;N-iWQkLA+~!Cr0cgnB)w0m>LL*9bJ;Uq2yr{P+6JQStT8ErYV$T z7cfJ@o{vhGgCP|LW1IKko!#2K6qYkheGZ*YOTffnMb6X7xwNX+@?3X_9XzzXTU!yC z^|6cXuZb;*vRs@8JaRT`2D6q1I{a?sJFvsR5a>Y07Lwm@z&-(l9bKJR&)2bcv`6fa z4NGyV>o#P)n>c!AMb%cB5#u;vDm0b_gtGK~^NtPp&0o47WrIxxhb~>XjOE#@$O=$I zAq7XP&A%yU5)>)&UsnK7s#Nb~Et2WJY{h^V#}+j;w|8S00kP9QD?aJ9wmxJk#Qs&S zQr*!XcHeUP;;Jd1FIhvL%6~8#(|>KWR&QF4rE%=|B+?xEQRBhK8B-bE@23UdduM zUTW8&(2lVGxnGTX)g*}A#h!OgCp&R&3iD)~i($EG&qf^FwgCY-#QZGl*`n6W9TF{a z+9JoVZt4k&b1i9oT1T~Vl&KuzwL*JpR!-13Gd#V0o!Gr=qt?_qI|59~wpH8&02FE& zF&tuS#Uql@tz%$0`dJ(^Nrp5;Ubh$rUl3QP7O^<9EKXYhNx&`0p_^$f$pYDWfWp7uPf*VI;F?-4gLwjP3av1d`+)aK!NZrAs*Z z;>(Cj)P3)J?!)G-+mMn#gZlhhZeY4JlUMb)WFnz|H!&Z>?D9J1t}e>j6_xa5&`FFA zHT*la45LRJlG))x@9>BKGZz;?z)w|G-j3{w;61nXU)RgO_9y{3%(YN@RIRkzqR zTSU?t+vG@6PUhOJsHzDe?==)KBj7Nq!{T}8n)&2?vPBD?dFFN0B+~AP1U37#K#qz$ zL)hoRia5}i*t>jVuxqk@%H*#iM^d_Jcyq}(A_SD&DU9=L*G_~d)Nrj`bA z1Y{ieW74))qiW-m44GY9!v;AQ%r=l$LH&=v8(YUWUYWs4QleR}2P0dyV_Q){W%F9h<}G?eXLI zLJKcUlyzOoMY8|hdMim0uK(b8qZm=STUn4cQr2li+mDhM_D2p@LDk{;JrWS z#GcLlV&^u&DCf=M@*u*q$9V@90nTnYAM$sLu@pu-ZQ}C^n46Ev$17_KxZ7@4a%ePB zKw9kQ%z6#yma3SR^L=$;6Rea_;zW3O3NHm<<7P!q*#RqN~!9jls zjrBR)GHmC>&Tn5=03ero%A_)>9C>PzO=`%2XyMG&1O^3t8zh1=w(5fOtN%IdvAwO) zYQ3X7?9;yRP{jPHOez^zUnna1f)bq^i%CKVheA-$Ku)x#TX?a^nxs%mMIdEV9AiQ@ zGcBl<&XS+OtV|&@`-eltVrf^glzpsREB=>itKj^np5wRw?I(oGou4vV34qoP}TT2@ojplOMAm*YiEAD2}g7H!pk z4uhapbfxTIWbyZ>7V-LQ4xV5q-v7><@!s2aV$X0$Wq&D!%aNay zTAf+TX)Ty(KBfVaq|=L$;9PswxETexy1KAy-wtda9Y9~F|Jsztme#us5)u2THYb;o z>Fk-Mgx1LzG2|rjkHa)X~D;KZIK6nt4NP&Ydm^w6G>lER-wYvt|X-0A|D9FRF zs_aWm9j@$OrYy#{pwhmKvg{Ve>QJ4QOep-I$?SWgz@^O?0(iE59mOOqE+7eEsre?he*Y|8G| z7D!5G%k%Epx&imyID*3?VazWlar)8{rdY;EwM#Inl*yJkW5e9A_gzeSwk_4& z!0T78N))$-@k8?u_8y{;OZc=l<=lV22pUE~iObwpjNk!HCH^g8qf|4BL#< z;q%`}4CdOg-AX4X%J=1JMocee)KD|>Vdf|#276*YA;1p`^r*9Z^hnej>FiK4C%FY=oKtU~QFu}vV`R~k%7{uu3D{av!4j=063Tfm|a700>B>@0B z4_^K)Mc$1#0uI|5?(m7zAC|r7z_QpJwqFv6Ps}gsTJsL^#rLrPZ^*`Pye`Db&klD5 z&}}Ixhed6dJh$IY_05Ws@rv?!=fuHWzA~fb&XEH*;r4gF3vO}V{27knJw2I5(-T3W zYT>LBjgXm*@|s)9_7^k(cRizHvlqp-lfMz{8$jIXM%@!`57sg^mBC)f=FuDW>##0z zXvhul%iVJe=qCY|8Q-(9G8QO6B{t>6xoH%QF2vVj$i%24kwC>6#_LfD7{tbS|NT*%TFfKm>_jwI!42I;?B6+zFMsuMJn`HqeDIFVI;MKg5c%SD z1pqdaahYi_@$lMv$yy~POj|~)wu-SOVM`y|X-e$#9SXSZpI~bQS+)yvNkQ&%h{RBo zng&IoG$?GtB6)^Hv54Ozk+og>MA?H*a@qV^9>=FLi09goHBm1mM6F&Ioc?w{KGVvz zM%mVAll8$L?{w*>m*vTfGkV`q!GR?B|MwreXa2no7Q!JWib=X1w;fR2W1M zE2E{(a1bH+(mr)c#L6L_SjpjXw5Smt9RNp+T09=uJPvK`V*1E!u_0X)V^FX4EUqMn zigL*xJUpO^R5L3n^a|?j@=>S0rTWw+Lwho!pv(Q&bMOAKS3X}lG~}}yD6ru`{aRFJ zR895slZ*KA=~-O7x{3oMKD_(zM%;V*9*w-|U>UhmeUGHjnmGNI*{YclMyLEYe>kWB zfSmv&UvcawvnOHsdAD539iu&nct|L9E4j0;*M|K&x^Qr74|Z?qLr0H9Arh%a7nijD z84CE}^LyJ<6)6x!l}KtunA(7p4u|i{G_={$o-k>?1~v%GxNkg9#fpZh0^BlkNnNt$ zmiFh?@xJUBFe4?-#wiA$!<>c%A@N*Kt#Qat6B~Pc=yXEmJ}0AT)s`m#k)%4-)Cpc$ zPDmv(S{G-gh`I$Nf>FnyE>4pqNIKs(aZWK1w;l4iXKDZZKcWlOb%ZHc8<|X1^ z5g?J))<_y-3g9>mHT1g-I$k-&+R;?Sb2Tx+3IPp(^^!+$+U@t~S z1~p)4lyd5f`E#3stc#nJ$3dB8i#!tt%5-!^FgVb!&UiVN&`EpQbW)9QFyvRkARdL7 zM3;0jOLI{i*xL3Y1t%tIdQtXm?EGbAATXawt$heCn2NCuWyY7@HGN*05?B%)CyR+sRt$DYLC=FLjHec`pM7>je#V(`xE3IHVPZCFgKW7gVw(T%2Fe3wZO zvd4uja;aZDH;*APk~i$^#%esNZ5%CCP_w9XW?BrX+tSkLdayI7wV|@XtSU;2i*cC6 zVK9oIG-x!M(9}QcdNiySN?KBI$SugrCq`e8lvxgu-Dc#}z^i7f>wPfTf8ncNe&$Wn zqQCx`zeq1HmT&6m^?dF<5BAh|?+R+Ow?%hB3n$K1rxVToD=UWfT#bGp6k!Skshj!tvEiNps z#Bl8NB)PEsyd{J(BOAK|enI2*YpPp0)e10T zEs@oWn3K!ENG9lIB<+;t^}^})i4k(b?Fhl80yLePL71Ie#d9Yo)hUF-J~1RFt<_U4 zl4*H5b;@4S?K3-*5XVuHNX0F{kVb@25}hrK*GTA1xo_nfz zEiWu9>$xZqU%ge4XvGXqfT`FzJYG8V8s4^lGd_6#?HJv@S$oHS_|g?TfBLF?hMcAc zp-O3WiJlwS8Z_ZxzemB^RI=Dg)(szhtNU&=v_=^{Qr4E4n-y`moNeK8xiLO5i>Tb& zr=L59p}_$>^3VhL%*Q^65e{;4+psR@m}ivP43>pR<|H8Wh-2#M?t(|0`+9s$0T+Ka z4P04_A-gR)1xy77H(<%IOeo4UNW8ZrkFPxb` zGLu6-n?f;_f{W6&T7EF$;%o*bu`#3D`URM@XVhB-U@>YW0WOwEsH$JPE?iSS%&Zu2 zip0=?)a6c742&5=W-Zy(kS!Fjw4TBIN*rl9^VF}PNDO%)BVB%s^mic;=|ERU1k_(( zM&QWSA^iMZH)7Yw231D&%3Wv*D7regpx}j>8D^4Ry)YqQvntM}J<8f-Fm}J$UYpGp zZ?SH`<*Y?VOb>PhAbv*K`J86y=7c%bh7goB;Q%+5Y336}Ez405Y&BoQ)M{CPYYC_04INO%&V>sS0B7;Q-TUyzU-$!T z*}e%s{L(k@zf}e`#UeN@Ng~%R{cdf=7!b5{^gN02ATCT zIz4uD(V#^9I>Vb80H$;qtFhO)plRDid)N*K!LU<642ww|zun(}&081XU=(9&B^fVc zF*|?D)O7Cu?l(?8{>{HVn)^wfOY-PDU#D^A{Vi8PDn#6FL-5hMvg3m2~?`mo`ARy1>ldp?QNqs*WHGGSFfyYN z3<_9_O2o((7fSgS#kefaEy)@~`^U+aX10MKAr$Hm2W!Cm+=`CWomx()hziHPW(5S0 z6Jk|mNRv8Vrg0hR@io1YM8odV?28o*6{l(sW`d^6S+-Hh)mHP`xN3u@RiAuuOn_Fc zt!KdAYipZ;sS1?xJ0f1Y3eQkQkdZR$maHop*g?>06NUKF79yir<&xWzF`F7ic)dO) zDwf1~b;>?(>kGgm&%wbtJ~_uX?e4?J-}^S~-8?Mk%7CYjP2eQ~T+7jQZIPqXVFrV( zQ^RuJck~2QeQ{d$V?jU(_3mo|ncwQZ=X2S9yR6;rZJV&EzY~dcMu105*M)6|d>*a| zd-05x}mFusGe}4Lt(xyy^f6k{Q9GlGp4_ zDVx@`EZ6_g!6DpruQ(C`%3K$AH?WjwyEa&gTKZPAMY~G!g)&MK2rvy_%_S6gvj*;y z1GI627dPI#72RQn_DJiQAY0ny&@zorqSOw*8Rc9W=~O~KPYEqCm?m*lCKJVBOl{r! zLLH}0FG(boL250l<*lNe>6idO3QI8n=?~knf6%RK&o2^#Nf={RkenAP!qHLDh83@Y z0NMa@PMB%p?_pM;C1=Fy_Ozqmo(=(E9wo}XegAf??^6JZV1X=dmIpEtA#NY)lY8XD z#6lF)%d0BH(;M_*Lrgx+kgO-Az>t_?e2s7FAjzjL4yhc|V?*(j*67J>5C zuoZq3P|C=?6VSs9XKXErV<*nx!kM!e?Q$R=i{kswj^XI*bE>gmmY=(2OHiZUpn!g= zL{>Ozxn7ZT4lN5@xiY2u=Bj02E$3j#uZ!aoXWI~f?(fB^Sd(Q6oQ&1*t&3TVMQfV* zV>@C~iPhrZ@)ca3S`wcU)k(0N2SeLi$Wruc9?69KIU7S$Y(;0lg^884c5*Ny!)^oK zxAnRe7nTcHNL7)kn=zM`KP5mj+TAK5MY1A004lXNny*^ey`^6zsUscj;a6@`6=7Q) z+1QE#(Of2r(~~PWdVU$%@F*Oii0YlN@1LNSWd%-moS9wKV2Iiw6vblph8iF}64>k? z^{F_K!REoNtiv7wSD*Ohhj7!o?!>9@{{Ux?9>b5vSMmL;MVS2^nkAy36qPw|?l<9! z|MgeV*A>RoM_;;aRX=X8Nov!1=Va0blTPm!WPgW7j=Crq$2+=Mp%pnx@R(qcK+*(^sk*Tx->xnk2wxDU>=o^tWTZtrU zG`qwJI5}RJ;xryci!rOs`|qq~|KQ`_e*KRh|L*ah$npRGU*G-SGp*nK>|@jCU#|b+ z?0D|4>vB0LN6bm&0yG zObp88N&-s~{Sp8XL@-rMq8R_X${HkzRu-G~ZBjXkDKU>mkzhUkO$9!#$19ae_q3YL z9c*Dt*MP-LSzEYHP1Epnp5p2+*a71q`krSXM$h}n1OGoB!Q#A+L z5Z_7JI7wG6MdU9)}i#I%a|kcrJSJ(UHi=TC>ldg4R358%@eZpWYe#w|E7 z9Mp6%71P=TzIN$HC&|g4F)F?w(JOny3F?ZavN$3mE(@4ip>BCr&eM7hK5GEGA|c$e zd83j@Um9D$cYZvE*`+l-EA45LEhSn~6y&BMiE8Csj?F}IdSo z{UO}Ge;c+8^ke3O>rMUGI~s(sQcyKdw?x$@0X_wS2ys{)F0(qSEAs_B`otu@ z_~^Jqe(kZrRJu&3Qt*j`-Z|jKP^VL8J-Cb&Z0q+cBYW3qKWq*GxdI}n2w0XoLI6)b z3Ii9O1Fp&3avXEf1ezS3>+otekkYuj1|vAKeNc5OV(Gj_*)uC^7@L?z$m>F%MEy2# zc;}~=u(BGJXxoC@_wB&W(UCStl`|$7?D03->q;s6Cr+npmgQFR4PxT-UYJfJE}!|( z=3eD2&>k(O^4Pv1ggw0;*eWTc)?*6JE6lQG6PTY}#8+OJ!^kc7pzqMF64iI2*fh%8 zrB&v*Ep=SenF0aoV^CgO0CUhP;GmH~DLRjqfZCATrZ)cN`5exyl#py%RXi%wFbmN3 zC{St^b1DrV7Oy<$vuJ9b+@oayK*!Hr)%V!GZ6h`f^(gl$5nF@T)X-r&Zn?iCg3_5& znz_r@<|Ms7o6$_{7IzI>eJzy-ubOO{DWF0j0i0bsf|{BIV2~Ts$&(H3xZpBTy4!7R z>UnA~>zFMfL@tx~KZ`qvb7e1z-T-_qcE{D!CUGbp$&@@hJZn3LBN&mucuoA@xv40A zbY@M-s@r>Ac=&y{;-O#tDB^R|c=Z4JfAGwSS-iGxL&_S~QjLj2;^Yi$^wja0`*-2c z&Ovk0r~pP0={1ay*2PO?M&j`kFyRIUn7 zIIKpQ)4T+ft6sm+Akl$1NpK{mT#hTtVhB@3)jsFNVnm&yx)n#udc@&V>_nW2)#;S~ z?}FXyhASu!?W<`T%wr2cKwi#f@qE_Y?AY<0Z=C#3-~Q&ynVUe9-|4?V zGE#wxRC1BY6t9S5w|FG74>{oI=#bClhBM?7=Wmj)DqK!Rl}&h!Huy{q#G^SeqDjOl z6r#j1+Ukaq0*%I|s%^1sLXsT22IAtp=y*7EhoDjAP-O$nhUP8TVawk1WilBFcUyFf z)Bq=0iEQ;1F$Pyx)7aMQ$C2THYVO;O?al-$#Z50JaO~0=#+S=z2p~InXg5B1@6BSA zdtkD+@cK+rCz#a)S`T;m#&HU38RPPuazXm7Bg6_7*TGS+dA@SlOIh+Q~a&&yE_oRz$^y!5-v~9!7X6ukq7U^ zu904>iZMNPX$FBn01w^!Hr#RN+ccYV^4vu{|J*UW{Ngbj+`AM1$8UZHx8JZA{T)G- zcy_UrC%}K1BYg$*M*=?WUH_hS2Ck34=!oLmLt(+QlqyoOiDleikKATM@=`%Y3G z&sY7LOl*h8rbCn1>P8^cRNaJzmRT5ZtjsLoi6<}M#mfoIte13_hEL8}ho>cg)1}k( zD8YQ_;6^dLM(qK2Q;>l>u;fIf)H(-=VJgid#iw%QH`jW-XGO14dlQYJjS&XN!EOw8!Z$6e( zCCE-$-}%Kg%u(jG&WxFWBLTq#$A`8KX+J%C?N3~oL@AfWjt$+|wP{$}`4*N}ls}Lt zHCm~1bMDRd`s(;=rCPOmv(w=?wUWr_ePEmN)pQH5%op`OMZ{_HexJHLue_9-cl5*A z$mwt#e!ttoUih4Ro^^G=Et?Cs-+d=W_8-Dx)rNH4sH4B>I4OkG;kF`ZX{ja#qj2(_ zNEBuUttmF~sVf2+O0v%BoJ8RbED9)QJ1B?z+`4-}RsUS_b0jFTtI9^(NdAHVYOc@u zOVg+e*dGkouwzI-5(gTY1*cRcu;lN}b8@2{Nx62f%}|cHsrH=RJ0Wq-9hN%w2b&mh zL8k~hjqRaRY)vF7w8wAL_g)m+#MueiOaZ%hj4J2IC=STi*&_j|8+*15!`UHsM(+Id z^ep;(ZLJ5t08|bo3dk=cK%vcN+CS(pV3YW=-5Y&s=c(%-O{B287RMu>dQ+P$savW?9G3=-*w1_JPRT4DD~MBZW0x>?X@%!r}* ztj`nL`@P3s{@k}8`}@QjAL&QW{NQ6hdisg`e%eQR^60U~H=jN6^jfibBvGvVn$c`Z z6iSsAoTlo|O+jpAxuV|yd!Pw_uQ(TXRg6swkqGPN;@m7=l+qFfh_l$S!Hq|5?Z?g@ zr#cX`!KB1GZ9S^D%LtSg%~jhooJsCtuRYVCEHAK6l@Un3+WcoP0m!DrQe3rHwWgm2-QpC}d7xCxcdKG{B#Oo-F4Sw*B z+wk%C--S&h{Q~fusAVN0uhPJ~H8toWhr?>qtkLAvMV%Eu&OkWiR|P+&KI#1J3NV4c z`SNpk>W8mkekrBSfsS5@a3s(qQvx1VRo|OrP09rG-$>#Mhyfu{j5Frzt#)TdOPw{v zx+@q6+jYz=Q*i-#FGhREXVbW_npgDPCr*UJKA6UT<@F`}^H63vdm#N zwo41zc_5#MV3BES?%`T8hjby2L?$IsYgJ1tR&h3LRXVVxA1BXDy3y0ysfvP}GRcw$RTjH~E;aZ$0TfJAE2xnDU5sY&#L;QIJS~nj z+m;Sz3ne>QKJ=-3(bW~eq&V{*KRbqri?dojVs?fiF&ys0(nUa`Yeso=TA6%N)4AO5 zRgxR!_c>LoA3C+}rP;DNOPvNO_he#f9^)ro!{rN8I!TTqN>oPmIUNe1$tR)m8za}K z08E?HgH8d!RRKr?0k@nz59XKS3R1|6nV6f$%;GX`*gmXTl@$RTFP)pjYBY+CodN9K zG6I%tuFNlKmq3>|g~?ck9Fc``wfaxmiJ6NhR?Ut4E$#U#1_e~)5tQqc=7qM(Qo49h zfYX(D8L1Le`X+VC4C=Ucg$8t(4!KXSow+#D5bH)$&%LH8y(-&~K$=>?UfUQE}N_*bo0;qUH&!R=R_|88*- zLH?ZlF1PGQSpHkNS90G;9PE;N?6ojkXH?COV1!%=lY(S+rEu@*%o${{97Pf;MU6ZH zYKFW9iTs;N*rs|Ro3*TBl)D6cb_IQ!LCoeWNC{9$h+lJe_hP*zK|{%)w)=)1L)f=# z6nETq7!ijAC)tGBlHP$10U81Twhqi@rP`)@`wO69*E~8&Aw*#HMMsXNbY}+N# z=i!amw{r;lH*`pJZLrQwtiJMhkG?eX4?k0*(M$>={_QXP#ee&$RRQ_>&a??dZbjU<47PIR1o0d5bAkPV4nMaCsq( zuRK4m(Wgd;*Df8j%CGaeH5JDomNjFl(&efKIp}uy1u;%XGg`kk8jSz!ZGacXUtcnt zj31F``1j>tt+4jXk(}|mpl3Ky5L3*No6nzJlBlkV58T*`8;4w4)O`^co8w{2j!}= zKOId446OUH@0!9e6dU4;d=CPKrS+WFatTtK@~l^va}uG|l?6^FbxS2?Rr{pUup&tNZ@wW8Y7$r!v2_kSxrv zwqD#CDC$o`o$iXUfI8{`0;Z&qS!JD+^&>GV&s=T3+F?o@#hbgLzI z;w7ui-XfoZHE*iuy)41og_J?t=CTse`&kODNQ9nABOuNykuE3+v7++L6+~lkJpa-u zOw6pH-)qLlAG#T9u_V6o==0dVxmS5i9FW94qkt2g?r_km9Xj08rKp_m)ueWZSgILu z#0_lRvQ?Z|uh@zR;0>bgi@;#BqsM1eP|9;kGHHidBK}$dQCT}`Q84Sm5+$YBYhtG< zMnqsseggp{IVI(WAmwNc8F@Am^+IBk$Xyzc&(6*QD)|wpQ`{#b0Hr2(oxvEhdJVG; zMMnr``8?iWSVhj3#gP}95~!>%0l9V53pveFaxl;Hudl)&do?@|(so^ryQb87yx7EC zyoCM^4?gw__u~DZ`3Ppl#_;^(&noeCA=g0N)7j>riOn#SbJ*go;P-#&9t`();e}I^ zZRvHZOF*TaG8j5L!yN3QmdLXXUtrH;$1nWcm%jJR;v4&Z-2p%aI9Zf~GZ1oWD(gLh z{I+zsu$rpLqCo3qoSp2kS(U6)qk*s2@b_n?OV3~FG4@1Qn?-s1m3oN`{uLX z`S#btKkG9-@$&1@XHT9xkzCDmoIJbGIlG*}@=Ud!T&>p4axvrc6-WOvegJq$FXCQ@0kE;!1|$nh*prif1bS zjR}kSLYpK*$IoSAT9m6sc}}^5nnfYGjBA*Y?|c<8pgh@-KvaMGB|Njz~RFDvZe<8 z4jW8l>Kf|U88+d_kRPpj13wgWe|{yc-53^$Jf91D0c7Pk5B*7M|G0jOE0{H6X&lWE8x}} z3aV6cx>OMZSkp`wRsCq-I9tN&lnAYyfzw#ldTH42)n}&;|LS^HNq=n1r6xLSNDbYy zx=teG9FcyBkU2Afsbvxu*>cN>joA`vDr99plBt{u4P~>1YZFFIc=^RK%!)H-CZu^S zKgnpTSgdM?05PT4X4ZDMy#y(Tx>ZSza|R-voyb&MnsQE+YMMr$Us%%f$xAo@B&;3$Im<(Rg; zHcaCDv&CjQU-_{_vyVs={Q0X_r}97Po-eJgK2@^??{Ip9y{Y)pZAO#jZl}Y3WnIoV zGgx$-4UWu9-}} z_~3v0!r#2(iKm`j_=o#`-2uR}$1nWp+vfoi3b~y5fB8@b!-@6v^(Vz~+ALO^>3ThD*kkDq`y3SH!otVzG|}_*&D90&iR}iSIX9nrvA+vgt2zi*X82a z&1P#MUH)9QZW=XN%ms;4tN0o=FQi@m)z6%b-Zh{Ru`7>AP=R&jBuh|#^f1+BXA zy=TrKpD!x1b%{}~vCdBp00o4In#G~6$a>Owl2Sth%`Ax1)gCv>b~2L4n8wSb)5?+; zsG!cGzo%0!nH?8bGUBY$sxC%{$!Tp4I)6sY-XQ4ol;!ucidNdUvzB*&_5=nANS z&3dMt%8$w6CHpuf4uShgzQ#gAfB=nUvrVWnJDl(b!UA%f`W)n`?Ah9f58ihWp@0un zPD2wv@S1>>S&4S2r_cIuOd_K?Ghbc{4i5&jq%t;_!0Guc)~iM|Z7w96c9kpU_*^kz z3nsJaPi;2K1Cx`N|K{rA;#(%X{=?VvFCMGDdi>-|5}o~gy3}~ze7gM7h2>O(<8OIZ zuT0OYVY^}H7JTsDTU0~++2d#N-Dh6H+S)n}AKZoEpywW8(58CNEt^HkHMq zMB3?^0Yf8$>X=g;a3s&2DL2Fzwg+Z)N|3;;VWHf@)#)|7`rH^S-Vo|eKbF}!lFp+b zU+n8QHY(cPq3WO1bI?}Vrk)X!@J6=`W7poDu)19;N6XF+W@}s?H@0jVR{aPn$#sfj zb=qwjghXRGjmRi+!*E(Cri=dhpP~mW6cw`9u-JOUCuwiQ+ z)k);BaAk1~^Q&0GG&#pHpv-|%R{YX%a_Da{<^%?gHw}No}QbXep3a&fB0Hn zUTB^^f9~k?{9Kg(e|2X3IdQVPnJ#Z$n~9;p3P(bV!y3<6AovLTM!Mj!QidHWkxUK{ zyI*+TS>pAW>pBkm)yX&}8**VN;>1XY69G=&Yt}W@ zK3g(ltenVrKJ~zw*uhEfMtka{%OZX>v9zW|a)zX2IgW z{Rgiv{^qw{o=CDj^YgcELQ^hSx=>N1&x1lC2O=?x9Q?4KH5hpaV-*`CHf-th;kCUp|_8ULn)!Qp0Y?G7t_~@4M$6|5Fy=!S_9! z$ma9k5ECDf|NH)*-Dd#F$fj+o0 zzq<9xbWERv5d)pVg{8DaayE4Lb+yM&n%j|n{aTBdb`4xhbt{QZqDYRzWokN_tBi@i zx!dCL|JP0z{#&2d^p$L>_FAUgeDpeO(@*u9xN>pY?{j@pqTyR4GXJ7D&KgN?9Ja$= z=8D;Y3#mFDACIdF)o9p_pF7x%(V$7YCTf%qt=2Ft(auvZp2w+kGb)JWu-nxMa4jev z<&-bV_;MWy4(XutnVgcZ*iX&%Q${dX-fV6w2m9iF(_-C5z26HxIMVXVHJl}*9;C>f&#YUmRPz z-ur#_?CbTjV`I;X6Z}x2T>HZ0N+P!`20PNx(I%#yxq#Se6x|YKzvGqzSd{4O`O_D0 z?6q@<1iZNI=0orY{TjKQI(tzE8?p8}Cy{F`k=FSIn>P+(vpBX5T@mzm1e5@rZx~f6 zkXaO~ouXcyI(2h(Ev4cqOjnmBx;AQ0c3X*%XF|!KJO}(!oKZGc#C$ZRLR8y!?a=;g zmfUFYsVd5$E&+c)oiZb7MuJowO=e3vf1Vl+tnD*xOc3A}!@YaQAO`xyD7#Eb9((+S zDLiv_4coVF#3w)T2=2V=Hl1F%aiCixb^gBYkVK{qyOIX?Z5csdS0`3v9kaD|{h7#r zfmsLvluTI@Y+e?iba{43yMVTB=)#bISq`6>U17VM9c6j;#YDa-&;E>j><=$5Eq;D> zX8I?N`v0ky-D>$o5*`KMRxBpt_pMfYr5)W@wJT|@?$oit)DhsMyd83G1FeFpFqV}c z(9lc(=NGVrc|DPnGcbWO7iZBsJdC^FbuV@v*e?!zL;?XP=3{AW5_`|i0Gkfdfwls3 zJaov`L~K2wS{KZ;a4IV$kq5l8*O_(HSEgYpCJ?k6Ri)2Q9f61oYZY0?WECeDa#~iK zot#pUC335Hw~MmJW|KvUkA+N1IR`TWFi1XLlFwTUbt7ez^XGP>H{!*HfK7P;dcJZ3 z+A~Qk3OL)peGr`vvFXVL&3agyWmPdu2}pHVjEIQicZls@jOQ>FH^Ax>farH%?|=s% z{lt54#|Pep@#mk%qyO)h@#C|Lm`W7!bNB7TC+`@7okVl-l{aqf#107vit!cv#g~78 zi)&T9eeWQ4^!aghy{w9(ln`%~^T*@y zq0L(^TwI9#>3Riwk6wvg8k>$)-+y>SkYk4$a2hrQZDTnzQBpr6(C zqRK5&kmb4aYdCs(1`oV_JNml>`6km|zs1w(BPfpPbxc#;bUgMAVZ|gr1an`?c*MTi}eE6Y-QY0Yaj z?Kj%Qm?@zGR-@I5t`-^}3=M5RB)?-zpS3h)F|`gaCriIlG1}fUwYc)nPXK(A*NKxS zTH_bSCdJA93;DR8HCQZbvWE?}yfJ;xbUIV)tY`i3^##mHG<9&mjRPCJVhqY!J2SM0 z-KtoL*5jydNTMGHnBdw(K{n8|+F22%PPEo4j+K!G8T5=sxW3G)F#0o?+U;@F1z`Ck zCl}gjWD37Mwv{9xhs%RfLy&u>DA1({uRH)FM%DG+4vJ!8t7{2yAUPD*lZeIQnlcVM&HrC1f=90?XDrV- z7LB$#soyJ+zW~HI+hX%|9GlCl9z-F!jKBQj&m*@M#c%%BzrcqcegL-|+Knwccj3Wz z-id$t$bER^o?DbeyDaaw7>y$?AjEFvnXw@(AeFlEFPyuIW9O%^mPlcv0HWLWZiCn7 z7a+5y{1TIZz-4*w)l~k~YQ1r{*pdg978l2^_nx1kG`lG2=!(15UnV8LnJ!XqI62U|1q?v3uz?0bf~-w(SxCrK#d5Ae)?l`Gpl6J$441 z`O@(}+ItT;H_Iz){ODDeMx*K727;JhA9YPvq6Cfev+azqtm%1sN%_hqx`KJ*A zp(b=N9a9X(HtuEmX!>YIy^VU;`%3?N9}Vo+vYSQ*gYWMAV#ec<^giXD+s;3eC#12g(SWtH%&4U(o*h~sQ6Rt}i7{Qa8l8eUR*LWy0?%Ft0l^Gv35;>C z4>RuR2=J#gdhD>paMYcLKf~1P8cdE1ixFUc@4QEZ3h8v&LU8x13a}_?!JQwZ;Kk+f z3n|{gqy~dZ6(TANeHKX&`fwvEHxH`QG1Mc4%4`5hzgvtUa{$3v0^U0ujdSzh87pcr z6-f%p>1TF!pxLD0noBp}lVAS=(vdJ8`|&UEo<*dr`xD-dhbenaLg~SLIU$_gJ zDlxZ~b-X3fb&6sEOIj^-X%_Ljr@LU*sYOSnKbjKkZH)XYjGFK0b=nPDgTdNbZM=Hx zmQ98=tCt!0_5byUKa!L2_@R6@^WV-zpR#%hi%!I20iRK?DsEoUR`FCko|+X1Cl={S zg{65#rPNzAA2w*EYkcu6cde8e&r~WQRxzj6026JLVP8sQHaJQRMABl0nK|MY(FW(g z6VWH9plKsESae)xE;u3?OeM3QsWxhcbaB^9^s9b4mCA2-#)|6(oPj%gM;w1qUH9Fv z&aNZ5qr>ArIX+@<((1KmQ561Wsifq1EsY`qRtsJBLxjFx>u|zK5zv)uS_t)45*jY2 z#2JNiy^7O@f^L+edQRiAlV>(NZpvc<6`sXytEr%;0mYU^7|2u1O;C=^TV^Wrjbho8dfP& zmnno*Wl2v4HcZXBWL^tq(cx3mg_Dt3w?GBGiJVb3Bbr)y-rGPC2(y?m??AvG!5asM z@w@GZvFF&Za5{YSIMvN8g>7^hT(rC%XD)6Ky$i4Q`OzCz5LL9Ij?JHo9ltE+WAtbr z(d)HqhDK~x4h;1j>gnnJvx$EG8td!r9U~xcQzlnfGva%9}y%(d?ts|7b)b}p_yXEWB&^K2%kU!c`q zylg4vFItF}rEAf??p*BewqyGXuj7pa9YQ3nI&?0R-X3< z*XXP1k@4u47^h9AF*s_+z`zhWo(}Z&4~Ywv?+nfc=nSTEqbKbtE<<=JGN0R0k1eYf z&}V4F+~!(ZPlMh#+@Zs)Ga3HyH;<^44_Ix#*vHZd+rO78K#!O5YH zbLjltpwa5>9K5h9A!qd1-Sq`>=yNA9p8(ofOPWxnM4_a43cF15q$%c*rD#1sXD$|v zp?_c$Zyh|2h<%vAS`j;T?8BZ;2Zn=rApy)Y_Y_2n20-rLU?*KifSc}JaZkkQnQ<3A zcjhyDD4(e@dZ#W;9lC-=xME7#M+AbH^7};ZRbVPgARs4tM?^0X3oXS{B70;IB}W;0 zx+`kv{tlD#=j>600=vVb0YqvRVnW*twaFy*SDjuGlGO>?b5n_OIf4CBg^2j|VtU6L zDogaf-W7zkWj5y5>hZZ7&VypCfR*mE=kNFx9((8oJl`9JnZUwVKX;{2KK#vnFA5G4 z-d`+e#G~OOQ3*j~8!8%_VNXif>r_zN(vI5N>VTwH{~#QR_7|WknJwlE{?zq-*8spD zv(8ax=U_vq!NkL!J*L4d}--Hid5CLmF8<|^!Q#e zU-)h`mb8eD@LajZU1OH8tc^elIeT{`534~XfQ{1|M#SS5i_aR_{jLOJLNQAcUE@6e z8cXH+d>LsQB<+tIt;UO6Ez)thkk^d{b8C+bdv6(;bpJ`+yfbA3gMIr<6~;>`YWjvG zNt24q-BU%;?aSQUSwecx_qcFmJSa$Owl1m>SpZJma}7=~J&Kw9vX~7s=W%4tk#AdD zJzW6hRH9B(ZpdN{0nQ-t3^2=EG8kY9X0FGv(~iayg4Iljn@FrTFok?F3WFqzy1zn~ zK&3$|=Jt7wnC7>pQd=fjQk5@;Xfh`ndYJi*iAE&)x2jT}02^lj8qnBWht{@A(adTx zDlk{bba;)TT(EJ)9IRPBS6m7_67?22{xC&*+=(nW6}W4hbqZ+jZ=A>}u0`+xoc~^p;d6|J6`h9vSmTN_ta; zAUHbIWy8SmI5w|ZhD*-aC_dx9&OyAm>kvj9UPK8bty(-E>1Y(MzVQ|g9qvGqq9BgQ zM_nPnTrDotH+CNo)WZetnIQ+v*)?9zRIUJDI4;&s%>T;;u_w6X!j!wB5sjVGv<3&K zPZ=1BRDOn{_CQ#yFLOCyS|44oND46=1s0Q@_NWoF8m(fQXHjc4TI;P8^_xY5VJt@u zJ6i;oLMly0K_DX`N9*#F@4k@>UD+$uLqxV&U%Fv1KzY};YpRSaZxMg&}GSYdV$XGy4ls(y0{6I#W+Gi zzgTZ28AmA<#gnfcN33=UR$h1&8dh(?P*jCzk?Fa$IB!V}<{Ap9*UHc+1;LIeGCn*( zLjfa?=`o|6aUKDt`y~>Ki6Bf%L1ZbDD)S8#3PP}nCp#E0>dQh6 zVxK_h&k*33^9U2DbO+;z5lHB<4IwJ4VQp(edu_Qvw~By5l|e1Cfy^(;r6m@x+qS?2 zT_G;`2rQbiT=wGH5^nqF&*Jn;&PIhQhaFG7fnDVIpSLAZN`!Ffg`06dfyaG&juS`< z;U{1EhMPDu~fV?Ve%|{nFMYZPnj5s-^G9B~^u{klAc1<<}(Bh2Ctg zuxnz%{!X3joK34Wb4{6FZCpOD_3iKZUCxCAwxOE|?H_l>ac5CydXWy?Je@|XE9P@I zRj3PdSpbG3R_<`;9tI{K;cn!v$r!TqdDxkS)8)pbMU5tkUdbZ4&Ef8ZM6X^g9U>W+5X|JNkY_7f$CGKGQ%Dd@9<`11vXCkGaz6P5QwJ5I8ct& z-sqxft~H4)5ocR?R+R9H!opBcdz z;qHPFTTfYzFMaw_eEjmQSUjg0i>(SichNk|qt{_wbM9hi(8nWvr?#1}q`6x3dhF_R zp~n@5KS!_5^wI{ybcRDvT_2}Ee?+6zo@E>C-99ul^zQ3G|A{tWv$6Qs|0k=o7p4{I zJA=NMorPM=)+%@c5$r$Gfw8d(eB!e6aNd?R6b*%OpnnuoX}ZKMCNT=Po50bY0T*`l zOo#@umMSw+i6nug7y`kNaD4O0Axg|@S1bx%2rd`J3ETx@Nip7+%PYbO^SR>e5%-iZ z@eKD#ET7kcCGCx9s;->Q5J_dKRn~nd6vX*V=Q8v=OQP?jj^0SjW^6cRF0Q(I zix^vOudhT?Z6%7dUNOFt=pITC@C?&?4`*QAn@*SMfnAZ5>`&xglS|UYDwX;MpV#x2 z%jw9^|@pdkh3YlCVA@O zR&uT>WU?7@qO7_IF>ZOdXADPZKbUG7u=1P>(Y{~-6aoN4zBDf1x(LlY#I587Sr=da zB0I;K;T8hQ6@h|zs#6?mGA7&&a*sofJ(m?e#`DJrU6v&1n^O7A&pGa=*O0Dp*Ha zUuM{1vJg?5i=;Fadev#mnk#-(S7o^^tEdb*y}q8J0qxmax4grTeaifrji=13zM)je ze~JHl{rZh6eQoP5b4}y-qIvag8jZHas#SfRV#5!G;u(E_qC&02_jn@d zd%XqYa@%C!{+|B+_snJg=U87~muF6U)9r+Reyi2#9;VA7&k>PPr-$BX2F1Y$MDv?;CtfTNY-vU!p=dxw}_!6fLjiQ^E$Ge#H#bnTk@taIOD^hMejluKz zJbSLD3py1~i&UhA5e_YVyIP*#dJ`J>4`nZ|N0}U~!#9QA;Yrc($LGuRh8H~OiL@| zpT!we3F+vyOIomEL6hjOVGRf!Mblhv;$}Y1Af*WGadgV6i##>JnScbneuBh6EH z%|6)Sp{O~-=Y_yeNz2sbiXzHjEa2nIs0F}Z)KV)H0sYB>V0~mRe9pq8=sEH@qmS16L55euzc-0oN@j+&{x-C&>6w%HOug|n=XJa91}qTJKQC06_`y9I>`;8 z^vy;J1Zs`6CQ2H9pC3iFUJyj(jaF3C|1y0vPYLik7zp(7s44gQ%&ImBBBR>IM%2=I zZkRm}OO`Ig`n5~2a>;BgX)>bCq7qG|e2*XRbqoI!Nh_jsP;J(uNIUjFwim8V0SOXTp)ZdYtAJzEcJ ztFurLcU*FcZ#<9{hmOawxUQ@dET_eH$aJQaOUrd8dPogbCi+jT9X!w z8Y&23Qsg?7j1)L~0qt${#sjqYgY=1h5lkyrcG>;k>>nDLQFQ;eu$Oo4%Dve$@VVjf zk?XZu^D>2*8ItDDW`!=rt*L=mU=eh8r z7%LQ1y)v`)l_hw&92HCD%8tUMql+maMpsRBm1v}BkkhzX0)i|o^0CWK!&h!RkM05; z9($<^_ddA~qr)zA_6(zA*o}3IngwO;MJsA?-Iiv$oHW=!9K;)AQMluI@eH)a3PfQL zmjD6O3AzBjXf*5Q^$&FasL$5zoym3lbIfM5?WT{go}TLsL*A%u#Gepgcq$slYkQBu z;qc(P%g@Ix*IX!iIyghbnh#bA0a+fECn#zux;fB0BET0nn{j$s($F55)r!`}Izhn1 zStE^@`7a4!C{|!hq|4)(Vj6(n3wQMMeX(`ha$J7ZR#aD%MYXujUqx#g3`T^cxQEh>RQeIbHf7UwaAD%!Uu3*%YMN4sG1k7!1KnnqxmTGgkA(j3OkFc_o z?8h(IjLj<+!Aa35>wGY81Oq#sZD-ziRsiHKdeL~M*CUun!4ZlGai5_Hr}%8#Hby}p0UC@AVl$F!nkUm5){s+93J#4@IeI4mHZ(ecw+?mES`<)6=kCT&UxoJ8YCQe)>tbSv zvnO2o^Mj3Q%r_ld29cnCHFq?*zD@D;dF&SVFk<+DvJikdV9tcNn%+1ZUJ|XH_DtGB} zO|Z%&V_^#&JVIt%w@t)iR_0=M!Y(W8uU;wY|8^V3h97WHFM`Q$s zeF?m9+%3|Oy`z4dvAhM1R-;hBJVKYCH>(m-vpk}hmCJn(^hs0n7JskNN+0SU==oJ= zXUBUg()&}bzppRf-`D$4HkaQVjAy>?NfgFHDZa$(>Dd-2x;u(csI2J6{2xLyjLxjX z7Zj|Dtocw)&a^zoU(VbxaS~KTY(0H5zVX%nfsb8$CYCnWpsvy^N^mR#&5oVHgrxn? z5zL$-;h~3i;qKqQfFyxKx=95^>#*GecPLR-_G5KEawIv5SPR@#Nb5Zn&j^J|X8B_R zDt1g-!RcT{1D)pv0`uz@wqkjEBUa3+!#V30;o@!U(Y9bd-s&1gFM$Wn5EKM|Kv4h{ zE;Gr~84NM1D8645>TEfos;R(esl;S7O%64`UzW5VB}a3m!#UY8(`)+QS@($jhEY%8 zDqq0A+hgzhtz5{=rN{hhqtTRQ5Y38t%#9H)Xa(CPvnq0igU9&_$*>^ek&9(PG#>4X zghPT$VrnXi-mV@X8pKl%KaUrWIFPBGCs-d@?GKh}r1W(NWz`4=r$qk*v%K)^zYs8^ zHLN$%nJ1u;O~&c@)*z*;g}QY=c@ip!p@&tlV;yx^{)gTp( z3JP~V|AKFX_CvK1SUk&2XPCgF#sG!hhq=m%N&+(HZC;33^VgIT=6(Jr5Ox*7N6PNT5I{ToXdVcl}^{O=mSUW7K2JefWbr_tyMZSR2sQ` zP0+=1>Mu@?Nh3yvcp3m;0V!@QnOAEPn$FR5E;kv@J>*H2Hai31?~mA>4u86|A(NFy z$r*0b>(!R*DoVJ83ppl7|Igh(td1bi0F zYez#v6Rfq3LS2#ReY4XkW|nW|%n_@1apWPBGsz0YwzBFMuP`?V@}-@pXl>KH2BC1+ zGG_sH_eHStwIg_OSC<&sDotw&FkR@X+0|G*yA~_j8}P!R3EaEejxJvod5S*Fg6Dvo zm#zYijFwKk{vJuK+Dg0Urr!RZ!I@pDzl{wI+LH7Pe?=JV`d9%k_d7%RDdtb0bL*#P z#c6KNyi9BRh(Aly(=<$dTi}uloh$C*=QMpjn->gdZkHP)6CP~Yaw@i*vK-9~b*QeX zf|dS_#bOboq#V_AiJ?UMDU_8ENGU=*NDtfJ2Li&wW6N&!j{Lt`G)*3^jK9igaGFgeqE ziNs@Kq>w>MK`HkIF(ysZ@2;~Lv9P^K$R<0c5>X2L@1e)=DM_t)*5z`(Yu)|trg`iG zY1c^C7bwEsq$F$$7+>ViA(1<9LUC&s>;%_^*HtrY67+$gG{Ky6vA8qK-_GO-ZC zkuZ$KJk)XyJC0Almn@;NLX8a@R^goU&O}P3hu!HD&U8gP-Qh|WRxOuVb)wgXS^Kz$ zr-APAVmgMf-wVG#fGUe#NYhu(nnOUKMs(@3*2p3{tE=ZX;PeGmFcy-S@PtJ+Q9CX3 z%mA*2K|?HMrp`pTimghmSt+41WLg0ZfGQXl`kQ zfdC|v)pyiaSJ^9$V%TgNgt^E}c%qa;1{N zWLWsOUaJ`g0RN};`%+(5lXqA-%lw2S-q{}r3)?EL6?a|m_kSsHfj^0vyaP*i_$ zvmBUtEZ~@N09;u|iiC6JaYy zm@?`sL|3t5pGxlz%i7b1+`$`sfxtWS+9M8cZ})`%e;gb3tUA`!KlR6+$NSB?dpdJ{ z13ho*^@i)s2JN?7TN)>8m>@}8ZvNwolG(xdxiYa(B%{DXi0X{R^_Xkbz-nNIuyU5b zLs8=kZ+7F}$KS%yqc+T%HwRZ&xg)hjT^2!i$H@4V?(2OY)2Q4*j(_0)8!FFdfz-3 zT&0z9*@jlkYpg{7WEc!k4UG() zkQe?Rvw?wuOi%y7A-dGA$;iqzj;YjZ6Ol|o(bHvEUqjD0TONZ0M0tjzLe33y6Q_pN z8r9`&8)u1_RLkS@Vdoof34y0GPg{>$Z@v!KU;QDRvuO?HG}WWlWI%0&Nl>P0j7Gut z&y>vEpvLEuOL!b1a+!eFH%x$!8Ti=w$LTc$kr3;>)81pAh@N2wT(rk{G>nxyjb;<= zAq#;31DcwfF>k>_$Z9i=^-sX*iwHi71c5{r^x}rCY@s0d4ve&?;)#qHmn)CBmW8QA zmV@?UFquyWlex#0lIjvkCEe=vxJO-1$A8OL@?JORNLO#AUh^=gyZK^dx?^tI9-#BE zc3!j4esBx5QwTvI9`Oq#a-#g(Rzbill|-?SEBAL4C8T08cwHV0xD!xOfM;r32o*VS z?k}34!0;oNor?M@=8{Q5N5KNu_$@j*mkd6bMx4Ne1zC&@kAZ?O=*^XAsIL{IbS$-P z)f6CQVi*{4AV46nuCiRZF;N80LEKeV>Pz6UUe!+f;}ZPirP6daD#`#vRTL7JTpNsJX|ozB8d`R+AJXS)ZPjF z;q_re<9VF3c?E8~;v$^4b(N^sX$VR4lnC>Gb6VMAsUXB{Mxvk+5h-UHm|3l^p%FC= z&8VoV#}qjLPT_CbupFPg`EqPozZ7wD{!i?8Vf)S=*hU>t@>!9-<>;nPSHjh+s_^-X z7Gq>GglEa|?;DRJQs72Hvl!*#hA&0V^FAFVpmT{+gSo&dN=RZ2r-SKqBdhWJTw`5MZP=JV1Frk%6*zU}LbSIwqOrLN7HcJp^t?5+w$e0hG^bLTDm4^|isnK7 zy-fSdr6BG#;q%W!yI+KMWSdLkIIyT$#( z4UG<8K-9e}=(SV%yomHUBgADb1{d7$B}xL*_Z61`GKwma$|=94+v`@3+jYS0J~5;I zKW4#LdbNkpH955ddY%4TCSyf7SkU3PpPmPWYia0&_5n|RHBfZV;?_@zM7jJdGikw?Tj8~Lghv7ajm)D*HQA>6}z4EZEH zPM63MEt}tp6${$%_+bymGZqd|ikV!##KLizY(8KyYF>J>bJFyc-@L@M@9#=O_dgd*MNIkPuF{gV=(~TE=V7ar@8A^%on0dN@ zq6sxcEPj7TEI~`9M$p7^u##8GbN&!gv?cmJ*EEYM`kgjTD`v7;-Aq5r_qyHn@N=0T z{QALr6{y!YwY2=)XfkBG@w&4kZm%;5VJf}BAbL2`90l@z zpbPrcQx@YZw_JfUx2}ahoWVmcbl`z!4#Dm5qo~pgfWie`mJ43ntj8sE;Y8_@`o(kI z=<%iylquBI=;;EmlpTvArbNaiNxF^p@hOADwjDjaT_=2$_vct|U!Pl1ik~8Yw>X)} zJncsK`$Fg#whOKMxpUgk(o&B%4|b3fIEqIf z-i|@rAg;gRqxjUvFUN*u3kWRKqrQefyR|})E%61x8JZY5^Z|;z_=4qr1nwb@C9|UL z$}{=Rbk>;&ir*8Ozq+>*9Rn^Q=F&MZfgphc4jQ@!$As$N+_o}D z|B!D=aD#ByHUnC|3vvW35=>#u_to^2LSCU)kkpGbI^9-2l&&OYCUBhEH93OR= z{Nc#yp2?vf(FM6kQfnVnbd?2X&Lrra`Tk}#$Uue()4~+FL(J8elrqaB695W&XXbXu zDPqEh`$+a5I0{#PC&C^#e*WvH;3!xznK2+HYcMo9CF=GZ$+J9nX}TQ1w7lG07p1+R z$`kHRg%Ng)3Q;d+GSp6Io_Ly!`#@|HKKP?D-8(?@+_^%bb7`Xy*IaWpF2C^;u=n)h z!GF60FYX<}lvII|wVnbDvxo?p7h)38W2hNVju&pTT-ReZBy-mLb2dE&sIJkS4^+ItcJylq`W_Tv>j=nCN7&ECet3(RROS_f%=Q*vz zjVFS-7E*B_nBC`3NCmmLjvbOws-b?CvNoUR8(7);0>x|1^U!NiCS*ItQHik9%InZy(<; zRJ1;M#Rh!ps*PASuZ{pkhA#CKT_y^W@g$;jL2#Oufdm7NjZ5nB)tk=4XRbY8FvR`# zi9@*K(YIi;`vq9!)GlA%4P3vRRfX2t3c+3Q)Db6M8JI#aPf@<4hozztdRqR1EO^K5 z6oLMuS}Sd_4fXx9tGDxS#@BwL*}%X+sjsikOMv>r;hg*xwZU|MG%JUh?l>(gLdlV9 z|IF;iPI>*JIau6Uk3=d3+oVruNnC#3skr4+*T734|9IaJ_8soRV~;$8ib^v+_R-66 z*_oTLw7mh1)h5w}A5CQ`2ryxGYa`klYS3O^Mc`g1f`*2MI@HwG3P;Wjg3K_-%z}DR zhGIuxqV;AfSp!ZvWgP|yKpg4p7y5w1lP-)qJXkcR9aYv!jQN6aOvMFXMSEQhDhzZt zac4V$4K7>eC2}xEb5_zCU(;wbJ2Uy>PKV1iWw+Z;$Z_Al#2lkNVdrS~r{qFmqfT$! zr=2bfaE3&sl*g#OiJU0&Evz#@Dx^iW=It*QbK;UrI+I2rm4pF#?09Pc{ZS2S=C8oU z^DaWm;*}VSm5?lH(PY(A(54oX7d)|5%laUyVmXMTy)+(33l>No2WIs^Ih#dmg(TPp zD|M2f5O&g?6U`P;tI6VHA6kQ3zw-rh*cv?fi#zecE61?MQ-rUe!@7kH_{cg7O!)+i z*$BRN!c9MBa5s{QE_{H0*jR?k>`@5_r#t|nsu*_?!63u~mS^165Qg^mD zICVs?*LTaM(j9rZc>hd4yA#cRbjJhER5tTVgQfCZdZu@2HR>!k<}qmFsR35K;<3TW za2l^3bD&5E`;yg-SUcMaBOlml4sOadi!mo|YNDtkOF#chI%Ss`&Bi-z1O0zPybXs^tse z^!w2>G=}G%e;u#B@+Ml_XW`OIF2uT3%h29bL*P*>`VFcpO;|j;6?0lxbf^Ya3r`#v zgu_pe)Aqy@NbqcWUKa16iJbVHwpP^DHwi9}QM*%6T&K8UFqslrrM9L9jC%bTaJWT! znnxPf%%4ThzdW*Rq}MKR?Y{(BrP04ik>02DM$?D&2ICdKxq|Y8U^ub(!H1vtqiTKc zww(|F;BA{KR_*L{B(C>FvUm8R*~jIQ^oT*HeVv?nw%%$MjU)>iE3t8YEnOBWAs{m5 zOQNJT(q__&T9VaLA#|R(8Hm6nEQHeAYc1pmG-8%KNhdYM*sEA(=c+N3sYimz!nEUu z=rHAaf0!;QN0=fOS;)yw4Gs;8l4@{Kz=NLu5^6==`o03|46FJDMB}2vTyh@X02f&V&C2tRO*vNTXaN||yKCbsZZd#%P##Y`%o#duP`u|(iYR!iyCng?uyPQdXXeu-5 z@qA!2LGPF%2ieiS4X-&U6V$=-8^K;TkW?ApX?CqN8{}v#@u)u(UVg`K@B8lK zPd+uHWxx{(0QB4XO1rzp2S!55m-}7m8wr$$=hmAqZmH7gV(C0y>2yT8#{M z-$oI7m6$26ud)d8h^A^ibXrLu_X?w4q;9!(UzXx6iZ1jr1#1blCZ>RuM4gxGKHO}` z%|b0!9zR6Pmz3|!$;BPqVNOn>pwVdlH<2E1AgFkJ{*#yfUuoTao$h{H_f7Q2v*;N= zC(BCKpHkq@X@xQ%)22=-T0GU|$16uBv1U#kZaaS-mN#gjE*1n=1ea?<@eE#kvl|b+ z+=sZj3Ug+)2?3EBiwW%wwL+}t_Iq}sYb=0BDktih*>+ z%l>LKT0!C4Jum_XMcHyTjZ4ox9krE4^o=>O^FTKaAMeA;#q)9T1!u5y6b4xMXJ`yN zU*9Wg{b!wZI)S@OXw91i5ziQXv8=AeTtNYWMf3<&S5~6Au>td1n{dwhl~}iIAwm%X z3z4Yc2yl1<^m>zMs;kEQ_Gb8_2{@*rh$oY1wN_wBTeJAPXfj0r#Utovxhk3C+3{Sy z)KuT}#Mszq>c4*yA1q4}m~uoiM-2wU!_^h~pKv)gUnn<)GKWAs#~cD&;sX}8)j_K_ zh%De9n-9@~BqXQv)6JGVWsy#^nqLYXT?5$l)-gz_5UxCL9kfao_dW6k_KZa7-<1TL zAD?s95@7xb7W}f-RHAKm3sTTybSi@mR}_1W^dn2a;@P_&AfOS)0iO&{B!f%WHsO<3 zZ^K~k5Poy_<2d!~E%?G0KY=>BEAD%GACAOm{Y};Mdm(7WGnuSlwbf_CKYi$tC$HW9 z^mBibTHm{EClmm9+iauu!kEXGkqdiy2~5SX{f!>%>J7lqGzYLY z;K^M!yz*8*MS2m@k<4E_u2rdFmA)EmXs>`Tn#ME7C(#$mBC2Sh(VIln&litEsD!rA zPOVmZsky>*+0e+~8$Es9C&AxeU`LJ|@z&SYd{dItdzmF}Z`TkerjlYpW8CS%xGMlT zm&Gj~zk`l1wBNyYkOV1I^e%nr2j7v^mjSDudM1{$O0Rk5(awfje6!v%ZBScQ% zYyt*L=CpwB9CVF3p;2V4oj;o#e-#V{9fl@7qLf%ySA$W1#AOf0Ud_p+OjcIxvRpjj z**)J48=RPYq;Jx*$q@)$pD$uz9-3%Hh2?l*dTfw0LEJbQlO?<{5*E`tJTfY4jhIZ9 zd0@EgC+MF`iU4cOCdjnV`D6_FDL-C&?Es$d48gK&3##WXMp~r@y^febVV1x|u85

l*C=h*nvRnH%_KVox)Q|o8f;IxHxf`giAg_--?Y%EjJ z@(3T#eRG#6UnbE^L8J#6g);LS>sVK4Sxlxl8A>bP&MSDM#;Ccp(JcK(F0Yhg8Tlte z4)5zjlg{^yOz->Ey1F}xeFMF_>0sTUR;$0D)@YnXL7Q0;WI!6d8ChsC8ddO8mlsF- z9k_ViEL^#!0s2x_%=I(nFtfkeodF!_bK+316ApJsu(gSkSt^flu5K~n;#Dp5XDtp5 z2hdIXCMK%|vFZFCY7E}vk8JZc-k z2!W233)-=5?J^{DdAzv419aXor=bdGtXqkehFZEOa-y`*NRB^|FO|kaiPuV6{VA2! zst?hooR`WgtB!Ve-VuaenAVE?46oLQN<|c7R#3EBr@)^NFa^6{M zgi7Syf7pTTgK0!`b+FXbi3UNwkE~Tzg@EBhl~(J9Y&!Eo2HN4;ruz5PnCA%%0EQjD zNMZtsj2QhWD|4v?aZrk2XHsTaT1h5Q@oGF@ys_G(Db`zb8&xP-k~#TXk+gip z;&q$*X8JjN(5%UZ1kxEkiGcB5jdA&K2qt_cabbvFi;D{@YyI&oE+n>M> zKe7rJF0Dj+tr1OTmEdVmie)kj0xI?CNMl=$7$=92eH=IAI(22!SGIqlqMQ2u)4F9}3}U-!N(_E3k3d0wFoxHRixuU4w$s zx31EHmiij{`4omH-C{4%9x1tEsZP2lZqcZ<9}W0?A-Bg}^7{S$377MPq;;Mt^Tx9u zO{t9LP|$mGg+cW#7RwUkZ)%O0*vU(JaSqw}v*YIpFs-VH*@gldw0WG;LeAVEqY#@C zQ(Zj%%hKSTy~Egdq?a(65$9iYA-0}(9+s?IkGgrwFn{ePG`7wX(*ka{2e!dc#N%mL znc0y7!f-5&Y$SvwjV3H#(2V(QwRpY5g-4H0A)u~9ZEdZH_zgy8inMY$qCc5Pp0|Db zvo~~h9PK=A_g?;+on1>l`_t{;n(62JzuE~60N%FU9fQ8@`)&VnY}k3P!{vSlY4?r0 z6255mcf*17hk6{*e;;v2JWHA@5sc+TZIJUvdD>h?+FY4@No*t*@~PBQq^FXUoLt&b zD5U1dSjEEGq*gYP>oi7g1_w{1@hn857%CNEHXQ|%BHELY2l(pX* zF_p+-+!qz9f!r&?4nRYZKaVO&Dpi;;!!HRltRfKd%f9};lM(;BY>tTuza*({FO;N< zGezmU{jRWkd@2ov^&Em>yt4bS7`3|S^!2#*(z8V5)?pjNL(lEP_=FP+2?(6Kbsd`P zE0In{uz$}!9DVC8=vkdoXTagkL5vIzqf)09Kp_~3Vcn9sVp@PDhY#7t;c)x0xVau1 z==)7ImEz~(@gyoL%&g*Ok7kvhCbIHnPM7l@r^^wW>1TE#o6qM_oQ?@`uHvcO`7Hdy z={BuSKcJ=c%CmxGzEEx!l*@V^btW}|do${)jPUyWFra{D`m>&*aV8OB;E*Q3HWiNG z;Nc$Z*>@BUf0WL89?^6G0oq6QaXTE7PS__L^d5rZj?mLrWrYciQW7^^yb{-3umR3! z4!b=PautoRR#%A$GZh60_4N&0j+{!Ra-YBJ-UnCS|NBRF&*+2v6YPBf0RAf*_xcNV zZ_wdSwU}W-HF<=;*>=x>Z(d z8}~%v2&II=q!1mNZum4w1=O1)jCsT8pYRH5&}xej(3+5uA&7xwIiDqe^NLodIkmdB z>Ys*&ZT^}5+#e(x8X77M4Gr2PwdRNUg8aEqB6G|eO&6KgxuLEWc6R{Bx(9{6!!;Le z!!;k;CO98<9kF50;a=ofikTvM9v2&;Xg)9%734k9WD*H-FkNFV#A698Y-^&Ze;z7K z26T-&FgWQE(zVOmn=!AA$)2*9@J z>Iq<&gzBiZrkYs;^YLWr?r1!<;J5cYaK}tOvp>h)_W6iN0K(o_ z{+^Uv3|Con$T9(ix`aZhly=8+zoAIui^XEmT%jp_i!PgDB1?`xA%Fhpi2KOMEO-lk(5mC$l~?WJ3c3rSY+m7qn_=OFS)q z!kbdYJ0}A$Sgk@hrF&o;hmQ4wVlrHP-d3zzF&~2yPCW9)Q9O6h26s|{sltLua^fuQ z+fZc{S_7Gqggv$ibdS4XoAl!4Lp{Pt%xkE|rsa!ZGMUglG9gsjtd$l_C3BPBc=l#U zn$;eU`%S0Ib+XCtU9g^^;laM~iC>OQI*0kcJ$Bn&N%N!gVtu=7_X5(RuRSJHf0DXD&+$QQqqDM~+x$*Nn9 zje2$uI=v@HcuoN8ALz~72Kx>V4E0|rX|O`0mX7eaCyxr{=o?u{hg4B5nEwO^J$+xH z@25*TA?M75L%CvEAyBO<*>W1gDThZUwhRo}_V)JoWM}&GowTyq1_tE*!NDg~YVB59 zsh^Ao63Ibd9M;-8!RB|ABD-PxBrZPdRDAEBZWP2n;aFVI0Ot#NLD9>R9`}G)jC#~p zTcN1681YBZKjA@xwE}0HvJ$n`RhaY#G2!tE(jRv?kq)Ht&(qy-y3^^r&*hvv>G=Q7 z=C=1GoFiSgRv9$cID;{$==>&V75b)1m|JHQQU0;XDEhp~vRIp1L9LV)3WMoX3R!aa zy#D-mMuQGD)fQA%ngw-lEa*qO6vOxbVJkj)$!5AIAhciRG*;unHEr;O6YytBsIF_^ zo&-lUp1$_iciy+340LUtECUeSnq1JrQWHwLftD8Ga27QcENTre_ z)229Xce--}lb+P^5!Y{y4mGyro{xY-K`fX}SI-?}1KkQ5tdUDXpOg+lY ze3%v+Vq=8QUArb`Z7UXKgHF?<(`v6%Yc(6kC&s&G_EVg+|H_7jhrBwy;hPlET^h?N z`?@9r$+)b7)oMj9U%=BZzJb2JL454uGx5nQ&!GrWMG>Bu%@_2q8c7A_8{j5VIiDqG zQNo(JEm%2!HUi-YyrD1w1uZuYmPY)Mj%*QEOB(GzIGoOt>HZnEY(8Hj2zH7J^8+Mf zDhq5;NicHq7X$h7>6Wm1b{#g)w!&HnBNg%^nM{Z^R|!38MKlp=lYXpfRAKWe^D#O$ zh6i`{z}(md9_Du?fmcUUFxE9QSyVC@3g1rqX5n4Gd*IQTehz<)eV_oqzCL^GvAw;w zA0Ku;IA)(L)YjF?dAZn|%E@`c08WYsPIl|Q&#ZT#C)YRF`*co{&ZKkv)lyN8$`k=I zhX8l&iZA*z&+`*N3{7PU|Dy!u4BKGuBesFwll$QWu-@Kod2nE0PqA3Kv>+F6c7{{I z0dI^V{Sqt{798st!Qo@QAc&8TUwjs%Qn}$*2+Z&=p#N^Nny|dB0juY=2@2p*r&ml0 zaR$H}Pp4dwj}U9CD^%&}p$@em$1f(7wp}p-u&0kmE3>gQ$#8;A0(ScQret#RQ`x+-^qvPE`uYP8K2(_LXYkkB2MPfEpZ56UPs(W-U&!Q@ z%|(@TWo={gNv`(ysg2vmqxOlhU+DC@ZIUGYj4u5&i;A!SRIyZys@1CR>kNj|h8@n| z4UG()yx>nj8y*~p+4}pQAV;t*D=R+^W{NpSFiID9i*WXx{UZnjLV|scX?#T&xl$DJ zz#6*fTgdUpGdc8+ImCD{>k-%k(Zi{{a=zQ`xy9#k1!wv*p0xk8P6B<$M(iJR27}-A zdnO~E$$@XnN^zm2);(%8TSS_lNrRa1CsWkmXh@BoKmo1I^*D8Y9nP6=!TECyxO7!L zHY}*eqfhO_!+RYF>Kjp1X_3#-KFMYUWw^SK84Lx(=iG7UJs-L2clW(-vOgba0KoS8 zk-pFyog-ZXc9;Fg@s5+tUGH-n80_yG85{hH&Y)Ya(MUhlXf)q281xGVhi%^<7`Bbf z^!qz$f4vRZY(utw+c$I2tc}X*7i`{WdMYnr*4()mb9>Q0=@yrIfm!Ah1<9O@on6B? zHe^SEqQp28wvTJA&UIk7CNyDk6QRgyzx$lfg07H&1?8ONpP|^WejK zXZrK~t?dI308ZKm)B5_lCi`tYU$qVP{=03U_auqW`_TIP`vyay&=qo_c%?6zwjCP_ zloX8_+t#ndvNNK0kEVnm;Li9J-o63lQ|blh!c^+hX8JR|8+H-^oV1g6(%!e`^Z81>eZ4znS-C8q&wtqwPEL$WN_kPm;Ive-Kt_a35oaKp2YW1&mIzyb9{!*!o z(E8vpVHWZ-=yXsCDtHN&N-K&`g;J^H7u^o$!s@EZn~R2uyK^Pg`%>BUBmg*RC+(!Y z@2#(|XS}z!=jVlja&9tPc*7sh7GgOCiF}dOK6w)O78$B5-5&R=KCe47)9?4B{q>eC z;ETSzaiPXs(-895zfdX_7pYY0=U8Dflg%N|*#V{lSF2G|Qw?iXwUWwY9!RD$3!Z!K z#b3pevC?Zt`|kVoi-$k^wYy$?W2QgPyJ05*z)3r4C+!1cHe3I=q9_;T3-X5}sk|+g zk&mOKI@Rm({NCenpDYEvk8EUYsMtH$KW3j86N<*^k{sh-}~Ro{(tn3XiIWH$jkr$002ovPDHLkV1j`c BiB|vs literal 0 HcmV?d00001 diff --git a/assets/images/game-icons.png b/assets/images/game-icons.png index c12cab3efc46405bbcafa3b0ace6f2e495246d37..05694aa5995a81f8b9710156c74f9614891c0e62 100644 GIT binary patch delta 72764 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\lQZ>#3Tw0#1FWRi({Ym}-5=UA*?;-PCoMPv z?0RWU-`;=M=*pFA-h&f=hz)kH>*kFj_x3`j@J)I&Ud7JNUqDZDGpe~b#&fyv4Grx5 zfj{JYY2*s$cGt4QBXgW*pZDY=N#pVL-S&PZTfR9$@=%j_&J@J!&fd`b)TSo~Ueh)} zrBW%m++;{+Q>e23UtT*}Pg_VVmst3dFJJ{+cs^dj^h69kw+AzSV)w$&q=IQqm6gje=jpq?i{RIS#+$Gc9R z=lI)i{MU~Kx$&z-wFEOW3uP|JS^IjF=$VfG@g9uCVu))F#B&B51hRZW9xl>14;f0{ z8G_vrM|h4M?tlV+f!I5FMN_srAQ)--Ff9<&9&*ff=-Hj9#m4BoDyoSx#BvgFt0r$2 zIo${t<()cjko(E`JkMPFo$p_?cWC&3Z5-(xKlzUj)U?uvv|0t*cWl9t;TTkwT7x23 z5!uqECu3&3ADP6IH50cX(qIGw;m`l*j-Tv*wb%U3uYTEo+1%XvVFVO%i3Ew>iicmX%7i`iLniNyS!E3;C>v9F#zS@_)>t zKI_fb@q#CR^7d@L$|YyXwxr4kxJ+w`Q&T`jdsKr=R(5A5i8%z`<^~TVm5swL?}eZ( zgGppMR+OM}c1YP00$c{2b6E5}0yMf&BSPV-nHZbOTD?~@MA#!fj|DKxsbgx%$yQ#Q zDLAa?5E~;`BZ4ckf?U3YN{uzccWzv{Y}rq?@7iU5g?R#j$i??Ouvz%i15ZP&DOl3G z244wkHG$AvU)E|EQ;S;X- zUof42TUtrPPf{xdRQ&{~3uRQu1_X`{;4(rzV@|Rmr(b|7-MkkF%*M$l#+y2=wl8*d z7rGz6pX<)&f3fW?m+v|0H+TR4(pPYBXvnOzu9%ukX>)pQ64sHedx2zuIiehaL@&?! zOzQ~J{oJP;h^6ynGh_s;YG~mlpHU<*XQ$VH+RWPGx#i9lqPr3YzrZHZ*o*N7!Idd^rI|77#Lzc~BNnw7EP)oE*qk z*P?RpXUH7MS)&|T2O`PFiX%A?hbLtft&T*BhlixFL}Brzb8(z*qWYNa6yvQ0dN~hI$yfx@W zI1sTat-#eg+_TY$#i@hVO>i}IVRqzyAQIyP=vr}>HK&`69TFrzN(3SYMoRQL9j)yh zq=*SZtvXs3oepQP8RI)1L6#h3(}LCXjEGTXr=j~=h`e~MFgO!W!tjU$LdcME4^B?O z-V}sNU`g?JDoyJ4i;8Vsk;BjZ8H>ACpn+9x5fc!Z;nUc9?dY4^iLZV8H^hp612|*- z3beK~qfj!)FmgyFGx*^9-;7|`i*zE5j*cd{9jcY5wejqeNl+Clbk3r&Oji`A4ed*s z;PIOn+%IF0fbMv;3^}Y|YHA4CLIw-Enpa2M-uv5I+HQRIp@)8S+QyA9IpRGBcK%H= z`xW2+qxQ+g*M7UE3&QGeKUF+`d-1|rHJ|OzO4GZHzOKHCEZE(hbHNd_$@lMt5syK$ zkx~Rqx)%aMixn)}TE!#*@OcC#@7i+!`%_^8*iGih#4u;gJAJXO0vlsSo}sm&9b-rL zj~U~oFBM9a!f9a`73kZRR1m zZ_Ul^=xI@}4M&1pE|v10cHJ4|E6ef=v&r>)?%Vdv*G}EA>9h;ajZ}9(JSBn#QJq8&)zlFOzbUQK*t}-M4c$5|Tzh zokxWpa5kAm6OpIvoGy%i4~=8ZfQeWPDZbVbq*k+aoAx;TVn*S z&JfIOh0TY>;`2_iDrcGSmWnLDQSl$|I`99_Z!K#RE1$9jMmeq438j_v3@b5o_ck0p zG>M6Vh=L|T7g_H;5k+#isge#GSyZpxjb?`gE}|k~I*~qe z*@|YG*Hlf-T-jpLvhSTW{$hv;9;!rjbuuKzw+bv{S;^JQ&Cv7;_K|Z*9 zCdIaIYX}j6+&7-X^g|UaZBcQ~GB=2SVgDqLoH}YnZaRHa=x$s?yg`9GXv0j^jj7op zvT^!~7BQ7jNgjEtWl6&vFBXSg$lTwLVyObZQ?~LkvMd|MML*HKHOY2cgC}G*xkLxU z1kaBe=<*YPwB&LS2ym7v_LDzy{q^CiZn(jsoV8jdl&^3|C5f06XW@5@L(){VENMl! z(TPODME^hxHUh=0kO} zPp9c~XlM_2!0EJE?dei8NwVu4w%_|e4UVPi}Ue_vsG3)&y>-mxOLYeX16&!k2vq*~K@Q@rd zz#eWvaB(|)a~8wd(hVB{EyW!K&)NyG^=cLCJoRZf^;5FyZM!!Dd)q?rORhp=Hcd7x zhQib!N|T3BiI1X~APg}UC!Cpqx4DXjRbEJcEQgYvL~xNPkuO&naQ?^qxUsQ8OlER6 zHpWqwnU9Z>&as?i%^F=gH93H3;%X@(W7U$0*-Qo^$wX&Ugz&UZ7|4WFp+yTC)||Z5 z0->hCk^(qP5lNbujf0pY;UgJ5=`!7|rJd~^Dgnw9nxys5dWc3%H*%T{hT4D}A?gKx z9w)-SV0{QX8&zjjXtAN+Hbmy0VzCBi;{pO&k0LdF7==t6O)U$o|Ff#pgsdKxpi zGC4p8LXAyOY)-4mc`-RjMo>cgNv9#wdz#g+syy3o^{8a;cP>e zSqvsBSl(nO!BF7yxEmV_lNgBffhEm9Annp4hB7qWA#^%e|cAy`9ts#5Rp?>Ar`<^ww@cwgd{lb5LKJtpW zm0#a`TjKUuix_ENyzZds{eW}z*~@HA&E1kyAf}T;hj<8256aMqNp5=b4+!@}a8V|Y zP=o;e6-#H-r9wo`IN^Ax`;w3{yV z(5qQrsZc1e=$E6lk%-W&l}l##d$4%kB8(p#L?O@N!X+N?ib_T#VvWszlDz?(d)gu- zjt*n!KW>NE6oQY|eEJy+P)-)HZRY`Ez$zSmKh~eU6n3{0vcpNNOuz(z(F&2WG67eo z&2c=b1Kb&>t+DlZowq2J%?=|&V5+Hv=yV7Jk8VN#Y#zhu3h4_mV>{^}(Qli?6Qh;j z_6S5CTu|&1>dC?q0WuqZs*EsAOtJ#67s_3zj}@P+Yz$%zxntiR=3-{|V; z>-nfZ;C~Y*7#@zRk!hjNNfzEAYgoCckwjPQDwc|vm^=!eh^Z&oV+NWUOxCk=VsZ>y zw;d>?QY0;UHFFgPnCA+j%#BTdPW+S=Gye4<*;P4Hs>-Qc1&#EqN7H4Cly!K>vXlz=({PG>8RZVM>lO(bfC6XyR7pqY1>hQp+5V)`_ zI5L(-rmS1D+(nXKEq@FU5Rw$FK@?TY5(wBnO~hF$BCOhfq1yDI$cw%7LJLsZC1HGC zlLNcr0wM%Viv-Fdbk712KiqZ$JEK)3-2{TkPFC_ow9>7~q>}^$^T?LcNRfSDz{ao| zUMp6ipxaI73b@H95YDC7IEdIsh|DSUp+$j+R5n59b|6l+P9yu)-WDXM&mZ9>77j3# zU?9ly{!VCrzBW1fWvrIdDF`5P0li$S@02MTX0ru29dteNDYf_%Tpl0km<*B3r%=p8 z6BTS~T#mNg&!CAH8l9&seDLA-zHRxrKl-o#CzG4DTyvq%=Xb4D44fn^?Ahaf__=7H zSP16^C%L4i88wbiND^;TZL+FxoanUKOt-9Xszn@sMC>GjtdK_4SX8=NL7x1J&+Q#Q zYu}-r@0dTwNB6%m>JA;cd-ImRuXo3QUp?obV&zBb5HZtlAh7rhEdS8WL=HpN&JFYT z9LK%%Myst4%dynT(5ktYwx$1)>u6dsVxwok>H$RA2079SM?)uk^G>cuM_In51xBmL zP7kAhI&>4{7J9vrXC#_ri?Bs4QSW+i9QX9%v(56Wv;(@smOk!?vQv$*@ezK4~|P9nd&5r5vjk3jH@HMvb> z126!qK{_e7`rhl_cItC4Y%;-ABHth)Si1 zQfiz4)e?9+SJfY$UV%FlLRHT}qu-h1W_MB`R^a!C&=n~`5>jZ1G(lC>`dCI1xoSBL z*~X#}WHhT;Vka%IMHX41Wv!S%t(b%w?zO7<3=&L@z{je2bMU=;A3|62dIU{qQ_;kK z7u{0r-p00O+tAD~mdw`RY$`w{Ku9pqs`L(r8?b7{d~yhJ>)dB-ScX_ELxxGgHCJu4 zCIPYB3swcrGC)FP1k2?Lv1(#C9-GB}zx|y%Ef#Y9+s?BX&gFJAbhy#H zKS~ZThqO=}jw)_tWGvQn-;U>87oX98bQf?`NxM5Xo;Zs!d~nl zogd4T@WNxypwa6@m$wlYU2!fMIabBUs%AyhN+oNwqwEl^=C+ke+55h0FA;xu+kJmG zy4Tg-=D7Z%b1ron{j0cQ7N*mW_(%=IPwv9jDHCI&1Cn1srlMP&2qFQ05&66ZAAyAS zhAwcj8>-6#huy4i@i^R2$WRqYcQdIX3HWB-+qTXSc!>Z;3bmR8Mb1YCEDn450OVy0 zP@SA0R-b?tBQi*avfx&4-=E7Ly?E`~$Fy4co2xIo{B^B9eDwZ%Tbo)UcMlxg#}~^S zRCka?x=36J6q5s}kYY}MOc!A0*$6iQ-JQ(@P{LZasJ;I;_ul)8y7b?Vww4y|#((?h zMN1aV|96MW)>f-zWz{Zo#H3i9nKOBVv)SAx`B<}7E#csC-CL!dK%@>_exwnvFxu7hsWEa3Gh244#SCpm5bHtB=Su#>?J+nrUy ziB3$iVu@@Sev;LH#wHcHxQ+tpM}>$p&mw}XXo8*v$u22y4tXNiM{=|9+Wn~VD%>!H z7LRK+E%_<7Zb8rd>9j?i-&*B0)h9R4qGU};BSJ-XX{JEB;J3j}`aDcTlP?odN*0hU zR$#L`*+wAw1sy|avc8-QSuioL(Sz>hpk?zVB1b-F5FR3bdtMtyYs$hQ6PYLdGin;$ zEC&=9ou9Aiic9&Sq>6+sxNtBO>Y}B+Vm1BW9Gafh*~noQ!D9Zv5bBX!zZW}3CoxhM zQOQ-o*Ccp!9zj||)fC_s2xRC5NI^Fa1^t-w)-w_4i8QH({NUY7Z#wJp_kQYi)1BX6 z`)0l~lUh}O)6DTz4?bp1>DhGNdMP|Syx%;mHEOihV*e2!p0FZeHo8YA*+hPh#~$L? z^fj5c`i~}!DyPwBRTC-L2#gJ8St=RX69UkxlNH}OQG~hr`Q`Jc9*HKDW>N4(dfoA_ ze(YUab7SLsUKopSuDN{A|I4OLf9V;_(teIr$5^9(nHi`ez}m7<0@vteQ%@|g#{x<$ z+N|qkE3dI0g*1+VN7H(*u4@*Vvi|lW?yM(zTGRCOS{W~P?qJQ)*$@ey>@ORtl9(Dr zHa3EWmW_~``=CwVPO>#=jeFEc9`&^Lmvg?t;dH9P^w9y6^=U0;qIB*$reLA(xe+(q z-T>Kuge3VFvd3hL4b~Bd)}xqZlyhkdQ1WUBIyu1@$<4Qa{3A?GPeTp|fvR9FL1U%Q zL||9|P3FpQtG44#k@{ypMA_lyS@T;|qVSO8N$focg_wn`vW<~?@2FyY$jY^FN9I@$ zzBD_4TJH=xv zR)%jnPGGNrWUpjJIt6;F?|=832_%-RP`}4*C!nYjInco|w8InhELU-KGHQ*=8yTB_ z#w-EOI04Fq3%cPT<1%<;92Z}--s*eD`nZLH9yZB_)<&!~Y)ALn4m8XpQB99JhT@rD z?5fDC9ewVO(P^{!;hn~(R(6M0Y`*o}x1I9tm(S}7cw5eu7q7Y1vEt-bqxc-!+R|wB zxJ4}KxNoXPDR0gCm!61^>a@_L+vo$5Ilq)ACI42??8bJ@|ljy z(Xrf%p<8Z4i??X8!L!2>o;OzDbKkfS_uunt^fzVE^ud;%z4zmZ?BUo)Xc<305n<_A z)FHs&F3P9MKP;uP3VZH`UT2XnX{$9tm8WMfVUDO{j8xR&axR)pq{2=+tI^bdZYos@ zM>>%hL8)p(O9PK-a;~Oc@SSnieD3i_51w$pK>%Su zp1+k%E3`Pk6Gzv+K5LsgR?YwL!SuViibrWlL zBT*r?EXZ*4iWTv1>Y8gA;c6iPp*o9XUL^TWLXw~5-HmI=jvLTR8W~6qUa24Uf7SD8 z`F0fV-_4n26QY}pn}+~wEjRbVuAb*h#r!R+-ub~F8y zMIwO>9ait#Qn?6~Kn5Fnz$ziL=^SPzX5giJoyqy8odNTUPe1eQQ>JzLoyr?xjY ze<%{McL)+MmI@;0aQiu@(~WXIe~XckDRbYxZAiysEWe6t?d~zpK4+cDn;A|J3<4)^ z^zRrp6KTP;+j&=|D3om~b6mCRf@MoDD`v~{n<{=`y3k$7AP^wEWayjfk{S^Oahu(u@@zA1~G1iJcY`Dt^37g zf9PXho%r52|NX0ekLSxPe>w$jtf->okdTW`VAj)&vh2XjbDNNMbi2z&iRiu~ZnUi8n}NRBgPqN@ZfStKY&Iw#`5_<)rM$Rb_&##UT( z?(%Zy!nRM_Bkp@Sfg>)(pSf+vwi7OHs8kD4&aPsNuIa2+$@TWwxGX% z3Y?lodsl-600g}PmB1~V=Ug=$2!~{3&b|yYEEtj|!0hFgH_8U9`o8f**YfucEgHX6 zK5gmO%Demb-?jdfPhauW=Kj`P<~+WpHP%!W!X+D!fAR(7qX~G9ZFMX3Y@@8mCgQht z?NZSUqzlg@-s#2of3I$XpJd6wTw&FKS1{n_bPSYgsOmaL@VvolTSfQ4HZU9%eXjF; zqHz8ilC4s;T)1!bvhy$5vg;YPHq&YuWIGm6U@2Cj6-^BExlm3%j`Z#}xaMCCu|!xw z5v;;lfwk^KJzB|vO`Nq=E?OO3UW`t@tl_EmAhOav7$`N!f8U$re?|`NMmjcy=13gg z#*1K9)ATildLwn+svoeTq1-=4l;8g0&!(=w^qim0OwW8UL-NLojZ5_Y=5PZ#yOuyS zE65gD;Eh8`;}Or#VjwkzeUCklIgv0HEm{F5eLU8cz(b@c(9lAJs0y-hU>WM{lWCO- zpvczelZ7e^e}kRy4E2B3L(~kOlk#yG$tWs>^u>j%U=OsSMt>{kViqeX7R%7accWIx zTWCE$u?N*+8oK0$Mh>nx8%K}RPxeW`&d2UVxVaN{ZwRTeS-2W{;r4}*N+%FwlathT zIEAPsZsb{(D+v!Nv$l96e`fYRPSk1_ihL11*XhoOHa%uu zap@($@Y>AF38?VHsRCM~BuMS1wLQoLh%dl4TQK$?{Bk)jP} z7!|ZjEE+I~Qqz3cdweKdd=?qI`g~I#t^bWJf1T)^(}Kd%d3gGuohJ!9p=0ivSWrdxr?_YIgKXXzx?=Aue4fBVoA)8wp@cs^Q%-BzLdL3(B|ry!u&9fIWY zz^>PF-4j`#Mv?mq6Q;42-EJn*MLe^X=V z_l8-Sb9iV5vsn$5Vv*KZv+gakDQ@y7*-{ztnJ5uDh4im|I@r{*{SrH|Pj7(csTOm3bmyJ@^rRlsm zZ5Z={;g;WBa?xey-}k3`#(}@zQCT<5Hlp3=Z6uwbS8W;6F_kW%&qL44BjN7dH7q2% zb=G1xezPqNj|?K38Xn$XuugDl$Cf@rB9e^b#4q-qu( z>_JVS7xEH{jt+9THLGd3LBOK3llA%~J6>>F`E1467&$ruTRDUDm`5-oAWwiZ>|n#* z|8o4x$3Jn?&%X2dNqbiRu`t2OHF}=4U^9|}gNVD1Omi<%Loq~5nQSl-hEfGCm)8mw zq)pZyNrlL!@JTuW=psh)e}GCvBXMLatEz+UFfbCEMULdcZx4}9HK4Vl9StELx|Rli z>TdCT*Y5X-Q&ZE;JcOt0s`|3}_DaXBfd6dcgbyhU(TY{1m$#z*>}3S*d2AdnqcCXV z(YT#}Fu|r|3VeyTI+Mt((1SojZ+wj8Ertgxq_0Cmm|x4`oYVTQe}3j?pS@}AJHMPe z(e=FCkrS{?Xl>}JKKe`-kwZ56_al<7y_Ze=7$zV*ar2$%W%<=)!`y@f1(N|v4l@K0 zcN`prNHQ7{$&Qe1i&{aI8sa(+g>B^%*^H7w43f_XkZoj*&Yhx!4n@K*rjuyQ=23ob z7rnOLT`R0Ajx>a~f6*@%pyU{!v{F}J-21LKg;ZE!nIxe!{S#!#Pq`Pq18l2UlpTKo zie`P94p%SJKK9=FdCX&{`^$d%OCif+)!e2PAR0Tg2g#X9v^A&DvGPqsjAo!GABI^= z*3V-c%bTpXB|ff0_4_~LE0^A!PG=e%gd^yNVNP2oIP$eMe=dXZLK%BWmIpEgWG7<) zkq#GcgY0XBzyfimNV*w>vonmf^XH?1BVWl^t(3f4wVwaZ3Wa2r!ekdKLor@iKDQsOF7mYo0Z5TWdQ5V5 zZ1O<{qQDY5N(8J@U{!Y>e;Dd%SD-LE0)=(O=R|AH^|HmxwZ7FGmZXmyLVIt*8X09*i3z0yqz)2j z+2yDgf7Gz)0%XvM^g;3gpHrq_lv=EDWqS%?+;L_>f$<1AIDYx*pEO+~3e}0a&hIz&OjrP8-22PQ`Zq$q`YpiEV zu>f8FZ@cBBsVZH|p-3j@P|TcU@O;_Ck=X2Be^X#1xIEqlwu4FTN$8O>J>E z+H>)O>dM*7QhZ`iCa1zOx=GH`C}d0I;{T@uJCQ?Dl`;d>l1oK!&7xyD!4)gn-zGY& ztqOK9($U%)LbS65?kg`t@sY>y+u12B?g+zSt0#zevu&G(2pPv|Y#o}#zVG}O)~#BM zf9u|NG4gq0thuz+%10o>?DPArZ8=jbvw7Z!Uv6#1VyXF5zvCSRz&DOLCmad`X^N`(cI2yW8 zh)%=EPte+OC~e(>){_6Q&fhEy&}*YLNMXGQxb-t*P} z_^l^=SaQp-T$~#`|{eSVelVK@7a3vdP2zA zL}D}z1_dtLYs2*XB=(Nf(VGrq?TSu(_n{G3y3I*@d?gVmSA~(SlbzZaD;pF5WptAN zt5zH6T1|f3F;OcP5fD=Z1Y;PPDIleV`~LV|zwRCX`n^xR@3T>Vz4Y0DrbzLyf-D7E zmxxktZvs^!GG3m@GX1Vje?Rk?GTK_YP^^~FVDpX}6i?=DiW4rn)~!N`>^Mj3M>UEV zBU|wk*ogQ7Xbll*A{&W>eFP}|Z2a9Fo$EHc*~uX}n_CFWs;_9;`ZLb-ymM?QcjL;f zztUadx{-kdwzLH?H=e~M#1`JWb`82)#Tc3^nvg5WFUl~)2#gr5dM{1Rlc=Y<4zfmq# zH?{lx)qBp~c^c})3t7V@+ec(_Q=_hlq%3mfhkmvrM~omv(UN`d#bX45RV-yos4;Ec z_D&+$i*RHcGR3r#e{SN4NA@ENh>CosA=jGT7}7A@XkdnHgms9sjm*L_yyIl_g<(6M<3Bw6K7LL@`gLP5SAZJKp*FcudhXUzm6k$*wV@mfsTPXUj$ zx)dh3UZom{L5R1K4I1oPSS>5Qf5R{B^LOn0Mtd;yf2FGCmkYXp?naIR`3m|(K|ZIJ zP0!i1VJ-gaJHH@6sNvwFgSh>UM_^eNww8$0(B(mVZZV9*YNA}5i8ww+G_rp z83T&N*m7;MuV7f?as@8a;^eI-G6y3}99U9XMU$)xOj||wCFYM2-H$x*lv$J&r>yREerV!gXi7&|bkeFt zj>z#`;swIWmF?ctY&Px%>Ob~}{J$hjq>mq)f9xE-pn52pklzov-dfV7-F~IzJ+Cd;yd5B`g-}yfMzKmw2XMbl7L1K$dFZQcdet_D+euz(oWmpAw8ef1N|7 z;Gw9ZXV&w4of?+PWlF}!riw!&V{bZi?ARlzcr2*&_iVRp@vD|zN(X`oo{=0HnMA&7 z&?QyK4+e;vTC~`8OV_Ov9(@qYsm=Qpwcaoqn$;)?dRdS&Gb0XbZ4Jn%Qs5?v9`Kuj zt%PLt>J?o*KO{hT@QK%V!MPW;f5%d7FZ}Lzzx z;zVlv!8k;lsYUr0>f*cTsa>+Jm%!P&MdA3JBGX1RRgl&h&wLJ{&qRa9$4 zhVpr|&^c5k44*FnMf9N_@ZtE}JSv6-h5TNWfNzIbo+8|IhgiODJ=q{j9LVGsiiS*JH%ma5)oKbj zB}_h!2-y;mH<>`cY|-c1)`Fi^&w^Q)Lq}IPcI?;%K}%t&tp#<_y6_KM*9@FfOdk3KuiYNf535nPT2ez*#;@~1)f30EHzsbSdPke^>+0$r{R&3gsvNeQc`DNl;MYn$NC2<=-vJr zIQ1iN3ilF0-c7b!g`l!wW#aM|Q~D6`Os3@|2iDww_0^X+2lqTEHz;7-{o^|z)4g^= zGPy0fW+FLe#6rAxfIuS9H?SR=QK4rvkGfsO!r&|xa|O)If6gODB-uxR+;*7+82ApN z<`a;Jhf0ezoJOvtyF*484OPn zD_$|Ld+40ni!fa?!<=G(bx#TCYHz{IUU)G+@X@c}O|N?%vB?FjUXf@cDC7!_Nx$;INwnfT*CMIZ+e~@E#^9;1mPY76gb%bMa@r-rF zhYlRYwc&KjDsyDE%@x%rA+K$lPZ#IP#*0$Vd)`0D+HSCR#dD`6ML zg`R$Qu^L?~hN2MT1R4e`ym{^zk~=kMvj(Dz69~@bpd~kBcXlnlJ?UcC+#wERusr>s zIW?fUe;~^QD8Ijuh8f+0qkkYLbAf@jR09`WtKqN``^@McvU@ty{SVD9jXx&2q4*TF zYfaB31iQXvHOz!js0b!GQ&xEYL#kUJY(7)ySjbw?=^Xhoe=WLH`Qc{;Ch4da3#KWUvM{u&|C@Wmz!Xo?p!;L?~(UB#TM@G^2x@*w-+;cF0bOb$T?I7?@*U}Qh#Gye< zEtR1ARCOrulZn5&~_*|*ON*5-oe693DL7w(&tr)9?e47)doQ>qp_E6ZD)cWw&NV0 zvF`uen_v6GXf#wA92Qi0+f73l~z3Io7g`&}a^I*pp7xNgL$Y80^ATY^x zAaVpwDjdA9dRrfTMmAYxeShzgC#mOL`jXd8{q-~a`Qv-v{v;b@bXK*_W<-}gNe+oF zJ-2X3%PwY@4%TY*0)6G8;qXE%AkJH6BQ2{9A)C#4V^cYojVzUjad<8(nzpSM{Rre7 zem_ zpUnV%;6rcU^R93H`T?VATtifjK)i)arGW~4IR%3UQ!sECr2FF-s0IQke-UV59mb}a zM`0nu#yTU}wH8_;jhNEFl^fdJxp`Y8w*oE1WTj$Aw`q5GtqFd+_w*Mu6?2<~?G@Mc z8uvf&U?7=H%DG(buS#8A^TMmoPey~QiMh|-EfHKkApUOrc;=D-Yt~%K)A>U1vl?4Z z;$;GgM1&4>-HwDLf!`X%f5ZEZjl+;~wEoI?e#nQ5dSdWxnL|wYDbrzGReGWIn_FIT z|$Npx59h zyYQ{<;hLS^;Xz^~MFRgmERY`w((i?XD&LtB$R3y!4)Zmi7|oyB>j74JOa)0c%o*drfpZM`s)syfA)i0@4oLK-==np6a$oI zw$g4amqO{w4K?iCGf-Q}{}g_W`k)1E%|8(43^aF)9v7Wl#*L2y$dj*~Oj@1#3 z_4ObUkXVB^0u&TesVG|FT7|&m>o0x%-~W$jm;T>6WO_hle>c{J$#NO-g*li;m2ZQj zH(iMI#^;d@k|R5E3oY_4MB3NEGioS~KMZSO2muNVD|*5Rr~9G#eH5fr^lUvJ;YgC= zwTe=9zBwz@H4!DoRLzc%!Lix~GBRSOq1H7796M337l|3!2&7lif44Vhx|0!PcHe+% zDFcgsH{G$Ge~X`=JPr?@nCWiod?Xg=9>^5ug?R!X?X?*wN*5fq^WkIL-ZIv$>cq!C z^io7)3eG=oHP4V5#3V#H%!kI4J3@n4=|j5@p}((#zB;*f7u$OEJl1gt4m?C5&NEW1 z0FQx4&t%FFb$Xv557eLq6i_O!+`QI2(6Qk~Uwo>Ye?2?D^!k>UU-uO$X2{NL1*fml z$;brc${Hd*4+DW&E3c$|X5_S9l>md|uR?6Kaat~m75!|cFM+Jp1S)%IVT{o|oF2!8 z#5xZzWiV2zHg&+z6rfa?>`F+&!zT>vFW`qlNIo6e6J2mye5GwZXaR@UN3FXo&DO`y zNAE9ve*jDP2D{E=ds^~qfioOi`jX>12}gHe874V=n@c(|(#OF5$tge05|_s(6|f8u zW3;uhc}7cH;;hAm{QZB<0B^kh{oDI{I$!ha>wk5|p^*%N;Q*raTnwj(@=_h$14&_e zEcc8-WnH&ws^2C6LqtGOP}5!b>0!!Dmy)Rnf6(=@Y_+0=kW44hR+>Y0(Z$bxd<*{Z z-Pd953oha_vqxvLC_XxZq3JnZ?-wQ#k!0P}^I81n#@j(?1*^jm^aMlbh=vfn{0u0m z0I_iODW~O}wPo$4zRKWAS70{SP4{RKY&NvXntCjqfTBb}OW<-{&4B_G)b%>gjI-@m zf2Q?n=q9qhB>X}HOZh4SD%r6bK(hQlQif{it`wqYUIH*_IeC5GDe5~AL0nhi`$O@OH_ z_+-=59iONwboc#AC0JIZ&T!ZJ^4UUSa$=%Xt=A?@$P=C|ukriEc&+M1jM0TQf3eGG z#M|3UziW%ORh3LNAgST#Yof8_E4u>z#`=w?PWs3IWm#QQtJTE5p1v(XSt_t5K(>4& zuc?y2I#>OY13#^!2Fq5{vtQpzfR4arg?w9)z|5@&>u8TSe7)^xCs3h=F*rAZR5Z;y zeaTVNeTbvdn5N&S^-FLl7$KV=e_xfaOhU0`w2}?>#AVEjam+8O&>cV7R}xlTgw?3< zVIzW|5`k&sb-}jT*z=d}yKL$APUM|yqD1g{MyP5*rgNK>3Vie#+u}(C>^dr87fZQm zl*nFniZ=!YYJu0%6A+=#OJE9KIE1-mn|tZSso#1FQ}e<}Dxdum~N zoUBLmIi8pl9Iw#Y)#-fZQ=gJ=yy>q?mA>-T*9G)?ey3sRYXVvaE~Bh+t2+DG`- zVGT$H#LiLHZ7k2ei%$9Jf2V7^cCi#bv{?Se*Y*s(gVkg2A^>$t!{CH{1(C%dk=xQ- z293-NACbxSq5ZU;-Nho|I>rG#3|37LkHn)TB1V}Cn9PPytrKrFv3Ie81z!-)A@a-g z5bue^5JjECW}V#xDwR|}fyz5kJ3?f6+k2q+BCzxl9@h(E8$C*MT;s{v z-;cf81eyzz!(6U#V5NZGQ(p_GHcXJ>R=Abj%OdN|j3m#Ty8PtLFY#n-e0s~dGgW2e?w5nzjw8zc?pt3_fiiN$)Jzcm|VpC=onwWjNq{X8aBIIjYjc; z%P&K{qT}KH({zu4L`Z=^@n*@34-HjZ^K)PL*4D$1?wQ!P@8R;HLkIt+n!o=Ohup9% zwXL-kg~$MGVmYxkGBBUGECC>L_4(+EjiU6CQungvARG&rvtI>%?LzVI9LnwX$cfgSDR3eJI?%s>{{Nt;UNJi*dOkS{T zSxxXEi8_JHU=vV*jNX^#X?Bk-b#lUN+JvA)1)p7h|M5NF-?MJVs~-I0o9#H>HNlB;1fsBwe;LT9uv}ZTRj#1#KkT}$B+$L2fFLj($7aVqy(gPU zJAiY5evVcVhFle@!S)G%DkWAtVVtNPl|v$3!Bsjf?ccP6YH)7ym@Z-rSr0GCZU@v&M$ z6D200vs~ZFvcHaFfXykf3DCQ$42zyasj71_q>^p=g*l8?vWUgVmTEeDvJbgZ9YH1# zf1S&~bYye~30T){MAZU3jqb)$m2WqD6vQ1nO28!?s|nJluKB=6zI1SK_*qYExa@hC zh}XUHB|n(UlwaD@mBPYO9t(K_y@fg&4GKzi0=7(s!(k0k4i{<<##*s$)5hs!s`dFh z&b#DqA`1PLul?$Wy*-@|pLh1IcSl3se|E>Qz0t9S4}A0!|K~p|l}gh{!TiDs(H1nB z>34c&4vLf`A4Si=g`n$(r|0s{^vGg(E>k+SP%G)>YGr?+T>NUaT6@&Ct(xEOOGZQ5 zmPjc17FQ5A6*C1*OT=6)7&9eT@u1gS(=|lbA7M3_nkuRr710u0kA-`~Dpku%e?i&H zL@CHHjYw8XvqYE2)>MoLA=|ZKfxJDe@!H3(kRJg#ibBoAM4^W1qRs(D``QHgvmrcm zVlUs)RA~MCRUhARDb{OX+1aL?mkX0k@gOSaF_$cRF=o)(f0>=hqh2ca zRBH1Gr{g#{p2M-aikeA2!sjOtmqt7uqL@NJI9tJ^bq(9|BiQ24!a1IWFBnF>cNHF7 z?81c$49S@!Sy+B&Yd=h1^WZNnxUUzl%hqVsNFmy5zT%4NQ5$42$aV)F!biW@;D z3sh@h|G_~nVD8X*e^MnEhY74RUF&G%(e06V>Q`-RPsPqNFLj=?W7iEI{KN-eHa$E0 zH&NC2pE}6SRMn~#kf4QMms==k5qMDtVrutRZLbOR8y4onZ< zjh=`=>@&zaT^$Oz)oK|txdIN&<H_mR?9ybKlGE!`!~Pz@Ds0B zgnPmpAAfF;7#n=7ttD~+My#XAPY)4bm0-`1lU-Lwg3D#d z@DY&o^#suv)luBvfWJ#d%f+nsL*NUD+cRT`bg7LAe+RMC!|-Q{Y)P8=>in@(q^ski zzQp_+2hM4K*Y|#iQeH!4o<7-ME4!Ytj- z#FH6tgz#O;9C}o*7X<2*`ha*t}*~*2cS$SvA>hwpahL*Tl{o+S&i3e5hA4Erd zPpQ%fe+&&C!rVd$nXH9|S?6~uZ8DObt12@+rm5e+9vJu~(@>idN~q)}WhBpy>Ds-01}v z%<&VDU|I4?qry5F(2;IMjevn9MvyNSI7uTQ!a=D{?^Qz_(@RC}bT_P7f#Ks5=xiep zQeLFTMgUzVXItC{uZ8LDb;C_(AlU?9YBppigsLcha{U|L{)K0HkDqva_v>Fws=??F ze~ypNT+*405;0)%Gz?5uP_Gu~E6N?VqF->ywU<1Dp`U;G zc=ao;S=ZT-`T?z$0RrA)zED2$-Vc7_Ym1AEY|Hi}pcMY;op0h%2U1ZG(6qdAn&!2$d1^e`G9L zEEj2_rk6N*rc-mjzy{H=-8z=E5$QLWrjZwz&9IIp-cZmzul zPTXGB$v@a!ZjSA$`2#*|K4lXgonn*UTqN2PyxFazvz6ANMu6VKBfE!ie+OAoi0*SE z(uJrwgj!HWXY5oY?<`>Fr6&*|+lEl3SS0#Yr}nk)Tyo*L*ZuC!ze?8L{f>9{qEaY^dY+BQ9HsX-K_**nf5v8;)!keCoP2RJ zy*0(++tW|XaXy2?tLg6k?zT2$ZqLD6VIa&JnIu{>sR*wp9XmdZ^+Y5bx7k%8aCH?A znj_)F_$sMa&-VZ$R z%r5hP*CG3~u%x;Le*~NYY_SD73YKljWK*D&9v~a3CrU}a^_?BtLq^#^`}*ggI6Dl5 zSW&8fHLCdq)C+Sw6I3VuThM(lh;fcAl@S(>z(+njmhOS-k8n-B{N!;|mX?s{S;;k{ zWFp>qZNK=TstwHEa67=6Bw6 z|3R!>)r;||8O+UP@q#NZZb~BvI=vqo!axXxu$Cth&}8*BuowuVYqpkaLM%F;(_Oc# zFT)>ck;NmyfA`e>>+>5s)9EvxJFrc?WaA24^v^d8`h&iQCr9Vsdcy+*sG^(6PfS3H zdwgu*W(sZ_u>@l_421xJs1exO97JG+hWe2zv9xA}kcq)N%adx@mO=931iS`?HDcKQ zgmy-&)IE|ZOy7CO{SSX=^{Vh^B}Af~@i;Ca_Il0Mf3tBScnpK!U0N$#$rCC1d9LFM zW?g@5aZ7{*l#mLuW^xD2x9J4oG5H{w7;rF}KsmR_U$-fGb+mOWrd@xFZZ7?l-sS|m z4T}JvB8eE-o?s&noK++1e?)dojsO+~T?z<`4-N7WVT!79sVR?jL_`C0?PRMdKUVYyv5<@6$Y>4=kJ0wStY5PHvmn zf3JM$Yv1@c1C#8ChLSH)8C88Ot^@*s({h>F$S73IPEBKO zX&yxa@wQRJ*bu8xt)S-GUMaWaMtzaKjy7TE{QTlE+wpWqXK!PZDVPMD3%9h!6KxcU zKj7)EVwhrG^7{qHZg@~6zgf=tv`}5~f0P6nPk=>Biua9?`T3dZ*n!=HkEys zGVM`6o6AgV)@2KoHJ{P@v57DVf|mgzMyx)tK`%|z8W^4?%O$V}TEk3RmWjaoe?wt9 zx5PC8jrw-_^r=>h^^U&-}$jm`Jz3Yhw3vAttycJREW&6+DfKVB{B_6 zI=AJ>tc!}*$O|mk3IT+)2o^FKm>#Wzauwxj<+{BKj$WI;?+d*Hrv!?nk~lUpA)m70 zv{NkIj>o&hUzHWXdDENU${tGnf6HI|0#gHdYVhVbA3ONK-KhwHs!5}SRXzO}V`EwA zx)`uz@@DbYIF3wZAa{1d3@8Xkqo_0XM*buimC>_t6HGab6MOezn=pkaD~~FaAkQAa z?(Pc^{oDz3UA+r-ycZ3r6-vHz=6dD97tz_T|7*5esZ{$%#wL6W__JLIe-?u$DGqL; z*wi3$RxMV*v{3{u`P1AXy(7>$Y zZP~MB+t}YB%g+um(A1k=e-aWhmKk z&z{4FUUL4zCmSVUJ#JFO34xirZ`f6+2AGB%FG^t^oGB9xJ8T8+WQJ5Vg zHsv9jB;FhEBWJV%e;c}zND1VBvLi6aaE>3ihX7~<$^LaH%^WAEokt}zf^aNFSb}Y3 zH=j0tJOobWvrFXM$-wObd<1p{nOI4q^rVab^&P)=FTP;gZECdVisQqBsdTziU}D9w zuGLJtoaEKb?bhg)vnk27{{e?dAP!8_mjO8ox5y}0?- z2hr2piBnHK1>3hKv2G=kAgZvo7M_`O9EqppnZPXzj;x-I$rSPNPh1_C7PuTELoN{j zD7}7m_xndXcYf-dU5S1_1=d)jRP#!xUYjY*U%P3|iu8Yd=h(}mg4%mUFrw|*eFF9+ z?}E8-9Fc7Vf7;tkl=B3|4-&ZUB_6kAqVh0n)OF~~M{ATEz^({)EY0KOBBb9p95@vR zr6YA97=|y=c%Rc~9O`XfanI!Z=(qplTelZlQ!U#QQFq|Lky0Y}%~H2zIX~z3UlNjT z!xrgrip>TbHrvL0xOj^od2 zk`FtkX0mpBtd0DcDX{VlW($q^GKOxr7bbyNwq>CPG}x+*N<0gRnD|{2GSBF=DjvcX z>(pjbe{XDZx*?t=8l`~SOAh&JUkHa}kLxCeB>{Pr03~^=uJ!#$ccfs}8}#goPdU$9 zF!dihPS4r0J3_a19@Q$Fq07Y3eTb0*UnIM-9fN1RgM!PSX{kVKMXtiCmx=r&VCo7~ za?lEEAk!^~#M(U~d`o1DdR6BgmMRgq8B6w7e}~F~$7dsx!w;;1S9u6zo~Rusn{L@S zUT~eae(>wxdRDbNJ>6a6r~mbzpQ9W0kM{37*2#70Nbc1iFdbI9O963Z5X&~TF zuzAA>2AK9*0G(Z{emyik_u`Lz`rXgEUh~f#suJk-Y{g^c4KmZ#3kA-+?6QkGe)_W; zf1lVn?g@g|0juU^vpHdU;RI?{oy*ouP8wdlZaJb)HUwReBEg{RR|UIn$i$zRsGR3` zOr(}25D9$E%r8{dbobpI4g_B3kHphbKqE#=PtkFiz;4ymD|xyHEvnCM5tNAEQx&_E zUAW0K4a-=XL4^3X^KuPlP*hp54H-UByGAv#5#e^jwo zc~TM`NujAcKx>HA+Y?lVsS2pqJzmx!5MVKk0-duB9s%cSDMutZg6u*G@pvpPDuJGF ze&^@LbI!jwJ2^b&J9=zX+^~AbsgmIB_i0il-O>G65RNl_EiTvIJkGl4+Lf`6uGdJi zy4kAawfUJT)R!g)tB$p%i`MT9f5kX~PhGK*FV+!FTUg!Ur~7DgB3H=Oz{-%0&$>ui zAp|uOsh&PW4m^UG#fD+@!WX1_7Lc({o=1N7EX)%nbiTM3KV7+zH)r&33cc@!NbKh? z{Lzhn(WkL;tgen$KH)_sKr%Wy2Hj=6gzgnR|4KB1bfro$(QQhh3C-p$f5Y#G#-^6B z9p9jz&uC}eoH z10hV45A%`F->E9tTdUwV^!}{oJnr*zUBwZjAy=x6P5h#rTvO+fWlkwBxsLa^(g^u5 z!ER2|@#r2Kd0LZ6!WJ@{f5^x9$rFL&a(43x%{a@-#>)vV0;6P`3@@0b#6hfQSGd-s zr<-t1qbZWkGJw46Txf#L<*em1Iez_vpC=Q+)E5qq4EZI06uu%$tQdTx;=w=cM)%e& zICA7sA|MHzy>$nUkBwsYqlZyy7^oEs@P}Jyt?7{2&MaL|Y~>oXf34bpc-uv{<(iY?+yR>&by!C<^HcZO;IS z2idk@(+!LbbYTAYf1{`!ei&=cx&nc4jEJeq8*SM11KT#ausRDy}+yTe@Q=QmvUH`k=u6oHUZeF*3&8x;H#-&ZIU1%p~6l`my;9ZAh2DpK4S-%RC z(|NA4|FRdIfr0)6I@@TGG^!ZrZo{hATuYy`X}HevXsBSAe?XB_6txu!}AZ&Re)^-ay|u$=Kvw#>>hxbyfqVXG#+{%A$N+n=NJrQ4fS~DgVN`A(=Y}O3IB)k-+@b9rS2j4bhP)9nsoxrk_1cqFe>7(by zx5?=hrps6>6}T;VMdxjnp>LocUw&k8@j#_mxkORLf9t0Ul{a5;`PN`@rtxN@R-z~% z<_*_->)m&ZJZmt?jt_kN^X5H2|6Id!ER6;Ig~b8}4~{~Z&A^Unh}m?XT1^zCA}`q9 zlM%qwiZ1PT$!Qq4|9BB)vPV|C*d9q@Kp-1+%V-dYtoUS{LO^qg?3S1UFVX1PK+Llx zfnb7ce?Jsq+J#Rkv2yQ$BR{@qUGEoQC7JpR0s#*Z+u*unW=KVv$iO12-=zRjv0Nm4 zDi+9DN-WdI*}lYU?BcOF*XT2HIjDq6gP|ZYnJiaND3#0T>yE%8e_fIYIIAv#J*Uv0 z8bk;P{LV($H0X!F{@c5+{I45t&pwm;dd6wHf27r`*LGii$;Fok{o?b-X6Da7cw|ff zU_hV0YSe33vt|XCPzn$ruTelca`Xt{$~=OxG&xp_01F3#Nr3YIJn?t4i%a))wzgQaxh2&UT{avB3$v`g zQ}Ei7L4RX%rd;z}W2Tg^d~32$yf2f>&bUnh(F-0yLC$OxcPeL>iLQm57?SH@Z zmw%CXC--S6Xy4tL)6ql6(uzA!a@0Qp2Wigg)XEfqu}F|bPcOj##?8bLHle^*&V zK7Y3W$EcyZLqd&S)50m#$&Xj9Hf~ev*R)>yf>*reOIKa}k|RDQ zi2S;~@cGY)<#H+TidVm;cJi9u^tQi0vyvJ6&9Rx8*!H!Z2*+Zah|PHRnj_Ok(Kk7X zXx9MBI;-id5)sT|_LzZGR}T)K$e_xU6MqO`VPpt$suQV}BpyDTMbAJl)}41TTJ?R{ zc;Q*(uZtL(r)SdBg3-6X2HxF!;T%4QOK5G5T=Wu5`ZjMqJ5aj**4Mt^>xVSU9`;u5 zDI4wv%QSO0e*bHaNekt&g&=({x1l%0CJP9J|u%(P-P31g;83J{pj3yHdHN&MYjQa>M+8_JVZT6 z0gntI+6tv*6C#N&M2VSPS+>LV%Zz#nYGW2YV+mem z7WFDUsMKosqn&^90Vbyx8kKVGaPNvW#_WKJkadERCsM5*%Hd`2BK;lopgymRzzNE{R zQTMpY8=rYjU|MF~@x-l(Bc4S2kV2GJS?&ibkU}V>GIFc(yaCh_cF*66&?OIW#tLe~pdq zqW8Mzj$4<`z4B#W?o5O}lP}ADlQj{rnpau@bqY2t78@384rHY*9@numq~_Q8|^Pqx!Qn0^)yxCo^CaeQnH1p*6kDU6wt zpntXYZFv>>mk+Vgb$8!$xACqwRBwreLzmXFjh4BhX9RZpE|i7_(W%#9sv&eP&fx_<5w)dx{Khh|xuXmF#^zuL znBNE?;m_g3s1Mb01KE6quD>1oYFm(=EPr6fiZEin1tHZJK3T*tf$5HPJL2>n$20TT+0lv7Tozj+QOE=+ z*C-+ei8u}f1K8tXDPh~o^U8&$?j+mVW?55St9!BwhLJi2f}fRA_@Nhbh_&=Lf9^ak z8Op%x;^Z(ECXVpYgsHBTJlkpe8h_9tDe|X2E^Wp7KUvR)QDo7EC3JjXiW!2BC8cRy z1Ohy3%SJ8oW*j94Y22BzoFm!fq#?$>A05LGc049^> zR(MBP6HGE1Z&=MOZfSXKuzEpZ`HStSuuPGjokwkM5MF(j81@nelYeS#7tCT&C^qU` z0iT)icshZfg$B^SbtR5qEk0YUL0TV&Ff$LgP+abmm9UC0l`jMD0vfZ*@c``c_~5@&{2?R5CF~*Ov)3B9dvPfHjK72J18iXmg_wJdeYAo z#cZizb_+_AK}xKKHGd*=pp$N)c&SO=(vr>;>6>7Z-<##F3YM9*m(L*xk8ORku2Gh* zl>O4J>?NOHozr=fiBqjuq9sDVUvh*6rnBo>?smVb-TcazZoTRUKRIyVzhm5%Vc0qg zrf##lpVhlChN;kfV512aTpz{?9jn`xgKU!SGX)HXDIl=V!3d7cCXcYjC<9Rf zPE!*FG|1t%CVvynt$Yd;lFh{Vm(W4rB)%~X1>8Q#M?>u#nxjZ{M&qGM$B9t05E)@%m%BO5}zB z0)CNSceNoCqnFVDzL^n(=bbOb2DGBRGuuY zIXpJf@CTfd=_>J_-afHft~#Y$esFGX>B}Q?i+5Y5-7rj3cU+zZv6jOTkt|6(`|Jxh zw4^)#DI5-MlqE6dNtWV@4NtU9Pp?)Nii?Ziot~PzWol%i=wss}6oq05c|eLVj%Dj? z@0|T)gnt(5D=*slm4E)_{ry{d<8MDc=?Oa7*<4LTR9W8eBb$|%yKNg3%CIXU%gr;D}`VKx;w)HvPDAW`L)G&0rj)jJZfbD}!t1X?>;E%_UZ0&~ZTS)<$ znWzu7y2ZhV!N*}TNj(9vkmhwq0^fh%2S4;DT7R;Ce0=1?A5L7f{mjjF&iKc|)I_gM zge;hdS35)DfCXKWYE|^6MIu9G)Z$FiHx02`M1rJpTYr>8E(Gm`1tpd@K?u*fTtVJ+#g;vx~SP6h?7o4n6JdICoFR+~{BpmVsb|h$-O&0*&Q-f-vj? zk*`+3%<@S%0>cMFv)N4G$|L^fwtu*Ha=|>k4COT!KkqWf_EK|;(D zrc4DS4BK8)dahRC$*2%rSCBwtjbex2t>MfIUXH$=2ru_?g)sSQ0{+z^`NRTJ?W>Ur z`Uu#oM8XrE8c;HmGt;kq^Sj=5N2y%;Z|%zcZ#ZN{k{e8$QKkpxO2onjHh=T9ZQ=eO zB2gZMP#c3+i9u%#F2sbj#41FBA;bxQ)`)Rdb2BK6J%H9t&xa=mAcfK#fwOu-n~X^d z#E2n#Fe_P97EZ8{cu?s9_$auo=uSeLeh5l#ju`3y1tbdCnIUeR+KO`!@88DfkXDx_ zxrBw|JoONSk-?#Z;XwGS>3>A*{nMG*_{z>0qT`1U>OPN8v2kp+Jza$o3K8y=;b%a| zV-8y3ph=Xgd>e=b4A;~7BIbqZEL-`+t~DlY3HGHom3iltgGd~$l^j(#jPy&)Y2B%8gaw!j)0)KK^qU)grUXy}! zpYssICFIEQk5mJw=sph4^79JHK?}8<$ic1FuR*Lxyu16P{v#{kHWxiiP22d?F%{m~ zEoe|sDhx-USLLV0&|J@cgQUne(eL^MS~yG%+#sgt3kvXu6fd8*MYdQUuXAZ$4?X(2 zC<`h9UEYe%>VH^mv46Wfqm?49V~Q@HwM+MPhJq}cTr+p`&4X+Uar}jsZM?{_%}x6T!&S!aLF3hv8}Ti^h*uQuo8jHXPStBt+pmZtd4U2kFRTZYW(9{Vu351+=7r?znKV^g4i4ZK)H_1(ID>E zb%ZFG$(am_jRq771_f60X4(YMWEiHyr%*7bl&#b_m>lTnK-I7*;2CJqZMgY)a(=*$ zGoti-=<}8FGTBHWx~}BPR_P$T=UCpq59ot`#hh{(cTf> z7z#%PoynaA5}spa?ITBquYTPdKgeXfp0s7VXU_pM6@N?aFBp{rvg~ZJjoPsj!{Z+w zo0yqs=(o=-ro_+C^f3 z`^aXPD!}cJngp2WZ|Z}{k{=3dek2kxE{vba6}jY4AQHlgL~LF!Eq(s4-S^Lb_H**u z&duvIzx2Ds$-K%M(?SV9dRMi4QgQIYkx6W;k$<18S8TA!X1;Waf@=^-L*$v)bhHh< zzCMhMl+nLAg^}4qP|X^x9&%t>n^2stO2Kq+WhfF2SBeEgvMk~6{^^wmZu{}?e;b&hZZ!(P?8{04wxDdk7IO1}J$kz!RJvM~C?p7gRYP^^( z_mLZZ@naU`-gf5c=blrk8XFmjND~+h27k27U}mm!f1{}l$>z85boug5P%{sZY@vqf!2>AgGVm(}Y}gn;YTz2M?Q6gL2%@=LQ5@ZkTJ(i*T2J93`#vqk z6H=xdr^Qo)sI8*~#-?snnsJ7grv>RfSS=V^T@v4JZzewedi72W7&Y|i(r+Nli1qUc}UEc5KdHR{u(_MXU)j7XYb$`zP>`{T< zkk0Pa>O2ofM=~sJJ)KRWbpmcSm$k^u;AuU684ANn&b5qnG{nZ9G@X#gqiX2|ENfDtW1Ar|sL*`qoE1 zkQ9#!lF^`g^VR4fHh7|4tAEKK>lSx9XOCuldcSZIBzwCUl7rU5Cvb z2uZZ(D0>x~z9aK+ZoBQb`F*>0{^0Ged&SwqCkuOmV${{>GUt)(ydlZH zx?Kly@90`}v>|)sXMfm>!c2*}vJ$P_FW=u`%5ZFxL}EcnjrgUWYnbZu3NBsK;0Pv$ z$8PFfGq642@t)YMnFoVjGhX+4T2w+bsuk17EtL(udd<>PB<#DY?&a+{4VSNy*X6k_ z`~1<3TJuoo)bn;!KU&vOTL0PYZl=}$GaGgD!Btk5{c6XKvwzNB|Jt6gbz0MGyt7em zKGW-VfBiKtJ@!4*ndt%igx_j_a?Ku*)x5J!;jfN!wawaC%ny zyMuD-QDN1Whh+P`b8>K^ETzbDZMC2Z(;4-1_{3t3sGcYl$1`Lm@wvtZX5(<`*wTH@XEFoE(t-}vtRzkcV_ZgmH(GxdmjU{yrQ zWse%gx@1FdMaC)-skJ0T&pWS8s9>lD90M_*j2A4a>Ph!1M7Z5vNvQ@|FPKWIL7Lvq zR8LHA*9$3BaBNX$^znmgbgX_J{~2r4&od{ryLX8z)_*D0_{wj*{$20QJjxf*Yc$O6 zJ$r{kH8l_$KA^r=DI+tpOZK(1a>HinYKux)T~A|UW2y~EtLv$%0*%Mse_Z7<^>3!E z9_qeod}8($pZV&y9(SF~N5c@0FA$8VcducuA{`x>`N6AS|2{o;_>V^a_ujLmG1x!w zj_upG7k~P@+gcWuQr76iO!|ZqkBzTeyP_`=4fN@|s=-Kmzd9cVRSVJT^9N2=*V>?2 z%2<~Ane&C5e`!(g#pOh8tez|+^f{ZPe?0IAZUK=UGIL|+>u`QKp^NZlwI#XxRmlW7IV^(aOoAF zUw=!s_kA{(pE}L&X&&#AthhRcQ~(&2LTX-$)q*6WK3U(@CoyZK{q(4BOYq9gN8_ zm1z|YgYx1N&yttD?gE)mFZ{oM@?TY0NZC7Xl|mDeYs6$RRkx>dl(RWWwne1A=Q;^A zr)A%qPs&Y065YL0SN+DONL8vGt#aRiNlC0bS~i*~bzOJMbD#H2`Nd^d%2S_qzJJ_y z*S)HnX_pV5wBog0FZt-+6W{)+Z|LLh?&^Hj{^5O>sVH=+e!6BDo@cq;zU6wqmR;RZ z4O$N`!xVeIt!m@TP0I}GCo!&|e`mT{-yVVA3^*5e*0;S{uqmO$^Bz9a#T$H zZ*^TqbT1rK1Ci2#{WW!!0sV|g&wrtx)|)M;{%zbFl#Kej{=R|hulVK9?m2wL9a_$9 z@84~g&XGeiL)?+&iY?Z1*}2L>RXi7eQ}65{GPPm-Z6(#(BbGQ&esYJa(yEi3H>jQVM1O|><_M3)$P1-=fEI)5~pxkIbI z^)b}t@Z3tnEsx4?ufFQ!K)`?g;^d43RW|Vl{E~>M)L2WX0auHCb1YY^+fTcy*(I^! zh8kQq#^8qKP#wDNvRn-@jbaaIjB0~~y zq$E^ZvP%Hzx^}f%O@hfmbpa09VXBs!m4zL@lwx*4yt-g?D5_4N2{F_t#Ur~_2fj|c z?W^sbYV`Y5>a(yZ+k4 z(aY@1s|E4NIv1DeBAT4uu}|MmHDLiLzcKyu34>S(_76 zjU*4x_b}@&Y1Be?;oW+oP_9gd~#xO)AOQxPCG53A8WZ~ zchQtg-7kf^?+{as>=t(Q9eRwcxADnKU!qeaxl)bfcG@ey^+nn?bdPxb_N%CVK9{`` z@b>wN{Awbi!sKQNMb}IHKHbn{rM&N^|7-36h3Aw@Z(S_3A^_yGglFv)44$mnW;(rA~rAm(<%A3D)~hJgvud?;X>2$oSpOdvwUrkHxQf(RmwQ+Nf2(?~}l9 zyA#oGzVL$M-hcOtpWT1NvGHSYkMUftgE0`m)%QQIl<#s=dAijRV1!DlGMno#1bts zq<0NcVQzlMLCK9yO6MsX#M=>nQQ873pqcuqwoi@FRfDEp!GzwsUa!QhRHQ=< zz80!A*%)!j#-K+6g_5{V+bDt9v@~{1h^r+jrC{$*uDbt$3m@%k_}=%w=jMO?%q5#P ztzU7|)_-j~SJu>^ZgFu*CRG^kP@%l9Ghr{LJ8*DRss+_PxR&qmdwgEW>1l?EJ#lzx7`qHBIxuV&;G7@}K?O4%YjI z_b>Q-Ue`Bmo?O)t4Qjn&3~3%lT{ie%PM6pt5I(@4R ze5qMChW+mN$sO&jmI{;)*`nj&rxhHY4)RdBvEeUTzQ}VbemQMbhu@xyb>f)? zQ7@O%JLjl&NUvO1!(0`>8uoOain<G$^X$zXFO)N=gu-`iU*i)Oo zyJo_ZSM6=*%655R_zp?UjmpZlujrewpf6CubkKLdsWODebR&xwI!@a_+fLk?;TN4#^K}R6k=(P8;Z!iGR7Y z+_NVw9u?TV>3LZg-7Vc6L3KU)#oyK^+xLx0uevrjw|7f;c3$%R1G4S5EppVlAvt^V zpahOORaRF^GJVVKGOON)Gfr41t6O3%)qtU|OFK|r_VY`p&p+q<7tAd#eoyta=ec#c zr$npXQqw3D3+j6rdfsEk<#J6{>VJ(*_Iv1Z<<+$pv;(NP)~i6Y&*gS)snp63M8k>q z7mLND4wd>tFK+2k!B>Y;Jvhk58)C zCn%}OF{$SlrKH-rk*R6Buz&oH+>%5S5m9MD{Vc8Woaa18dI$RD_8WGp0e?eU;(qDu zO*GS$`sJ0fdGADe=APNT+YWx@lb@Wl{_x^|lJKaD(tU7dPKI~R%1g|eY+f}a>AFYE z_|fWQQzI2`t7^3B63p&b;Y*jpiAgLNmux<5?>=JLTf|vX&Cf|tU4M|?mK9Q*-YT9@ zpR^{T67E{Pd}~^dUi5s12bc4kqMs(|9T_A)uip3(sfAi36X>(kew%%PE>R$7{{pI?} zdkc+crmNN~gC4Ved%*eUkKt9z)F z+q6r-PR~u5!=uCcF^lU?vs?0(SIpd;G`%`_s!yt<@A6j57khmEsYiKEnB{!w7olkQ ztOfC3wLiW1^RO?pL4DOXRBM$%{V2O>uXSHu$*l_oWy^KDHeRc09fxodRoXR zzSV7c7i-JI-p9t}ZdA$z8J}KKqiXd&sX@+xG1aM7Eqkhi6;olXQI@f)j>yP z(YL%`gI+DYc}@*_(w-fk8%J$iC+XTANiVd^s>p(z zw8|%Ujpd~tR~@^qLs_Xyq1KeJPhUH3JIw1l4H@-&pVg37Prwdx-DXwRsnL6XtSVM( zkMylo0e`tTCaz?wG>g%ZiOI>gy!%U+KBh%aVAt&XE8c5C4Fh%Oec=SZ(#)8AaRdi7gg_15=4+VjtmFMs2^m!5R$G4F{) zg8d^S6Yu=-Pky2At{t)Q_Ika>%U<=m)4Kcm{(rS;HrsT^t;?ahEYqdZqhVK=SE)*e5^TwfEzM#+3!vXd&-HrX!-@m5#*{kn+_hile z^O&MORSV98fkiB5Qug+zENEbCX zs+C&)-!A#;C68()etO1wm02ygqAGxR!@lOwNvl6(b6it?Hg5GiMnEX!AL-tWq%o#vB|wEEP3rmxcO{eg6f)!^~NO@ak~au z!mH1J+L^KIetSoGRsV`Wf6u_{YjxGk70T+}XxR6m3WeX#mA(4cH=Adie#(n7xyr@C zP~sG~w|UT`M*j2Z$_wM3Y`}8&%h6kJlB16~PIk8)FW=hUkfYCdft+&lEmA+@41ZaS z$7O#huezrlQpgXn4aq<+^oUc{=-`J(Uk_XZS>D{nad}?5D!WpN_P`xJRp6wzhoFu7OSKC8h(`$Zn z&B8O!Iq&6#T=5;{O6BRvmX?!MSby{_F3owe`K)SEU2%`!?@NT;yVd)4K)qbs!okpi zcr3QPRLbw&wquw5LM$vS$T6Ewm+XC=xfPe)7A>#KS%2OhP0y)Z z4s~S_)i}{GY z{SZqp^zr!MOgS$?{xkH-~TzH{m8R&BB0 zUMO^=R^XZEo)hu9j8~~^Uw?*F@NXJENp^RsL6G{gYmTu?%FR!YN&n~$*?iIoa_0l1 zGOEtcfI2^Wl0iA?*j2K3@3i{yOA=6n|Ms5NN%cbg%~>yd#a&1K?oSS$dZS)iEH%|d zD{|lHf~+4Juvhg?83}tupk7@XFYXt2Y0eG_MrlD}?Q3Kqy(FD&J%4t&9DT>d+rfis9*6u0y3pDmqZsRTyhFs^T;B%5ptgtVm0KRI<4aY3RMZ zL%mXNy6kPQCekI@twQ0dR=-SVAGsf_Hyia#CY_s`UHV~rpzX~ICGmy)ak1|Eg)Bax zF6?m7Uh&;fBbRbnHGjT^fchRGsjCrr$)gv)wAuxL8(wwNsP8#FT9#~~E~SF08oH49 zJvHf4Kkw0NH%mf|R1@*23L0ipMT(h#H@n{-FpBkh=JI;oclBu(Tr~ek$2mGPtS`nJ z^hd7V_aoH^hAofx%|4I!HNKVsX%w@|9fS^T^|B0956D?YT7R0E{Zfwh+X4H=#fsdP zs@WYty;=Xo_7GS?r?{=^*olk3cSTdsm|=U+GW=NVKF~7_886yYjSud+q=F2ICd z2jjLZj|#Ve{(si!Dfztlj}tTbyDPQottvQAGz~NRNC)Ee7!!IRr#kq1um0tp$6PDX zWf+B8WqDpt!(Q=NlNE#glG!&QeaWy)r}Br&B84R$^~rszb9Q;``P~xq`y{VJoO(Ui zw{@xxIw6y)vv9XXq$l83=R{CCH?5Y*Y)yJjT`%sSUw=w7GqV1b=c_X!C4m(kDxg*# z_73GC#xp;8#Hs=ZwFRQ=2ogZEr^QjK$QTswNxN_nPo{R3ohZTs0+- zO-Z#_7q8yRO9eKc>WYUvUP*YOYOq(5h^~{VM#Tf2dXjBadIr=0e&0^1`;yY=N=(nD zmY#p+2Y)X;QgP}(@`bN{yYiV&eE98gseVm0m&wk)6_RMlOR}R~g~r40Y}8 zNn_K|$Mq)LyFaeZlMX!+(p679E?sn}q9;AL?0;IGR#=UNy_TL7xR=-~=%KU<9?>#-xR#t;PUFXiD&JnR6lV3ZWmKSZ_ENlBp zGJi3Xkx1Ap1;0ma+#7u z-Ri=T#YP_j%-~ftbt$B2sDQCAKgn>l!gRrzIgbEPu^P z+VY5=e;3W9q*1HN=?90UwY5vyUiY`sGCC^FnR)fPhomK^+M2d5X@AO7Bz^5Q(%8OT zGUu=Tqu2Gi-(0KP$BW!<_y0X=!=_L?mhAKU1Dm_*8h>d1r0JJ*W=X=Ku%!G!D-;aok7?_Cz~>MAyi`d4 z*5h?;z52ReKEn6VbMtknp7`>k=0k5_NAuD`EuAc=2<>IC!@Adg+u&pg7SoDaPFoi7V`Tx~;}I;E!0Rej^cTW>CJT%yaj ztMhVkeq2sC>*+F=E6HNPEn#;|T6|U6u(C`2TUOGuv*J@>vah!zXNH0=JN>-p-+83> z=E=mP0-HILYe;uXK$Z$s`+xD3-hrW3T;7ATr8+O|`VBXi`*X9Yf?y*j^}4#?i)qyu zwd-1M%jF2wTNY{^lzeJRg+$fCgj?*rWtTVOTt2Cqs?iu(Sh9m?OLIXr7&*H$4p~)+ zR7TYa9gupURU-Dj#d<3~Q@RpiDOAcbn|tIyslR+fh04+Ki7`(&xPN25SUom zAfDo^6w~^Nt6#GIZYfoKD)8#6{(kjyn(F6JBjZw4og`uxCUvRNb3D=}{jFgM_xhwI z5s^ex+_Tflymv==digcntLI(mOYFZ=P2;DBb`Z#_ zVEL)8GgTlisX(lLwrbubN1eP<2Btfodg)KE)04d~d5q^pW9q={gQ0k4bT-{R*!fRt z^!8)3VqV^KdwTR1TyA&08ANg= zyzFgFNM3bttACzzsvNcQTs2q;*>%#~@rcaabho%wzz(ZG*^+Eo^Cu3kS#LJYN-h55 zK&ovzdri(Z*byQD{Ts1d&!fca~3P&HIJ6(sfC#gfsq7r#aI z)V*q6der$6Y)eS*kVl^>xI5 zIF~AV?;WeycVJrL9%=J=B^lLE0itSvyL?x&rrOBWeSXcycWRCz*5Gf40Y{=C?L;rQ>>4a5-30*)h^5mHD)k zvQ3#huwd-nH7>D8P#woUDb_NFO4~LisQLxBx^Bz4w3OBLuzhk$4dh#mRHi2yHvH%7 z1%D%MSJ?{r+{JLxclpp!$JGDj;tP#%IAneOEC1$x>)*fQ5!>STU3{57H-35OL%#KY zUVn_cl6^t=o*T~D7>%Bk>1*|;4sKn_rc$NTSGoF^vQ-JFHh5xuOv0WXnNL@x;Vyn_ zqHkm4Z{L32Th@kKUF!~v$Y`b@ySD5QV}I2uIdNe|ss|?|<5vCP&weGHDpczE{K-O5 z%sx{ZGqX~eosyawFq-XgnODj9k&ai-v)jI7o35|9_t3{is3Fp<_uGi~R zxmm9d&M(eKlP#^G&J}AH!o~cSQmJ@nF_*sSz8%{p9^F^fwI@UBmw@WCB5Guz3x82A zH|=&^WPQWXJT#+B4H{fBDUJExsLz{K?beJKDo-rWv0Sbea%fMS2RC07b9q{q{cj$h zIxos%Rmar;dcqE`c7Sz-)%Vt$iF#GGU12{4x96$YUrg^9SCeMmlt)<8YukCxEi2pG zIzMODiiUpb+OTI(H0+RkxU!4hbbsKu)1N98b!FYZecXOZ7jV~Q0bIfA@uq;z53xGt9x=$3MGK>Ah> zh%XeA#p%7WnAt04^b{GJIw(Wxjo0%f^_!<}lZTUC5^G-})%2(Yx{r}sDSvO5+i?fN z_Gn{jVO~a;3X=3xrL!<2zV40EdDPRTl3$eZxjC`CF$s9}LX(;rHRsgGuv7L2W0H*d zr7ixjwW1F*tBpo&VQHZ_P#XN+U0WxQi=NhR6!pBFl3sCEm73lfa!J=dbIC$ZbWbR>1RSO@Gd5T48EhLCw|_O}H?1DM)uYE3 zSZuh=zw-xOM#GfXRSL!%R6|f#nc~ZtbZL7c8u*~)@;s+$n%>3vMM*TxPt8`>dq-yW z>n+F@j`-S$aaW^m{rsxm?E9lf;*aW}4V4b`R))G{h3c4m>ipI#&j-3YWY5%sBzk(p zr|%l5hGRZk5fzlBGk@umnkOhVT?{=_l;c;m+qGCXoqe2y*K|oUU6P(yNXG8orB1k} zcsukt>9QAq6z3Ks*qxBX+5tV+NvgGa+Yt`YddJsq_~KXIbdoPIpZb_TJQYl=I!(RA zQHlKa4%wvop}CSe_M%%|k1C|Kl%){zsZd!FtJN#6rdPt1rhhsPmxQ~b(%lu1cqkzK zL+W}i7xgV(saLvli_NwdJ@2Dme%xVFTU=ah>^nGmqs!$wGaPM^V%?Cser=l>@wzP; zohV3IHQY%xFl}uK%f|H^Zk}Jtz4F5sUi@dDH~ELY^p)@Yd~q>#!~x4WPd&vLTD9&e z;bh+j4Yy~7VSiO!F4JD>U^dLU>2moh;tS~d{qC^4=?Z)5`BJI+wVC+;##%Qp0m z9G;R2@^zV8Py_XZYO|NJvdz^l^G3ujA=4HN>Zgw~mw#_aGZ0nplOeNN)xy4;m(rtS9eu&Z{w61?7Dcro)g_3kBfdx9#H>(?Qw0p z>OpbcdGn2aUoc!d{*+V7kM%Ww(lxKd!-}{(R{BnHwXBrp+@xxEb?!CAYAy%9Lp6l#&$mpK|1buN^p4SK0n=%kk3wt^M5EyIj>oZ*r^eiW-M^*pub2)5wb9>$YoR>L+;3 zN>GLNQF|NWWfPa%Hzc>q`RS=V9=H0th*$2tWkia) zM4zi6U2&JRceTod8aQ@6`d`5 z$1qu=UZ7*caq)TF_TrmLPf|jvL9**M&L0%3raH9LuoPR@%YlglDuDH<2C75$-}Vz3 z-1HQQ_a7|*6$Gofl%yx_m*mP*#HZfu`AlB6PiG{o-k5|dFRpN_YCk+uQH_jV;D6KO zZ(ZI!$Lo_~rDT_JNvICAJ?gW!7#m0&ajBNsP(!)-`MJxg)ykW$%$JVp>rA+kA&;cg z_nw{4NU5s#677<{{-g}8U1PPYP7g_Gep}>T($)vkNmh`pVS`yHl}3Jc_09Xd9^bC6&ej{|^W#nr)4sH(#}-rFeQocp)S9=u>a}}4 z!N51GbyxZJd&c#nzW4O>xBRObwM8rC+Masdv=&mObZ1v$I2!O?5lwg-rGG;4_=CeE zpDgC`ddsdiAL*N3b=|%zoE)^*7i@U!{ctTQ6wAwXHB~z38|atuSrsTdy5#udj*&a> zzQ?|^cFOue**}|+WP7_zq|54hh)PE?A$m2hC*YT^zD{YVQ0LXV1?k8Ax?$6UXf?ua zHdJHd_ewdHlbBl%#`V72wSPyPLwVS*@7~*7b@$#+U-gmmf3`kleW$%|=G54#wML+0 zNZR(@DeKk1sxrPHiw853YWT&kI?>K(K*D7;5K@h#r^PS9&Y<{W0f~lP5>uhVFx5wQ zQtE}a+`fYU1!sT!%a3b6^gpz5P2b+VW3y6jSh6^_&)%RUtghh+^?!HON?qbDEz;H+ zQvGC`dZ`=#`QZycQ#kThed6uid*Fyq364E_gE2U?@|;Mb<0G|N)#r9s%Wj_kYTmhLd)wvQ2%d^+? zir?p!a|^R&81pbb1v22l~f(dVp=MthJUf=`dfe9bLytgREqNI z$%Tb=*-};t_FVd+#MQ^N8t}v;Ei$XF%ZUYbE$aD*<+MH3C!lxTQUgF;|057pX+9ZN z1EaF^cEx4*;IvdSmQ-C8=?b<J*; z?Oj(?(^&LP@^dweDK?KFl}wX~viP_CM=9?7sIp=fC&)Wy3l4U0kBa~4O(3sYqkDNU9d`uX@W?h;2oZLe%_fcK)@#IJ8 zl*udqw0X5R9xtUF>~N72Y1Xb247cUW5N9VIkC1XEq$$+~!J*x&vyy2?tGzdv##c;k zSsj2cjfmwDA4J(nKVXIDXXlllA097#Rbw4?eR#MWS2!PKwOLwS+UW|yO~2v-cgcbq zX5Ea+eS-nKn6=Paiz+*-xof9&4YZMq0=KDE1&{s=ijVFJ7=098En?d|A<$9X%(CyGwI5-^3p3zVuXN5{`OC zhQxqU64LhtCcSa0*fX=d@%AN9ctFJFkR$q>`EM}sGLxebitOn>t`MEdDrl{V<4zKT z@i!;cwr*PedMuIcH`a6sq#U<>6c#$aUg=ddIJ4rd=ZW)6b@+PG;&HR6aOs^&dxw+F zZqpCyh$1U?9*;l2mh3))+RmY1bDA(V{V>8*ZN{8Bcbde{?KGaN6xGNAcBsHu0}P@e zenf94me#VJWI}23BXjmJp@HjIzZzbNJR%OUFLb^Yj^Q;o^s-Gta>NU&W_p9@X;}&F zk;}V%wfdk)!kjev_F5xzc79`IlGo1H(of{&eboVF4BnnKemE~Z(=jh&Kgg{*D}JEn z)@N^0$2oid(7}-AY~ZRwR*L;KMACEYUWJpD$Dd2EvDF{tdR?k8pPg*y<~FGg@WMS% zGe~@>C?g0rRhd#Kp3nE~dZAL?r1;wHE72}n#_VH<6|F3HuX`Tiq2dq%&j z;k{@m9!Ekwl2>sm+v(9r8svoX0aH?~Ssr$`=hEI6GPa4SalqTx+>}c;u^tY3p_vdB zY1;sQn`uVSOAnJjChHx!zcZSWNqw5G{QI0PbqQpy+d%Vhx@fFebEO{c36A7;%mYi( zwCXYQzj)S7tcZmupbV;8s^$Y5MhKcAs_k*_Nw++C!(V=Da3KU`;gfN((Jfbll zQ{C8`kI*{Pyf7vJDp1PndfDI8ldk`;l~P)H+sL5&uK8X_NDntdv@#;dCbz51iq!MX zW_&n>(XVgQT~Hj+T;7=Ya`J7!xB6gU-H4GE%j4zpQ)N4r&k*E06V+2@m6J{pY@Q&x ziiDaSCT9iXY7F|iDf+1=G)|QrdGHzyv8D#I-~IwMtH?Hs5p#5#%$u8tCIbXzmjP6D z@9uA6bkwY$R%|bS@I9#S`2<;7imDZ2y;h{oa)1KADSPVPL6SpkY_;>=0ZK7ZBDABA zHfkR4${TgR5kHQrV_Vw7QBqKgHYuB#+2{>XtflJSJCVWWSE+A|wj*3z#bTt0P_K78 z@SrLSNYmFoSjgJ3`R-Ov*b)4Bxt5WaH;O<=oaBj@=j;~t58-cg*50@3*AI<5h*ek= zR(kK*xnk2TXhVSJV&yGW0an^u6=iw$6*1BFq;u-nFWPd6rJ6~Z*&+}R0lxGYd zvfkYnBroI2Je$#)emR7QuZwJsnT=$DB3ViN4wJVZZc$K#tcF;(_bu$w?$K4H4(0!( zlg;%(7JQuO%K+7_tr=42oQzBrXibjua136NP%tdI+8EQ&*%<|_<0Ti#E$i*s{jMQ> zHNYr7JpQ8{;~jJ^;EX5ggGFX!N%~sbH>I!dq}#S{DAzr*T#{m@F$q0Hh7KD-;07TL zl_moL^Cf@Wk~HJRPQ(hDTIZ-P?b6^EmyZ_jkg<9yE_PZUyGw!*Lk z^3Y8drf}RdU?w2KTV`d`b(#U5aIea_>Lcq?3lQ}9T?u2+NDN78bb4Wk*UkN498V8d z=XDN{Fe zL%ZT*7$yM&frWad?M;2;hHdV<2O%bG&* zmrKTFnT?UgiQ-i~htRhhnlse7+a%s}JYFU?B2NpdRzM&Z&8}rYBqTD=9y`$K=MK&& zEUaoD1MXKwZc$wL%E;(=0*{Md-~LipN)C0&kq!qaOoaKT0Eq}x;WMydTEz%lCILuY zsSobZ#|Bj^9Q$;=45Wkzz?WD18p=(++spDo$y$b#x)@x8ic(*s`7Cdhu6{V~wz@FK z@766_H!O#fq9cOnY{>SU%{h@`YD0k0Ay+@J_+qD1=+^`C-ELyj&LW%oRT@T@BrE+-HR>a)95mT^yCG`sniFKtw1K&btFruk zMiXqS%}S+dHercZLW2@k>Rp~Dws!C8ef`oTFATa))?MJfBKOUH_sD=81|-x-pMU#A znN!Y>Fc=eEP(^!rwxk=qa}<^^)?cDr9O}aR?K{w%j)1RLG}0cdtn(krGYtz{2WiT$ zRGZW^oGurfzuPmJ23Ppkck_IZa$q*KH{ASZ{kjtu;-PsqVC|v!256w?qq1=LPSjzv zUqZ3SGmJb82zpj48U6z8zVX1|Yy zd-71nE)1IKf&E_K1T)uRQYOMC&(hL*rFm!W5>sf#PuoZ@N-qR3Bx^&&ewDH*Nmaas z(TJ#hS9t6C*l>AW(DeT%SG12dRCD-L#$9%iY`1Dj6TnolfY4fap` zZ`z)^M=XU;1ra+-bo!>a)zwC8qo(^;Ohu-xPFsXj>(ae^$7NtFH$p8^swzxKSoTsMr9ALRd+kEoq3U|y!~F`D+N3-9usU%^_2{pqAf$g_LOq zDH@aPF?QzZ0$35dK92^gVsCqwhLrAOUeaS47em)MQ(ch||E;l?+!O%6hVP|u>$*;6 z1QTi>!4Oqj9q(_3orYs^ejLfW=-wgW-Ck^i)j-W6aFL158GH8u*YRI?UP~TRACB|B zqxk3K>2RPnP`%|#!+?1@cOqTMknUU7hb~#7h7Q!S9-EZ|#9Ld!Q0g*lI+}ffI4BeH zM?oAlKsS^}%#wldH?=4^eCxs%@1-{W>+Sz)SrYvm@Aw4fiy)6OFF zSKgH=<;<-zQj08!vRaCh6K4vIofSL;GYsB94Cr)Jee>1aSg3m$B-*3R7bsSSoY3{slU|dA?mmYc+$D*361mGlJB9I@WMo% zM`SuU63xH8wNlXWx#XYz*fSRa&DdIfA3P;vvZPzDTw7aP2G}oY)j8eV)T8%#p9DZ- zqoY*jfhZ4AW&1@z8+MhQ-&oaa^HUHlf4+q8i_&F$9K5$?mUisG_!5T`vT(m)d*Icx zCv!D*ofGWPD7S3&S{SX?KkE|-InckgF2m!>Fht#3>6?rJudS^4IWi5+_CfPoXq3<*rFEny`E!vHhqpHto%9EdF;?ys1 z$@6*7W7ifZ3?gYSq)3sY7xl;#>0#KL*)upNLtEp78s(kn4!Uo+;1w0kv4c`X{CgEy z#MG$1yo}ZDRbV@M#9fp<^fTe;3D>a4i1Mqr+wYv-Z>qyMW+ba>{eX*n1z> zpid2AoG~^#u^>rx-ZXga-5kMEFt!Bf7(SRxs2KMz33th_x?A(QG_gQp%+2@Hk^#IE z7P!@41L8Rq`s=ZahasT8S#N`7Ky76Bgf#xigY}#vzLvF3U+C%)S60ikZ}4F2Y2KOl zV>jt(GBh9(TyLcTI$6xkO8h0N-`X-1eOrQM(tfbPj~Y=F=xB4ijC3B`{jP$9w&0gr zt5Tdw@;S{sC@lO&#!q@94FhoFl^g&`Gc|l)7Qc3l*OLWouz>#^dpN-_f)lx1l&?aA)x@T z@8R{FcHyu;_8E4iy7Nfw;7-d%M?AAl^%#p_VE5Zco}?wSjhn7>-#P1dCk@J$!U_J( zu4vDe&0;?MTlU+5o#;Bk_g(DXebSp@(9Fz_Gy<2MD2C7k^>ZlnplGCm+tw zs4qg7ww901d8)l18YS#(g=N1^Z`??BLvpU`T%y3|XNLLhsPCrKjaTvW?;eGp=b+eN z{`@QITnqo{n@Ik?E%nU}<9({&`#!gB8B><=j8q#6iSY2WZ3$6P;%0s}u)`4MkzHBN zC0@|*DbqEfd$gP*+fe}#e7a6KV)k>F`{VW+lD9FESC2i{y)gNfT_Mb;FYi{>PrWxM z8hjt@oW=jPM@Z;Pd3@xD!?~zA>Fue>emMUFYR?yIIjh}5Laj%4D+&n-os|941@ND@ z{*fKvFS+~sSrEwo*9!WZSpfdrEPpfA{~;=n|37%fKf?m>SI+qlumJp6^UDH%8kPTm z;QuW*@MF!t8~BUd^iOC3_@U7+YX$tD8T#+l0`Nnlf4AcQ*PkDofd1L~vrPzPItm#5+sm72ofLx0t9yr7F+@Z4Z&U4U1n!TcD$$EOS-E5?^O@{W5DjRlKjH` zzS`O8X?b1s>b`T|z4shXX#nY*h7eDD;)n>XK)}E1urnovJh|sonhp=c%7;->1Qaxp zlk4P*rc{5+d%twef`MwXwiQS~sz2H>oIT^@ zbAR=PbV|QI@9d$&zdQG^@6SDAuFvKD?fQOYW_3`9Tjt3+SMBet za@M~vW7718^2BfY4y@ue#~AMQr(c-0@V%~|geuHqk3C`bozamsr~huowTrSD#ai&( zeEomiC-1a;KDWt=Zj*piFZ6ZSToX9={PUCfd~vh4 z&>Y3c?Xpj3+;fceb2kAx9VHb*D^`PTn*mpY0~G|F_pMQ(3N;XGN*IVGapUdnXdLH( zFr2_(MJsA1Igw|o(e-Yc*0`W*pBc!KAw_@tKSkxvKZZBli2ZhGg5={~j%AX+UH$f* z%fDzm3)goTbrYw3+`Vq~c#A~0L;XWYq__t|mDR;SWy=gQ`=@8(5rVMDXOTs$wz0vM zv*U#-2u?qAiQJ--Cg4qNgp8$RlaH#}a%2fmN|E1Lj!YWj07bI6YlUXBd)-o6u3~?5 z=$27ZM}&by-jJKdDuui;dAoltPWJESa|k;NPPy5|WEojjj1IOipA9mvDbkcz zm%sMNHmmFVDtq&-k9wP%8$aO$$6++xAWBrhrI0F>N~_7SP}lV(`Al|sG&<7r;!AI* zetOdPzoQj1j|hV3uBixstqMbLz7yG09}0<8*grTQ16}iwE3JcrO+wD3(Yb%*Rn)w& z4%*9GyJIrP92#RMVNaNqv7oduRk+vO!3hyqGELdz0st%rdPH&zEa6={KhDQ&2U# zHj^D~0AeZs9Hm+!{}(0R=E9^Wr&Q91i^(->tic4;awb@JA$hMEWZb>v{hG;Q5T;CV zV5mX|lP^;{$!;6?fJ!9VZ$g0!0hW9nIifHrN2Y9%#iR%aBa+3OgU5f*qSEx(JF*Pt zE_a1fY9&1fhAhAg0j-un#sCl0WD=^HCu8&wFr7hG7>1EGp{vzoKB?|Mt z^A}_9op;9Ax(b_)DCE=N_gZMI@?#`N7PeFr104~&$%Sc|fH9LM6aOGVEzlkL&69kj zc66lgiHG;vV+p%##gTt%Q9DvFO4Gf%!MMovbEJr`p?cjEB+@c3YqVL4r;Hht)l&c^ zQlOlDr3`I&Qfx&Nsx5C)5J}b)jxv~VE^=LDyTQgTt}y65EW;K~Bn!1b148n>WE5?w z#~hge#bgFTE{Q$@E{Dk^6QcuP37TJe@*(4_x#y%cPOcn^=MaDNdC;d2U=ZZsjZ)%B zHyFu-s-SQ4MAyHDU3}-Qv7?^4xXb0?rWzy|%ci}^MtBm?g}{_(3cLtGCO>5cvfqKa z5LE&t_e6w)PR@@IRS*!<_HJ2;oa0im2#eg0MozcSX~}X^CQoEbek42kq%j1&9kV&BAipF_2-21{E?|m}=E9c{LFFbFNCqJMR4H+~ZGPaMzu8KJxfu zD=s`_FZO@01N|?&T2nLUS9=`U7QvIyA73}m)*<-pvlia>QhQWI4R0+d1?iIa>h(@@+TKM zx{{x+>rdP>7HeO6=)9kPW#rS{)pK7*N9X>TY^>S@vqaAh{yv@Rpc==_q?Yl1j(1Hq zSoe7f!Cnr1KpXE81^UMg~*%_Xf| zE@ywP?Ov}r9g^<$I8|~2^QvA-k%cgs&JE^=hF!f9XSkeBF@nVAE&AU-?5HX2-NTRV zd+0$^D_fgT)zX4|B8EtJAD(&U8LVB~X>;8<9Ugmkjp~U`pD_VrCy#+MSc`@O_D0yD zV5qMf1VbX3%#j-*1)W4lNdlBDuN$T)Z&rUt>8F1_lYXZUM~^SCFU0-Z#V)+$8uL4c z?R!?RWGto`RaMoO7mDiguCA`gA#+domw9#VO?SM|zWlx2JYM&=L@B=;$BVm4lC#bq zsD(e+1he(q2v5nOCAEP#^+hPfJ|;l32gUvOKyc0+6rX$+dTkw?tCz#7s|3IMoOFLW zmDngAw5$L68iio}Czg)qAhD>;9l}P%}CdHiD2y&SQVfi5O!V znUb>6wP=5D;g)JEMHcm7y%*kU9Yxa#xGGWP38)K6Xchf%h*L3xGKrf70@Mr8g&;Cn z1q&9eL?Br4wXz7uz4yUd^5YK=jGJ;LhS1ms(%LzwGhA~Y@Bv^@D4i~vLUb>}2 z_0tLXlkDvs*o|ERt;uuK)U|&Lda>MWLoc2|VH@4ObWnFpKq&3{rMuom#a>uTT8i#% z!_Cm99s?PRRu+N;+)kKICvm?9hDwf-*ie6MXV%cT1a48hKgpHcfStfY@7^g;JG)9wX~l$pe2*qNNQa$RWWC zqxivTWR?Jeemq zP^I541?sgN=+RZwkQF08M1n;E)M;0BHv!mr3cMMC{O&@sBpry21eDbNaP!^0=bwJu z0Xt2q|6aDJ{q=-%u3vw>spsVPS6@f{?AWIsHkj&s5>E&d8x%?0|Yo&|oLn;iG!rHkG@ZQt79cl!obHpP_2*sN)j4YEL5Ydh9|zR=wGdWxkC zvOhcVcqSb75N9j%rmmV7UwQTB7xw*=optc|;UAoG#!<|Vf48vSr8G^M&M>p;;#iv& zv2-|$C+0a&@H&4`Q}02AXn|%O9Bu)VCwMTqQN&2fL@ZxGS~XC=ON-T5$>i(87;+2B zhUmgK&VBy6D_5>-Z{O(gE?>3i6NlhyAuEH21D7uu`Oj{AYjt;f0;51E;H|okoUb>P zVqvHF@BsSzd+ox1Rnd~UZ0;}QIbF2iheQ-I$H@Y5_JV)KlvI}G$*sl>khK6F~c}@~pJK-jvQRRKm$;3|=buyn)HXTi&M2Ul) zR6*!JW(R+9TiBFY2T&6H-*4>m$4}d91^Kb!rfV;|?ZEwat8{z3(|LhE&>_i(aDv?I zayUhI?PM^ulgKonB-CyP#wESByc9)p6*i@=BPLSMNG{GM|@C(31GCy!pX3W;4H;X?bF>hYqZOpCSwgPLjLX`zpHjvGow*NR7>7RB@4HOY;2f- zq&&0?@q0rC{F;Si+>InzjE0g!YfHRi-oHENUuE?*)r_A6j7ye(N`R~jEiF5Oce==# zN!dE$l-YuIzfw|mgPdbU^N^!}H68OXlwN<2Fx$9AcHB-_w~}5UI?u19$D>Wt$7s3q z5eCcuz$oTtmI^7FsI_Zg8J1rEqD|0E84KtIxTlHWy(wqX| z2%L7L;L4G8AS;r9g*J@(Z9?@NtpRt6=r08zC0O{zkT`~{AwoidmhvH%R#8p9g^{(8 z;=;(AG<_b0nv{_z-#y{wkWbLRwW5E>^+1cnkPA66LgJx}8#eC?J$Wn0e9JoBRA8*jMyyPXdIQI}*OOj+b|CJxii{0e% zd6l<6c&^mbJ76VJsejnDc>Jkn{yFO0DpA#wci*0vdB_h=S*^A`xXTz+tGO;FpKzgC z0!}~Fz@Sovg4M$I$Ty^IMuZ( zcJ@g>*{p?B(bM!?eUToteU6fuNOp`l@$4lZFL*kg?5oZzc^Yw8EGxcPGV`?lg66y& zWFGiTs~7_776f-=E#q)k8jMZVtm{f*P&8r%rI7cFyxc8tj)567C-i^Fp_*Kp_#DAE zo}B-$)O!E7HmAw^=Cmo?>_TxpQgIn;B3^Kw5PG{Jm^O71HuQDF8zLvzSwkkVfJR3K zbN1U8mB9wMCE8G+AQnlY!so?8O7u#cvN5V2;w3<{t*xb4S5te{W)1Ft%zky=#dOZl z7B@Qze&zXxe$&#@*yMk7d1i}}^c`7t?ZxqYh0i6k1jmCR3#1w#3-&8-fX}9ZrUsOB z^^@aHK6{G=a@*7VfpEK~sy$@hx6#xz1$Q(67Hlxd+R6{+X!kb-e=euPZl0mLi&RZQ z;L1CXG+j+QBWb#vKCWa9j0XKSK;!u`x1|jxsY(@1kkVZM)@FYmu+yj1p|f^zyaa<% z1u^9gVvT_Ns-QbrT@iZe)L-7NJ#y>G=kL;}z2)&2W!6Z6ch4g@qzRm~Q<)#GGzhv= zN&D0oD`+>|QSM2LDtFj~=5$~on@7=J+X=9wO+i$e$HtO0H*Tq;e*7u~B*oUbo2I}E$*!S_d6E2@66*CF+f4#a}I zlW{`MhG2gdNzkB?dw?p(P_lf#1j!RcWIX{r1qS}{1Yo*Ckm4~U^(F$oQ-~WLRLVSJ z89#CrwRSlMleDm=uK*_%fDqj1F4ZEQiX-;P28?Slit^q& zZyIO)?mM3DUVSK;%WU>{8d#gYd*+0|Ig@`Ti(-+ge#CNMjR?l?&LF+Z4#*UDL3`IA zdIu8(Br=#iXAfl337ZS6p!?to)??fl7KIHlGDwGAt3)~UIL;)Gwo+( zaBB3iBJI+qv_B|!gHtd`L8(YduBjR&mFuH0oYp9DWyKx())a`^6f@RnD%2`>2}`mY zDwRRgHBCp4tlo2r5%H6>Xij-Dk)84z1kYnyU~(q2*nU_I%GX$RQj=9MQd0QTlr73ITBFY=DA~^(`(gs$ZEFcE&<~RIJ@VzBA_nD`{{MH?n#tTkK92&Wxm!?{4lj+0Y}XYuEOWz@>i&nY<1v zuf29j0MjQfH zxx(WLJ2{cJ48~wB)#>31o?uIA<(heqZt3&05oVVhU;6Zdzg#TWRgG(|zSa?POTC&G zX?+|Fg#-=O2wVY5M`c3o5#c7F#xXIDVRsm6l$>8S1k1~#x1EQ}@5O(xY=KgUt3@w_ zd=dkL4ElOw7+=jInK98dyuw<(GWBMPY5Ayq5n6)|&sbGcTFI;rhukdhv*e=IlX|T` zn}14o`CoYP@l&>mSjBcS0xHj2cg-~yT_sD-8x*x@@3hlk+T=v&#WEcb=k!2w1uTc) z>=Q)CJLHjPiI-ZFN@RZq4>|068>K4rWwud(lG-)5{Q1>j@R0po6%}8Lh;w^bG%{lH zOYi%m(sWGi+wtnNrnE(jCB0^6l4X{Tsg5nHuJA0?xbT{vPxB6L@3H>9tzEx{az3?> z9$#gnwQK)>Be-N1OC_~f{QJAe=MSi=?#viuif!(=Zg}C5zNUX2e|d)LsQHz@f~{8t zhg*_arlyL;cyAV*rep6J0kU}1prc1YgfJ}wixL)#mXaNr9 z?6P;?)M-0?|DxZXwpppQZ|q-fhdBq*4tRP#=RNYk?KjMtF}d35bWRsV;SiQ(rzkA@ z#JV+||3YZ~-)w)J=y>)XmO>L{Fz^;9yyNfm% zI_#I(yGnvZse_Di*Dob9*UFo@rBNbt+AU529|fEAy~=fxybUHPJ&aAJEjaA=&gZhI ztMIND1peaB#{1BtZ(0vMil=`0v%`D6oOnrFLt)QAh>?E--eCel4O=UncR0vW6nkzVLI4SA~* zmKFtXc(883kC8+YSzUlikn;S&)L?fibAeVWKJj&~M>?I(Zy2KDEe1lB)kvgFC_)I? z_5!{)Z5)4QObQ_63lh-pLMGdeH$Pp3Wea0ypT7h#HH%QSH~4ive^c%3-lY^=UQdsE zChpwKNy5>|k;wO0Lz!7gCWMdbuov@iN+qaNKF{LFqU}I{vIm%)-N8;5Rw|#s3oc}N z3EgBO2s`fiYTJ&xo_NXazZ%}$u|0U#4b*SHh0lMwTK~hWr>Dm_@v@t5d*a&LKYjV* z_J@Ow-1zF+3h+f8E|vsfnIHi*K9m{-#HAz({1_B+1!$5FPSHf>lMWcU0Q`P;nU|RH zfiXl3b!d2EVLzGY#)kD-e%?Slv$AM1e_OQZ#l`hp?^roBnE2iaCmy%S zWlmpZ8x1I_oqx&8p8_v$=`m~*d*p$KwG(bQ=bBux_~+=rfHg4`O^<7BO$yG&^sbXy zwp$aywl;?4tg5gp+c%K@T06+U{N1gTN9%vS-FKZMo^awBvwc;rgZwbRl`3Xuaa_M> zxuXo_)@zvLL@*T~i>U*tyoF?L1U>!TXkU@T;fL%%H+K-I2$;L=df%5=`lv!qnHqo6 zvbhMYQ7J>!Dr<6o-m#6*I?K-*jA==RW%FFxn%p#{Of`LJ>XYg^mz8qLK$AB7uy*R4 zB2vCg&5_Mg77f&d-HRJ)LxvZfaHwY?n( zXHI7L0w3_3AmJybPYNPjI|cE?Fg{#70*Or{aHjv}7#DO) zK*m{}t7sX2esJuVKN2wcKe5&vbKV&XPC5Cu9X@#d?O*IXr+Dr@v&IK&1*(Ich2SNa zm|S12B!aSC#l%`fG!gH3LPvfJyFQ%?`-I8 zUwYnomuyn0#m3qu0m}b&d*Ij8D397^b1(j1FqfOvd8dfE1Dpv?iTqSyj+R-->Lrt)DX!_J0 zuNaJW-TmteF8^AZrr+2%_L;r)@qB%+IkV1idIKM6#oX^zO>MTwVlN7;y*pWMswsE! zmg^$fGLU32N^D-1CKP`Jro3}WWfOVcCJVNKqFHXtDQkXHEZ9kVzVxv@oMmvEm z#-LN8((tEz6!0Vgc()~kr7u1oQ?)r zRH_tRbCR)o$UuEEl}JICTeEbhLlRuFD9E~@3bMrT9w$eB<;Z{6(HVi)8A)InhaeC@ zDi9#bm8p!146^Br)zKR-cK1dzv25|vWYKt3DV5$-)chB6?QM$Pc=ZL(9`xNG?^RIc zUd&`y> z$JezJ-+h@jXxxAB;+41D7#WT=`_?R-JJ8;BR6Us4HBOVG3NcnB0f~|Ao$WLM`|ieZ z^J^=rPPqPq$Jb!~_WlOj*6zON7c_->O{8bfdzZb}ch>l(svp-?NloFP0FUfIfq?Bu z#>9q^4e0G0T%8_{zED#c3al(RTJ%-Dm8n#!J!eYqYjS`1xp}J|jbPPZxA#~2kGDi3 zVV?Z+^X`B2!8<&min_Bzfv*`FN*;Ujt$%!O-u#7Iopbqr3Q&Gyz-d1^Y^z1c`CQIA z>u2Ai9b*5GB0KxSOZnz;<7<3w%$E7|4$*9EyqmX5$*lI*2{W6Xz51q$zEsZX8~esK z$KLyNF?D~Sj~uk$w;nf&=@|^Hy%-p~)9w&~gzFB6I7%&K=dMixy-F-cwQ4Pu00NDt zTab2>3!784QcBSb-ZV|#G)g8xX- zWyc%AfdA=xpL}(rci+4Fd+FGRMvuQY=8Im#GaP^8kQ|K2OSCnQckm7u`My5GGGvZ` zNrq)4GIS|hEELFhl6g+#cwWpW)6sZIHiJ)fXP2vYww9{C;OFUTp5A zsq8+{h%QmGaM@#pgB2qaOvX{8s8)M(OY`FK({}hT@A%Ds6cfU>wvPVy z6CM4yI5766OEU4;tm3e7KrW}aD8W82drcMYw`!Ve-+$%(vn_1#B<%m58HQn0s4)CHeFOU(;1BPV3al zvGPFQu=&%sJ32OsUvO*MjaObuCv)HE@t5s9oPTSQBr*ZNpJ{BU<96F+M{&})7O%@8 zxFw$PbByVAx||-#A-f0wsjw{g4PAe6Dfz6QEWj0(p?V8>MHHQ$g?%HL3(xq?HD9~9 zc`F$6$+M4Dc`EBp^Lt!JcsMF2VKPQ84Kp)ht9XFVfzfETr~tOkWig@$fvKm}Z0~_@ zjQ!@k3wS}454!w{i~nUCe^WQ=&x^5!20Is@wxTj|Cdd# z{Yo$QpJl(D_hJ`DYc4;3FxC%rk?%DB>FqYIZE0%kbtg)RQ!OKZl3DDT0DXWhvkEY_ zgsIKdWwX-Q0+{7lOm)>Qnwft#e@AF)>tGb`gV=nRbNto+Q=1m)blSS%#ybk+*k~%cEn$4;hK>t7jIu97 z3$^7+Wcd)fk_Hkv71bUN6)p~BjYXADx0%#=U4^FPK)@pkN*=}Rh)sW)VR25R;zN)U z>kyu}HyptR6e8>3@P)zfG6=FHxKV+U9)McR*eVmDhUs9av=w=Mg7Q7)awryZ*pQ^; zwhU^#4#-9V)j|&5sy13=-bkMOKLIF5qES>b*5xPd&egxLf#W`yr*714GC6IIgR4#7 z%ws&xD{o64K_1%ffO3C(*toV9=CX56+Rf*2?kGr3ADMq{n7T~j7Ah#@b^AjS`M*!i zXXH|;jBY$ZojinUCK0o0$jRefck+Jn_(d+p5IG zMhUy^vJ-p5?=OGeGwcoijL{`;CYo9AlKKZbdXsD0>c^hSI4hW(sz1P)T+rhA%twnp ze(vWN{c@W#Y_CWnU-s`vHX8?E?MG#ZTMPly(f5Rp@7ulS8^obdwB%bEVA_Qi-xGS+5wn_1m0kcHA@QDi+R>N<3t zvlmyjkc6I3*c29&cbC#`3>R4xs7B}TFq*SHaMs09FgUDx?*&wkZ9^)wlPx*rcW{uE zER0-)yq|xHIJx|u%JBq@N^p=j>r)IQvnooO3Pratq!-cT=aEiE?FHN46!L8E6Sl1^ znM@+YJ8MI3lRN6@eGo5po{bgQjS<8fmq$B3@6c`wHtl1UjPLWZaDIEFJFOcA<0kiI z*vkpz^7yysci0@8yHk}{B|hVog^~2vd7o=8`00Nc5>vX_!JCXI5JyAlSFAEmoM|d# zx~p(VE)wKOFo>h#q_V>+C6(;|K`a{m{#y$_ ztaQub2anx#%m3LbwVmhe#9VmAr3Wyq_&vrd9l#jrI;L2lJ?7wa9wP+?{frBF)kL$8 z$_bE2azKU4;X<`jKzlSNtQ}UydZp6XqU?V?VaE4P@7m*-QywWryI+3#PxmbO;GH+O z7P#bYzTzy`>>XzxQWXfD%W2MO@jjCs9-#88BD}Q$Oso@Ok^3u`HmEAmK~z;nayYTu zjNOJ`d+cu~9C+-vU)xG!`^LVe>Csmt%T)ftIhtmJ0!7XNKJYE_-znrsehwUMa(;h% zM{-mYfbLY}PXQ&k=+P0mkGEL-x-`iD;0YwS<%{w6@#ipCf7IW}r)aMw=6^ocuw?Tukk z*Dy5lSRILQCpNiI>k(`|qeNnuT&RB{I3te|i9j?)L~q_iPeR3*Fu9b)2)URdBwsZI zPX#1T7?GZC1gmyLRx=UFmEa)G&zXsn_fVjgingXK<86XJP=VIU0NPa*J<$|GvWS^u z8&K)@qqon2?vXA`sZ4EijnZvl<69?khHmhxs!@8gQtxn&RVWl-dSoltQ^J4bo%?Y1 z3HqU3J%K&%e_G47X!ThUn8UKnwTg+Ua~qhGmnBL*G2DD!Mk{f01B=QE4hQZnAGmg? z@%XM(?}PMn8iG|Uanyp#=hK$0S-MSjpCrFen7+f*G0{|^yL-{%FRdlI>agh(DopL} z&gjV1Z}7}(rINMTi=0_f=kk9p2k|R9x?cGJt%vOn!6g$gN#>j>+B&DSM9yT{QDRR9 zYCM6V_VpMhfmCZl1?rp}`q!mx`Mk=eI)q#5z3k{ zqdPL2JNB<*@4fW&F_Cy;H3gJc{Ngn3nB8|eM=WM9U}1WVM9N|X0;qqJJ>+||lZ!(a z@>)#^$}<6a4@A(z%+FeoVdxt5?kFKdGPUuih%Pmr4ZZg(|KLY!bPh{uxcRC z*81%B9^2Nk`yabU^4HW{EX%GleX`4`D>YS#m95>rQ`5WZqha$tYQUj4E1}8Y+M0+#eqG@%N8E`WL_W$pJTcr?uv1;Kk3ThVPrZ&JO3-*noW;K+^J(i58k)|Z#WL~O93W2b@ zT8Dh^vwwQ|L9d_x;JnMuduQ>|W!t0{?4yTHs~;Ir&JsBD2a|tiwg}l=?&s4EyZ%42 zTxWZlkz0B^>uTMN67Msd3nl@PW}yqF(nSDWlscy*t!iJPeUQ>%PH6bin{|5YRJ3{nUf^Ivv%-92~-+CPN zJ`V(^5Ak#yk+g-BPzNc!6tb>B4vvA_=phRms!Fg&1UB4%4+WL zPkWdJn#TT!{Ha4x6sst^-uQS`?f%lA);SvYcVNm7PxE@}R^9yE<8PokuH&^KV~@0E zEX)tL;HH0lMvlIES>fm^&4pRj66Csr5eUA}P%=~FKKnezbR69hVjj~<`Lo9aoB!0A z=-DD&oL^?V%Z0sn+i|C%;V4G{<*F}w-g$?PX_(N`_(C{b@lNukXW#oeV3L~C<+9vl z-f;vhiE=xffdF)>5Q1_SZ;3dpqLP7OnvjTN(i49%^ek9_0W$COyoz-Tmt$!}Awd(1 z=5b>X4uz19#BEYz(dmRO%_ll+8X3DYnByQw4mg~$`!k*BZE154-k-bevI|Z%Ij(-s zgXcc}>}3~+W;WJ7$d3#h!Z{swEd+`~jGh{R;O`;8m+shB6CdoicM=%c3&!Crl~F?I9-3*-ecSv z_ToElk7Fh47Spm0^ts${I|OvZ2a(8U(79m**+LEu$pg_d5t`^F^K}5M!Js8fQ{O2P zz%x)t5Z@SfLgX1}RPxTG4Rkv4@g#JG5A=VV6^Dc@rSZml?mpz~lTYd2665~cpYL=u zRaf3+rgPslH77Q#RM5L7V$(2<+o_iLd?(_aNfe6)w48;S=?*eQ^B9U{F>|LTB%*or zb`Hbq_tb~#L%-$8N_5q)Z+QORXYW4tndhF~s=G|jUU%FyzfXEc7R>+*4IBYHPJw^B z<%6d#dV7!W|KWd{CFEbsOq^WaL8Lp58ua9RFb46f3JF+C8Cbbx4zx5lUzS5H zDzMV?z<8z*zwSb@GmHGNhTe|Blkk5Hpxmm)3ofrY7)c^WF4P;#yD@jiu~N*c4 z;x$M#siMM)BUIH0ku0F18b`qrk>&kR$wl5{QXQ&;kn2x8SQCQ*q`#YQa_8s$f;rdM%c*K2P;3jDG%y>8u$VtoyEGQ4)OMF?i(wnr8;F zt}l;WXNqHbhsB1zLe!(63sjjPvxZ zTyg!m3sPUZsYNLiif-8nWoQ^Y^I83l@eh4gXMo$!CD&lB#T&A z8Uj@rs0v}gR1t_p&^D$5R5yi!Me5kd3ANvqvSL$p6sn2Ji=t=N94_gX#ywo`ibjhyh#wA@oo}KSygLKFhWOw@ zi1=a&MtlTp1p$^$0cL-Ww+v0N_$q_tj41hvo29h3#qG7fJwK+luHiW?6Q9!E(}`s( zyU^b^j6yC>?j;$mElm&Ia@RfEwb|sHU;RpI9n)Ola=Dv2yLuL1cKPLBTb1LL_deLi zbcXM-jC^Zn=SK*(O(TBPgRwPZFu)IDS?790as{xtG(t)mE@yuT>$xI4BsduA?jylZ zCGo@UFgP9sgpkS>;THu=tFHxDC_&8;0Fc#4z$Icx8=5LI?2CW6)AJz(rvY$OkWbi$ppUp;~6lDEV&O2lo)cqI8EZ;3ox@;d9$RLV$_X-tVtNC6jj@##7aoOc}Klb?Jzf2^vY%Cg`(i%@b-q6^5 z*N={zd;8P&DLh^QD!Vy{DKFTx-f$mv&&zk#!hX z31O~F6CQZ83LlSQ(Ihz#;#^oHTIhH$h~xG(aOgp$18#a?`2J;U@{d)uIeu7}9Y$N5 zQNMo|r(UvlvHs(?Uu`+LFW$XXwO`+Ve}4awM;;J5^3Xkk58nIKzYxw|bVz;W*ygdv zMK|>QvA=g+*OvH9m)nyUBoUdxD7;Ozb_7hF6J;5&)7^q(N6M>~uRyM=lgvL0%@f1W zmiF6Pr~L$Ij*uW}WN-+^NQ?mZ5!5YMgK>XT+c0bIo#7Arh{KbRB7dV=&Y`+0aAbCR zJZ^sWv^Ea`lwZU(__eLMp`N|_@=K4DMeg7le`%s$5@s2NVs_c$b;n%1`vHHIl*s-r zV+AX^bU^lzdol?oorOUhuFyz;BN_?#4TDP&09{!L*2luim}H@6OtHqnP)cTo)-ZoD z+rL@;?KfWi-q^8?H?yW$vvNZo>pO-q)ISV`fJqOT=gp0cDUp}|u&u{cQ(ePc{JTr4 z8td!6<#sy{V>x!8T)yZosK&$LN_`1^&haOn9t>92?l;i0{^dVE`M@T1nZEwTBg=Q$Ek z2H<3)h@^8!kn8Ow_rxCK8=#RzJd+?-x@17m448r!(OhgsF+X&d#5rp&yyD_3ul((0 zo1C?|;PjtVG&hWSO&zhO=mj0Ao+8>;_n~Kf1p6G+24~2Ho)7ymus#8|&y9b^i6H_e zHN+zYCB|898C@9eu^ zQ^)sZ_T+7+vBp5}>G8gSQwuqIzBF>)P568{IGr*y7{74!_MPf*y4DwR{$01^XKxF8 zW}n%ojo*LPLD5zHGke$f3T=O;mGG#>;y!=y?nB=DaPxZWjWlQJ8^})uW_~$LjjD-q z<=RJvE@hPKbjSvgpEq!`qt!qxPqj(QyN@E(O7|hMUv1hac+_KgJweVL>7nnC`UroerdklW`LuXF6(;*IMB&Y#`UY1ULi8L$>97nkqx~#j|WJT>EOG zFtiN$p_SkrZWN;HAq85>4Oq-5QLspWr!1jJMQ|&pz#3WzqmY8;sz8ztz~ahc<|Vs< zq6D8)LeS%egV7N3xVC=?pgjEGZ!wyhSNGU+PxN%I!|F9_(K(V9a?1MCtAqZ-4%%hP z^^Ajie#791udyyTv#QchKHB+lYg6r#zr6I}X4S^kgc}20!(G=+Ic3HH#ey4=Za)dk z7!*tqZ^#bJnn4x?W_UU{*7@0M#)leaJ1q4Ig(*yXDqciv$#{Qs*Bf9e#$wr9CA8NA zJ>!Zvc-H|uzqktVaV8qv1s~YAfPB8x(;Hd8Rd#3JdGka4)Kh-gKV#;UJ%Oj5`scq-_toCutEg3; zd-Cm1a$*0(pPaWa~6M%a^MT0b}3I@$A^m)NEMKCRX2o>Z_a~4?-IExSR48y4mqol|Uo41S{yWLXW z58Qp{F%FM+KaMG#VCouHcMM`xcM`!s0FtFb;~9AUZURa|cU*VdEhF1x;$W zM){6MU;Si;%DbN77^cZ>_1Wa=;~X3uY8q@J2c2BILOxC&FTv|G&`|3_U2r0NZUPXC z!{}5@MC2V&DyUFR7y2V4aLpzmaTeUN0G*saQMP{w*i1noi?vbEU}+k(k_R!=G4-M3 zxeZ?Ds;ho?!E+Z}e$~eM1Ugr^A0PDXs{~b=qH!e@lLdI%LddSoq7-DnyJbX{q~NUx zz#x8a_3}gqd@{uqgd{^rk&d+UHeVD;fCRPJ2ii9|4A>}n@4n~lFpuDtbo3ca_~(#2x7BA zwJX6&G2+Ae$_+Ml{Ur1G1|^JqnI?$VW7@l(md&hf;|==*XevGj)5_ZonOj{D{u={d z`dFCbW1kA|vBNTLXxUdzG|x#{VSVSp?;lgR3u2b+(;uvj=94%CGFc8w+N0H=S8 zs9B7`*32c2iFTIo4lfBn2q4w+DCq?V1TfO#TY6F5E)N6}fts#L5tNCx9b>cI{vak-W-9GXsAuVFz>T>~Xcnj-A$WkLhy8W`#8#EC%eyS9$Y(DPRig1bLQ6KW?A_9^bu{_dk2m$Z zZKPTA7q5PM_6}1{<781ZirVMt;9CxF-FwQu2OrV#!AIv3FuBFF)LE89AjFBNu{8W` zjWAL=#o8}3SU^6b!(Anz_tOCkw|C)) z6L%)pOTh55JhGY!mn0xh)__z@M_o%L9GN0qvI}9iiS~{tIEMt80MCxjK?0KV@Kt(| zB6B$u@?eMrK$0XM@cg5HPdzpFN8jIKRM_cwZl6qsfN-uFO}-p;F( zK>>xziX*;#_SFMh>NCI0R$Y2l6{oY#`NQ#^jn2A*0%!1#Qktr zPa$jK%OvQfSeM2&Hs?keRMMs$vSw0-AbV%hEO&#`r`bD~6l^fG2Sl?|Tl zE69SXrF)P|#i5iWxC9p>gS{vzSqSbhnKv#76l9XC>~Q)K%k;oSq6s%Ig7MTN!35y4 z6u8w9h{^S2Xex0iGax%;jISk;Y0*i6=fA9QOWt|W?{_zI-v>#3n2;}G4PLvCd` zu1yfQMqcJySfNzKahjJw(T;7L^+zF9GsjMFcJjfhtk26heI9)e@{yYaD1)x5F{rCMPntU80B$vlgYsz{=ITVr&7xO# zW1ZB3ad!QGF$-2;SYG7pl((j7R>3Jf@N`G`xQYF>u8Q4=(~XduL*bzHb>oT)PDd)8 z#_w;uDV{7eFgItagL!g7pJ}f`CkgDMFAhOl*FAjZ@jr>a`q9UYedDUZ^vYnn3) znpHiB`l1UTzaGZSxsA0SJluO~TkDhs9bGH8N^{bGjn~|ndivSB)2E(xT+1KM`Zbvi1(?)VQs^bjtdNO13e{0Y4t%!;QTKGc&-|4A5J5frMs zz(^!ei#kbIA0UBx5}sx!q*gL@NRULkDYf+lXbCSk!w-a8K1u<2)4eaWLm;07hQUBbZd>r_M;@|Dv%_WAbQUkfhxiMm-5)j~d+FBY<1Z>Y+D zS6OqaBuO8rs`{nCz*}Aad*TJ=w0FnRVhmz(xC*kwV`Wz#YP=#?r;3E0LPFKxmHik~ zI}@5wB0aTy_aZ&Z@B&+2K?X{jm&QUEGVIR0%$pfC(fzN)u3L zvE*kl3tb`a3lAn%sTj(5oY9=~gkUg#cvm8k*r;8~=We|1a6@J4l_CLUBrr*>Eg}{r z{xqK;pn%7kH~KL&l7=@Nz@U}|8?M2!^)WlxlW3i#5{H+7Wq-hhWlPthx}gTujaBIC z?t-BiNTl-^Ag`Y_yA?sdb9YM@Pm&#u3yO-eWwII$-?8Cv)0H_UxTilIgQvEC8f-R& zpwEX5ZVOEnw9Lh|7kTF6TjcY;#FiX1=io)3%zthdrzlPtOuqc&!TVmgOjlQ5_2!3P zcrDgFdiC_gVC+{)XYU@WWmXN>2b_jO{1#Wxeq+s;hSEqJBkMXad2$;HpR9mN;`%(# zV0Qdu>v72Med^Z7dW#C3`jdBBxi}+kMOM+I( zo@;ih+a!w~Cy%mY7zEr}h^#4K%*nq7?{JdC1%;VH=CXs#`LQsR1Obr6(K<`(v!0$Z zb(>AFF-m-Hl*eS(W|H@(RBsk}0!Mr{tN+LJ82+C%UT1jUl%NAXzlU6Z7=r{bA{tsI zBiZ#aoP}XH$c_fvMZu z?+oWBi@pn$Z58m<)naHMf(`;CWw8#va3yhgaV%fGp8T$1c|w-8SnZT*A*^|W?rgB4 zaNLvkpZop~PP(plV^1)d?qlGT51N^k_?rVrmISDnhCt2)Q&>7E=J zhLK^0oCH)9iLL>2Ty$44>zY;tSJ72*jRcVu5J3@!oMxDur+Y%@SY0`us*`@tduqUh zNRa*h_n~=`?F$vsg1yw!#~zJh~kLu@SFT`tf8OIHPA0os7Nh@56xyV_d99J9qH$ zv5(M}zwt_iDi8^-i$ogU=JN+jiPZE@r-vu@uu(|&KeGLQ;nO#s^qw1kaLXr*O2v4M z+WL{U=AKo9o9@|o&HcmaSNgtxx@y&GP4#%t@R}9K9vE_Db&fzEo5>t#Xu$Bc?Py!* zf|^rt()q7LEEcEN)X=_Q8ImVvQK}ixvSke1^9U?ih3h z1*Llod}0>bQ4vP5O8Su@7Vjk;A)WN}kezNp?rCBangKOdf*p)PBiWWnZdAVkKBPJW z40ubhB^={yI|N&DK~I#)PIH`)Nm%^&IA_aEq$kRMKk9M(-SyKOf6yI@w>`vLxefsp z?AW^>qmv1YPZlsSnL%G)KY{@Xidc3EHpprG`uZDxc+ab`m$zJbxp3k6=Ux^G20uWA zwqN8eiSQ6tD3^qh@mZ8An&t5ecBYa?Q4@P?eXdtDjklk2>ggm9!nwtKiZy{PR}HJi zrqxt`Yg)NhEzSPD_x;{)9yv)Am8%t!QA0Bk4Yxq`XhgUrENf|m;&o#V!ub0iguV&H+ zwtA3HSFnF@f(Sq(GIKg?kAlX&W{eEyFh;Ulw!yEF@9tcZjxP97LZPs!5R*EdDpvd_~P~7zWU$a_YXPQ_}3dBeEQBC|MSX! z#I9ZU@gDyoCE7x^R)OYoAygp1t-2BDU2s1B4*=!CZ1KaNJTxlb+S`Qp9-ch^`Y!GK z(__KM-~EZpw^;GWAw+`1dCoKb?R%dd`{}QKYYu$r{cRIF_HCNq-WiqGEr%Tmqn1lS zCf~-oH%L7#7`)*ojOkVAW3v$QMKEuFVzG2J@)ymL6;9;~KWQvw-h0bAr>(o<4-db{ zR0qoejZS~xTA6@j2Ao>&@=~8_&(~Z1xL6BmyWVcn6^30Qb5D4J9a-C)gyg{tIga^g zTyu6kcY^SO?O%z~^v|J2yU2APfKAu0BUIkOl`Pfnc&4P2Hfb95qje{W>7?O*(VG#5 zfLdY=64^k$SL0FrbN$I8GT?J&8=3R~jvm{O?(TlLRbRcuC;>o;ER5T+LgbRv09dbM zmB6Av#;Q2EmxOi_YP=6}q#LeC7s|<_^$@h;Cqp1Y@V7xAhgHc=AwRhXyyQiod4U5s zHQj=tcpzIEJmf^IatbwzoE|%W24_=Bw1RVFPUIA!Xe(sJ^<4BUELSdYN%B>am=`!= z<`y|Wrv~SL7eCj91PtFwduf-z&97MMp9lh;nh!7)wd?$fW6Riy` z;I$ce6%D3G#bbNZ=+1g&iMPV7E#B3Bzx&cqDDaI;CikMGQ)p=Yf$W=q*W8{+;;dzZ z2>R=}`8K_Oj`rNi(PvZ&7}(p2#08z6-m~Smr4nMgQL}qKw*adOa+v$!%zlVxpMsX~ zZY*gY$Fv+k21R7841A5EsuA-NDph#XQ#kMPD9%}=Vqt8V)0b;UMd!{qvkBUa2V0)W zVd~MRP;o6sjc4NqHJr46{4mnHRzh0T9_jUIpP9L3 z<^|LATvbVWloU&1~TNKy5pldjtrk}yg@b!~q;^0_2jZZ8@e^~RUG?{FZ% zef52BzjiJ;*YNhUHh#rjDPO`J*&7Bo2k!(S>XgmrhB&)I zKr$4DRe8mQ$}ehN|CRq-?^47sXt~23HPY9C!-sH)07NpKb&f@I=_HDoDa`L^MpsWO z{Go;%J%LY8PEGzJJp}n(7x%eO{##G?yq?d7L;elAUiGVgqA3!4;EH7Uw5o>wz7}Kq zt|{blI+xBG94iizh$&lQU`55`L#XA6j0z?(V#S0Ynl-CX6Uv3TRN}f9icWs)>)#Vi zZ~T*%rc2ps6;&bxDcyrfv4&#@wqQfF8&!+7u_C)@M##Z(*gaAIP86O21sBJrZsn0oR-AT0>{L*8OPHC<;#j$csu&<&n}AsU<+E3vGs5p5w4-A)(frmuD9rChL{U&m z=1?9UMsp+xW2_syN2jo1Nv9L3fjqgvwJs)rObg9?%8@!f-vNhsVb;WgF=zM+p;T9gbC0YI=wwGM-Xpi)SG zAwRMMf+q|Q8%ak`&^D?N>>5~o4lB}O$+rUQJw*>#B!J0|%2S88LXcHtiWVe#=qXPh zDg+ZHtqQ;BC9q0PvX%x{N})Khn@CKEjB6Domm880b3EJ)U-+oUXXig{76_HPS#xG` zfOd$%ViL3&=%zr5B zyBkB9Y0^JG>B5>3-Xq0H$>MPWA-)pU9}D1)Jpm+|I*1M{pK4DRFf-GR{jM5+vKO5K zax+*QO{1*yXkDe@j;Ws$AmVZ2H7hZdRbd>p;O*q_zt<0A?5ciz>FpX=8ezX$L^@^R zi{CwpY)Zpx->9!zpED~bc zLR*LI4sQnQXPhk!93HymtaWRDzS-a^=Eyq}ZAfm5({u0{ zEDBPg|G&UlXQ#*n>9PHmP#*YLJ{|=cU|qdYQvg;uel28cQ@B` zi<>wiafL~TvpNEu46E!S<2pMIzOx5Lum@H?4}13>xEcj0CtrbDwv6mZ z6$+6KZ<_^y_2dk;(7z3&!zo*zN^zDsAC)vYfLxYHT?#^B%qiqCixL};X0zz)tG#Z% z=_Z*d;Fn;h*5``_6!JEIcI+QQHdkWvXdT^WGcd|_0YjriE-OTGgT-5ZcFWKIkvXbP zP{cG%G8txrVOe%MIU5q7i)ygX6l9lubl+~iv&C;ujvwNa2_kdp5(AjIVxhbf0+ncm+g_>m@});X_>e30{);8PWpOBGV@ zAl9#V%@+gFzcVLJVP!$fkdOobvF~$Q+(T z%i0x~;SC4C`Ev)5xpU8g&4Kn?u9)3h*?!(R*R1~iA6}KdhCxO6#09Eru28J5es^2* zeuEd1$F#~0O%S?~t@TQoM7J=K=yTa-Q<&!#c2?DI&r zgtc3DBV$^BkgH54s+f>OEU49-ZYu1Ag^fTC5STMbPWn9_*9T7982j$SkN*8)Hx?0e z8k8GOPNP$WZ4gor>jl;I%I;@pdDjygIo@(!XWK02QN-Nt8pQZgw5&UyY$(aQmUF{3zopFo87Hh3C{FsnnbiTp6RehySB{PA4L!0HvyD2$C_{(u8uX9VHgFGQnik2YlE(u0Zj$;TdiYsq74-_LV5kjiX&6vnH`6yN$-8Rg4BJ&B7bov^-)6?t~D}g z(I{j{pB2=KSz&7>!v?dGENIpjKK|w3{q~nX`H0VZ#<@o}-}%4WgrL z9TCW$oL9(g8sV79ey+A+$C@@U=UCV0IwS*jW*Frdcc=S_q-4q=u3?{$@6A6S~~62n-%b zuwil@5LQDXYhwF!BZ7W6RxR>j*Jv4s6D3Gu3v^Y$L1d9BY0d;Z1{BPEnaI6aPy2Cl z&P=Fky^BVPMF&{1LOuhFNKkzovW!AahD3lZ8WiAnl~FXKl3A+!+c*E?GdF(hGym~t zsXlIR=$jjsx4ixFM@Gnhew9$Icp&>p)$K669v4=f)Q@7WhS{VBcPxNdUjW8j279(` z#s!xw!meG1v1Qu`3?l$fP{i!aVe%)1+%%w7Z+OZ~p$Sv(D^Z@xZ2!zYMv1!L{Xj;`(CD2HG0{hZs^4kRP37|0|eS~bWmz>^#p=m4(xY1M9 z>!-(d-LJDVQlEf^?H9bk#gsK_zo z>GynP3_fD1#o0qnp2W<-&Gau1(H%f@(1oU@-Q?`(b!_w-{k*D?0V5|;s+5R*izpjp z5REKcETmq4CDvM)a=_bUV)osY%0jSOVXIn(=q2YW(%y`UBi>*5@*VipwO2WSCY#rB z|E4{7%avz1@A1r*{n)o@%ZC=F+Q2ZL8*Vo>1`CYev-S+V(w{E@ry6dlhUJ7Jt<$>H} z;kv%3_7AZC@A=+fYUI7W+ZPx0D|#A|#|=Hx4jyea0@OJZlWq1u$1G+3XG}Imp`t+e^wMxax(S)-JEmmC96CZoI<}N8hda@ z2Fpr+CMhUC)%)W?r|rhqcHf-;sB!7{^2ti%r3z5{nt8lgv`?>OYadqBhSiG87K`aw zCkIxLMN5peq+^Ex9=Z+ohw~_Y<&FSD|z1!zU#fp>t1)!nWDm<0@f(yJ-cwtSvY3)+dWF!q4oxo1}vEu~g{3R%eRPN#1Sfr92GKr!LO5;r{=BZ;7T}tEyHG3W3Ff z;6t8ofS*{#(#8<6)QB@)j%lk!=srF0B=@1Gx2nA3Tld`3CIRaR@*E z#eJBXPT-BNKOM`LcVTJNgUF#N96ZcpN=%XC)li_%H!(4P0M)g$*Q2=ZJbC4sEvNnB z)=$o)6OX;)BOm!Q(G4~SP94_se-_2uA}sixG3Sf>n@8gbSJwwwY`(j1;)+Yor{SVyN8k--u~4Yt^QAnlxa0T8OK}7jEW&iN7c&Vj6w#=s ztk8Q*&eRKkDxA?)46u@3PbY1bbbn~eBUd=TU(XMZ%=4ak&Z7^c zt%o=Cr~^t~yFO0xpi4Yw3*uYO+}QWnrbh=~*3s2}v#VOIR(yUkq;nb6%2mig6>TRk zCYDPq{LyEy3O+oYtYCT~fsj9dnXwU)hAJXtp!{TbS-(DsSogx-Zj6k~qOm28cxw!< z8eOZc75Q8N8awI{Z5(@K2V$%H98k{_Aga~$*o)mK&vX3E*Z%9rqTKiuqPqe+HwR@t z$yvvL22|*oj{f!zj3g3B8XhDI7CZ#9LP8Nf(zgH^O5WQ5)ssYQo(g|Nfk5n?yrM0u z9tc*JK1?eFcP}|+6=r@X^u!ol*Ns|AgQ%qezuV^RGG|&5OWSGk7P+57DDcEp-}>$a zdxwT!wQ;1k{OG?vpc|DB8+sMncWlAY;RM`&EVTw@up+YM%TL72_yBULDRNRg>1?AF zjl@3pgIj*I|K+~tuYU3Ku9lXzk3i60$4gSv`1F*O%a@aLvvZ&8>tD3$wwr&r#G{HE z>A9~Ec;aC$(XNq?JFtJC{OIGm_SCd$&GHDQAXk;C$%(th$3_o$yuO&n?GC#I;oBa6 zw`xeD%=0b}`9e*p)@l#fqLNlzy1P`&2NXpS2vGjnEb24f^cG$S#NU=L*0}T>*_Mok zh|hMWI5kIPbj%u%$;$4`r7@4d+x+NZWb#R<@?HqW3fM%Z6J-f*PKA`OAj0L)HIGH# z<3N)iIuQz=ZewgJ@AO{H5n+#q0uI1`)H27_l9QdhHe2vG(IK9d{t>|ySwSveK~-nX z@V)1*Ua{gw+js49!aR{k{K7kaw^_L7caKBV6)fvp2fN8dI;=yT=S6pen@GCg9A`I2 z3}j0@QiDS{a`Xr`-7msR(0eACha!`l+A2MB6UYCWM9YCh0qfr!5V-wVW5>uE+ z&0+=Fx?qHj!w?V*y5aS!Fib0C8HSLXN}KmTeE&U!YO3yo*`Laj;KH&G03w%(gup z8d`YHXOs!dsq|WxtuKAC@68>bLRJTFHE`zfv5JHRsu>~^855-xhe|O=-xp9J5LsjC zK?c3IL^2y{!m{^&8j6>EIRQo%5VDVZP!>^i`2s|oJSf)Iqk8bi$Q>$r{4RX;x?d41j^MNntI*ck zf>OmI!^k0(&f$aadlRCuAhM|}Iy;-;_qd%rt#i*jkpxw7Lgy@h8q0J=@w(8ltQmo@ zjU)SI93h}PUeh4Q6iiJGAz#X2QFqJQxIcJrM{E1FPu_Rm4^BS!+~*weo&!7oDw+M# z@BY^K#L}y-(@jBGd);H@GnX#bYsLIPUYg!z^>_DIWkL0K%?GErP#o9`E17`dBBhAf zbT0%nELE^#YYmfs1i%*%n7nPz0qoDj2w*qcBNM}%Gw<}-whC;F8F_}b#tw`f+dpQF zSH4)PR1aVLgFF5TENtKYwDFfe`&+AhdApz`tc$gL`P+u3s{*}8Avptf%sfQ*t)-;{ zy{+!6V(}izvVRYm74s_TzbQvySF{@)z@s?bn|g4cu~)v3Xe_Yygk7goI`FY(K9~#HHMg5pRzm?=NkHt%gFg3NHgN$A)>l{EthT~&0y2R4?qxoXla$4YIKEchl`wIQ)n?c?mlwz zW%#^-?|s#{b)n!HGV zrEClgCg37a%nFH+*DyAE1YFI+{rBy{Xm%7?miKGdr<$k)E}{*6h|+T}5{hSq9La2! zK8JyhSSP$*m(!juGm|E}9z#`Q#J3Z)9%Rs|*cEFSv*eG6BiJTZaj$UcQA#Dm+; zIs4ob{&4@^mnx(Gh$YzObuo$m{f7&$Esn2kQ_8Z zjkO}Wv;(1eOW|$pfh$CI$R7pI+6l4!8dc{y^=UZuOS0;1H5iB5z8L(n%h8n2k_}6s zG<5`($wR0nM^R1@h8Rl{&ded$QbXgKAS9MU$u1)JNR-HzyBTnP)%fv0*YPD* z7c6%p3h-c2z>8QYS|7sBM%6ig6Gh3 z@O$ttSDuIUuUUfM{Pt-w%Ck6^96$L5Y<}hdjvkxFxo5uyA)gmlU3C$H#M(tRJCx;r z&qJcP%O$S{dX zZhG`Li1o&CL9U2~o)p|IGCp;w7Z2>p;MN^aL!HS$$Rx1ttSCkw--*RS2;BrW2MG8| z4Qb?gAJ+G_;pWj%zEmU=BAKt4uGyyeTD?-e(X1INi_DdCnYvAXAtkQnGdP;h!R_@% zCPqCUHuM_y?B4AZZq=$4R_OGbFMj2}w=~H5N0#}y&-Gn?{*m^v>BcQj9E3L>bgMzv zNrkd~QrOq9o8Icam$Sc0sZ?UoFHc(&5urIJmrM-@uynx^j2}FLQjx=Jmj%EpZZr`< zwABdYU<7BMyacI#W5XEwubUw@Hy}iNKJ~Ok(9&gW+j)Q(up6Fm7#q%94%P34?C}yS z6EHzwv`VB*BjDs-A-?+r?Iv&YI2m};(IcDezB4{pK0ToJ?BD(MRk zQzacF`t6cIdB$1^kkxbFG zGGw#6I|J}41TItsN5`_rX{IyFT_pK6ipK!~AxY60L{Y;Wfq?DPM4Y8EVr~^~ml+j# zv5#JV=m1Jp62=!ad$21hAWp!vOrR`I_bd_*!>?M{Ia@>8Pav4=WVKjE8{L{*HcddV zhC`u%jRh@X4{;aqx+hlqWg$ecnSS`>&# z?NP`wf0UP4IKWn-ktoaidr9-P2?DKPwVYmmK>(2pn3`TcDN_v05p2=*cPg z0wK~d86ufap|3u` zI6+w4yT^b3Q?rqBDONZ#$)yd$(m6gQNxaMLlHCf&iC&k>_R9+Ac8G(BokWlo(kPvO zMWt(16v@AY{K4@v_8r>!_J#98bpM-X{SAk1-@N5->)mnSSI;@9I{A?%M9d6Wh%7w~ zD?fZak;4Y(x7`4K4$I^eyb+jEBanUni^#G#mf*fyyr?Cs6 z1t->{qb%Rj0i!kKr-xA+`U>P$dc9SDWF(qoi?D^Bs&_qj^&Z7P*YPEiE^;||QL;m% zTqYu_3|%XLS3~5B%BWV$sAR?oP%T5SYfb&(nN|238c;I}FzEl8<7QMsAXX3#H=sLS zfh1(m8gGW%?XHhyB$2CWS;#IHg&?C@%M&|kg)6?qNeVW~Dd^=i+_65Vn$IGC!88bb ztXVJ*-?{5Pbhm6k)P^xNoBD!Z((h_&Z*dLH3}e}x4sUY_ZUTe^1D#6mSga9iRxKol zkaVtn+Qt<~Bywb!6kK`vxy~dYmV3dfz*z=J0~tY0s}idwh7)i(?Dx8F-HHqu!>iwV z9u_a@V3X=eF>G|LRMFV!N6Y?yS#p2{WQFqZtm0Qj#uCkU?|900;c4ApIc4Si*!k;x z-#X#SJ2EeH^CdOZAw*lg*>LW~t7^K&Nm3Gvpus+g!rICEhZccnTpJLAQ#_D-WS zxB?4vk3hCdkVOOCZ9FC(%Ho_e)?)LvLm1w1n0te}&0+kn?A zqh(`H>(JA=&sc@o1FcORdncwxUsm+5cjf%^^O@>xdPP@^Dw`6c*O}_^j2RX*_7YHM zm8#WpHcvl(m>2n8a+KXJ5{OW{`zLFanP4$f`haKky3Wqlw)J`1=WMBpMGIRoK1&8J zT}5TK_)fe`KWNi^T8Wbobwk?sa#xd#=9Vtk-$1fi+w?58E4mMsh^Q@T0r1b;`z= z=z$bgkgJ+bCxUoHM6qZfL?EG~u^XK1huar`N44umJRUz3UO<_2H1VK-WDeb0Go^NvPTZ$SW41HaS78J_RE|WKb7S^1E-|Unm~CaQ*s+3|;%i+KVrF zMQI0rxgNatuD0fX*7)s%2lw%1jf2}CWsxotR|3W40IH;z6VoNwMK;1sKu=c-0hE}L zFB|Xs)m?YJpf3ICYHw}juKo9qUa)M*!vFO6T=e?WKzskS(BlWRnu;j+Q`N$M(PVmV*5y`(`F)*V%Fbj? z8P8Xk&9el$B5%~JKqz9y8-vEeclK_i>j<|Xn?I(yfi5}JboX6U)QT_T#p5)uW{HL(qi(ICn-KZ=%S7W7F4U-swM@wrZR~pVghpz(ru}NS>vVkv$;*?e?+;nV}ODWydeLh(L-^2;B4=hni1*!Z4j#*&+LU+C!%ZQAt5p1~~b=XiCD zGn$!26$!AmE|$PG1=-XS2kfzc5{ovQrsm`|)}xTtaqwt6e`}hCL#CXcJ&QZ*iJs0h zJyX~4Z08Qv9Gwl3;K}~7p(?4VQREXNXly+fa!Wsq>6=NmCY^DQ2FatDHU45Hg2J>C5G#>}5H4HMh>+U}Ve_}#ecuCrSHAw--^7CErF*A%OeHjgrjrEr8cFslPNY*H zL;1dUy@^0##R>HX{3-!OHw?oB$IuQ>&{M17*yOA;DsN*@D*j!xTuE%)!V zKDoN5Vb$gv&wlGk?|S~co=C9uY3k6G zmJ+JNesU@bKK}ci*f9SjH0p=(`sg9_hIstX)gF|{AfN8J1f45`7`ovmvoBUE{M_7diaCdT0PdVJm`bE!1fy5^*%P0rOeOQF-wSjav6;K7$nE_qI@F*)FAAK86>$Ix}z*(4jf z19=q!gD!uwv**tFZP@wXGuZR+GiYAF8gF>x20Thmb?+^AW3;6i3&?Tjw(dlXoTblC zj!Yus>tjXhbr8*%FRQZWg?h^lX8&Zxt+Y19{^mUI8$SE*0Zx?O)a~LglcX`;j=GVW zvoW>xAP!CBFiHTRNl0UVgvb)hZ|7x|WVwVu&<~H#k5pA6CoIFyD^A3}xqH52glnY~ zgxVa^MUmt?4M~2I_cyI0J8r?O7%1mB1f>C}YZub?Rg~}D&Doj_(Jz1x5P;PS^IzND z`*fvTykX5dKKMgC*GTS_uixMO>xFG?O}{ZMt*KBZ64==3^uDcsXl1wwWU!$JM$JS% zTfof341#p8bA`~fH)4O`@h6^q%(h?rJ{I=(yE|H1J{*s$or1)Rm6FJL{9(@P^+PM> zF)}h`@7uQx*<^y{S8;7Uz4n=BonrHLjuQlnz=SiE++)(3D-}%OWemNWn{Ku*;fg3Bk zQSnG9%uZk~&;m{NVCJb!$a;F<$`zaus3zRV>9g>QGWvauII)ABPg{ru!$gY0;K*JK z%(+O8b7Z1_YXmH1@)-rvIS~iO2c0}X7U?QBwc&!ZS8830+drko{daK!M_h_OecO(0 zkGxd5tkz1ioa)9Hy{ETUBij>&8t@@stzkCWih+SCaPBNRx*MGnG{LOGP2kqz7J9p_%Cikm}JSr zTw%>ZP_W?VObk|Z)J&5jc;4u=t)hG28XS&_A>Vl+Q8;fM$yTMNmF`}<;=GHt?0SOj z&30OU2Dy$06j+Lt=tL7E{XS^vhmqag4&TBnAXW$~D1uWsE3npms7EVVu!(by%0;K6 z%d^qR=QTX_9z;&s2Lq)R`ForE&&Z+O$R?)H5>F!7bOG#ImcGVNZ=`NI^#e{cl=~;5 ze6^QoSUe{cld#gWs1ni_7q5XDX@gEb(+UZHhZU5|8jSJX(5po!0$7~bgIYNYQ}V+g z2Ung;qSqTH`y^oJL$@N<(giixfXvt&e2snZhhoTNQ%JDMN$w7Kg;__)D~bK6NM5jh zE45-8G8vA-$dfEr5&=?XW9hk=+50e2t6eDbWrTdEdhgrxkbUXvF8XQEWnV%-g&)p; zl+ZRyg4AI<$Ae6OIGcBvSG{|_2zI1ZD3sCEsNm4xIA&+2@Z{5b@TNDMhIpeFH{J3F ztXjDU7oLAI?!9L--bkQgeovcIIrh8XZ-I*)uXMCv+4A0cKP{_9NZdf6y#ZEhEN0J4 zUNh%%joIb!4k1JNqv2;NQXI>ceJ+;aHh*hi|-nj99q^Ev) z`!D~lvyS()c)%;~td(nDtQIw|S*tzQ-r>2$)tg8#y=7oiC+0qPY{HzKOZ5Z;UNpo) zw{3f7&!;gAsP7K|P(ZK0oTGDS7$@+crRQ)g<44beX6NXPC+43?XX*QV{^fv4woG2u z4Kg;O$Bp*$*P}+H!(PxzcFb}ncd^Mye~jqNTSg9#Kx3k6;K1)TK^z{zvL(IXPg;Zc zp+OvcbTjFIj*T1EAn#HzNwU6o_aGXZ8i-`nM~4Xn0t7OaCTBPVhJoo=G|Y~@pBGTB zSwL+JUv^3>Z;QHAU(5~QxMSNZ-C?rKy}*)e*{FR zq;t`*kKR+EuX-wokDRg{ijvbO2W(_vSOT(xhv`AMIa%?*B*zhs=3tMph$Ihf-#+Jv zO70O@@g=wvfthe@UDX@8<4vFb(l^gN=d9q(x844tqk=EA9{Alok>;j`J1x69@9@wJ z=JE!r;U0y5Tcl{@T%(`hKNyxfTq2xKJb_ ziju$;3GT^?!sRj<`~FRj+dH@IwQ`xF$<*q+3r-ZyJ^u`PY6KCaFweZtu3dZV@!5*4 z>s6a&(gZ?wLrIgp%$JEa-<8glD!eFFWkr-EGBWMm?WNhtq-<64KEvege`(8F5RJ9| z`l1UiKJV^(?idIDc1LB?w%dtzqpyi{f?l;{$i!5(g8l$KuYiQxck5V8cI%9#e*9`% z76BPVG!5LpzvS4Zp2j8UBADk-0CI{9qnxdagw0$T-(^q5C9E}%$&Gv;B#-v78h^lu;k z%8$SGxk)u|{!p0UWSyR;9&JHd@DOn~k!$HgW+;KUEt3rA zK_ojuwr$o4s_00XJQQ}6Ph@kJ3}TdgMuco5Yjo}vC3Gqhemb2-Q@#l8sa^EidUvgu zTk&Keyp{gP0hAmAluqjEvuEG+rjQCNER!TurhkMy`6>V6f49@mWfsBuBT%&K({#9c znfCF&tzXAJez`xMr#~06JYLOhI{~7xLwk^(nM8X_2A!+lNW^FcX8L~Edb)lc>v-N| zy)E%S*Q>ll!_WmK5Qis7irUeQKw}$nLt9at8Gzt&Iei;>Q+E0~S$Y{3u~@BUpy+Bx zLTth0tcXfwe-6XR5;iRDa~#~*_DyK)nGew$g6bi*KRk-v!&A;2&yYWiHXr#~ivXm^ zB0V-aJ2v?s2T@=N9Tfsr8L+CmKsW~X$(N!uHv)xq#pgt4&h7cWGq%xoCI?xLV6!Pe z(WY*xEjQXTYWli8O$~kFwpG`g(?7oE-e;;^&wJNxZhdI&#%0-~htScNaz;j}ZUdE! zfXqPxe=WN_^@18UU4RTakv>Qv;IqmUtV*jhu53>!hFgvf<6lG@!CC@MXa50G_y8I% zQ|ch^xB-AY432=jKU#oI3TO)k@{*ss=ylxUoATVv5B=dO)*9x47ZQ?={_aLjk-ui? z)@3^DnNlvnH2>tZoHW%<@8wY>oAW4kL9%#Wf3tBkF}K&(;QFSnnHSlj_4sTmaqqK# zQp{(z_?y&1vgGy^Ty`ZnaYQDk!ZNx^&ax=wE9Bz;+=0E6CAk%ifojR;Ms(eh<2k`q zC)wX6dI(h34Gkk5ZEX#h?P`VpvWroE;353)+!U5}#^7<)6U2Miu}x!~jN>%64$WcT zf46>yQ`RiSTi$yiip47Og{;%cMfgr|Tb(*$-Cc*PLWtrx5l>a}yx~`trOh81OJHI)k5sM-i>1mi zQbP7ctkC_FIJC62!S7Mg-PKu)wRXJe%6ES7C4)(}4h{~QULT(nL?;BoX(h5Lf4Ydr z9@~n|Pi~(%IyU~aEXwZIpl79RmSY^x8j4ArAc|Z%HERzXP1w3oa|-=9A_w;fWpp(7(BIR7pKU%0T{9fg9-2o4 z%92q{mPt+x4CSi?PDQkk{VNtLXq``X+^nIL&BDz`2?z#pbRvVO>g)W=e|!CofBnMG zzxs*m0XNJPd?6IrI$YVECVw(* zke{&7x}X<1lH(NVk6g`Sh-Ae@V8gGt5%&^lA{p`f6atj=Q&Zd2vS6u|96c;1Q!_qM zRFdjU!%*v|K-RpTlc^DqM)43 zMg^bGUn=DI1GnA#qkOKoq)BlVA6>s@Ey~$5SivPbM`Z1$j#)KCe?;Et{LsZt7T?NE?Fu#CB64kgx`x2c5)b^@koKqQ}*(`wjnll+Jr0=zTR(CIb3*sJ15rGg2P z5mq724l++-5kJB8$F^N;6Jq1>a3TwN%A_-*)0a|^HMm=v!Oh-;!a;i9MenZv4eKu| zx#|VKtRbOOw_rIZe@OLHPCOTl{Nt*pRMi0{=NylZ9omi5)Fe9ElLRI&BQQA$Grxmu z-)`E6>9pc|7J!m6JIC+;T(9mTlWPZqjVpP9pPFVA! zCXe?!Mb#x{G!AW*6gl!9`a~XQPA!|Bvt&^}Zv5#T1PE2^-ZO{?Ha!Df*RgW>JTy0k z2?&%C35W3Rf2%Kc_NNdlJ#p=P=QA?~6bv$Tt+OwWPj%WASgaSPQ+gsgV1zXX796OmnP9vD82W1nksax-UT!9f5*#t-Z(M zD9>ii%F#onVem4qc%Uv`03pnw-CM*8AF=gx0a>jHf6H7gI8x}uua6AiqAmsQLsf8t zM~<^S;CQ*bvrLP}_^3&SK7gsD4=u40W(l&m8~Ao3EG=8J;583!edej>jiU4{pWk6Y z_@1fjXI9ch0+Z!G2a~9`j?n2%i#i!1)3%5gaP#Ih*<+K*knJwt>hikoBuu0YADn*f zJrs|;e>V^ouZ38S=QLrfORD@3)yd{ksY>!9Z0 z`2so{8(>)VlnmA>gSXhIF2xzTP|KH5I&=g=M>E#F?{Y+M_!&7q4qX)kG1{>)zsu<{ zIWRWuOv7OfuvG>zED^bU4(V*(L8i>$sWO`mf2G4M^M4Zzdn`k0fOe|T>7zY)FgG!b zBcmDY6Wo|C_yK`NNYvc8^5oF&_VD_6)?>Ijk}VLf}us_y@ue$MUn^)l~G?=UkkbVLwPP(dWID=~>BCMIeUHEI$| zG$ye`V~M?MBtjHKX@YcSh8bqwl-GN?@AiIA|E<068$?AB6Muhs_*~wbcgs0n;bF)4@DJyt5M+d)U;JDQ!+g@R~nm`di~K8Cmu<~V?kwT zV6W|npR!x!v=L1370I!QSrlp}T~d|&ph4W!rpey2TP^#PPhZ4#>&v>LHkxKLe_%Ju zLaQRknS}|Lm9`o(Y81GMq8qv;I7&!XuU+3a@O1)|N1l9rmtK5%cP!QQ;xB*s6Q48$ zS9)yT#bS{r4aL(^(?g3uVUui3CSW-jGjM#x#z*fPL#F27oFNGptXB}Elf}w1R;m`h zafrsU`4gPE=`xhbM;Dtu0)aGif4z<@kv|qowV*`6!y?U?HN&)+!H5HvBc(Q+rN- z8)ORyGWms)DHGVu5zuA1ngY%UlaC`pmL>8g6X=(1`d)iF@L#nHU=^3p)7Ou)&e{(l zkV3Yr0}au>?5AgL9=_=Cf9Nyr=rs57zuN1}uijQqDJhYusx(wg9UO&HETIUTBP3$) z8s58&h=YfCt&aV(ODO9G`gd)DH#viFp#(1&M46t~k`ZuTPT#3ZQdsc-CXbEb^UHap zX^gvw2!~nTBTE(YWe!dQ6z?t9#_ouL6^fTj7R71;WmS5Fbp3&if8DW9J~4J2_$hpK zk>^D+P{zjcIh3ar%1{4%qN~kmPReR6oK6gVZMH|~FeV&tl|AN2;qv&4$me=oOu^v)ebn68;&PSGHv zCPv@a-GP_A_zJx9y`RMEU;A8QlPlP?KFxv+zVx|wawg>xL))}@m;()u2XVGtthCEZ ze1Z3K<%LLWi5h%o#A25xsy~Ok z_Ut-WT&kFVe~^0abN@mPJPKCMxFucUAY~>OLV2!WC!FH6Ffin;)}mX)P!wXEK*O+& zH!Ph%^6UTtizcG0GYBr_5lHUBqq!~k;;e`LOGi17!CLo&cGrORgsc#t{OU>?R&+Ox z|CF4}r6#&kOt^w45<`U%Mme??Q&zRd$y672ed-Lw*Bu_{>P zOj+XnlNn@N>&pXgzU8`tS*puH0tTbw^8)#$spXk6Q@?BcqT&6)4gbO3@~PARzTpKM zPb_DOJu5jIEjmY`LP0TAdHA;ilXTQdMavQ_a_nS>Edqu_Du|@FTX3@bC@Wmz!Y2Ft z=}rHIf8!HbR3;{|?zPvU^Enq``S=6|E;x(8J6%gh3^PYZF_*1Cbv=YAFh5Rl!u{U8 z=pmxf!xHt0{YvyxWn15D6;V@CPjy{i{O~^*-SN;XWphpx65S|ebQB9sEYf*Obb%G- zyxA4fm_`E;3Z~?ofR>%gU&;uGw3fe9O z=SDI~zq?o&D6*Ldh(Do+iX1ngO1^fPwyy1_&9*u^oqf^t* zf7?I2ul7{e|C=7S-Tcj~Lec1leK_&e)dEImGRPL21SZ)IM4rG&m4g?SZ>!OFWSv#E ze+><0eM!COc`teG+|!@oS07*b;y(R~Yt0=d;(_DH?nDAFSz z4To1^hPZ57&9tmGg7@`^uP-zPA>SG zW{|V+g_{ame9Uij&~cUNO{ zkG5^yyj>7Ouiv-#obT2g!D7mkRRSrN<+U{+-L~)H^$6q~elk@ge@7&oeA>uT6C)&w$zCCn@@Knq9+xC#PV7~Qc5e?pDJ zsHA$4^>x(Q>6p5;YY^cu(WtPUV}TbvuvV9qq6{?@WBI(las}|6?|$=vw|@Tn512Lc zI-+s};%#KAO;qXO7EK~ftayXKVUWhhHBmDRlnJ!33S-MEptzD@ZJm+q+k!wMjhNEJ z3%7T9OUsT(ZUuo5la-1g-4(dIe{XZ}i-YIBsI8dWPHeAwp5MIxfd`FbGAZZt`KOh- zy6(l-K06r=ZX)Ksc(+7w`LOt->64i&4OXm~EztQw&{>YHFYz=1MIu56x^7oOlE81B z;^BQKreR8Xntv5MKcwOEff%&iONa^IVLFT(%P+QnaO+E6c*a$K`#-<2e_qY(KfmdE zsoea69MnR3SVJ>k!R&*NLnarx=(re&h0!(C3w_r%0_b(oBx6^r_tUq`kCnD8gBdU0AY)13~#V8xaU;^#fsq(?ba88U-m817|)P;YgC=wTg0Xx!o(& zvk)c5RLf0}!Li&1GBRSOq0Y?&9DC7dl!zHQFwz_7-`(w=?qmd+M{h)}oPkZRP4{f$ z;^(K2W22`Q`n!4`e~B6W!4VF5J~X!NEo1A(UcCR^FGVz_;MtdK z;wz-4Nx@7G^QQ6Sj?g4l`p~0CF|@9S9=%+oQ57eLuWGK~F?%HA$<>st;a=R^Fo_V7NR#YU@>LrXca%$@|OiL>8Z8 z*O}~2OM1XKf6sNKe|J4M;kI1ZrbQ0l;gU{_^f9o1y30>H#pP$63fKw}W3=^(bzVnT z;)2zc!u`L>0B`#CJN6C@^uFf%-~ONTj!t9{3>%2jeKFk`F>#{5lL1}y_~}jZn_p1qiHc99)-=e|Pj;bIx;K@V^3+>4-NJOCZ}h4q&a$W)D}Zs2MJN{vaBX%=4Qau7Bt!NTdpRm z3XQ(5RD%^o>J9h3y^t#=W@l!~wMKo$f;{6p@@8EZuD*%2i!* zq0>BSvejz3_uD!N&=I(-l5Z;!n7Qpp1Kkmq&$qL>2~?Uvktm4sleQ z^YnU}zXXSZ5wZ#LRfXy-6h}rU+2BB2f5x&H$MUL*maCI}C1E#2*v%?$HX;Zr5tuHX z7wlNg1HW$EWxI9nOu@Y_N(7&;2-R%JbZ)C!g+|}8E1rbmG*AtD$mZu!CVOpByfLjB zZ^Oq_mza_~fhqXm5SEf%-t(?V{m@^TyT9heYl+U#qaA&bq9!_XEA!K2J)-9Ne_~Q_ z{bFZdulvs*{Gfc(%}*~?`pQ?|V6+;AvrV(L*$BiEWnbEDukSC2@+N$JW?6qz~S|fbxvI3+cVize} zcT^VNN~iqLZ)&?vsT@AKTKU|ke-DiP70buoPXOwSrpXEWdLoNKBDdwG44Rn*-XfFj zL+do3z10%oI>rHgjMgj=kHq6;B1V}in9PPyYY=a=aB#JX6)lKo5&0DgS=>O>P^qMb2vpvQ`UsKbJ%0m5i@__hc^fq-1kRt>*E9|Zi82SbWLQvy&}n1PCb6|_Cq`!8-IWNE)ThB+iF*5CyJ3_ zIK*;dU1VUIxF!K0a`k#6e-MVzwG+M?L?F^d;Ibc?$8Sb%>M^>ZC1OIcw)xQgN)YRv zm|MWqN*)*Q+5}Bg`IeljhY3_hQL3#F(CI;}e;15M2M23m-YvoB+c6xYFj)a&ukoR? zA=wU2WSd9@VibISwu|_c@80NKcJ9Sr4Jpbs3yvC?sRz)%GJ@W9f8AWvmkCY#YkR(1 zHV=@2u^!*;igSsKKC@ip6>rRGe(=LzLMjo(U3VYE-~8pPkw`}9S}dNhY};+{A&D&l zm%%om0vWxQ=F99po9g6*S+@y6i3*xidB@2EUp=t(tXDnwi-YaD{&m5#pDizZ=3ilj z41^N_RF*?Dj!~!ye+xS{6a!1s@}OWu;g}1M&tTQLA}&f4F}y{AS5r`WxQ5^c1)cj9 zgt`jIKTyI&o#Ze&#kJK*k>9t}MKM@;`g=mfqd%yA)+^aL#PCs0G7QOp(16v0Io~OKket(bWd6Gb5NdZA%e>#rsihcS_){)3-=d-ZC zN$tqPB_Eg(ICTHqcCS^X)wJG`=#IXmuw4GR1odx!{S|VOfmW;b-d44G(K+YuK)F^z zt))WKRiqO^9G}QsJD)3jf?huPObxr$vZ_iXK=xyTts@(HfQ$FD`)U%fVLfkkJp!HX z)b?AyqG~4?EBFb|J<)VU-TUV_i-(mP=@%%p`Qy z@;lk;uj`s%eM%ex^qwliru$H?wKy44$u@Oi36s?ve_}DRrFsjRtRY`+Ajkxw^BGvK zjQ$`2>xP4MKkt=*)6%L<}gF4P{3bz;xX9rMXl=kw3H*ZFZR(-5kDSfI|t3U2Ic3s!CWHc1m9SJ4h z=n3M^Ql=OPBw}757_%f#@zJV#mS>8d9$`6|e*slgcPOGQcs>jFrd_U8vO(F;L_-SK zh-9fWS-Lznr(#41*{%%>lM8ZL#i_WJII9y~K(c~Lc0*?9e0v49$ zU^OiIegs6wUSSc*A%BvN^uwUN~ z8Yjpq?`I47jdIJLdTi_`@BR4uEjpgIfBj-Mo7tEO3xCtuzawVS+*w>Gpi!<2RO`zK zr{g#>oyUoWin>KULemMvr4f&ZD5ek)&Q~co_ejKge*)w^ zTZa)zC*Vdk*eorOd{4`4Aw>7fGIYK} zERM;EV|ZCSh6VDu4-jCzh%mrB#afGEBEn43gupaikH*uG$;aCkPlKov=&aH2VIm46 znf#m93*okukCcWZh)*h{k=A}+e*=dWgSAV9A!5kgdpkl~-UK7oO@6KN1YqP175m3?5L7!ZofLFdBe(ez7E#xb)Ov{?d*s;YGJ=pG&vvys-FUtAd zA7p2$YS)WM(8O=Z9Tc<(JhE*Q_AE05BLTgCAK#X)FFXz>6sJiy3?a~ifBEsdF%S`m zeFk}@t4jg5R;yqkU&PU+0=lEK*fKQ0^D3E91wsid9GpgcU<;QAVy|x%=MaeZapU5E zg!S=cq=J4b;S@@lEZ4>icokl;{p6!i&K~IM=p1n&pJloUVJb6+?twDBHP)80;jYEw zmECm$0K0b%;?CO-Vdu{Ee>ik#9^1BVAW$J=YWdKx>#S`bnk^K9P#_^<2I#vHZH> zi@M+Xm9L{*2p~1Mo}S-8lP$xqQSj9t((BLhnK+>32prsdZLKiIn3;&MNPv=wCo|y4 z)2hkwxn7%5$}%g`f3o=`4o9K{vX#p$tNNCKP3iZ}jb*u6{r5NC7!RucUx=>wH|1*6 z7#lr`rIj)=IU7x@!AB~jIT+{-xdp4X8@~mhv^~Mmj1UDe@@3>;OuGL*1d5=EPbqFL2-+2XwE-zwqj9aHWwn8#s}s0u6=5>RPe6jL zl2@BmR>^>#bSLTr3?wmvLaE3}8UYawN?Y_x@~buueviZvp83CDC>Cko~K+n1KJqeMu3MU{kR(>HC)%?9+S9|(osA$ivNO1`3K zfn>&o5|Bkvb{h?iUQV|?;#!X6Taqo%|2+b1#mUKu<$9%bS3rxrJ{^>V9ATkO@^uZm z{|y2&e=PS=AivV2Kv}0?&xlNj{A@aGAWh(Mcc+aL1aQ7{sEkWP6$E`5t2SGUv`}xA zIeDg2bHBhE(Xo_bwrC^LE10I0KvX5@D`2y0?uPO@r7F=I5k^}e=cO(i`7b@dvSVpu3l>d18Vr{ zQm%fjB;e+7U+9i=&e`v!Qz`w7-MgKazv|UbNsPAcyXbYHzU`NkR;H~D_uq-zD=qR5 ze-4+MW4mg)p<&k-^>+(VgJMZaux7G!Fv==zTo$=os!GOA66AHzR$BT4SgO zW%S1OA$eyJXFu;046Z~Y+&MguFy{fx|;)xEM*;9jc zMDkhZC~OnRUqyoWZlQ>@t`S*LaRJ@)hsXwgy1dBKyw30F1X(@qq4zMI;fn~UK3F$7 zFugPx$8s!nXth!NH*)zeZKFMjgGe7wT2*BB#}oXf5}tJ z+6^(<%b60e_9;G2w|iTw{$?GOLNPQ7tVQM|ea0y=*-AS$J1pFz7o_R{#$Hme=v!0PGlKCd|MR0edeiCipEJBiy>iETT>f`Aj_N_};n~UMKfCb(0#wmmP@ath&V=FyE#VgeT;KWMaUl8V8n}RL{gw=;ed5bVbRk_rZ&nhCK23iN?XK43wE`L zLdJrWG9b2O$h9i(Si!nZnT--8I;TmaU$af#YBA!nGzAL4l7~ue6}4O*)kK2qq=E+7 z^KfSvqhsR&2&5iAQ9`d0Lb$w&76nAq<{%872T8BNTPi{xe?AlD@d8|<`;Py9^w1e}YvcE%H3 z6p7#Iw>-tP#D=8ng6lMWD3Wef@>(F&P<$mp#$&K(N%6ipvAn!cn>zgHXk^!Agj%(5 z&~UL*vd~E+mw`&A6{!gxPakPHKFTeJ0F4M$YLiE%e^9ZbBa9RD`@wuCF6nXcKyDm$ zvQefzsBhe#`qls6_{Qk2V$_+E^t2sSufCxs^<(N@NjE!QnHEQn@TnP0Yw= zY(M7=yXC~={ozl^ir~Kf4S&XNO5+nB{}@vPd1~AD!OWb$V5&NxQqGLU=wVHpa-(I^^>y^%i&e@0~t?AQrQ4&&6pL)atCA%h9;(FGzR?HE(D9gvlIt+ zQfz7xIjfbbVA?1Gm;7nxJ@TE5wHj#5E)v-lU??(L1lqSe>q6`!@W_gC7ME5CB+Cfi zfAvd3B$3okCC0N)lUgeWaIPL z)s8klTTVP(jKk79La_w0b2?fkeMdJ(fB0c~4RE5pO389IJ%OjqG3hD_XzjM5`27my z|BpjXhC`B8UxjQJ5H=T3lb96H68gLQcpjIcwnaymy=-E5-+A=! zV<;|85u5T6O%m^ouOnx)9^3npND1VBauYDga84e+hX7~<$)T+%FPtQ&T|hN6e}Ql; zMOcDuWw-A(zx4!8mUCHh?quLj5gLJAK_*txEdSiafB%l3cvoDy=f`Sv-~}hgM^ou^ zufW8LV||;Lb~)FnVim7_-Cel!g5CJ+7w*E?#3Ihvz7DtE{us{NzZ>az1aJAXSK=r4 z9mFlSJ&1w9UhLa<2KJtr#MTW=e}bsW%3Ao!r0Yt2S)K{pvf#+_*_ccbZ~w&AfoX!v zF*4*b0f6!!FFyK?$=Y_Z-0D@s?iRRngX7lJ^_xgKgmnXmYA78k=*qQ3sn}~YDheyhZ{O8O4w(WkG zkG~`&y{045?G)PuIIOp?!&fDZG+mEHPxwD`8a~VY^V+^!XNlHmx47c%E9(_dz(%~I zI~`5-+^~6v__fiI`H{zte@<>$SSX0Kx>dET#+|-LWq6XO_UyH<&Fnmn#^%@m^R2Vp*Fx)d;{*9aQ5vNW{eNnvwa6 zPN(7{Y_m#j*7e3AryJs{M3WS72gxB{tA%h>_PK6iSQ1cB2~d)^f9l&dgmg~|R-;Mx zuJn}syalWEP1hZ`Q1&KhXqQo|u^zfi3|&Kv9QZ2PmE)Rx)jKG7{GOJJG*{#r>_&yi zPXbm;fl3ZqVFhF@8zQlGp9o);Y_U~q@d`_oh}(iK`AHD;a495nJ4Ne z$)-DYOcy=(&))T!e=q!2{kjMG`@|3ZO`F5&L^ZwMx zQL;!AhMr*kh7mN#jsgaH`!;@mY3 zME6z2Dd$#hwk*>&vkQn2|CTg4To+{C45CQ@g^7!6bnS6n;hKqaQXmpQA{H3G?x@ zE+|)>jlPsbPf}EqugJ}gPie6Bs=roQFCvwGn9V`v$ z7=zG)G-ig3t@1Jok1oPGRYvb04&pl-b`T--F|;%E z_8TLy@4omOH~m_l#?rC+dOCTB7nwl!W& zM}T!LV>`YnCwL9t}lJMs4B(n6`TLc%!Xv{ks zHJn4lZ_x;0o_v@_KL2c0!NGbJKcLTNIp=XrM;A?j39~6zYt5bfqTPI3=aH?PQe5&} z|7S`g>0k>ATB)EYsfmB$LUK+z1O zqcN5($hQKRgX`LSKH_~l$p@Dp>k%v+yC03xB04s*zQz&MGspQBc9M)3*Y|HkFR;jD{k4kZS$+9W~QZ`oqgygXB6z}q~P6vZ5iA^cW>K>+4%xj z*?-v|oQL6|1bVw@k~C`=?(f3J*IZBEvu(KUT5G6am_U(RB12|9&1Ejp(ysodf9LHQ z>$%eIP3qS<@ZtimtXiC{Bvt)Hy{2}$tQ79XNA5Gjb&bdwdg>J3D7r$7*4?H z?EnGoR+8pq15U}qk!2fqEt^<(e^D|vdzbmL%0h$XK(#XEnVV!$zOPs;Bu`D*p@$Ah zamRBXV4p;HMC(om%$b$4&T>Z`Rt&-QdLmpe{qx;6U z$>|xU%UCZLxh?r6_f59h+D?DIdSrCa%*GDQKg zV0!+a-F?TzZw)5d@y_>u)Vk-pA8Goo9biF!WwnUWBa;voGjL)7#2gx@P75We#1pm; zWCSp^qUZH{C>2cHf3k!M*(1wc?2aTcERcX4RI`}czNv1vn!|)MtOav4Pupt#`A_J=|f0qJC)%K9k zRIHG*l-NoiXZsS*v5UvzT%*s-=b;iR4TgfqWO7_Rpv`wwmo{zM(tq`pS6m&`f5qodEi6C#$jFq` zY}B!N^Lj3!WDp>)Q$QLyK0>&%fM6^QnLtu7AR`nGBAw`|o2K{P_kHN!{?EaruIqv* z2-_Kv&~zpr=KCETiLU2eb;%0}Q2xu4Uz=Oa-q+jNVK3&hswH}?ISf`7S$?PBcO`@7 z>_VmPd*(v9Q2oMef3b95CZAjI+5)Ce)-_=%A3`J+-<=3Y6(Jt9Owp1YhwhSZYl34| z>3RYz%Zxyi?bd5{rc(LFqlb?EfC98k@G+%|saypsx4h6&pTSce2 z6(nHEWHqZ~2RfTyWCv`!lT3sFse+5PcHw)+XR$>~VgI@wf9wbi;oBpJ(V!s9NHvp! z(a44wP+=UjB`C-cu=Lr!I$H@BLC*X%BCQ`{Bf+Cokg^77?l>w z>8ueE%wh3_iB#VJj-AS&#*`BXU}0hma;g`pjwBvFf0o0*@F2Ecas@hDhp^+a3&>xW zFt$whWS|3+Z+;E@yAQ%Wb_7?_+?=@lC79KA?Yht?fBUvSdeLW&25e{C-*BK}dfRQw z%HQUtS4KWWSzpoQBDe%=(E*y#CH{8qP+~+F3e%u)jU@4w5xVm4zI0| z*;2I6TdcP7Rp`40Ie>4sWd7N~cDb6>GV)6^e+WjCh$K6}nlea=##bI$VIspK3IRrw zHA#_EyxdGWd#v%0XxDaX}idC-(XYLrn<}$=q ze~@zRKJfj8s1vA`xn#P9Rd}y^Npx z?2B;4MdxtU|7V{Y<}8`@Zsm-M&dr>&e<*t7`k7B`(s!rp7uYho$RD1|GLdASGsq@~ z=$FYzTm1WY#NHg5lvaKQk{*YorqG&P;!COoPJ6BnYGZfQ7AF{x_$4r5yLPKk zZppUU@VUwx?|DvOT4vSp9s-8oXDW`swyZd~aqP81rS_IZdn>%1Ma{gtM9=4!=2#DR z!wAXa>>R4-9a3dnsYqUGpewA8e;!@E#<%@wjp1_DpZ4TmEl=`H4$F3HqND_v(_yR1 zO&UulvI$}%i;9&8SsUuEf)lGU8ua{gHfiXJ`Y129(4R=q_xmw%2Nf8X%;@2kyy z>r1Q7LU16^ry|uaVC4auY)#{OspYzZ9NotxfrKgr^r9oQbC79XD2j(-rA)vqKmj-p zrcuZAV|ys*Rmc(7>mC-HP3}hn!6?!~P_Sz)1XB`}2mzJC2<)Xi*%!-IXik3a=G)nd z(=#IP^YaVhpTGWfp9&b#e{)8s=AeNTfT+MkKz%$R#c?0 zmNTrv9)YS1_0m6j^It#qe{qk0@%Y=f{pC7U7X~B((t3muB330P3LS~aS@C#ODi+I6 zfFj!uj_T3Qe6UvOm^e`sO;hy^U2@%8v9MCC-BoS6H|4XLkz9c$ejvBHFAtkfpZxVD&Ag)NWhH z=A=pwjclhk62hhXe>UU3!?UQ&w($I|4ldtxChlE1N^_d|jdmqD3$~8KTK)(~#3JbH zPNS$A>gmIw zsU9`Nz*Mju|VDUDa^ zb?lxyfTh$v+}VHDb*o1ozOGskaccRXqooGruC7l1Re$>D@Biw5e&X-h^-7w0Ppwci zo2{0?7FZamFUTssRjuP%T}6^WSeMu?GSTT?GW}n+e@mpXRY3C72KfZGeJ?RBT2_oj zpOML|Vu?OG6bvG41Tf8Z9?2Fr>Kcxc&pHxJkWUsdPGGtx-HkYX#>vbw&hF{OWIl&8 zBT>i%C^su2Mu|8M2MrwXkxe+xTEB9ktvkuKw%MvF&(%F$1;a?40zqe~6gpa^JYpR~ z?Z3M}f0GPl;B|F&94j*;VD~rGw}G#AI$9HfNQ(Ta#-*)T{U@v0FiR}jumv3rOff^y z*rGJe3&Y^6wyf16FUC>A^gd&)^Um+rDSy|8EGcSJBL*2u^eRv#iyksM^xUnX~f1+@O{En^9f!nOIc4E+(z%vY}0DM4$ zzp-@)cC6=BeoP{yV_=ZyWj1NTGP$)(u|SNth(t)``9eao06lXQs@p)IXFL4(0MEY& zbdZ6mpNsAgtfsWqw%KQ-r|qf+JAtKf<%)5aGqdNfmrOQhud>ube-Qr zL8-hZLG$#Bex4}i%1x_ZP}&SqVmYi4nFF142gS>P8n^ldnOjne?lS1RbS`)5#8m5Xqat#M(uM1$;I*kMJ5&_U7 zBRCH0Ji-d2OhgGd&CL|iB!}ObOtiQ1DNslb6X(yOhrmgEM;a*16DT2KwU~qARO!Yf zZ}t6Ok!SV`hhZ4Pj%{0&>t6W0z`irKKW}9vx8u;EacN>chgQ9gP&mZ%kLfCrFMm}r zH#-A|<}oWMF*E~)F7oTHHDv@s(qH}2Ti<^0e{)}d-LZGiu4e~A9!+UMR8&=9g>{x` zm6w(lK2xp!ytL-{)I?J^+_L2<@qxj0Vy#kh%lX3S(o*&l6HBXi+m_QbEvw~vd>O=E z3r9qlkXrjLI^0Pnrci+2zX!l_J%_nDlp+$DKR2LCd);9deX63bN z+a{6vhQC&CNIydqs9Dj3Aq9{KC^&0d4{s~m`K*2v>k14tjF!{DN>ha4Xn&AtwxyE+ z=w4YAfhGfxDW=2bTV&-KZE zxCA`HQ5{FUCf~KG&bnx-1b-0}DXkj;y~DY~6N!X{GwFEurz+Et@v| z(p+0!&S39WHkSm}ic4r(S5X^=P%kx;8K80qbfMhs>{$2fOjD8xmQ-5c}9eN&-n-*Asr4ft} zF(sTpps`X&5QbeL^3@4gIo=6JVE9027n=!Oc|>n-i~FZ1%) z15zjrnZ+rVw|_>)6fk1MkbPLy9I7j)SW7&pWEdI+xApx=1m+)tl3yZ*I!pnH0(NGM z8)snsMTigW;eAMJ*;y`O;kr-V1Yu%y>`2%Me=41by<i zwx?@QLLtJvGIR!neCD7P4w^){#{bJ{%oNN# z0ZEGhv17pU$Z@w1XZYZ9kUPI&e|IOtt1NfpKvnY8c(ddCN-lNk~0OjM%*%6 zfJ^~7Ez$MR1g}d$8s`GUa2a`W{1Y_;)t1J=Sz%d0C1|6b7df~M3@9_7bwWG&NWN7+%n3x z5T{>!)sD+u$NGTidzVR$+b@dhk6-zB+hE;60 zSP%Ma6ALWGqQN>9DR|MjadLPqjEyv~z(j)S`U6ZDyjUc=W}Oy@VVgeISBfwb5iGFP z9SV@6_aB7U(}DP457N6fpm2N~f!;I`0Y~jjhFBiu@-JT3_}ujScgKtuxcL#}9OzzA`_^zFEMn=Z3eeLVs#bmsGZp-$-fx}iRmVB&e zRu9XvyW27ACr*t|zh`P@VW!z?*&Ar0qbn0 zrBg_C2az;IzVe!mc41Ikhlz;_hIXYev3L}!Rj1iQ4lK|ml;&$vFdf_wiho4I)l$)v zY+Lw~zkcQ6AAj>FKZ!-dSBn{3GC#NQgI~O^SR{1n>@z#J)E_J0RJMYN;~C6tEMY^u zLjJN0yXPWUVhk_nP2mODkIKRd`Su*{J#qqLC&$o{4ijl3@>?#V>~2S0))1n*R#~l~ zMbA}Na?lb1eEars?C+<`9e+K=(+MJU&w}O(?!5USq%PTuOJ>tJYeyFrjLRU5O(QN> ziF}>H@e^ZM*WW1=%FREd%YEd=|NbTma&J2S+>0-&*32D@M5GCf28}>wG_%zEShKAS z$@;hPMY#mytaHE*-%$kPf3yG|7f4qZ;SBLd>7cZH|sLT zQo@)l6~$hrP0bE=WmdJtm0H4m>*-|{*+L!jqlZz+XP_%ZY~NuZHGCb|_OmX#_RK31g23}hsIV%7*JHBFw86#n+-&W(Xg`rzU|LJ zvtEE6jBya?w5qVnOMH2nEq&N}0x|)~ut7JU*h6G+9|1%%^nXYu-F`%bqnpH}~9KJ^O^t z+;NyI!xY@(e9KVqm^kz*frAb*vjEL0o3E%b(5MO?&lMHex(}=N<+@eQntH#?J2*5e zHPn|TQOzu%MSnoVmI2tX>x7;gAe=i)h z)7N#hE*tmKctq%i*r=e3oYhs)JnlYn7bdQF9jrnHes={KhuB<0MfQ{d!6tTDkx^b1 zkm$3y4&>;}Cc4@HXMO?~aP<%t%Lum){1IKh(+2>l`l%aT0Tan|05Cya436*?PU@dTbmLkDdRi$=&PC-;#RVL5vbNgXV z&0PbXEN_S6sLh}>`?n;yMS7p-*xnOWRyW={GIG(gw!FG0=%4F)&YPX4d$}S>pLx~w zXS`*pWPd(*=N%`Xu{Tgu-HLZ~@wROb-SK?{MGa-fT3Iz|Dv80R74&x-IA>dsVtD|^ z4rVYl-$WzS)>bnJOqb|;R4IVhDc~9eC^cEfnPD9dk&T2l55k|#;6%2J&8&;WOP`14 za+cl2zhw9qGJnQBus(!lQ-)m=v9-5_*;WWvJO-9` zZp{-gB*HNeQ88EZVX~xq0uhp=pg<0p6-*_Fq_K9UEHORJ3MmLU#>t}h42J3bO=uTv zr0zRkxrP=;m_Ximv1fohJ&aGj@Nx^bUa`Y5gVVRb2Um>Udi*hGrV;tx>8{@ zX@5S=&d!n#DAIf~3DC&A|2W|?dd|a!b=^Ohn_qd^zkKR*zqQWgZ*mBbRow{D-E(*q z>CE)<*I#+V+gR@KFD=g>JaEVv>>qg3v17+-{oSc}Hec{(=9i0m_ni^lylJQ}6xRFL zs;Uu6_me#gl7~pDntnFTwLz~@@_qWw)qh$=TU^Nz?sHjHz;_>b^vFM!%9R${%OS4m z^H%JvvTef{B$DS_pg=e;<=ja?h#Rj*s+cPzzk}IRn*NI5@8ivy800DhxzVN-^M7} zOr7rA7z{Uv4V#83CPXRT1O#0YcYmCH?nOgC%B~cA%kmW((rhMkqvXq%iec?fo_on< zH~!r_{)YG1`@jePxt*Gz2mgFDtKFe%HqxqvNdlEz|C@IV%%gJZr})p{%W?H!1iM>H z1P%>cx9=jn0c7N#Z62>wr zny!FYcQ0&;8#_WK%#I`;n_NI_^Db=n3N){~arN^r$IUl>9~WQpTs-i|K?*Zzyz{J~ zS9iVmU8DQn`2Nqb>+bIAymDfE>_&n@=dj&1L6EPMB(<&g>vMHSBCV&(Fv0U}$;UTc z-!s@wjA&@bi>AH3G(Up@&wt}v(3+<4-5U0ojdbDg>@v1R=-o80Lo5m#L_``{{+Uc= zfbB7{9Qq~JY=Pq2oMNCvf9vlXxZ{6r`QCxkGj6S%oA>W#CM{qsGeiot6|85r zLXLNFrI+~=4`Tl0LB4yoVZDdWjTaM848f^C4!YgOOu8f=`-trR5u{`miJ$X2VVjf@jxir2Aq zsE6kRE-cPLCqQMYVHCqdsD;)c>J$*L@>~Ll>DpPA1tT#?6L1{|Q&wdK*^ygNFK3}J z!RSDk3}_w#u@o66DSv=(hLYaEJJql@hP)#L+v0X>oYgEgDS$_IA)%)bsEnf3s4}?- zr1c;gCON>S!}Zsm>hM_Kt!AUKFk84Q9*SQ$l`n>edy=Rwjw8^2K7D*;4fI%$-5d;k z3lMB~#`CNUJYtfk^F2IUgkuG`@UGpgbN#>Nxp~ycnK$GhIe&^y7CO)@)KMzuP-(6p zN344}SB2_UF+h&g9vJdNfx*A`lhF?X?KZxPh3Sc11L>V?X(tlh6EpuDblR+g6V4xT-FERo0gJowNO2N^%T=M)1uY+L-cYp>XPy?+IUDnm#>Z3< zCG6SMi=tr@(6ezeU&dZpBb%j1p@k>~lEU-~BC$Buv93V`=2l0hP?=dk=l-owI-<}u zrQ@lt>woy{qPJ40{P|#1`l-LV6xnj#Ub?miQJafVpvd958$8@GQ-BwWqOZ%v3d`4W z>qzMY&^)%Qtr9~QVA2T2SnqmPiQ8(SgUDCbw6HxSV!I(juQec^yB&ID3C_qo#CQS? zqxU;MIDGVdzv&ph^{?M@&&U4d!#j3t8T!eQ$A6CxTSV$|xjYsKjCT+y@9T{5V!Fwx z8JIQl4`O?TUse^ADs`Sfzj?SnFUrar-}{mOc*cE}pXo7na_Z#z;Q?Qibzx;?HT#** zefwRW=RF~2{)@+dfA1y^tjESDvZ|_xFaCp$+9P&e~a-C={0WgQB^R#F_u4GwWUk%d>63&ggNVs!s zSAchiVio_27Du%_i~rZ&eZbjq)p!5@&41k9@80dJ-PKB~YD<>oCRZ@VKuiFGNuE3e zNFdZCP6DB~hk$V$41~~YLJ1`p8)F-6+zr^c%1D;1vMQ_9YWwajdwZMvKWA2U2w*Jx z*g*b#-mllb)KL2zs8!ylU@2HiQo)tqs=^l!`yYG~f8b7)H`?Il-8f=$hVym{G zx6LmdAxE`|CFu?gw9Y*7^yW`)829E?d)vLTQy$uPx1?td$;yt+Zi%wE$_sj7P{>ma zpLxpWi*Ekml@Hp%ka&DPdpo%+-rtGEk~+yzP5w4C8TqT0>@ndn+8=bpGi%GX{iov(VCbSC5SoadY+ zU;p9Vk{>)?{fu!rW3X4oXEJjCo{V@^VE1KaW$w^!>Fo-s>oFjKj(*uOGJht^)U~;> zvsWV1vyvYel(*{cFtq3K8@oRH4Gt}*qp|MR17yq+KV=sw;{62v81|tRG*@{1CO4H7ggW0v@}oBZd*SXf;iy0BJE*Or=Q*BU zUUgA=4o=O;zK5sfPn?=;93GNP-78Mw1a-2hk&3TfHClBE<@T%arOV;OB_2vhE}wCC zA8EN;#I>ZFpOKKdAb)*rE2KENO}ycLX-~!^(z9ym*0dhI==ltfF6VbdKTXm*GDv<_ zz44<`3%5x&*zeADtAx5F?2EZ0uehLW&JUSuS`K$_0B{hmxBmTBFHInR=zV=Q<5r25S`l_#~)+)>OqwJ=;)_rLu zw=NWvE7#rHccozP2>%ePV1+qf#!&;mHLxs#fol8sr=p zQ=MwH$v0BN~X3)GIO0W9G#O>hy8NzSYGN0)v@b3l$E*^YE6mw^|j-1!@R!J zkX66;w;Iy!4Z1p9ZvRu43X zSFL)(E8h6FV?F*8hFQtIp`{ z?|=Wdrqk@u9k(urYPTF)rAN!HFt1W$tKPC2El)!Y{M^lrb>)U+H1^GFufO?i8#f+5 zw3sjG^YoDb`-JYse&}!iqWID4AAIwXn&;}NT)j8$71=Nxm(I9PezIdmwL>k{f2woM zQlZk)JAOHm$t+b>&~?7bDj=5CcR%EB$bWKm-uPPuw;l1RL3?-mN?G2~D+l*gRU11e zhZj@_tp@7)akD3$lwc(6mSqX3b2$*!Ma`{hrI!En<)6L$m{#IvrdqFXss%fy0*Ei- zZw{Th>b;h_{3dt)Aav(0{|lquq%3$;8y?O}DVtNFI3!(d?Xs-%cokP-c3dwQ34f|% zOYf7hBYRa?^0|+2^SQi))HN4hmXLVVV-L1PR$cI{bK*Ds`0n!Xz>45N-{7ljb=Axj z%Ie){xc8w7h5w!_d;O2@aLzt+%S*D0mCHimW{c>((R{G(_{1 z4Gc~``%D?C*TmVgL*(RBB^~eS_{G=shFfl!d+zzqds$(zcyYN>IWN`Lc7K`*i~jkA z8E-D1Q%$O!@CE|@WW=*uy>AE9%e6fc3Li+s<2y>F{NC+5ceyXb+}xaO+IaFrBoz6i zWm`$z8tctFmh~)?La8L9&792mJ?ir^@?XJTcbm=P!i0Rgy;H)S0mtw6Pj`l+x7TWw zs}{3!x888mJ-<+H#dh1GrGIrfOWULAIh9MHt}LSZ26YL!e@?9qN~o7InUO>;{>#L^2r11Sk4JEg0&OX}58@%CLLq5hYt zPq{;VOZ~J{*GrBrJzdntW5<{7T>5)eTO4o~3Q;@g>F2FC>ZSQoQ-58wA`c##lXXLb z?yBAiE9tHX)T>Jq#r@(b&A1`KD$Pl}bG6K67NonQ&n=gu?>L6L*Q?L>N%rtow+xV; zgQK@P3`A05d3%=XP^poM4wQ8j#+r?)_$|G%T#ptj(w09Yxy3GN=)Jwe%cR`2-EFVN zGbP!rLg8?GKqhmK-G2|(n~i!ln^~NhUifBbu;cY}CGm#?328m}eVKnqUD%P3yW+c{ zMlR*DYJ3Yp^*zE;S0nP0S1*2PcMAYFd@2;F?>Tv>EV)8mN(DzXbYTg2Yto~B-V@er zl%yJ|CKE9gG@Pc26jMQ8Zhs(X73=lvRrR|6`ZF%NboQ~1^MBCPK7BFXsy`O%{ohiJ zV5H^sz25Kj{e{16P#VSDQU{?!TfHoU)dTVjD&U*T+_K@Gx2cB3KbNmI zz3nnts&afls!Us!ae?LBzzpqlO-l>A~c++xnk98nEuQjemzN&+N;QAlzdBU|4ZObauDogWv z8t#h6nygqpAlZ>|=}$#uGMztK7AYc$m|q@LowMzA=YMxgDBzd84sq)BT-VX9I_RVv zQJsaSBPxACk2)tp(!F7o9Ld$B@AP%z2?eAyH6`m_et|k8(h^+Jr2=Z@*WRH#%6jex zZ~MvU*Z%K=p8eOK+;{f>k<*(~vVCt>_Kd~VpsFUvtM4`L^{Pf#{kiH$Fqf8Uu`WLK z^E6dp^M9+Zc*yIMq&KDpdnJkLI+=-p40h{DwlV1&R0H^tho$aMNuwt@Ih|g3(K+wD z;_-@8|B8=)_RE!zeBj-GlaT7?CRH@;?q4Cvw!EaeI#rk)SJzTTop%K_!1PP0+;r!7 z>Lj&#c*xnX;rM_3lUM!y_nzqYe6sfKJ2*c%HGjP|nT+na;YYXYIdzX4>N-19)`k-{ zFH3dyzF(auU3w&>tDbmmU393TCq3A1Ezed&jfH(JJt@d%x4i1wn_kG}ewxnZwmZ#c z>$Y2O-!K0?^Z!Y8UvGW>N%wvDh8=IpH?6PjpUemM&9&tCzM!0Te7lT{E=neENq1XQ z1%F3dW}BA8;~rTSQNLd(%Iuuu?ma!&QC5RKzjz(5I!DBPOn&3OjJ#yyMp@HelJTjm zL?b>a1iX?`?N~G(Rc&2aVyZ0+s9+fg`6bY*eLZt%F;PgXor4N0_ ztXR&uh5Qp?-OA2OUSG2ke%_y6^vZ{y|ICZ-jM%}!NFaFJVyU9rG+oEb~I(nq@tg|F@;|)OcnIC(7 z_O|P9`oUv-4?Qfxg7UewcyehCcQ;QAhZ41gmq3S-q(mOEJic5QNwpC}P zQ@`QPQh)9=RS;|}O1-Ww_R=*m?%uLXn{h6k zR87@rjLt2%LA0$ory7h!w=)j6suHanQYUm!>cMu2y89OEt@s@2Nk*hlDa-WYV+Tt8 zBbieCj@U4PX-pngtA{Tym! zT&l{9uG^<4J*&}kBHAGX?GcGA^GjPYD#@72eSS|_HCnqZr@lAp_3Tzd{5_UkJJ^&& zy4>onk7nj(UiId;A9MZ0`NEOyy=}`+jt?GxL1J~U%p53)6->CJDLrey)$q&x54W;! z*;$@kdJXsLc~|-p`+u)=)B5hw9RzYJSbnJI92JO5DiEumt(v#xxYJh3;AHnRulVjw zdb0QBPw>2GOdObgG!#!An$Gkt?|z5Rw!ZCDoU581Z=c?R%j2mxqe=g^Sje~1QQ@(q z5?rj_C^VY#U8mXjtlyS5svI}fY_#6h-QKx;$7stN3QU|w;N1UPr)OoqAJt=wB!43cM>2lo4=cvI-*sYW1NknDp z_WQ)60(L|N%73<0+v?wNc+GmV=~QZoZwJ$rlRcTOGWd)aNNm~NGIsMWxp&?tM>4kb zsSY(7Py?_)O%4VvSyVw%&s{88O?UBIOi$gb=A}=aFQJa4EF1Ewb0{v$20W4q=&_3} zm2zx)+ryRfUijW`kNk$?`HepOsV`prfw#Wt;;a)oM}KcIY4`NX^z0G0#FwvGRD;K+ zjH&^t*W;5=G$_5j?Xq^w+8<^zi|>1~-}A|sUs!N8&vu^VhEK}!4B^s5r`Km-xoiZ^uBZX2~ zN-EqXVsZDbS}^KwL_N)aZ00jx{Ol+H>6o7ibblp7r%uf+id|RNh_AY>WqB9d22XWn zc6@J-Rn2c}E|xYgQ^Dn6O=ZVuzf@*3Qpz>u$bmU)@2!^||qbqaX66|M%)mo=Wb*$o;pTe|#)UkUqF=A(u{< z&K$M}7IIYysy2B1@R&rreKMP=O2bq9(s=*zjX(RF=e=P~q}^V7U{nre3$knLPO*lE z<&?Q8sU94ctVi{O-}|9-t5B)u^QQ_$aew+9X-rK^WqLwtYQX4pCS+D6a(J1WT6XDE;a3LU1WX3&^bDzObr@rIUpY*}7a#z-Oso7j(-Jm(9xvU;ohOz2yd0jHk(MtmL7!e|Qd6VmMKv<)mi?i) zq~ZbTNc`Gb(T{MdjYe&5VXin>TK=_rw;kCWJ7d5q>UldQz2dAYHN7?Df`6`kX3N~7 z>Y&x}>OH44m5EmweWWE%K%PSILHF49LNUXQZt&rUFU3+&UJz_Mx%eulh}2 zqdU3$hV>KH^J|UL!|ln?bAR_|#%1t;Bk5fJpt_>AJq3qpO^wY|`<8d?6HjX_;I#Vm zR*znPaK2$Xe-#MXR>P53RSMQ?R6|f#nc`E~Ole0l7JO&R_Wp6xaeVW$^O9^jADXVN z^NmjJ*ISUyJ??8G*1e5->+0)n8Tpk*;>UE*mP!YDD??qfLUl}jb$@>AmFI)KU9x9l zPLh3n;@5W#)L>~gR}mGIr90)9nl~ghT?{>2l#_-#-CC?0&fP4L)jiV8l%y{nma+SG zsS~a#zAk-E+U^37;>?_cdXth|GpOe}Nwrq*c$`CQUHti5Kk=E@o$61{rr#HcOoWod zXQ-DrCea_?EgMunG=Ec4$6oZP>rsWYwz3q$eibS!(rWjK-SkPM(p1M`OQa_zy*)um zgo83Lq^{?3QQzW~dZl-9zS;4T7rysXPdZF$^YinKk%NbBvu*pFNUTkYbxZ2{wQXj_ z=V{5I@q(09!<|wC)AqKA9KUY;9kUCIFMszXm;Kh8@lSr{D}Ptd&!-=Ez;gaGw^&2N zYtM?L`rm1Jyeq6$)wUgXse{vS>W*#uE8-98`u(1Wr)fvL^?a#R{oK^-{8zv8gFB8) z&CES53yl8l?XPeB{l9$qdwm;D`RhPe{3C^8y{%N#Ma&l^9I@1S9CynF`E0+ty=cJ*nF4g`8}+J7w02x+P>fLLvS1QD*WDX$E8JeX?YFJ}tqJU(at= zgD8>qo_M1xBJW+Cn*7`c-}$;<31j!(aZS?awf)UTrE==If4X?}x4-Be>hFro&DPp7 zv(3XVphCr1xdc zyZe$?bTMreM)m%&dh5knts!+ON=Lj`1!*;?Q^#0QnOzs}*K?vf6A96e$%E?uui4zO zs~!^jo;z*}_(PG}Nn5s*pXh7;x(|M3yZS|ceDD(=cw?+m$i|C>>h%62b8k9jc-==% zUsIKR>3<11bYMmvZq!vD<#W0Qdf)ZN7oRqG=V9lsZ}!yWB`2LKtJP7jTDel3gQF^N zE)(n7&zACm5h?AMmh5N0EWu};Bl$z4QrLGuWLd9N=jNrjc9n!qT_>xY>c2b{=g)8Q z^M|(HseZZhsrq_WHQQ)KZEq`ckJxQ1r8#p%wST)h_d3#QE(N}$HH6&HZ#gQ*XYTri zU2!aV?4N2O0u_NuO{qRafH8OgEPhX&YY4;qTUy7BITgD}+I?&FT z-`!$tF!{JkwVZ|;%FWKsTv@GFUUzN2bbnlbchXLUy^>bndwMo2rK;Xbv`6{}QZlq= zb*obycVDun9QOLhq7nZ{AnZTv4SDvoTGiV!3p2Cd|KaVq|M=GTp0<^#wr!10&rMyk zbMMdVzWkAAo)g*F<21)=%e_g@P`tx)pD*yZjboxccI}Vvy-g3__HA6h-YJwyqkrGK z{*Do!*T1W$yZzSL{9)6>bg$^ivH5gwf5%%YwdT+4dhG#kDEP%{-7f#^{=@oF-#_*Z zwEdf-h8mS}ZBM=KwC2*KOm|OmUo7amCYJOyN`>M{2ltJBu(+7lTXwzv@xJMGH;r6l za?n11sNr?@!)-~SSYE2DsnS9J;D3M|o>qaft4B`Syh-l4?|%2r+Lm?8W&dxNAaqSXky*=UFz@JTtnC~=P-jO%^3 zYmYgH@)19}Z*Oz>zP%s5?mZWLZ(X|emCpXD)8oTytYFuWbd201>(s!ia({SE<_~5i z-3UlPb)wy|phU`QAfy^eZ(Be@-68SEgA$9_5?7(Za@0p}O6rA<#gRhb!gJsMsVB7` z`Y$_vb^qSIW7AS@v}As4#ND7IqORd_^>@`uT@r0=($OAP{bYxFsT=?K?n^#Wc>J&W z)Z4rFz~eq8IPrw_*7Bj1=YL0&UGJ&Ys(z2BTK4$Wu-Y5&tF}hvW`8RbaIAQs+44(s zv{ER2c7CpKMI%S5AH5UyO(1!H+j0{^MFzubwTg+RUa{ z>U`~S3srih+Oj26a>S!T{$QU+;worP*g+Y0)VUIm$n#e(lYrkN<$r2bvUwf+3zAtZ zh#gX4)9bheqlbqFTPF^tu509GzWC-}6DGIcbK_tz7|gCc@tL!C{^(n6xBTGS@$OLk zRprUb%bkT@WH6Xm1PG0!^8`{>Hms|NQ5jar={f?f>EZpVznc-FbqioN@Md zZhI*G!P)s*Xy@Uj`Kp!*@R5l9(9JjgApQFD)_=}FQ-+aj zt2VQ%HSz5e^dsd~s#RLCm|Y0&9iOvPsdia$!a7MLJgv5o|xJH$iGdFXMQEQ&P-=@n0bjDy~j9y=zmwL=sazTg+eV9^1C%LOVyfNbY1VK z5KW|Hc&J~Jv8Xg6VRwO;Rj*2WJi4?sn+lyZb$+f}*6!}$qzmcl(QZ6epjSMtWYHir204+DgP#+nSPu`W&m66=(6V zwDLjKd8clgY1n^v?)xv__f)-}dndD}6f1gVc}@japSvi;QLk&Tl$G{WN_;9%yZcOg zVxOFt%Y5^x`o2$x<%2yZm1?!~edTP%uYcZf^)^<-srjQJUwdbpFXHn^+Ec6EFfo<8 z?v}fD9Qg6i9{8=_-T3Yo{hk*@Y+tA9Na z-ZPl+ed+Goum8-)KKxJhUw15OAeKzEcez*b*?;usnTdyQ_0J!kZ?}AfeNJ_1Ma@%b zb;mu{p@j+A-|&d97>@gYy7xnl-!GA1sMSb?tawU|w5lO-ioJ5G-zPrRH$?pnNe1+` zk~QgQ8`{rwVp6Z#GCf(8 z{(fCd+w!Z>vZ7k8t-0o!Ywo)6!V8V>jz4hsokvdEeB$3eu($L@pY5n|rBA(nzJ;zt z;LZBqt;r*?O^2ps<gU*eLKRqSm>W$x~1_q;Z6?cNPE>p2R5|ZbfxLTHXcZxrflCBjeNo8@`ee4ob z9cg-Dw=CK*8Lcl@F)<>k^1KS3qY~{|ExFkVu@gzTan}(^s*bk9TYr{BZCWIENXoj_ zS+rlG9V;XpO-aLP$&T?k*|vY&-I6J$AMI7ta5Hy6j`K~5uXIF$-OoLipQ*8C#Kg;$t-t*Okx$$zKhpdQ2QRC;ma>SeF3x9tBaSF3B)U)}O_em@f)Pav)LwbgeI z^~8R?_)G6wtQU{199}J%g*gcX)u2TMNUsWxLp=$%?rF7LmUKBJdk;_PCGXPJ*)EYp zhu+yoe12Q(ft33-8!Chra%K1TOY?s$iLW1!dcDz_pI+F1WPi4B=@U8L%f55#+z;;Q z{j1Oa%{kxd4@W-H9Gh7iJgy-A&Tf%-uevV!LI4)ioEUwkIeS@lLE z{_dWRc&ps7t$&usxAF3S{oL>MjHP;`Hd!uazS^iR>Nl@4dT6RpC={#xeI4Gu?k;Qp z!6W6L-M8)c;%9jOt6!vh&v$<5rtNnX>cO|weZI58o|dI|cuM;9W0{tE?}M^3k&v#0 zSAtec!ofvZ=3A8h4qaHvnnWKlr= zbWvRsk%h%#LdAx*?w(!=sDa(g-kgk$&C5h?K?YV2NpU*suF~xchoyhbdbxSDtrgU< z(DKUaReuSI`@ND{rb1j>Qom%YB`_0uVM!k3C; zbFa!CDQqfCI5M;KehJ2X5?vM(k6-U)JfVV~N36Kltx>6`9O=j0w#Qo9E8bmyp~Iyb zB&!7B)YZQ?^rVxeJCmNE-`!eLtTuHTtFPh7A6@*@aMx_Oi*NLXq|JuAgP6WE+RWFa zG=F|T>gBpP>bz;zo07@-w)Jn`@@2V_o0Lx{ujBC-bp5PFHG<7-y;DM~&rls~)vbHs zx9j3jo#wvTjI>4d)<;PRM3drGBi!8R1M1ffi6_;lOiw&)*ncWgd^oaNp$Mi!TI&k1;tClZ+Z6cEJ`kl~lesOW)zI{7xi=DIDnwiVwFZ;y5 z|6X(y|6A?YH?ia7O@q&B>CMi6j})i}kXN-&PA!{BXJ@CUiqT|yE)tE-_=Dladab!wQgmIF zN~M&oR;xwTES3w!dQP>K3)yU8>Y+WOWB2XY_k{0i?HHSK&U(icH(m7)f41qwl;Lj%LhJ6~-3BDW;0*85aPf83@G z$NRSLeE9eF{J8!{cj*rOS331Qdk_8&j^^0B?zx-vs`cx>@T0BgG-A=eY=7F;xh+Tf z`w}64UsBKIwe;c*sXI-Hw0FoE(Kd;?wJX(`;tNV4;jWOV`7GyPtX2Hh_pkiSzg%(6taM%;Yew<0h7^Vb$1`L~aL=)JY|YnR{CmPp=NYlXh-b((?sN-!IX zMFXqWu4q*&rSrql*mA2`%YP1TiuLzw?9Z1ng}*!W;KAYen%c;~@Urb5r=0S8%Nx($ zyu72MbD13u$H!Hw+-cXXXQz8EuQL@Oi8U%C+YIDHL*FUDn^V)3yW8 zkA_3f>+Fbk>g@`Bet&#qWcv+I>jT2h>^N>Q~@Zj~54El7n&Z_?u~wsGpt(q7*U$1Pdu4u;&7;5vLh zvO-*aerTx>E6j-n3Ym3FWrHRLYi0pfK zLcPhcr8y+JsBRy%d=&DEBEGO9Ro38{S^M?+^?B+-_7z#DBxlGd+jy0A=!+)Nn&+ofubU}?a%9SpU zKi>=w>n$fzh27gmkL8Ejy?t9ref;Q`|NgogpZXW@57pM~haa;v((j8mzV|afzU-~9 zJhLqseSvDgV#QLv84Sm(wOYNB&8BB8@l}G6NM$TtJG5tHa&CY6XLh~eG*m~{C|7FL z>G^DV-=T?Gsee-at>49d=_kIU2R;8|x;tFI=W)}MXNuX2*Kd8ae(~Mh)kikE&-N>D zU#e*T^M&d5J71?t=T}E9&$jt2nZ7Q|>TLg>zyD(wEjRr0M*Lsq_lx|Q{##>zY?>Z3 z<5w-;qcBfY@bGjDyKk3=*-mI7NvQ%w9y)xhX%(Hym`O1g$#p^$w&#!&o{^je$pRN0g zp3B$$ygc1%vGC`4X@`G4ywD{&@h?07^+OXU-1Av%Xmg>=IVaS;B&{avy6XF%4b|)R zJk^hTX#eueWj5>JhoYy$*R5NTT~?pHHK2Fpg9+TMdl$}oT6+}O>#^S)bM|fSwb}9Y zr)S4KzL)pu_uufnk7eI~&$X;QJ%LMP@6@c)``YmzKm9N4sra>~^7Pro*7lz+86R6- z{=anNl)A0)ud?U5ofW%XS#ti-wVKZ_*p?N2xxVzi&Qy`Al!twvUp&9@b7_6;la)Ij zi+R^?m%rs^K_#eM##s3$6 zjQcdt|GwJ0OPUY<*Wdek@&BDwbvLVg=I{ACI7RMcN*XJ9y>eD5=( zd54|R_XpFTm)U)Ywv(x6INssF$-uy%%;E(kB@_dgfa*9Jg@7D4jwwLWz-a*s14Dx# zlPZwYBcQ>^z>v^FH7)h@u?D9tK>Mla8=ThQv;}BCUBVfcE%nrmPF&XDw56V22@QwK z>ZzQbaae=XmU_D7H0&;`r*?+MZVgUb>giY1V0BqN)k`$2*5I_Io{nWLHkZ{?zevYs z4NhC?=~>xeaT&1u#ThsBh#H{%^sok}Eui{mAOoPDzReAc6i`o-Rt<(VIBlt+OMC0* ae}+3V@47Md2`&J}H-o3EpUXO@geCwYAmz*e diff --git a/assets/images/game-icons.psd b/assets/images/game-icons.psd index 38189302c32bd7c99de1589e7ccf6810fccc7b13..4217d1cb31e4fc8f99e9562f5f0227ea47d4ded7 100644 GIT binary patch delta 12047 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 Yz-+W&1I>MO-`8BgOzfZ@&2QZQ0G4U8tN;K2 delta 6972 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 Ug?Lz=Mqgn&EZQ1BA>qA$0Y9HvzW@LL diff --git a/data/totalannihilation-artwork.json b/data/totalannihilation-artwork.json new file mode 100644 index 0000000..1e633a0 --- /dev/null +++ b/data/totalannihilation-artwork.json @@ -0,0 +1,99 @@ +{ + "_readme": [ + "Drop-in painted art for Total Annihilation (spec: src/games/totalannihilation/sprites.md).", + "Paint a sheet, drop the PNG under assets/images/totalannihilation/, set its `path` below and", + "reload — no code changes needed. Any sheet left path:null is rendered procedurally.", + "", + "`kind` selects which procedural painter stands in for an unpainted sheet, and it is the reason", + "adding a sheet here is a JSON-only change: painters are keyed by kind, never by sheet name.", + " unit — frameWidth == frameHeight, art drawn FACING RIGHT (+x) and CENTRED in the cell,", + " in neutral grey so the runtime army tint multiplies correctly. The engine rotates", + " the sprite, so keep the silhouette inside a circle of diameter frameWidth*0.7 or", + " the corners will clip as it turns.", + " structure — frameWidth == frameHeight, buildings are NOT rotated and NOT drawn facing right.", + " Two frames per building: the finished one and a translucent wireframe used while", + " it is still being nanolathed.", + " terrain — one sheet per theme. Frame order is fixed (see `terrainFrames` below) so any new", + " theme sheet drops straight in.", + " icon — build-menu glyphs, full colour, never tinted.", + "", + "`cols` only sizes the procedural stand-in canvas; a painted sheet is measured from the PNG.", + "Adding a THIRD ARMY = add two sheets here (kind unit + kind structure) and one `armies` entry", + "in data/totalannihilation-rules.json. Adding a THEME = one sheet here, one `themes` entry, and", + "the theme id in rules.skirmish.themes." + ], + + "terrainFrames": { + "_comment": "Fixed frame order every kind:terrain sheet must follow.", + "ground": 0, "groundAlt1": 1, "groundAlt2": 2, "rough": 3, + "cliff": 4, "water": 5, "waterDeep": 6, "metal": 7, "road": 8 + }, + + "sheets": { + "arm-units": { + "key": "ta-arm-units", "path": null, "kind": "unit", + "frameWidth": 64, "frameHeight": 64, "cols": 8 + }, + "arm-structures": { + "key": "ta-arm-structures", "path": null, "kind": "structure", + "frameWidth": 128, "frameHeight": 128, "cols": 6 + }, + "core-units": { + "key": "ta-core-units", "path": null, "kind": "unit", + "frameWidth": 64, "frameHeight": 64, "cols": 8 + }, + "core-structures": { + "key": "ta-core-structures", "path": null, "kind": "structure", + "frameWidth": 128, "frameHeight": 128, "cols": 6 + }, + "terrain-grasslands": { + "key": "ta-terrain-grasslands", "path": null, "kind": "terrain", + "frameWidth": 64, "frameHeight": 64, "cols": 9 + }, + "terrain-snowfields": { + "key": "ta-terrain-snowfields", "path": null, "kind": "terrain", + "frameWidth": 64, "frameHeight": 64, "cols": 9 + }, + "terrain-tropics": { + "key": "ta-terrain-tropics", "path": null, "kind": "terrain", + "frameWidth": 64, "frameHeight": 64, "cols": 9 + }, + "icons": { + "key": "ta-icons", "path": null, "kind": "icon", + "frameWidth": 44, "frameHeight": 44, "cols": 10 + } + }, + + "themes": { + "grasslands": { + "name": "Grasslands", + "sheet": "terrain-grasslands", + "palette": { + "ground": "#5f7f3d", "groundAlt1": "#6b8c45", "groundAlt2": "#55743a", + "rough": "#7d7048", "cliff": "#4c4838", "cliffTop": "#6b6552", + "water": "#2f5f9e", "waterDeep": "#1d3f70", + "metal": "#9aa3ab", "road": "#8b8578", "speck": "#3f5a28" + } + }, + "snowfields": { + "name": "Snowfields", + "sheet": "terrain-snowfields", + "palette": { + "ground": "#d6dee6", "groundAlt1": "#e4ebf1", "groundAlt2": "#c4cfda", + "rough": "#a9b4bf", "cliff": "#6a737e", "cliffTop": "#8e99a4", + "water": "#3d6d95", "waterDeep": "#27506f", + "metal": "#9aa3ab", "road": "#9fa6ad", "speck": "#aebccb" + } + }, + "tropics": { + "name": "Tropics", + "sheet": "terrain-tropics", + "palette": { + "ground": "#2f6b3c", "groundAlt1": "#3a7f46", "groundAlt2": "#265c33", + "rough": "#7a6b3a", "cliff": "#4a3f2e", "cliffTop": "#6d5f45", + "water": "#1f8fa8", "waterDeep": "#12667a", + "metal": "#9aa3ab", "road": "#9c8f6e", "speck": "#1d4a26" + } + } + } +} diff --git a/data/totalannihilation-rules.json b/data/totalannihilation-rules.json new file mode 100644 index 0000000..c7a6e3d --- /dev/null +++ b/data/totalannihilation-rules.json @@ -0,0 +1,276 @@ +{ + "_readme": [ + "Engine data for Total Annihilation. Everything the simulation knows lives here — adding a unit,", + "building, weapon, terrain type, army or map theme is a JSON-only change.", + "After editing, run `node tools/verifyTotalAnnihilation.js` — it cross-checks every id reference,", + "frame index and closed key set in this file and will tell you exactly what broke.", + "", + "Sheet resolution: a unit/building's `sheetSlot` names a field on its ARMY (unitSheet or", + "structureSheet), so one unit definition renders correctly for every army. That is what makes a", + "third army a two-line change: add an `armies` entry pointing at two new sheets in", + "data/totalannihilation-artwork.json.", + "", + "Units are drawn once and ROTATED at runtime — there are no per-facing frames. `turretFrame` is", + "an optional second sprite that aims independently of the hull." + ], + + "version": 1, + + "constants": { + "tickHz": 20, + "tileSize": 64, + "unitCapPerArmy": 100, + "projectileCap": 400, + "startEnergy": 1000, + "startMass": 1000, + "baseEnergyCap": 500, + "baseMassCap": 500, + "pathBudgetPerTick": 8, + "retargetPeriodTicks": 10, + "separationStiffness": 0.55, + "shoveAfterSec": 0.6, + "stuckGiveUpSec": 3.0, + "arriveSlackPx": 12, + "friendlyFire": false, + "buildPowerNominal": 100, + "eliminateWhenUnrecoverable": true + }, + + "sizeClasses": { + "small": { "radius": 16, "footprint": 1, "mass": 1 }, + "medium": { "radius": 26, "footprint": 1, "mass": 4 }, + "large": { "radius": 44, "footprint": 2, "mass": 16 } + }, + + "armorClasses": ["infantry", "light", "medium", "heavy", "structure"], + + "moveClasses": { + "foot": { + "cost": { "ground": 1.0, "rough": 1.3, "road": 0.85, "metal": 1.0, "cliff": null, "water": null } + }, + "tread": { + "cost": { "ground": 1.0, "rough": 1.8, "road": 0.7, "metal": 1.0, "cliff": null, "water": null } + }, + "wheel": { + "cost": { "ground": 1.0, "rough": 2.4, "road": 0.6, "metal": 1.0, "cliff": null, "water": null } + } + }, + + "armies": [ + { "id": "arm", "name": "ARM", "color": "#4a90e2", + "unitSheet": "arm-units", "structureSheet": "arm-structures" }, + { "id": "core", "name": "CORE", "color": "#e05a2f", + "unitSheet": "core-units", "structureSheet": "core-structures" } + ], + + "commanders": [ + { "id": "vance", "armyId": "arm", "opponentId": "ethel", "name": "Commander Ethel", + "tagline": "Holds the line, and makes you pay for every metre of it.", + "aiProfile": { "aggression": 0.45, "expansion": 0.65 } }, + { "id": "klaxon", "armyId": "core", "opponentId": "klaxon", "name": "Unit KLAXON", + "tagline": "Efficiency is a weapon. You are an inefficiency.", + "aiProfile": { "aggression": 0.7, "expansion": 0.45 } }, + { "id": "cybro", "armyId": "core", "opponentId": "cybro", "name": "Cy-Bro", + "tagline": "Builds faster than you can knock it down.", + "aiProfile": { "aggression": 0.35, "expansion": 0.85 } }, + { "id": "nadia", "armyId": "arm", "opponentId": "nadia", "name": "Commander Nadia", + "tagline": "Hits the flank while you watch the front.", + "aiProfile": { "aggression": 0.8, "expansion": 0.4 } } + ], + + "terrain": [ + { "id": "ground", "ch": ".", "frame": 0, "buildable": true, "blocksMove": false, "elevation": 0 }, + { "id": "rough", "ch": "r", "frame": 3, "buildable": true, "blocksMove": false, "elevation": 0 }, + { "id": "road", "ch": "=", "frame": 8, "buildable": true, "blocksMove": false, "elevation": 0 }, + { "id": "metal", "ch": "M", "frame": 7, "buildable": true, "blocksMove": false, "elevation": 0, + "massMultiplier": 2.0 }, + { "id": "cliff", "ch": "^", "frame": 4, "buildable": false, "blocksMove": true, "blocksFire": true, "elevation": 1 }, + { "id": "water", "ch": "~", "frame": 5, "buildable": false, "blocksMove": true, "elevation": -1 } + ], + + "weapons": [ + { "id": "rifle", "name": "Assault Rifle", "kind": "hitscan", + "damage": 6, "reload": 0.5, "burst": 3, "burstDelay": 0.06, + "range": 200, "spread": 0.05, "targets": ["ground"], + "armorMul": { "infantry": 1.4, "light": 0.8, "medium": 0.35, "heavy": 0.15, "structure": 0.2 }, + "fx": { "style": "tracer", "color": "#ffe6a0", "width": 1.5, "lifeMs": 70 }, + "sound": "sfx-battle-gunfire-modern" }, + + { "id": "sniperrifle", "name": "Marksman Rifle", "kind": "hitscan", + "damage": 55, "reload": 3.0, "burst": 1, + "range": 460, "spread": 0.0, "targets": ["ground"], + "armorMul": { "infantry": 2.2, "light": 0.9, "medium": 0.3, "heavy": 0.1, "structure": 0.1 }, + "fx": { "style": "beam", "color": "#d8f0ff", "width": 2, "lifeMs": 160 }, + "sound": "sfx-laser-zap" }, + + { "id": "tankgun", "name": "120mm Cannon", "kind": "ballistic", + "damage": 90, "reload": 2.4, "burst": 1, + "range": 340, "speed": 700, "spread": 0.015, "leadTarget": true, + "aoe": 48, "aoeFalloff": 0.4, "targets": ["ground"], + "armorMul": { "infantry": 0.5, "light": 1.0, "medium": 1.0, "heavy": 0.8, "structure": 1.2 }, + "fx": { "style": "shell", "color": "#ffd27a", "width": 3, "muzzle": 18 }, + "sound": "sfx-battle-tank" }, + + { "id": "rocketpod", "name": "Rocket Pod", "kind": "guided", + "damage": 130, "reload": 4.0, "burst": 2, "burstDelay": 0.25, + "range": 480, "minRange": 140, "speed": 340, "turnRate": 2.2, + "aoe": 80, "aoeFalloff": 0.35, "targets": ["ground"], + "armorMul": { "infantry": 0.6, "light": 1.1, "medium": 1.2, "heavy": 1.3, "structure": 1.4 }, + "fx": { "style": "rocket", "color": "#ff8844", "width": 2, "trail": true }, + "sound": "sfx-battle-missle" }, + + { "id": "dgun", "name": "D-Gun", "kind": "beam", + "damage": 2000, "reload": 1.5, "burst": 1, + "range": 220, "energyPerShot": 500, "targets": ["ground"], + "armorMul": { "infantry": 1, "light": 1, "medium": 1, "heavy": 1, "structure": 1 }, + "fx": { "style": "dgun", "color": "#8ad4ff", "width": 9, "lifeMs": 260 }, + "sound": "sfx-scifi-launch" } + ], + + "units": [ + { "id": "commander", "name": "Commander", "role": "builder", + "size": "medium", "radius": 20, + "hp": 3000, "speed": 62, "turnRate": 3.5, "moveClass": "foot", + "armorClass": "heavy", "sight": 420, + "cost": { "energy": 0, "mass": 0 }, "buildTime": 0, + "buildPower": 100, "buildRange": 200, + "builds": ["energygen", "massgen", "barracks", "vehicleplant"], + "produce": { "energy": 25, "mass": 3.0 }, + "storage": { "energy": 1000, "mass": 1000 }, + "weapons": ["dgun"], + "deathExplosion": { "radius": 280, "damage": 1200 }, + "sheetSlot": "unitSheet", "frame": 0, "turretFrame": 1, + "procShape": "commander", "icon": 0, "spritePx": 56 }, + + { "id": "infantry", "name": "Infantry", "role": "combat", + "size": "small", "radius": 16, + "hp": 90, "speed": 55, "turnRate": 6.0, "moveClass": "foot", + "armorClass": "infantry", "sight": 260, + "cost": { "energy": 120, "mass": 24 }, "buildTime": 6, + "builtBy": ["barracks"], + "weapons": ["rifle"], + "sheetSlot": "unitSheet", "frame": 2, + "procShape": "infantry", "icon": 1, "spritePx": 30, + "moveSound": "sfx-march" }, + + { "id": "sniper", "name": "Sniper", "role": "combat", + "size": "small", "radius": 16, + "hp": 70, "speed": 46, "turnRate": 5.0, "moveClass": "foot", + "armorClass": "infantry", "sight": 500, + "cost": { "energy": 260, "mass": 55 }, "buildTime": 12, + "builtBy": ["barracks"], + "weapons": ["sniperrifle"], + "sheetSlot": "unitSheet", "frame": 3, + "procShape": "sniper", "icon": 2, "spritePx": 30, + "moveSound": "sfx-march" }, + + { "id": "jeep", "name": "Jeep", "role": "scout", + "size": "small", "radius": 20, + "hp": 180, "speed": 175, "turnRate": 3.4, "moveClass": "wheel", + "armorClass": "light", "sight": 520, + "cost": { "energy": 320, "mass": 70 }, "buildTime": 8, + "builtBy": ["vehicleplant"], + "weapons": ["rifle"], + "sheetSlot": "unitSheet", "frame": 4, "turretFrame": 5, + "procShape": "jeep", "icon": 3, "spritePx": 40, + "moveSound": "sfx-engine-medium" }, + + { "id": "tank", "name": "Tank", "role": "combat", + "size": "medium", "radius": 26, + "hp": 520, "speed": 90, "turnRate": 2.2, "moveClass": "tread", + "armorClass": "medium", "sight": 300, + "cost": { "energy": 700, "mass": 180 }, "buildTime": 20, + "builtBy": ["vehicleplant"], + "weapons": ["tankgun"], + "sheetSlot": "unitSheet", "frame": 6, "turretFrame": 7, + "procShape": "tank", "icon": 4, "spritePx": 54, + "moveSound": "sfx-engine-heavy" }, + + { "id": "rockettank", "name": "Rocket Tank", "role": "artillery", + "size": "medium", "radius": 28, + "hp": 400, "speed": 75, "turnRate": 1.8, "moveClass": "tread", + "armorClass": "medium", "sight": 320, + "cost": { "energy": 1100, "mass": 300 }, "buildTime": 28, + "builtBy": ["vehicleplant"], + "weapons": ["rocketpod"], + "sheetSlot": "unitSheet", "frame": 8, "turretFrame": 9, + "procShape": "rockettank", "icon": 5, "spritePx": 56, + "moveSound": "sfx-engine-heavy" } + ], + + "buildings": [ + { "id": "energygen", "name": "Energy Generator", + "footprint": { "w": 2, "h": 2 }, + "hp": 800, "armorClass": "structure", "sight": 160, + "cost": { "energy": 200, "mass": 60 }, "buildTime": 12, + "produce": { "energy": 20 }, + "storage": { "energy": 500 }, + "deathExplosion": { "radius": 110, "damage": 160 }, + "sheetSlot": "structureSheet", "frame": 0, "buildFrame": 1, + "procShape": "energyGen", "icon": 10 }, + + { "id": "massgen", "name": "Mass Generator", + "footprint": { "w": 2, "h": 2 }, + "hp": 700, "armorClass": "structure", "sight": 160, + "cost": { "energy": 600, "mass": 50 }, "buildTime": 16, + "produce": { "mass": 2.0 }, + "upkeep": { "energy": 15 }, + "storage": { "mass": 250 }, + "terrainMultiplier": "massMultiplier", + "sheetSlot": "structureSheet", "frame": 2, "buildFrame": 3, + "procShape": "massGen", "icon": 11 }, + + { "id": "barracks", "name": "Barracks", + "footprint": { "w": 3, "h": 3 }, + "hp": 1400, "armorClass": "structure", "sight": 220, + "cost": { "energy": 600, "mass": 180 }, "buildTime": 26, + "buildPower": 100, "builds": ["infantry", "sniper"], + "spawnOffset": { "x": 0, "y": 2.2 }, + "sheetSlot": "structureSheet", "frame": 4, "buildFrame": 5, + "procShape": "barracks", "icon": 12 }, + + { "id": "vehicleplant", "name": "Vehicle Plant", + "footprint": { "w": 3, "h": 3 }, + "hp": 2200, "armorClass": "structure", "sight": 220, + "cost": { "energy": 1400, "mass": 520 }, "buildTime": 40, + "buildPower": 100, "builds": ["jeep", "tank", "rockettank"], + "spawnOffset": { "x": 0, "y": 2.2 }, + "sheetSlot": "structureSheet", "frame": 6, "buildFrame": 7, + "procShape": "vehiclePlant", "icon": 13 } + ], + + "commandIcons": { + "move": 20, "attack": 21, "attackMove": 22, "stop": 23, + "hold": 24, "patrol": 25, "guard": 26, "assist": 27 + }, + + "aiSkills": [ + { "skill": 1, "decisionPeriodMs": 1500, "apmCap": 1, "squadSize": 3, "expansion": 0.6, "microLevel": 0, "reactionMs": 4000 }, + { "skill": 2, "decisionPeriodMs": 1100, "apmCap": 2, "squadSize": 4, "expansion": 1.0, "microLevel": 1, "reactionMs": 3000 }, + { "skill": 3, "decisionPeriodMs": 800, "apmCap": 4, "squadSize": 6, "expansion": 1.6, "microLevel": 2, "reactionMs": 2000 }, + { "skill": 4, "decisionPeriodMs": 500, "apmCap": 8, "squadSize": 8, "expansion": 2.2, "microLevel": 3, "reactionMs": 1000 }, + { "skill": 5, "decisionPeriodMs": 300, "apmCap": 15, "squadSize": 10, "expansion": 3.0, "microLevel": 4, "reactionMs": 300 } + ], + + "skirmish": { + "sizes": { "small": 64, "medium": 96, "large": 128 }, + "symmetries": ["mirror-x", "mirror-y", "rotational"], + "themes": ["grasslands", "snowfields", "tropics"], + "defaults": { + "size": "medium", "symmetry": "mirror-x", "theme": "grasslands", + "aiSkill": 3, "playerArmy": "arm" + }, + "startUnits": ["commander"], + "gen": { + "waterFraction": 0.10, + "roughFraction": 0.18, + "cliffFraction": 0.10, + "metalSpotsPerStart": 4, + "metalSpotRadiusTiles": 14, + "minStartSeparationTiles": 40, + "smoothPasses": 3, + "startClearRadiusTiles": 8, + "noiseScale": 0.09 + } + } +} diff --git a/src/games/totalannihilation/TAAI.js b/src/games/totalannihilation/TAAI.js new file mode 100644 index 0000000..31d8287 --- /dev/null +++ b/src/games/totalannihilation/TAAI.js @@ -0,0 +1,533 @@ +// Total Annihilation — the CPU commander. +// +// Headless: no Phaser imports, runs in Node for tools/verifyTotalAnnihilation.js. +// +// Two rules this file must never break: +// 1. It issues orders ONLY through TALogic.issueOrder(). It never mutates state directly. +// That's what lets the verify script point this same AI at the human's side and assert +// a campaign mission is actually winnable. +// 2. It sees only what fog lets it see (isVisibleTo), and it never gets free resources. +// Difficulty is decision quality, reaction time and an orders-per-second cap — not +// income cheating. A cheating AI's win rate would tell us nothing about the balance. + +import { issueOrder, isVisibleTo, canPlaceAt, rngNext, rngInt } from './TALogic.js'; +import { worldToTileX, worldToTileY } from './TANav.js'; + +/** Desired force mix per skill. Below skill 3 the AI just buys whatever it can afford. */ +const COMPOSITION = { + infantry: 0.34, sniper: 0.10, jeep: 0.06, tank: 0.34, rockettank: 0.16, +}; + +function memFor(state, armyIdx) { + if (!state.aiMem) state.aiMem = []; + let m = state.aiMem[armyIdx]; + if (!m) { + m = state.aiMem[armyIdx] = { + nextThinkTick: 0, + ordersThisSecond: 0, secondMark: 0, + squad: [], squadPeak: 0, phase: 'build', + scoutId: 0, + knownEnemies: [], // [{x,y,defId,isBuilding,tick}] + lastAttackTick: -99999, + buildSpiral: 0, + baseX: 0, baseY: 0, baseSet: false, + }; + } + return m; +} + +/** + * Run one AI slice. Call every sim tick — it throttles itself to the skill's think period. + * @param {object} profile { skill, aggression, expansion } — mission JSON can override + */ +export function runAI(rules, state, armyIdx, profile = {}) { + if (state.over) return; + const army = state.armies[armyIdx]; + if (!army || !army.alive) return; + + const skill = Math.max(1, Math.min(5, profile.skill ?? army.aiSkill ?? 3)); + const tuning = rules.aiBySkill[skill] ?? rules.aiSkills[rules.aiSkills.length - 1]; + const aggression = profile.aggression ?? army.aiProfile?.aggression ?? 0.5; + // Economic ambition is primarily a SKILL trait — tuning.expansion runs 0.6..3.0 across + // skills 1-5, normalised here to 0.2..1.0. A commander's own aiProfile only nudges it, + // otherwise every skill level expands identically and the ladder collapses. + const skillExpansion = (tuning.expansion ?? 1.6) / 3; + const expansion = profile.expansion + ?? (army.aiProfile?.expansion != null + ? (skillExpansion * 0.6 + army.aiProfile.expansion * 0.4) + : skillExpansion); + + const mem = memFor(state, armyIdx); + const periodTicks = Math.max(1, Math.round((tuning.decisionPeriodMs / 1000) * rules.constants.tickHz)); + if (state.tick < mem.nextThinkTick) return; + mem.nextThinkTick = state.tick + periodTicks; + + // Orders-per-second cap: the honest handicap. A skill-1 AI physically cannot micro. + if (state.tick - mem.secondMark >= rules.constants.tickHz) { + mem.secondMark = state.tick; + mem.ordersThisSecond = 0; + } + const ctx = { + rules, state, armyIdx, army, skill, tuning, aggression, expansion, mem, + budget: Math.max(1, tuning.apmCap), + }; + + observe(ctx); + const mine = gatherOwn(ctx); + if (!mem.baseSet) { + const seed = mine.commanders[0] ?? mine.buildings[0] ?? mine.units[0]; + if (seed) { mem.baseX = seed.x; mem.baseY = seed.y; mem.baseSet = true; } + } + + manageEconomyAndBase(ctx, mine); + manageProduction(ctx, mine); + manageScout(ctx, mine); + manageMilitary(ctx, mine); +} + +function order(ctx, cmd) { + if (ctx.mem.ordersThisSecond >= ctx.budget) return { ok: false, error: 'apm cap' }; + ctx.mem.ordersThisSecond++; + return issueOrder(ctx.state, ctx.rules, cmd); +} + +// --------------------------------------------------------------------------- +// Perception — fog-limited, with memory of where things were last seen +// --------------------------------------------------------------------------- + +function observe(ctx) { + const { state, armyIdx, mem } = ctx; + const seen = []; + for (const e of state.entities) { + if (e.dead || e.army === armyIdx) continue; + if (!state.armies[e.army]) continue; + if (!isVisibleTo(state, armyIdx, e)) continue; + seen.push({ id: e.id, x: e.x, y: e.y, defId: e.defId, isBuilding: e.isBuilding, tick: state.tick }); + } + // Merge into memory: refresh what we can see, keep stale entries as "last known". + const byId = new Map(mem.knownEnemies.map((k) => [k.id, k])); + for (const s of seen) byId.set(s.id, s); + // Forget anything we haven't seen in two minutes, and anything we can see is gone. + const cutoff = state.tick - ctx.rules.constants.tickHz * 120; + const live = new Set(state.entities.filter((e) => !e.dead).map((e) => e.id)); + mem.knownEnemies = [...byId.values()] + .filter((k) => k.tick >= cutoff && live.has(k.id)) + .sort((a, b) => a.id - b.id); +} + +function gatherOwn(ctx) { + const { state, armyIdx, rules } = ctx; + const out = { commanders: [], builders: [], factories: [], buildings: [], units: [], sites: [], byDef: {} }; + for (const e of state.entities) { + if (e.dead || e.army !== armyIdx) continue; + const def = rules.defById[e.defId]; + out.byDef[e.defId] = (out.byDef[e.defId] ?? 0) + 1; + if (e.site) { out.sites.push(e); continue; } + if (e.isBuilding) { + out.buildings.push(e); + if (def.builds?.length) out.factories.push(e); + } else { + out.units.push(e); + if (def.builds?.length) { out.builders.push(e); out.commanders.push(e); } + } + } + return out; +} + +// --------------------------------------------------------------------------- +// Economy and base building +// --------------------------------------------------------------------------- + +function manageEconomyAndBase(ctx, mine) { + const { state, army, rules, mem, expansion } = ctx; + const builder = mine.builders[0]; + if (!builder) return; + + // Build orders QUEUE on the builder rather than replacing its current job, so the + // Commander rolls straight from one structure into the next with no idle gap. Issuing + // these unqueued caps every skill at one building in flight, which flattens the whole + // skill ladder — a skill-5 economy then looks identical to a skill-1 one. + const maxQueued = Math.max(1, Math.round(1 + expansion * 3)); + const queued = builder.orders.reduce((n, o) => n + (o.type === 'build' ? 1 : 0), 0); + if (queued >= maxQueued) return; + + const want = chooseBuilding(ctx, mine); + if (!want) return; + + const spot = findPlacement(ctx, mine, want, builder); + if (!spot) return; + + order(ctx, { + army: ctx.armyIdx, unitIds: [builder.id], + order: { type: 'build', defId: want.id, tx: spot.tx, ty: spot.ty }, + queue: queued > 0 || builder.orders.length > 0, + }); +} + +function chooseBuilding(ctx, mine) { + const { rules, army, mem, expansion } = ctx; + const n = (id) => (mine.byDef[id] ?? 0); + const eGen = rules.buildingById.energygen; + const mGen = rules.buildingById.massgen; + const barracks = rules.buildingById.barracks; + const plant = rules.buildingById.vehicleplant; + + // Opening: two power, one mass, then a barracks so there's something to fight with. + if (n('energygen') < 2) return eGen; + if (n('massgen') < 1) return mGen; + if (n('barracks') < 1) return barracks; + if (n('energygen') < 3) return eGen; + if (n('massgen') < 2) return mGen; + if (n('vehicleplant') < 1) return plant; + + // Steady state: chase the energy:mass income RATIO that the unit roster actually costs. + // A Tank is 700 energy to 180 mass, so roughly 4:1 — an AI that treats the two resources + // symmetrically ends up capping out on energy while its factories starve for mass. + const netE = Math.max(0, army.eIncome - army.eUpkeep); + const ratio = netE / Math.max(0.5, army.mIncome); + const TARGET_RATIO = 4.0; + + const cap = Math.round(3 + expansion * 7); + const roomE = n('energygen') < cap; + // A Mass Generator is an energy CONSUMER. Building one without the headroom to power it + // just throttles the ones already standing, so require spare energy before adding another. + const upkeepPer = mGen.upkeep?.energy ?? 0; + const roomM = n('massgen') < cap && netE > upkeepPer * 1.6; + + if (army.stallM < 0.98 && roomM) return mGen; + if (army.stallE < 0.98 && roomE) return eGen; + if (ratio > TARGET_RATIO && roomM) return mGen; + if (ratio < TARGET_RATIO && roomE) return eGen; + if (roomM) return mGen; + if (roomE) return eGen; + + // Economy is built out — convert the surplus into production capacity. + const factories = n('barracks') + n('vehicleplant'); + if (factories < Math.round(2 + expansion * 3)) { + return n('vehicleplant') <= n('barracks') ? plant : barracks; + } + return null; +} + +/** + * Spiral outward from the base for a legal footprint. Mass Generators additionally hunt for + * a metal patch, which is what makes map control matter without a reclaim economy. + */ +function findPlacement(ctx, mine, def, builder) { + const { state, rules } = ctx; + const mem = ctx.mem; + const ts = state.tileSize; + const bx = worldToTileX(state.nav, mem.baseSet ? mem.baseX : builder.x); + const by = worldToTileY(state.nav, mem.baseSet ? mem.baseY : builder.y); + + if (def.terrainMultiplier) { + const spot = findMetalSpot(ctx, def, bx, by); + if (spot) return spot; + } + + const maxR = 22; + for (let r = 2; r < maxR; r++) { + // Rotate the starting angle per attempt so the base doesn't grow as a solid slab — + // AoE chains through tightly packed buildings. + const steps = Math.max(8, r * 4); + const off = (ctx.mem.buildSpiral++ % steps); + for (let s = 0; s < steps; s++) { + const a = ((s + off) / steps) * Math.PI * 2; + const tx = bx + Math.round(Math.cos(a) * r); + const ty = by + Math.round(Math.sin(a) * r); + if (!spacedEnough(ctx, mine, tx, ty, def)) continue; + if (canPlaceAt(state, rules, tx, ty, def).ok) return { tx, ty }; + } + } + return null; +} + +function findMetalSpot(ctx, def, bx, by) { + const { state, rules } = ctx; + let best = null, bestD = Infinity; + for (let ty = 0; ty < state.h; ty++) { + for (let tx = 0; tx < state.w; tx++) { + const t = rules.terrain[state.terrain[ty * state.w + tx]]; + if (!t.massMultiplier) continue; + const d = Math.hypot(tx - bx, ty - by); + if (d > 30 || d >= bestD) continue; + if (!canPlaceAt(state, rules, tx, ty, def).ok) continue; + best = { tx, ty }; bestD = d; + } + } + return best; +} + +function spacedEnough(ctx, mine, tx, ty, def) { + const gap = 1; + for (const b of [...mine.buildings, ...mine.sites]) { + if (tx + def.footprint.w + gap <= b.tx) continue; + if (b.tx + b.fw + gap <= tx) continue; + if (ty + def.footprint.h + gap <= b.ty) continue; + if (b.ty + b.fh + gap <= ty) continue; + return false; + } + return true; +} + +// --------------------------------------------------------------------------- +// Production +// --------------------------------------------------------------------------- + +function manageProduction(ctx, mine) { + const { rules, state, skill, army } = ctx; + for (const f of mine.factories) { + const def = rules.defById[f.defId]; + const builds = def.builds ?? []; + if (!builds.length) continue; + const queued = f.queue.reduce((s, q) => s + q.count, 0); + if (queued >= 3) continue; + // Don't feed a factory while the economy is already choking — an over-queued plant + // starves the base builder and the AI never gets off the ground. + if (army.buildEff < 0.55 && queued >= 1) continue; + + const pick = skill <= 2 ? randomPick(ctx, builds) : templatePick(ctx, mine, builds); + if (!pick) continue; + order(ctx, { + army: ctx.armyIdx, + order: { type: 'factoryEnqueue', factoryId: f.id, defId: pick, count: 1 }, + }); + } +} + +function randomPick(ctx, builds) { + return builds[rngInt(ctx.state, builds.length)]; +} + +/** Pick whichever buildable unit is furthest below its share of the desired mix. */ +function templatePick(ctx, mine, builds) { + const { rules, skill } = ctx; + const weights = { ...COMPOSITION }; + if (skill >= 4) applyCounters(ctx, weights); + + const total = builds.reduce((s, id) => s + (mine.byDef[id] ?? 0), 0) + 1; + let best = null, bestGap = -Infinity; + for (const id of builds) { + const wgt = weights[id] ?? 0.1; + const have = (mine.byDef[id] ?? 0) / total; + const gap = wgt - have; + if (gap > bestGap) { bestGap = gap; best = id; } + } + return best; +} + +/** Skill 4+: bias the mix against what we've actually seen the enemy field. */ +function applyCounters(ctx, weights) { + const { mem, rules } = ctx; + let infantryish = 0, armour = 0, structures = 0; + for (const k of mem.knownEnemies) { + const def = rules.defById[k.defId]; + if (!def) continue; + if (k.isBuilding) { structures++; continue; } + if (def.armorClass === 'infantry' || def.armorClass === 'light') infantryish++; + else armour++; + } + const seen = infantryish + armour; + if (!seen) return; + const armourShare = armour / seen; + // Tanks shred infantry; rockets and snipers answer armour and buildings. + weights.tank *= 1 + (1 - armourShare) * 0.8; + weights.rockettank *= 1 + armourShare * 1.0 + (structures > 2 ? 0.3 : 0); + weights.sniper *= 1 + (1 - armourShare) * 0.5; +} + +// --------------------------------------------------------------------------- +// Scouting +// --------------------------------------------------------------------------- + +function manageScout(ctx, mine) { + const { state, mem, skill } = ctx; + if (skill < 3) return; + const scout = state.entities.find((e) => e.id === mem.scoutId && !e.dead); + if (scout) { + if (scout.orders.length) return; + const t = enemyBaseGuess(ctx); + if (t) order(ctx, { army: ctx.armyIdx, unitIds: [scout.id], order: { type: 'move', x: t.x, y: t.y } }); + return; + } + const jeep = mine.units.find((u) => u.defId === 'jeep' && !u.orders.length); + if (!jeep) return; + mem.scoutId = jeep.id; + const t = enemyBaseGuess(ctx); + if (t) order(ctx, { army: ctx.armyIdx, unitIds: [jeep.id], order: { type: 'attackMove', x: t.x, y: t.y } }); +} + +function enemyBaseGuess(ctx) { + const { state, armyIdx, mem } = ctx; + // Prefer something we've actually seen; fall back to the enemy's start position. + const building = mem.knownEnemies.find((k) => k.isBuilding); + if (building) return { x: building.x, y: building.y }; + if (mem.knownEnemies.length) return { x: mem.knownEnemies[0].x, y: mem.knownEnemies[0].y }; + + // Nothing in sight. If we've already been to the enemy start, sweep the map instead of + // parking on a stale position — otherwise a won game never actually ends, because the + // loser's last builder is hiding in a corner we stopped looking at. + const start = state.starts.find((s) => s.army !== armyIdx); + if (start) { + const sx = (start.x + 0.5) * state.tileSize, sy = (start.y + 0.5) * state.tileSize; + if (!mem.startCleared) { + const near = state.entities.some((e) => !e.dead && e.army === armyIdx + && Math.hypot(e.x - sx, e.y - sy) < state.tileSize * 8); + if (near) mem.startCleared = true; + else return { x: sx, y: sy }; + } + } + return sweepTarget(ctx); +} + +/** + * Rotate through map sectors, preferring ones this army has never explored. This is what + * turns a won game into a finished game. + */ +function sweepTarget(ctx) { + const { state, army, mem } = ctx; + const SECTORS = 4; + const cellW = state.worldW / SECTORS, cellH = state.worldH / SECTORS; + const cell = state.tileSize * 2; + + const candidates = []; + for (let sy = 0; sy < SECTORS; sy++) { + for (let sx = 0; sx < SECTORS; sx++) { + const cx = (sx + 0.5) * cellW, cy = (sy + 0.5) * cellH; + const gx = Math.min(state.visW - 1, Math.floor(cx / cell)); + const gy = Math.min(state.visH - 1, Math.floor(cy / cell)); + const explored = army.explored[gy * state.visW + gx] === 1; + candidates.push({ x: cx, y: cy, explored, i: sy * SECTORS + sx }); + } + } + const unexplored = candidates.filter((c) => !c.explored); + const pool = unexplored.length ? unexplored : candidates; + const pick = pool[(mem.sweepIdx = ((mem.sweepIdx ?? 0) + 1) % pool.length)]; + return { x: pick.x, y: pick.y }; +} + +// --------------------------------------------------------------------------- +// Military +// --------------------------------------------------------------------------- + +function manageMilitary(ctx, mine) { + const { state, rules, mem, tuning, aggression, skill, armyIdx } = ctx; + + // Anything combat-capable and idle joins the pool. + const idle = mine.units.filter((u) => { + if (u.id === mem.scoutId) return false; + const def = rules.defById[u.defId]; + if (!def.weaponDefs?.length || def.builds?.length) return false; + return u.orders.length === 0; + }); + + mem.squad = mem.squad.filter((id) => state.entities.some((e) => e.id === id && !e.dead)); + for (const u of idle) if (!mem.squad.includes(u.id)) mem.squad.push(u.id); + + // Home defence always wins over pushing out. + const threat = nearestThreatToBase(ctx); + if (threat) { + const defenders = mem.squad.slice(0, Math.max(3, Math.ceil(mem.squad.length * 0.7))); + if (defenders.length) { + order(ctx, { + army: armyIdx, unitIds: defenders, + order: { type: 'attackMove', x: threat.x, y: threat.y }, + }); + mem.phase = 'defend'; + } + return; + } + + const target = enemyBaseGuess(ctx); + if (!target) return; + + const wantSize = Math.max(2, Math.round(tuning.squadSize * (0.6 + aggression * 0.8))); + if (mem.phase !== 'attack') { + if (mem.squad.length < wantSize) { + // Gather near the base while we build up. + const gathering = idle.filter((u) => Math.hypot(u.x - mem.baseX, u.y - mem.baseY) > state.tileSize * 8); + if (gathering.length) { + order(ctx, { + army: armyIdx, unitIds: gathering.map((u) => u.id), + order: { type: 'move', x: mem.baseX, y: mem.baseY }, + }); + } + return; + } + mem.phase = 'attack'; + mem.squadPeak = mem.squad.length; + mem.lastAttackTick = state.tick; + } + + if (mem.phase === 'attack') { + // Break off if the push has been gutted — skill 2+ knows when it's lost a fight. + if (skill >= 2 && mem.squad.length < mem.squadPeak * 0.4) { + mem.phase = 'build'; + order(ctx, { + army: armyIdx, unitIds: mem.squad.slice(), + order: { type: 'move', x: mem.baseX, y: mem.baseY }, + }); + mem.squad = []; + return; + } + const pushing = mem.squad.filter((id) => { + const e = state.entities.find((x) => x.id === id && !x.dead); + return e && e.orders.length === 0; + }); + if (pushing.length) { + order(ctx, { + army: armyIdx, unitIds: pushing, + order: { type: 'attackMove', x: target.x, y: target.y }, + }); + } + if (skill >= 3) focusFire(ctx, mine); + } +} + +function nearestThreatToBase(ctx) { + const { state, mem, armyIdx } = ctx; + if (!mem.baseSet) return null; + const radius = state.tileSize * 16; + let best = null, bestD = Infinity; + for (const e of state.entities) { + if (e.dead || e.army === armyIdx || !state.armies[e.army]) continue; + if (!isVisibleTo(state, armyIdx, e)) continue; + if (e.isBuilding) continue; + const d = Math.hypot(e.x - mem.baseX, e.y - mem.baseY); + if (d < radius && d < bestD) { best = e; bestD = d; } + } + return best; +} + +/** Skill 3+: concentrate the squad on one target instead of spreading damage. */ +function focusFire(ctx, mine) { + const { state, rules, mem, armyIdx } = ctx; + const members = mem.squad + .map((id) => state.entities.find((e) => e.id === id && !e.dead)) + .filter(Boolean); + if (members.length < 3) return; + + const cx = members.reduce((s, u) => s + u.x, 0) / members.length; + const cy = members.reduce((s, u) => s + u.y, 0) / members.length; + + let best = null, bestScore = -Infinity; + for (const e of state.entities) { + if (e.dead || e.army === armyIdx || !state.armies[e.army]) continue; + if (!isVisibleTo(state, armyIdx, e)) continue; + const d = Math.hypot(e.x - cx, e.y - cy); + if (d > state.tileSize * 9) continue; + const def = rules.defById[e.defId]; + // Prefer the closest thing to dying that can still shoot back. + const score = (def.weaponDefs?.length ? 2 : 1) * (1 - e.hp / e.maxHp + 0.3) * (1 - d / (state.tileSize * 9)); + if (score > bestScore) { best = e; bestScore = score; } + } + if (!best) return; + const shooters = members.filter((u) => Math.hypot(u.x - best.x, u.y - best.y) < rules.defById[u.defId].maxRange * 1.5); + if (shooters.length < 2) return; + order(ctx, { + army: armyIdx, unitIds: shooters.map((u) => u.id), + order: { type: 'attack', targetId: best.id }, + }); +} + +export { memFor }; diff --git a/src/games/totalannihilation/TAArt.js b/src/games/totalannihilation/TAArt.js new file mode 100644 index 0000000..689af98 --- /dev/null +++ b/src/games/totalannihilation/TAArt.js @@ -0,0 +1,561 @@ +// Total Annihilation — procedural art fallback. +// +// Every spritesheet declared in data/totalannihilation-artwork.json is optional. If its +// `path` is null (or the PNG 404s), we paint a canvas stand-in with the identical frame +// layout, so the view never knows the difference and the game is fully playable with zero +// art files present. +// +// The one structural change from the Advance Wars original this is modelled on +// (AdvanceWarsMapView.js ensureSheets/PROC_PAINTERS): painters are keyed by the sheet's +// `kind`, NOT by its name. That is what makes adding a third army or a fourth terrain theme +// a pure JSON edit — a new sheet with kind:"unit" automatically gets the unit painter. + +const BODY = '#dcdcdc'; // neutral grey — the runtime army tint multiplies over this +const BODY_DARK = '#a8adb5'; +const OUTLINE = '#26262e'; +const ACCENT = '#8f939b'; +const GLASS = '#3a4450'; +const HOT = '#ffd27a'; + +/** Deterministic per-frame PRNG so speckles are stable across reloads. */ +function frameRng(seed) { + let a = (seed * 2654435761) >>> 0; + return () => { + a = (a + 0x6d2b79f5) >>> 0; + let t = Math.imul(a ^ (a >>> 15), a | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +/** + * Create a canvas texture laid out as a spritesheet and register its frames. + * `at(frame, draw)` translates the context to that frame's origin; `finish()` uploads. + */ +function mkCanvasSheet(scene, key, frameW, frameH, cols, count) { + const rows = Math.max(1, Math.ceil(count / cols)); + const tex = scene.textures.createCanvas(key, cols * frameW, rows * frameH); + const ctx = tex.getContext(); + return { + ctx, + at(frame, draw) { + const fx = (frame % cols) * frameW; + const fy = ((frame / cols) | 0) * frameH; + ctx.save(); + ctx.translate(fx, fy); + draw(ctx, frameW, frameH); + ctx.restore(); + }, + finish() { + tex.refresh(); + for (let f = 0; f < count; f++) { + const fx = (f % cols) * frameW; + const fy = ((f / cols) | 0) * frameH; + tex.add(f, 0, fx, fy, frameW, frameH); + } + return tex; + }, + }; +} + +function roundRect(ctx, x, y, w, h, r) { + ctx.beginPath(); + ctx.moveTo(x + r, y); + ctx.arcTo(x + w, y, x + w, y + h, r); + ctx.arcTo(x + w, y + h, x, y + h, r); + ctx.arcTo(x, y + h, x, y, r); + ctx.arcTo(x, y, x + w, y, r); + ctx.closePath(); +} + +function fillStroke(ctx, fill, stroke = OUTLINE, lw = 2) { + ctx.fillStyle = fill; ctx.fill(); + if (stroke) { ctx.lineWidth = lw; ctx.strokeStyle = stroke; ctx.stroke(); } +} + +// --------------------------------------------------------------------------- +// Unit shapes — all drawn FACING RIGHT (+x), centred, inside a rotation-safe circle +// --------------------------------------------------------------------------- + +const UNIT_SHAPES = { + commander(ctx, S) { + const c = S / 2; + // legs behind + ctx.save(); ctx.translate(c, c); + ctx.fillStyle = BODY_DARK; ctx.strokeStyle = OUTLINE; ctx.lineWidth = 2; + for (const sy of [-9, 9]) { + roundRect(ctx, -14, sy - 4, 14, 8, 3); ctx.fill(); ctx.stroke(); + } + // torso + roundRect(ctx, -11, -11, 24, 22, 6); fillStroke(ctx, BODY); + // shoulder pods + for (const sy of [-12, 12]) { + ctx.beginPath(); ctx.arc(-2, sy, 5.5, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK); + } + // visor + ctx.fillStyle = GLASS; + roundRect(ctx, 6, -5, 6, 10, 2); ctx.fill(); + // nanolathe arm + ctx.strokeStyle = ACCENT; ctx.lineWidth = 3; + ctx.beginPath(); ctx.moveTo(10, -3); ctx.lineTo(21, -3); ctx.stroke(); + ctx.fillStyle = HOT; + ctx.beginPath(); ctx.arc(22, -3, 2.5, 0, Math.PI * 2); ctx.fill(); + ctx.restore(); + }, + + commanderTurret(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + ctx.beginPath(); ctx.arc(0, 0, 7, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK); + roundRect(ctx, 5, -4.5, 17, 9, 3); fillStroke(ctx, BODY); + ctx.strokeStyle = '#8ad4ff'; ctx.lineWidth = 2; + ctx.beginPath(); ctx.arc(21, 0, 5, -0.9, 0.9); ctx.stroke(); + ctx.restore(); + }, + + infantry(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + roundRect(ctx, -8, -5, 16, 10, 5); fillStroke(ctx, BODY); + ctx.beginPath(); ctx.arc(1, 0, 4.5, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK); + ctx.strokeStyle = OUTLINE; ctx.lineWidth = 2; + ctx.beginPath(); ctx.moveTo(4, -2); ctx.lineTo(14, -2); ctx.stroke(); + ctx.restore(); + }, + + sniper(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + roundRect(ctx, -9, -4, 17, 8, 4); fillStroke(ctx, BODY); + ctx.beginPath(); ctx.arc(0, 0, 4, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK); + ctx.strokeStyle = OUTLINE; ctx.lineWidth = 2; + ctx.beginPath(); ctx.moveTo(3, -2); ctx.lineTo(20, -2); ctx.stroke(); + ctx.fillStyle = GLASS; + ctx.beginPath(); ctx.arc(6, -4.5, 2.2, 0, Math.PI * 2); ctx.fill(); + ctx.restore(); + }, + + jeep(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + // wheels first, so the body overlaps them + ctx.fillStyle = '#2f3238'; + for (const [wx, wy] of [[-8, -9], [8, -9], [-8, 9], [8, 9]]) { + roundRect(ctx, wx - 4, wy - 3, 8, 6, 2.5); ctx.fill(); + } + roundRect(ctx, -13, -8, 26, 16, 4); fillStroke(ctx, BODY); + // windscreen wedge + ctx.fillStyle = GLASS; + ctx.beginPath(); ctx.moveTo(4, -6); ctx.lineTo(12, -3); ctx.lineTo(12, 3); ctx.lineTo(4, 6); + ctx.closePath(); ctx.fill(); + ctx.restore(); + }, + + jeepTurret(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + ctx.beginPath(); ctx.arc(0, 0, 4.5, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK, OUTLINE, 1.5); + ctx.strokeStyle = OUTLINE; ctx.lineWidth = 2.5; + ctx.beginPath(); ctx.moveTo(3, 0); ctx.lineTo(14, 0); ctx.stroke(); + ctx.restore(); + }, + + tank(ctx, S) { tankHull(ctx, S, 34, 24); }, + rockettank(ctx, S) { tankHull(ctx, S, 36, 24); }, + + tankTurret(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + // trapezoid turret + ctx.beginPath(); + ctx.moveTo(-8, -8); ctx.lineTo(7, -6); ctx.lineTo(7, 6); ctx.lineTo(-8, 8); + ctx.closePath(); fillStroke(ctx, BODY); + ctx.beginPath(); ctx.arc(-1, 0, 4, 0, Math.PI * 2); fillStroke(ctx, BODY_DARK, OUTLINE, 1.5); + ctx.strokeStyle = OUTLINE; ctx.lineWidth = 4; + ctx.beginPath(); ctx.moveTo(5, 0); ctx.lineTo(24, 0); ctx.stroke(); + ctx.strokeStyle = BODY_DARK; ctx.lineWidth = 2; + ctx.beginPath(); ctx.moveTo(5, 0); ctx.lineTo(24, 0); ctx.stroke(); + ctx.restore(); + }, + + rockettankTurret(ctx, S) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + roundRect(ctx, -8, -9, 18, 18, 3); fillStroke(ctx, BODY); + ctx.fillStyle = '#2f3238'; + for (const [tx, ty] of [[3, -4.5], [3, 4.5], [-2, -4.5], [-2, 4.5]]) { + ctx.beginPath(); ctx.arc(tx, ty, 2.6, 0, Math.PI * 2); ctx.fill(); + } + ctx.restore(); + }, +}; + +function tankHull(ctx, S, len, wid) { + const c = S / 2; + ctx.save(); ctx.translate(c, c); + // tread rails + ctx.fillStyle = '#33363c'; ctx.strokeStyle = OUTLINE; ctx.lineWidth = 1.5; + for (const sy of [-wid / 2, wid / 2 - 6]) { + roundRect(ctx, -len / 2, sy, len, 6, 2); ctx.fill(); ctx.stroke(); + } + ctx.fillStyle = '#4a4e56'; + for (let i = 0; i < 6; i++) { + const bx = -len / 2 + 2 + i * ((len - 4) / 6); + ctx.fillRect(bx, -wid / 2 + 1, 3, 4); + ctx.fillRect(bx, wid / 2 - 5, 3, 4); + } + // hull + roundRect(ctx, -len / 2 + 3, -wid / 2 + 5, len - 6, wid - 10, 4); fillStroke(ctx, BODY); + // glacis plate + ctx.fillStyle = BODY_DARK; + ctx.beginPath(); + ctx.moveTo(len / 2 - 3, -wid / 2 + 6); ctx.lineTo(len / 2 - 3, wid / 2 - 6); + ctx.lineTo(len / 2 - 9, wid / 2 - 6); ctx.lineTo(len / 2 - 9, -wid / 2 + 6); + ctx.closePath(); ctx.fill(); + ctx.restore(); +} + +// --------------------------------------------------------------------------- +// Structure shapes — NOT rotated, drawn to fill the cell +// --------------------------------------------------------------------------- + +const STRUCT_SHAPES = { + energyGen(ctx, S) { + const p = S * 0.12, d = S - p * 2; + roundRect(ctx, p, p, d, d, S * 0.10); fillStroke(ctx, BODY, OUTLINE, 3); + // vent slots + ctx.fillStyle = '#3b3f47'; + for (let i = 0; i < 4; i++) ctx.fillRect(p + d * 0.14, p + d * (0.16 + i * 0.18), d * 0.24, d * 0.10); + // lightning glyph + ctx.fillStyle = HOT; + ctx.beginPath(); + ctx.moveTo(p + d * 0.66, p + d * 0.14); + ctx.lineTo(p + d * 0.48, p + d * 0.52); + ctx.lineTo(p + d * 0.62, p + d * 0.52); + ctx.lineTo(p + d * 0.46, p + d * 0.90); + ctx.lineTo(p + d * 0.82, p + d * 0.44); + ctx.lineTo(p + d * 0.66, p + d * 0.44); + ctx.closePath(); ctx.fill(); + ctx.strokeStyle = OUTLINE; ctx.lineWidth = 2; ctx.stroke(); + }, + + massGen(ctx, S) { + const c = S / 2, r = S * 0.40; + // hex pad + ctx.beginPath(); + for (let i = 0; i < 6; i++) { + const a = (i / 6) * Math.PI * 2 + Math.PI / 6; + const x = c + Math.cos(a) * r, y = c + Math.sin(a) * r; + i ? ctx.lineTo(x, y) : ctx.moveTo(x, y); + } + ctx.closePath(); fillStroke(ctx, BODY, OUTLINE, 3); + // concentric rings + ctx.strokeStyle = ACCENT; ctx.lineWidth = 2.5; + for (const rr of [r * 0.62, r * 0.44, r * 0.26]) { + ctx.beginPath(); ctx.arc(c, c, rr, 0, Math.PI * 2); ctx.stroke(); + } + // drill spindle + ctx.beginPath(); ctx.arc(c, c, r * 0.16, 0, Math.PI * 2); fillStroke(ctx, '#3b3f47', OUTLINE, 2); + // corner anchors + ctx.fillStyle = BODY_DARK; + for (const [ax, ay] of [[-1, -1], [1, -1], [-1, 1], [1, 1]]) { + roundRect(ctx, c + ax * r * 0.74 - 5, c + ay * r * 0.74 - 5, 10, 10, 2); ctx.fill(); + } + }, + + barracks(ctx, S) { + const p = S * 0.10, w = S - p * 2, h = S - p * 2; + roundRect(ctx, p, p, w, h, S * 0.06); fillStroke(ctx, BODY, OUTLINE, 3); + // corrugated roof + ctx.strokeStyle = BODY_DARK; ctx.lineWidth = 2; + for (let i = 1; i < 7; i++) { + const y = p + (h * i) / 8; + ctx.beginPath(); ctx.moveTo(p + 4, y); ctx.lineTo(p + w - 4, y); ctx.stroke(); + } + // arched door at the bay end + ctx.fillStyle = '#2f3238'; + ctx.beginPath(); + const dw = w * 0.34, dx = p + w / 2 - dw / 2, dy = p + h * 0.62; + ctx.moveTo(dx, p + h - 4); + ctx.lineTo(dx, dy + dw / 2); + ctx.arc(dx + dw / 2, dy + dw / 2, dw / 2, Math.PI, 0); + ctx.lineTo(dx + dw, p + h - 4); + ctx.closePath(); ctx.fill(); + // marching chevron over the door + ctx.strokeStyle = HOT; ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(p + w / 2 - 10, dy - 12); ctx.lineTo(p + w / 2, dy - 4); ctx.lineTo(p + w / 2 + 10, dy - 12); + ctx.stroke(); + }, + + vehiclePlant(ctx, S) { + const p = S * 0.07, w = S - p * 2, h = S - p * 2; + roundRect(ctx, p, p, w, h, S * 0.05); fillStroke(ctx, BODY, OUTLINE, 3); + // gantry rail across the roof + ctx.fillStyle = BODY_DARK; + ctx.fillRect(p + 4, p + h * 0.12, w - 8, h * 0.09); + ctx.fillStyle = '#3b3f47'; + for (let i = 0; i < 5; i++) ctx.fillRect(p + 8 + i * ((w - 16) / 5), p + h * 0.12, 5, h * 0.09); + // roll-up bay door with slats + const dw = w * 0.62, dx = p + w / 2 - dw / 2, dy = p + h * 0.38, dh = h * 0.44; + ctx.fillStyle = '#2f3238'; + ctx.fillRect(dx, dy, dw, dh); + ctx.strokeStyle = '#4a4e56'; ctx.lineWidth = 2; + for (let i = 1; i < 6; i++) { + const y = dy + (dh * i) / 6; + ctx.beginPath(); ctx.moveTo(dx + 2, y); ctx.lineTo(dx + dw - 2, y); ctx.stroke(); + } + // floor chevron pointing out of the mouth — also shows where units will spawn + ctx.strokeStyle = HOT; ctx.lineWidth = 4; + ctx.beginPath(); + ctx.moveTo(p + w / 2 - 14, p + h - 20); ctx.lineTo(p + w / 2, p + h - 8); ctx.lineTo(p + w / 2 + 14, p + h - 20); + ctx.stroke(); + }, +}; + +// --------------------------------------------------------------------------- +// Painters, keyed by sheet kind +// --------------------------------------------------------------------------- + +const PROC_PAINTERS = { + /** One frame per unit hull, plus one per turret. Driven entirely off the rules. */ + unit(scene, key, spec, rules) { + const defs = rules.units.filter((u) => u.sheetSlot === 'unitSheet'); + let count = 0; + for (const d of defs) count = Math.max(count, d.frame + 1, (d.turretFrame ?? -1) + 1); + const sh = mkCanvasSheet(scene, key, spec.frameWidth, spec.frameHeight, spec.cols, count); + for (const d of defs) { + const hull = UNIT_SHAPES[d.procShape]; + if (hull) sh.at(d.frame, (ctx, S) => hull(ctx, S)); + if (d.turretFrame != null) { + const turret = UNIT_SHAPES[`${d.procShape}Turret`]; + if (turret) sh.at(d.turretFrame, (ctx, S) => turret(ctx, S)); + } + } + return sh.finish(); + }, + + /** Two frames per building: finished, then a translucent wireframe for the build site. */ + structure(scene, key, spec, rules) { + const defs = rules.buildings.filter((b) => b.sheetSlot === 'structureSheet'); + let count = 0; + for (const d of defs) count = Math.max(count, d.frame + 1, (d.buildFrame ?? -1) + 1); + const sh = mkCanvasSheet(scene, key, spec.frameWidth, spec.frameHeight, spec.cols, count); + for (const d of defs) { + const draw = STRUCT_SHAPES[d.procShape]; + if (!draw) continue; + sh.at(d.frame, (ctx, S) => draw(ctx, S)); + if (d.buildFrame != null) { + sh.at(d.buildFrame, (ctx, S) => { + ctx.globalAlpha = 0.35; + draw(ctx, S); + ctx.globalAlpha = 1; + }); + } + } + return sh.finish(); + }, + + /** One sheet per theme, coloured entirely from that theme's palette. */ + terrain(scene, key, spec, rules, art, sheetName) { + const theme = Object.values(art.themes ?? {}).find((t) => t.sheet === sheetName); + const p = theme?.palette ?? {}; + const F = art.terrainFrames ?? {}; + const count = Math.max(9, ...Object.values(F).map((v) => v + 1)); + const sh = mkCanvasSheet(scene, key, spec.frameWidth, spec.frameHeight, spec.cols, count); + + const speckle = (ctx, S, seed, color, n, r0, r1) => { + const rnd = frameRng(seed); + ctx.fillStyle = color; + for (let i = 0; i < n; i++) { + const x = rnd() * S, y = rnd() * S, r = r0 + rnd() * (r1 - r0); + ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fill(); + } + }; + const flat = (frame, color, seed, speckColor, n = 10) => sh.at(frame, (ctx, S) => { + ctx.fillStyle = color; ctx.fillRect(0, 0, S, S); + speckle(ctx, S, seed, speckColor, n, 1, 2.6); + }); + + flat(F.ground ?? 0, p.ground ?? '#5f7f3d', 11, p.speck ?? '#000', 10); + flat(F.groundAlt1 ?? 1, p.groundAlt1 ?? p.ground ?? '#6b8c45', 22, p.speck ?? '#000', 12); + flat(F.groundAlt2 ?? 2, p.groundAlt2 ?? p.ground ?? '#55743a', 33, p.speck ?? '#000', 8); + + sh.at(F.rough ?? 3, (ctx, S) => { + ctx.fillStyle = p.rough ?? '#7d7048'; ctx.fillRect(0, 0, S, S); + speckle(ctx, S, 44, 'rgba(0,0,0,0.30)', 22, 1.5, 3.6); + speckle(ctx, S, 45, 'rgba(255,255,255,0.14)', 14, 1, 2.4); + }); + + sh.at(F.cliff ?? 4, (ctx, S) => { + ctx.fillStyle = p.cliff ?? '#4c4838'; ctx.fillRect(0, 0, S, S); + // lit top edge sells the height difference without a real height layer + ctx.fillStyle = p.cliffTop ?? '#6b6552'; + ctx.fillRect(0, 0, S, S * 0.30); + ctx.strokeStyle = 'rgba(0,0,0,0.45)'; ctx.lineWidth = 2; + const rnd = frameRng(55); + for (let i = 0; i < 5; i++) { + const x = rnd() * S; + ctx.beginPath(); ctx.moveTo(x, S * 0.30); ctx.lineTo(x + (rnd() - 0.5) * 12, S); ctx.stroke(); + } + }); + + const wave = (frame, color, seed) => sh.at(frame, (ctx, S) => { + ctx.fillStyle = color; ctx.fillRect(0, 0, S, S); + const rnd = frameRng(seed); + ctx.strokeStyle = 'rgba(255,255,255,0.16)'; ctx.lineWidth = 2; + for (let i = 0; i < 3; i++) { + const y = rnd() * S; + ctx.beginPath(); + ctx.moveTo(0, y); + ctx.quadraticCurveTo(S / 2, y + (rnd() - 0.5) * 10, S, y); + ctx.stroke(); + } + }); + wave(F.water ?? 5, p.water ?? '#2f5f9e', 66); + wave(F.waterDeep ?? 6, p.waterDeep ?? '#1d3f70', 77); + + sh.at(F.metal ?? 7, (ctx, S) => { + ctx.fillStyle = p.metal ?? '#9aa3ab'; ctx.fillRect(0, 0, S, S); + ctx.fillStyle = 'rgba(0,0,0,0.35)'; + for (let ry = 0; ry < 3; ry++) { + for (let rx = 0; rx < 3; rx++) { + ctx.beginPath(); + ctx.arc(S * (0.22 + rx * 0.28), S * (0.22 + ry * 0.28), 2.6, 0, Math.PI * 2); + ctx.fill(); + } + } + ctx.strokeStyle = 'rgba(255,255,255,0.20)'; ctx.lineWidth = 2; + ctx.strokeRect(3, 3, S - 6, S - 6); + }); + + flat(F.road ?? 8, p.road ?? '#8b8578', 88, 'rgba(0,0,0,0.25)', 6); + return sh.finish(); + }, + + /** Build-menu glyphs: a dark plate plus a shrunken copy of the unit/structure shape. */ + icon(scene, key, spec, rules) { + const entries = []; + for (const d of [...rules.units, ...rules.buildings]) { + if (d.icon != null) entries.push({ frame: d.icon, shape: d.procShape, building: !!d.isBuilding }); + } + const cmd = rules.commandIcons ?? {}; + let count = 0; + for (const e of entries) count = Math.max(count, e.frame + 1); + for (const f of Object.values(cmd)) count = Math.max(count, f + 1); + + const sh = mkCanvasSheet(scene, key, spec.frameWidth, spec.frameHeight, spec.cols, count); + const plate = (ctx, S) => { + roundRect(ctx, 2, 2, S - 4, S - 4, 6); + ctx.fillStyle = '#1b2029'; ctx.fill(); + ctx.strokeStyle = '#3d4753'; ctx.lineWidth = 2; ctx.stroke(); + }; + + for (const e of entries) { + sh.at(e.frame, (ctx, S) => { + plate(ctx, S); + const shape = e.building ? STRUCT_SHAPES[e.shape] : UNIT_SHAPES[e.shape]; + if (!shape) return; + ctx.save(); + if (e.building) { + ctx.translate(S * 0.12, S * 0.12); + ctx.scale(0.76, 0.76); + shape(ctx, S); + } else { + // Units are drawn facing right around the cell centre; turn them 45 degrees so a + // build icon reads as a vehicle rather than as a sideways smear. + ctx.translate(S / 2, S / 2); ctx.rotate(-Math.PI / 4); ctx.scale(0.95, 0.95); + ctx.translate(-S / 2, -S / 2); + shape(ctx, S); + } + ctx.restore(); + }); + } + + const glyph = (frame, draw) => { if (frame != null) sh.at(frame, (ctx, S) => { plate(ctx, S); draw(ctx, S); }); }; + const stroke = (ctx, color = '#cfe3ff', lw = 3) => { ctx.strokeStyle = color; ctx.lineWidth = lw; ctx.stroke(); }; + + glyph(cmd.move, (ctx, S) => { + ctx.beginPath(); ctx.moveTo(S * 0.28, S * 0.72); ctx.lineTo(S * 0.72, S * 0.28); stroke(ctx); + ctx.beginPath(); ctx.moveTo(S * 0.72, S * 0.28); ctx.lineTo(S * 0.52, S * 0.30); + ctx.moveTo(S * 0.72, S * 0.28); ctx.lineTo(S * 0.70, S * 0.48); stroke(ctx); + }); + glyph(cmd.attack, (ctx, S) => { + ctx.beginPath(); ctx.arc(S / 2, S / 2, S * 0.24, 0, Math.PI * 2); stroke(ctx, '#ff9a8a'); + ctx.beginPath(); + ctx.moveTo(S / 2, S * 0.16); ctx.lineTo(S / 2, S * 0.34); + ctx.moveTo(S / 2, S * 0.66); ctx.lineTo(S / 2, S * 0.84); + ctx.moveTo(S * 0.16, S / 2); ctx.lineTo(S * 0.34, S / 2); + ctx.moveTo(S * 0.66, S / 2); ctx.lineTo(S * 0.84, S / 2); + stroke(ctx, '#ff9a8a', 2.5); + }); + glyph(cmd.attackMove, (ctx, S) => { + ctx.beginPath(); ctx.arc(S / 2, S / 2, S * 0.20, 0, Math.PI * 2); stroke(ctx, '#ffc98a', 2.5); + ctx.beginPath(); ctx.moveTo(S * 0.20, S * 0.80); ctx.lineTo(S * 0.80, S * 0.20); stroke(ctx, '#ffc98a', 2.5); + }); + glyph(cmd.stop, (ctx, S) => { + roundRect(ctx, S * 0.30, S * 0.30, S * 0.40, S * 0.40, 3); + ctx.fillStyle = '#ff9a8a'; ctx.fill(); + }); + glyph(cmd.hold, (ctx, S) => { + ctx.beginPath(); + ctx.moveTo(S / 2, S * 0.20); ctx.lineTo(S * 0.76, S * 0.34); + ctx.lineTo(S * 0.76, S * 0.58); ctx.lineTo(S / 2, S * 0.80); + ctx.lineTo(S * 0.24, S * 0.58); ctx.lineTo(S * 0.24, S * 0.34); + ctx.closePath(); stroke(ctx, '#9ee8b0'); + }); + glyph(cmd.patrol, (ctx, S) => { + ctx.beginPath(); ctx.arc(S / 2, S / 2, S * 0.22, 0.6, Math.PI * 1.7); stroke(ctx); + ctx.beginPath(); ctx.moveTo(S * 0.70, S * 0.62); ctx.lineTo(S * 0.78, S * 0.72); + ctx.lineTo(S * 0.62, S * 0.76); stroke(ctx, '#cfe3ff', 2.5); + }); + glyph(cmd.guard, (ctx, S) => { + ctx.beginPath(); + ctx.moveTo(S / 2, S * 0.18); ctx.lineTo(S * 0.78, S * 0.32); + ctx.lineTo(S * 0.70, S * 0.72); ctx.lineTo(S / 2, S * 0.84); + ctx.lineTo(S * 0.30, S * 0.72); ctx.lineTo(S * 0.22, S * 0.32); + ctx.closePath(); ctx.fillStyle = 'rgba(158,232,176,0.25)'; ctx.fill(); stroke(ctx, '#9ee8b0', 2.5); + }); + glyph(cmd.assist, (ctx, S) => { + ctx.strokeStyle = '#8ad4ff'; ctx.lineWidth = 4; + ctx.beginPath(); + ctx.moveTo(S / 2, S * 0.24); ctx.lineTo(S / 2, S * 0.76); + ctx.moveTo(S * 0.24, S / 2); ctx.lineTo(S * 0.76, S / 2); + ctx.stroke(); + }); + + return sh.finish(); + }, +}; + +/** + * Resolve every declared sheet to a usable texture key, painting stand-ins as needed. + * @returns {{keys:Object, procedural:string[]}} sheetName -> texture key + */ +export function ensureSheets(scene, rules, art) { + const keys = Object.create(null); + const procedural = []; + for (const [name, spec] of Object.entries(art.sheets ?? {})) { + if (spec.path && scene.textures.exists(spec.key)) { keys[name] = spec.key; continue; } + const procKey = `${spec.key}-proc`; + if (!scene.textures.exists(procKey)) { + const painter = PROC_PAINTERS[spec.kind]; + if (!painter) { + console.warn(`[TAArt] sheet "${name}" has unknown kind "${spec.kind}" — skipping`); + continue; + } + painter(scene, procKey, spec, rules, art, name); + } + keys[name] = procKey; + procedural.push(name); + } + return { keys, procedural }; +} + +/** Frame dimensions of a resolved sheet, for sprite scaling. */ +export function sheetFrameSize(art, sheetName) { + const s = art.sheets?.[sheetName]; + return s ? { w: s.frameWidth, h: s.frameHeight } : { w: 64, h: 64 }; +} + +export { PROC_PAINTERS, UNIT_SHAPES, STRUCT_SHAPES, mkCanvasSheet, roundRect }; diff --git a/src/games/totalannihilation/TAFx.js b/src/games/totalannihilation/TAFx.js new file mode 100644 index 0000000..ea21c2f --- /dev/null +++ b/src/games/totalannihilation/TAFx.js @@ -0,0 +1,229 @@ +// Total Annihilation — procedural combat effects. +// +// Every weapon effect in this game is drawn, not sprited: gunfire tracers, tank shells, +// rocket trails, beams, explosions, nanolathe streams and craters are all Graphics strokes. +// Two layers are cleared and re-stroked each frame from a TTL list — the Star Control +// `fxList` idiom (StarControlGame.js renderWorld) using the repo's glow-stroke pair: one +// wide low-alpha pass, then a thin opaque one on top. + +const MAX_FX = 420; // hard cap; oldest transients are dropped rather than dropping frames + +export default class TAFx { + constructor(scene, worldRoot, depths) { + this.scene = scene; + // Under the actors: trails, nanolathe streams, scorch. Over them: tracers and blasts. + this.gUnder = scene.add.graphics().setDepth(depths.fxUnder); + this.gOver = scene.add.graphics().setDepth(depths.fxOver); + worldRoot.add(this.gUnder); + worldRoot.add(this.gOver); + this.list = []; + this.shakeUntil = 0; + } + + destroy() { + this.gUnder.destroy(); + this.gOver.destroy(); + this.list.length = 0; + } + + clear() { this.list.length = 0; } + + push(fx) { + if (this.list.length >= MAX_FX) this.list.shift(); + fx.age = 0; + this.list.push(fx); + } + + /** Translate a simulation event into a transient effect. */ + onEvent(ev, rules) { + switch (ev.t) { + case 'shot': + this.push({ + kind: ev.style === 'beam' || ev.style === 'dgun' ? 'beam' : 'tracer', + x1: ev.x1, y1: ev.y1, x2: ev.x2, y2: ev.y2, + color: colorInt(ev.color), width: ev.width ?? 2, ttl: ev.lifeMs ?? 80, + }); + break; + case 'weaponFired': + this.push({ + kind: 'muzzle', x: ev.x, y: ev.y, heading: ev.heading, + color: 0xffe6a0, ttl: 70, + }); + break; + case 'impact': + this.push({ + kind: 'blast', x: ev.x, y: ev.y, r: Math.max(10, ev.radius), + color: 0xffb45a, ttl: ev.radius > 30 ? 380 : 200, + }); + break; + case 'bigExplosion': + this.push({ kind: 'shockwave', x: ev.x, y: ev.y, r: ev.radius, color: 0xfff0c0, ttl: 700 }); + this.push({ kind: 'blast', x: ev.x, y: ev.y, r: ev.radius * 0.55, color: 0xff8a3c, ttl: 520 }); + this.shakeUntil = this.scene.time.now + 320; + this.scene.cameras.main.shake(300, 0.010); + break; + case 'unitDestroyed': + this.push({ + kind: 'blast', x: ev.x, y: ev.y, + r: Math.max(16, ev.radius * (ev.isBuilding ? 2.0 : 1.4)), + color: ev.isBuilding ? 0xffa040 : 0xff9060, + ttl: ev.isBuilding ? 460 : 300, + }); + this.push({ kind: 'scorch', x: ev.x, y: ev.y, r: ev.radius * 1.2, ttl: 4200 }); + break; + case 'nanolathe': + // The stream itself is drawn live from builder->site in draw(); this is the sparkle + // at the receiving end so a site under construction always reads as active. + this.push({ kind: 'spark', x: ev.x, y: ev.y, color: 0x8ad4ff, ttl: 220 }); + break; + default: break; + } + } + + /** + * @param {number} delta ms since last frame + * @param {Array} nanoLinks live [{x1,y1,x2,y2,color}] builder->target streams + * @param {Array} projectiles interpolated [{x,y,vx,vy,color,width,style,trail}] + */ + draw(delta, nanoLinks, projectiles, timeMs) { + const gu = this.gUnder, go = this.gOver; + gu.clear(); go.clear(); + + // ---- persistent-ish world marks (under actors) ---- + for (const fx of this.list) { + if (fx.kind !== 'scorch') continue; + const t = fx.age / fx.ttl; + gu.fillStyle(0x1a1410, 0.5 * (1 - t)); + gu.fillCircle(fx.x, fx.y, fx.r); + } + + // ---- nanolathe streams (under actors) ---- + for (const link of nanoLinks) { + const phase = (timeMs * 0.006) % 1; + gu.lineStyle(5, link.color, 0.14); + gu.lineBetween(link.x1, link.y1, link.x2, link.y2); + gu.lineStyle(2, link.color, 0.9); + // dashed, marching toward the target so it reads as material flowing + const dx = link.x2 - link.x1, dy = link.y2 - link.y1; + const len = Math.hypot(dx, dy) || 1; + const ux = dx / len, uy = dy / len; + const dash = 14, gap = 10; + for (let d = phase * (dash + gap); d < len; d += dash + gap) { + const a = Math.min(len, d), b = Math.min(len, d + dash); + gu.lineBetween(link.x1 + ux * a, link.y1 + uy * a, link.x1 + ux * b, link.y1 + uy * b); + } + } + + // ---- rocket trails (under actors) ---- + for (const p of projectiles) { + if (!p.trail || p.trail.length < 4) continue; + const n = p.trail.length / 2; + for (let i = 1; i < n; i++) { + const a = i / n; + go.lineStyle(p.width * 1.6 * a, p.color, 0.30 * a); + gu.lineStyle(p.width * 1.6 * a, p.color, 0.30 * a); + gu.lineBetween(p.trail[(i - 1) * 2], p.trail[(i - 1) * 2 + 1], p.trail[i * 2], p.trail[i * 2 + 1]); + } + } + + // ---- live projectiles (over actors) ---- + for (const p of projectiles) { + const ang = Math.atan2(p.vy, p.vx); + if (p.style === 'rocket') { + const bx = p.x - Math.cos(ang) * 9, by = p.y - Math.sin(ang) * 9; + go.lineStyle(p.width * 2.4, p.color, 0.22); + go.lineBetween(bx, by, p.x, p.y); + go.fillStyle(0xfff0c0, 1); + go.fillCircle(p.x, p.y, p.width * 0.9); + go.fillStyle(p.color, 0.85); + go.fillCircle(bx, by, p.width * 1.3); + } else { + const bx = p.x - Math.cos(ang) * 12, by = p.y - Math.sin(ang) * 12; + go.lineStyle(p.width * 1.9, p.color, 0.20); + go.lineBetween(bx, by, p.x, p.y); + go.lineStyle(p.width * 0.8, p.color, 1); + go.lineBetween(bx, by, p.x, p.y); + go.fillStyle(0xfff6d8, 1); + go.fillCircle(p.x, p.y, p.width * 0.75); + } + } + + // ---- transients (over actors) ---- + const keep = []; + for (const fx of this.list) { + fx.age += delta; + if (fx.age >= fx.ttl) continue; + keep.push(fx); + const t = fx.age / fx.ttl; + + switch (fx.kind) { + case 'tracer': { + const a = 1 - t; + go.lineStyle(fx.width * 3.2, fx.color, 0.16 * a); + go.lineBetween(fx.x1, fx.y1, fx.x2, fx.y2); + go.lineStyle(fx.width, fx.color, a); + go.lineBetween(fx.x1, fx.y1, fx.x2, fx.y2); + break; + } + case 'beam': { + const a = 1 - t * 0.75; + go.lineStyle(fx.width * 2.6 * (1 - t) + 1, fx.color, 0.18 * a); + go.lineBetween(fx.x1, fx.y1, fx.x2, fx.y2); + go.lineStyle(fx.width * (1 - t) + 1, fx.color, a); + go.lineBetween(fx.x1, fx.y1, fx.x2, fx.y2); + break; + } + case 'muzzle': { + const a = 1 - t; + const len = 13 * (1 - t) + 4; + go.fillStyle(fx.color, 0.85 * a); + for (const spread of [-0.35, 0, 0.35]) { + const ang = fx.heading + spread; + go.beginPath(); + go.moveTo(fx.x, fx.y); + go.lineTo(fx.x + Math.cos(ang - 0.13) * len, fx.y + Math.sin(ang - 0.13) * len); + go.lineTo(fx.x + Math.cos(ang + 0.13) * len, fx.y + Math.sin(ang + 0.13) * len); + go.closePath(); + go.fillPath(); + } + break; + } + case 'blast': { + const a = 1 - t; + const r = fx.r * (0.35 + t * 0.9); + go.fillStyle(fx.color, 0.55 * a); + go.fillCircle(fx.x, fx.y, r); + go.fillStyle(0xfff6d8, 0.7 * a * a); + go.fillCircle(fx.x, fx.y, r * 0.45); + go.lineStyle(3 * a + 1, 0xffffff, 0.7 * a); + go.strokeCircle(fx.x, fx.y, r); + break; + } + case 'shockwave': { + const a = 1 - t; + go.lineStyle(9 * a + 1, fx.color, a); + go.strokeCircle(fx.x, fx.y, fx.r * t); + go.lineStyle(4 * a + 1, 0xff8a3c, a * 0.7); + go.strokeCircle(fx.x, fx.y, fx.r * t * 0.72); + break; + } + case 'spark': { + const a = 1 - t; + go.fillStyle(fx.color, a); + go.fillCircle(fx.x, fx.y, 3 * a + 1); + break; + } + default: break; + } + } + this.list = keep; + } +} + +function colorInt(v) { + if (typeof v === 'number') return v; + if (typeof v === 'string' && v[0] === '#') return parseInt(v.slice(1), 16); + return 0xffffff; +} + +export { colorInt }; diff --git a/src/games/totalannihilation/TALogic.js b/src/games/totalannihilation/TALogic.js new file mode 100644 index 0000000..2d7a102 --- /dev/null +++ b/src/games/totalannihilation/TALogic.js @@ -0,0 +1,1451 @@ +// Total Annihilation — the simulation. +// +// Headless: no Phaser imports, runs in Node for tools/verifyTotalAnnihilation.js. +// +// Contract with the view: the scene NEVER mutates state. It calls issueOrder() for player +// commands and step()/tick() to advance time; tick() returns an event list the view replays +// as animation. The AI calls the exact same issueOrder(), which is what lets the verify +// script bot the human's side and assert that a campaign mission is actually winnable. +// +// Determinism: all randomness comes from rngNext(state) (mulberry32 seeded into state), so a +// seed plus an order log replays exactly. Never call Math.random() in this file, TANav.js, +// TAAI.js or TAMapGen.js. + +import { + createNav, stampFootprint, clearanceFor, findPath, smoothPath, formationSlots, + nearestUsableTile, tileIndex, worldToTileX, worldToTileY, tileCenterX, tileCenterY, + SpatialHash, segmentClear, +} from './TANav.js'; + +export const SAVE_VERSION = 1; + +// --------------------------------------------------------------------------- +// RNG — mulberry32, state carried in the match so saves resume deterministically +// --------------------------------------------------------------------------- + +export function rngNext(state) { + let t = (state.rngState = (state.rngState + 0x6d2b79f5) >>> 0); + t = Math.imul(t ^ (t >>> 15), t | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; +} + +export function rngRange(state, lo, hi) { return lo + rngNext(state) * (hi - lo); } +export function rngInt(state, n) { return Math.floor(rngNext(state) * n); } + +// --------------------------------------------------------------------------- +// Match creation +// --------------------------------------------------------------------------- + +/** + * @param {object} rules compiled rules + * @param {object} opts { seed, map:{w,h,terrain,starts,theme}, armies:[{armyId,commanderId,isHuman,aiSkill}] } + */ +export function createMatch(rules, opts) { + const { map } = opts; + const c = rules.constants; + const state = { + v: SAVE_VERSION, + rulesVersion: rules.version, + seed: opts.seed >>> 0, + rngState: (opts.seed >>> 0) || 1, + tick: 0, + accumulatorMs: 0, + alpha: 0, + w: map.w, h: map.h, + tileSize: c.tileSize, + worldW: map.w * c.tileSize, + worldH: map.h * c.tileSize, + theme: map.theme ?? rules.skirmish.defaults.theme, + terrain: map.terrain, + starts: (map.starts ?? []).map((s) => ({ army: s.army, x: s.x, y: s.y })), + armies: [], + entities: [], + nextId: 1, + projectiles: [], + nextProjectileId: 1, + pathQueue: [], + events: [], + over: null, + elapsedSec: 0, + }; + + state.nav = createNav(rules, map); + state.hash = new SpatialHash(128, state.worldW, state.worldH); + + const visW = Math.ceil(map.w / 2), visH = Math.ceil(map.h / 2); + state.visW = visW; state.visH = visH; + + for (let i = 0; i < opts.armies.length; i++) { + const spec = opts.armies[i]; + state.armies.push({ + idx: i, + armyId: spec.armyId, + commanderId: spec.commanderId ?? null, + isHuman: !!spec.isHuman, + aiSkill: spec.aiSkill ?? 3, + aiProfile: spec.aiProfile ?? null, + energy: spec.startEnergy ?? c.startEnergy, + mass: spec.startMass ?? c.startMass, + energyCap: c.baseEnergyCap, + massCap: c.baseMassCap, + eIncome: 0, mIncome: 0, eUpkeep: 0, eDrain: 0, mDrain: 0, + stallE: 1, stallM: 1, + alive: true, + explored: new Uint8Array(visW * visH), + visible: new Uint8Array(visW * visH), + builtEver: 0, lostEver: 0, killsEver: 0, + }); + } + + // Starting units. + for (const s of map.starts ?? []) { + if (s.army >= state.armies.length) continue; + for (const unitId of rules.skirmish.startUnits) { + spawnUnit(state, rules, s.army, unitId, + tileCenterX(state.nav, s.x), tileCenterY(state.nav, s.y), Math.PI / 2); + } + } + // Anything the map pre-places (campaign missions use this). + for (const u of map.units ?? []) { + spawnUnit(state, rules, u.army, u.type, u.x * c.tileSize, u.y * c.tileSize, u.heading ?? 0); + } + for (const b of map.buildings ?? []) { + const e = placeBuilding(state, rules, b.army, b.type, b.tx, b.ty); + if (e) { e.progress = 1; e.site = false; e.hp = rules.defById[b.type].hp; onBuildingComplete(state, rules, e); } + } + + recomputeEconomyCaps(state, rules); + computeVision(state, rules); + return state; +} + +// --------------------------------------------------------------------------- +// Entities +// --------------------------------------------------------------------------- + +function baseEntity(state, army, def) { + return { + id: state.nextId++, + army, + defId: def.id, + isBuilding: !!def.isBuilding, + x: 0, y: 0, px: 0, py: 0, + heading: 0, pheading: 0, + turretRot: 0, pturretRot: 0, + vx: 0, vy: 0, + hp: def.hp, maxHp: def.hp, + dead: false, + site: false, progress: 1, + orders: [], path: null, pathIdx: 0, + destX: 0, destY: 0, slotX: 0, slotY: 0, + wantPath: false, noPath: false, + stuckTicks: 0, blockedTicks: 0, + targetId: 0, reload: (def.weaponDefs ?? []).map(() => 0), + burstLeft: (def.weaponDefs ?? []).map(() => 0), + buildTargetId: 0, + queue: [], jobProgress: 0, rallyX: 0, rallyY: 0, hasRally: false, + _power: 0, + produceE: 0, produceM: 0, upkeepE: 0, storeE: 0, storeM: 0, + }; +} + +export function spawnUnit(state, rules, army, defId, x, y, heading = 0) { + const def = rules.unitById[defId]; + if (!def) return null; + const e = baseEntity(state, army, def); + e.x = e.px = x; + e.y = e.py = y; + e.heading = e.pheading = heading; + e.turretRot = e.pturretRot = heading; + e.radius = def.radius; + e.destX = x; e.destY = y; + e.rallyX = x; e.rallyY = y; + if (def.produce) { e.produceE = def.produce.energy ?? 0; e.produceM = def.produce.mass ?? 0; } + if (def.storage) { e.storeE = def.storage.energy ?? 0; e.storeM = def.storage.mass ?? 0; } + state.entities.push(e); + state.armies[army].builtEver++; + state.events.push({ t: 'spawn', id: e.id, army, defId, x, y }); + return e; +} + +/** Create a build SITE. It blocks the nav grid immediately so pathing routes around it. */ +export function placeBuilding(state, rules, army, defId, tx, ty) { + const def = rules.buildingById[defId]; + if (!def) return null; + const ts = state.tileSize; + const e = baseEntity(state, army, def); + e.tx = tx; e.ty = ty; + e.fw = def.footprint.w; e.fh = def.footprint.h; + e.x = e.px = (tx + e.fw / 2) * ts; + e.y = e.py = (ty + e.fh / 2) * ts; + e.radius = def.radius; + e.site = true; + e.progress = 0; + e.hp = Math.max(1, def.hp * 0.05); + state.entities.push(e); + stampFootprint(state.nav, rules, tx, ty, e.fw, e.fh, true); + state.events.push({ t: 'siteCreated', id: e.id, army, defId, x: e.x, y: e.y }); + return e; +} + +function onBuildingComplete(state, rules, e) { + const def = rules.buildingById[e.defId]; + e.site = false; + e.progress = 1; + e.hp = def.hp; + if (def.produce) { + e.produceE = def.produce.energy ?? 0; + let m = def.produce.mass ?? 0; + // Terrain bonus (a Mass Generator on a metal patch): resolved once, at completion. + if (m > 0 && def.terrainMultiplier) { + let best = 1; + for (let y = e.ty; y < e.ty + e.fh; y++) { + for (let x = e.tx; x < e.tx + e.fw; x++) { + if (x < 0 || y < 0 || x >= state.w || y >= state.h) continue; + const t = rules.terrain[state.terrain[y * state.w + x]]; + const mul = t?.[def.terrainMultiplier]; + if (mul > best) best = mul; + } + } + m *= best; + e.terrainBonus = best; + } + e.produceM = m; + } + if (def.upkeep) e.upkeepE = def.upkeep.energy ?? 0; + if (def.storage) { e.storeE = def.storage.energy ?? 0; e.storeM = def.storage.mass ?? 0; } + recomputeEconomyCaps(state, rules); + state.events.push({ t: 'buildingComplete', id: e.id, army: e.army, defId: e.defId, x: e.x, y: e.y }); +} + +export function entityById(state, id) { + if (!id) return null; + for (let i = 0; i < state.entities.length; i++) { + const e = state.entities[i]; + if (e.id === id) return e.dead ? null : e; + } + return null; +} + +function defOf(rules, e) { return rules.defById[e.defId]; } + +// --------------------------------------------------------------------------- +// Order API — the single mutation entry point shared by the human and the AI +// --------------------------------------------------------------------------- + +const ORDER_TYPES = new Set([ + 'move', 'attackMove', 'attack', 'stop', 'hold', 'patrol', 'guard', 'build', 'assist', +]); + +/** + * @param {object} cmd { army, unitIds:[], order:{type,...}, queue:boolean } + * @returns {{ok:boolean, error?:string}} + */ +export function issueOrder(state, rules, cmd) { + if (state.over) return { ok: false, error: 'match is over' }; + const { army, order } = cmd; + if (!order) return { ok: false, error: 'no order' }; + + // Factory / rally commands act on a building, not on a unit's order queue. + if (order.type === 'factoryEnqueue' || order.type === 'factoryCancel' || order.type === 'setRally') { + return factoryCommand(state, rules, army, order); + } + if (!ORDER_TYPES.has(order.type)) return { ok: false, error: `unknown order "${order.type}"` }; + + const units = []; + for (const id of cmd.unitIds ?? []) { + const e = entityById(state, id); + if (!e || e.army !== army || e.isBuilding || e.site) continue; + units.push(e); + } + if (!units.length) return { ok: false, error: 'no controllable units' }; + + if (order.type === 'build') return buildCommand(state, rules, army, units, order, cmd.queue); + + if (order.type === 'attack') { + const target = entityById(state, order.targetId); + if (!target) return { ok: false, error: 'target does not exist' }; + if (target.army === army && !rules.constants.friendlyFire) { + return { ok: false, error: 'cannot attack an ally' }; + } + } + if (order.type === 'assist') { + const target = entityById(state, order.targetId); + if (!target || target.army !== army) return { ok: false, error: 'can only assist your own' }; + if (!canBuild(rules, units[0]) ) return { ok: false, error: 'unit cannot assist' }; + } + if (order.type === 'guard') { + const target = entityById(state, order.targetId); + if (!target || target.army !== army) return { ok: false, error: 'can only guard your own' }; + } + + const moveLike = order.type === 'move' || order.type === 'attackMove' || order.type === 'patrol'; + if (moveLike) { + if (!Number.isFinite(order.x) || !Number.isFinite(order.y)) return { ok: false, error: 'bad destination' }; + // One shared destination, fanned into formation slots — a 40-unit move order costs one A*. + const slots = formationSlots(order.x, order.y, units); + units.forEach((u, i) => { + const o = { ...order, sx: slots[i * 2], sy: slots[i * 2 + 1] }; + if (order.type === 'patrol') { o.fromX = u.x; o.fromY = u.y; o.leg = 0; } + pushOrder(u, o, cmd.queue); + }); + return { ok: true }; + } + + for (const u of units) pushOrder(u, { ...order }, cmd.queue); + return { ok: true }; +} + +function pushOrder(e, order, queue) { + if (!queue) { + e.orders.length = 0; + e.path = null; + e.wantPath = false; + e.noPath = false; + } + e.orders.push(order); +} + +function canBuild(rules, e) { + const def = defOf(rules, e); + return !!(def.builds ?? []).length && def.buildPower > 0; +} + +function buildCommand(state, rules, army, units, order, queue) { + const builder = units.find((u) => canBuild(rules, u)); + if (!builder) return { ok: false, error: 'no builder selected' }; + const def = rules.buildingById[order.defId]; + if (!def) return { ok: false, error: `unknown building "${order.defId}"` }; + const bdef = defOf(rules, builder); + if (!(bdef.builds ?? []).includes(order.defId)) { + return { ok: false, error: `${bdef.name} cannot build ${def.name}` }; + } + const legal = canPlaceAt(state, rules, order.tx, order.ty, def); + if (!legal.ok) return legal; + + const site = placeBuilding(state, rules, army, order.defId, order.tx, order.ty); + if (!site) return { ok: false, error: 'placement failed' }; + pushOrder(builder, { type: 'build', targetId: site.id }, queue); + return { ok: true, siteId: site.id }; +} + +/** Can this footprint go here? Checks bounds, terrain buildability and occupancy. */ +export function canPlaceAt(state, rules, tx, ty, def) { + const fw = def.footprint.w, fh = def.footprint.h; + if (tx < 0 || ty < 0 || tx + fw > state.w || ty + fh > state.h) { + return { ok: false, error: 'off the map' }; + } + for (let y = ty; y < ty + fh; y++) { + for (let x = tx; x < tx + fw; x++) { + const i = y * state.w + x; + if (state.nav.blocked[i]) return { ok: false, error: 'occupied' }; + const t = rules.terrain[state.terrain[i]]; + if (!t.buildable) return { ok: false, error: `cannot build on ${t.id}` }; + } + } + return { ok: true }; +} + +function factoryCommand(state, rules, army, order) { + const f = entityById(state, order.factoryId); + if (!f || f.army !== army || !f.isBuilding || f.site) return { ok: false, error: 'no such factory' }; + const def = defOf(rules, f); + if (order.type === 'setRally') { + f.rallyX = order.x; f.rallyY = order.y; f.hasRally = true; + return { ok: true }; + } + if (order.type === 'factoryCancel') { + const idx = order.index ?? 0; + if (idx < 0 || idx >= f.queue.length) return { ok: false, error: 'bad queue index' }; + const item = f.queue[idx]; + const take = Math.min(item.count, order.count ?? 1); + item.count -= take; + if (idx === 0 && f.jobProgress > 0 && item.count === 0) { + // Refund what the cancelled head job actually consumed. + const udef = rules.unitById[item.defId]; + const a = state.armies[army]; + a.energy = Math.min(a.energyCap, a.energy + udef.cost.energy * f.jobProgress); + a.mass = Math.min(a.massCap, a.mass + udef.cost.mass * f.jobProgress); + f.jobProgress = 0; + } + if (item.count <= 0) f.queue.splice(idx, 1); + state.events.push({ t: 'queueChanged', id: f.id }); + return { ok: true }; + } + // factoryEnqueue + if (!(def.builds ?? []).includes(order.defId)) { + return { ok: false, error: `${def.name} cannot build ${order.defId}` }; + } + const count = Math.max(1, order.count ?? 1); + const head = f.queue[f.queue.length - 1]; + if (head && head.defId === order.defId) head.count += count; + else f.queue.push({ defId: order.defId, count }); + state.events.push({ t: 'queueChanged', id: f.id }); + return { ok: true }; +} + +// --------------------------------------------------------------------------- +// Economy +// --------------------------------------------------------------------------- + +function recomputeEconomyCaps(state, rules) { + const c = rules.constants; + for (const a of state.armies) { a.energyCap = c.baseEnergyCap; a.massCap = c.baseMassCap; } + for (const e of state.entities) { + if (e.dead || e.site) continue; + const a = state.armies[e.army]; + if (!a) continue; + a.energyCap += e.storeE; + a.massCap += e.storeM; + } +} + +/** + * The flow economy. Income and drain are RATES; construction consumes continuously and + * slows proportionally when income can't keep up — that stall factor is the whole reason + * Energy and Mass Generators are separate buildings. + */ +function stepEconomy(state, rules) { + const dt = rules.dt; + const nominal = rules.constants.buildPowerNominal; + + for (const a of state.armies) { + a.eIncome = 0; a.mIncome = 0; a.eUpkeep = 0; a.eDrain = 0; a.mDrain = 0; + a._mFree = 0; a._mGated = 0; a.upkeepFactor = 1; + } + for (const e of state.entities) e._power = 0; + + // 1. Income and upkeep from completed entities. Mass production is split into what runs + // for free and what is gated behind an energy upkeep (the Mass Generator), because + // those two behave completely differently in a brownout — see step 4. + for (const e of state.entities) { + if (e.dead || e.site) continue; + const a = state.armies[e.army]; + if (!a) continue; + a.eIncome += e.produceE; + a.eUpkeep += e.upkeepE; + if (e.upkeepE > 0) a._mGated += e.produceM; + else a._mFree += e.produceM; + } + + // 2. Gather build power onto each job. + for (const e of state.entities) { + if (e.dead || e.site) continue; + const def = defOf(rules, e); + if (!def.buildPower) continue; + if (e.isBuilding) { + if (e.queue.length) e._power += def.buildPower; // factory works its own head item + } else if (e.buildTargetId) { + const target = entityById(state, e.buildTargetId); + if (target && target.army === e.army) { + const d = Math.hypot(target.x - e.x, target.y - e.y); + if (d <= def.buildRange + target.radius) target._power += def.buildPower; + } + } + } + + // 3. Demand. + const jobs = []; + for (const e of state.entities) { + if (e.dead || e._power <= 0) continue; + let jobDef = null; + if (e.site) jobDef = defOf(rules, e); + else if (e.isBuilding && e.queue.length) jobDef = rules.unitById[e.queue[0].defId]; + if (!jobDef || !(jobDef.buildTime > 0)) continue; + const rate = (e._power / nominal) / jobDef.buildTime; // progress fraction per second + const a = state.armies[e.army]; + if (!a) continue; + const dE = (jobDef.cost.energy ?? 0) * rate; + const dM = (jobDef.cost.mass ?? 0) * rate; + a.eDrain += dE; + a.mDrain += dM; + jobs.push({ e, rate, army: e.army }); + } + + // 4. Stall factors, then apply. + // + // Claim order on energy is CONSTRUCTION FIRST, upkeep second. That ordering matters: + // if upkeep were paid first, an army that over-built Mass Generators (upkeep > income) + // would pin its energy at zero, drop build efficiency to zero, and be permanently + // unable to finish the very Energy Generators that would rescue it. Throttling the + // metal makers instead is both self-correcting and what a TA player does by hand. + for (const a of state.armies) { + const eAvail = Math.max(0, a.energy + a.eIncome * dt); + const eNeed = a.eDrain * dt; + a.stallE = eNeed > eAvail ? (eNeed > 0 ? eAvail / eNeed : 1) : 1; + + const eLeft = Math.max(0, eAvail - eNeed * a.stallE); + const upkeepNeed = a.eUpkeep * dt; + a.upkeepFactor = upkeepNeed > 0 ? Math.min(1, eLeft / upkeepNeed) : 1; + // Gated producers only deliver in proportion to the upkeep they were actually paid. + a.mIncome = a._mFree + a._mGated * a.upkeepFactor; + + const mAvail = Math.max(0, a.mass + a.mIncome * dt); + const mNeed = a.mDrain * dt; + a.stallM = mNeed > mAvail ? (mNeed > 0 ? mAvail / mNeed : 1) : 1; + a.buildEff = Math.min(a.stallE, a.stallM); + if (a.buildEff < 0.999 && !a._stalling) { + a._stalling = true; + state.events.push({ t: 'stallStart', army: a.idx, energy: a.stallE < 0.999, mass: a.stallM < 0.999 }); + } else if (a.buildEff >= 0.999 && a._stalling) { + a._stalling = false; + state.events.push({ t: 'stallEnd', army: a.idx }); + } + } + + for (const j of jobs) { + const a = state.armies[j.army]; + j.e.__adv = j.rate * a.buildEff * dt; + } + + for (const a of state.armies) { + const f = a.buildEff; + const eNext = a.energy + (a.eIncome - a.eUpkeep * a.upkeepFactor - a.eDrain * f) * dt; + const mNext = a.mass + (a.mIncome - a.mDrain * f) * dt; + if (eNext > a.energyCap || mNext > a.massCap) { + state.events.push({ t: 'wasting', army: a.idx, energy: eNext > a.energyCap, mass: mNext > a.massCap }); + } + a.energy = Math.max(0, Math.min(a.energyCap, eNext)); + a.mass = Math.max(0, Math.min(a.massCap, mNext)); + } + + // 5. Advance construction. + for (const j of jobs) { + const e = j.e; + const adv = e.__adv ?? 0; + e.__adv = 0; + if (adv <= 0) continue; + if (e.site) { + const def = defOf(rules, e); + e.progress = Math.min(1, e.progress + adv); + e.hp = Math.max(1, def.hp * Math.max(0.05, e.progress)); + state.events.push({ t: 'nanolathe', id: e.id, x: e.x, y: e.y, army: e.army }); + if (e.progress >= 1) { + onBuildingComplete(state, rules, e); + // Release any builder that was working on it. + for (const b of state.entities) { + if (b.buildTargetId === e.id) { + b.buildTargetId = 0; + if (b.orders[0]?.type === 'build' && b.orders[0].targetId === e.id) b.orders.shift(); + } + } + } + } else { + e.jobProgress = Math.min(1, e.jobProgress + adv); + state.events.push({ t: 'nanolathe', id: e.id, x: e.x, y: e.y, army: e.army }); + if (e.jobProgress >= 1) finishFactoryJob(state, rules, e); + } + } +} + +function finishFactoryJob(state, rules, f) { + const item = f.queue[0]; + if (!item) { f.jobProgress = 0; return; } + const def = defOf(rules, f); + const off = def.spawnOffset ?? { x: 0, y: 1 }; + const ts = state.tileSize; + let sx = f.x + off.x * ts; + let sy = f.y + off.y * ts; + // Nudge out of the footprint if the bay mouth is somehow blocked. + const nav = state.nav; + const udef = rules.unitById[item.defId]; + const need = clearanceFor(udef.radius, ts); + const mc = udef.moveClass; + let tx = worldToTileX(nav, sx), ty = worldToTileY(nav, sy); + const idx = nearestUsableTile(nav, mc, need, tx, ty, 6); + if (idx >= 0) { sx = tileCenterX(nav, idx % nav.w); sy = tileCenterY(nav, (idx / nav.w) | 0); } + + const u = spawnUnit(state, rules, f.army, item.defId, sx, sy, Math.atan2(off.y, off.x)); + f.jobProgress = 0; + item.count--; + if (item.count <= 0) f.queue.shift(); + state.events.push({ t: 'queueChanged', id: f.id }); + if (u && f.hasRally) { + issueOrder(state, rules, { + army: f.army, unitIds: [u.id], order: { type: 'move', x: f.rallyX, y: f.rallyY }, queue: false, + }); + } +} + +// --------------------------------------------------------------------------- +// Orders and movement +// --------------------------------------------------------------------------- + +function requestPath(state, e, x, y) { + e.destX = x; e.destY = y; + e.wantPath = true; + e.noPath = false; + if (!state.pathQueue.includes(e.id)) state.pathQueue.push(e.id); +} + +function servicePathQueue(state, rules) { + let budget = rules.constants.pathBudgetPerTick; + while (budget-- > 0 && state.pathQueue.length) { + const id = state.pathQueue.shift(); + const e = entityById(state, id); + if (!e || !e.wantPath) continue; + const def = defOf(rules, e); + const nav = state.nav; + const need = clearanceFor(e.radius, state.tileSize); + const mc = def.moveClass; + + const sIdx = nearestUsableTile(nav, mc, need, worldToTileX(nav, e.x), worldToTileY(nav, e.y), 6); + const gIdx = nearestUsableTile(nav, mc, need, worldToTileX(nav, e.destX), worldToTileY(nav, e.destY), 24); + e.wantPath = false; + if (sIdx < 0 || gIdx < 0) { e.noPath = true; e.path = null; continue; } + + const tiles = findPath(nav, mc, need, sIdx, gIdx); + if (!tiles) { e.noPath = true; e.path = null; state.events.push({ t: 'noPath', id: e.id }); continue; } + e.path = smoothPath(nav, mc, need, tiles, e.destX, e.destY); + e.pathIdx = 0; + } +} + +function orderDestination(e, order) { + if (order.type === 'patrol') { + return order.leg === 0 ? { x: order.sx ?? order.x, y: order.sy ?? order.y } + : { x: order.fromX, y: order.fromY }; + } + return { x: order.sx ?? order.x, y: order.sy ?? order.y }; +} + +function stepOrders(state, rules) { + const ts = state.tileSize; + for (const e of state.entities) { + if (e.dead || e.site) continue; + if (e.isBuilding) continue; + const def = defOf(rules, e); + const order = e.orders[0]; + + if (!order) { + e.movingTo = null; + e.buildTargetId = 0; + continue; + } + + switch (order.type) { + case 'stop': + e.orders.length = 0; + e.path = null; + e.targetId = 0; + e.buildTargetId = 0; + break; + + case 'hold': + e.path = null; + e.movingTo = null; + break; + + case 'move': + case 'attackMove': + case 'patrol': { + const dest = orderDestination(e, order); + if (!e.path && !e.wantPath && !e.noPath) requestPath(state, e, dest.x, dest.y); + e.movingTo = dest; + const d = Math.hypot(dest.x - e.x, dest.y - e.y); + const slack = Math.max(rules.constants.arriveSlackPx, e.radius * 1.2); + if (d <= slack || (e.noPath && e.stuckTicks > rules.constants.stuckGiveUpSec * rules.constants.tickHz)) { + if (order.type === 'patrol') { + order.leg = order.leg === 0 ? 1 : 0; + e.path = null; e.noPath = false; e.stuckTicks = 0; + requestPath(state, e, ...(order.leg === 0 + ? [order.sx ?? order.x, order.sy ?? order.y] : [order.fromX, order.fromY])); + } else { + e.orders.shift(); + e.path = null; e.movingTo = null; e.stuckTicks = 0; e.noPath = false; + } + } + break; + } + + case 'attack': { + const target = entityById(state, order.targetId); + if (!target) { e.orders.shift(); e.targetId = 0; e.path = null; break; } + e.targetId = target.id; + const d = Math.hypot(target.x - e.x, target.y - e.y) - target.radius; + const wantRange = def.maxRange * 0.85; + if (d > wantRange) { + if (!e.path && !e.wantPath) requestPath(state, e, target.x, target.y); + e.movingTo = { x: target.x, y: target.y }; + // Re-path when the target has drifted well away from where we aimed. + if (e.path && Math.hypot(target.x - e.destX, target.y - e.destY) > ts * 3) { + e.path = null; requestPath(state, e, target.x, target.y); + } + } else { + e.path = null; + e.movingTo = null; + } + break; + } + + case 'guard': { + const target = entityById(state, order.targetId); + if (!target) { e.orders.shift(); break; } + const d = Math.hypot(target.x - e.x, target.y - e.y); + if (d > ts * 3) { + if (!e.path && !e.wantPath) requestPath(state, e, target.x, target.y); + e.movingTo = { x: target.x, y: target.y }; + if (e.path && Math.hypot(target.x - e.destX, target.y - e.destY) > ts * 2) { + e.path = null; requestPath(state, e, target.x, target.y); + } + } else { + e.path = null; e.movingTo = null; + } + break; + } + + case 'build': + case 'assist': { + const target = entityById(state, order.targetId); + if (!target) { e.orders.shift(); e.buildTargetId = 0; e.path = null; break; } + if (order.type === 'assist' && !target.site && !target.queue?.length) { + e.orders.shift(); e.buildTargetId = 0; break; + } + const reach = (def.buildRange ?? 0) + target.radius; + const d = Math.hypot(target.x - e.x, target.y - e.y); + if (d > reach * 0.9) { + if (!e.path && !e.wantPath && !e.noPath) requestPath(state, e, target.x, target.y); + e.movingTo = { x: target.x, y: target.y }; + e.buildTargetId = 0; + } else { + e.path = null; + e.movingTo = null; + e.buildTargetId = target.id; + } + break; + } + default: + e.orders.shift(); + } + } +} + +function stepMovement(state, rules) { + const dt = rules.dt; + const nav = state.nav; + + for (const e of state.entities) { + e.px = e.x; e.py = e.y; e.pheading = e.heading; e.pturretRot = e.turretRot; + if (e.dead || e.isBuilding || e.site) { e.vx = 0; e.vy = 0; continue; } + + const def = defOf(rules, e); + let tx = null, ty = null; + + if (e.path && e.path.length >= 2) { + // Consume waypoints we've reached. + while (e.pathIdx * 2 + 1 < e.path.length) { + const wx = e.path[e.pathIdx * 2], wy = e.path[e.pathIdx * 2 + 1]; + const last = (e.pathIdx + 1) * 2 >= e.path.length; + const tol = last ? Math.max(6, e.radius * 0.6) : Math.max(10, e.radius); + if (Math.hypot(wx - e.x, wy - e.y) <= tol) e.pathIdx++; + else break; + } + if (e.pathIdx * 2 + 1 < e.path.length) { + tx = e.path[e.pathIdx * 2]; ty = e.path[e.pathIdx * 2 + 1]; + } else { + e.path = null; + } + } else if (e.movingTo && e.noPath) { + tx = e.movingTo.x; ty = e.movingTo.y; + } + + if (tx == null) { e.vx = 0; e.vy = 0; e.stuckTicks = 0; continue; } + + const dx = tx - e.x, dy = ty - e.y; + const dist = Math.hypot(dx, dy); + if (dist < 0.001) { e.vx = 0; e.vy = 0; continue; } + + const want = Math.atan2(dy, dx); + e.heading = turnToward(e.heading, want, def.turnRate * dt); + + // Terrain slows movement: speed scales by the inverse of the tile's move cost. + const ti = worldToTileY(nav, e.y) * state.w + worldToTileX(nav, e.x); + const tcost = rules.moveClasses[def.moveClass].costByTerrainIndex[state.terrain[ti]] ?? 1; + const terrainMul = tcost ? 1 / tcost : 1; + + // Slow down while still swinging round, and ease into the final waypoint. + const misalign = Math.abs(angleDelta(e.heading, want)); + const turnMul = misalign > 1.2 ? 0.25 : (misalign > 0.5 ? 0.65 : 1); + const arriveMul = dist < e.radius * 2 ? Math.max(0.25, dist / (e.radius * 2)) : 1; + + const speed = def.speed * terrainMul * turnMul * arriveMul; + const step = Math.min(speed * dt, dist); + e.vx = Math.cos(e.heading) * step / dt; + e.vy = Math.sin(e.heading) * step / dt; + e.x += Math.cos(e.heading) * step; + e.y += Math.sin(e.heading) * step; + + if (step < speed * dt * 0.2) e.stuckTicks++; else e.stuckTicks = 0; + } +} + +function turnToward(cur, want, maxStep) { + const d = angleDelta(cur, want); + if (Math.abs(d) <= maxStep) return want; + return normalizeAngle(cur + Math.sign(d) * maxStep); +} + +export function angleDelta(from, to) { + let d = (to - from) % (Math.PI * 2); + if (d > Math.PI) d -= Math.PI * 2; + if (d < -Math.PI) d += Math.PI * 2; + return d; +} + +function normalizeAngle(a) { + a %= Math.PI * 2; + if (a > Math.PI) a -= Math.PI * 2; + if (a < -Math.PI) a += Math.PI * 2; + return a; +} + +/** + * Positional separation. This is what makes the size classes real: three r=16 units settle + * inside one 64px tile, an r=26 tank takes the tile alone, and heavier units shove lighter + * ones aside rather than the other way round. + */ +function stepSeparation(state, rules) { + const stiff = rules.constants.separationStiffness; + const shoveTicks = rules.constants.shoveAfterSec * rules.constants.tickHz; + const hash = state.hash; + hash.clear(); + const movers = []; + for (const e of state.entities) { + if (e.dead || e.isBuilding || e.site) continue; + hash.insert(e, e.x, e.y); + movers.push(e); + } + + const near = []; + for (const a of movers) { + near.length = 0; + hash.query(a.x, a.y, a.radius * 2 + 32, near); + for (const b of near) { + if (b.id <= a.id) continue; // handle each pair once, deterministically + const dx = b.x - a.x, dy = b.y - a.y; + const minD = a.radius + b.radius; + let d2 = dx * dx + dy * dy; + if (d2 >= minD * minD) continue; + let d = Math.sqrt(d2); + let nx, ny; + if (d < 0.0001) { + // Exactly coincident: push apart along a deterministic axis derived from ids. + const ang = ((a.id * 2654435761 + b.id) % 628) / 100; + nx = Math.cos(ang); ny = Math.sin(ang); d = 0.0001; + } else { + nx = dx / d; ny = dy / d; + } + const overlap = (minD - d) * stiff; + const da = defOf(rules, a), db = defOf(rules, b); + const ma = da.massClass ?? 1, mb = db.massClass ?? 1; + const total = ma + mb; + const aShare = mb / total, bShare = ma / total; + a.x -= nx * overlap * aShare; a.y -= ny * overlap * aShare; + b.x += nx * overlap * bShare; b.y += ny * overlap * bShare; + + // Traffic-jam breaker: a unit trying to move that's stuck behind a parked friendly + // tells the blocker to step aside. Without this, groups deadlock in corridors. + if (a.army === b.army) { + const aMoving = !!(a.path || a.movingTo), bMoving = !!(b.path || b.movingTo); + if (aMoving && !bMoving) { b.blockedTicks++; if (b.blockedTicks > shoveTicks) shove(state, rules, b, nx, ny); } + else if (bMoving && !aMoving) { a.blockedTicks++; if (a.blockedTicks > shoveTicks) shove(state, rules, a, -nx, -ny); } + } + } + } + + // Push everything out of blocked tiles and back inside the map. + const nav = state.nav; + const ts = state.tileSize; + for (const e of movers) { + const def = defOf(rules, e); + const need = clearanceFor(e.radius, ts); + const cl = nav.clearance[def.moveClass]; + const tx = worldToTileX(nav, e.x), ty = worldToTileY(nav, e.y); + if (cl[ty * nav.w + tx] < need) { + const idx = nearestUsableTile(nav, def.moveClass, need, tx, ty, 8); + if (idx >= 0) { + const cx = tileCenterX(nav, idx % nav.w), cy = tileCenterY(nav, (idx / nav.w) | 0); + const d = Math.hypot(cx - e.x, cy - e.y) || 1; + e.x += ((cx - e.x) / d) * Math.min(ts * 0.5, d); + e.y += ((cy - e.y) / d) * Math.min(ts * 0.5, d); + } + } + e.x = Math.max(e.radius, Math.min(state.worldW - e.radius, e.x)); + e.y = Math.max(e.radius, Math.min(state.worldH - e.radius, e.y)); + } +} + +function shove(state, rules, e, nx, ny) { + e.blockedTicks = 0; + const ts = state.tileSize; + // Step perpendicular to the pusher, whichever side is open. + const target = { x: e.x - ny * ts * 1.5, y: e.y + nx * ts * 1.5 }; + issueOrder(state, rules, { + army: e.army, unitIds: [e.id], order: { type: 'move', x: target.x, y: target.y }, queue: false, + }); +} + +// --------------------------------------------------------------------------- +// Combat +// --------------------------------------------------------------------------- + +function stepCombat(state, rules) { + const c = rules.constants; + const dt = rules.dt; + const hash = state.hash; + const near = []; + + for (const e of state.entities) { + if (e.dead || e.site) continue; + const def = defOf(rules, e); + if (!def.weaponDefs?.length) continue; + + for (let wi = 0; wi < e.reload.length; wi++) if (e.reload[wi] > 0) e.reload[wi]--; + + // Staggered acquisition — the single biggest CPU lever in the sim. Re-scan only every + // retargetPeriodTicks, phase-shifted per unit so the cost spreads across ticks. + const holdOrder = e.orders[0]?.type === 'attack'; + let target = entityById(state, e.targetId); + if (target && !inWeaponRange(e, def, target)) target = null; + const due = ((state.tick + e.id) % c.retargetPeriodTicks) === 0; + if (!holdOrder && (!target || due)) { + target = acquireTarget(state, rules, e, def, hash, near); + e.targetId = target ? target.id : 0; + } else if (holdOrder) { + target = entityById(state, e.orders[0].targetId) ?? target; + e.targetId = target ? target.id : 0; + } + if (!target) continue; + + const dx = target.x - e.x, dy = target.y - e.y; + const aim = Math.atan2(dy, dx); + const hasTurret = def.turretFrame != null; + if (hasTurret) e.turretRot = turnToward(e.turretRot, aim, def.turnRate * 2.5 * dt); + else e.turretRot = e.heading; + + const facing = hasTurret ? e.turretRot : e.heading; + // A unit that's driving somewhere still shoots; it just has to be roughly on target. + const aligned = Math.abs(angleDelta(facing, aim)) < (hasTurret ? 0.12 : 0.35); + + for (let wi = 0; wi < def.weaponDefs.length; wi++) { + const w = def.weaponDefs[wi]; + if (e.reload[wi] > 0) continue; + const dist = Math.hypot(dx, dy) - target.radius; + if (dist > w.range || dist < (w.minRange ?? 0)) continue; + if (!aligned) continue; + if (w.kind === 'beam' && w.energyPerShot) { + const a = state.armies[e.army]; + if (a.energy < w.energyPerShot) continue; + a.energy -= w.energyPerShot; + } + fireWeapon(state, rules, e, w, target, facing); + e.burstLeft[wi] = (w.burst ?? 1) - 1; + e.reload[wi] = e.burstLeft[wi] > 0 ? w.burstDelayTicks : w.reloadTicks; + if (e.burstLeft[wi] <= 0) e.burstLeft[wi] = 0; + else e._burstWeapon = wi; + } + + // Continue an in-flight burst. + for (let wi = 0; wi < def.weaponDefs.length; wi++) { + const w = def.weaponDefs[wi]; + if (e.burstLeft[wi] > 0 && e.reload[wi] === 0) { + fireWeapon(state, rules, e, w, target, facing); + e.burstLeft[wi]--; + e.reload[wi] = e.burstLeft[wi] > 0 ? w.burstDelayTicks : w.reloadTicks; + } + } + } +} + +function inWeaponRange(e, def, target) { + if (target.dead) return false; + const d = Math.hypot(target.x - e.x, target.y - e.y) - target.radius; + return d <= def.maxRange; +} + +function acquireTarget(state, rules, e, def, hash, near) { + near.length = 0; + hash.query(e.x, e.y, def.maxRange + 64, near); + // Buildings aren't in the movers hash, so scan them directly — there are few of them. + let best = null, bestScore = -Infinity; + const consider = (t) => { + if (t.dead || t.army === e.army) return; + if (!state.armies[t.army]) return; + const dx = t.x - e.x, dy = t.y - e.y; + const d = Math.hypot(dx, dy) - t.radius; + if (d > def.maxRange) return; + let score = -Infinity; + for (const w of def.weaponDefs) { + if (d > w.range || d < (w.minRange ?? 0)) continue; + const mul = w.armorMul?.[rules.defById[t.defId].armorClass] ?? 1; + const s = (w.damage * mul) / (1 + d / w.range); + if (s > score) score = s; + } + if (score === -Infinity) return; + // Prefer things that can shoot back, and break ties by id so it's deterministic. + const tdef = rules.defById[t.defId]; + if (tdef.weaponDefs?.length) score *= 1.35; + if (t.site) score *= 0.6; + if (score > bestScore || (score === bestScore && best && t.id < best.id)) { + best = t; bestScore = score; + } + }; + for (const t of near) consider(t); + for (const t of state.entities) if (t.isBuilding && !t.dead) consider(t); + return best; +} + +function fireWeapon(state, rules, e, w, target, facing) { + const muzzle = (w.fx?.muzzle ?? e.radius); + const ox = e.x + Math.cos(facing) * muzzle; + const oy = e.y + Math.sin(facing) * muzzle; + + state.events.push({ + t: 'weaponFired', id: e.id, army: e.army, weapon: w.id, + x: ox, y: oy, heading: facing, style: w.fx.style, sound: w.sound, + }); + + if (w.kind === 'hitscan' || w.kind === 'beam') { + let hit = true; + if (w.spread > 0) { + // Spread becomes a miss chance that grows with range — cheap, and it means massed + // infantry still trade meaningfully at the edge of their envelope. + const d = Math.hypot(target.x - e.x, target.y - e.y); + const missChance = Math.min(0.6, w.spread * (d / w.range) * 3); + hit = rngNext(state) >= missChance; + } + const ex = hit ? target.x : target.x + rngRange(state, -24, 24); + const ey = hit ? target.y : target.y + rngRange(state, -24, 24); + state.events.push({ + t: 'shot', style: w.fx.style, x1: ox, y1: oy, x2: ex, y2: ey, + color: w.fx.color, width: w.fx.width, lifeMs: w.fx.lifeMs ?? 80, army: e.army, + }); + if (hit) { + applyDamage(state, rules, target, w.damage * (w.armorMul?.[rules.defById[target.defId].armorClass] ?? 1), e); + if (w.aoe) applyAoe(state, rules, target.x, target.y, w, e); + state.events.push({ t: 'impact', x: ex, y: ey, radius: w.aoe ?? 8, style: w.fx.style }); + } + return; + } + + // Travelling ordnance: leading matters, and a strafing target can genuinely be missed. + let aimX = target.x, aimY = target.y; + if (w.leadTarget) { + const d = Math.hypot(target.x - ox, target.y - oy); + const flight = d / w.speed; + aimX += (target.vx ?? 0) * flight; + aimY += (target.vy ?? 0) * flight; + } + let ang = Math.atan2(aimY - oy, aimX - ox); + if (w.spread > 0) ang += rngRange(state, -w.spread, w.spread); + + if (state.projectiles.length >= rules.constants.projectileCap) return; + state.projectiles.push({ + id: state.nextProjectileId++, + weapon: w.id, army: e.army, ownerId: e.id, + x: ox, y: oy, px: ox, py: oy, + vx: Math.cos(ang) * w.speed, vy: Math.sin(ang) * w.speed, + targetId: w.kind === 'guided' ? target.id : 0, + aimX, aimY, + ttl: Math.ceil(((w.range * 1.4) / w.speed) * rules.constants.tickHz), + trail: [], + }); +} + +function stepProjectiles(state, rules) { + const dt = rules.dt; + const keep = []; + for (const p of state.projectiles) { + const w = rules.weaponById[p.weapon]; + p.px = p.x; p.py = p.y; + + if (w.kind === 'guided') { + const target = entityById(state, p.targetId); + if (target) { + const want = Math.atan2(target.y - p.y, target.x - p.x); + const cur = Math.atan2(p.vy, p.vx); + const next = turnToward(cur, want, w.turnRate * dt); + p.vx = Math.cos(next) * w.speed; + p.vy = Math.sin(next) * w.speed; + p.aimX = target.x; p.aimY = target.y; + } + } + + p.x += p.vx * dt; + p.y += p.vy * dt; + if (w.fx?.trail) { + p.trail.push(p.x, p.y); + if (p.trail.length > 16) p.trail.splice(0, 2); + } + + let detonate = false; + if (--p.ttl <= 0) detonate = true; + if (p.x < 0 || p.y < 0 || p.x > state.worldW || p.y > state.worldH) detonate = true; + + // Proximity to the aim point (ballistic) or the live target (guided). + if (!detonate) { + const hitR = w.kind === 'guided' ? 14 : 12; + if (Math.hypot(p.aimX - p.x, p.aimY - p.y) <= hitR) detonate = true; + } + // Direct contact with any hostile it passes through. + if (!detonate) { + for (const e of state.entities) { + if (e.dead || e.site || e.army === p.army) continue; + if (Math.hypot(e.x - p.x, e.y - p.y) <= e.radius) { detonate = true; break; } + } + } + + if (detonate) { + state.events.push({ t: 'impact', x: p.x, y: p.y, radius: w.aoe ?? 12, style: w.fx.style }); + if (w.aoe) applyAoe(state, rules, p.x, p.y, w, { army: p.army, id: p.ownerId }); + else { + for (const e of state.entities) { + if (e.dead || e.army === p.army) continue; + if (Math.hypot(e.x - p.x, e.y - p.y) <= e.radius) { + applyDamage(state, rules, e, w.damage * (w.armorMul?.[rules.defById[e.defId].armorClass] ?? 1), { army: p.army, id: p.ownerId }); + break; + } + } + } + } else { + keep.push(p); + } + } + state.projectiles = keep; +} + +function applyAoe(state, rules, x, y, w, source) { + const r = w.aoe; + const falloff = w.aoeFalloff ?? 0.4; + for (const e of state.entities) { + if (e.dead) continue; + if (e.army === source.army && !rules.constants.friendlyFire) continue; + const d = Math.hypot(e.x - x, e.y - y) - e.radius; + if (d > r) continue; + const t = Math.max(0, Math.min(1, d / r)); + const scale = 1 - (1 - falloff) * t; + const mul = w.armorMul?.[rules.defById[e.defId].armorClass] ?? 1; + applyDamage(state, rules, e, w.damage * mul * scale, source); + } +} + +export function applyDamage(state, rules, target, amount, source) { + if (target.dead || amount <= 0) return; + target.hp -= amount; + state.events.push({ t: 'damaged', id: target.id, amount, x: target.x, y: target.y }); + if (target.hp <= 0) killEntity(state, rules, target, source); +} + +function killEntity(state, rules, e, source) { + if (e.dead) return; + e.dead = true; + e.hp = 0; + const def = defOf(rules, e); + state.armies[e.army].lostEver++; + if (source && state.armies[source.army]) state.armies[source.army].killsEver++; + state.events.push({ + t: 'unitDestroyed', id: e.id, army: e.army, defId: e.defId, + x: e.x, y: e.y, radius: e.radius, isBuilding: e.isBuilding, + }); + if (e.isBuilding) { + stampFootprint(state.nav, rules, e.tx, e.ty, e.fw, e.fh, false); + recomputeEconomyCaps(state, rules); + } + // Anyone building or assisting this is now idle. + for (const o of state.entities) { + if (o.buildTargetId === e.id) o.buildTargetId = 0; + if (o.targetId === e.id) o.targetId = 0; + } + if (def.deathExplosion) { + const ex = def.deathExplosion; + state.events.push({ t: 'bigExplosion', x: e.x, y: e.y, radius: ex.radius }); + for (const o of state.entities) { + if (o.dead || o.id === e.id) continue; + const d = Math.hypot(o.x - e.x, o.y - e.y) - o.radius; + if (d > ex.radius) continue; + const scale = 1 - Math.max(0, Math.min(1, d / ex.radius)) * 0.7; + applyDamage(state, rules, o, ex.damage * scale, { army: -1, id: 0 }); + } + } +} + +// --------------------------------------------------------------------------- +// Vision +// --------------------------------------------------------------------------- + +/** Coarse 2-tile vision grid. Recomputed every 4th tick — it does not need to be exact. */ +export function computeVision(state, rules) { + const cell = state.tileSize * 2; + for (const a of state.armies) a.visible.fill(0); + for (const e of state.entities) { + if (e.dead) continue; + const a = state.armies[e.army]; + if (!a) continue; + const def = defOf(rules, e); + const sight = e.site ? Math.max(64, (def.sight ?? 0) * 0.4) : (def.sight ?? 0); + if (sight <= 0) continue; + const r = Math.ceil(sight / cell); + const cx = Math.floor(e.x / cell), cy = Math.floor(e.y / cell); + for (let y = cy - r; y <= cy + r; y++) { + if (y < 0 || y >= state.visH) continue; + for (let x = cx - r; x <= cx + r; x++) { + if (x < 0 || x >= state.visW) continue; + const wx = x * cell + cell / 2, wy = y * cell + cell / 2; + if (Math.hypot(wx - e.x, wy - e.y) > sight) continue; + const i = y * state.visW + x; + a.visible[i] = 1; + a.explored[i] = 1; + } + } + } +} + +/** Is `e` currently visible to `armyIdx`? The AI is held to this, same as the player. */ +export function isVisibleTo(state, armyIdx, e) { + if (e.army === armyIdx) return true; + const a = state.armies[armyIdx]; + if (!a) return false; + const cell = state.tileSize * 2; + const x = Math.floor(e.x / cell), y = Math.floor(e.y / cell); + if (x < 0 || y < 0 || x >= state.visW || y >= state.visH) return false; + return a.visible[y * state.visW + x] === 1; +} + +// --------------------------------------------------------------------------- +// Victory +// --------------------------------------------------------------------------- + +function checkResult(state, rules) { + if (state.over) return; + const commanderEnds = rules.constants.commanderDeathEnds !== false; + for (const a of state.armies) { + if (!a.alive) continue; + let hasAny = false, hasBuilder = false; + for (const e of state.entities) { + if (e.dead || e.army !== a.idx || e.site) continue; + hasAny = true; + const def = rules.defById[e.defId]; + if (!e.isBuilding && (def.builds ?? []).length) hasBuilder = true; + } + // TA's "Commander Ends" rule. Without it a match can sit at the time limit while the + // loser hides one Mass Generator in a corner with no way to ever build again. + if (!hasAny || (commanderEnds && !hasBuilder)) { + a.alive = false; + state.events.push({ t: 'armyEliminated', army: a.idx, reason: hasAny ? 'commander' : 'wiped' }); + } + } + const alive = state.armies.filter((a) => a.alive); + if (alive.length <= 1) { + state.over = { winner: alive.length ? alive[0].idx : -1, tick: state.tick }; + state.events.push({ t: 'gameOver', winner: state.over.winner }); + } +} + +// --------------------------------------------------------------------------- +// The tick +// --------------------------------------------------------------------------- + +/** Advance the simulation one fixed step. Returns this tick's events. */ +export function tick(state, rules) { + state.events = []; + if (state.over) return state.events; + + state.tick++; + state.elapsedSec = state.tick * rules.dt; + + stepEconomy(state, rules); + stepOrders(state, rules); + servicePathQueue(state, rules); + stepMovement(state, rules); + stepSeparation(state, rules); + stepCombat(state, rules); + stepProjectiles(state, rules); + + if (state.tick % 4 === 0) computeVision(state, rules); + + // Reap the dead once per tick so nothing iterates a corpse mid-phase. + if (state.entities.some((e) => e.dead)) { + state.entities = state.entities.filter((e) => !e.dead); + } + checkResult(state, rules); + return state.events; +} + +/** + * Drive the sim from a wall-clock delta, draining whole fixed steps. The accumulator lives + * in state so the verify script runs the identical loop headlessly, and `state.alpha` gives + * the view its interpolation factor between the last two ticks. + */ +export function step(state, rules, deltaMs, maxSteps = 4) { + const out = []; + state.accumulatorMs += deltaMs; + let n = 0; + while (state.accumulatorMs >= rules.stepMs && n < maxSteps) { + state.accumulatorMs -= rules.stepMs; + const ev = tick(state, rules); + for (let i = 0; i < ev.length; i++) out.push(ev[i]); + n++; + } + // Spiral-of-death guard: if we hit the cap, drop the backlog rather than fall further behind. + if (n === maxSteps && state.accumulatorMs >= rules.stepMs) state.accumulatorMs = 0; + state.alpha = Math.min(1, state.accumulatorMs / rules.stepMs); + return out; +} + +// --------------------------------------------------------------------------- +// Serialization +// --------------------------------------------------------------------------- + +/** RLE the terrain — a 128x128 map compresses roughly 30:1, which matters because + * localStorage is a ~5MB budget shared across every game in this repo. */ +function rleEncode(arr) { + const out = []; + let run = 1; + for (let i = 1; i <= arr.length; i++) { + if (i < arr.length && arr[i] === arr[i - 1] && run < 65535) { run++; continue; } + out.push(arr[i - 1], run); + run = 1; + } + return out; +} + +function rleDecode(list, len) { + const arr = new Uint8Array(len); + let p = 0; + for (let i = 0; i < list.length; i += 2) { + const v = list[i], n = list[i + 1]; + for (let k = 0; k < n && p < len; k++) arr[p++] = v; + } + return arr; +} + +export function serialize(state) { + return JSON.stringify({ + v: SAVE_VERSION, + rulesVersion: state.rulesVersion, + seed: state.seed, rngState: state.rngState, + tick: state.tick, w: state.w, h: state.h, theme: state.theme, + terrain: rleEncode(state.terrain), + over: state.over, + nextId: state.nextId, + armies: state.armies.map((a) => ({ + armyId: a.armyId, commanderId: a.commanderId, isHuman: a.isHuman, + aiSkill: a.aiSkill, aiProfile: a.aiProfile, + energy: a.energy, mass: a.mass, alive: a.alive, + builtEver: a.builtEver, lostEver: a.lostEver, killsEver: a.killsEver, + explored: rleEncode(a.explored), + })), + entities: state.entities.map((e) => ({ + id: e.id, army: e.army, defId: e.defId, isBuilding: e.isBuilding, + x: Math.round(e.x * 8) / 8, y: Math.round(e.y * 8) / 8, + heading: Math.round(e.heading * 1000) / 1000, + turretRot: Math.round(e.turretRot * 1000) / 1000, + hp: Math.round(e.hp * 100) / 100, + site: e.site, progress: Math.round(e.progress * 1000) / 1000, + tx: e.tx, ty: e.ty, fw: e.fw, fh: e.fh, + orders: e.orders, queue: e.queue, jobProgress: Math.round(e.jobProgress * 1000) / 1000, + rallyX: e.rallyX, rallyY: e.rallyY, hasRally: e.hasRally, + targetId: e.targetId, buildTargetId: e.buildTargetId, + reload: e.reload, burstLeft: e.burstLeft, + terrainBonus: e.terrainBonus, + })), + // Projectiles are sub-second transients; dropping them costs nothing and saves bytes. + }); +} + +export function deserialize(rules, raw) { + let data; + try { data = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch { return null; } + if (!data || data.v !== SAVE_VERSION) return null; + if (data.rulesVersion !== rules.version) return null; + + const terrain = rleDecode(data.terrain, data.w * data.h); + const state = createMatch(rules, { + seed: data.seed, + map: { w: data.w, h: data.h, terrain, theme: data.theme, starts: [] }, + armies: data.armies.map((a) => ({ + armyId: a.armyId, commanderId: a.commanderId, isHuman: a.isHuman, + aiSkill: a.aiSkill, aiProfile: a.aiProfile, + })), + }); + + state.rngState = data.rngState; + state.tick = data.tick; + state.elapsedSec = data.tick * rules.dt; + state.over = data.over; + state.nextId = data.nextId; + state.entities = []; + state.nav.blocked.fill(0); + + data.armies.forEach((a, i) => { + Object.assign(state.armies[i], { + energy: a.energy, mass: a.mass, alive: a.alive, + builtEver: a.builtEver, lostEver: a.lostEver, killsEver: a.killsEver, + explored: rleDecode(a.explored, state.visW * state.visH), + }); + }); + + for (const s of data.entities) { + const def = rules.defById[s.defId]; + if (!def) continue; + const e = baseEntity(state, s.army, def); + Object.assign(e, s); + e.px = e.x; e.py = e.y; e.pheading = e.heading; e.pturretRot = e.turretRot; + e.radius = def.isBuilding ? def.radius : (def.radius ?? 16); + e.path = null; e.pathIdx = 0; e.wantPath = false; + state.entities.push(e); + if (e.isBuilding) { + stampFootprint(state.nav, rules, e.tx, e.ty, e.fw, e.fh, true); + if (!e.site) { + const bdef = rules.buildingById[e.defId]; + if (bdef.produce) { + e.produceE = bdef.produce.energy ?? 0; + e.produceM = (bdef.produce.mass ?? 0) * (e.terrainBonus ?? 1); + } + if (bdef.upkeep) e.upkeepE = bdef.upkeep.energy ?? 0; + if (bdef.storage) { e.storeE = bdef.storage.energy ?? 0; e.storeM = bdef.storage.mass ?? 0; } + } + } else { + const udef = rules.unitById[e.defId]; + if (udef.produce) { e.produceE = udef.produce.energy ?? 0; e.produceM = udef.produce.mass ?? 0; } + if (udef.storage) { e.storeE = udef.storage.energy ?? 0; e.storeM = udef.storage.mass ?? 0; } + } + } + state.nextId = data.nextId; + recomputeEconomyCaps(state, rules); + computeVision(state, rules); + return state; +} + +/** FNV-1a over the serialized state. Used for determinism and round-trip assertions. */ +export function hashState(state) { + const s = serialize(state); + let h = 0x811c9dc5; + for (let i = 0; i < s.length; i++) { + h ^= s.charCodeAt(i); + h = Math.imul(h, 0x01000193) >>> 0; + } + return h >>> 0; +} + +// --------------------------------------------------------------------------- +// Queries used by the view, the HUD and the AI +// --------------------------------------------------------------------------- + +export function armyEntities(state, armyIdx, includeSites = true) { + return state.entities.filter((e) => !e.dead && e.army === armyIdx && (includeSites || !e.site)); +} + +export function buildOptionsFor(rules, e) { + const def = rules.defById[e.defId]; + return (def.builds ?? []).map((id) => rules.defById[id]); +} + +/** Can this army afford `def` right now, out of stored resources? */ +export function canAfford(state, armyIdx, def) { + const a = state.armies[armyIdx]; + return a.energy >= (def.cost?.energy ?? 0) * 0.25 && a.mass >= (def.cost?.mass ?? 0) * 0.25; +} + +export { segmentClear, tileIndex, worldToTileX, worldToTileY, tileCenterX, tileCenterY }; diff --git a/src/games/totalannihilation/TAMapGen.js b/src/games/totalannihilation/TAMapGen.js new file mode 100644 index 0000000..25d1766 --- /dev/null +++ b/src/games/totalannihilation/TAMapGen.js @@ -0,0 +1,295 @@ +// Total Annihilation — seeded procedural skirmish map generator. +// +// Headless: no Phaser imports, runs in Node for tools/verifyTotalAnnihilation.js. +// +// Produces exactly the same `map` shape a hand-authored campaign mission declares +// ({ w, h, theme, terrain, starts }), so campaign and skirmish share one loader. +// +// Determinism: same seed + same options => byte-identical map. Everything draws from the +// local mulberry32; never call Math.random() here. + +import { createNav, findPath, clearanceFor, nearestUsableTile, tileIndex } from './TANav.js'; + +function mulberry32(seed) { + let a = seed >>> 0; + return function next() { + a = (a + 0x6d2b79f5) >>> 0; + let t = Math.imul(a ^ (a >>> 15), a | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +/** Smooth 2D value noise: a coarse random lattice, bilinearly interpolated, two octaves. */ +function valueNoise(rnd, w, h, scale) { + const lw = Math.max(2, Math.ceil(w * scale) + 1); + const lh = Math.max(2, Math.ceil(h * scale) + 1); + const lattice = new Float32Array(lw * lh); + for (let i = 0; i < lattice.length; i++) lattice[i] = rnd(); + + const smooth = (t) => t * t * (3 - 2 * t); + const sample = (fx, fy) => { + const x0 = Math.min(lw - 1, Math.floor(fx)), y0 = Math.min(lh - 1, Math.floor(fy)); + const x1 = Math.min(lw - 1, x0 + 1), y1 = Math.min(lh - 1, y0 + 1); + const tx = smooth(fx - x0), ty = smooth(fy - y0); + const a = lattice[y0 * lw + x0], b = lattice[y0 * lw + x1]; + const c = lattice[y1 * lw + x0], d = lattice[y1 * lw + x1]; + return (a * (1 - tx) + b * tx) * (1 - ty) + (c * (1 - tx) + d * tx) * ty; + }; + + const out = new Float32Array(w * h); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const base = sample(x * scale, y * scale); + const detail = sample(x * scale * 2.7, y * scale * 2.7); + out[y * w + x] = base * 0.72 + detail * 0.28; + } + } + return out; +} + +/** Map (x,y) onto the canonical half/quadrant so the result is exactly symmetric. */ +function canonical(sym, x, y, w, h) { + switch (sym) { + case 'mirror-x': return x < w / 2 ? [x, y] : [w - 1 - x, y]; + case 'mirror-y': return y < h / 2 ? [x, y] : [x, h - 1 - y]; + case 'rotational': { + // 180-degree rotation about the centre. + const beforeCentre = (y * w + x) < ((h * w) / 2); + return beforeCentre ? [x, y] : [w - 1 - x, h - 1 - y]; + } + default: return [x, y]; + } +} + +function startPositions(sym, w, h, margin) { + switch (sym) { + case 'mirror-x': return [[margin, h >> 1], [w - 1 - margin, h >> 1]]; + case 'mirror-y': return [[w >> 1, margin], [w >> 1, h - 1 - margin]]; + case 'rotational': return [[margin, margin], [w - 1 - margin, h - 1 - margin]]; + default: return [[margin, h >> 1], [w - 1 - margin, h >> 1]]; + } +} + +/** + * @param {object} rules compiled rules + * @param {object} opts { seed, size:'small'|'medium'|'large', symmetry, theme } + * @returns {{w,h,theme,terrain:Uint8Array,starts:Array,metalSpots:Array}} + */ +export function generateMap(rules, opts = {}) { + const sk = rules.skirmish; + const size = opts.size ?? sk.defaults.size; + const symmetry = opts.symmetry ?? sk.defaults.symmetry; + const theme = opts.theme ?? sk.defaults.theme; + const seed = (opts.seed ?? 1) >>> 0; + const g = sk.gen; + + const dim = sk.sizes[size]; + if (!dim) throw new Error(`[TAMapGen] unknown size "${size}"`); + if (!sk.symmetries.includes(symmetry)) throw new Error(`[TAMapGen] unknown symmetry "${symmetry}"`); + if (!sk.themes.includes(theme)) throw new Error(`[TAMapGen] unknown theme "${theme}"`); + + const w = dim, h = dim; + const rnd = mulberry32(seed); + + const T = {}; + for (const t of rules.terrain) T[t.id] = t.index; + + const noise = valueNoise(rnd, w, h, g.noiseScale); + const detail = valueNoise(rnd, w, h, g.noiseScale * 2.2); + + const terrain = new Uint8Array(w * h); + // Thresholds are PERCENTILES of the actual noise, not raw values. Smoothed value noise + // clusters hard around 0.5, so comparing it against a raw 0.10 yields almost no water — + // quantiles make `waterFraction: 0.10` mean literally a tenth of the map. + const quantile = (arr, q) => { + const s = Float32Array.from(arr).sort(); + return s[Math.max(0, Math.min(s.length - 1, Math.floor(q * (s.length - 1))))]; + }; + const waterT = quantile(noise, g.waterFraction); + const cliffT = quantile(noise, 1 - g.cliffFraction); + const roughLo = quantile(detail, 0.5 - g.roughFraction / 2); + const roughHi = quantile(detail, 0.5 + g.roughFraction / 2); + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + const [cx, cy] = canonical(symmetry, x, y, w, h); + const n = noise[cy * w + cx]; + const d = detail[cy * w + cx]; + let id = T.ground; + if (n < waterT) id = T.water; + else if (n > cliffT) id = T.cliff; + else if (d > roughLo && d < roughHi) id = T.rough; + terrain[y * w + x] = id; + } + } + + // Majority smoothing removes single-tile speckle that would otherwise make pathing ugly. + for (let pass = 0; pass < g.smoothPasses; pass++) { + const src = terrain.slice(); + for (let y = 1; y < h - 1; y++) { + for (let x = 1; x < w - 1; x++) { + const counts = new Map(); + for (let dy = -1; dy <= 1; dy++) { + for (let dx = -1; dx <= 1; dx++) { + const v = src[(y + dy) * w + (x + dx)]; + counts.set(v, (counts.get(v) ?? 0) + 1); + } + } + let bestV = src[y * w + x], bestC = 0; + // Deterministic tie-break by terrain index — never rely on Map iteration order alone. + for (const [v, c] of [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0] - b[0])) { + if (c > bestC) { bestV = v; bestC = c; } + } + if (bestC >= 6) terrain[y * w + x] = bestV; + } + } + } + + // ---- starts ------------------------------------------------------------ + const margin = Math.max(g.startClearRadiusTiles + 2, Math.floor(dim * 0.10)); + const [p0, p1] = startPositions(symmetry, w, h, margin); + const starts = [ + { army: 0, x: p0[0], y: p0[1] }, + { army: 1, x: p1[0], y: p1[1] }, + ]; + + const clearR = g.startClearRadiusTiles; + for (const s of starts) { + for (let y = s.y - clearR; y <= s.y + clearR; y++) { + for (let x = s.x - clearR; x <= s.x + clearR; x++) { + if (x < 0 || y < 0 || x >= w || y >= h) continue; + if (Math.hypot(x - s.x, y - s.y) > clearR) continue; + terrain[y * w + x] = T.ground; + } + } + } + + // ---- metal spots ------------------------------------------------------- + const metalSpots = []; + const SPOT = 2; // spots are 2x2 tiles + + const canPlaceSpot = (x, y) => { + if (x < 1 || y < 1 || x + SPOT > w - 1 || y + SPOT > h - 1) return false; + for (let dy = 0; dy < SPOT; dy++) { + for (let dx = 0; dx < SPOT; dx++) { + const t = terrain[(y + dy) * w + (x + dx)]; + if (t !== T.ground && t !== T.rough) return false; + } + } + // Keep spots apart so a single Mass Generator can't cover two. + for (const s of metalSpots) if (Math.hypot(s.x - x, s.y - y) < 4) return false; + return true; + }; + + const commitSpot = (x, y) => { + for (let dy = 0; dy < SPOT; dy++) { + for (let dx = 0; dx < SPOT; dx++) terrain[(y + dy) * w + (x + dx)] = T.metal; + } + metalSpots.push({ x, y }); + }; + + // Mirror the BLOCK, not its corner: a 2x2 block starting at x mirrors to one starting at + // w - x - SPOT. Mirroring the corner alone shifts the block one tile and breaks symmetry. + const mirrorOf = (x, y) => { + switch (symmetry) { + case 'mirror-x': return [w - x - SPOT, y]; + case 'mirror-y': return [x, h - y - SPOT]; + default: return [w - x - SPOT, h - y - SPOT]; + } + }; + + const spotR = g.metalSpotRadiusTiles; + let guard = 0; + while (metalSpots.length < g.metalSpotsPerStart * 2 && guard++ < 4000) { + const a = rnd() * Math.PI * 2; + const r = clearR * 0.5 + rnd() * (spotR - clearR * 0.5); + const x = Math.round(starts[0].x + Math.cos(a) * r); + const y = Math.round(starts[0].y + Math.sin(a) * r); + const [mx, my] = mirrorOf(x, y); + // Commit both or neither, or the two players end up with different economies. + if (!canPlaceSpot(x, y)) continue; + if (!canPlaceSpot(mx, my)) continue; + commitSpot(x, y); + commitSpot(mx, my); + } + + const map = { w, h, theme, terrain, starts, metalSpots, seed, size, symmetry }; + + // ---- reachability ------------------------------------------------------ + // Guarantee the starts can actually reach each other for the widest unit in the ruleset, + // carving a corridor rather than rejecting the seed — a retry loop makes generation + // non-deterministic in wall-clock terms and can hang on a hostile parameter set. + const maxNeed = rules.units.reduce( + (m, u) => Math.max(m, clearanceFor(u.radius, rules.constants.tileSize)), 1); + const widestMoveClass = pickWidestMoveClass(rules); + ensureConnected(rules, map, widestMoveClass, maxNeed, T); + + return map; +} + +/** The move class with the most restrictive terrain table — if it can get through, all can. */ +function pickWidestMoveClass(rules) { + let worst = null, worstScore = Infinity; + for (const [mc, spec] of Object.entries(rules.moveClasses)) { + const score = spec.costByTerrainIndex.reduce((s, c) => s + (c == null ? 0 : 1 / c), 0); + if (score < worstScore) { worstScore = score; worst = mc; } + } + return worst ?? Object.keys(rules.moveClasses)[0]; +} + +function ensureConnected(rules, map, mc, need, T) { + const nav = createNav(rules, map); + const [a, b] = map.starts; + const ai = nearestUsableTile(nav, mc, need, a.x, a.y, 12); + const bi = nearestUsableTile(nav, mc, need, b.x, b.y, 12); + if (ai >= 0 && bi >= 0 && findPath(nav, mc, need, ai, bi)) return; + + // Carve a straight corridor wide enough for the widest unit. + const halfW = Math.max(1, need + 1); + const steps = Math.ceil(Math.hypot(b.x - a.x, b.y - a.y)) * 2; + for (let s = 0; s <= steps; s++) { + const t = s / steps; + const cx = Math.round(a.x + (b.x - a.x) * t); + const cy = Math.round(a.y + (b.y - a.y) * t); + for (let y = cy - halfW; y <= cy + halfW; y++) { + for (let x = cx - halfW; x <= cx + halfW; x++) { + if (x < 0 || y < 0 || x >= map.w || y >= map.h) continue; + const cur = map.terrain[y * map.w + x]; + if (cur === T.metal) continue; + map.terrain[y * map.w + x] = T.ground; + } + } + } +} + +/** Decode a hand-authored mission map (rows of terrain chars) into the same shape. */ +export function decodeMap(rules, spec) { + const rows = spec.rows ?? spec.tiles ?? []; + const w = spec.w ?? (rows[0]?.length ?? 0); + const h = spec.h ?? rows.length; + const terrain = new Uint8Array(w * h); + const fallback = rules.terrain[0].index; + for (let y = 0; y < h; y++) { + const row = rows[y] ?? ''; + for (let x = 0; x < w; x++) { + const ch = row[x]; + const t = ch != null ? rules.terrainByCh[ch] : null; + if (ch != null && ch !== ' ' && !t) { + throw new Error(`[TAMapGen] unknown terrain char "${ch}" at ${x},${y}`); + } + terrain[y * w + x] = t ? t.index : fallback; + } + } + return { + w, h, + theme: spec.theme ?? rules.skirmish.defaults.theme, + terrain, + starts: spec.starts ?? [], + units: spec.units ?? [], + buildings: spec.buildings ?? [], + areas: spec.areas ?? [], + }; +} + +export { tileIndex }; diff --git a/src/games/totalannihilation/TANav.js b/src/games/totalannihilation/TANav.js new file mode 100644 index 0000000..bda0056 --- /dev/null +++ b/src/games/totalannihilation/TANav.js @@ -0,0 +1,441 @@ +// Total Annihilation — navigation: grid, clearance field, A*, path smoothing, spatial hash. +// +// Headless: no Phaser imports, runs in Node for tools/verifyTotalAnnihilation.js. +// +// The tile grid exists ONLY for pathfinding cost and build legality. Units carry float +// positions and a heading; they never snap to tiles. That is what lets three small units +// share one tile while a tank takes it alone — the packing falls out of collision radii, +// not out of a slot system. +// +// Two things here are load-bearing for performance: +// 1. Units are NOT stamped into the grid. Only terrain and buildings block. If every unit +// dirtied the grid, every path would be invalidated every tick. +// 2. Clearance-based pathfinding: clearance[i] is the side of the largest all-passable +// square with tile i at its top-left, so a large unit simply refuses tiles whose +// clearance is below its need. One DP pass at load, patched over a dirty rect when a +// building goes up or dies. + +const SQRT2 = Math.SQRT2; +export const MAX_CLEARANCE = 8; // clamped so clearance fits a Uint8Array and the DP stays cheap + +// --------------------------------------------------------------------------- +// Nav grid +// --------------------------------------------------------------------------- + +/** + * Build the navigation grid for a map. + * @param {object} rules compiled rules + * @param {{w:number,h:number,terrain:Uint8Array}} map terrain indices into rules.terrain + */ +export function createNav(rules, map) { + const { w, h } = map; + const n = w * h; + const moveClassIds = Object.keys(rules.moveClasses); + + const nav = { + w, h, n, + tileSize: rules.constants.tileSize, + terrain: map.terrain, + blocked: new Uint8Array(n), // buildings and build sites + moveClassIds, + passable: Object.create(null), // mc -> Uint8Array + cost: Object.create(null), // mc -> Float32Array (Infinity where impassable) + clearance: Object.create(null), // mc -> Uint8Array + // Scratch reused by every A* call so a path request allocates nothing. + _g: new Float64Array(n), + _came: new Int32Array(n), + _stamp: new Int32Array(n), + _epoch: 0, + _heap: createHeap(Math.max(64, n >> 2)), + }; + + for (const mc of moveClassIds) { + nav.passable[mc] = new Uint8Array(n); + nav.cost[mc] = new Float32Array(n); + nav.clearance[mc] = new Uint8Array(n); + } + refreshRegion(nav, rules, 0, 0, w - 1, h - 1); + return nav; +} + +/** Recompute passability, cost and clearance over a tile rect (inclusive). */ +export function refreshRegion(nav, rules, x0, y0, x1, y1) { + const { w, h } = nav; + x0 = Math.max(0, x0); y0 = Math.max(0, y0); + x1 = Math.min(w - 1, x1); y1 = Math.min(h - 1, y1); + if (x1 < x0 || y1 < y0) return; + + for (const mc of nav.moveClassIds) { + const costTable = rules.moveClasses[mc].costByTerrainIndex; + const pass = nav.passable[mc]; + const cost = nav.cost[mc]; + for (let y = y0; y <= y1; y++) { + for (let x = x0; x <= x1; x++) { + const i = y * w + x; + const c = nav.blocked[i] ? null : costTable[nav.terrain[i]]; + pass[i] = c == null ? 0 : 1; + cost[i] = c == null ? Infinity : c; + } + } + } + // Clearance at (x,y) reads (x+1,y), (x,y+1), (x+1,y+1), so a change at (x,y) can only + // affect tiles up-left of it — expand the rect that way and sweep in reverse. + const cx0 = Math.max(0, x0 - MAX_CLEARANCE); + const cy0 = Math.max(0, y0 - MAX_CLEARANCE); + for (const mc of nav.moveClassIds) { + const pass = nav.passable[mc]; + const cl = nav.clearance[mc]; + for (let y = y1; y >= cy0; y--) { + for (let x = x1; x >= cx0; x--) { + const i = y * w + x; + if (!pass[i]) { cl[i] = 0; continue; } + if (x === w - 1 || y === h - 1) { cl[i] = 1; continue; } + const a = cl[i + 1], b = cl[i + w], d = cl[i + w + 1]; + cl[i] = Math.min(MAX_CLEARANCE, 1 + Math.min(a, b, d)); + } + } + } +} + +/** Stamp (or clear) a building footprint and patch the affected clearance. */ +export function stampFootprint(nav, rules, tx, ty, fw, fh, on) { + const { w, h } = nav; + for (let y = ty; y < ty + fh; y++) { + if (y < 0 || y >= h) continue; + for (let x = tx; x < tx + fw; x++) { + if (x < 0 || x >= w) continue; + nav.blocked[y * w + x] = on ? 1 : 0; + } + } + refreshRegion(nav, rules, tx, ty, tx + fw - 1, ty + fh - 1); +} + +/** Clearance a unit of this collision radius needs, in tiles. */ +export function clearanceFor(radius, tileSize) { + return Math.max(1, Math.ceil((radius * 2) / tileSize)); +} + +export const tileIndex = (nav, tx, ty) => ty * nav.w + tx; +export const tileX = (nav, i) => i % nav.w; +export const tileY = (nav, i) => (i / nav.w) | 0; +export const worldToTileX = (nav, x) => Math.max(0, Math.min(nav.w - 1, (x / nav.tileSize) | 0)); +export const worldToTileY = (nav, y) => Math.max(0, Math.min(nav.h - 1, (y / nav.tileSize) | 0)); +export const tileCenterX = (nav, tx) => tx * nav.tileSize + nav.tileSize / 2; +export const tileCenterY = (nav, ty) => ty * nav.tileSize + nav.tileSize / 2; + +/** Is this tile usable by a unit with the given move class and clearance need? */ +export function tileOk(nav, mc, need, tx, ty) { + if (tx < 0 || ty < 0 || tx >= nav.w || ty >= nav.h) return false; + return nav.clearance[mc][ty * nav.w + tx] >= need; +} + +/** + * Nearest tile to (tx,ty) that a unit of this move class/clearance can stand on. + * Spiral BFS, bounded — used when an order lands on a wall or inside a building. + */ +export function nearestUsableTile(nav, mc, need, tx, ty, maxRings = 24) { + if (tileOk(nav, mc, need, tx, ty)) return tileIndex(nav, tx, ty); + for (let r = 1; r <= maxRings; r++) { + for (let dx = -r; dx <= r; dx++) { + for (const dy of (dx === -r || dx === r) ? range(-r, r) : [-r, r]) { + const x = tx + dx, y = ty + dy; + if (tileOk(nav, mc, need, x, y)) return tileIndex(nav, x, y); + } + } + } + return -1; +} + +function range(a, b) { + const out = []; + for (let v = a; v <= b; v++) out.push(v); + return out; +} + +// --------------------------------------------------------------------------- +// Binary min-heap (parallel arrays, grows in place, never allocates mid-search) +// --------------------------------------------------------------------------- + +function createHeap(cap) { + return { f: new Float64Array(cap), k: new Int32Array(cap), size: 0, cap }; +} + +function heapClear(hp) { hp.size = 0; } + +function heapPush(hp, f, k) { + if (hp.size === hp.cap) { + const cap = hp.cap * 2; + const nf = new Float64Array(cap); nf.set(hp.f); + const nk = new Int32Array(cap); nk.set(hp.k); + hp.f = nf; hp.k = nk; hp.cap = cap; + } + let i = hp.size++; + hp.f[i] = f; hp.k[i] = k; + while (i > 0) { + const p = (i - 1) >> 1; + if (hp.f[p] <= hp.f[i]) break; + swapHeap(hp, i, p); + i = p; + } +} + +function heapPop(hp) { + const top = hp.k[0]; + hp.size--; + if (hp.size > 0) { + hp.f[0] = hp.f[hp.size]; hp.k[0] = hp.k[hp.size]; + let i = 0; + for (;;) { + const l = i * 2 + 1, r = l + 1; + let m = i; + if (l < hp.size && hp.f[l] < hp.f[m]) m = l; + if (r < hp.size && hp.f[r] < hp.f[m]) m = r; + if (m === i) break; + swapHeap(hp, i, m); + i = m; + } + } + return top; +} + +function swapHeap(hp, a, b) { + const f = hp.f[a]; hp.f[a] = hp.f[b]; hp.f[b] = f; + const k = hp.k[a]; hp.k[a] = hp.k[b]; hp.k[b] = k; +} + +// --------------------------------------------------------------------------- +// A* +// --------------------------------------------------------------------------- + +const NEIGHBOR_DX = [1, -1, 0, 0, 1, 1, -1, -1]; +const NEIGHBOR_DY = [0, 0, 1, -1, 1, -1, 1, -1]; + +/** + * A* over the tile grid. + * @returns {Int32Array|null} tile indices from start to goal inclusive, or null if unreachable + */ +export function findPath(nav, mc, need, startIdx, goalIdx, expansionCap = 20000) { + if (startIdx === goalIdx) return Int32Array.of(startIdx); + const cl = nav.clearance[mc]; + const cost = nav.cost[mc]; + if (cl[goalIdx] < need || cl[startIdx] < need) return null; + + const { w, h } = nav; + const g = nav._g, came = nav._came, stamp = nav._stamp, hp = nav._heap; + const epoch = ++nav._epoch; + heapClear(hp); + + const gx = goalIdx % w, gy = (goalIdx / w) | 0; + const heuristic = (i) => { + const dx = Math.abs((i % w) - gx); + const dy = Math.abs(((i / w) | 0) - gy); + // Octile: exact for 8-way movement on a uniform grid, so it never over-estimates + // once terrain cost is >= 1 — which the rules validator guarantees. + return (dx > dy ? dx - dy + SQRT2 * dy : dy - dx + SQRT2 * dx); + }; + + g[startIdx] = 0; + came[startIdx] = -1; + stamp[startIdx] = epoch; + heapPush(hp, heuristic(startIdx), startIdx); + + let expansions = 0; + const closed = new Set(); + + while (hp.size > 0) { + const cur = heapPop(hp); + if (cur === goalIdx) return reconstruct(came, startIdx, goalIdx); + if (closed.has(cur)) continue; + closed.add(cur); + if (++expansions > expansionCap) return null; + + const cx = cur % w, cy = (cur / w) | 0; + const gcur = g[cur]; + + for (let d = 0; d < 8; d++) { + const nx = cx + NEIGHBOR_DX[d], ny = cy + NEIGHBOR_DY[d]; + if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue; + const ni = ny * w + nx; + if (cl[ni] < need) continue; + const diag = d >= 4; + if (diag) { + // No corner cutting: both orthogonal neighbours must also be usable, or a tank + // squeezes diagonally between two buildings that visually touch. + if (cl[cy * w + nx] < need || cl[ny * w + cx] < need) continue; + } + const step = cost[ni] * (diag ? SQRT2 : 1); + const tentative = gcur + step; + if (stamp[ni] === epoch && tentative >= g[ni]) continue; + stamp[ni] = epoch; + g[ni] = tentative; + came[ni] = cur; + heapPush(hp, tentative + heuristic(ni), ni); + } + } + return null; +} + +function reconstruct(came, startIdx, goalIdx) { + let len = 1; + for (let i = goalIdx; i !== startIdx; i = came[i]) len++; + const out = new Int32Array(len); + let p = len - 1; + for (let i = goalIdx; i !== -1; i = came[i]) { + out[p--] = i; + if (i === startIdx) break; + } + return out; +} + +// --------------------------------------------------------------------------- +// Line of sight and string pulling +// --------------------------------------------------------------------------- + +/** + * Can a unit of this clearance travel the straight segment a->b without clipping a + * blocked tile? Supercover walk (every tile the segment touches, not just Bresenham's). + */ +export function segmentClear(nav, mc, need, x0, y0, x1, y1) { + const ts = nav.tileSize; + const cl = nav.clearance[mc]; + let tx = Math.floor(x0 / ts), ty = Math.floor(y0 / ts); + const tx1 = Math.floor(x1 / ts), ty1 = Math.floor(y1 / ts); + const dx = x1 - x0, dy = y1 - y0; + const stepX = dx > 0 ? 1 : -1, stepY = dy > 0 ? 1 : -1; + const tDeltaX = dx === 0 ? Infinity : Math.abs(ts / dx); + const tDeltaY = dy === 0 ? Infinity : Math.abs(ts / dy); + let tMaxX = dx === 0 ? Infinity + : Math.abs(((dx > 0 ? (tx + 1) * ts : tx * ts) - x0) / dx); + let tMaxY = dy === 0 ? Infinity + : Math.abs(((dy > 0 ? (ty + 1) * ts : ty * ts) - y0) / dy); + + let guard = 0; + const limit = nav.w + nav.h + 4; + for (;;) { + if (tx < 0 || ty < 0 || tx >= nav.w || ty >= nav.h) return false; + if (cl[ty * nav.w + tx] < need) return false; + if (tx === tx1 && ty === ty1) return true; + if (++guard > limit) return false; + if (tMaxX < tMaxY) { tx += stepX; tMaxX += tDeltaX; } + else { ty += stepY; tMaxY += tDeltaY; } + } +} + +/** + * String-pull a tile path into a short list of world waypoints. Greedily skips any + * waypoint the unit can reach in a straight line, so units drive smooth diagonals + * instead of staircasing along tile centres. + * @returns {number[]} flat [x0,y0, x1,y1, ...] world coordinates + */ +export function smoothPath(nav, mc, need, path, endX, endY) { + const out = []; + if (!path || path.length === 0) return out; + const px = (i) => tileCenterX(nav, path[i] % nav.w); + const py = (i) => tileCenterY(nav, (path[i] / nav.w) | 0); + + let anchorX = px(0), anchorY = py(0); + let i = 0; + while (i < path.length - 1) { + // Advance as far as we can still see from the anchor. + let j = i + 1; + while (j + 1 < path.length && segmentClear(nav, mc, need, anchorX, anchorY, px(j + 1), py(j + 1))) j++; + out.push(px(j), py(j)); + anchorX = px(j); anchorY = py(j); + i = j; + } + // Replace the final tile centre with the true destination when it's directly reachable. + if (endX != null && out.length >= 2) { + const lx = out[out.length - 2], ly = out[out.length - 1]; + if (segmentClear(nav, mc, need, lx, ly, endX, endY)) { + out[out.length - 2] = endX; + out[out.length - 1] = endY; + } else { + out.push(endX, endY); + } + } else if (endX != null) { + out.push(endX, endY); + } + return out; +} + +// --------------------------------------------------------------------------- +// Formation slots +// --------------------------------------------------------------------------- + +/** + * Fan a group out around a destination on a hex-ish lattice, then assign units to slots + * greedily by distance. One shared path plus these offsets is what makes a 40-unit move + * order cost one A* instead of forty. + * @returns {number[]} flat [x,y] per unit, in the same order as `units` + */ +export function formationSlots(cx, cy, units, spacingScale = 1.15) { + const nMax = units.length; + let maxR = 0; + for (const u of units) maxR = Math.max(maxR, u.radius); + const spacing = maxR * 2 * spacingScale; + + const slots = [[cx, cy]]; + for (let ring = 1; slots.length < nMax; ring++) { + const count = 6 * ring; + for (let s = 0; s < count && slots.length < nMax; s++) { + const a = (s / count) * Math.PI * 2; + slots.push([cx + Math.cos(a) * spacing * ring, cy + Math.sin(a) * spacing * ring]); + } + } + + // Greedy nearest assignment: sort both sides by distance from the destination and pair + // up, so the units already closest take the inner slots and nobody crosses the group. + const order = units.map((u, i) => ({ + i, d: (u.x - cx) * (u.x - cx) + (u.y - cy) * (u.y - cy), + })).sort((a, b) => a.d - b.d || a.i - b.i); + + const out = new Array(nMax * 2); + order.forEach((entry, rank) => { + out[entry.i * 2] = slots[rank][0]; + out[entry.i * 2 + 1] = slots[rank][1]; + }); + return out; +} + +// --------------------------------------------------------------------------- +// Spatial hash — rebuilt every tick, drives separation and target acquisition +// --------------------------------------------------------------------------- + +export class SpatialHash { + constructor(cellSize, worldW, worldH) { + this.cell = cellSize; + this.cols = Math.max(1, Math.ceil(worldW / cellSize)); + this.rows = Math.max(1, Math.ceil(worldH / cellSize)); + this.buckets = Array.from({ length: this.cols * this.rows }, () => []); + } + + clear() { + for (const b of this.buckets) b.length = 0; + } + + _bucket(x, y) { + const cx = Math.max(0, Math.min(this.cols - 1, (x / this.cell) | 0)); + const cy = Math.max(0, Math.min(this.rows - 1, (y / this.cell) | 0)); + return cy * this.cols + cx; + } + + insert(item, x, y) { + this.buckets[this._bucket(x, y)].push(item); + } + + /** Append every item within `r` of (x,y) into `out`. Order is deterministic. */ + query(x, y, r, out) { + const c0 = Math.max(0, ((x - r) / this.cell) | 0); + const c1 = Math.min(this.cols - 1, ((x + r) / this.cell) | 0); + const r0 = Math.max(0, ((y - r) / this.cell) | 0); + const r1 = Math.min(this.rows - 1, ((y + r) / this.cell) | 0); + for (let cy = r0; cy <= r1; cy++) { + const base = cy * this.cols; + for (let cx = c0; cx <= c1; cx++) { + const b = this.buckets[base + cx]; + for (let i = 0; i < b.length; i++) out.push(b[i]); + } + } + return out; + } +} diff --git a/src/games/totalannihilation/TARules.js b/src/games/totalannihilation/TARules.js new file mode 100644 index 0000000..21d0aa6 --- /dev/null +++ b/src/games/totalannihilation/TARules.js @@ -0,0 +1,307 @@ +// Total Annihilation — rules compiler and validator. +// +// Headless: no Phaser imports, runs in Node for tools/verifyTotalAnnihilation.js. +// +// compileRules() indexes data/totalannihilation-rules.json by id and validates every +// cross-reference against a closed key set, throwing on the first problem. The point is +// that a typo in the JSON fails loudly in the verify script rather than silently at +// runtime three minutes into a match. + +export const WEAPON_KINDS = new Set(['hitscan', 'ballistic', 'guided', 'beam']); +export const FX_STYLES = new Set(['tracer', 'beam', 'shell', 'rocket', 'dgun']); +export const UNIT_ROLES = new Set(['builder', 'combat', 'scout', 'artillery']); +export const SHEET_SLOTS = new Set(['unitSheet', 'structureSheet']); +export const TARGET_DOMAINS = new Set(['ground', 'air']); + +// Procedural painter shapes. A def may only name a shape TAArt knows how to draw; +// this set is duplicated there and the verify script asserts the two agree. +export const PROC_SHAPES = new Set([ + 'commander', 'infantry', 'sniper', 'jeep', 'tank', 'rockettank', + 'energyGen', 'massGen', 'barracks', 'vehiclePlant', +]); + +function fail(msg) { + throw new Error(`[totalannihilation-rules] ${msg}`); +} + +function requireUnique(list, what) { + const seen = new Set(); + for (const e of list) { + if (!e || typeof e.id !== 'string' || !e.id) fail(`${what} entry is missing an id`); + if (seen.has(e.id)) fail(`duplicate ${what} id "${e.id}"`); + seen.add(e.id); + } +} + +function requirePositive(obj, keys, what) { + for (const k of keys) { + const v = obj[k]; + if (typeof v !== 'number' || !Number.isFinite(v) || v <= 0) { + fail(`${what} "${obj.id}" needs a positive ${k} (got ${JSON.stringify(v)})`); + } + } +} + +function indexById(list) { + const m = Object.create(null); + for (const e of list) m[e.id] = e; + return m; +} + +/** + * Compile and validate the raw rules JSON. + * @param {object} json parsed data/totalannihilation-rules.json + * @returns {object} compiled rules with by-id indexes and derived tables + */ +export function compileRules(json) { + if (!json || typeof json !== 'object') fail('rules JSON is empty'); + + const c = json.constants; + if (!c) fail('missing constants'); + requirePositive(c, ['tickHz', 'tileSize', 'unitCapPerArmy', 'buildPowerNominal'], 'constants'); + if (c.tileSize % 2 !== 0) fail('constants.tileSize must be even'); + + // ---- closed sets ------------------------------------------------------- + const armorClasses = json.armorClasses ?? []; + if (!armorClasses.length) fail('armorClasses must be a non-empty array'); + const armorSet = new Set(armorClasses); + + const sizeClasses = json.sizeClasses ?? {}; + for (const [id, s] of Object.entries(sizeClasses)) { + if (!(s.radius > 0)) fail(`sizeClass "${id}" needs a positive radius`); + if (!(s.footprint >= 1)) fail(`sizeClass "${id}" needs footprint >= 1`); + if (!(s.mass > 0)) fail(`sizeClass "${id}" needs a positive mass`); + } + const sizeSet = new Set(Object.keys(sizeClasses)); + + // ---- terrain ----------------------------------------------------------- + const terrain = json.terrain ?? []; + requireUnique(terrain, 'terrain'); + const terrainById = indexById(terrain); + const terrainByCh = Object.create(null); + terrain.forEach((t, i) => { + if (typeof t.ch !== 'string' || t.ch.length !== 1) fail(`terrain "${t.id}" needs a single-char ch`); + if (terrainByCh[t.ch]) fail(`terrain char "${t.ch}" used by both "${terrainByCh[t.ch].id}" and "${t.id}"`); + if (!Number.isInteger(t.frame) || t.frame < 0) fail(`terrain "${t.id}" needs a non-negative integer frame`); + t.index = i; + terrainByCh[t.ch] = t; + }); + if (!terrain.length) fail('at least one terrain type is required'); + + // ---- move classes ------------------------------------------------------ + const moveClasses = json.moveClasses ?? {}; + for (const [mc, spec] of Object.entries(moveClasses)) { + if (!spec.cost) fail(`moveClass "${mc}" is missing its cost table`); + for (const t of terrain) { + if (!(t.id in spec.cost)) fail(`moveClass "${mc}" has no cost for terrain "${t.id}"`); + const v = spec.cost[t.id]; + if (v !== null && !(typeof v === 'number' && v > 0)) { + fail(`moveClass "${mc}" cost for "${t.id}" must be a positive number or null (impassable)`); + } + } + for (const key of Object.keys(spec.cost)) { + if (!terrainById[key]) fail(`moveClass "${mc}" references unknown terrain "${key}"`); + } + // Flat per-terrain-index cost array — the pathfinder's hot path reads this. + spec.costByTerrainIndex = terrain.map((t) => (t.blocksMove ? null : spec.cost[t.id])); + } + const moveClassSet = new Set(Object.keys(moveClasses)); + + // ---- armies & commanders ---------------------------------------------- + const armies = json.armies ?? []; + requireUnique(armies, 'army'); + if (armies.length < 2) fail('at least two armies are required'); + for (const a of armies) { + if (!a.unitSheet || !a.structureSheet) fail(`army "${a.id}" needs unitSheet and structureSheet`); + if (!/^#[0-9a-f]{6}$/i.test(a.color ?? '')) fail(`army "${a.id}" needs a #rrggbb color`); + a.colorInt = parseInt(a.color.slice(1), 16); + } + const armyById = indexById(armies); + const armyIndexById = Object.create(null); + armies.forEach((a, i) => { armyIndexById[a.id] = i; }); + + const commanders = json.commanders ?? []; + requireUnique(commanders, 'commander'); + for (const cm of commanders) { + if (!armyById[cm.armyId]) fail(`commander "${cm.id}" references unknown army "${cm.armyId}"`); + if (!cm.opponentId) fail(`commander "${cm.id}" needs an opponentId for its portrait`); + } + const commanderById = indexById(commanders); + + // ---- weapons ----------------------------------------------------------- + const weapons = json.weapons ?? []; + requireUnique(weapons, 'weapon'); + for (const w of weapons) { + if (!WEAPON_KINDS.has(w.kind)) { + fail(`weapon "${w.id}" has unknown kind "${w.kind}" (expected ${[...WEAPON_KINDS].join('|')})`); + } + requirePositive(w, ['damage', 'reload', 'range'], 'weapon'); + if (w.kind === 'ballistic' || w.kind === 'guided') { + requirePositive(w, ['speed'], 'weapon'); + } + if (w.kind === 'guided' && !(w.turnRate > 0)) fail(`guided weapon "${w.id}" needs a positive turnRate`); + if (w.minRange != null && !(w.minRange >= 0 && w.minRange < w.range)) { + fail(`weapon "${w.id}" minRange must be >= 0 and < range`); + } + for (const key of Object.keys(w.armorMul ?? {})) { + if (!armorSet.has(key)) fail(`weapon "${w.id}" armorMul references unknown armor class "${key}"`); + } + for (const a of armorClasses) { + if (!(a in (w.armorMul ?? {}))) fail(`weapon "${w.id}" armorMul is missing armor class "${a}"`); + } + for (const d of w.targets ?? []) { + if (!TARGET_DOMAINS.has(d)) fail(`weapon "${w.id}" targets unknown domain "${d}"`); + } + if (!w.fx || !FX_STYLES.has(w.fx.style)) { + fail(`weapon "${w.id}" needs fx.style from ${[...FX_STYLES].join('|')}`); + } + if (w.aoe != null && !(w.aoe > 0)) fail(`weapon "${w.id}" aoe must be positive when present`); + // Reload/burst in ticks, precomputed so the sim never divides in its hot loop. + w.reloadTicks = Math.max(1, Math.round(w.reload * c.tickHz)); + w.burstDelayTicks = Math.max(1, Math.round((w.burstDelay ?? 0) * c.tickHz)); + w.burst = w.burst ?? 1; + w.rangeSq = w.range * w.range; + w.minRangeSq = (w.minRange ?? 0) * (w.minRange ?? 0); + w.targetsAir = (w.targets ?? ['ground']).includes('air'); + } + const weaponById = indexById(weapons); + + // ---- shared unit/building validation ---------------------------------- + const buildable = Object.create(null); // id -> def, for `builds` cross-checks + + const units = json.units ?? []; + requireUnique(units, 'unit'); + for (const u of units) { + if (!sizeSet.has(u.size)) fail(`unit "${u.id}" has unknown size "${u.size}"`); + if (!armorSet.has(u.armorClass)) fail(`unit "${u.id}" has unknown armorClass "${u.armorClass}"`); + if (!moveClassSet.has(u.moveClass)) fail(`unit "${u.id}" has unknown moveClass "${u.moveClass}"`); + if (u.role && !UNIT_ROLES.has(u.role)) fail(`unit "${u.id}" has unknown role "${u.role}"`); + if (!SHEET_SLOTS.has(u.sheetSlot)) fail(`unit "${u.id}" has unknown sheetSlot "${u.sheetSlot}"`); + if (!PROC_SHAPES.has(u.procShape)) fail(`unit "${u.id}" has unknown procShape "${u.procShape}"`); + requirePositive(u, ['hp', 'speed', 'turnRate', 'sight'], 'unit'); + if (!Number.isInteger(u.frame) || u.frame < 0) fail(`unit "${u.id}" needs a non-negative integer frame`); + if (u.turretFrame != null && (!Number.isInteger(u.turretFrame) || u.turretFrame < 0)) { + fail(`unit "${u.id}" turretFrame must be a non-negative integer`); + } + for (const wid of u.weapons ?? []) { + if (!weaponById[wid]) fail(`unit "${u.id}" references unknown weapon "${wid}"`); + } + const sc = sizeClasses[u.size]; + u.radius = u.radius ?? sc.radius; + u.massClass = sc.mass; + u.footprintTiles = sc.footprint; + u.sightSq = u.sight * u.sight; + u.isBuilding = false; + u.weaponDefs = (u.weapons ?? []).map((wid) => weaponById[wid]); + u.maxRange = u.weaponDefs.reduce((m, w) => Math.max(m, w.range), 0); + u.spritePx = u.spritePx ?? sc.radius * 2; + buildable[u.id] = u; + } + const unitById = indexById(units); + + const buildings = json.buildings ?? []; + requireUnique(buildings, 'building'); + for (const b of buildings) { + if (!armorSet.has(b.armorClass)) fail(`building "${b.id}" has unknown armorClass "${b.armorClass}"`); + if (!SHEET_SLOTS.has(b.sheetSlot)) fail(`building "${b.id}" has unknown sheetSlot "${b.sheetSlot}"`); + if (!PROC_SHAPES.has(b.procShape)) fail(`building "${b.id}" has unknown procShape "${b.procShape}"`); + requirePositive(b, ['hp', 'buildTime'], 'building'); + if (!b.footprint || !(b.footprint.w >= 1) || !(b.footprint.h >= 1)) { + fail(`building "${b.id}" needs a footprint of at least 1x1 tiles`); + } + if (!Number.isInteger(b.frame) || b.frame < 0) fail(`building "${b.id}" needs a non-negative integer frame`); + if (b.terrainMultiplier && !terrain.some((t) => b.terrainMultiplier in t)) { + fail(`building "${b.id}" terrainMultiplier "${b.terrainMultiplier}" is on no terrain type`); + } + for (const wid of b.weapons ?? []) { + if (!weaponById[wid]) fail(`building "${b.id}" references unknown weapon "${wid}"`); + } + b.isBuilding = true; + b.radius = (Math.max(b.footprint.w, b.footprint.h) * c.tileSize) / 2; + b.sight = b.sight ?? 0; + b.sightSq = b.sight * b.sight; + b.weaponDefs = (b.weapons ?? []).map((wid) => weaponById[wid]); + b.maxRange = b.weaponDefs.reduce((m, w) => Math.max(m, w.range), 0); + buildable[b.id] = b; + } + const buildingById = indexById(buildings); + + // `builds` lists must resolve, and a factory may only build units (not buildings). + for (const def of [...units, ...buildings]) { + for (const id of def.builds ?? []) { + if (!buildable[id]) fail(`"${def.id}" builds unknown def "${id}"`); + if (def.isBuilding && !unitById[id]) fail(`factory "${def.id}" may only build units, not "${id}"`); + if (!def.isBuilding && !buildingById[id]) fail(`mobile builder "${def.id}" may only build buildings, not "${id}"`); + } + if ((def.builds ?? []).length && !(def.buildPower > 0)) { + fail(`"${def.id}" has a builds list but no positive buildPower`); + } + } + // Every producible unit must be reachable from some factory, or it's dead data. + for (const u of units) { + if (u.buildTime === 0) continue; // starting units (the Commander) are placed, not built + const madeBy = buildings.filter((b) => (b.builds ?? []).includes(u.id)); + if (!madeBy.length) fail(`unit "${u.id}" has a buildTime but no building builds it`); + for (const bid of u.builtBy ?? []) { + if (!buildingById[bid]) fail(`unit "${u.id}" builtBy references unknown building "${bid}"`); + if (!(buildingById[bid].builds ?? []).includes(u.id)) { + fail(`unit "${u.id}" claims builtBy "${bid}" but that building does not list it`); + } + } + } + + // ---- AI skills --------------------------------------------------------- + const aiSkills = json.aiSkills ?? []; + const aiBySkill = Object.create(null); + for (const s of aiSkills) { + if (!Number.isInteger(s.skill) || s.skill < 1) fail('aiSkills entries need an integer skill >= 1'); + aiBySkill[s.skill] = s; + } + if (!aiSkills.length) fail('at least one aiSkills entry is required'); + + // ---- skirmish ---------------------------------------------------------- + const sk = json.skirmish; + if (!sk) fail('missing skirmish config'); + for (const [name, tiles] of Object.entries(sk.sizes ?? {})) { + if (!Number.isInteger(tiles) || tiles < 32) fail(`skirmish size "${name}" must be an integer >= 32 tiles`); + } + for (const id of sk.startUnits ?? []) { + if (!unitById[id]) fail(`skirmish.startUnits references unknown unit "${id}"`); + } + if (!sk.sizes?.[sk.defaults?.size]) fail('skirmish.defaults.size is not one of skirmish.sizes'); + if (!(sk.symmetries ?? []).includes(sk.defaults?.symmetry)) fail('skirmish.defaults.symmetry is not one of skirmish.symmetries'); + if (!(sk.themes ?? []).includes(sk.defaults?.theme)) fail('skirmish.defaults.theme is not one of skirmish.themes'); + if (!armyById[sk.defaults?.playerArmy]) fail('skirmish.defaults.playerArmy is not a known army'); + + return { + version: json.version ?? 1, + constants: c, + armorClasses, armorSet, + sizeClasses, + terrain, terrainById, terrainByCh, + moveClasses, + armies, armyById, armyIndexById, + commanders, commanderById, + weapons, weaponById, + units, unitById, + buildings, buildingById, + defById: buildable, + commandIcons: json.commandIcons ?? {}, + aiSkills, aiBySkill, + skirmish: sk, + // Derived conveniences the sim and view both want. + stepMs: 1000 / c.tickHz, + dt: 1 / c.tickHz, + halfTile: c.tileSize / 2, + }; +} + +/** Damage multiplier of `weapon` against a defender of `armorClass`. */ +export function armorMul(weapon, armorClass) { + return weapon.armorMul?.[armorClass] ?? 1; +} + +/** Look up a def (unit or building) by id. */ +export function defOf(rules, id) { + return rules.defById[id] ?? null; +} diff --git a/src/games/totalannihilation/TAWorldView.js b/src/games/totalannihilation/TAWorldView.js new file mode 100644 index 0000000..9200b06 --- /dev/null +++ b/src/games/totalannihilation/TAWorldView.js @@ -0,0 +1,475 @@ +// Total Annihilation — world renderer. +// +// Camera model: a REAL Phaser world camera plus a second screen-space UI camera, the +// MiniMotorwaysGame.js:213-227 pattern. A real camera buys frustum culling, setBounds, +// zoomTo and — decisively for an RTS — pointer.worldX/worldY for click-to-order. +// +// Terrain bakes into chunked RenderTextures (CivilizationMapView's forEachChunk model). A +// large map is 128 tiles = 8192px, well past a safe single GPU texture, so it tiles into +// 1024px chunks created LAZILY as the camera reaches them — 36 chunks eagerly stamped would +// cost several seconds and ~150MB at mission start. +// +// Units are plain Images, never Containers: at 300+ units the Container overhead is real. +// Selection rings, health bars and build ghosts all share ONE Graphics each. + +import { ensureSheets, sheetFrameSize } from './TAArt.js'; +import { colorInt } from './TAFx.js'; + +const CHUNK_PX = 1024; +const ZOOMS = [0.5, 0.7, 1.0, 1.4]; +const CHUNK_IDLE_MS = 30000; // reclaim chunks the camera hasn't looked at in a while + +export const DEPTHS = { + terrain: 0, decal: 5, fxUnder: 8, ghost: 12, + selection: 15, actor: 20, bars: 55, projectile: 60, fxOver: 65, fog: 80, +}; + +export default class TAWorldView { + constructor(scene, rules, art, state, playerArmy) { + this.scene = scene; + this.rules = rules; + this.art = art; + this.state = state; + this.playerArmy = playerArmy; + this.ts = state.tileSize; + + this.worldRoot = scene.add.container(0, 0); + this.uiRoot = scene.add.container(0, 0); + + const { keys, procedural } = ensureSheets(scene, rules, art); + this.sheetKeys = keys; + this.proceduralSheets = procedural; + + // Per-army resolved sheet keys, so a unit def's `sheetSlot` renders correctly for any army. + this.armySheets = rules.armies.map((a) => ({ + unitSheet: keys[a.unitSheet], + structureSheet: keys[a.structureSheet], + unitFrame: sheetFrameSize(art, a.unitSheet), + structureFrame: sheetFrameSize(art, a.structureSheet), + color: a.colorInt, + })); + + const themeName = state.theme; + const theme = art.themes?.[themeName]; + this.terrainKey = keys[theme?.sheet] ?? keys[Object.keys(art.sheets)[0]]; + this.themePalette = theme?.palette ?? {}; + + this._setupCameras(); + this._setupTerrain(); + this._setupFog(); + + this.gSelection = scene.add.graphics().setDepth(DEPTHS.selection); + this.gBars = scene.add.graphics().setDepth(DEPTHS.bars); + this.gGhost = scene.add.graphics().setDepth(DEPTHS.ghost); + this.worldRoot.add(this.gSelection); + this.worldRoot.add(this.gBars); + this.worldRoot.add(this.gGhost); + + this.sprites = new Map(); // entity id -> { img, turret } + this.selection = new Set(); + this.placement = null; // { def, tx, ty, legal } + this.showAllBars = false; + this._stamp = scene.make.image({ x: 0, y: 0, key: this.terrainKey, add: false }).setOrigin(0, 0); + } + + // ------------------------------------------------------------------------- + // Cameras + // ------------------------------------------------------------------------- + + _setupCameras() { + const { scene, state } = this; + const cam = scene.cameras.main; + const margin = this.ts * 3; + cam.setBounds(-margin, -margin, state.worldW + margin * 2, state.worldH + margin * 2); + cam.setBackgroundColor(0x0b0e12); + this.zoomIdx = 2; + cam.setZoom(ZOOMS[this.zoomIdx]); + + this.uiCam = scene.cameras.add(0, 0, scene.scale.width, scene.scale.height); + this.uiCam.ignore(this.worldRoot); + cam.ignore(this.uiRoot); + } + + /** Keep screen-space extras (music controls, fullscreen button) off the world camera. */ + ignoreOnWorldCam(objs) { + if (!objs?.length) return; + this.scene.cameras.main.ignore(objs); + } + + panBy(dx, dy) { + const cam = this.scene.cameras.main; + cam.setScroll(cam.scrollX + dx / cam.zoom, cam.scrollY + dy / cam.zoom); + } + + centerOn(x, y) { this.scene.cameras.main.centerOn(x, y); } + + zoomBy(dir, focusX, focusY) { + const cam = this.scene.cameras.main; + const next = Math.max(0, Math.min(ZOOMS.length - 1, this.zoomIdx + dir)); + if (next === this.zoomIdx) return; + // Zoom toward the cursor: keep the world point under the pointer pinned. + const before = cam.getWorldPoint(focusX, focusY); + this.zoomIdx = next; + cam.setZoom(ZOOMS[next]); + const after = cam.getWorldPoint(focusX, focusY); + cam.setScroll(cam.scrollX + (before.x - after.x), cam.scrollY + (before.y - after.y)); + } + + get zoom() { return this.scene.cameras.main.zoom; } + + worldPoint(screenX, screenY) { + return this.scene.cameras.main.getWorldPoint(screenX, screenY); + } + + // ------------------------------------------------------------------------- + // Terrain (lazy chunked RenderTextures) + // ------------------------------------------------------------------------- + + _setupTerrain() { + const { state } = this; + this.chunkCols = Math.ceil(state.worldW / CHUNK_PX); + this.chunkRows = Math.ceil(state.worldH / CHUNK_PX); + this.chunks = new Map(); // "cx,cy" -> { rt, ox, oy, w, h, touched } + } + + _paintChunk(cx, cy) { + const key = `${cx},${cy}`; + let chunk = this.chunks.get(key); + if (chunk) { chunk.touched = this.scene.time.now; return chunk; } + + const { state, rules, ts } = this; + const ox = cx * CHUNK_PX, oy = cy * CHUNK_PX; + const w = Math.min(CHUNK_PX, state.worldW - ox); + const h = Math.min(CHUNK_PX, state.worldH - oy); + const rt = this.scene.add.renderTexture(ox, oy, w, h).setOrigin(0, 0).setDepth(DEPTHS.terrain); + this.worldRoot.add(rt); + + const t0 = Math.floor(ox / ts), t1 = Math.ceil((ox + w) / ts); + const r0 = Math.floor(oy / ts), r1 = Math.ceil((oy + h) / ts); + const img = this._stamp; + img.setTexture(this.terrainKey); + img.setDisplaySize(ts, ts); + + rt.beginDraw(); + for (let ty = r0; ty < r1 && ty < state.h; ty++) { + for (let tx = t0; tx < t1 && tx < state.w; tx++) { + const terr = rules.terrain[state.terrain[ty * state.w + tx]]; + // Break up flat ground with the two alternate frames, deterministically per tile. + let frame = terr.frame; + if (terr.id === 'ground') { + const hsh = (tx * 73856093) ^ (ty * 19349663); + const pick = (hsh >>> 3) % 5; + if (pick === 1) frame = this.art.terrainFrames?.groundAlt1 ?? frame; + else if (pick === 2) frame = this.art.terrainFrames?.groundAlt2 ?? frame; + } + img.setFrame(frame); + rt.batchDraw(img, tx * ts - ox, ty * ts - oy); + } + } + rt.endDraw(); + + chunk = { rt, ox, oy, w, h, touched: this.scene.time.now }; + this.chunks.set(key, chunk); + return chunk; + } + + /** Create chunks the camera can see; retire ones it hasn't looked at for a while. */ + _updateChunks() { + const cam = this.scene.cameras.main; + const view = cam.worldView; + const c0 = Math.max(0, Math.floor((view.x - CHUNK_PX * 0.25) / CHUNK_PX)); + const c1 = Math.min(this.chunkCols - 1, Math.floor((view.right + CHUNK_PX * 0.25) / CHUNK_PX)); + const r0 = Math.max(0, Math.floor((view.y - CHUNK_PX * 0.25) / CHUNK_PX)); + const r1 = Math.min(this.chunkRows - 1, Math.floor((view.bottom + CHUNK_PX * 0.25) / CHUNK_PX)); + + for (let cy = r0; cy <= r1; cy++) { + for (let cx = c0; cx <= c1; cx++) this._paintChunk(cx, cy); + } + const now = this.scene.time.now; + for (const [key, chunk] of this.chunks) { + if (now - chunk.touched < CHUNK_IDLE_MS) continue; + chunk.rt.destroy(); + this.chunks.delete(key); + } + } + + /** Repaint the chunks overlapping a world rect — used when terrain changes. */ + repaintArea(x0, y0, x1, y1) { + for (const [key, chunk] of this.chunks) { + if (x1 < chunk.ox || x0 > chunk.ox + chunk.w) continue; + if (y1 < chunk.oy || y0 > chunk.oy + chunk.h) continue; + chunk.rt.destroy(); + this.chunks.delete(key); + } + } + + // ------------------------------------------------------------------------- + // Fog of war + // ------------------------------------------------------------------------- + + _setupFog() { + const { state, scene } = this; + const key = 'ta-fog-canvas'; + if (scene.textures.exists(key)) scene.textures.remove(key); + this.fogTex = scene.textures.createCanvas(key, state.visW, state.visH); + this.fogCtx = this.fogTex.getContext(); + const cell = this.ts * 2; + this.fogImg = scene.add.image(0, 0, key).setOrigin(0, 0).setDepth(DEPTHS.fog); + this.fogImg.setDisplaySize(state.visW * cell, state.visH * cell); + // Linear filtering upscales the coarse grid into a soft gradient for the cost of a few + // hundred pixels of texture — far cheaper than a per-tile fog RenderTexture. + this.fogTex.setFilter(1); // Phaser.Textures.FilterMode.LINEAR + this.worldRoot.add(this.fogImg); + this.fogDirty = true; + } + + setFogEnabled(on) { + this.fogEnabled = on; + this.fogImg.setVisible(on); + } + + _redrawFog() { + const { state } = this; + const army = state.armies[this.playerArmy]; + if (!army) return; + const ctx = this.fogCtx; + ctx.clearRect(0, 0, state.visW, state.visH); + const img = ctx.createImageData(state.visW, state.visH); + const d = img.data; + for (let i = 0; i < state.visW * state.visH; i++) { + const vis = army.visible[i], exp = army.explored[i]; + const a = vis ? 0 : (exp ? 140 : 255); + d[i * 4] = 4; d[i * 4 + 1] = 6; d[i * 4 + 2] = 10; d[i * 4 + 3] = a; + } + ctx.putImageData(img, 0, 0); + this.fogTex.refresh(); + } + + /** Is this entity currently drawable for the viewing player? */ + visibleToPlayer(e) { + if (!this.fogEnabled) return true; + if (e.army === this.playerArmy) return true; + const army = this.state.armies[this.playerArmy]; + if (!army) return true; + const cell = this.ts * 2; + const x = Math.floor(e.x / cell), y = Math.floor(e.y / cell); + if (x < 0 || y < 0 || x >= this.state.visW || y >= this.state.visH) return false; + return army.visible[y * this.state.visW + x] === 1; + } + + // ------------------------------------------------------------------------- + // Sprites + // ------------------------------------------------------------------------- + + _ensureSprite(e) { + let s = this.sprites.get(e.id); + if (s) return s; + const rules = this.rules; + const def = rules.defById[e.defId]; + const sheets = this.armySheets[e.army]; + const key = sheets[def.sheetSlot]; + const frameSize = def.sheetSlot === 'unitSheet' ? sheets.unitFrame : sheets.structureFrame; + + const img = this.scene.add.image(e.x, e.y, key, def.frame); + img.setTint(sheets.color); + if (def.isBuilding) { + img.setDisplaySize(def.footprint.w * this.ts, def.footprint.h * this.ts); + } else { + img.setScale((def.spritePx ?? def.radius * 2) / frameSize.w); + } + this.worldRoot.add(img); + + let turret = null; + if (!def.isBuilding && def.turretFrame != null) { + turret = this.scene.add.image(e.x, e.y, key, def.turretFrame); + turret.setTint(sheets.color); + turret.setScale((def.spritePx ?? def.radius * 2) / frameSize.w); + this.worldRoot.add(turret); + } + s = { img, turret, defId: e.defId, site: e.site }; + this.sprites.set(e.id, s); + return s; + } + + _releaseSprite(id) { + const s = this.sprites.get(id); + if (!s) return; + s.img.destroy(); + s.turret?.destroy(); + this.sprites.delete(id); + } + + // ------------------------------------------------------------------------- + // Frame + // ------------------------------------------------------------------------- + + /** + * @param {number} alpha interpolation factor between the last two sim ticks + * @returns {{nanoLinks:Array, projectiles:Array}} data the FX layer needs, in view space + */ + render(alpha) { + const { state, rules } = this; + this._updateChunks(); + + if (this.fogEnabled && this.fogDirty) { this._redrawFog(); this.fogDirty = false; } + + const live = new Set(); + const gSel = this.gSelection, gBar = this.gBars; + gSel.clear(); gBar.clear(); + + for (const e of state.entities) { + if (e.dead) continue; + live.add(e.id); + const def = rules.defById[e.defId]; + const shown = this.visibleToPlayer(e); + const s = this._ensureSprite(e); + s.img.setVisible(shown); + if (s.turret) s.turret.setVisible(shown); + if (!shown) continue; + + const x = e.px + (e.x - e.px) * alpha; + const y = e.py + (e.y - e.py) * alpha; + const heading = lerpAngle(e.pheading, e.heading, alpha); + + s.img.setPosition(x, y); + if (!def.isBuilding) s.img.setRotation(heading); + // Y-sorted actor band; buildings sit just under units sharing a row. + s.img.setDepth(DEPTHS.actor + (y / state.worldH) * 10 + (def.isBuilding ? 0 : 0.05)); + + // Build sites show the wireframe frame and fade in as they complete. + if (e.site !== s.site) { + s.img.setFrame(e.site ? (def.buildFrame ?? def.frame) : def.frame); + s.site = e.site; + } + s.img.setAlpha(e.site ? 0.35 + e.progress * 0.55 : 1); + + if (s.turret) { + const tr = lerpAngle(e.pturretRot, e.turretRot, alpha); + s.turret.setPosition(x, y).setRotation(tr); + s.turret.setDepth(DEPTHS.actor + (y / state.worldH) * 10 + 0.08); + } + + // Selection ring + if (this.selection.has(e.id)) { + const r = def.isBuilding ? Math.max(def.footprint.w, def.footprint.h) * this.ts * 0.55 : e.radius + 4; + gSel.lineStyle(2, 0x7dff9b, 0.95); + gSel.strokeEllipse(x, y + r * 0.25, r * 2, r * 1.1); + } + + // Health / build bars — only when they say something, so we're not stroking 300 of them. + const damaged = e.hp < e.maxHp - 0.5; + if (e.site) { + drawBar(gBar, x, y - e.radius - 10, e.radius * 1.8, e.progress, 0x8ad4ff); + } else if (damaged || this.selection.has(e.id) || this.showAllBars) { + const frac = Math.max(0, e.hp / e.maxHp); + const col = frac > 0.6 ? 0x6fe27a : frac > 0.3 ? 0xe2d16f : 0xe2705f; + drawBar(gBar, x, y - e.radius - 10, e.radius * 1.8, frac, col); + } + } + + for (const id of [...this.sprites.keys()]) if (!live.has(id)) this._releaseSprite(id); + + this._drawPlacementGhost(); + + // Nanolathe links: builder -> whatever it is working on. + const nanoLinks = []; + for (const e of state.entities) { + if (e.dead || !e.buildTargetId) continue; + if (!this.visibleToPlayer(e)) continue; + const target = state.entities.find((t) => t.id === e.buildTargetId && !t.dead); + if (!target) continue; + nanoLinks.push({ + x1: e.x, y1: e.y, x2: target.x, y2: target.y, + color: this.armySheets[e.army].color, + }); + } + + // Projectiles, interpolated and pre-coloured for the FX layer. + const projectiles = []; + for (const p of state.projectiles) { + const w = rules.weaponById[p.weapon]; + const px = p.px + (p.x - p.px) * alpha; + const py = p.py + (p.y - p.py) * alpha; + if (this.fogEnabled && p.army !== this.playerArmy) { + const cell = this.ts * 2; + const gx = Math.floor(px / cell), gy = Math.floor(py / cell); + const army = state.armies[this.playerArmy]; + if (gx < 0 || gy < 0 || gx >= state.visW || gy >= state.visH) continue; + if (!army.visible[gy * state.visW + gx]) continue; + } + projectiles.push({ + x: px, y: py, vx: p.vx, vy: p.vy, + color: colorInt(w.fx.color), width: w.fx.width ?? 2, + style: w.fx.style, trail: w.fx.trail ? p.trail : null, + }); + } + + return { nanoLinks, projectiles }; + } + + _drawPlacementGhost() { + const g = this.gGhost; + g.clear(); + const p = this.placement; + if (!p) return; + const ts = this.ts; + const x = p.tx * ts, y = p.ty * ts; + const w = p.def.footprint.w * ts, h = p.def.footprint.h * ts; + g.fillStyle(p.legal ? 0x4affa0 : 0xff5a5a, 0.22); + g.fillRect(x, y, w, h); + g.lineStyle(2, p.legal ? 0x4affa0 : 0xff5a5a, 0.95); + g.strokeRect(x, y, w, h); + // Show the builder's reach so it's obvious why a distant placement won't start. + if (p.builderX != null) { + g.lineStyle(1, 0x8ad4ff, 0.35); + g.strokeCircle(p.builderX, p.builderY, p.buildRange); + } + } + + /** Highlight mass spots while a Mass Generator is being placed. */ + highlightMassSpots(on) { + if (this.massSpotG) { this.massSpotG.destroy(); this.massSpotG = null; } + if (!on) return; + const { state, rules, ts } = this; + const g = this.scene.add.graphics().setDepth(DEPTHS.decal); + this.worldRoot.add(g); + g.lineStyle(2, 0xffd27a, 0.85); + for (let ty = 0; ty < state.h; ty++) { + for (let tx = 0; tx < state.w; tx++) { + const t = rules.terrain[state.terrain[ty * state.w + tx]]; + if (!t.massMultiplier) continue; + g.strokeRect(tx * ts + 2, ty * ts + 2, ts - 4, ts - 4); + } + } + this.massSpotG = g; + } + + destroy() { + for (const id of [...this.sprites.keys()]) this._releaseSprite(id); + for (const [, chunk] of this.chunks) chunk.rt.destroy(); + this.chunks.clear(); + this.massSpotG?.destroy(); + this.fogImg?.destroy(); + if (this.scene.textures.exists('ta-fog-canvas')) this.scene.textures.remove('ta-fog-canvas'); + this._stamp?.destroy(); + this.worldRoot.destroy(true); + this.uiRoot.destroy(true); + if (this.uiCam) this.scene.cameras.remove(this.uiCam); + } +} + +function drawBar(g, cx, y, w, frac, color) { + const h = 4; + const x = cx - w / 2; + g.fillStyle(0x101418, 0.85); + g.fillRect(x - 1, y - 1, w + 2, h + 2); + g.fillStyle(color, 1); + g.fillRect(x, y, w * Math.max(0, Math.min(1, frac)), h); +} + +function lerpAngle(a, b, t) { + let d = (b - a) % (Math.PI * 2); + if (d > Math.PI) d -= Math.PI * 2; + if (d < -Math.PI) d += Math.PI * 2; + return a + d * t; +}