From 873f5251016d792927b8672b1fc22a2d725675dd Mon Sep 17 00:00:00 2001 From: The-Tysonator Date: Sun, 23 Apr 2023 20:37:17 +0100 Subject: [PATCH] initial commit --- .gitignore | 2 + assets/backgrounds/background_cave.png | Bin 0 -> 43219 bytes assets/backgrounds/background_menu.png | Bin 0 -> 51435 bytes assets/blocks/block_cave.png | Bin 0 -> 314 bytes assets/blocks/dirt_cave.png | Bin 0 -> 358 bytes assets/buttons/ipaddress.png | Bin 0 -> 368 bytes assets/buttons/menu.png | Bin 0 -> 449 bytes assets/buttons/online.png | Bin 0 -> 451 bytes assets/buttons/port.png | Bin 0 -> 240 bytes assets/buttons/survival.png | Bin 0 -> 495 bytes assets/fonts/Blocky.ttf | Bin 0 -> 29036 bytes assets/leveldata/level_data.txt | Bin 0 -> 4768 bytes assets/players/player.png | Bin 0 -> 197 bytes client.py | 358 +++++++++++++++++++++++ inputs.py | 389 +++++++++++++++++++++++++ level_creation.py | 51 ++++ network.py | 105 +++++++ requirements.txt | 2 + server.py | 335 +++++++++++++++++++++ 19 files changed, 1242 insertions(+) create mode 100644 .gitignore create mode 100755 assets/backgrounds/background_cave.png create mode 100755 assets/backgrounds/background_menu.png create mode 100755 assets/blocks/block_cave.png create mode 100755 assets/blocks/dirt_cave.png create mode 100755 assets/buttons/ipaddress.png create mode 100755 assets/buttons/menu.png create mode 100755 assets/buttons/online.png create mode 100755 assets/buttons/port.png create mode 100755 assets/buttons/survival.png create mode 100755 assets/fonts/Blocky.ttf create mode 100755 assets/leveldata/level_data.txt create mode 100755 assets/players/player.png create mode 100755 client.py create mode 100755 inputs.py create mode 100755 level_creation.py create mode 100755 network.py create mode 100644 requirements.txt create mode 100755 server.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc6664a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_STORE +venv/ \ No newline at end of file diff --git a/assets/backgrounds/background_cave.png b/assets/backgrounds/background_cave.png new file mode 100755 index 0000000000000000000000000000000000000000..8dfa2eeca5df3b1112b3d02d229dd42f2de5e4ec GIT binary patch literal 43219 zcmeGFc~q0<_C5|nJr1q5mbPMX0EODx4c z?7i=6PZx()eCPiDdlTL>GBSGqpGy{hZ)7waetjo@)Cl--aoI<~@WVLpd-rdQGD;_j zjf|!k{d4icA3{#G*ZFL%y_)^?lfzF0MxpN>UDz;HYT-WOr|ZKCL+AeHJ#*Q#?=6?n z=o!Pm+U9Yl=T^-pITIUsbY8et6>OLJfTinF*XzRBYVVyL$!&hp;u%|Is&AA7e#prd z1rPs7-g;3uNvM(21T!U75B(Ff++8df*SPAod(wTKUQC(v_qduoUZNAFWx!hvtbF4o zqIJ2!Y?ALDdDAnYNV|$ckxt?_l{Js9gD>oImhE7oXSTq7>L?xy;I5>eu;1}(ot%`@ zW4X2Ftb!HX3XaidTW8hbagd94W|cG@KL}qb;~yYzJc;a&9nvB$MJ)*;S%i*>UxUp?p%FC@pw!gY3*#@Ee$Yh1e zq6RcN_?82S)UBBlB3cvv?vl2+ciQ4fymWqx_%x5-ap$yc5Wn3FeUKTHR1>uTW?p4? zhCe=6+2WitF}UCXDU-T5$@VkJxyGl;9G=hk#EJzy=l)_aNF}74rjuUWtm%E@KhXd~ zuRK#cK9^Y5pMHVcdPmHmw3utcS{fCH|Cwm}30?mF*F7W3xj%j6u$umzq?qQ17j4|H zVQ3AhWV<{(hZ7BG$_u0?tMEGB{wxWWP#7;LZU_h;K83YM48Ow^{u9=;RgxKO<5IKX zbIGoH7g6A)gP9SmZ@IKW&hoPPhjY<^8!#*{nYfT^f`r2J^@!Z{F zLs7S^4`-*U7p5^7Hp1At%V%I#)UvCS{Z7 z&S1EEs~?9qg?r(F!-r+EBX05VWoJqY+}Mi5OWTFuBi!V%AkXq5Z~oSciNfxl#FD0TSJ+j0RRt0Xv^aDdBw9ro&IT!?q3L<=lB*sR zb-=G>v>u!5p7dFDRv_+9LSd$`=TDXcos$ZmN=EC5WrmxPXqXX_TtzMeHoM1g3$>!H zIf>oK4%2nzweA+o+~L4)KODQ~N~2dDLraye(H1l&W`_ECf^2}mNxVDfB~W*{q8s&p zYonxm;_h8Dx<|B?)Mu^nrkyCs40N4`hTRG?*p)FIPYKCj>9|HDA{V6l+PTMgGjvz3 z?9}*^#fAHw=E!AhoEDfOG5-jQQTuB2n59@JGB=>9TVws!?%9+zNK3H-R-6}W$kHlLVkr({!{?MIwm|wD&2-oEjpV%}u(p;39 zu4IpKm4ncArmJq(<$ZoLw0uKomg{LxE*276_;zK+y=_9-YI4BJ<{#-;R8CTAxwJW_ z97(aB-j6~n%Dpx3Va2Jobe_dnF40zTbyGyu{z7inuIMb2Yv#f6D!jt6prIf+S65=v_RuRFiBgP5>{jI=rXuoH(tn4g-w@u9E>>6ZLdA)(KT zPS-?RPlg5E=%M3@&W<|kqG8E*U6C{POQLQYs zT*~J)x^hR{@Fy>0?(y`XpQTdJM+JH@q+w}4gA^)}6@_lPjF3rPPsp%~)4<4)GRrE$ zb_*7S+oGrYC1afr9^~S%GM9Z%z!Jk zKa;38Nt`!cW~jTDeSM$TLb|HRdp7|uZh)GDS>>?)i{WwYz8MS{Hx<}s4JQtf(vNFxR$v<&&1h)Ir(DQc9 zz$WrI(&eg4eX~8I{9b!B|IZ-K;@WlG(6=^M<95s5KhN~@lxY>p^31S?J?diN!*rXQ z`8%{;`_Ut8>Z-A3`yNE<)Bmk@t~q%hE68O@k>4cEPBEN$e{5gVY2RnC_%4i+w8Sh| z3~)%g7W+&_r75F@nb^1)h=mOM;jL$VU9jgVwZUp8Pk`4e3}lc=4bdfuiJZf z$w$Y;b_YE}US4~&A&dg!zrJh*d;g@4!rRqRX7S1V1S(-tBw7YigG$19baK7`UF!eV z)KI-80RUCCjyo?O@J59|;B+@L6v@{M7zM`^+(#)WH)u0H&(v+3^#|k+bh)+BA+ROb zxV;WWRpo!Q-JmvEYdvfH$%p0|PzoTcfDw{<0;RkSu)?D=;IT2}a=P(W&G+j_naw$J zGZI&-yjFJ=b~2u1g+?9S8JM>ntuoUMD@|SVu6oc*sv;7$yMcRUZLe05({>}1;m-TS z+k=yGt+{foU22YLpRpVFD>7Vbj`f*^lb_~4Lf-t|hulV*1*up<-|2GjM-R^Jp+ zzj@y#1yqslH|Zy(M@=+~T7Io%5rbAD6=X*3+r26kNyDY90GMsWqivU{=5~` z_tyKB33qFlmEJB_`Ar==<**3&9KhCIP=N-8RvjOJWKR%!Y!g9-r!qw0nqDcVHyTWm z=j3PAuBA=!K4m+OW;>;N^L75@B2i9lAOc=!Xj#3iA`(?voPkWifVbLNjA0LsxC+7Y zquJ+kVox_0Iivb8%4j&kF=;~JAoFE5gyuVu*Qn^MklKZ1hI+duBHV|Nc1K+Vh9NNs^x+9Im6osvIX^ap zR@7ju4w$J>+PKtUkho0W&~>t_LLu$2H;s=yJOE}GwGyCYWx55*fTP;wvKNN+*I*r{?U&^`Cj_!ihZo+zpLBR^Ykw6eU0 z@vUvJ181!}06n_(4ZRalXDx_9>UemY!9Bje6N5@iEQQD)#y)&b{sqC;CrW6W>Call zQL^?yffWahh(^eyf&!;S_F^z{REOM?W1lhZN&t-p@%@94%wcHyx{EHd(pI%bvi@jU zvj0WlDU5dL4$-p=*8itWqKd#Gr1W(5|VK8Wd%IcWr zdyC6sW*q}9$kIo8q}#LeO_$X>DG(%3jpvROgs)oX zz%PF42CDPQkD!(uql9?n(WvC#N`T_g>@}1Ws$UE;gjB!nFMeEZ zp16CmCGxa5kGeDG9hMiKDgjRp2SJei;2$Ai3&6$)qy+?OK^Rj;zHjjhxEjZ?lkm3Y zs^FcY1%E=6<1mD6!WpsAVcU|bymPaBPk5HP6nzMf;GXmLMw*>D2AhO_Zyn_dZl+$0 z?i{(fDl(Zj(+AEx`|paleFm#T<<5z($jl~O!e$Gu-zt7Jwx7a8w(O)?J`tnR9k8Ex zIqdFsy78d6HMs&-&45&AxQCCv==JY8tPx%By|VVOAwTdPjTX4pA&jJM6J(Hj_IyK? zrV%Cuz!Oq~YQlJEtCTd_ZaBR}3x@GMa$dstwwV z_75Up9R?369Re}i!Iog-hES6OXK`PU#70ozkl<+v(1!2;07xa2Seqb=f>8>JeLdEP z)eu5sFdEdp-T0ha>zR;ft_qKLLWd6A=X{EL0R{V$C}JiS$4KJ-L=ld3%6n;4MQ^q< zKo4#IU{#Q$jJ4IV6Ta=o$GACJUAwgw)}c7j1VNIx*a(qGyUQnJm82!^r#7+tD`o9T znxA}#&=tg8`aDqU&qKkZfjvX9xWoVa3$_9@=~ifE98}E)l0DIWQkSZCPDbB$}lMcH3f9+QOB!&ap(dt*u(M#6EG5mwH++7 zb+1MO-l~wuNH^r{%YT8<1v$0Ns3@?_{aFW>C<2X9-$1rkUVU<&+^_mYJ#!`2- zsgg=)Ds?Oq0WXjZh@|r9*P8tS+u)<7-&;qy9(1GXONA&rA=na-zTTl<%&H%<0)ypw z469J-_{-oGUhXY_SSim8J!dtsyYNvtt!vQm)e#it<19uF8;K?<=VJhFmv=^)!xy!& z|CVoPuleXh@96wL87eM@LXUdguS2w(u&M+&0_(@45k`WDG(iAKlr08!JqABHteX$lJuXJdl5e6lY7s^Xo0MxoWCX zyC;m68LF(TCRJwKL(w`$8R6s&{R$(cVEEkbVdXVMa9_w}gzu+2&{GThq7PhV41ys; z2DjFV^#%#zVKX3ZC#Ye5yPeiS7$N{>7_~qQ43BK^>lNHTz01&N1?PeEx5(n$c{9N zOR5H^xbe8Tj}*>6RY7KG^!=|4QBG^5_d}tst^y(-AaTjMWvXOxet`R^4s#e3DZpmlud$EgoArt9ns|02qYDn`#TTxD)ojrh&$ z89;GOmq%Nhta!!_Bvu1bUW%|`7Z+hq>-d^*2lq?n@!%M~K75r6SPE|p95x4q%T$ZH zAw%;Kb`sv!TzTRkFu1Sk8j%OTJ7#RB-QdCYfpPfDN=C>yMVV%bO~Q7W;9MJ|b?$7w zCg?L*uSTz0y}YrDT_cSq+wCFbyHj`3Z&9pdZl}$lxb>$iJtKbW1FDUMqIdiaajn?7 z>=D0By)T>gjL`+h0~XW7s^f$fwBJXENs5xeHVOEd5g4KraD4LesmmA)d$&U&Xv+=3 zDC{`B=HffL+KW?+l~v8x1d++9&7h)4p`E)}9L5s-?>>M`4WYj|r(JxLwT zV4PLAm2?dH8xf0W^C|fb4(%T0l{PWyrd{3~?5D>2nBmbCXGFrtxzC*n*Y_4ac0rDvgl+eCCd~|!&EoMYG@nm` zWrH+b46M{bC$v&HO=mNr3k??IoUBjrcX8k-$6x;bbvtzlyLz9F?pUqrBH>cD8D&TdnlkyUUD+w1IuPXjJ5j4fmLw8USo`omNJ=54`3!vQpJoUphKztQJgpncSLS3>4yD6xh1OYjqr@Cu^#$KI zpr{aDpy0EG2<0GS@EvlWTHn!!bC#vaWtHU2NSyy?Dsze8ArgOp5UDRTS^t*okAin* zmFsRY*3u>Rx6`xaGKN#lF@x0B3KPq%N!)Zn#Z@mPHZ_5ab16CU+zF0Fq2+l(wQR(c z?LPwu_Lghg4<`!O;9=Gr-hq!Cbdy(p?+)c;J?_ZXT^F2(8=WL)B$7GJCy8hb`T}!* za!QU8y`Cubusw`cex5!gV_hc)4?aV(x>X$SN<>`zJl)^}*GL9A@Zx;HHQwZq+nuqK z^XGq3sHQ}KaGUi(xaiaw@00y7b9NmfM#-hW#sIdgc8#ye@AQ1e&e6 zya3(j308}|JUE|Yi8y}2U|FwmKk8?*kRq%V#w8iAHh&Ns{0B|=J5mzD>g|9$&ArFG0B1v%JqPd<=WqbPA?r-In-Ug_}g~OT^vu_>+Ru3 zW2c7V^Y!&RnlH?uxafNGbT=n#Q+=lpMh$LIK?qn-nIWzxY#!P7j97QzLc+v9tvEIhC&cNGP%hV`>7e}@mQ1=( zQAmrpa;H%)tNLht)G7;2*ilkE){6;vnsI2$^hde$y-MjV#rB zUq6NPZEKU;vqIWL-y>@_@ELGN6WV8luZ+gA@;n6g>b4eR=)R{tM&NKi2ta5})I1zZ zM1yyWFLgPhTr&lsDHH&?XT=FPHt(k>em>Ai2NL&uR2d?#HU|>#6sTn zsh>l{LeQ>F^qsHE@TrgMi-y!5G!G4ksSJ_X^JQ))sMudZ6+G~5@3Z#itP(q}$$6<{ z4F{@x^xK}y2|e$4BS`DhhAM#y`+q-O*h+v)DBU5mX?q=Sv_E-~k&?+Q(_)Y_0>Kh_ zj$4S;@@9(-QD|);N5$dU-G)= zKv8zHSG!8NXEn7Oi=lmh#_LRD`(!A5Qp3sR0(uY zFOVcr9**l>PUXAnrBOvpDPFwK%41dE_|)IU)k94WL6^>? zbrp_mb*Hs-xOyc|w!W{L!f42H_1dr(S&Ln1)0_9S#0X4%qp$*0UxGx}8IZPP_HHL^ z8te<8j7euJ_aQ%cPpDHZhq@v^r#Wb+LAO^YXQ48+z3eKJwp*kWCL{#9#q8N7>KQY1MW(sf@~!4mpEs3@g26!?&e8ow89Mxk+}} zjaRG5N?(bz24ovd+ifqYfQaC9Vc9I@T@pTgtJh`=pU{oZyXBSzGVhMkcx3 z6x2*ye`Iw%OP?e|wjp_WvuJg>s<}7fDPeb`>`${xdD9JVW!`3fX}j932CA^2+-BGk zY^2PI(xg0?zZDAgahWEi?u!ZI%U6vr!FjaBGQysnVwdIj|A6EgtriNf?!%@(ZF1|v zHX@6`-e%EdF}lJ2dm^%-Hv+DSHfVrS2*@M-a9vZsrlD#MoDL)rp(3#S8R$dBrjo2) z3lY0FLpw(_Aq;|6-PPnp48mxbaGF&9+tEQL#0OdWsAj7_2m=B0Zb1Q{mHIQLnX(vV z-43=Ai$MyGBAmIk4`@@0o3gGrhR72)L_KpmVO6;)37H>M!lV~&UKfsZ0>m>c>~j>1 z$aaB>xbM zIpO8hzksP1<<_mAVAa!g-M|G6F9gs67s`M$VK`h+X-wzw7veCx=Q%Eg zB+Rc`?B(43C+qC^%~ATC-il-BOc>+>PL;MNKmOIROFe%{ksB(VBE-Qxtt$pnb}R8N zr_X%x^x2i0b`p~cI416mxa5|qpET&W1&z=&x@siK@a1fmG!2^JOM&{glLlwOE3wyf zUFPF5mpz~QM){d?u*wVo&ZeysSP;K^R1)zwu^(Y*Kl3SG1uP9&`X!?mygTnhv@$m}|$N7VyG% zA<=ERVBdMoUJ%)RCT+VJQJ-a)L#_Qxec=Q?B*9b2yW@MT4@n|V!6)yIz94ekuNSYG zU{|NBg_bCen#IQ%`@{cq)}Q-MJtwIQz_p?*DDJ{W5w_i(B3(}3EAGv!wEkR$&5dQc z{`=mj%|Kth#no1hqVKJ`X`OVfM&0gH#8vj6Dd%~f<{RKDgH2`^sR@~7Ca@J-tk8Rho zLWiLgJ;dnB@ZN%e&q!x&a|z^u(Ul&=)jNjwkbNca%$p!I;CtuZSJRN!GO?NU=2ng^ zwO2}f_QF2FnU&MtDm4MngcO`v5Nv_a0puv74JV?Mt41cs28zQL>;0X?`X6LyWgpHriA zJlg~n_uKkc+%uUml}pfh+J5*9@~K}7p7-V6f$orw)eR-*5bW@(j~V+Yy7ECy#AUYy ztBy$*!r>Ef`3^QU9jjNCjw}No=KcgLbiXSbpe!F@q3N_pWc0&S+Ik(OH820SarruF zeE!3jl8}vPr$M*D>=Vh3KFIJk7TfGasjEue3q=?fV!)L8qOnhW!NVWyVufI}X4%*N zq;Bj7fRqdM)%kd425SuNCIZqEnv+gLshIvy8tfHOhXS2K1SGaL|C#`9m5OP-zy5I;7oGDMd*rlSmYe$)GR?JAt$vT8)y5>MV$$BnwiZ7$2TI#nwrR z2;a5Bjcs%f*{x!Cp(c~5TUd}q0E&prCJs^J_EhioZ{VJLyVvq>LTHUWieL}6ezf=9=H1#Dv&IR z0+^26XSw#V^HUeoLfToPtPCj!#B(B?ydhM=YlgZ>eK;ViFw+++n%a*4F+sRE82qN@U`{oQ{+h8oYi43(js0iO|}v{K$dRQqpIq1XA&2|P`Y zs+n@_x4uF&(%lJ9kmp>9k`p&9a zEQ?L=PoO}V+FGJaZ7b5tl}(x84~0VY{=}~MZ@MSE+h=8w(L%eK*!JR7ef8S88{&27 zOu%v160FHi-RCqmsBTS$Tm!)p=)=M70FL@N1h^SZxN?oa*GOQ>@^ROJ;ROE6-hn-Q zie{?JI8sd5hC4MKU4cC~uS$gWGW2Z$1}rbPJIxm8HyQ$-)#OlC<<#Nfef@ z_Qs0v>%7)nYl%iCvNZtQI>~-|^mp_t`*IAD9t=__SGt6dX zzP4~3Itfyn`XvPYwll6Vj1B2QKZltR?i^hUS$P-jW>%eD{&am@(*Tq$)5?mUj$5C7 zRM0YU_ZL?5nRVDW&~vhJ8-X9;9-|U`q>o_5c16sFC_x!27bDD%aO-Ro3t0oHO}!S> z*xd1-n$fin&PHe~sGW{gPglQh$oK?ak{9BzLDuzZr@<22hZFRjQNQEB%ODqUs-Pmc znhwRzqp?wQd)giv0w?uW&{mL)0o59x7f0JqX=Sa%q)^Ot{D!Di$&ExpB^98mhKH)h~)CcO7Kb<*oeH5t;qI1;H5n|j+`qZ@w z2HqYol{|++aTHoMwY@KNTfKpQihWlyF9CN#gsfCJ+?c?0DC$)|hve{W*X~3XbdhRw zaVF>aq7b`syCurM{YvH?0=)Q0=7);dX>6D5L{jo7HH;7n4ulV(BxY^KT@zKSJ zr>$>j-Bu@0jwr}MeShoO|AdH`*=~2J4{S=T=_Yk%!Wd)Q$NhShXHvODM1d}br^1Ka z>C9!&L0SuYeO6*yXZ`thSg>Q=!q>l;j;}yX zq})+xxd_ZW?yS@pz>_#|-tSSm@e)QgSA5r@JTEIk8t(hUCqLfgn*levrAK`qUE@Ag-FI zYrLRnm@O!AO%V_+^8l03Z$-cJ%7iB|_m% ztu|Xns?KD{xRckHDv-)BMnLqGE(W!6KaojdTNTGAF3fcodgr=}YD}8^`Jz#Q=cz|K zV_+xBl)LuMt3fKHk4d*KsE&&ryQ8QH8f}^Ko+_s|i>Fmu*%4QfcP_R0@I@ZPl8@PD z`Lo=2we?ixf8XZT9&ZMomvsv@hM1?^_C^S?t~hN2G|?e z`-g;Rf)~toPax$-(~Uo$IrA6lUA&>2VC`({GVAe9Ua2nw*{bTo8&ciXq6QO3JUX3w z0J`23dF-P_ix{#Z7FGI+?HgPbe-S+I)+$sZhnuUqi`BPF8i>9OzngOiagb=B2P5#d z^c!z07TZt?2(5pFJ!GOzi@CbzFc<>;1_1iSBNVEk)Af$(E-0|R6&6CzSab;hU&_0k zWAyR(@nxy$DaUW)>8}1*vu#o0CPOViD98(1&H!06pK9AVfp*5l)~XmSW@?nSSJi&o zXT!I$W{@DXlv!m-E=k<0w6sIo{K#1X)NPpfXXz(ZdF&98sG996D<;!qAZ})=e&SZy z!oNStt{Y@SX4Vjbv@yL5$zKD=pV+Rj3)$&4$u8Db#f2bInQ_W*$ZJ0Vxh3&#*(VTl zDdQIMi$>YifNCJI*Jcp2cg`KJLgQzS5Ti0VzNms&FL$LSnuI@00;z;5uNu$%c?-D# zW>WRE1B$9Olqt7~(!;8JcQ?-E$wD>M1b8Adx7Cb$zZIDm(i}!(Z=g2It%?F7@13hG zY=Q0@m0*G|8!3fYXq61*?ad;VZBfMLh=_)O3UysaBr%4pQEIinT|JCA+p^uJjxZgj z>)A;Geqk`&ReKGVKw#M}$t;OPV?BJ)Aku60YFOD-Pq~%ra%!~uD`Lx1RdIDsOPiv; za${H(N5VRlQ;&kE#&-U_S73EDzcaW&p z>W6Mi_^<~GYspmwW82?Sf$uA?LOuDh+pcmPAFj&Brv($L2T=e5@9oQPxHMBV8fw>D zzRYWp(v?)B>~-$<8*Dh~Sr$-=XV0-UL9%wfpR8>mQ!Jf9+LrfQ#ftob#bIDgh_T4s3SzC0mMbD zVr49})f;fzjP17SxW0-BuSYM{NQ7MO-ZR5H{Dg1l>BQ4EFQJ41W*AWS-7|2kAoNG+ zohAtEj`#YxG_iC0m251lZR?0_qkhW6j32^R4Q3~GK_O3{$suWe! zuJy1*N$AW_d?BD?B)r`gLMOHDrk!Q7mYP<%M)0)T3=Qwik?z#N<0b+iPgR z%E~3m7TRZ=Z-Gb!6$d;d^-Q=&D=$KsZ{H}u=yw2rZB-T2ze8HHJCU4jkk>a2qEt4f zwNZ|O5l94?A-1&fFIwY{kR>A58IljpHQ#>5V3<`BqC4;_Z<5+XVI0S3#*&ga^UB)) zMzXHhn*+;DwT=p=0=4?BxFTTh>LqAFtaqx9(~v!G7IdUqDIw>)@x=@z~J3SA}vR^IoweZ`uwU zq9~5`Cr`I7+%}UO{`U=)bN9)9b~kfwdN?Gcln#<}DZ%^xw&j9!vb->ADXC!jN)O3< zs2B>_!Z*!epc!-ru&~cYV%h1Fn&R1KOW*$cnZni_BOcUKn#Nd3mb8j}R}&Wf?Ij|b zq5bGXLn1o!7IYEzFUjxM1^E$liT1_c4l%#vxt?uwJXE{2hIjhEF>^Be@UY?QisNyX%Y2V)xEfP^`8@y>;-z@h_ zi))c-%>G6U{~epQgN zQc-^zjZRYT8<@?0Q9j(!1x+d$qxr3qHQ)P_xQR`P-hm})Ly>9#BSc?s{B20U9fm*< zB0?a~eixboQ=qyq6Z5^H#4R)#4BZae?Euv#5ZfjCI)88u%u!b5#U^#k{&fiIvd))C zIJ#Fs@!sXjW{;Arzm~;vSVZ5$6x1$)a;gJ)G+t`>%nT(e98t|W1eX%@lhQMl5n0$Y zT!4hTCfspe8*;ie7M0M?PO=c89gHlgpI@NKt2R)R<_mfs4iUE{v zg$i(-c^#r3w8dUy$g-}61__B5UGvyEA?Ho38jz>*AyM>{bC9-eAqo+@F>k?6s6@m} zWrj*bXozErcJNV4C>oleTWO;_RbrQCiJRd1y>(eBe*fzQra%qU)U_r(-M+Z#1)E0y zo=+4e!X^2SRNpAb_bIXLHPyM{4xFHki_GJJTMpQSaBCRu@aa#i0c=8$(}6M<*a>O} z>fFKQR)`x&#nk-0uJ!YjcYbP%aa?W7BSJ4Hb)$fKP2Z+I7;{-;Z=M6LjyKS7%&+Gj zbNS3rSorOgk#5nFib}Q1{xi5h0mplOeS(^Js?U@H7Zl3Oybbe)7^=$ zSl2FOAEgI4NQ7(h?pE6)9uW`QT$FZui&uV-JjyY04`0V%K7v-mn>Bf!eEzeNrhsvw z5QxHyiAn%bzw%JD8*$cLJ-MQy^LQgLiZGa*FSn+XFsUL0eK1#;6$l9$*&D9^@RaNG zo-m-hHGe3fW7(;%bNn^jWkCZdIA9opM%{C5eXu-ERav`EUlq6pbxv?=tMYeD#x!iB za0dg7WJuNxInG9ky3de@t#{y@bsrypY-CC5Y*)7@Grq4p^iGB=8$=6BWMy|kZ}Bw)1$AfL&gnd{)VD1dzpk|kFtwEDF*J0 zd$@ezEhsleAnt{n@GmbWR!M7mA`^wG{xlj7xbAOaHtQy@nF)-%H9Mbpag!;Bp}nrW zHjtJPU-1$j?8~GY4_+pe?b6pRSbx4Aj$Rqc+u`aXsDy)qa%j-CT-emtJp#n9_o}gAL&BHw)MDO(4?;RD5mVWwc}u(#E7F_U4uey~4rGs2I73U}U7!XMI+0Vevi76g z+%XImp_;oNvV!FPO3eN@pHc|@>P6ii%e)2rrsGHa3$6YIEq?=eVfzzn^e2){<9fK@ z!b8jrfQ}8Uuip08I}ySwO^H%>mr$eFlIw zv{ICe4B^&e5;&^Ee0|j&V9wWEgrKI1bfT#OsI-aFtpxWI2L3kGQadE-vb~(6BF={w zbQ-9K!tYt9Pb=WH0TdedRd)$m+CjO^QZt3WbBu~*z%0B8oZ>i7MoypkXx63RE|Nw*mrOPl)f!uBWDXz52JJC&j5FIH3xmJE~Fx8JL5wFcQA_B z0qn7bLe%!)uH19Q|4tbtE+ZxQ{DhQ7a-YG%tgr)kHBt@S*N94XOkZ>G$@jeq>aXVP z9u&8hwf2kK#acmS!i1KsfFR%LGkvuD=9-1S!;pv`{|<-yrG{F7SLtF0 zGRgj8uqqlk0_8WE@YeuXtdc;d1nfr+PGd%pIliFqm#>1L40K5lh7R=-3`Jx1GKK#$ zXCbb%>|@>^KoI*T!v4o7+EHmcywZ*t?wte-s_ZvV^1sA#wnVtB=r)K&-b|;|VA--u z5b!_A+*$&(zb|paz8uENeTiIlx)Amcg&uByh8Pl|ueKPk5o$94zKo#(*u9!wf~g#n z!2U~BTqmST)`Vx@T`qhxjoHhL{r9{zuWiwVzVv(uM!gYY0oxF$DyGgrZ2ta+NfE4N z*@{0k#hS}FjL}CAOJMQAqFfCfuW7(tN8&chNeLz1D5fKX9$N#?>ktU+i~y_-2T0=G z5nXgXm&%pzQTq!Au*1F_$LbtNEqjKjmbi~21r^2|p9aKQ23#OTnYr>_e$(J|u>+W7 z3sJExv<#q71uZMP5)9>w_*FlY)dDH(`-uKzW%KAN@DRPUn>V8hsE0A{hi*hXbAyJm z`jg6LDaF(mJVDJ8xeIh|y}h4!GW^|x>F&UD+eiXaWEFMS19{iBlTq6 z`T~FRPe}!y-V)uXhg*?(f0+`NK+u?o_RX=b6y&QR#mRCFTPHhVDD$t%XVN;%X@ZT2 zZ<0$0+0d`7;QmYW&Lq{(;*OP$tLY!0nj@E+jtMUl!$Oz%kb$lYncg5lY%CUHcx||< zUqKim)Z>bT7D~^tD?0e~1#*R_(f1Ch;RS_aQ-#P!n2yWP;RdhaF?)%yKSM^2)73-A zVFjhhJ8%zx$NMzq`Obo2f2G`bVexd zSB2U!-;)NS;o}Hy4u!x%px_#{2w1PgEVPPY1%h<6ARNRqat&cG${R0zl#RGAmAS2h zI{fv2h6j~(9{_UGzN(AcEsc=zGF(<&g^HjVA&_s@K2MSFWcZoRLlC$>(fm{%p~7*itkn%D3V*OW-P-&B=2k}TQ^(bLI1%w z1XqAF5RVD6+%Hp^LCgrv)Y}b2;1aivZd@=@BKp%g4|tdW?39@L~|BKcp0e$$yK}Ij}6I(DXQftXEuF><)<^)yIeis?^z*% z2q6A4)a!p&1J6u)J)rYdhk^#<8PytKSc;r z&GF72NTvybXUJv6XNZW4fx^rnS81LHpx0=kPpBYQ@c2==T?Fwbeg+bM&C4$Vf|404 z^r03wqvaC_t~vCFcVx0`@*;Fj8fZu>X%|q0n#f8e2vQ-TYXhu;X7td2bPZ+#-V*h_ ztdC>PlXmP(QlE^hN&ZA**uO?@n$LrOqkKsWPkaQ3~g-JI|tLi+aK%^x^1m7g>eE{sQ`fg(kcuwS%?-e~B$LUNYta-CTc{ zYUdd1-$i3tSu9J>Z}9gQlS>4t!w%Mt-)wy^*&;5(BWa0g|K}RMv6Dp!*FKjwYsWJ8 z=6GDrai~n8N53>i!)M{{oS)>zzZs@{@X>V(W7B_rpvj|n%{jcpc}~!>{gIZxM31Ta zWoq)l2CGFmE9`oVwi!Ko;`b~j)AxQzUU)!9MY`^j@5r8>q6seEtlJ)@Y}>4~iikdCbAPjLa^R6}7Gva;?bm}W zc3w3LYw%xLdLh!v3=i@%#ppWNkd5eJ<=PEBvHPlQgJ36)vfyLisiVc-rhdE!ESY+t zA|}&yUWdyO3GL&Uk;|1GUV@_K=#kXLcRV{i#k|>EN=pH$jS+v(z0|bvitg!Yg@_i$ ztd!6Bl^DnE1sy!Wb46O4@8`tRB7zmqb6tjqNW6cRAIeX37nLPHw>)KHCQz(4-3kx2 zhSPg!tN za~oBO#+f{4YbdjFvFh^1{Cy7xOemqtbm@QzZN1|?UGt5yOfs^H7bFuLh*7YDt z;79ut%G^t1zl5f;{DT9qv*Hd$`<8P-}HjCkt3eNW%F@ zTBpem?L3dMM&e=T)#lgSXW>0Gi@9`3aa&Uyp3E{SwQ^>_I!4!8ozQQ11|N?#hWQUW z6(O8*Gcl|zX-D)r{f;wPcTz0g>Fz3he*26UD<@2?Rdu9O4=livdQFg{j0ogwlkXkv zGDY{!S<7;nqb6?E#!^0jrO9h-RmbS}i7d%GAhZn<*?5YgiwWUZLZ5Yt5rwoaH+pU@ zR*2?J6n?OSvi3k(!6=Ya_qBC>s{rCH$uHN|KPS{QVn;Q*`Lx0U*(bd^ERalkqVAU7 z<+v1e^M01CeJ0$Usal-zID3imRE2vp`-<(q+;$*A^*nkG^!qaqjxTl$_W~h(@$v;T zowL!g4Mz_w1HtsY7t%0Vo;fC&%NvUy*q9A` z`SRyexJN889 z$JhQHtz@%Hb%o=VODN|fxj(d6t3Cb?PpAFdHeg(~*%VC#WfPr5Us$`{=pGhG=BJwRzaQkS=2~KTw`WVYrr#5KCWBdu zpQBi()98|7KpwoM=;`e&`)VZX8^$U`I-S`yP>@%@5bZ%sxouxKe#Z1>JCO$d=%OVr zj*yKeFDWPeKMy<=6lcAClGiaRdug=)lO5N@@d@Mb6DudtURQncJ(jPoQHo6>?^Dqe z3*++^-;7=N0s5j{tF3LcVuEUN0dZgE9A(9(7bexVajcNb?%e4n&MTbMQWd#cf}3g;r7A~`rf}f;+i))PuldRe(+bAVU!wO zSBIKiN0)ZQ@SB*6nH?8?NyJ9L>1o@%BxXJ`4`qaTm{J>^);Ba6>EGL2E`yB2lae%X zWy(=EG=;5JRHS;GlIM-h(G+&|zgO;?l|UcseLGcRpD~KE7sR`FG(KAh*}`Yb@1SZq zBMH%hn^Bx(;lHaDU-xNlL}L-(f~Rc{H3rd2TJjZ%@)%iat2xfq2WsK$Sn?GC!u#nq^wwAhRE2EAbg%+89yy917l zpU_$N;*jL6PSGgW^xtw-aZZoXQRj8J94cp}x}*Wk**uh{GY^(T8%I8ak=cC@BWbIm zJqp)bPgtN5L8jn>#;=<=dDXTF{k~nzzGN268+J5&6lwQ7wArNxBa6CwPle~}gj~rc zv~Kkij`wW20uT$?c(4kNrJHp=l4Q}?%fx{k6T7cpku}$inIB--ucN>yGce zGGtwy4cKhE*A9g0E{`?XskO0_>b@y{?gq!SV9E!M_-GPS$e*I)hxS4QhrI4(jYJ?K zsc>9azE0ab!$S4cTwHYQQXDcJ{~Uf9J@xhG_M4LD@@?;V#Wn`UWG|egxhjjtnC)OB zzt>%`ScM?cIsI^v>>r7III<70*}A)l`<(;#38mVq`;9s!$75oSLsH2XmZ3;{UitA{ zX0*bv1&doB1M>gufOhh&8D_4ZSs-O`Gr8x!f9t)YD;Iai{O*0hjAb;}sCAniY&{$T zKD=y2i|xCU{|9{k2Ymkrd`15QzURO~yb1W;Tr@M)Pz&%s5d1$7+z%1|4+Q@Y1pf~N z{|^NJ{{RS1c@h5p9#sy6Ep?nPN@D-pSDVd5DHg`zvNLp1H0#l` zF$E+KC4@rh@He0ZQ%W7G=iO9AY%67qION>pFD@9dJ-e*btdz1huo4){%Bahf_6IYg zc9m|^TAs>GTzDqh*&DpOQvp{3_e&wv+^DWLjqHskCD8e~WWO%o_<;RH%(!&N2r-WH z-j4IlHk)(gmUclwv;Ui4*%+%;s}~p@dNM~v=2E6vXxPj4mP^M=(t_Y#o)XV{?mE#& zPh`Ef7g(QOm@BDJ|JYro%UL{^#O^9x3S=itdHC@#na;-KJ3b{p^R5y{3ifx-pC>Bd z=$x{hAu`;QFg1D79+-niY0UZp6hCXpJg07$^wJw=BkOp>o=wkGoeV6>Yo^vxVP-Z} zf>XCteuY4h3&Gu6u&G{t%M|usY=_%CY=(9EVtaNFCAR7pi1MDI?~jzJ9-`lD>RqsXV8jCshSlI`+rZ~EJ9 z1TtXH9?Mg9r(oWQn#}hEEHn{A4&>0~#*bH4nc6*f7P563D(!T^-4Ce~J5D|6^}Tm> zMlp;7iQxNB{gY4Io#M?fi`zGklG}eHtfYoH2@(j{ zRuJQZP%#SKw4xPnFkiTnvcJl&@M@F!&l;s9_dS-qRF$lnQ|z$lj=geJJC7~+u_ZMg zxXR+zgB(CadBr82Gs(!ws^%vLkxj=lVdr)~UTeXa%#x#*7A8onPgMZ%Q1IvgSW9%@ zzkVxGj>FH&+vnh&ixX?(6&_z*dRPp?z)4T=46dm zr7fPDdYU(?d6Y;ra`&xm@nPNvH1#Ys^$Jy9MxFTN6uK#O{+=L<(Ci?%vq@I=8b@1CY`dn8D_Wf*oFsi zuUFoMy| zZ}&POMT!l&1xin>Hj);%1@6za+EKG+ul+n}(icehj@3T^;kdUQ-Q8{82dsji*S%52 zFZVEx4d@)ZW5HQ>QRjk$H>+5G-CG8M>n^Rt_C2R=MX`FDovO08Broecb=`4d^f*eq zb~|~KnOJ&GjohGZr^8=*KR6GtvY8vb~u%VC@*cC%N#+!pOINC9p&(HhyeYWj06M zIaYF4t&KjISd}xr2gJ(Hxu015tyfA{7C7#>Is=5*t>AXnNi6~?Q zJSXRxh%mC{sfyAmSaf@Rh($>1aCNpdDcu6vT2mbn225pmqI`vAQrxI!b5YX*BeBl- z%Yt;lS9A{3L1cG#`X4?(Jthy~AS)I^D9e_tk$S}>!79Vln5Ala4{ z@o6~}3Rl;ZvHW=2)huh@c} z0v#p61&7$|+G>dPDkwT_=5vMu!pioT2Pb4?WC4gg^@V)5BW>?W#V_n4h2J*u7BY-MFV>XP zEPwwgvW)ZDCZ(ALIcoMbcOZv$pw!oEx>7Dqy7tNcE2|UTqg74b-Qn^qG35lw;|prn z-`Wh;^Qlt|We1k~LTku<7nWXKFLL!u)`uB|{Gy1Mq3oSgq?pgz5&H&Q)*rA@cH99) z98+*7k@8*DTK@k_TU#*f=%+J(^*Hh?I>+vBZvhk6Fc(?vtEqrJJU!uWC^ z`)JKnQqT5^Ow}t0roVX|fO#4sais&5)2{eud*ylf>JZQBKZvC+N#;g}4TTR&Cii}_ zt}gR}A>x#iRq6d#ly3v|F|_61G`N_fmm zu)Q(wzEXbhV(`CMJ>-tVkaqcd0~jTWOL7UuoQRV>~er2F7{a!YyeKI#xJet)N8vK_;Q-Y}4gOwp0Axot_(RDO%FUlux0v0lf|@0i%wM7` zv(su!9+O;xYZvq1A=Yu|eSdjCIa#;+Y})sGI7$zbnvZWJCoMkXLD>g+y;l|CO#=Q< zDW1Ih+lSK1n~Cqs#cd#Z#5jM+6sL9!xy!L#&!UZ#+USZD`jtMNd4m`Y6mLNoQEOGB z)a~DT{=brqBi#W7!=BYBH5QI(9u_5ke}72@ys<_(GB^;CQ-MTu?^c29)V7g-?#sI}_)h>KmdQpvKe zMdejNh`a(mDp0_bqOd$s@VVpO92^xqx_=fx2_o>3t-09xA z?{7D9HYijf@7|guWBO~S=cVl9z%g^X7OzpLoMHEWD5bZvxBjx)&A~PqjhRu|KA+d8 zzyEd=@W$G%%{cDXf5ll}D_9b$_BuMN;&Gr?^*hUJ^xa~H*Q77^{M0Zjp3?QTHr|zeC(_{4Li}NyAiHzfU6oHA3}u$y<}qj^F+&V03f#2S+~|P zP2x&}KX)4apZ?X4kGu1%@j3x7Ve5W~<8?Gx`Y051d3fy~4wGddz3&miJ>Z=Y^@DXL zjvoYdM6l0KbdaOW0OV}U&N%BKi{tZt=4ioV0!q_8)A9@o0CsTbqnA;XQ(3cenD+gB z{&LmD6y}wn;+78J0nIqvijT=v+o(WfKazCWvzp+2afm|^m_X@&452`mkOxvj`hTp3 z6MK*ctZWZH$kBiZnyy6JL%hm*s1uL~(E6(7o$mP(7mkVnHZ^l@dH13h=>iUjSa~bn zSk*?>a`1lVPs@;8y|AyUgMco`|A7%Oo06Edbs8xXmU&JyE$|=)W`zGQRj7QfGffec zIa1A-&Xp@?y@H~+^Vp`~4`QK8;UO0TX%Dp1>`}!W>zBfeHpEV`Y!HrkdB$&aJ2P&w zHB!WO{3fo@+&y&zWi`D-MlVs`XM9+`xw%8|Cn$|`3|QmIw)FR10N{Q4-j=~f_Y0_i zAoiEou;QyWsPCr~X8PAqIjjZ%dGPl2%0^LH~l`9 zIU}Kk#YM)Bcw<|^7GXlYOpHxJU=KN8a5s898ZI)=ikL5gluAc*$lwfOIk=HQXC3<} z6})B4$TZ0$x;BVrf1v#64>rPQIcOMb6R~rzty!d0`^yH(VtHJrF!caiD5@L5xXeuq z)Hp{T+oKMm_Ph?)>n!FN)3;TC>0xC|uUP+>d#&&14Z|TA^SV0;{%GOoW^kc0Roo=r zkH0Mg*OyB&wX46Xm+d+^i(tw*qGy7+0cS|F8h|DG7QnrqAZXILXEEvB6(;qhN$Icx zJLh+&!dGI~XgXS12mzChfqcReo1fS={6YSaSH233K@!&{_pK^cg%)rNdY`W+ZDGR)J9oQO~4 zi7u-$q`WnbHs|$R8n;tX70?FXBZ#~I0TkRz;5t?;(Cmag_%+m!tF7m=?p>D{t*%Gk z3)aqeGD)aF_B1g()Ct$mhR$sBXljtA{n9ocjAvtmO04w~$8aSqJp~v^C zJXw+G`4**XF9F9%9AyLI{_kPL!xT4%zw{(4?-bGF&J7A?%`wa<>3cp%IH-iI2it4e z1PFhY-%~O@@ZPWg-1#3?L;cMJ8A6}VL}eOq zVT!f6fu8_s?0W=7aB$@sC~2X>o$MI*3-ZhW6UwTr`k8$8psPltK2 zTtbRj3t@t}9FjdyX}hCwkYw@6D2GgqChuvu!+`#4*3Q5{sxP4}| zVw@O%2MW6HpiR834Bz6NZ5e;3PePLR7FD*2--E%)g)^9Wk(jx#!=^x(urb;e8$z(Q zL?P(HT{S4+ZH~st3IqdFHEN`>itQ~h0KK}L(b@bP!-H<1?-i1{RBa0C&EWtPeHz`E z^aGzMKJ1v;KHs`w2W4G4Pg&?M4l|}N>{CczFT!n4hcc>3XSX6xuXOCwOh~7QO@6t7 z3n+cPwt?~+-Xi`gK(mT=Cy+AXM_kLz&nJ@p;Nq+XX=kn~M)_14Q>l4#{FTg}IYfgB zz#r4sNFAe#Psvgh!(=Nyax|Kq-=mb7RMa_PGaEZ5RY1kaFfXDipwIZ21L4QN)+hgE z{s-!1{e99#tOtuetBIwcyL$t;WW%U( zrDe)wK66|&>-+<0g_2Rw&CyY?#_}+OU%o@-i{12am(k{K5*HFhK#~OnvM{N31{0Bk zbP9}yA>On`QO?gFK;%jUK%z~9zm5?kN{*%W`500E8~L_oktx&e@)WD;)vFiUKsFAR zSu(W;%wLh%OSvTfX_{bO56nt~MqrRk)Dx>t!h{QOp-@`r#(VN1=vZQ@vd%dD_Krbf zFx>xrLLyAlsCskl>+Ni~o4i)tDxTm`=97Y5vuiZA+l7o!uTqSw>VOW0Cfi+LEl;K7 z(dFpoo26{AP#dQ?##X9=CEi>n1PN}9rZvf#9l|R-637m?)X`|1GC9k2Ux~H7H#~ak z?T~G;DOVL;7);U%q_a6G8|=gb4?39rq$|FyH=!YFtKkJ~YQ3&lwSjmBNpraJ1#P${ zD+<-6a?t%-1!=Zaq`DP%hhLPp6$lH=B8?fggNKg3It!%fx@)=LVS0!)3!^>b1?!kc zl_SHh%ja#ScE8Le*$?$Hd07*Nh&a1IB!$8_^qI|VEK6opi8ym6M2dAv#1j_z^LZQIzs#wg#-gV*vUD;H-h+Irx>H$*C5 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/background_menu.png b/assets/backgrounds/background_menu.png new file mode 100755 index 0000000000000000000000000000000000000000..b0e93254ad539d9f718987eeb4e8d7164606de1f GIT binary patch literal 51435 zcmb4Lc_5T)`&OMwsgOb`k|dursgN*ALR2C}$P|^bhGd(eQwmuoB`V8^Bv~fex6vX? zwvdr^vM+;aHe<&2d){|Ob(Zg(@AuE?%y{Qr?&n^v`@XK{4L*57Z}rNJEBW~NRv*>Z zG2!D|3jTL-3I8(i&uhcAp5UKF?k0NLe3|u|M)>%)@Ez4TWa_vRzF^PRm5}Y`*XjmJX=~VZfI+zcb89XHDmGMUPjiVxHy0P%R02**y=aG zPmnKc);)dS_^7A?xhn;WBFIVWT#=OY_hFM7j8Gx%lDRQ;asfdCrG(EI?Vy6sWPvX3 z$CDVTHB$2To^3ZLsG-S|mQK+a;_euuFNbkbw6Jl@e7RP!qJQ}GfO2Wc{rK}mE?w~t z&a(%ol$$j?dXl*68X+XG0khGCGQH^seChJF3&GU2A$vsD_oaDzuU}wxHca$X6K?( z!hJNwVWXB7MtL>b6h$_6WcO!ZkM}lguO02UWr7+;a-fGs-FN_*WN9+=>}+Y$SHjpf zU>6qw1I)AOVftZ07N+fJCSo0FaM>2kch^xj)~?-2iX|vupLFTr5#aL!_`x$ZF5iId z*LE}>vTZT#aT{#QOLE?jr6`|lFVn{3`JT%?o+ze?v-^v+=lUncnv0+M`quUv2BW|- zC}1Ou2nx_sMXxObw__$LgF~AofV)A+`hmO2)X+t;GY{es+em|WK16kOLbut?VN2xX zmEBrekGg_RBiZ(7UuS{KbYXA~J=j$`+%0#Kdmn>ili zj;r(3rTp+S=rrm{oFfM z!vS%(4c)uZ7!Hq@;82m)pgmgs5*>OOi0ill-AKrTkoMqL-9!Gh^DVDi{ZvpdWTo{^ zu=~@f!79+%Yd*u4(2;;{`V!a05?A|xXW0WgAkw&@*hs1j-GKOyYoG~|?K5p4>C1TB z?$uvBs4!xLWS5}1j{0D(|6xN7cb&l>2wCWe)@a%}`gpEls(kkJE5F}#9O*L*&gBqA zTxL}z^AW>L1dAph$yPOTsMXwS!7;!MW-L;xzqnNoh5BSyjBJ>Yv2#9UVx~Lk)V<_> z&fyJ>W_>#`wh!?*$}r_gyTCg37)F~B0DdCTA*W-+eF_`r#7x8hxi9U4DQR%U!KO8r zbWrj{7aLmJH0|+@4HB|dLqj#VZ7qIJb6Tw-XWcLg=N&kgAP&dDNvT1Qjm8Lr2Wguk z+V-+jC%$}7Cm5m@g=?Ho$9(y%#;#AqHNiUJlWL$*P(0U^0mCc6DLip5mlNQ?Vo4ZKgXm?kBrT&u3y0}XuTGvq0Iq(e8_(kV+-th+tA7y8trrU9-(K^&&~Tg zCQ~k|`VTy$j8XgH45sLB5DPmZyt4c5C?11A0 z+hLb@84l4!)XX-*2&upiXFRTdWfOKz-!~#;RNe;;n}-w_+Q{ym=Fi=6i}JzyVZPAX zV=UvfJ8@W-aU_ixzwHs_DW&%C=2Nt=9>@R{lX2cY@V{L9)N4Qq07;0))KtB zE|Mm=DfKAo%xrZfE#sVF8F~)MS^5eTlF5YHpSJzwLmrajbCNKPj?cBxK{c;<8?h`j z7aMh+?TB$OXeU}#Re9EV2I?W%^^&#&kBpElTI|Fru5)^N_1kjZD9|`21P4N`am-K( zzGXIi)R!%MzqY6+s^EFu7~yG{HoJGKeE%2c$xm`P``hA6VGDvJ@eDl5sANoV2_cg5 z)aXlA@gn`>ySwBDUtHIy_Qd|l6IJt!_@&ELTnLM;EWBDy)ZQFe8qD1O20eg%203mX z{y8EI#I6fP=?lDG4+!a;hem|b%9FrE+CkR)x|W;_YTK_KPndgJfW}~w!)telmC%%U zac`oxowML)6_bw+>}ec|0Y9_oX016G6yfE^DViQ^p}p9SilOvSQ(lF9%Fd%zoAuQ| zX9qAC!g}?Djqs>Vw-+Hp<$XVGOhRl{_=3}2fRWesVKLo!HT7<#K@MavU} zYsB_*%pMOY*meCr%%zA7WtEZ5su zyrxPu|4+ZP>r=RD1ah$_2IHr4!$M`P9?Qv@FMb8K{Y%c~Y5?JmzDD0b@~k9;87`HK z#11Ho8xj1y*(<-(n_GKjf@Bxf52jr;yNBM1Qw=a`&UAJYv00~GE~D)fER-a)RZ`gA zFhKmy*INXzhGIT5nrQsQqW$khMn{T^wb&ev2EtlWQjN8upk_okX-A-+tI=eHZOFR5 zH=3n+Izh&f0E}LgQp?j<+SSqQ0M2wojls^EY$V?Iowend*bo84MQT+c-5jmKC0-z8FTu1jDUzt;#U__&5w>)Z7Y~us|x|4B!OCLW5VGwy9lu6~B@T5{BhDFw-_kKk*Ltsr2WnymO99`t3e#EQ4 z`y!oO!*v`OqxrH%`l2a%tLs(D?L#IV3840eQ0PH2?){2guk$941RLiZdSWKbGiR;% z5eebJ`=p;;J=uEWGF{QzYi6Ww&Ez9O zKb_^2n@Y{i$?~d=LkbR^##Pc|s-xqg`n-V+z&ht42T$MvPciX$gnBjo_LxkisC)&n zvU7xf?bRHU2|?Y^*T9$R^>C+jNF&R>6@7s7Gsp%LmAy7QTy$_@q$aA$Nr|YCNUp}OU zTeyQR<1C@@Qzd9_Mv%)iV~O<6CbioQJ{k@E@O>%fFMZ`ti#xR7q|SwkCubj0R&i#o zcsrNFrL~4?BJ!bouHhKLOycI3-ofxmGqis5oRKs;JQD~lpxz*0=z+(7#n1TB_lnagUZ4>R;>!1CO-Y#QSL z0Y}RwU%yS>QS+VxqZ|{Y1n%WfizeJym7~qEfe(yS94az$8$OH(_ep*c5Wk1z1}x;M z=R9A{xq|9(2n6u2pPw=F&9gHFEp4p{PRLkQVY_qz`xCkjJyFexC zE#2*6^13w|4TISm8}@Rz0qUg$HBNeQQo?9%43~&kgPTmM#}X$kvClFP1KHqK zt?O_fRFTDmk8z!tTp_z6RlYl$Z6BK}#!u@a7n6IFlldh-o*xfcn{?$iFVC#C4QO90 zt=kAN^&tkxJHLE(?GGs+MB;JyyT%ww{@i2PnxpxxFRll=ZHM6Ic^Z4lpyJ4iHuruJ zr+%Y@_Nb21NzkPdVK;_CSqk8K7;P`uLkVuOKt9tWyo8eVG|_gDJ<{&9+Z)dfCivTzbHQn! zhnBbLg)5qbI`~h<=Niw7m_2L2D&p2dj>{xuS6*~U^<9Q)TyfCg)(Fz()Pu+em4v5Bl61A!zDu+?~yyD0ypv%}#6RXdmif1S$FE z%~(1mA__Fn$)ClxfMNhAO1#TR8_gvbHo`|bFIRG+;uYu5#0ZZ*8YSf-_0Hn!M-{w} zjTiYZBLz0qo4irKp`O_AO24L(;Y9sD#W4%kAeFdL?+$;ZA{>D7tBY@=IQnr@F&6xH<8hRNRDT9p}9ih9=@8cVw(~`gVQQ!DI?4U;# zWnlWnldRvKMB8D`PAc(2m@)?kHPk0ypYTq~s?>+N+_R`Cj5%kRe26{4il|hmHoLy~ z+G^={*QI3N0Z)svCJUr1rmH4ts^nKIHC2jy)GIB(e&eQ~s@#uoNb^cigiYryD@&-} zu>cI~t2J9Wz>I3BG;4}J?H_?D=K6>%<}6cAswSWD#i}Hdajq{Hl#L8bBjUWz^yC&) zCrDCo#eC&2jPVs3)HCH7eH5ibk_=#HEyrl?AYlmYRMWlwx(wwwzD-ww^IieYOUy6t zTb#Cz9A=LVb1@0+|6_{iBAd?AnT@d9!FdROirLT#noFWYqHL8|O|+Ul=p}QDNFd~p zm>C*|9q92v(e-Fd8%9~;4(%hg>*&?7Qncg=tT>C4<+u4VZAH&N|TMN*lJ3PzFXPhIg$#Xb)Ui3E^qIDmPTm0Tl^q7bC_Py<$guLW7`mxpFspX~A{3S8XdFcuJ^HosInjzDOy zfy#XovhEnNYsqK6kj)nC0)l!1&Mj>1Pedq04LWJLwR}hUMh+-69Zc`wlo$92Obm-} z=TZIR)c88NibE(F?E3#HSsRl@w8!XvWGt9?8p)k?K25FfPI;(fw30^y~VbzGfV?~eYjJymvyxwGKCz!5&(&;=L zw2l>Y=OWtWcc2^Q56uC+B8M~Mh|YK17Cr%{Y#Jhxxv5vxA8^OAV`y%B#4pYH?5nv$ zs>ZAxhn}a=)_()7=|?>wL#%y6{{nnx7zrhn?+6IegNvfHj>gt6QT$3Pvz9{b5rQ%0 zJ7wLqC0`#1{O<_lK4*lDRbEOfIMY2h z@TlWS8OsN3rzS`B6=b0CQ2^A+aWO0gWFsB50fR85_NA-pK>I-`kemK9QrDq!lYK70 z@B87Y@(!xE))^!Lp?>N=F?m*6SW_R#kuyh1p`3?Yf4>x5Sz#9d<>V`i);9AN zosnG59W&qOCA?!OgIZ9%8iy*MxIBlGhneKwhc2w&ps_6JQDv1_<&Z1Y&CXxu+!o!k znun8faO1^rY)^p=0W^D|ah_ z$dZ2t##Z)MHtYG~hI4Jk>DMf#hQ&5rCw0x%@y-E;Wz{Itzx(2BYqT&C9>o(MF*PNiuh+hD{6o*Bo1QD87zx4=| zSuO4WC73Sk$B!qwgOCzs$$cH|V$u~sV!O9N7Pq6&zI*RKJuj}*1ai9PfD)~(SOb)R z7A;*E5TEm|jUznu8<1!v$!W{yb50`m=%Kw zEu{!AiWNw&CB{rYno9>(5Jf!~5KnYzaC*mcgt9sKR{s{we^b_i#+Su|4jyr8cHc`I zehzSzrD<6hGlVs|gyi^3w zZCTSqXriD0oHetbwsW2$`UUu|3YhVNHU{cqgW0bo1!ZxL#868IC?i#9z7*TNPKq0_ zkf#<;n9lZr85Y!T{rdSZ*(K}pIjOvg5_AW8zsuHobFnY6p~Ux!AdxhijhER)i4@$h z^+_wuMVN#}dB@k;DaYFZAPwPvstv?(U=L_*<#pRXz}#<2rF`nBiKJQzdrvmi-P0AuulXZ2f3xD6DbsUAvg9ZsP)wm92nlePY-S`t4x= zg?{iu&0hK2{FVr%)Tlk$r6#iqx4}$=(k+!4 zL6PFISM{8Ym)-&>F9}*W2$PR^d{Wj9O-GIx%si12!hFR$rnAxZs_1nM?_&qI(mDkO#E7Jg7<)U2In<^u9`Mhhs$mj`Yy6j zi?>@CoE$hkZ#us)>ER-ISpqj>wg`ELTTCM(_gUm`xbXH0->0Dg!_?W{OS9YahZH?5 zr>m57OK(1#s^`2Jv*k79q*-qMAx2-4T4)a@un|xZhDO-08I7&CX==wT3KeT<0mAi-aag_o{4fI`>_Z>pukOn{xDnVO67A!5y z^8%G^0NO6S&R5qp)p-IyCAwoe4 zuVR|cZ{>~LVw{wCnOjbO;0;Svmn67oce z7D)-xbMfjwrHIy4&JY<)-FcvyVhzSscz{^bRdo#SBcc%QUqCzS;86xb4kE>-X@cat zmc0*DvZw7;Rt|*p6+dkH>dS^2tqG>>?19?;KcaQH09ANY$l^#tX`^BXJkZqH3_2EQ zYGPwOQ6g@?0ZSz80JUpajKymNrS^jEy`SCK@pB9XrZL(<4khfB*GBqH)s>cd3hiy|>y=IJQ++>_ zGRNBd{hCjC!P42qx&rqf^Gv&d)fZ62jT0?pfMb~Xd{kT7@$<3-Azkxx;Q9jw;Yy#D zu4;*X_H!l~dV`U_!%^* z4N_)+qLW9auQfW3l9^VaEHZy^|7ovgj8W)qg07^(|CG3sh;jj6hh~XVI2= zn{F#-Z+8~s$i1v}oTZ_nTWmys$-*KsLdoF%vm&9X0Zu4!J>W&Qm=omVN>c~&^v2UM z4m%0;C;MES`UdD=>34j3{qyQJ;3Mc1dDlkhLYW%CsIYya!(`K~p5TY0$5rc#;Z{d_gE#^PTp(MbFU4PX{f;ZZerPs0Lxzs5v1pneK zi;S#fwEbktL4P|G1<}g)NdT3r3>TJi)(;Mo^?YV%AL*xO`K$Pi%#j32Pvhh=Jdu#B zYKx)15py3&InfPGY)SfJyGs%AR|}J;xAv=Oht^C!pXu(|gjv;uAx}Qcwk#rnN2MqAmV_!*(iQ6PhQ%EHp(g zRKBg>Rz*rPA5`4OmM%l9`Wil{k;S)hhQSs0ly~~5+ll*2)}O81(x}&v(efggSVpU6 z2^P!o86uTlvP};`tgo4*do+c8l`wtKYkvls$bMW0v>=gZ(Z=B3bPY}C+oxmy%Qxy6 z*obiyhQiy@C6O*j5`gV4*OFas5(%2D&7VmLnz6aKZIh$EzX4bt)}N#$CYVxzD|3IY zq|fT=BIzSJ?$m4cGi-Iejkh5v-?e#uV}}1g3=iI5`|Xx+da@>_D{0ksXBnVOY?5qH z09AgqkMju9HH@DfCiIupy3x?;Tl$KLM@OJRhipZgG4&(eSqpR%j{+<6Nf*kRqLcBH z{pVBZwU$j4HG1KV2c)vuZyr9jEJ7GKR|C1ppsIE#W2On{OllRo#ANd?0+9(Fn8L?z z1o|s+EqsTbMT^W80F8D=ih4Za`!c)P7sJ%;hM^IDiw?5@&>kc37lsMhl5Hm#-uvyu z!P7meteptC-@(rt+q>TdHUI~1CMaT|RWYjvlRcZ8w7NcZg}hu`runo%me=i?tVbGw zk|2@4C@;M!J^kyl8rvKTeu65(0$)WOuFc4Ize^R9t%!WgP6mfLoz1^GtAIn}y_qsw zh8!3Uh5F}4SlF>jD0*2>B2yJJX@`8c9gG`D?vG8b1 zZ69_JzF~M&@7v`Z;G^6()y!%2pH%?596<)(fCQ;^^nkm?v;no??Wg|z(HN_d1~7^& z83oOw4ps5lKos|pFJEDoeo*6xKc9~J>}JS1QsopNzBgwf6b!Evo-59odt^j#Bcj9|&An0TQJTv&@({W|+Yh5*P2pFPWS)MAZ9_9a*w|qrD(?ll=TL`wzKHivL1wAv zY{jk@9J&G`AXTY~+UQ<9)XXsp@`2{)@1s4WyEvvVQz${%MjrNR?k$s-ASl2|rz^!^ zV=W;g3Jy6!!P5-xu?n1SxR80RUS&=rc5m3BB5Z6&)TD3%9%P2+iW$R7j`D%$VW^ifA6QkR%{VjR@d;I7C)3%;ZM*--R^$hZ!)CP&mE$B$^_ z0EZd%DqQ~ULm#>CceCYXXxiC`<;AC^h)4lqmQ`3IJZ}aZSA$}gT&A#enXt_|r_bR2 zf*o|KT)=rG7@f{+dsVGmXjNO+#ql|+*#iNRB36M4O?(ZB>Jvo#g&PmCsSj;(>MI=y ziZF&(n$w5lrmr0umYdE`0cYkaIV?)wOu}x%1M!s&h_3vBhVEg9#WfvP{;9HlTSwX~Uc%vV*$W-hQ+ii-HXHZkJ#8fNR4)=Zhp!ObE(BK(X zE3C9YDe9mULb6`CVoR5qK?4Q>xxl}>i0B-m!auX5Za^M1FD57^P$jrf?J-lCkfei^C_Z} zb4uPF+5bXPS{G3{jD%9UQD6#)!QLtQHTzX=aHPktF~SA9m;VU#E)Y7tB(OADT(mZ; zU(n)xR00V2deh`1JvkOx?dI!L%!h1M{=0u~<0qeXkn#Xo%$xw`-3B1(4v5T~aM~wn zP=oXrPj15WZWN}03ec$gywiSGK}Qpkk{sCIa$;Pg{m5W_h!`i!6RaT?%Kea*hIEDN z#b*IH2E-FfD+KOKbm_1MH$Ytp6JIia79NFrw&tD~Ur(S^|boP0V>x9A8&X|iO z<>%aw&Ax&e#J0Y@0f3=}l0Ckkx}n#cdM$h($W9w?Z-T3Tb>H6dtlpkYS7*HUD=$+6 z#`G{S<(GgYDEtTPT+Rz(J+$7PYoBC~kSAiS$SD`20DQR90F}P~l0&!)sU&;ZuQYCN z=pneq2)z$iW$~@+b+*tNZ=Zf%)nrDydTK<@L=tBjlK;;(@3pRiaNcC;G=o~>=?2xg zG>>t>F)kqKciNDXq|r+`E3buoAKssZrnO8h$Wns#=FF{6l$_-c8V>)sCO-w}dANUi zeUmWv$i<2@sL5v-KxB1-!tvzUzk@h90qbaTgoUKne8z0CDHoqze79F-Flr)c2*9-d z8QmZy13$pz0iW~%Nm9QaB(eSeK$Ci@OMoKX1qp zabDi&TcW_fmVt-`vc&*!{l6sFVoR_le_Ey_4D{CxP?^Ld>4WCbpm2q zFyF~@^ko%@v<{zJo;a#vJ`+i2;yDV1zL%{fyprwP{FUtss_R$KxF}`X`uTHr5&UKs57l^ zLrc~_sYpQQ5jGkf?Xdo3L~EBpsatkF^D09MTMsFdhujN}PJz;=Onfd+*2 zgY;|l`<4Ol0d?+bF)&PtY^Y2DJQ#2WFrC#+PYz`9X>?p_0E~QPa&_LEOMfN(Fd2;p_%F?P^KMeGM z%l_@9DMi?l?Xj-29eZ^-UQ95FU!%&`8W*wrf{T<=p)s2A)c_;wq(^JC-B!6B=x^N| zjcuM!4=X*eeF#+TYI!@9nlKFU>a1!Y?zo-X+yp(<)p7L>a;j!0jLW91Cl*b8uTskd zGS>&I*fZxO2^MayNs_90kY)!=e_*fpX$luBh#DMn1TEJw+mi$p-Ltk2fZZ(EQ4+z* zN@L^m=HTj*-Ahi!9zL~Nb---kF^+@s3DLpXV0Hs=BfEn6>+%)fC6*DO znT!v3ysuC1YhI=YyB3;Umz1n5YP}?wk_`M32m&D%$Bqt~hK;v@$_5(w#nuh=#9m$X zX(;=UVnsiDgo&BRt2kvOrCHG{wMe+*{zF4ngssBIb|cpRVjW*hzw&lSK9o5NB(g6q zCbhNeg;47>`Lg?8_FdAmgG6esQEMJU{NFwR>?vSc*{`oeTZ8TAocG@1<}fJ4L)OZ4 z>0u*y%YP&|Vj`(hKm0gSopd@LC+={}B|5O-Lt8HpXoR(bNi&u4+xhD9j0<4MyC}91 zc;FYa55V_Rm=fXt7Ptk99>@28;47DB4(qCpfpwR;7_sX`iB6SxIJS9 z`8#EkK#j zM28<2zy;D@;W@u;f&B`wEe_Jf_I*L4|4S)oZRE5z09lPil2op@-+zi-o&#*!HtOpk z_|_vy@hYse5IKtU17zO1ah+7K2gidqncmSJ4DDwbo!J6{`N%!v91&S|g`GqnH=2beIU>x`Jo1Q=wzB~N2ANq_C^DW4UH9(|s zXF(+J2ToelEwr7lgkP=jIxHA`Ve07;K6bzja!g63dk45I77z_a+CW^(zkDHT6H0;h z4ecrK*M*to(cU`?Geqb~A(`lZ9Wwqnpd^+L+H?a%0ziO(3L99EZ^7~;AQmtIVgd6{ zuvj4U;Gg&)Q4pq2lZ51x^Y84FOJoi;aTyGsavLRzqPMUCP1s&QzJis!8VtyvQhcz(#k5&Y z8=QWum7slRUhKs3f&lK$vjrb1P}=qE%O5n#&Y^}ofMtKOIe3>);0;h9ym@%PGw2tn zICT2QmNTvp`+pGVa=EOp99Sh_0d3QP)O}#>JrF{22#^F9Wmmh$@ z=5z7t; zj}_l*=`vx)$p$#h65croM7WOuNP>hzty=Ky1O4=Z>VqsL191>wTiXx)wNzgZ_NIoG zlJm}uE&2UD)Nv{?tvc=_bcVC!G>rQ$F4+%hv0RQ)*up$f(!a=bb$CMCu0Dl1-d48i zuwKa9b0#05-<;hm1!kd;RBrejyftsZ9WsHWd_SP4{`+S|9%+>9zt&{WJHsXLrnRU$ zIhWHX!u%b8E)^2tf@B8^QUCV;1(YkIS08L##!b>Yl=4c9rFhy~nNKrYK+AFjOm85S z(3OAsEL}M@*F(i65o(s@zGsr`XqPM=xb{N;wGP@Z@G_96+FD@V{;RC(#{mGPLYt(( zCcJo@wSPm|KwVd}nEc<-^M53U#DCwBduO996?-$tsj4~|u7R%O(5OyUY!OUK0v#7m zt^SV!+dtCw(M`TkXz&Jwe2}Cs!e-|?j&@bw`_XW&2K*oC`0YMmh6p8H+ZC_;=jXz# z03i*ZE!Y~L4)Vg*kLI^VNm?ap@MD{;#K4%bBvy~W{Ccjex)l-~`IJBY$-R4V6Rp<2 zlJpA)N}=dxH%D%tT(bKvgnpaJy_fKa8lx$$^R?<^nh}3-w!f3q@^ZTt$}Wt8@q4@0 z0$3!QP_^VftVa2XXWd}IPaN~eW2uEr2$1yB4kJ9Mrvq1yQ5>LeftWUcEes$eEHQNn zO_Af12Z=2R4i10|6w^tan!7}+luV#Kraz?nhcXqR0yF(HUx@a%_d?eNR0!ee1AUKtbE)~}N*#Cq94N_3n z{Kqe%VFjl(oey{zQo2Om`!)q^q5$l5t01Jr*7g#Ch+9{@6r}A4_`V$q$3ANvGHj<`{OWD()OF&K6iO)J9)~X0Dj;CG~tLoj@tj2?Fn-}V%t$`A>vEax*e<7e$ z!GiQY8*Jc7nzxz<_5Ff)cEN)GLmEB-3)5CK-Ud2Ue1%(0OmPa3kR^wn#GB5S^XsI7 zhuC>3z$cj_r~Bv451_UP))@yyHg#w}6=~$Sk8rgdnl>7={5&dm+I@+;w*krkVvqor zTjC+FRI*w3%O5N6QQ>zcAm&d#xIf_BcXsG6s_37cOrR<-_`;A@NIS^&GXTs#8yKbC zkV-;kbI(N2FVuSSbh+}@e!#x|7k)QY$#2VYv5)!AWi2<_>{ciJKRERf?m+&HrhJ*_ zemyBb#};YIFOU^&@sT8TFoXnC{SsC}U}YX|WBfa_?BaPKAIOrzl5#I}I}e0KPu#9t{`FsboawH5=BlpAcEq0u>@g&t83JD+FUG(fxmKJ6sz zfTqNMX?uU-*Y95Wk7M9Hf>o?h7%A$Sw*4_^3FD@NIE{;y zo)}2i3UuaRaUu8&>Y6(PQ2YP@i+71ZTmY<^WfgVtGwlbddR#6DpzoJ+rOv9_rBA(Q z8qu&E1niWPA(nvvGP`I8G{p~=Q{G?|X2bqRCY`4=*N zRAY8C_}vj&@C}%)P23FBA;`uP5cz4Ga~Qa~TzfDi_PBRr7*MP{7RI=t2hSJcPF$iU z*hK6j{C}SXhqET_cs<}Xc%a}38r~gt>cQ&>u7-%ae@z^bBR&GI_ivne5*8m@wJQ<7&aia{?A*317*yaakfD-4SK=zHJH-tX zuwsHk{2<>b+(FLmo9wMHUyLd7+OG#$?hl-M;ec;8&?MZrQ$=8Y!}z_L4<3Gk$^8t( z9D8ZMxZ3~k8w+;#!`mUfV6zx4h|Hs2jZyv3T{?lgg!~re0`t<;mk62{piWglt^}~T z51$WP!Rw2dH*~uape}$|ptg3v0jn_@qG1hXpClTNOF_Q|oo9kYHq>~B%sZ1Ce`403*dYk+~i8vGv z6|RQn9sqU2_x~i5aVa0b_Q3@

A4MMh}4$pV$R>&y4dyJPDvjMn;CJWFy!NSupt}Y^;d8q|-Gv4#r zuwubwA9tg+F44Go3b8+2EUh5Ly>k}?@n6M(;RZxFr&_@Ul zD(!-xEZFpmYsz99gdpwF^BkUTJW$&Bz z$Y1^rGynf8-1!^Qo#zXX*2~2XXk0Y;=mJ*RA!pyHtA+f5Wd1w*{~;3{0IzfS1(U6+ zou+U9<#W(WXz$z)j285JT_WyqxRzxGfpz{Bpas#^;>DX!Br*CnZEgb!SIiXqww=Gn#oG6d? zI1uyyGq+o}Bj3s&=H!>II8?TH=g*#`{OzC2&A#7c&e{Ux;b1gi7g`j(C~(;QW&Xm3 zLmsqlcp70X0mNN0@KBGXA{Vup2wr_7Q$9Gd4kKUqHnoud>7QR=HnakB{3Vm34D+Mw zNr91bPsWntqD2LPCI_D5Ta*-QJV5TQKp4nl+%Z~PGPW;Dzc6Rz|jCEFU;f@U< z8RnrE@KZ?4%${Sj*^Cv^?bP&?i@K>Pw*-Ek8-lq}^*A3>zFJEoo8)VVokJwpJ^p8M#VRW#`mHsFEwR7{xXOtToRq_UI-oPVazvFftu z`m=bSBC!;tS3!k-j5FD<9t8u6cJXBAQvAR?I;%?<7wCd_ z0s{it%XrU^!JFYOn)=X87ci1YrJqpfm{aj2*0b=M;4zWcyXE!*pY2rFT2-i zE}220cNM?PDfl)=^7qr|eExk(r*I#!b5|t`JbjtBVh}Fj6gH>EQ;VEs{YKiWwbxsX zl|OjqMRNU^WN@mc9&7YHe<;XX{{Z~tq!jc%+IX>KrGkV;r)>{_}lprxedv*-)r{iRo!+%5k$UhewDWZn1fVn4j5?`$Tq@j-jea~9d!&BE*K zsnSvT2rI*e(U<{WJj-cidfC}Tj)^r&7m}bs!%o0{ew+d~XUGR$L5C6qlkS=)O$B27 zmYd@0i;LLN9TcaNUjDMrwhvQpRe_rY!5A`1N>VtBlKU zk&cUZQoFa*=9KH5)A8<15;U+dXsGnCcH zw~IEoO&)qjyCIQXQf8Gbi_eufYBrkMs%i*~qxGBmEyDIMK#bR2Z$au=_U%DNwL|G5 zAXsm-DITrGTB5ANy&?`)kORWkTVk3;jjg}Lers~rdZp9bzGOX_qk79_pMlW_>$WS% zQCsqo6((Vm-QrSVmrCEvUE5lmM>~JcNE4}Vj8O~Bxhn$`jF2wE5KUySwzHPYIM^JH_ z1@S&DZW|w(7@bCC9!81S&-&Iu8lI~?)30})%vEPtjsvak7cjF^*KMzm8i-c+JzNvA zUzfesbeJXD_5Of$kxQPXORQ76Ie~yki4+;CMCw@W93APB5`9H*Y>f_r)Qk(Nnh+yC z=cU&e&v~b8KAJEXzquwa303vHoUL;>d;8@+a^?b34-bl_C zHz_=7Z^|2{jH_M>pG^I%dIB-}<CFOqV%QDslt~dv zae}q!WXW4O!uGwpysC7xl&vKKJv2)@hN_KAzxoPV6DnWqyFB}OdBk*1iHc$UM4{Iu znMdaMsLy@bmm@jaknwBoMZjtfpkP7!afWn1+35)y3Z#MeX+ax)4^Xz3^&k+dkp6S* zecb~qB0IE*s!liK;yJRxXzbU)^tt5n;>x=9 zxTR()u5KOb-FCh$&7mKcN63d|343|;;Tf*NDkheGt1fQeN{qI;orE_7FTVJiSgy}% zv;nWjdd<8OiKa2Z_P>%?Yc$vtcDlqj#+LnF0U<=&p1_oUTm15z)!@8@e167G#7?}G zsfn1VqK^OZ#QNql7G($Q>iAPC3`cPun&TtcgD0X28w&Va3qzj$G#Pi<_eif>;^xj&;jr~VXW_5P4kYDvNwgwL(l;5}n7Vu+#~ zHgG{a&Ab8h+W=<_jCn6Q<)-908T=l+LACS5is8iYBXzxFAP&<)K1Eib9=Gf?r}!L? z!pt*S6#NNt{hJj9xo11b3=+7-BbzN_EajcB@0jyt@nzX3i?VVYJu3qvJmbIP+t$^M zwL~-fi1UOwT|EOA)x`}QQ_bC18U?-|3T_C$DcRECg{Yk%yv5J9&+H)-8`bZkXHB@Xtt<*1;w@vp zSq_F+JNQ<3UfMP8bUtjKaLtXD&Ivzb`-}?#F-ojp@bg~?erAfY1H9VdgDp9 z;6xsB_ZiKu7kZd#x?M;T$FtR=GCLDKrk6K%~Tqoj&x6luoi6oG>>tTS^0Vt3%O-mKF1qnM;e zCDM;nWdWq}WH7H{IohxM?PjA6E;xTug_qCb&qe0k+Df_i(1-fwW4GMOMx#ZAt-lWasl(pMvijuQh3p5%o{}L?T_bW!EOMkaw3L^5$N}&`-vg< zGyEIOtsJ{N+($C}3{+m*f3LsrJfx#-SJg;^s{hJ@67RY7)`aa*b8Y-N);rA|XEKbg zOjgx(F&QgGnGHqp4=}!7B`L<4f)k0!@qWPTIg{i0LNVfJ%v+lRM7dtSz{9Jh-qY z$EqssqvyWm>F!0Ihv^USZ^t#ULn(f&@dF={;w@~>%?~4u>}@n-%CZ}a<8?nK5*)k8 zSPT3xwJ{hSrwf@~0p&6jK>>U99LhS-cezfh5gQQFF_c30eHED_e}OD^!1tRgF0jfx z&iZWl!;+D8%(-L9WlzuTMNAOv9<`Lzc~zDXq*_EhiD_qc_K|a+p;?+&eK;?Lz>_~` zm5d)6_r-h6hMY8NNT?iow~C2fD9mS2%oPX8Mgt45{JHCZ_qfws!~`PAD6%0{?G{p? zwDNfM;$5=EcyaS!aefLFi%>6H>HRLse4J4NQ7R3C|R^bz+u~evH0`H8RW8tKl zfpE)Sl+_&Lplsjob5T()-P#Qt*vKt=59*U zV93Lje0sO!doTX}@wr15ot4nUAv}05$FN*b5Wem7)`oo1;RaTZ(gdbyYphRJfFw>iNlTCO^dy<&0E>ao zkih$N(&AF%+H-S&PGIDDBPaJ2dhHboDer6h%`s-GbHGx4gGsHoLhIcK!vgmsdXsRU zUR36fsme~=&5EXk`xxL{RYzYOF_`fgZS==-^wQ19(|mrQlbgW0G3z_xLQUrbgjmm3ACx|D z5F_Hq){#a9j4N(3AlQ#FePS`=-Z`t|&JgjZ=9tzYOZ82|E7rLFo)g|POxh_HK>Sj! zr}gMK79Tat>KRc&jL^dl_WQJpv{H3HjfcmSwW zde3W%#{NCD|2NTiz6a8vdurd-owm9&9-z?AT zud{_bO6}oy6hYeAobRP1`<(%g17Q(<8m4nIQfbW@Ihl_oK(6 zi%qtsj_8Bv060J+uGd;)qB>z|l?pk;P$5ce%CmIxjKFgjlAobGvlR85dF%5=#cy8b zF5=CYD4nM+<*YYtqwgFW%&g>0W+g8D(%|F#mYN4uOZ#j0RO{osEcbTM*SiLpp!Ueb zZlt*0y7GLrlXUuHrqA&)@Fs-IFD^eUeom zTPh6%I(;ITU=@-^ftzOk?12Hakdf|-eI#c4{47gpmR)m-13KAbS~DCy4w*t~2B5$? z_sP`>oqk7xo_SeyB{ic17Z;Fm;UcL9e!nbW-oTr>lrTjHM*>-L!^F>G0h+x{ck6Qr z8NlOCR&rjNJHP#`lCVO;@u^EIC#AJnn6pmhb7pH|OPO*x6TF=l;&6H=lUpnIPN^92 ztp#uG056mPZ|>Y5`uf>kZJc%x5;}v?w$!{8(j^HfyznThdV00@>~`o?9GQ_D(7e~b zDUTaO06}`NzMSJ7A$O5ygvd!t3!_xjp>^7wA(^d{&no3UCE!Wm#j6t&F29k|L~hDx zJc|b;8<4T15J7T|OGBP6y=^uhp?Y28b@#PTCvlZ;G=W~APlWJ!$Z`VbGuz~TIB*7; z?T9BU`=R&tDPh4rG68ay${Pyic;8S84M&M@vzyi321N3Ub5YW-=RTQZL&j!MS34wi zhCT7)LG*3OeM5#`^+akHJ66Lv`*RZE3uucnu6klmB-Kr%pOjc!^6UKJNiR_&Q8@+kjHNZ$t@%IbJrG>~j;KU+S zuG8>cv){>5^2w2NGXtrH$4cFc#&EQ!7l&=*2|cD9P9r(w659Ls7_k!XMPtFtyGmU{ zOECmtj@B)WO3mTZ^TW)2E6W9IGm(S;@s%^sI|~+{257uvggU_JCq{lEVc zX)&#OciqZ6UQwE~)ebwiM(#Rz`PLj-?(6oC;D+lUAf6%_>`3L-)h;@FiEv4DaS5e1bJl_n*L z6+}v;B~qi(2@oL(Aqjzhzi&XN-8=Wr|F89bYkk+XW@SR&a>_n?Kj+!|?EUG|FB_xd zeJ;|S^ut^UzxI<_yB?HJ*L%S11A?QB##;SgK+&8hdQGR?XZ81EAsxf0sWem3qzl?+ zH|GdkTG9+$!})1rui#C09f?guWI>su9S%^mZuweTd{F3_nS>-jeYMS6vO7ZjqBnIEM z92OC%xs|{EHnBSuat>9ET6ceH`i@kun>D_}9!#1sU(~$n_30}RIo9>yjk(3LSLf4L zxaHZOYtY7+OXz z3sGh~rs71afw@UJt~qi-Y{AoD-rT03)2AlW?v)i%Jg7f_(qS4rK zJ1Kg1Z!k1%FJUA^3COsqF$0#yn6vY06vLcT?UEU?XZ@Rd9@7`h(|LaKiA2n+Ss)Dv z**c&LnxejUsY$(Ab|~Z;#U&2xG^lkqk3!CgTnB}r%3*E4we=@i+WXj`x1_FL3Z4yI zcfngac6*224D1_FbV~e=zu%)^ZF8q|I?dd@eNjCem9s^4Dhjm_%mwnA89m;;5u7oS z^}CAIKbz$H1e%?<@u6wsHxT%ZTS%!@k!Fvdrq%e%F!SVHi=zmFh`UZQ;d@18)7v{a zWI`a>?#V&Ez^^DoduKT-Me?an0Kg+SF+MCan_2!Lg&F(=na!zU($0Q!BzR@5v5@d;D+Mkjh4X@VWpg)Z@P#tTjU zB-bpuX@S)09YxhWd7Pm+Dd?S}Vxi*2au&ydum5@7^Gf|BN(x7Pi8g-;%Xjx|-f}o= zd(jy3Mo|UXQGNRRHZ-c&SgThjsrwhqNXHeWGVj)`@;rJz&j(|nwb-zQ&0WdVX2;*R zr%&w*zg_XMbkxnf*0`(Ja*aNi-i<9UTSdTF%rR$)Z$I*HpLNpHyW)2HE)MG|-Im4( zPm0f9C_F4k6J8dya4F;HdD?Jfl6)yW@EV;Ci8@*PNXL5+? zadL^rs|9VNgQvY|&JJ#LCzW&)8%I*;^u#UdF0altNH@f*J4I3SD>-Gb zdDRqg4nMoTHG*Kdd#_?O0PLazQzN?fHY^lB81+hKhl>^@YF7lN?D=i>)*${=;nrs= zF^0t(mhZ`twRc52S02q8!SA(^GTMG5#CQS-Gn&loCa3yaRDZ}k12ZL#E8d~Vt|y;o z{+wdE!-hK#E89YT`Uq13J*wqb>zSWu%?59yq+p=AeVEkl$+8cXBqC&vF~PrkXPRuE zGaql$Fd>Me9#DXslns;j)%-6t|rllweOVNSRI$UDhNriWV0&T1(z_vQQBauKS}b7 zmKrr>(#=K|JALO}ES^uYEXF7d3K-9`jvY}1lmfU6#Khljd3JBV5aX>#nZE&BD?b*j zadEXy#zj!{0v9M%!4F+UhMblcS6$yiGO^TN44L^L#xp>A<3;1B)|Lge3nkm;Yz=sI z>Ez1NFXhKKbkx^(a^JK`!#{LK?#%OR7Gi_y+j;s=OEUwKT*@MxJ zw+)=}dU!=dpvVQ#gRq3|ug{Fb51T(1M5n)eBb=|Z?OxEKp(XkCzGP-f9^eXY> zPM1?owNo2547jmPjn|T}o#PHJNoU~9qDRSy4^a)%rk-?8sDz04-nK*;0YXy8FxUYcE ztv@^e^V*z)BXYCYF6``va5a7LC3e8NMDAKhCzT-OJII~y@#@=ua?#2?&O0JXj`!P2 z~s~g ziMt@Xc+oyX^V*#HvrhKOUg1s7hIpX?y;7E709JgtSa&_EUp`$S6W|;+{g_B;nEk+P z`7k5XRN|2};Ml<3Ys9v^zgZ&>8~Md!U3@dwS@5ZtVCjEF__S`U=>60#Url63M1bV< zCKo)*J3oz_Z31Z?jb6Iv9dYpfHO=J{ja*UIy{%Y`9V>YLf>CeqqqX{aBRa2p3D(7U ze2MzRv=1~wgVX+<+k8$_((PVM$=TGsl^nivK&2#DU^G&fYwnTA+S`~Ppm%iHJ&t-? z*2a!~8B=|8Dv|R?b(=0GwC~>Id8gMgy-1>(x`+I#BYDD^B)c_K`)ayZxYwQ1nw8TI zotr*;U@iXCLZ*l4d1J1OGa7=rs!HTYK7c$n` z*@bxXva>y&w-vAoH{;J428Y0CF|BAIZ?wrBcC zYkF@45J{kuFKPSfM4&#}}= z9k$SNObgx&Pdl3Jx{Y5v;Y?qjq3OnOt+6|TpA2)gD=^Yty>EpqqvT6$6E~|rggNec z9AKcgZj57Fo-HtXr;r&aI(FG%g5v$RT1o_nu6 zqOk5D#lV=WUUGq>sv=3AcCh;}9ES2nFk$iP>k|yAc1f%B6`w~YOu7aR-%Zf$gUr1F z>9gC#mx%OZ0p;{&kF~4=Zx&`STO0>Mmk-<49py@#(7&olbO&S`KY_TQ(8GSrowqm0 zuI{PPhN&O5z^Yv1|Oc1-hG&vhZ6WPmDUH zUrSl%&ko6R5mW0re~LYFU-a?oPlu~Dtpllck4RIWsKW@<$mTpNd}tnM@xXx`Q^7K} zLiWGimjYbwD9^mD{pfvQ*ggARleYIu{w6%y%(lkD=%M6#_ax5Ix zHh&Lazcfilx8Qn?%MM8xU*UN+`?(x<>M>?*drBSEEIZ^pY2Y4RMQhEbCnOTy==#*B zoI^g0&=SmZr$wpa$YAyzF@ZmQEXUNv{B?J3yQcYRt0K~~$O+|kFXj=CCGsnffpK@K zuEMY9h$Fwwu9Dd?nM`AfN0bIhC%r!`9Lg^g@~W&LQwi}3(>sv6;B?l*24nvDW1bYh zx2F6t!~H5);tazfd{*VmKEyxg=1s#Nm_fcuSu z_7bKZ#pw7kwVk9pQ^W+LGGq(fWirbIck$qu&^!wu%K zCUmMT-`KgL^`h$kEr%3hk#OZVW?pzf2Yd)EN z=zyp{wf4)KZTb)JX_-QkE)sKIBK{hT5m6lZ0ap1Wh1RfC!i*N&DZ_h3rXIVZ1$Vf* zX1F$oE^E5OzSYGXb2SdK$>Bo|h`k;&Dkaa*^D4>`BpabRSLEm6{6llvGGkmBpSp{u zkuAW>9Zeqv^r5T8p-H}p)b}@3@8|Fz#S4;Ee`wKKtXE;+v3STe&XAszlq~J9{Xq(e#rq2$>ots=O!jZA1<2F>JyB-AZr&atuX~WZNQ{*1i zBEHEhvDd$0TxgTiCgZlm#c(Yl+( zw(iWv8umi1t@H3$f$xOi=#09Hfz)NToOr8kGQXM$I=bcRZHPGI3gmohCsMi$;^yOi zrM7t}etJgok%iYym(;ngCdFRCM_p~+Ub-mbDf4KXI5Gmq)9xyfcTD3w8(^@UI3u9; z81++jRHV#pw>_~pvFjw06TF?CIOomLH^Jhz3-|#Wl=Kot#5>?8b%wMBF|MII;ez5) zvn446IOGdeOfaj)jf3PogrDJd5{i9nCepz-BWoIq#&IOjaD1R$OG0<$SrhJEHk@= ze=MZ3Np)Dahh>oGb(F$`!gnP@2fg9^A0%{$C>4jW^a%O$KfR9au-g!OkNZh8g%NnY z*#k>y6`um;hR)L#NjAtlR#po=&*99qwJXhE7&|fEcNGPWKbk9BODRSPlpp+dw*p<) zsys?LPjBK3=@Alffn9`pWH{@1IK?7K^d*_=5Jhw*S!I5Du$Ib>9LvtgGg_Lc(HcJ6 zC1b{%wY)2L+4umGcHuJf4ON` zq*Rx)jV`yCviuiFWH~;iLOy5<&#xNwQ08sKtclJ+_TsA`U75)lB)b(5LP!GWCFINw zj>G$iD9UsK^LPFj*xq!AJYEzuZ;{`Sw0yvvY%T1OsnH0nedNGrjP>VDI9+$7iPJqQ zukjOoajRnpvnMxKU#$e~0I8h$X{_vie`#WgxOJxZ9P;XVC(fzJ89hA|NQz&ja)18K zPCdvH9)}sg4)DbBUi!6iTPkrfW@6%{<1dMREWIO>{Y6o8LL+=9?x&d~bvZ^ei!+=) zo|egGk!PnJF@AOaY+Gv}D_ViD#v59B(x+mJxxRsxdvgQqv(@jm6$G*h`js@wuHA0M z8YF>G2Qs0xBZ({z+g*gU8#?<)e^O@sz8J}$1lpCj}5z@hJ zFk_MILS1y|#CTCd+v%5b?G^XCcCB(*Ez4-9x~L}Br>r2~u{R4jB|aG^PwWa{Mw^&; zGoBqx@6|QOSLwf4wVAB;rQ>E*q#{h0p;7#V#;5{6^?CU0{M&C$s-iM* zc3HVc=)etz7xE&_YFwF4x!i!P&&lWN55;FEHjk-wn}o%AsFdIa!fr!3QM9DUy-84`hs?;YH-A6sG=$c z-&uR@WzeccKQR{FwbpAJt@ePGU7v7^WwzWM`y`aWww6`}<_kOQ$|sErVBE`VKIaRg zb6(cmnsWXqdBgl&;3Usz2&%8vbaIf<-ukr~ya*cu#o{9ONN?gwbY!Cl_EsNfKeMV| zU-*8k))W!=Ozg+5Ot~|>Fn9-ENnk_vU>?`1o6$G+9pt6cN_4TyJpy6eP!$iuBw+gk zo`t@aiPhHDndeZSavu0Mt~Y}F{T0(s0p%=l6Yz5s zjqod~Z8Kwvcn`(%WMHF+Wa3Ub8+rtb3vwb#Z*Bt&}$G3`h`ErY`JP@G6jj&OpwW01AV<)Bb-Q#7KOJ;^hcC#2%H~0WXib43Q}x0?*5n8K9Tr2iCUqCcns)6%o}4*F=SCR*+JIc(Dg$dK zf`{A;>=mn)J4E+_t$pAB{d*=9_nisX8x&<7qVm!P;9OkzOLP zFfqTL7WnIyGvd*w{eC!1C#OQkb%B^b=I}CIy9y{+SQE@uu!?O`T`nY=_IID3dnc)@ z8!B+2A)`o$jzVB=9tY7vSz`6N!>)b}vQi?rd)U^jrU%HZCeHx^A$IOg<_z3$e$d$@ z$^ydnT}$?P3HGORsof;oCPLg?j*IwHBCqsB8e8J^u&ab;pfB{KRz?#HL{g_+7YzL~ z=dG2w8Fya5XYdn#sjZb=7L-^yvY*=MVEmT2l4H!>z{P?bzYJ=xu0^L430*gr?vt<1 ztEr$bi4ze>(tF1EWp&a^kH=))%{jk37{6{7hO?lOu_@OnruWF#kBjG{nkAN7epR}p zD(&JtYe~l{-8PTsrY-@r;iCE8p5$K{nQTpy6FJg zEq2pUyW0%&=TBOqu0~*?Po}|-=uQXIo2xG$V07PgBRkPXiaO)313uc_y<{yTW8Zz* z6f)u_gX%J6TVGp=8R%xYvtaWSL;OVAg6Z)p)~{u!z0Xc6k36=PH~ZPWMw2;33ZoYP zr=f)OW{-2+^Ghn+ifptxvA4wj8DGfnL!zk%NP4v4w}w~o_}ooW(eoCh;P+Cwf5qY zkJ)P~W%5}IHeZx%ypR7H;L~VO@8TLK8PyK13C5A($!1!ZXyHQ%0E$Z|?Kv4b>3vVD zdk*;={h7TjRW!*lfEv6U6D;Wmrkzx9h(gDB1c=U%m~`Svu};9Z=KZqfr7Y8=B& zZl?k*DqTk$B!4k_z@2cn@!+Q&BtG3Rp!@4dHf9omF(1HVPq%=mb>BY+=;-^$1@O{2 zHB;VPjlE5);AQ99>X!&gR#%-*H}R_|Gii_-xU73;-ElIL|KlD3civiZ$^5%d?)#1) z5e$nPzy~}GDz4>;#DNeea3gcoCYmT|KbPeAKdpJV5S!MJdD!CPhZx5E)A?J9smsT(wTTND z%ft1&4h^t+iszep?LVA-;N`Xv(KEh|u5(n)xl5>IOdhuD6zk>stkx+BH|H8(UZBe! zrum$^DSD#8QtcZLWo9WD1}^N-uKDSLN4M(YBXO+jvY-0;;;4<+6n)}}YXZ)+;=+z> z4t()r(+FOg+maHO@Lu=6$af11@oHfwlY19WR=d<#Qq&kLIef5NI_1-_D_V(E!L`Wk z0jDzi{Pz2Ewk}Jd*md%_Prk68>b}7*yLd4tas##G@s*BdQ!-bU-e=7%{3OrniAWt? zMt16t^$-<(j=CY&u^bQ#^z&ZRo#Mv0cbFDtRQH6$1n+NsZ7_#q*MnO{7CG$?ZmJ-B zp0jksb-~YW-EGs@O#wUbad~=M6oJ1)hZf4^K0evA1P@p?j`7VozsRI^It2?9iMSD0 z>L9(4WoU_Z>K*dkp7xiH<;2T@@_shW{`_tZ<3eN62Zp%lyy@}&ruP}|W`qzvtG&Jz zHO!G0>rcgW=WZ&WBspL&S#2QgC)Ovpox!mR$i=I*V``3K-&e`2U23UD0c~2xrhW~y z7dDzF7ptrJhhm0JA9l6Xw9xva@a~E>4PQ0Wja91Sp-W@d1t$`ghbuk`A3dFt7pwDQ zkMn|KhOA;@%A;4rWpsr!!K*#EPm)}blMyHHH!mnNq^>V6&up$3TN(YC(sRrv_PudU zKQd5l71f=ecJ2&@NobdRNzHp|;zwknEJ zvFwLh;=7Wu9lv&mX>Bv7^vU>%m)g&LovvZM^uFIgJ>IJ`0V%Hg=VMsKzXU$id0C>h z%7HoZYv92i(o$(hPfn8qcDmUrmYQipc$y(ghd1C&Z(fprgSy`8spJTCHAc;Upnd+H zADe(csvTSSRTzZ}6$C0c#(O23DzT}W6Ta)Yqh@@_D!U z*(n(}m*BOV3!=47bwuhR;+KsdD9mWK+ULt_oAkoybBYO(v!lC~9=~j-4mV@;=lP!! zD6X~g*#!2P&d2N{{>O1vyt}iRsjVfdqXO-cu>y!6onP~AZh!ZI$34u`jWvdMJjeQq+wIJ>N}PR-dkpo8?L~I3?=>`UVVpit0?YEc#hvj2^ZptY zXP2)n_Y$-=O5hLnJmaN`6VizexDoS=&qlqW9|Wq2HQ%u3;Kyjny7H8~Xl(tQFB^Av zm9F{X81oqu+3(unVZq3ps@O1We5xCJkw#Zf?;FlgEC9K;eN4tI|2{ua`%E+Arh*v{ z%~`FkvIb*HtxC;lWR2_(iq^4;btHZ`)>Qd~%E2pwYYQ!TU+*yqB`NmSJswM&#^`mc z-jpz{J=psTZa{`HPJM2>)NkIcrZe5%FH9~Hiqt+AhEv?gv4OuTT1J10RTQzfpD^|3 z3Nr+*=7-{*5x5sl1#5qKjN5uf+(jR8@VignFI z(up2=e}>gc;|881@0O}Xwx6NIWMBqUE{=6e_EROxqRj{y%*>X`?8gN0=-CWN&*W3* z#WmfD{{ipHo|GPD*>`@?x{x(M27cc8PEuKe~O!XHCUxr?bY7 zbHk26@IzwIt3Fq#e{<~Zr0!U$X;yj)bFACBzE!U-x}99K1m3%)IkS{aknLm|bD;)9 zFREYR|68KCrk5wGPNqK&>sJA%T{C%~74Jzbjb36-YTqp%Z+{*0O4>#$+{}_jlx4W> z#z3lqwDo31tdsFli6$@FH#TOVFHR7^>#lNZX8JAqNxp+O;;5Z>(}i5{q$T@{v^2;v ztKNW~GKqf?_C6^_-s9{eCbcAV@GI?k%_fp7-B7Q^r>H_0LrnUiVUnsx6XixAty zfqj8l#f(-Q>`4OEdHllCSV0(Zq)asHfRQu1-2u*aLPjO!U(Q{HJa1S?9An+7)>7v8 z;|3&Y61+=0)B3JGZ_7gI+HQvD&$zmQSyk67Nw!S#K=Za!(V(2j1!BEhMH!=!d*`7q z#09{;9v_5|waXiAXKXXnCHnmZlgUx0Dgx8Q<9n>+7bkH{sRVwaK+%MYA_OJCjj>3> z(dRfh-+_jaaiWTz68NF|OB*z*1?eg^8e_@B3x@S}$c0?3jPv5@W>NMHFOnb!Tj@7j2D7z2opt~fN&wx5c|?B^w`4h!>rH;LoG z%V;9hC7EDVhaVK?mf+9xa0x*N|3CVp2oW{VK=g>)T@`d2s%h#i zZLT<^6dccv^~DheWD1mX25s!w@k`^2mU>xw^5Hrc$wsA=5!ISt&UD~WfAjqzGucgL ztL8?DnpPq~o2O?*@u3x2=OdZbkcrl|0O_J4YY7!u>}ikzoq0&=EoAVCBeD2o5qWn% zjldgd4U8nPN2f@&OOj_zE0u|`SqHECEyp`q7sH}dJBxG<86|p>9*m4+>?>3t;eWZD zFqk0X$i0JWqD|f{%%k%P*2fOtyPT(;e%gV?w4ssC!<%{9s8rbF)*&*lsuVw346+=@ zpbfe<(CahesHzCyn{J7SbcX9k!gz!D>6RdJryNO|%`!xI(|dH`z6DbzVet!@^lDq2 z@ydxm$ri%ejiK2k)F=2l`lP|L6W$*LmQBX*wdJ4X^hT75RoN+^9Ns!UyH`68$T2t z(n*4q+-(w_Bt1FcCxK)H(OdNCR!XHoN9ISMaiiNkRXJgrNZhqH_;X3d@D!=#Tnuva zA3szt;70AV9Qx<|^@Q&*-JyR|E|{vUzbfpIn^XF$iFa^n`4iy{@G;@r$2%NB<%d%> zd&pvP2v$&3;>?EZyr5^07IN<*Qm$UGoNdEv(sZTBEkP! zP30jE;iAvLRkHDHmHTkh)XdXV31whqfi*Le-R|@o#dMrkT5XH0-220k-iFt?AZlpA zGK!pd{M;sInDYOF0 z@uG=L41#dU0~fH99P5tN{Iq8|?P9|@5b!^J?15wX7l;0pzpx)RU2X86ez0KJ!m&SL zN4el}w0Ad@I{NpYc08_ped=&Af+AVzCo-G0MXmf}%Pu0;Mi7YgM5h;#U!EeM3Y~g% zfaT&;81%9X*_2P#>VfjW_WJW+4EC`J&0sETo$2;GZQhhG->;wd29+YTv$p8NQBjmJ z7paG=PLzo{x;h-WK{ztexdFKlfei4+V`1o#>3f@U75(9TMD_cN1>r`ToV z$n>w5oX4T!@rTc@jsVpcv495NWH#0cwI|3#Vi~GX=w^>l3yi9~$ZIPR&G(xhLJ>Ij zXbss%R_q*W&ofwkqY3tJeodNWnak%#%ajnRrA#v3{((Q>HG^UGooyzK;%Hsuz zf_eqde84lQES```7EJ+10nX)iPjOwaZ{XV%#vo`pu(Z~sAB5vkN)=lS#yL#vlb^{ zL(%A8Wldi_J50LuIWkew?v%635ur%$$qM)`nH(1-@KtKFDt;TH54hrqFE$M9Av^Op zCPmeJPCVgFWgH402bEs%S{l=uD?XV}7zL&L3XI2-W!k&B^+`(!vSf+Ecf^&QcbWbq z>I_BoG_s7NROZ&3QJgXeE>K{K%9lXxI=quDNpm=v?Wxo2B5EZbFIq?kkt{Y*0tHli z7_63wJa-$vPq3#+<1s;ldZ(|ii!X_54Fyo55sbT4(0lhuIiz=B@r0DxeA%<{%We4Y%w( zlX|Y9glfx;x1T%VuV1fZbOxSSKhL?wNPCKmxvs-n)I$B~lfLiwHa0>fXzqpnYPzAx zWgFbm{kG>d*U#0_cs6fci0)PD#LBfvC0eIO++A*ZhU)ATry0TLau!KyULV4rHNI!n zdI1NeM>Eqmnk?d$rNAwvs$|?doVVvKcjx4IH+a?q9T^Ey3RW$xcaN{j68H&vxA;q}HRvf+0M4%O;`VW?5f- zZzs2`NiPU_LRsIcrkRa$(Zn_TfH8IVF-C8Ub)T89Y~?<{dLk*L%0;&A=5c}(uCOlz z9rH|iv#ZR@^x*F<2^Dxu4TDC49WQ_+mY!RRFKXg5@ek-r5S)nR2i(fHPBwafxc}`c zAXR#M68VrEIx~G?yi{GVY`~?yIwd&*suWb+yWND(Ny+0X^j{EZ;}h*0#H(c?+HciA z2R}UKNv#fv0tEEV;N2A?4R`12`V;lYcC~vvItSdI=e5rPZ(qte4Ri3gE6MM3o}Bes z5a1xX<4;z!qpffM`79r&B z_6YKhq;NUCt%Ib?l*p(O=+CwgYr6jf%qxEmx^FSdN3Si|USHzj(i(IWZjnV^TRk}6 z+Ki?f%(3xOza;t}FYFW#ZXpGQCbHaYU6NX{8nY)VJBe4<;ZLB3F(;ao!tP|1PYAOs zc`W{HS)XgU4Fc%U{>8fKlf5wr)EWk$P}5&*4;ylZ(84F(pUB zP~#)>1JdS0o&80z=AFqDcycTo%2p%&@#s{$Ns*=B{WssY7qaZ(;1TTR0dy?BhDe0* zz1-X?Q;D%Wq-XsKYA--;31E;tgEQ<$4ngqox2wzgvYA%MZh~5)cXLoI7|h&wo#EYG z#V1`$5}EUOOiEw?zp|qfXdw#oDA?+H{z(4j*qOl1OoqNaE5A9%X=(YwJm6<6q5Fm=2j5Ni4k7TE1A<4) zf>FeC5(@4b{n&jC_H8BQrssZLF-XurmSz~rWsue%<{Jo`fBC)7UsdTxG3Y%xAouNu zeUG(lX~oCEtq$4=#`bpxLi?Yt%!B59|KZ#&SILD_?^ZT6m7@CVq+pP(v64S4Ur1KI zGyG955rITY1{w^ND(1lBt~!LoDKs~mFr*7}BtY6|3I!*Kv9JeI(l7=;%vK61HaKwh zk6ka?rlG4s`D44kNfj!=*qf9Ag$BZqm>{poj|ScZQKlcyNF_WPLL~gf4fSQls6+?E z=&V6?obZ;QzrBufjaPsw#M~xFAw3D9r0}>wwZ(S|;PiQ*%u@s#-i+urS$MR`fRf*7 z6e^X*66R~nusEHys(XGwZgeX@6JbpZ>6^q;K9@Mi*$m1VhI6?87R7N;5hj-P84nEU z^qtMx04(>6OM(+D9G8X>HmGE4i#>Oj~28%kWh-to1K z7-H}WF$-Jzhj^bMJ>7*eIU~L;UW_)PY>0z84VkBI)cQ-D*AO5l`V>31X|DlU4J?*U z$2y>*?B_;Kth^eNYc0P5(lYhZ{B`O3!Q+lYSMI(1D@NBfSzZ`vRLS1WCDw<%(9v#A z(9SWGXr|%c5XR@5O!+=|$m>>uGKOvBQ2U;OK1qWe)1aZjCkk&3vH1vIgq+VHP5up! z6vP?^X2guopr1H4MZk-pq*KGtfE{5*GpstGstRh)K~SUtJ(_7eM7x8=!ldWc0)c@D z-X>J=EJHG?g+8*benBVMTCRIr%_Mn{BpPa0{p}{6_X~PYNh$3D8li67+Lx@xD(rc|#x|gIq3J6~A zV02Dn{wq+H44`bm98;+K#sw8r?a3qhEaMtCEAdX{m~V=w)*AtI2XwyarV|BWpu(?z zS6|ge*6EWyNQh$cm>d=L7U^#58THYK83Mw42sQofcM70cL;$@<>3~nvHyjSDP(=qG zKaD{6;h#r6L)tM&gM+!)|M2?{6Vh_&7ZR>icl4OqIO&B3I?K>m2MMN0smYhR*RKG@ z5B&Jh%$oh1Z;6^LbT`zk8ld?sr}}xC^vLg6x8HDZuj65?hyHCHgCA~qb*79kIi-A| zt`vo2KZcR^zYCwJgvArmtPsAgs%9ccB!X`7PDGL2TBYo5FdrJ$oK{-n+xE{ z8;eD(oZ_3kXbcEQzvA8(z`F7gg4qMH4>BuJ4XHZdUam8!V|h!O7xLoWR1ko@B`OLPU5#JrZLH80@*8Lb z>v#z{L(3S9`nQz}Tz~cwknur136~rr;iE1hE(Ug%3)H~?*a*<#z2YZwNGmLkZ{Qe~ zXC0{1+`saTF`%LbYN)^5!7@AThtUgo_1&4(hA4 zGqg+*tl@A4K?l#%^Ez@tTi7>D+y4%B4+O>pbMRpWaZLDA3A z=tdz2MbF3PzckYfl@V zbKm5Qj5*vAaXDU))-wblJE7o^2S{5aaip4H%-ZCzo2Uz8a4Y6RebHBzF=|LF09gLx z_q+T0$yw+E22$;`(!!~kEaxyYX_W-9asF>m8wb9|A@3*O<38xW?^lDiMF9*jbT0?B zwm@uxQ%t@>^~==?Iw&>*wI(ccVlVfbjwsA#7hWBq7%QjFUl(GFC|je<0NhgL9*xD7 zccNa7hBoQDL8Rv*$i9FK*VTk(y%ePiCWg@E+fLEF7S~!I@lrnY5S~p~E-6%QPjjsY zosa(#_rm)5z7Jd_nE6@hi^-@S&@46n#28iF1cW)Tb!G|F;(IL&-$=p|>1a1hnM zgv94|)zh?2c~A8AX$4VobYI;aB6FM+OLla>?z;b$>OE+9xTf*`tmF5(hGGFgFE-9_ zY&z2ds{BDNVymsptiq*GUnT9pqb2LouXvE;Jqn7wc=w`@1~~)5Tci-ZqLWC4KhV?~ zTVmUhVL|+qLwi5)GXC4|pV&myk#@=XWuBZz7n*k$VM`+( zAIdee%|}{tz8M}A`?mT|UR)LNz^Zky4I54BJAKzv)E= z!W-VCWK?T`<6p@lIv+!dr+{{C-zypsb8*ZLx^jgmDMmIp2+<#3_Q?h8cQ6r3s<9_W zvjeq)+J&rv{$Rv+_~#pRXiD?MouEL9r@)q&O_I1t`lI?>l*(~AJCdUGTy_t-_jmG2 zQ=0Hj+vd;j1pnrlPzy#QNw3!p|2C)15X4)osr(2pydYS11UEtWNwHJ1T zv73mN*(bK!uu(0)`U~HTM$AFXR|%>+sj~~McYK5IBJAnKI*v-T4&5$6e4_is%9hiU zx(e7jtiX&>-@Kqa4`(&hM0A@XSfppoH^L?eRRrK09U9a>VdQYs;lyYjj)u@33Shp( z1A_t9>yVDpJ&GE3T-61T7VwXvY!|g>4zjVGVt4*XOnI*_F`pOw!COPcNH}#5&1t@0DcSRE98%d#yzZ;!>ci5x^CCTs|)E4X6ckt>v$r>+! z0x^DOHRLiS%>OoELWEOgDU~rIC`DX^a#dR5NXk=_&cM;LQNcqSsOO$j8%-+%_nx0% zRTauHg=|3fk>AEmt9jW#@^6e`E(Ea*%`Xzra;!KOacv+@c9VrgMW_(XY~(A>Q!26y zB%O{ZEo5M(4jiSsx0}t@4dpo7*2$HkkC^|4N=7Vm~A0{%{Ks>^8JU0CV^Pz zb2VNWY>O(*W1@Pbpr8_e6;h7dUB|ov#V-PX>mZ6L_&48ND|*olVg%(w*_FoH+@vCi zsj+cEOzs~)v~l1b7@CK$mA(2ehVZfOxh|~An9Z&FH4eQEGc{yUV5CMfpeFQfy>%30 zAaU+W!aJ||>~A3;)=X4aO7;$&sCdBXz2WRUou`ncr4?W$o1wx^h6Z5g1!Bpx;x;#^ z0)n@1Gy2DWHD=mlh6Nrk$q?k>+)s|?MQ6(aLVG3Cinz0TmRFg*mIfUy4j6q27eIhm zfTtNH#Tv!qv48h>FP#3kE{a%?xt!gBQ2(OF?P%M3c}reXE$wM^IAuZNvlU-porADc_iO^4+nf3YpgAccaS_q7g*ryM?i6-xtv@jmX zk!iDi{8Wxrp)QT0n`Pt>UFva21M`_pb9>-^QGY3b*mjtY2O0bEK2zi{$pMsBQ&=Z$Ru zy&gP)KVNt7;UHFaTLA<3pIj)Bf$LBEhUbzq-(1R)8T$yacZ_ed3}Jy6LVFQOoAlrP z&3sn~o@Nxl38qSz_TIOO5D44GoX9BJjG$-4cp;JrFxJ8sZT7Ju>~Nt#;5)6`y-8>- zM7zhW(OW#w@8U3reh^1lHgwtP%Th_}=uaAz)1i8G>g(*9`oh2pvIZhLqmx+lqdUx@ z=TmlpFd}brJ;UKStVp0i5ruI{Q1J&DqNf&ofx+zx_Uzh>vm7TVZ4c7(@h-Ajw0UI_ zD!hVD!Ha04)&kG7KI!n#+pSQ{ zp#h#MsflCa9KBc5vBEe^L%bKn0(H zMQ2|e7Q=!qv+D;CseH4gq6p`DC*tGAx>e7p9CnjOyXTv5pV3MQI^o)53hP?qzFhMS zvX1s_q2`G&XC9Lgp+v-wV0w^($cY8Aqz4sDPVG%a$cfOe3V-m8Ki`Nm+k}kx zme6$qsELx7{{a^B`(rH%SSTxy=USQl0D?}Nxd54ig>rwH{)=Zt3Jb3bq88ZE36X~W zhifjms{_IWO~A18svr{56aJ%o2=)Wp_H4>47~+5!nmOC0PLp_-7BRLt5`wB; z7(X-K-&3noN0lga)UYW8j0ohYkOXoR4MjU(GXKlJ#F7XNU!I|Hfj@HHmHkR#yhGa5 z0c}F0nOE`8|D2hZA)UBJ1{G4Ov*P@TwGc{XX0^rhq=3%va;XH(i1QA<_ZVrn;Fjx; z0I@REH&CEL3alEp&@DG9O+1%{AP|W5gd6t)nJvjNQZKsqQ_I45C?M z*$AWncI&LY`y*-T$9E9<^F2sVZU#Sb~>T)yYRjWorFValiSz&;g-zqH_8dn$< zHwru|#v-rABmbSq4?LBRzI&vs^LgmX=Mb3&*5f}k`sL4MQ}%2)=7@$5&?8Yfex3%% z71|-e^t4!dRxV=`37M-C#Z7$M};@|rJR^yJI{oiX`X@Kvr%1rZj9app!&!IjF0x18b z&G?-vzUtA3iazl`!K$ZDQN>Ny>h~j7p>YKQalCIM);D(Mg*tqeedsj=kzTL=AP3u{ zoR3oW?}=ga?@^xj7sFm;2p{Vga;uX##K|oMjR~-tieDCi@L@-oW1Ba+ZN~3L*|ER-ropT3 zNGFdumJd+@QG5tFK{wLHO@Mfhj}}+_N8dub$ynX?+U+!^1L~!(1W)~~;=9Z&WWxi= zhX4JyK|_zd`z=IMZCI&7`;>J_ykI=SR+>e@e`Olw4qSw(`nzxbxqzMaO!fL%lCw7k zM`l199Nq8BTTHHcltCml;!{4Z!?5!9goql18G~XRT$^M&h_*vq$ED=^nht;#_3w@$ z%aV^GbZUxJ_qOpJ3$icDIQ-o=BAMo;l5RgyL##g+iIe=RSBNz8(^?Y{vgyyW?cSI5 z%*$^W1b6zKLtJ^ci%|U3mcU1-n7{j0!{_(^_^BuL)s|8GfAoO%E=Daz`y7t!@EnP^ z_`A9L{Vc07JKer(5F1<_xIB6B=i3=ky@we8U@AnK134p4viYG=Esn#KgSp?wfUQ9cOybkW#am#H{13KjJr*8aM^QG^e=-dHG>Vg_+?y>%D)ief1|RJjtMnR!S4D8iXsa_j!*o9 z6{d_aAyWIQ{hB;(i7Qaw&3`wKIAI5K*urm?&wnR*+}AqpazczfZyFLx1m)7s`otMO zq$eZf?B9J8J{0i7LNxWF;~=v2pWJ^-&=L4KI{TuN$3IiR(c&cL$X*0#T%4Jy_sAZ9 zuIk=@skf}jzYAvELHo_{n0);nOmDL{=ld^!AA0n4iMXE|P7wQA|B=4)=o3zSfu*f3 zwPcU~5KsS)RQw-|!K-H5Q|pJ$PmC9$Bo%$pTFc_6IVmi0)UWA1m01vn^k*;nW-^}U zqF=ii&OiKsa!XG3TR01>(Ku>2U5G?B><5o5#NLB*6k&MVAxsg>Mkn{0@LU5d1LS-Y z;(3MBAz#Tk1Jw*>^GtRVdV&e@rXn$$z>XmfeiPT3JXyk7YwpgS*+P0aTz*d>N z9I`ho-^rVa@;^X3DFX}P288{IqFWRW9V|j(1efE_DBW^^1Q74(M5eit6^0+z({SAg zGlVeR|G~Fuq-@;C{eo@3iZA6vTeC{a`~qo1@q`F1DLL|QzUE8P-MRobteZe@TP53m z1db=JL;?v9ijyq9orH?KhImpmuOl5t4huk-AL4TP{RKb5DP8oWP1D?EJ9)Q}la=T} zFvLNM41ZH(B|3CS+#rl zImQJp$|E>sR4B9(A%H4LZ-zRG9H^`q;_HE+TpF3&HMp+mX&qWRwBEC%IxB${QA6rQGNAFK9Q9PN5y;iSpog^#BrI zk~~acGT+HOvcU0it&+9i?Ca>Ld`u_p)Z70S6 literal 0 HcmV?d00001 diff --git a/assets/blocks/block_cave.png b/assets/blocks/block_cave.png new file mode 100755 index 0000000000000000000000000000000000000000..8442ba50494c5632a39bef1fd3f2011ff1e24dcd GIT binary patch literal 314 zcmV-A0mc4_P)Px#^hrcPR5*=ewDfTK&%nUIU|9Bs;e3|}1B}hUzyRmN0J0inb;xQNVFpqQun0eT zKuz-)m?$;CA|#o523mw9Gu*s&1I18AA1xJ(Fho{xV$EEtO1zhT%_^-*7fA!vyuLu^Ncg%j|zx;TRWqf9WWO zVGImtUPez-gushy2QY#JZXmWaMF?Q?1%{VNF%+9RCS=zk8;Tz2*bKv_4re-iaqR#> z!(af}P$mWj^3xtR%i%7BB?Dp%Lv{eVdaMbXSn!kOH{n<&1z__D0I$P?PizFlr2qf` M07*qoM6N<$f=3c~`~Uy| literal 0 HcmV?d00001 diff --git a/assets/blocks/dirt_cave.png b/assets/blocks/dirt_cave.png new file mode 100755 index 0000000000000000000000000000000000000000..c7be9fe4819bc9f4840469a3fff8f31b7d62b142 GIT binary patch literal 358 zcmV-s0h#`ZP)Px$AW1|)R5*>Tls%3DF${%YMiZspjsqY-Q&5kz#c}`&in+lwdn6ieK*159Sc#ef z0VklNMnr_3CfHx@mE~oWlBJ`ia}& zCkt4_U>uqajKKgzBdnXYQywxiuZGWi`M{u>Wx*(b@n{0zx88uPUrrshIaJm;2~3&uRuwG@K;;Fu49&b-suUyJXzo?xmM-&Q%+ zHuA!PJ8=xn`l@RV}?#GWXmCQr#hf7hD50R3&GXTBjX*#H0l07*qoM6N<$ Ef_y8RH~;_u literal 0 HcmV?d00001 diff --git a/assets/buttons/ipaddress.png b/assets/buttons/ipaddress.png new file mode 100755 index 0000000000000000000000000000000000000000..fe69c624c8589860493ab87fded653c5ecaaebfe GIT binary patch literal 368 zcmV-$0gwKPP)Px$DoI2^RA_ZeCPVhe}1hePO-s7y#9z0<-ik{jcZ&{s=H zTmO}*l7Z?G`+7iftyXQZgjDJb;Z5(N7F(!8xoV-KHORTvSW_tMAtkG$HAZPH;bjI{ zfr!@fC?%MqlwhF`{bk0zG>PAdPk&8F|@=kh=NiW-U*DlBa+2POze2r)T3C3tY~a5gF` z8lVWS`Q3hFJzmKpKs}3(0W9?dS_U*_JkSp7U{me z>UA#m#hlO6{pSCan^Zh?**ViE*51oce7Sb=!bi(+?TxXm3?3e*=a@Y+oWV6Ss>%K2 zjFt}VXHK(r%_>o?_U8O$rSj~q+nco?))%Ng3EnkN?O4FMU1y@hH20{S&{5Q6l?pzx z?tImRpb(FE$75n=oDcqv@HzddiGZIyIC^fs{r*k=S&Ob0M-TV?R|(S!n65kn#tDO` LtDnm{r-UW|WFowc literal 0 HcmV?d00001 diff --git a/assets/buttons/online.png b/assets/buttons/online.png new file mode 100755 index 0000000000000000000000000000000000000000..e7d8c57ca49c7f55e1f09b5f8afbb82475db4d48 GIT binary patch literal 451 zcmeAS@N?(olHy`uVBq!ia0vp^DL`z_!3HFM{dms{q!^2X+?^QKos)S9WY>DSIEGZr zc{}H{FSDTt120#&3*($%rrJQpMYgjZOkxrU=X)q1IWeQ~+PTkFht7FS{Iu~?PXFI| zZ#;HZ*57+KIbnzFT}I2a$@6XY+4a0R{_5u5nk7^2C|ZaxIV&3AA(D&tdCI?gcfT*L zzSd6EUVH2O&v&OE(++&)t-H`Ei%s!zLeRBKMk~9QXP?qq+M1oLrTbOl@&>6-@*Qhr zC4Ej!V&-aW6iQ0m-e&SKI;BL`@$Cf7PYwR;aZz`t`vhsm)qPP3@i^=9=jyiW4b%Is zpPC>WRBS3}n&^EzJJo|XQ?lf2Y#598@zX8uCR|*t?flZ6d&Z)DSGZ(E^YtVYix^Z@ zlCJjsl2z_nZ7VU!@9V^*Kx-A%Y|n7TwaagtdOI#zvy#)Yc758Vb9YT$k9VttaHK9h zJJo?D-1%CcQ-TId8y@0M%>L*5XH4F>!#SabSu&>n#sux01YoQ%c)I$ztaD0e0sxlo Bu)Y8Q literal 0 HcmV?d00001 diff --git a/assets/buttons/port.png b/assets/buttons/port.png new file mode 100755 index 0000000000000000000000000000000000000000..ae67b287ea7d33c6415f439716f736fbc4e8ab27 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^dO$47!3HEPS?)vvDaPU;cPEB*=VV?2IjcNf978-h z-%dZzcR+yWSm=%_nfLYIEY|UrP3rh*yz-h#-SW<-pSNxn6KG+{Pp-Ol%+c-`Q^=#$ zz0*$AIP45?l4ieUap2X8X*&cSI}|Zrah!fItoQu_6Em-tH@^R7X3v?O+#6o+cPeV1 zSM<3nS46*-Pt`ja_RynkRjpM{TJ!dg*{A*ApIi6wRE0mwZu{Wf^K;Ei`_^h*w=f5y o70J5Ck1jsC?emWnW&O|Oa{~jq^JgYs0lJ#O)78&qol`;+04HK+z5oCK literal 0 HcmV?d00001 diff --git a/assets/buttons/survival.png b/assets/buttons/survival.png new file mode 100755 index 0000000000000000000000000000000000000000..fa6c38a3abae53292c3164d28bdbb48131b5a1f2 GIT binary patch literal 495 zcmeAS@N?(olHy`uVBq!ia0vp^DL`z_!3HFM{dms{q!^2X+?^QKos)S9WUuseaSW-L z^LCDJUbBIKTc+r?73?o|xu4iu)wJsRW1+iG7(%y2Zt3poaw$90!&|3zKl}!Ai<>TGbxkfDQEe9qDNC+`GJ0*B<@NhON zDjKM;w4n(WdYHawzbN~s$j&M&*$B*esu5kd8xxz(=Pn6Nib4zuQ@BB`fJYQ zGZO@^t?m%AX7~Q~bi!1{ds{>byQb=#)NEdza&}_JrUbFlk3btwh+X1We%80b<)_cl zi0XBEij!M@KKPYrGJirqb@zknx6}Gm#U6kAab<_oTU(XZH5)TtyD6Mow0q5ssc()> z@}1_cmECk-V%mf)OKMk~73=pXdj4oF)6L%#CQaGH#TohZ;GZqqum1Aatko>r%zyLI zv~un;+ur#MAhZ+w?BF+$jJ6 literal 0 HcmV?d00001 diff --git a/assets/fonts/Blocky.ttf b/assets/fonts/Blocky.ttf new file mode 100755 index 0000000000000000000000000000000000000000..cd6e4c91bc30c97a958d5765d5441287e458c3e0 GIT binary patch literal 29036 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 ARsaA1 literal 0 HcmV?d00001 diff --git a/assets/leveldata/level_data.txt b/assets/leveldata/level_data.txt new file mode 100755 index 0000000000000000000000000000000000000000..26d1e6ef07694d84d484054f826257771af3f0ed GIT binary patch literal 4768 zcmeHJ%MF7t37w2U!7o9)=V*n+YC^_i^Y&v_;6f*{5x@ z_%j}4w+vE)jn?*n3aTA*AJfoR{pw;3CA=lrZe2UC*|S)K7ma*5R-KnaTMfAk%^#0w z-v@Dje6-boILQDWVB*G6u-Jdhk{To*-%^j4qhNW6*k?6M4RIbvECa{NEnIb}hVps# zGO&s@HI(iOsk=*q+|#=>Pz|6zry;JszP)nQa(1-y8YCO#%UcXS!cZLEXWckL7UsZ6 zYp`6Jjq>Auf5m>D)Y9S$4nvohY!kYDjV=HerHw;ott%S9ndKZp00i_>zopr0A6fBX8-^I literal 0 HcmV?d00001 diff --git a/client.py b/client.py new file mode 100755 index 0000000..bfbf248 --- /dev/null +++ b/client.py @@ -0,0 +1,358 @@ +# Stop Imports +if __name__ == "__main__": + # Imports + import pygame + import pickle + import threading + import sys + import os + import re + from inputs import Button + from inputs import GetInputs + from inputs import IpAddressInput + from inputs import PortInput + from inputs import NameInput + from network import Client + # Pygame Setup + screen_width = 1280 + screen_height = 700 + screen = pygame.display.set_mode((screen_width, screen_height)) + pygame.display.set_caption("Shooty Arena") + clock = pygame.time.Clock() + font = pygame.font.SysFont(None, 12) + text = font.render("", False, (0, 0, 0)) + # Variables + menu_main = True + menu_online = False + menu_offline = False + menu_custom = False + menu_settings = False + online = False + offline = False + custom = False + ip_address_input = None + port_input = None + ip_address = None + port = None + network = None + facing = 1 + require_name = True + name = None + # Load Images + def resource_path(relative_path): + try: + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath(".") + return os.path.join(base_path, relative_path) + cave_image_menu_background_url = resource_path("assets/backgrounds/background_menu.png") + cave_image_menu_background = pygame.image.load(cave_image_menu_background_url).convert() + cave_image_game_background_url = resource_path("assets/backgrounds/background_cave.png") + cave_image_game_background = pygame.image.load(cave_image_game_background_url).convert() + cave_image_dirt_url = resource_path("assets/blocks/dirt_cave.png") + cave_image_dirt = pygame.image.load(cave_image_dirt_url).convert() + cave_image_block_url = resource_path("assets/blocks/block_cave.png") + cave_image_block = pygame.image.load(cave_image_block_url).convert() + cave_image_player_url = resource_path("assets/players/player.png") + cave_image_player = pygame.image.load(cave_image_player_url).convert() + cave_image_player_flipped = pygame.transform.flip(cave_image_player, True, False).convert() + cave_image_online_url = resource_path("assets/buttons/online.png") + cave_image_online = pygame.image.load(cave_image_online_url).convert() + cave_image_survival_url = resource_path("assets/buttons/survival.png") + cave_image_survival = pygame.image.load(cave_image_survival_url).convert() + cave_image_menu_url = resource_path("assets/buttons/menu.png") + cave_image_menu = pygame.image.load(cave_image_menu_url).convert() + cave_image_ip_address_url = resource_path("assets/buttons/ipaddress.png") + cave_image_ip_address = pygame.image.load(cave_image_ip_address_url).convert_alpha() + cave_image_port_url = resource_path("assets/buttons/port.png") + cave_image_port = pygame.image.load(cave_image_port_url).convert_alpha() + # Create Buttons + cave_button_online = Button(screen, cave_image_online, 480, 450) + cave_button_online_two = Button(screen, cave_image_online, 580, 450) + cave_button_survival = Button(screen, cave_image_survival, 700, 450) + cave_button_menu = Button(screen, cave_image_menu, 1150, 30) + cave_button_ip_address = Button(screen, cave_image_ip_address, 5, 5) + cave_button_port = Button(screen, cave_image_port, 50, 50) + # Create Input Fields + input_ip_address = IpAddressInput() + input_port = PortInput() + input_name = NameInput() + # Generate World Data + pickle_in_url = resource_path("assets/leveldata/level_data.txt") + pickle_in = open(pickle_in_url, "rb") + tile_list = pickle.load(pickle_in) + world_data = [] + tile_size = 20 + row_count = 0 + for row in tile_list: + column_count = 0 + for tile in row: + # Dirt + if tile == 1: + block = cave_image_dirt + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Down + if tile == 2: + block = cave_image_block + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Left + if tile == 3: + block = cave_image_block + block = pygame.transform.rotate(block, 270) + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Right + if tile == 4: + block = cave_image_block + block = pygame.transform.rotate(block, 90) + tile = pygame.transform.scale(block, (20,20)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + column_count+=1 + row_count+=1 + # Bullet Class + class Bullet(object): + # Init + def __init__(self, x, y, radius, color, facing): + self.x = x + self.y = y + self.radius = radius + self.color = color + self.facing = facing + self.vel = 20 * facing + # Draw + def draw(self, screen): + pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius) + # Game Loop + run = True + pygame.font.init() + pygame.init() + font = pygame.font.SysFont("../assets/fonts/Blocky.tff", 40) + font_player = pygame.font.SysFont("../assets/fonts/Blocky.tff", 18) + while run: + # Tick + clock.tick(60) + # Main Menu + if menu_main: + # Background + screen.blit(cave_image_menu_background, (0, 0)) + # Online Button + if cave_button_online.draw(): + menu_main = False + menu_online = True + # Survival Button + if cave_button_survival.draw(): + pass + # Print Text + screen.blit(text, (530, 550)) + # Online Menu + if menu_online: + screen.blit(cave_image_game_background, (0, 0)) + require_name = True + if require_name: + input_name.update(events) + screen.blit(input_name.get_surface(), (650, 300)) + text = font.render("Choose a name: ", True, (255, 255, 0)) + screen.blit(text, (400, 300)) + if input_name.get_text(): + pattern = re.compile("[A-Za-z0-9]+") + valid_name = pattern.fullmatch(input_name.get_text()) + if cave_button_online_two.draw(): + if valid_name: + try: + print("tye") + port = int(9306) + ip_address = "127.0.0.1" + network = Client(ip_address, port) + message = network.receive_string() + print("rec") + if message == "!JOIN": + text = font.render("", True, (255, 255, 0)) + menu_online = False + online = True + player_name = f"{input_name.get_text()}" + network.send_string(player_name) + require_name = False + elif message == "!FULL": + text = font.render("Server Full", True, (255, 255, 0)) + except: + text = font.render("Server Not Found", True, (255, 255, 0)) + if not valid_name: + text2 = font.render("Please only use letters and numbers.", True, (255, 255, 0)) + screen.blit(text2, (375, 375)) + if cave_button_menu.draw(): + text = font.render("", True, (255, 255, 0)) + menu_online = False + menu_main = True + # Online Game + if online: + # Background + screen.blit(cave_image_game_background, (0, 0)) + # World + for tile in world_data: + screen.blit(tile[0], (tile[1].x, tile[1].y - 20)) + # Menu Button + if cave_button_menu.draw(): + network.send_string("!DISSCONNECT") + text = font.render("", True, (255, 255, 0)) + online = False + menu_main = True + if online: + # Send Inputs To Server + up, left, right, mouse_position, facing = GetInputs(facing) + network.send_string(f"{up}@{left}@{right}@{mouse_position}@{pygame.mouse.get_pressed()}@{facing}") + # Receive Inputs + response = network.receive_string() + all_bullets = network.receive_byte() + score = network.receive_string() + facings = network.receive_string() + names = network.receive_string() + response = response.replace("[","") + response = response.replace("]","") + response = response.replace(" ","") + response = response.split(",") + facings = facings.replace("[","") + facings = facings.replace("]","") + facings = facings.replace(" ","") + facings = facings.split(",") + # Player Coordinates + onex = int(response[0]) + oney = int(response[1]) + twox = int(response[2]) + twoy = int(response[3]) + threex = int(response[4]) + threey = int(response[5]) + fourx = int(response[6]) + foury = int(response[7]) + fivex = int(response[8]) + fivey = int(response[9]) + sixx = int(response[10]) + sixy = int(response[11]) + # Player Names + names = names.replace("[","") + names = names.replace("]","") + names = names.replace("'","") + names = names.replace(" ","") + names = names.split(",") + player_one_name = names[0] + player_two_name = names[1] + player_three_name = names[2] + player_four_name = names[3] + player_five_name = names[4] + player_six_name = names[5] + # Display Player One + if facings[0] == "1": + text = font_player.render(player_one_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (onex - (text_width / 2) + 10, oney - 20)) + screen.blit(cave_image_player, (onex, oney)) + else: + text = font_player.render(player_one_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (onex - (text_width / 2) + 10, oney - 20)) + screen.blit(cave_image_player_flipped, (onex, oney)) + # Display Player Two + if facings[1] == "1": + text = font_player.render(player_two_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (twox - (text_width / 2) + 10, twoy - 20)) + screen.blit(cave_image_player, (twox, twoy)) + else: + text = font_player.render(player_two_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (twox - (text_width / 2) + 10, twoy - 20)) + screen.blit(cave_image_player_flipped, (twox, twoy)) + # Display Player Three + if facings[2] == "1": + text = font_player.render(player_three_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (threex - (text_width / 2) + 10, threey - 20)) + screen.blit(cave_image_player, (threex, threey)) + else: + text = font_player.render(player_three_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (threex - (text_width / 2) + 10, threey - 20)) + screen.blit(cave_image_player_flipped, (threex, threey)) + # Display Player 4 + if facings[3] == "1": + text = font_player.render(player_four_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (fourx - (text_width / 2) + 10, foury - 20)) + screen.blit(cave_image_player, (fourx, foury)) + else: + text = font_player.render(player_four_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (fourx - (text_width / 2) + 10, foury - 20)) + screen.blit(cave_image_player_flipped, (fourx, foury)) + # Display Player 5 + if facings[4] == "1": + text = font_player.render(player_five_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (fivex - (text_width / 2) + 10, fivey - 20)) + screen.blit(cave_image_player, (fivex, fivey)) + else: + text = font_player.render(player_five_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (fivex - (text_width / 2) + 10, fivey - 20)) + screen.blit(cave_image_player_flipped, (fivex, fivey)) + # Display Player 6 + if facings[5] == "1": + text = font_player.render(player_six_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (sixx - (text_width / 2) + 10, sixy - 20)) + screen.blit(cave_image_player, (sixx, sixy)) + else: + text = font_player.render(player_six_name, True, (255, 255, 0)) + text_width = text.get_width() + screen.blit(text, (sixx - (text_width / 2) + 10, sixy - 20)) + screen.blit(cave_image_player_flipped, (sixx, sixy)) + # Display Bullets + bullets = pickle.loads(all_bullets) + for bullet in bullets[0]: + bullet.draw(screen) + for bullet in bullets[1]: + bullet.draw(screen) + for bullet in bullets[2]: + bullet.draw(screen) + for bullet in bullets[3]: + bullet.draw(screen) + for bullet in bullets[4]: + bullet.draw(screen) + for bullet in bullets[5]: + bullet.draw(screen) + # Kill Counter + text = font.render(f"Kills: {score}", False, (255, 255, 0)) + screen.blit(text, (20, 20)) + # Event Handler + events = pygame.event.get() + for event in events: + # QUIT + if event.type == pygame.QUIT: + run = False + # Update Display + pygame.display.update() + # End Of Game Loop + # Final Dissconnection Message + try: + network.send_string("!DISSCONNECT") + except: + pass +else: + exit() diff --git a/inputs.py b/inputs.py new file mode 100755 index 0000000..87568aa --- /dev/null +++ b/inputs.py @@ -0,0 +1,389 @@ +# Only Allow Imports +if __name__ == "__main__": + exit() +else: + # Imports + import pygame + import time + import pygame.locals as pl + import os.path + pygame.font.init() + # Button + class Button(): + # Init + def __init__(self, screen, image, x, y): + self.screen = screen + self.image = image + self.rect = self.image.get_rect() + self.rect.x = x + self.rect.y = y + # Draw + def draw(self): + self.screen.blit(self.image, self.rect) + if self.rect.collidepoint(pygame.mouse.get_pos()): + if pygame.mouse.get_pressed()[0] == 1: + time.sleep(0.1) + return True + return False + # Get Inputs + def GetInputs(facing): + up = 0 + left = 0 + right = 0 + mouse_position = pygame.mouse.get_pos() + key = pygame.key.get_pressed() + if key[pygame.K_w]: + up = 1 + if key[pygame.K_a]: + left = 1 + facing = -1 + if key[pygame.K_d]: + right = 1 + facing = 1 + return up, left, right, mouse_position, facing + # ip_addres + class IpAddressInput: + # Init + def __init__( + # Choose Parameters + self, + initial_string = "", + font_family = "", + font_size = 35, + antialias = False, + text_color = (0, 0, 0), + cursor_color = (0, 0, 1), + repeat_keys_initial_ms = 400, + repeat_keys_interval_ms = 35, + max_string_length = 39, + password = False): + # Rest Of Init + self.antialias = antialias + self.text_color = text_color + self.font_size = font_size + self.max_string_length = max_string_length + self.password = password + self.input_string = initial_string + if not os.path.isfile(font_family): + font_family = pygame.font.match_font(font_family) + self.font_object = pygame.font.Font(font_family, font_size) + self.surface = pygame.Surface((1, 1)) + self.surface.set_alpha(0) + self.keyrepeat_counters = {} + self.keyrepeat_intial_interval_ms = repeat_keys_initial_ms + self.keyrepeat_interval_ms = repeat_keys_interval_ms + self.cursor_surface = pygame.Surface((int(self.font_size / 20 + 1), self.font_size)) + self.cursor_surface.fill(cursor_color) + self.cursor_position = len(initial_string) + self.cursor_visible = True + self.cursor_switch_ms = 500 + self.cursor_ms_counter = 0 + self.clock = pygame.time.Clock() + # Update + def update(self, events): + for event in events: + if event.type == pygame.KEYDOWN: + self.cursor_visible = True + if event.key not in self.keyrepeat_counters: + if not event.key == pl.K_RETURN: + self.keyrepeat_counters[event.key] = [0, event.unicode] + if event.key == pl.K_BACKSPACE: + self.input_string = ( + self.input_string[:max(self.cursor_position - 1, 0)] + + self.input_string[self.cursor_position:] + ) + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_DELETE: + self.input_string = ( + self.input_string[:self.cursor_position] + + self.input_string[self.cursor_position + 1:] + ) + elif event.key == pl.K_RETURN: + return True + elif event.key == pl.K_RIGHT: + self.cursor_position = min(self.cursor_position + 1, len(self.input_string)) + elif event.key == pl.K_LEFT: + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_END: + self.cursor_position = len(self.input_string) + elif event.key == pl.K_HOME: + self.cursor_position = 0 + elif len(self.input_string) < self.max_string_length or self.max_string_length == -1: + self.input_string = ( + self.input_string[:self.cursor_position] + + event.unicode + + self.input_string[self.cursor_position:] + ) + self.cursor_position += len(event.unicode) + elif event.type == pl.KEYUP: + if event.key in self.keyrepeat_counters: + del self.keyrepeat_counters[event.key] + for key in self.keyrepeat_counters: + self.keyrepeat_counters[key][0] += self.clock.get_time() # Update clock + if self.keyrepeat_counters[key][0] >= self.keyrepeat_intial_interval_ms: + self.keyrepeat_counters[key][0] = ( + self.keyrepeat_intial_interval_ms + - self.keyrepeat_interval_ms + ) + event_key, event_unicode = key, self.keyrepeat_counters[key][1] + pygame.event.post(pygame.event.Event(pl.KEYDOWN, key=event_key, unicode=event_unicode)) + string = self.input_string + if self.password: + string = "*" * len(self.input_string) + self.surface = self.font_object.render(string, self.antialias, self.text_color) + self.cursor_ms_counter += self.clock.get_time() + if self.cursor_ms_counter >= self.cursor_switch_ms: + self.cursor_ms_counter %= self.cursor_switch_ms + self.cursor_visible = not self.cursor_visible + if self.cursor_visible: + cursor_y_pos = self.font_object.size(self.input_string[:self.cursor_position])[0] + if self.cursor_position > 0: + cursor_y_pos -= self.cursor_surface.get_width() + self.surface.blit(self.cursor_surface, (cursor_y_pos, 0)) + self.clock.tick() + return False + # Other Funtions + def get_surface(self): + return self.surface + def get_text(self): + return self.input_string + def get_cursor_position(self): + return self.cursor_position + def set_text_color(self, color): + self.text_color = color + def set_cursor_color(self, color): + self.cursor_surface.fill(color) + def clear_text(self): + self.input_string = "" + self.cursor_position = 0 + # Class + class PortInput: + # Init + def __init__( + # Choose Parameters + self, + initial_string = "", + font_family = "", + font_size = 35, + antialias = False, + text_color = (0, 0, 0), + cursor_color = (0, 0, 1), + repeat_keys_initial_ms = 400, + repeat_keys_interval_ms = 35, + max_string_length = 39, + password = False): + # Rest Of Init + self.antialias = antialias + self.text_color = text_color + self.font_size = font_size + self.max_string_length = max_string_length + self.password = password + self.input_string = initial_string + if not os.path.isfile(font_family): + font_family = pygame.font.match_font(font_family) + self.font_object = pygame.font.Font(font_family, font_size) + self.surface = pygame.Surface((1, 1)) + self.surface.set_alpha(0) + self.keyrepeat_counters = {} + self.keyrepeat_intial_interval_ms = repeat_keys_initial_ms + self.keyrepeat_interval_ms = repeat_keys_interval_ms + self.cursor_surface = pygame.Surface((int(self.font_size / 20 + 1), self.font_size)) + self.cursor_surface.fill(cursor_color) + self.cursor_position = len(initial_string) + self.cursor_visible = True + self.cursor_switch_ms = 500 + self.cursor_ms_counter = 0 + self.clock = pygame.time.Clock() + # Update + def update(self, events): + for event in events: + if event.type == pygame.KEYDOWN: + self.cursor_visible = True + if event.key not in self.keyrepeat_counters: + if not event.key == pl.K_RETURN: + self.keyrepeat_counters[event.key] = [0, event.unicode] + if event.key == pl.K_BACKSPACE: + self.input_string = ( + self.input_string[:max(self.cursor_position - 1, 0)] + + self.input_string[self.cursor_position:] + ) + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_DELETE: + self.input_string = ( + self.input_string[:self.cursor_position] + + self.input_string[self.cursor_position + 1:] + ) + elif event.key == pl.K_RETURN: + return True + elif event.key == pl.K_RIGHT: + self.cursor_position = min(self.cursor_position + 1, len(self.input_string)) + elif event.key == pl.K_LEFT: + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_END: + self.cursor_position = len(self.input_string) + elif event.key == pl.K_HOME: + self.cursor_position = 0 + elif len(self.input_string) < self.max_string_length or self.max_string_length == -1: + self.input_string = ( + self.input_string[:self.cursor_position] + + event.unicode + + self.input_string[self.cursor_position:] + ) + self.cursor_position += len(event.unicode) + elif event.type == pl.KEYUP: + if event.key in self.keyrepeat_counters: + del self.keyrepeat_counters[event.key] + for key in self.keyrepeat_counters: + self.keyrepeat_counters[key][0] += self.clock.get_time() # Update clock + if self.keyrepeat_counters[key][0] >= self.keyrepeat_intial_interval_ms: + self.keyrepeat_counters[key][0] = ( + self.keyrepeat_intial_interval_ms + - self.keyrepeat_interval_ms + ) + event_key, event_unicode = key, self.keyrepeat_counters[key][1] + pygame.event.post(pygame.event.Event(pl.KEYDOWN, key=event_key, unicode=event_unicode)) + string = self.input_string + if self.password: + string = "*" * len(self.input_string) + self.surface = self.font_object.render(string, self.antialias, self.text_color) + self.cursor_ms_counter += self.clock.get_time() + if self.cursor_ms_counter >= self.cursor_switch_ms: + self.cursor_ms_counter %= self.cursor_switch_ms + self.cursor_visible = not self.cursor_visible + if self.cursor_visible: + cursor_y_pos = self.font_object.size(self.input_string[:self.cursor_position])[0] + if self.cursor_position > 0: + cursor_y_pos -= self.cursor_surface.get_width() + self.surface.blit(self.cursor_surface, (cursor_y_pos, 0)) + self.clock.tick() + return False + # Other Funtions + def get_surface(self): + return self.surface + def get_text(self): + return self.input_string + def get_cursor_position(self): + return self.cursor_position + def set_text_color(self, color): + self.text_color = color + def set_cursor_color(self, color): + self.cursor_surface.fill(color) + def clear_text(self): + self.input_string = "" + self.cursor_position = 0 + # Class + class NameInput: + # Init + def __init__( + # Choose Parameters + self, + initial_string = "", + font_family = "", + font_size = 40, + antialias = False, + text_color = (255, 255, 0), + cursor_color = (255, 255, 0), + repeat_keys_initial_ms = 400, + repeat_keys_interval_ms = 35, + max_string_length = 10, + password = False): + # Rest Of Init + self.antialias = antialias + self.text_color = text_color + self.font_size = font_size + self.max_string_length = max_string_length + self.password = password + self.input_string = initial_string + if not os.path.isfile(font_family): + font_family = pygame.font.match_font(font_family) + self.font_object = pygame.font.Font(font_family, font_size) + self.surface = pygame.Surface((1, 1)) + self.surface.set_alpha(0) + self.keyrepeat_counters = {} + self.keyrepeat_intial_interval_ms = repeat_keys_initial_ms + self.keyrepeat_interval_ms = repeat_keys_interval_ms + self.cursor_surface = pygame.Surface((int(self.font_size / 20 + 1), self.font_size)) + self.cursor_surface.fill(cursor_color) + self.cursor_position = len(initial_string) + self.cursor_visible = True + self.cursor_switch_ms = 500 + self.cursor_ms_counter = 0 + self.clock = pygame.time.Clock() + # Update + def update(self, events): + for event in events: + if event.type == pygame.KEYDOWN: + self.cursor_visible = True + if event.key not in self.keyrepeat_counters: + if not event.key == pl.K_RETURN: + self.keyrepeat_counters[event.key] = [0, event.unicode] + if event.key == pl.K_BACKSPACE: + self.input_string = ( + self.input_string[:max(self.cursor_position - 1, 0)] + + self.input_string[self.cursor_position:] + ) + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_DELETE: + self.input_string = ( + self.input_string[:self.cursor_position] + + self.input_string[self.cursor_position + 1:] + ) + elif event.key == pl.K_RETURN: + return True + elif event.key == pl.K_RIGHT: + self.cursor_position = min(self.cursor_position + 1, len(self.input_string)) + elif event.key == pl.K_LEFT: + self.cursor_position = max(self.cursor_position - 1, 0) + elif event.key == pl.K_END: + self.cursor_position = len(self.input_string) + elif event.key == pl.K_HOME: + self.cursor_position = 0 + elif len(self.input_string) < self.max_string_length or self.max_string_length == -1: + self.input_string = ( + self.input_string[:self.cursor_position] + + event.unicode + + self.input_string[self.cursor_position:] + ) + self.cursor_position += len(event.unicode) + elif event.type == pl.KEYUP: + if event.key in self.keyrepeat_counters: + del self.keyrepeat_counters[event.key] + for key in self.keyrepeat_counters: + self.keyrepeat_counters[key][0] += self.clock.get_time() # Update clock + if self.keyrepeat_counters[key][0] >= self.keyrepeat_intial_interval_ms: + self.keyrepeat_counters[key][0] = ( + self.keyrepeat_intial_interval_ms + - self.keyrepeat_interval_ms + ) + event_key, event_unicode = key, self.keyrepeat_counters[key][1] + pygame.event.post(pygame.event.Event(pl.KEYDOWN, key=event_key, unicode=event_unicode)) + string = self.input_string + if self.password: + string = "*" * len(self.input_string) + self.surface = self.font_object.render(string, self.antialias, self.text_color) + self.cursor_ms_counter += self.clock.get_time() + if self.cursor_ms_counter >= self.cursor_switch_ms: + self.cursor_ms_counter %= self.cursor_switch_ms + self.cursor_visible = not self.cursor_visible + if self.cursor_visible: + cursor_y_pos = self.font_object.size(self.input_string[:self.cursor_position])[0] + if self.cursor_position > 0: + cursor_y_pos -= self.cursor_surface.get_width() + self.surface.blit(self.cursor_surface, (cursor_y_pos, 0)) + self.clock.tick() + return False + # Other Funtions + def get_surface(self): + return self.surface + def get_text(self): + return self.input_string + def get_cursor_position(self): + return self.cursor_position + def set_text_color(self, color): + self.text_color = color + def set_cursor_color(self, color): + self.cursor_surface.fill(color) + def clear_text(self): + self.input_string = "" + self.cursor_position = 0 + \ No newline at end of file diff --git a/level_creation.py b/level_creation.py new file mode 100755 index 0000000..8a88faa --- /dev/null +++ b/level_creation.py @@ -0,0 +1,51 @@ +# Stop Imports +if __name__ == "__main__": + # Imports + import pygame + import pickle + # Tile List Data + tile_list = [ + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 4,], + [3, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,], + [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4,] + ] + # Save Tile List + pickle_out = open("./leveldata/level_data.txt", "wb") + pickle.dump(tile_list, pickle_out) + pickle_out.close() +# If Imported +else: + exit() \ No newline at end of file diff --git a/network.py b/network.py new file mode 100755 index 0000000..fb9d183 --- /dev/null +++ b/network.py @@ -0,0 +1,105 @@ +# Only Allow Imports +if __name__ == "__main__": + exit() +else: + # Imports + import socket + # Client + class Client(): + # Init + def __init__(self, ip_address, port): + self.HEADER = 100 + self.SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.SOCKET.connect((ip_address, port)) + # Send String + def send_string(self, message): + message = message.encode("utf-8") + message_length = len(message) + message_length = str(message_length).encode("utf-8") + message_length += b" " * (self.HEADER - len(message_length)) + self.SOCKET.send(message_length) + self.SOCKET.send(message) + # Send Byte + def send_byte(self, message): + message_length = len(message) + message_length = str(message_length).encode("utf-8") + message_length += b" " * (self.HEADER - len(message_length)) + self.SOCKET.send(message_length) + self.SOCKET.send(message) + # Receive String + def receive_string(self): + message = bytearray(b'') + message_length = self.SOCKET.recv(self.HEADER).decode("utf-8") + if message_length: + message_length = int(message_length) + received_message_length = 0 + while message_length > received_message_length: + received = self.SOCKET.recv(message_length - received_message_length) + message = message + received + received_message_length += len(received) + message = message.decode("utf-8") + return message + # Receive Byte + def receive_byte(self): + message = bytearray(b'') + message_length = self.SOCKET.recv(self.HEADER).decode("utf-8") + if message_length: + message_length = int(message_length) + received_message_length = 0 + while message_length > received_message_length: + received = self.SOCKET.recv(message_length - received_message_length) + message = message + received + received_message_length += len(received) + return message + # Server + class Server(): + # Init + def __init__(self, ip_address, port): + self.HEADER = 100 + self.SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.SOCKET.bind((ip_address, port)) + self.SOCKET.listen() + # Connection + def receive_connection(self): + connection, address = self.SOCKET.accept() + return connection, address + # Send String + def send_string(self, connection, message): + message = message.encode("utf-8") + message_length = len(message) + message_length = str(message_length).encode("utf-8") + message_length += b" " * (self.HEADER - len(message_length)) + connection.send(message_length) + connection.send(message) + # Send Byte + def send_byte(self, connection, message): + message_length = len(message) + message_length = str(message_length).encode("utf-8") + message_length += b" " * (self.HEADER - len(message_length)) + connection.send(message_length) + connection.send(message) + # Receive String + def receive_string(self, connection): + message = bytearray(b'') + message_length = connection.recv(self.HEADER).decode("utf-8") + if message_length: + message_length = int(message_length) + received_message_length = 0 + while message_length > received_message_length: + received = connection.recv(message_length - received_message_length) + message = message + received + received_message_length += len(received) + message = message.decode("utf-8") + return message + # Receive Byte + def receive_byte(self, connection): + message = bytearray(b'') + message_length = connection.recv(self.HEADER).decode("utf-8") + if message_length: + message_length = int(message_length) + received_message_length = 0 + while message_length > received_message_length: + received = connection.recv(message_length - received_message_length) + message = message + received + received_message_length += len(received) + return message \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3f2928f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pygame==2.3.0 +urllib3==1.26.15 diff --git a/server.py b/server.py new file mode 100755 index 0000000..6e9d9bb --- /dev/null +++ b/server.py @@ -0,0 +1,335 @@ +# Stop Imports +if __name__ == "__main__": + # Imports + print() + print("Importing Libraries ...") + print() + import pygame + import pickle + import socket + import threading + import time + import random + import urllib3 + from urllib.request import urlopen + import re + from network import Server + print() + # Assgin Variables + print("Assigning Variables ...") + print() + ip_address_local = "ERROR" + ip_address_public = "ERROR" + port = 9306 + clock = pygame.time.Clock() + tile_size = 20 + world_data = [] + full = False + connected_players = [False, False, False, False, False, False] + player_names = [None, None, None, None, None, None] + character = [None, None, None, None, None, None] + player_positions = [[0, - 20], [0, - 20], [0, - 20], [0, - 20], [0, - 20], [0, - 20]] + player_facings = [1, 1, 1, 1, 1, 1] + player_bullets = [[], [], [], [], [], []] + player_scores = [0, 0, 0, 0 ,0, 0] + # Loading Images + print("Loading Images ...") + print() + cave_image_dirt = pygame.image.load("./assets/blocks/dirt_cave.png") + cave_image_block = pygame.image.load("./assets/blocks/block_cave.png") + cave_image_player = "./assets/players/player.png" + # Find Ip Addresses + print("Finding Local Ip Address ...") + print() + try: + ip_address_local = socket.gethostbyname(socket.gethostname()) + except: + pass + print("Finding Public Ip Address ...") + print() + try: + data = str(urlopen("http://checkip.dyndns.com/").read()) + ip_address_public = re.compile(r"Address: (\d+.\d+.\d+.\d+)").search(data).group(1) + except: + pass + # Generate World Data + print("Generating World Data ...") + print() + pickle_in = open("./assets/leveldata/level_data.txt", "rb") + tile_list = pickle.load(pickle_in) + row_count = 0 + for row in tile_list: + column_count = 0 + for tile in row: + # Dirt + if tile == 1: + block = cave_image_dirt + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Down + if tile == 2: + block = cave_image_block + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Left + if tile == 3: + block = cave_image_block + block = pygame.transform.rotate(block, 270) + tile = pygame.transform.scale(block, (tile_size, tile_size)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + # Right + if tile == 4: + block = cave_image_block + block = pygame.transform.rotate(block, 90) + tile = pygame.transform.scale(block, (20,20)) + tile_rect = tile.get_rect() + tile_rect.y = row_count * tile_size + tile_rect.x = column_count * tile_size + tile = (tile, tile_rect) + world_data.append(tile) + column_count+=1 + row_count+=1 + # Generate Spawn + print("Defining Functions ...") + print() + def generate_spawn(): + player_x = random.randint(4, 240) * 5 + player_y = - 20 + return player_x, player_y + # Bullets + class Bullet(object): + # Init + def __init__(self, x, y, radius, color, facing): + self.x = x + self.y = y + self.radius = radius + self.color = color + try: + self.facing = int(facing) + except: + self.facing = -1 + self.vel = 20 * self.facing + # Player + class Player(): + # Init + def __init__(self): + self.reset() + self.alive = False + # Update + def update(self, up, left, right, player): + # Change in x and y + dx = 0 + dy = 0 + # Movement + if up == "1" and self.jumped == False and self.in_air == False: + self.vel_y = -15 + self.jumped = True + else: + self.jumped = False + if left == "1": + dx -= 5 + if right == "1": + dx += 5 + # Gravity + self.vel_y += 1.2 + if self.vel_y > 15: + self.vel_y = 15 + dy += self.vel_y + # Collisions + self.in_air = True + for tile in world_data: + # X Axis + if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): + dx = 0 + # Y Axis + if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): + if self.vel_y >= 0: + dy = tile[1].top - self.rect.bottom + self.in_air = False + elif self.vel_y < 0: + dy = tile[1].bottom - self.rect.top + self.vel_y = 0 + # Update Player Location + self.rect.x += dx + self.rect.y += dy + player_location = player_positions[player] + player_location[0] = self.rect.x + player_location[1] = self.rect.y - 20 + return self.rect.x, self.rect.y + # Reset + def reset(self): + player_x, player_y = generate_spawn() + self.image = pygame.image.load(cave_image_player) + self.rect = self.image.get_rect() + self.rect.x = player_x + self.rect.y = player_y + self.width = self.image.get_width() + self.height = self.image.get_height() + self.vel_y = 0 + self.jumped = False + self.in_air = True + # Client + def client(player, connection, address): + # On Connection + print(f"User Connected: {address}, {connection}") + print() + network.send_string(connection, "!JOIN") + name = network.receive_string(connection) + player_names[player] = name + character[player] = Player() + shoot = None + # Game Loop + while True: + # Game Tick + clock.tick(60) + # Receive Message + message = network.receive_string(connection) + # If !DISSCONNECT + if message == "!DISSCONNECT": + break + # Game Code + if message != None: + # Seperate Inputs + data = message.split("@") + input_up = data[0] + input_left = data[1] + input_right = data[2] + input_mouse_position = data[3] + input_mouse_clicks = data[4] + input_leftclick = input_mouse_clicks[1 : 5] + facing = data[5] + # Facing + if facing == "1": + player_facings[player] = 1 + else: + player_facings[player] = 0 + # Player + x, y = character[player].update(input_up, input_left, input_right, player) + # Bullets + playerbullets = player_bullets[player] + if input_leftclick == "Fals": + shoot = True + if input_leftclick == "True" and shoot: + if len(playerbullets) < 3: + playerbullets.append(Bullet(x+20//2, y-10, 3 ,(255,0,0), facing)) + shoot = False + for bullet in playerbullets: + if bullet.x < 1280 and bullet.x > 0: + bullet.x += bullet.vel + else: + playerbullets.pop(playerbullets.index(bullet)) + player_bullets[player] = playerbullets + for bullet in player_bullets[player]: + if bullet.x >= player_positions[0][0] and bullet.x <= player_positions[0][0] + 20: + if bullet.y >= player_positions[0][1] and bullet.y <= player_positions[0][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[0].reset() + if bullet.x >= player_positions[1][0] and bullet.x <= player_positions[1][0] + 20: + if bullet.y >= player_positions[1][1] and bullet.y <= player_positions[1][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[1].reset() + if bullet.x >= player_positions[2][0] and bullet.x <= player_positions[2][0] + 20: + if bullet.y >= player_positions[2][1] and bullet.y <= player_positions[2][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[2].reset() + if bullet.x >= player_positions[3][0] and bullet.x <= player_positions[3][0] + 20: + if bullet.y >= player_positions[3][1] and bullet.y <= player_positions[3][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[3].reset() + if bullet.x >= player_positions[4][0] and bullet.x <= player_positions[4][0] + 20: + if bullet.y >= player_positions[4][1] and bullet.y <= player_positions[4][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[4].reset() + if bullet.x >= player_positions[5][0] and bullet.x <= player_positions[5][0] + 20: + if bullet.y >= player_positions[5][1] and bullet.y <= player_positions[5][1] + 20: + player_scores[player] += 1 + player_bullets[player].pop(player_bullets[player].index(bullet)) + character[5].reset() + # Send Data To Client + score = f"{player_scores[player]}" + network.send_string(connection, f"{player_positions}") + network.send_byte(connection, pickle.dumps(player_bullets)) + network.send_string(connection, score) + network.send_string(connection, f"{player_facings}") + network.send_string(connection, f"{player_names}") + # Close Connection + player_location = player_positions[player] + player_location[0] = 0 + player_location[1] = - 20 + player_scores[player] = 0 + player_bullets[player] = [] + connected_players[player] = False + player_names[player] = None + connection.close() + print(f"User Dissconnected: {address}, {connection}") + print() + # Active Connections + def active_connections(): + update_connections = -1 + while True: + connections = threading.activeCount() - 2 + if connections != update_connections: + time.sleep(0.1) + print(f"Active Connections: {connections}") + print(connected_players) + print() + update_connections = connections + # Server + def server(): + # Server Started + print("Server Started ...") + print(f"Local IP: {ip_address_local}") + print(f"Public IP: {ip_address_public}") + print() + # Active Connections + thread = threading.Thread(target = active_connections) + thread.start() + # While Server Is Running + while True: + # Wait For Connection + connection, address = network.receive_connection() + print("jin") + # Find Player Number + player = 0 + for placeholder in connected_players: + placeholder = placeholder + if connected_players[player] == False: + connected_players[player] = True + full = False + break + else: + full = True + player += 1 + # Handle Client + if full == False: + thread = threading.Thread(target = client, args = (player, connection, address)) + thread.start() + else: + print() + print(f"Server Full Connection Failed: {address}, {connection}") + network.send_string(connection, "!FULL") + # Start Server + print("Starting Server ...") + print() + network = Server("127.0.0.1", port) + server() + # Exit +else: + exit()