From 95bf468d7d9ced87a2cc4dc78e4bf5591cd786f5 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 29 Mar 2021 17:24:43 +0200 Subject: [PATCH 01/13] remove stone.png from resource_generator.py --- survival/resource_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/survival/resource_generator.py b/survival/resource_generator.py index ba5aa3f..fe36c9a 100644 --- a/survival/resource_generator.py +++ b/survival/resource_generator.py @@ -13,7 +13,7 @@ class ResourceGenerator: def generate_resources(self): for x in range(RESOURCES_AMOUNT): obj = self.world.create_entity() - sprites = ['apple.png', 'water.png', 'wood.png', 'stone.png'] + sprites = ['apple.png', 'water.png', 'wood.png'] empty_grid_pos = self.get_empty_grid_position() empty_pos = [empty_grid_pos[0] * 32, empty_grid_pos[1] * 32] From 792a940388b7599ebc08d29856767d3c109b7f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sun, 18 Apr 2021 19:21:13 +0200 Subject: [PATCH 02/13] Add script to fix incorrect sRGB profile error. --- assets/srgb_profile_fix.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 assets/srgb_profile_fix.py diff --git a/assets/srgb_profile_fix.py b/assets/srgb_profile_fix.py new file mode 100644 index 0000000..8869722 --- /dev/null +++ b/assets/srgb_profile_fix.py @@ -0,0 +1,11 @@ +import os +import pygame as pg + +for i in os.listdir('.'): + try: + img = pg.image.load(i) + pg.image.save(img, i) + except: + print("Failed to fix image " + i) + +print('Success. All images were fixed!') From ab9be248493886b15fb6ae3919928314268c46a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sun, 18 Apr 2021 19:21:47 +0200 Subject: [PATCH 03/13] Fix incorrect sRGB profile error for existing assets. --- assets/apple.png | Bin 3945 -> 1262 bytes assets/atlas.png | Bin 10633 -> 10657 bytes assets/map1.png | Bin 3944 -> 1234 bytes assets/map2.png | Bin 3827 -> 1118 bytes assets/map3.png | Bin 3878 -> 1167 bytes assets/map4.png | Bin 4041 -> 1331 bytes assets/player.png | Bin 3491 -> 807 bytes assets/stevenson.png | Bin 2014 -> 2048 bytes assets/stone.png | Bin 151 -> 98 bytes assets/tree.png | Bin 4006 -> 1314 bytes assets/water.png | Bin 3852 -> 1165 bytes assets/wood.png | Bin 4143 -> 1455 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/apple.png b/assets/apple.png index 609435b5b61b8505dc8ef47d77ee1171bc41cd3f..b03705563aecd48a0dfe6d23739e64935be33e06 100644 GIT binary patch delta 1243 zcmV<11SI?E9_|T{BYy<7Nkla(Wo*2i`tjy1hD|O`$2;xHB z#Vi6@1{asR`(Hosoi|>bb$7=A@9?{qM?7>K0#Yhv3Du@RH8n}KX~N(BZ1?K=n=j3{ z<3r3vKJ^O zf@n{@*DIk6M1RHieY#shfM_BHc!E=>FFbJVhugMM0F?*@@X_VV53G-6!8f0LORP#P zh|~@NCB{H|sFFG38>wv|EdB2AOAY*#jT3i?;|mCVPS#KX9J;7 zdG}K73HWOJcJ7#Q3s?afKtO7D5P=bqf2ab-W@h;5z<&YK*|R=-@E}_c9>hhEexCv; zOXaPEj}pt0P*q*f@<53@$sJKe z+Lo}!=RsYQ?t>%1sM_Cf2A;xEU_Ok{Rw!3)H}t z;Z6YS(y)%fNCm#wvBO{8xszY!=P9PADf)d{Re#is#givV?zjl8J5wnk?%WK42~|Xu zLEEyaC|Ig%rh#i&g;Bd5setF~q6$TU#faoV6d$&o&;Vw}VvHE%bD&$dJa|n8cM)t{ z0tsj?T|#4I%bq<<@7e{b+&Xar16rVUN6b)nLG51J@RuE zkt@KJoc9J;aYsWS0;LE&5n>43t}5J}+MQ)HYIs2UwZk_GY@`B>8LG;1j^kAkredTP zp$GvL>3ZG_H)GkICX=YM71!H#)q(&Zvc82jBB0sFfe63FNFTad4WY|&p6nI(Re!7Q zrNm;>kh0I36L1SUi?Xs~5eOorl%x&86P@36N?q6Itk-lkeAkS-X52Glux7XB-qp4U zu_Ga)z(y;O2P&lmWl3$uvIuQTU`A5K%ow|Oe*iuFa9U;hm|Fk<002ovPDHLk FV1jZ}R9XN4 literal 3945 zcmV-v50>zWP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000D(Nkljt^>X)OGX6LqVuSv{!iV ziIQq0QdR})O}F;WtIs(I3X(byxHv!lboudVpzH;Tks#Vr@AXP315xoqpYN9tAex8) zp5V;ch5MfUc=v7!pc0_~KDlyb>lA$d*$>33#DYle5Kv+aw8t-a6@)ShrO3aEf3#~C z$(;n$?$nRsf=~aL@?yCn_WMYTEX>dI#FQJBYvo zEdS6Bd^_rSidV$;%L^fWK5}ih^EIFj|(VJLc{Q zymqI30D$kOo=;Yv?91W;*RBW z2V7)$0HIrSMn(X*cKkT)!UBXqZ*-J3cWM!C#fSqX(1;L4kbI6Rf-7Ag&z7-9;Fg?w)hYy1)q1S_=z(YU* zH^cJFYj@lX7h%N=cc%qvVCP^bfOT&0p*rx@-o5_%^fbTE%~4EDQ1tt>s;C)Dr%#jI zaS>W~##2Jvxfuc@s)#CUZOd3uuu|7d0M~LCbl52N>|Io$D6kljEJX1^+6fI{W-P^s zL7oHMx@F-txwwm9!vaV^bNMnFBRdZqU}E1sP-XelDGX?V)*UfJ-4PK~wX^8pd7yCz zcb>|HWY{2bpfN*$8z)Z^`h9dDz$`S$BhN$#A|!Vr&~6f9Bhq!kopBLvZ)F-@x_A+5 zTfA*MI+_Mj+Sr8wg({T@!JXKJ4k-tY<G-jwOtJ#iM zMHr8fT7)75RHVy!Gu(_-cbW{M&Qx4)+YJgr7OxKrKr@ePBK#R6eduB}gwD%Z+3W5b zRNYO9rKTZeo;5pQ89It`XNLk1L`W$~8-l0Icb!s~_1Wunj^R6I+%e;>8Ecz%Ywlfa zix4{y5{K?U7F0?J%97fQRT0{hz>K7dnX%^1?UY!_lTk%z2fXK&hg>{tC5SA8Xh19v zn(Jn+W&{zWX|VjOXFaQaP&kUP)V374QvqaP*`Y;PsM45$2$z}$+<7L(dnuSHX!AsD z^EDUYR!Wqa>!nQv6eu2$1G3$J&6I6E{s*w_0RA2TJ^XN5uT5DQ00000NkvXXu0mjf Dq25jc diff --git a/assets/atlas.png b/assets/atlas.png index 25b0add0b39400f3b38d3c78bb6543b4225419be..07f1d2d33d90cce83aa9be71cc6c332f15934b33 100644 GIT binary patch literal 10657 zcmb_iWm^8BLr2gFU^i2Id<+88V@8 zLHBcLKQdqILR!uno3G+4P`-AuUtvwg&VgEf1_r*%y>`33pxq*$(5sQIv%PU(-R}vY zuHby&MuGv))o#db(3Cj%Q51Yqz(IgGoo0Va$UwnYNJE54xHS!Q!Qc4u`TmR>fMTi2 zD6m9}NsodqS3!$Mj7b+37IWezmyOun*5D13^LF7Ae%%)6B6=3n~<~?n#iuNgzv((9>Jm z1q9ra=*jD8HTYi`dU!!fi7!!+U(tw3%fc%{tm6-rljQzYT)>W$bV>0N@(OUcKFOpJWHa+q2D@#WVX2L|;uRgH$bOsZn z;p=@}7ktj{M(|(&wv1E4=%qBHOSDc(9Fd3!Iii0e_wUpze+!F(d$8X@1L~W4V)l!? zU+H3X0bNvrs0!gfqRFOPi~6(h+HF_TkE@#IR)_(R%Lta|9+BsheZ=@AV(`Gj8&QfO zy}6dI5gESy>lTtB=_F`^Mw8HmY3BAxfH{h>&Cn+GyITn+9a>$}tc;$t8vIntWXrZT z9^I}?dsr0A9(;m6>WupF{`M<1^m-^8b1~}eQ;?H=GS?MqZGBo@=3Vlg@F4;x`3kAW zet&d)0G#{$s1Ln>(TseL;G#Nx3&4ZK=3lZ%qe5QEoU_he%Eg6NQ`8C0$}4dW?~&CC zMmLp88=#Wj2M`#IUnM5m4!=HBmDB&BRfcRc=qV0HUlDB-27=ah6rqEQFy5NYPYARG z@5*Q5*q{{mQ9$i8_@{SH6^q8j#6;pJdNJNF$CYr$5qNL(^TxqHz`y6^^PMYzyd!1G ziJ1D*Z}+j{?@kGV$cx|aP`^zpq1)JK(kl_mMYRxruugZhjDjzgUebQ)Y+LxQ#~p5< z+v@4Ns_m9UtKV6K&{H(`RSC7(3J}_o6!Xs@uP=hyenH6}?jEsomwirtIHP)p(i3`H z>oL|KeL?5ig*dtZ-=|7$^B^FiLVp2GG20j^^Uiiumn=?f$@&$_rIg>fq61{7D4A^E z{iA!RVN%ijIxi)vN%Ui~JsP`#@_^wk>^6a@uO29Ku)lAAmk5n(x)_La9c=(ozmpiK z8RxMa$5K>CamPXAr%3i$h&^&8rQ+TDQavDI0C3;XeU(S(lf`zt#xu0?0pN;gy8FX? zM`E))T(~UN7iTv>(F^)OAmNE9^1?-@!Hp|`j-wdy~( zU_tbBiaI_^TNU`o&Bl0#1zc$C)OPJIQYVcO#PI}xlbl7|A{2|@yME~q1%&XH#;Vu8 z;6AVEDv-VtiO$m7LWfYBQymQ}L90dKEolg0{FRQV1YKRf{e3yu1>4Vw15Q94WNC5_ zJ`382BIt!$)b!>TRY@d(B3Z)Se=z1I(9r*XcCtHfnmU{XR~s# zP7ep&qkhG02EuZUlK_{(GsUceyN{PEMalOX6A~jW%SG`@soCK)Wle4Gz*KO)(5rpK zu=g2(2M5>#tvBTKjSRR3S7aU9Iowp~A=v50 zr+=TEBM7Ledb20&{F1@R$qyct&w>C0K#KQI5d3!I5lKVrQ4V(p?J30!VWKKki%C~W zABp1++n$!FqofJw^fFr?7DV=+41XLY<1Y$5U|VknAHfS(su3FS#fHnBd4#9z^Rl@J zPT!LbOnB(A4@5vn`79dvly35LQAxbWt?pP1VrX9%;!WuKB()%Qn&A5C_V3llqL25C z4Ie5J>??~4;kgVF%eJ(~gd_{E~1t6Yn_-ykPSKWsZgVC`2;hHKUhU(?+ zIYf}c8J&rj*wx&8XH2&fV)r3WYX6&*OsY}B={B(Kjcn#DOLD8$MuC1T3W-xL$v6jf z?cx%}$CGnM(V;GP#90`)kcNkIJEi2e{XNa|507&Vz-hnRF5;>REUzhFcRp-$yV3Rx zIO_7aoB=g}oe;~IQgm-lqQdLbzWgLQQ8yxqBs%F*h>u2}p5h_wVe#MXz1Z9;NHxXk zQt=UQPqff8z~~jfGl3$Oc6`KmLtWSI5!dk^=X;OudQP4J!J8!T?QlTnk0)W5Q6JPT zCf}Z%Hvdux^uB`0)w)Rt@P-N?ER+UI*3rXE-SG#|{P7-kQ9!q;9CwM``CE1+0v;PL zbG5cXb5DdffsGBv^LGL8b$-5yRqPF-XW@<88Mj-+HcqkSG5Fr&{tnL4-iAx4`2-oe_dx$@!OcYo5O9XW zAsE%Oz&d!=_A51k>=hBvnA$+qz{SKD6_9%J^A(~iTb&dJ^54VFo+|8Ocoyz`K$iKn zNfoLC_bW$`Pxa`Y1A;GX^7p{r7pDDy&5C(b|AXtFaICtA?h_uIfu??mqGA)q@n$N@ zKkkkWqfLKjsZu7Xc0CG;{2Jp_B*Nw6%{!4EJa_~v zpKlK`;_p7+8HT0Z1V`bPL;p(LEJl~5|xaHsPvyneIu%#Fa63K2=tU~a<- zT7TN`^_PSdizaJWOONW`fpDk;{)!7wCi~v++JLW!+(t5d4pV$cnP;2{!}lZG=Lb%Z ze*N2tUaX-buXJ~GQYcXoo27ek28TUi@_8;3kX5w}1t7TpIOX9g^# zHcrIWHlTGc$4|`jlb&F`dcrmUC;yAf-DNg-B9W)J8koRWt5P8!MHta)R;&Sdm=8YG zDVEW@pKy?9Q9JI4@#A7_@`n+Y!w*IvAqM`U7rlP!`^VZi$O?hu6Pz7L6sH_?><`ap zV8!0+V)M>}TVVl|jmu48#|qc#6#eRx{p4_*vz>8C1OWAX3WLD8o0XY73{&sY8H?}2P)Rf7a;=H_8}Vr1 zCvvHs+NOq z&ra49u`}d9g=-Xuc36$P3p(brcW_uV9^c=0OtNm#y?=?9)^n3=_WuTd<%}EwLcRB=7mtyyGq9 zjj_pO(+t;X#A@w0GKfnVy?;Rwe?dVPK>v~oWfzjdz@1dZ#wB1}qs<8ZoWKx;Y?o=J zDR+7$TZMMKfSOWTly=SFi{>vLPfAWsWz;Hq2@MKr>Yareou6x#4{U9xS!TIk^B>nz-zj0tz7MueL7c zNK#g#(hS2MH16qELXHKwivaABCrEt0a~QzqoR%l_y8N!HXik+H1f6J}k#5&DA>ZJ$wX@m#z{HDw>_-LQHDgR}#m991H?n$({k#hnLzt?Q?fV>WoSCW@ z*>!=L`=z@_SfWk!Nbvkf45jc$@Hviho=n9+CQmT3PHhu>C!KNlyosE;1~Zw_s#n{J zR*5zK8-*;&r&F?r;PZT{&zhnN3VL0hn`juZoB84wzb90k^H}HC2OX7Y^1F}RLCuoQ(q}GJ`i_s~eEi2qO8Dh+1sYxVr zjs~Mclkub~@?+?Mxsf=(8b%+#Hk2L0o=;R$7P9L^M+MnEJk(ZWJNIG5h4(7I!g}2h zSU%(p>QbOzE<|yKM{3s~@!n$q!bC$T>nehAA9zqzjOQ zbU`Pg5aaPu+1LcOk|(1_3PZJ!gu!MY=lE!I-G`|z|4l_j#jYEI)axva+O%~RLKz7@%;j5u~ z8%?$fVO6Cgb>xwaC-cH?LP`5%*2ol>J19Tf>NVF`@|Bp*v&CGElzdaJj( z+B_DFCK~NQ!6O_>#1D^;e8Y{+%`zZ!*x_QWX_t3J;i?&3LxDYyK^%PO(AVUaO%Q9Z3ZT(scY*Q^Usk z{+LR3PP^bJo!BYOS|w}2ka0SI!p3vSTBa}ScU)6|<>-jKNrxfI7k>DlNeYlp#-d9G zv?zy)i~CPIyRWp#@v3^-gHtQw>m%WkGieV2T7v+nxpAG*zd~Cn*T`|!L~ShJ3>xD# zyY4qa@kvL9xPz8SX$y{Cjrm5aLY)OGJUF}gi@5PS(uugp271@eZ*((Vmg`A(=UtY1 z8_M6*wY_N1&&%7_*N3!Z91|Pc6@2gQb=y&u(yllwF_LAelJlMH6|EYpiHh|r7rc1B z97yPQp&WyK%7=%CZvyeD*;CFNYFjLjz}HQ@9dqAv1=-$E9GqD>m-d$y+PPX2ancbA zNB`Cf*3e)|DMW>3MrV>qXL^g+*-JcATZ@lTuG1pD8=Wc|V#KnMeBQ=IjD8K{n+VzE zKP(v4;!VS%?#c$f4IWWNA~|YxO(!sViy5 zHcGAow{IyO8mYIBEWo_6v{5DBoiUyISkN8th7J4DOd|4&+#y#tEXu8dnnw4HKiTe2}U|mlFy0c%bxW? z90ej?5m4$w(*pcv+#a4lqOb*u-}HuhX%yFN!O!2otId+#V~Kx@8uVUfn4F@b;UJBc zKb&<>*4-KKPIYMt3JO-&h?^2W%7~MV4qG(J4_~fnJtUeFz1el^=mfvTeWNvGxYsvf zp2i%$T7G!!!~CgKe+#lf;V2s(%y%x)Pg!N!znQ< z-q-MDBxyDeEaoyt+S!$0qlSocPSrOyYFdCEKupwLf{g?-CS0WwQ!)T}Hc$O{ z(684}TJ%5}=${R^+QZdok(PWKT0TwrTOb9I*bJ>YYHcF5Wz6J>Tm^8eU_LkZ&G5*G z*Hn|=nK`%FPq`Z10Seg~U29w0@15yuH`1bmCa%9d3s*hBv`eGpN7q8p)lRNuxY;Dm z==F&ipNsjlY7J3^qB(rH6oUFNaq)F?jSTy#tQxD5 zbUp~L^@&g%@XU{j^|KkRm$vghxGzRfq4mX$+UZToSQhi~)s!nw8oP#i>6F-U@mCO2 zqHyUQ(#=E&0>SflYjspBMKBf?mOouSERQ!Q3~D;u<~^gc<%%yBG&Y9u#q{_0`=6%l zL)X`-fy%XuWgQ)U$v-_X;@TsiZ`BBlii}Aa7!~~2iX=?qR!@&$ur2%|>8Q)6S(n(O-@p@d}y+IJRN) zZCxGtf1estkZc3`<;DFl8sd0!7+WvHV#j_JdKr0_)GJp~>h#|Ci0zs-Aq~msYUOuB z%7Cl*YhNgXU-akDA68dafByp1US<=k#SXmQcEaQJu9nXEhOayY5+M)zZibOF&R$OK zQ$@~r7AfRkI`cf;hBlex43b!(E~&|%BhWOz z)~0U8q87^Jd1FjMV_C<8|GX~q)>>tpIW6j)wG4|Pm7cNt zA7Q=38tb8;$`;&>q97jv%aoLRf7J1#2VgNFia~=pliECe##;NS-x*|b=q0;t@zx?~ zWGttP#DxcmR%OztqVwBPK;!mt;mqnZa3;b|Wy(}3^Iy8Nm66tGv8Hu|X3Z!N!dU`6- zcktDQC*vUAA&Ot?>!w!s9jJ0AhSFJf#}H8dThT9#F3a3sj#spLzMQP;4dSlGSUV8z zPvr7@@6VJ@n!TlM&0=iL3b^cobuHmew4vlKS2ri~nyPss8{@xQ7ffKejWCVz;flD; zCvMMCOGybtKq)Y8PVHSwl9t1Kejkk4HFGx3MWps(KY`fhukhYituU7ETJLHE5n!&| zoyPuiLlWaVm#LBCKs%)WKr^2&0Zyj`at2O&7rhan|KKf{Qwv|W|8|#@sAd|~vs0V= zGNQb6QJj3s;9%;@tB!ODx9zisvoAjrkQ}B8=%Q$231}eN3Yv>sS>#TYq(ud;nh>KQ zl#nG$yqw?$2-)xED}f_l_+nzGeme9m?#b$F4Xxi?^uIvy<|f|9Yc2Uc?&gGxwj1h&I#(|_?ZbZ(EQoUoh+id3 z)h>Q-Yfkd`L1_GAzt{~0d@X8`oXCbGZ`)|y)%>l=;g9{`c{dmyN7*F zCl9fz##5@Sijl4T{UrDE@aii2R4_o4q_y*%0BIXk)L*B^CB zrV79^=UR)RdIev{3JF=~u2|t~T(}aSm(kthTB|V16a4U1`yBA)2Sux_4{4R^P|+L( z6oNx(C5`(_rr>#kICkXS2_OeSRYDDyYHxEs{SfR~n^uZl9lR*8bLW zpArjIAWK{mNR(txS=gV2i^pLn4}DyGekUaY*c4N?oIVB3V&9IEKUeXDz)T=R?a(Zg z^UY~^D^M#5lYH9377-pPKxRWE<6F%V6ZLAUP-FxG3NrknqAMd9L;va?Ue)VQ-aR*T z0X-3kbd52vPJmKj?HyQ{-)Iv%L*-FI3HjbQjtz^_otmHhV zdt3J^YpYOUaevhxYAYfi`({D1#}ub5O2p~~pgJ1(MLnS_=@E9A<>i1=yh&t z7B&1Gx+86r)PIE)EIcxO$Qur)d;NmQtV27Xl{$SzsNDyGs7WbFT2#Q8K``n`9 zy^6%DH{8kAb5S)zPM=5=(n<|U9HLh(-`L=Sp7d_st+sICgYM67Z^MvOXG?veDOM5k1yk7J_S%&{E{uF;>28|@Ce!Oyjf;f zrKYYjX4g^YBtXni-6_&M^8stlk0h^bf_E@1G}0*9ccaQNzPxnZ%YoeS7isWtQ3eEe z-%pg8d5iu})ZIP~JYn@@BEaFdwH&B|^=cY>8frRd^5WIxH)m_IHdahosvF^GJ-h#I zodF)1LHNg(t|c;QQfZjrD+HW8^xz5*Ww4uWamNsPD=C(O`CUn-E5rM`5C0($*yH(- z7J6I-8A6=*({!B(^~^OrziwQ~=ntoRO+iIbsu*~2DAIaH+yAP-4{B%>Cb+rd0`24tI)(U?1MQq| z*mYXc+`<5QQqRmKj8ig)N*)w@sn1{+dq0@>Uwe%r#xCUj6H{a>JKjB1!6u%G|x4s zpBvx3MAF9Njt%bhjrYaYEA$`?iUTS%a*D&Z$##E2DbL>PlVuy_{?IAI_KCp`F+{#N zxjgTi7=UNZ<$44>fmWAIHfJES{>zE|gRuS**4!(BLI>B+IZdfVUy|@r?TR=pas@gv zh>-YV1lq(tpS}Apkc&WN8;F?YOFs$ETVWsgQBwZuKKOQwM<(nF#$ zC?Nmk6w-f>0~>umva_HYTwuCqSY+O}Z1R1V*h#P-W4i5tZDDt@GlCV_L&$*mUnNG0 zOPjI%?I_X|M#QbHbgtQiqiu@)_`GWXi`5trL zMfT&f-uN)opf9*uY*qee{~RFm$t0~_M z(!)}4m83n2;GjGStHs?;wy^79{yVWsadA>yPj_$1L{GN(*+F&5viV{|Kl6Bw=5q2p z#8wC`>mB@0V5V}sjzfcM+m}nyL=)^jFv0>nfFb#WT(54P)eAA-BPXx1LrW~vwI3=nDX@_doWMUmk6+rs+&uv)`wS3{!fdy8j!$?MrO8!sy&H1Fe3|s?~9QZKO=?-+xqeblI6^dXm;D2E#~Ehd$=Ub>7Nl zV7l?l+WVegf!=7M(TExKx?!Wl^JUD{Begc|=JgT4=N`|$7B6e3mE3C|cy|r1Z@uTo z_}%ORbJz60o;PX0e%S1gC;>J)EE7hyMGZ18@DC{$q?l?31`(5yz1J;iP&5Q`L@vIz ziOS4+ud8BI-CZWP8c?xI&12RRLknMP2&t`o5N&)B#S^-RfL4V<@eHXx)eTX8O24CC zOC>)NdsyqRz_)j8RU&cu;1|G+Z8(BHD;p9rVP6!lRjRAb`bKu6_Z_#!B)^^pk~Hz= z@vci4UE2j8x6QA}i_L_;56e`ZCi10f1PtPK&Gwch4Y6Yf0Shpb!F45@%OBJBFtWQv zz=a@EjF|((=2wi9>;Z- z4sh|E@xPBuuNjbeu;Pulr+nSLiS>|OF4fYO189B8J6MpU`)#%>BPE6~5<4B8HrV7W zZ&!H1cM!k9zrCuu+YpZ1W13QZHZA4!PP6V;3zlvuhH_9S#4Gdybk=v@X~^A#HA3+9 zY5~XKw9~CYZaoX>dO;bjyc$hR`G zrB$`>uhm6tM)NqhRQGHSg$2AMEQdxoXWsTrQQRNB)*ex#phkwKrN(MCx;m72;6LI-9tjPI-TA+o zlV-zihpy6mw$e^rH)MRY!mbJsd2+MQv;RjjRJeseHUEUOD?uzk(Tb1?(SG>3F+>JrI+0I|9pq{VrDMS z&6)YlnK_XfY6>`*A21OR5O9pT$4%hdkI z!9(sypm`V5c&dwG^WM@_h%mQX4;H{G5Soz_EATyz% zD-JT>b(j_Sc5?~nd63w-s0lJCzzn6cKrejaKtVr}L}N>%?B2)U?rDW_JwbShWd;Ok zk8<&Wi0EwWipT`C=txos{7WmDzo6!yUr6Z+q6xpx3I2Kdg{f*O6AnkOgQ9MISr64b zWX9HEO+{;9o|lL5umz0+`xGNFG0J&SRR*wY7`Dcx>KGAAWD2H9$chjhnwct^#~{J) z3fwVu|8n*N9IuRTK|<#JWD+Wyp@;qn^yqj+a0E#x!Kl=0J`X2xhgF;0+1Col_;2VP zGKOPlD=i0dB-GsKg|vaV9QV2ykL-e;Nl7k0V4@zYJ`2;3EU;M>t6zivZ!mFva8_B` zgBF`0Kc!+Qe0IkkDUT=e`z<)&i-9a!{t~)l;Zu54XK+C~0^wk5QG)K+*Ir`I$}-aN zR{f7}*47YfzyqShfz6xn?z#F!mjjIF4#tBTH0~)T@kb5g$ia$4i%My*Kh!P?vm@V_ zPewb;Rtx`)8MUFju=s^g_IU^6z*>6rM6k)ZZ1{3QRCtFH^j^qf82i?sLxVkeAxyIK z29x@(jqk;n@9k?W9D1C)ee&i^4r$nh)XMneoo`jZxJu7xp&?P6R;F~F1jQ=o2F>88 z$lAtW`tjD+~7?crAfW}7t%qUil!M2 zkR`iWcy3vDOPhXO7tT(pXS}H}=D?@g7RLk+ToQFlb*DV;x29o$uN7X-dr1((9E&eM z?vD#|5=r%<)XP(HxLq!MzoWWW8v;j?UbgT!`FxQ9<63RD{2&X`+kY2I%_hzMt2~P^ z)%MEWUKZF@QcN5}yH=arG`%?`^IlC14VK8JmialOA~Kf1**Fef-<4JsnOiMwEdJte zga+*ovlhOf4sooU-)%E9eYqRHObZBFT0CzJP<^@WQb(zsx?1c9>>^7Jss}y%z==0c zN5j3P0`9i{xH9&h&@*DH`yGwfCjOT*!rcWarN+bE; z83eq8j&qU2?*K0-8`pQ)z0ck80}q{T`B#^lP3D1%2tm>W9P2jUBxmB*diRq8LTNAN48+62^QQYdFN8rO~k7UO{|6)H7 zsz8Rxi0#N}sgS~lUyQ1o!&XPETL??(a@LtBzT1*)giojL0A4H2CO@en9iL``y&pw8NQ_y+!jj{qP&wJZ zR}Md7jw?Ud?4wci0$dJQb25bsg6Y!`BxP(XNrP(ksbI6cfG-j^3tU5&X-HzC0Vp7% zzmb53-4?xowljAtuBd$&6bDewpclvo1xBqS}I^6tA z*A+z)Aa?UNjr7`v(ThHfSm*$RN-vI5kJUWuuqPbB2<2XH<^a`Y@y1@#Ku1F6r?u*m z#ol#PcjeZBiJ0e!qmM7VV7DgRZ&&gq8nRt(|MnlNIYy?SZztdeoNaM0D1LAvyg&lr zTt%Z_%$l8*S7Wwsynrb$M>?~a7_!PiNOjVu`)md(jh77Lc&ah+;~~R<8|}iEc_SG? z&d<2l13m4}{QI{?$dZ4;WKsH^&Lq&`y3giy*AfdNZ%G0pkEMaG&-zIe}E4M2-}Yf;Zq$2^A?LS|?QT#$PHHT##fIjgVu zK>=rwoV`Ht7yZkX8k6SyQrC|!5fJhC+#H|qo>Gi9^4-nFIy|?&f#2GYUJl>NIDniQ zZ3P>?G*f*MgVuCO7(qLcWvBy5j|i9eo1nvp2d8=Oo0nf?LE(Dg=Rrfym(1Rp-CdH& z*0J(g)*z!-z|(K%W@31J&ZEdP3|!P{37;o(eUX%W5-{w836gy))KKZc>=%4)p)5gO0| zXzM6*Zcv)@YAE6g+HN11n#^nz(3^JuE$IA+MzbT--^hv!G#GBbV$exwY+hr1V~L1o z6{;laWQDnRo~+=+=Q8))n7=v)SY0Ij2UIDuuB#S_C>mZkMEO!5QQ5wt85pynZxUy8 ztw=JmKsUnCu}c2<-5@awSiJbxW-w|A~YSwC3a6 zyA?P({N5PvCWzyTV!P!lUS8|>W#braXV*__0h=`r^n}! zrd7S>k=|7x0PJ7n*bjy9@NYpr4%#sH)~qwPc!r?Ztse4SNg=O_U^r1*{HGXzi%n}V ze64)0bG{!!nMXK`O-h9J&|-7-;u1F*y)RVm-ng<~_O5-$;2eBCFqlxmh!I!&$4>Qy zLr)p!WxcC|_x1(j_Iwqq<}Cl`We!)b`tnDO{N2wmo?%#r_wCNvw364u*$zqdVIlRK ztEjit-Yvf0)6c@JpS7p;;x3=LN8EJ<)!zSYeFS+W5}Xh-1zPWE%=mu*#tF_Nsd-}u zOa+vSZSkIgeO;BTbzl7QoVPdIT4+Xs?kOY0oYx2Zu6jJS6;oq&JQ&RH((|Wqs6> zu<3PCCZgpqo<4K-gn_5JPD;KQhr=`a#?x4p)BIjmAI|!h3rn|++39S=VQi}ommK%` zI_&d9J2Y*sotBVXqBOjUOBAFgu)4T zO5vkL)BFk+OdOUdS(J1-Dn?`szfwliWJ~*9^zF0Q?$C+=A$AT6#1M6EWoh zEz5}q={;>y^qotYf9SS3}{J>IOyfJNTsQiq;RvY-4NK#(=Vsu;_J;rEKICB&04KFHrwD+6YE@tph>Std8Dk#I z4X4)gRB4fhE$EFKWRNfWCv36VC8jaxv0Lbj$Q(XGrYGK0O$f`tGvguhKnBO5%&zi} zd2IXd)WyHjyuGG6dwOeXwa~MKJqlK+Nkc+H0yfTDHoMFH%fp2NA1`T<%}Jw*F^ef|oGDUaeIuLQwmv6) z9q!c0o>;baE!kXr6BD8nfYo{rmHlKNr}3pmqImJ3&V%_A!rsZB>p&lVX5;oG#pYuC z=vCH^Y9H7aKv;`eheE0jOcSPMSf=y$79LdB774hWQQ#97=K_OfmY3U#eCW}mwM$A% zb#*w=c|}AFx3L>HJAU#I4P5Px3}|unh1TlILWg+(5zx`^`RECrav3jMICNpPF6eXp zlIyPvTzyR+I-g6VMT|K;hxGM5DI2hGKN}HRLX~i|u zyHM(}qgomYzm77%Zi0wPTz|!9dpKO}*8Dt|;YR1h0v5$n_z6?FdC*rRhv zD}=v{(GoF`i5hJ2AL6fq1-GLS3g>VmBIgDP`tgAq`U4e|TY$@r*rcE9sPN-bTlo6V z!iS5Fd7w6%-CKhIes_*f z*G?qXv@#1)J|yF-!&HJIBaBj2Y7lj0Wrdwy`rqlY{yVp)6QaaD>Y;5;QM3>KU$xP~ z9V_!<`V6TOx_R1?$30vAmhX1BZ?GIj;kFwQE-7(r^7D3S48j_t7mC2!+Y2R(hjJ=Y z!lT33MUkRKM2d_=V=`E$7?t_Vh|_y>jF)e)5hfQ3MMmCx=m77m<6f>`lycP744K%H zOrDKl`d7wj6;8tx3ISt+e8Z!NB=c$kDZ`Hfw^wUzo?^#-*N*BOon2ko4@Q!Ck&Ax8 z%+yaRvQiX*3bJ`$su^)F+75GYRFOWKbJ~&Nh7k5bYu2zOKPGsw_D6 zVa*=(h(z5O0079L99?SYs#T>(6r;>2 z8T=l4*NRwp9I)TbKw18{dLknv1X5+&jS-?crdGW<#J#swH|u5b`!oPXw4R|@HF2ay zKGE8uTE&JJqkH9E0N4Z8^6gp|y?rXAbW_Q<()(7&?L}+FUL5x4YN)UKK_Hcc%!~AwxtvTq#|N)QaE&)@&s%0~aEYeT6)Z zU`%g>CWAn`rq7~k2=P@CpEmd*)Yc-7c~h{qQlPKIeycKE^ES)=All`=SR132Zsdl; zbOea%yEwd!5AjtQdzPwnh8zg6U@o{%8cc<2nJz>m(9q zHTTh(nS)2w&cH0UMkr&iq=cCnoj%m6?iUsrw}p$#-vX=zH>nX_Pu!|YO4Gi*J3s<6uu*f z^C+B zP%7>Eji|n6my3AWGxJbBJbL+f(s$dxhUm&HLa4rUv#%HxqPWb10h7QYjHzKF*6-uu zd2Js<3ecxF%e|;)t9>moz$6BUoIVc+6A zTxVux`WE&mEmKfYRs9X(`rIf7`cWN(CV)1e%z;$PH5gf_v5$MVnSW}RK|9N38D8CY zP@cE1Oj!4MwM7~hyND~5-s_SdQ*SIkfv}RPm7c|u-y=GuD~-fllYT%}>u zy)i=rIwYU{*#T7E*DCium0}AAR9>B1S@LTv2c*$`Mbpwq-U?!A{a?!dv!rwhl>*GdHTu6eyb!ci-!Sa3l)`^Lqz&8(1Z`_T8 zDS+2V&xWiKh4O`FLHs4e0AXQps%EIUdwB3Ki}Wz>IACMDk2l^P>=tMgd*YuktcBl; z6d~F>K*i|2K$s{_v{CBs_}mFvjJ-Iqwvt5p^`Sjk3VBJgmO|)aJ+752!GZ|iyeeqW$D!%bKi&) zPobnVu}5j6&8LgQ47z-|i6rsS$M1C*?T?qdMlU+ud9jxWxq<}9vxx~V%h0kV!#B~X zB&E}{SY>4Rir{kj-cF*cf>oz7%Vc=)uwLgThAoinx`Mi5^v@?awVL#J;dpy zFp;~KiJO_M6%)_MoUTRWRyFXZt%4B}+|Ue&%d%c|*w=Zf2k;ZCmp6#5S93@WCh%R} zOw+jwnYd@NP%;|W_Utgzb9uO{gZ$nQ_mKyCt4hm*coF!)|*kIbyjg=Grsxc2Atm!N+`}`nTm;X4wbAP_xcJkF3-=0 z9&ZkpH&{#>S?;U!>lg4Eqg!T^E#I#$QuI?kDrcL0$rFxsRB9aJD#EcSIARmv#P&4k zbRgCw53i=gB-un00TR58Py&T|8~d(18HOfMxF(fUAY@wlxLB>69JMY%Z^sX+XGA(r|HZk)_k8lM`<*Q0#i_d zwqxdnc6q5cMc9eBwMjB#&9T3K6L{~=7C|788?x@C zLE~PDM{9Q{SozCVNe<`gvaGUtR<YnUp@hZ%E*x%3=VP5Yi03e19}LsDa5S#QvdojM!?~FUKeN zZbY%Jp40nvR#?;_4bT9)Ve0Zr3t{F2cs4)!|x6B0-O0`u^*n zG$w-wR@J<7m^u+6LA=@V_$yYadTzBRMKr!J?i7T0CP`%=zVh8_Y9p^ERQQBh7nM}~ zh0s}nU4WO`T?PC0&ikezn+uI~GBzTtY;4DAoSBc$cyE|M)#fxB!Jc_+;y|>)>DvA< zn%A0CjO;0A$;sULS~Ceb^m`{G3Nt5GId2|OdWn@|zMXk$Jv}?7O>-M}0GIz4EQ@9% z>cyGPRQuOyc{2&4*OipYre`WqZ(fD6xDO}b(i=ZNKRp|Am@ESimuF_90B~V5#Z! zxwCCMXRRBRtqvb0y%Zwddo78s`lVfy!3)8|LJ3`y=W6RF7v7npBJFQ9WOTELOMlLh zCr*GE76O(n>e@JRKNpyevT7o7aBxu6$Sc=G&q5^-_7(ea>#LHCJnm##cTl}CegCzyvF%5U8V_EY{T(3_fk5!gAEFOMhc#Ak1-+=cn zNUHBCv^|+xsgGGmcUcP1NwI=Ys)PO(AVUWQo@0i5!pOjPr{1OjQ-bh8)23dz1^w`O402jx*;GWDc*>Z7Co|;L}nWK zCKNwuFispVX>h@mtf4zzgg!>&^g+f~{a_~M@anN-#pgOULF%N>Uh>(HYfes4R0So2 zBEeJf@NLE(S0ap3`eS`U%A4Q3UBMLoMwkE3eM=$i3jJJkQc~!@-=$Y~xzs~E+{xei zu6}zW69=vUAM4}Wv=Bzv2H5ttMNwyIk^>hI0!}zKwTFn=`-~bHRbnPPj+X1P8v-KN z)5P<1FnPuGtq3NPcm`5v%AkYoGTBYS2+O%{c^i6yvdUyk7KyKayMUIB5G;mZGZB(xE!4PDhTX(R9;OO;f;?DI==x#Y_Pa=8H}W;`S{|k1%Xpn7@t7R!P_34T3rZzj zXP`} zjP%7SXSXi-hcgqKFmak)RsfL)?plH)sgG@|b!oY>qJ|wVMEMXm-5XQ#X_O0R_jygh zjAj;yd9AV^u$)0k{#kVRx46|^d#<=6sTA>Oz1Q{K(+~V2kPHOQ&<-3Mc?Y6 zZgAMVDdLS9q}QRQH3hR_7+u|7qj_D1D{Wi*2gbu5-d?Wu&0TeW>#F_|?j5K6G>CWs zJ9}58>1aSZxEKWgo%ZY*0G9bexfKV81^K?6vHCsxwN5Is>C4u$4appywKdp07T7D) z%=QQVihvI+nqNTfn2?D@$uH5kH%jg-)09YP@sL!n-HE}iuWS%DxOhu+5d@Ej_O6NsV^1FJ78QH zCPyc$4?38Mj!xmXBJ|AEFSpSQ)Mn3P$jMow+q3$VOI&YrA%JJXoba#jD$$OYx<7$T zJN##sH$ZXT%F)fd{C=dKSR#TyODslQldo;3QgR{t(=+Rl-t>u3*Gxu+pwn(Vm&YwL z&vQhjV}1GbO!(gKx$BmoE)Q*vV7W3S1RBOa05@XPHsGH>j>LzQ_d~+MaL`1mg0GT zC$^3m3;UteIf-h9A9 zaURe}LWuH2Re=zKzT+bxV95T@3or%6OwF&!PVTLaQ%k)JL4GIvBa%c$t+`feKHAkz zz`h@$sOc|G7d{fijIz_CE-%=`j)hfY#|Z+U&8n~EhoUU$|? z2X5Td)a3n-4ZRYtrWmZw!rR(`1va4c7$01BZ=(jl`hLqyFG-6HLMP89gdSKw{^m10-4x>S8um0Pm1zTX62k7LyXBahjpKIUe%0l|Mf(AkylfvH|tBi&$l=EEW4VhB-pheV2M zeqkGx>tw9{wnJ+qizGN{bAdQQZoWl_hy{Df@7T$GfK1}WS$KL8B>4zktpi-%kDcm( z0W%A->oS}s3yZNOUYH{h^84$Tv4hfF|KXc=91;lH^OL_9iAYruSZUzW@d{s1iBKND zFBLYdPZ%%c)t1FpvRIHpGE{RU?N-CX;}w@|ss6@X_v*q7dfUKlIsq5yT!vUy?`W;0 z92Pbd;$)wnFD)!%F5Za<{E_WH3$8RvgRIM_nE2O5RE6x?5ARV__Yn5HzC~|NT&2C& zo1f|Nh7@*IOyy8*Q#M{Lvt0zuC%c6Sacw;zPRL?cmu~2PjU*hzVJ=~A6US^)x}uYF z_S7C2V8JtI&hD)~KX*_z{;Lmt7AVK`?M7L=lOmbguaB@=2DzfKricsQcA;*-0^6%s zbTR`#>*7?3!@Fw@M}LSIdUmwiB@9*-lknF?o43!}EQ4UX$w>+|NuJ_+r3c$&RBkaA zEL=f+gsR=Q{VpbIQTdFs!&(Jqfk!|U=G~>4jfvxh+XKzC(^ZpX6Fl3I!zvwb-`THm z5m(EEaQXQL+mWHY|JDZo^mL8^I<;+|Z%0hyLBJf>D#bz9F|uSDKm9$H>Dv3#T>^`< zHC873vHmqA7FNHwh({}sr+S#`oAi8fFek7?Cn*YL*59ej0f`WA^TuF3llFmozJ=%` zZoK-{M~LxQrh%)1&MMKz{iq;gr}O}^j|DM7bii~nQ%NXx@l7-2-nUZA8^F9Y>L*-@l^@X&+D(Ct|)kyz3 zk0u_K%(A^Q@}ot|r4mgezk@}8u-7Q!m`B@uh?dKj(RDt(bs|-DTly5{d|5X7FAiO5 zpxqN`6JEZ$CF!d-Ty~{pz)lrE!kam6-Oj_^{PS{4DFE7iXOG%-*AxNs_G@k_U{alt z!;;glx0T-c^?}+(i}c{obU0fTgOixaDDjgqCaOZgM_23aBVDZ`HviOHpf&@MiY(yf zHCN$p59UHP5$xy{j(iz7cgH&kJol`KtK+((UcFC}6NBx?mWI49nN{MN>xCZurLV0( zo2gpnv@@Xzp+c%_4NJGA?GOl?0gI@Ra^1~-{JJ%9gmT)_TyC~IySz|fR!zR&Qw_vn z3e#%y@@L0wq5L)wd{r)@htzfV$-)RtZ|TX!&YL>-Ir{P@KB;|e6$@J25k%$QU4n%% zF04hJmCfaZ{gOHag5p+XLT6#cE^R zcAK6nvRM6EPIX#z`|j^}u_sqt=5=wg!7Y*I0?7FO`Jy+6TkN&pKydY=#TS$W(bTz?@6Xo4Q)6&WTve)zw3em$EelM%6O=2%W7c9h64rVo z|H5rvw^QEzwDRlI?80tLscq}stKoEEH4Slf-e$;DDs(~coI;{x0Jebl@IXW1iNueE z;L`cctor_k{S0WS8UhidLL%Vbho*87FWJY=|K8?KyE0YlF3p(uQ6!S|K$6;gHV!gM z@_&ROGCk6NmaCKqXzSX9Y!fEcHaHA7j5Q zsRmm5k*Y06>^^;?oS*2&DGOQtg!ed&IUWiE6IM?4CH1 zD(=0WXTV-)0SZLeB6VRNV~Sw;!At|(uuA12)8I^Qjr>7pF5i7bXP){?+hGNh_+g9m z9&H3gOrJm><*Q|go)pkTBA&4;2c<&1T%WV15^l!ztqlvKR>P~!SP!hNtCS3=)wtgh zkr0!N13x0USmtSXZEK(S<;R%x$<;B6d_NWjjTs7ptPH>HL_xBf3YX1sDH@QF_M?gw zDSQ5kB`Eu^?qS}D??c&AO>qSpZo-Xb(t3qi|B0q?(3ztot`W2Ko;eWM>KE*35Gj2Y zv#7~u!x3cS-1`a|GpRr!qAv@_d3cxS0}rSM+RVz+I~M9(Q=CV(vWs0M+0*M!%7x=x zyVTd>#4U>ExDg<`*-%QO5Xj>HKc+rXBp@8mOc86Z?UHu;KZ_-TlAM}swTwmZ{{g1* Bv339e diff --git a/assets/map1.png b/assets/map1.png index 21920bf7671487a801553afe5dc00742d306bc46..4562a3792a0bd3b531319e239eea74fe907afb5e 100644 GIT binary patch delta 1156 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 diff --git a/assets/map2.png b/assets/map2.png index 90e2f7e0749b2fc8c591fd36f7977884578c17e3..9b0c08ab9a70d83fd2c261ae58172791ff6c341e 100644 GIT binary patch delta 1098 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 N002ovPDHLkV1gN{Gw1*S literal 3878 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 diff --git a/assets/map4.png b/assets/map4.png index 1d49162665e460533a1b61b1acf50b56e5889a32..e5e452c4472677db0da5d60186630bf9dde06eba 100644 GIT binary patch delta 1313 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 diff --git a/assets/player.png b/assets/player.png index 3aebbc3f839d608c7337bcfcbfc453b202268a31..1386dd8a2add7b1e6dacf76688b70765009f0034 100644 GIT binary patch delta 785 zcmV+s1Md8z8>a@4BYy(!Nkl)71@P`GyZB|&c&D}Ang_&K`z{+|HY0N(!eYr;1U2zT~)_vU>BXddmCw9NrP zMTGSD0a_P~-oMStM<;~V0IwdO>-qjeg78heFDcH?*n9CAx=`7_eb5@9^%ndS{7RFC z9z_v>)9@z<0Plx~#qkmNuKwVW-Wr2v0Wf?(u1xYxWjHqfynj}mnW2~{P;^UmBtZLF^#&|p;uvbuq zX?7~01Yty$pI!bvfU2V26GRbtekNktb@%lY7!5DqKT5s>h4(%<1mKUSPr$&>a*uI5 zXcZhfoQ8M5J$kLdyt*a7d~>HYK>LXZaJ5@WuYb*q2JD8vb#c}05LNG)zg!>|`g^+= z={OG2QGp-|vHbwh%a+`AbsQtW7@%dzz1B}>%Z^j<_5eiCEl_L#z*bb)U<;%v!~YAw z8q@hQvN6Eq*)d`T6IdqCjyD2mU4|C{-}?K{=0+s45yVDaL^kMv7wcbzAWiuOYi`K^ P015yANkvXXu0mjf$Uky& delta 3490 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 diff --git a/assets/stevenson.png b/assets/stevenson.png index 00e57c8621bc8fe1d986a16d295a4762ae004a47..527dcb927faf87ae3cab7ce636909089bb899eed 100644 GIT binary patch delta 2034 zcmVuW$Eh5_xJf7pWpeN-+wti76g=1s(Ss8pWUN0 zT|+}40AETxUm+FZ(@(z})MRLVocVwM={*3-&;`1tp#X_O`Vv4?KKH5P{|jW#2;+oO zAW-xk_zGx}>GQupDUdRZwebA%3#3vl#dhM@PtCc&;V^HCOs=ycWRG?=(OC1H|?m10eeFNhbdK z3;>1>=*?#vYZ${1pTvl4srpp6y)X|w@XdD-LXa8z3x7&4A%s{}BQ#q=d@#~1t3}bb z!dFU>z5Ghcn0Hd8^KbW^1d$fzp~W`~WJdptp-0foC==&@%lxZz%>C(BC@1Pw?8x^5 z-D{To|0?7aF!V^vn0Jz)N03*_KGnX1DpUZ>7=NT(hDj6(As}x)AGV?hrR~R_tzIXN z{t$O@wtr>p^xO;Ny_Zo)c0G2gW!$ye_1GyClDzja>A4qHy@M*;DnPpU?h;Bl_@0kn zT1E<5G6&Z3hadS#nZGnw=C?>ny7*3ErTrf9!{5U7OU%?1h0W55=@UHrXckkC;1=i6 z4Fl;qDCzR`mwEt53=IXg7dQY$PM=dsD(q~ARDWU*hGAgn8m{N#d2t-q#kS9?_~kgd zrt#~q9bMNh3B*79I~9*NhhNd--~YXlO6*}_X_EpRRC`mS# zLx0!SKNYl_Uy=0v{%cpqna|(gfzN!FQpsg(^a7ndF;KFuFF%x&^fH+oOY>Rw?mI*z zV)FJIuhBEG9o^LXflNK-0tY}mHi+YROr|G9G8I#nWuj~462kW-#gcTA>{;E$X>Cr*zXQLf`=w)OX;nSyIK(tkK5hi!v9>FVyno}6fY0Dw0zFhC*N!T5~{ ztcXQ#Vh3A#wlY6Imr12kP+k=WsssZEfNf_fm6nmx$F`@iA{}Uz4)9!@QW2>Xxm*S- zvi{X^yZNU_jwk>reP(kuON;Z|fA|Q+lFQ{w7bp~Zku6vXODW0NlQ@N2?Ad>aXn(}y z{9EVni>qdyz>d_F0hWp-GE-?{@dOH;3#{T^d zi-qncGT&D&GX;RRyujX19>lUt-g^C2x}A9W1HxoBAKYH>3@|vzQpq7_r-{YmM6L1& z1aMrB<&s0ro-B_*s|gvP&HP&R4W!WYNI3&I4t+yAh(x30?8&Bb0S4j;0DnrwBG;}@ zU|JSihIY`~Hvquwbha*gb*%zir--KMl(&Sl*(`>+{zX{3`2e4P z`bD8>x|+>Tv5=qS@Po&29GB6H7l?Fi4ZR&(kEXNbBEM9)$-V=J${FBquONLFgbsBS zfKW#OB$Gq9o=?WMNhTBMntx8Ey}mDTT#t-BMIteTp|AfktlfNoUJsDsqR=qxA{|FT9+kX&%(M#`u24}>R zB;teh@B{01{8ef)|EXWSNpdJ&<;(4oef#f2D4)^){1YMG;EZ^(DePK(QF|T$l~U}x z_g<867`^l_H1Re7uYYm8E_;C|t=gY^pEx;k+c{>*eN_D62ang~Kp1_T`A?i2fjiIf z)&Bg=cYhhkUg%uud;Uu5Rn>D!o#xrF<~Ao^wvW$B+m%%KUihP7p#wlk$uS=HDvP9@rs z{#LorW`0Gj@?*U@4P^jb(^~dtkUOeP{EZTL(3mfwGeGj{7!%t+foA9=uZ}UfYww3Y z)C6g3|0>f0ZEK{?uSiKnYW#BD&xHzrm~~c|lau@rpWgV#9ysXvJ^fYxRL(tK$UBdMY)3x$Qv^X4Kc}|FLFc1$gQ} Q_W%F@07*qoM6N<$f+*hl#Q*>R delta 2000 zcmV;>2QT=55Z(`vB!8tzL_t(|ob8)?Y+F|y$G_*k>|}X0iQ{L|ZPie#jCNJ0RjANS zVk<)+?a%$Q|A0RN1B9rC7y^VU6#+upAB^(&XA%P;4S|FhDjSoArZTp*8ZAUA8*9=y z&V4v>8^>|%`#R_x$DWe~bbal6Z@Te~tb23pdp^H&&hPxr!+*yF9z`i_eeiETyo(B= z009In1IKk?I}|Pj`REgGd(G0fKFIvP{@^Zlp^``-3IbTGxS((ym!-Gb^CPGK?I}OW zj}gol2q;F5d>#;7tX%vtPjg*Ax%uZe{s5QKQ29)(!rO6S@tggSmDzQiuJiNd0Ib$W zkA4B7+z)Eo7=OLzb1*LboTrDP9UM_4ya$HGzvEL+qoU8Dc=1Q=RT2t+H$>vz$1#vR zv_0|kCp_in%K=0QIB@@$QMmjZsA=MklQSqX2k<T|E6Z+?^Y*XB=~t~0;Mj6s%yw)4u3+DBuJtF%XVPfDVUZ8RXtBrS5tg_ z`m-;d>}ivD;-CLDO{Ie2H}%N3ej_pddUX@)<#n)?*vRk!D2fbq;S3c1WeOLVJT0txg=#C&w3h@Gcw6wADaR@TO{NfVKx_^nWiQ6$SIKcS9I{;uO5(!k|eOOq`Ly0LE z$?V7Q&_0yQYg#VHA_0ikT?x+)psG6RY^--3sOmD5SRaID1K1YKdX4W+8HNTW*8ACU z)O^Xl2VR7N;IXt}NnO)bEz+{D4d#}JRnxcJ%yIDfTm zBadf8I*I_*S{>T*5|XJ5`eF(Mp|Kuj<1p)i`tlM|sWcBuy;juxc8#Vc5x;o@*RC(Y zvP_I5Cvn@r0Otp-M6xl#+qi8I*YkPEvVzgE4Ejg*@$_61@9O9VlrrQ$Nyiv=9N{}hh^ zvzIR+Hn7k4c5EkF(HoQedgTVDj~r`6fWN!|*RcS?S3jO7fat^lve`5rYc*9xHk;u~ zt!8=IES&X1OPVXP1$ z_}XFK(|o4Y)m4k}OE+$yY|KM|$a~(J(udLP0nQIvtE;P=P%69w*MHoEAj)vzKEq*Lo*N$>MSyg&b$>$rE)O!(hmZ3AXRo};zg2jSd&eLDQ>5eS$btLSu zvopJnF;(kf^4o7e5`TCle31E%ot?q|7~@;*`O9zo#8ZC0Vzq=*eBN(% zwj*qRfvlp|)uY5ZI8*{6r4Q!S-LfHY^i~Odd;I}hr7l#pjJAlOY;^PvJMnXW7 zuD#B0Dhhr(Q|e;hqpKqv+=hUbT(<)M{h;n#H~h%AxAKVCYjem?eF%ai^83P_hdeyhdkH&#DiebCQsXzpY|1TM?f5xg4!|0mX>#$c#uhOl zt+cS!z6tW|lS$_Sr+@9U576A!{nG6RBPsJvwKY8LG$wZaQ;smc1K+9lI9mrNV0tN5 i@liX0pI+Y*fd2roW@816B9DFm0000-HBn{IhmIX3=Aykj=qiz3>*8o|0J>k`J4qFk;M!Qe1}1p@p%4< z6riAvr;B5VhxgmFhKvjbJS+>X>R(SiFy*bmf!iCIv=$(v#|ghPr+@Lu>SOS9 L^>bP0l+XkK+kYok diff --git a/assets/tree.png b/assets/tree.png index 78004b941f61db50329778df96eee5b24777cda2..2ef55f169d32710ff7ee9e1a7a0328330b245256 100644 GIT binary patch delta 1296 zcmV+r1@HQ%AEFA7BYyZ%Who95r)6&K64+=a4sYzl9psdHj+2UIzW&| z2m(lel~vxsjuXT7Gk9rRcKi$h0%V~)&JpcPY?grr5hfmF%;Y1kp20VE9h}Cp~h)`)&5i?Zah&;GVOJ%8>-G7+4I2ak@~>vZXMIuw$G!C8;-Y)W&lN}*I>HD8kyJd^2^?!7%a z?GDb%&ynrVA3V(u@a&@}cG^Ee)aZ?K7X&p2mM9O<5rs<4Y zyT)ud$BDz7p&^Tu3jpBg;UUg>l8(oF&w3dcuO|3{V>TP}_OE^o!1?8mbQ@hd*5X1( zhzhmc5>b_Uqe83M-s-?hQ?MK_xV*l?oM9q3=kVUK-`&G`;jPEN!MW_Y6G0@wywLVd zyoZz1K7WD-0lBftwgA}w4D%+48FR*zc8Yxe!}sjB_rPF2UV=CdI`{eR+o zR=d^Bp63OqHp)23-aYxD4<O_mY`NktOC`Q1mU}{)MX@AyQs2Ve7lq>;BFpS}Fh-u94K)F%o zYB~{%#=;g{T#m4446j}=ygEmspaFJkyJ!fYk$Idz8rYa%W>j~obekR8%}#!RPv8B5 zX1&f}&}X;aq_eXNL21>ygb2`JYBd>8 zr;=)*UM^#Rrh&~S<@Bsa|3wcCMv%aGJ`q#n^Y^xI4zD?o{NXRZL!5xX{=osYZ!T=M zSm7{s>s4abAoy-iWqL*n;GH@*(jG&w=2TM2A4*#0JXfR_5L&xeS&+{_;XhPpiJj;Dv2ioL5;9n<6M42Z&?FiW~|ZxD5?h8-)iny zx9kI;8ktNNq#;tS6u`C&|91}{7>-npO)!LPk`~GDz)u-6iNQ0IznuU800{s|MNUMn GLSTYdqi=x# literal 4006 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 M07*qoM6N<$f=+jYasU7T diff --git a/assets/water.png b/assets/water.png index d08257ae42f8bc0b6be5facd30ee541e57ad6940..155a14eb104c32c2018594bd3372dddc36c9ce77 100644 GIT binary patch delta 1145 zcmV-<1cv*J9*qf*BYy-`Nkl5yd9QV&fQ(Sc6e9tT8MDOR4iqz8DTl| z@k`IH#5}MCm)^V_Vum85+|ZbDVz!A|MpOo*aH7_zUa6%g4p5p0CLpF5isg)LZIg1w z%-EVt*qm$vN{HZI@lwGZ_wvsWc=GiRLQ^7TqcJ6`p?_+aDw-3T6CyHHMg-y|ybiXk zTPs8O_^WSYqYc{GDROE_$p!^fh-8Qg9)XFX+3;e71mS>Jw65>ZD;1EWXwybB%H(Wh z%_Ic_bi9R#kj=<#!*slfCPi>0oTwggTm>XIgV(7cr;Ig5RznB`P*sW<>W(!nq6T3E z6(tBirhfuhgQm$qASYTgk_l!?Qbjjs$m9enikUK6!P$s53nLSHcYl%y5l|IMfVl2YL_ zZv$&A2vN>Y$K1EkuwjA;BoR~u(cy4XNCHf@V7iT|A_(L0WbMb-5&$P6uoP^MjP2I; z(=S8g5g~~n45a?t*FRN1fC4Km@LE__=sgHPR7fgZIya>_RN(T>9Z*4CP{60Bh`pPH z0e{CWL+?7i|82oy8B~V|*xpDaQACFK*auPmmbMtmux;1du#!M6J=edy&h?#+J6)lR z0D+UE#OXF60vFG1aq;{~3PFh=!toMBDR@~PSAo0Y-JEOJzu@Pkv(q_!4NL(Qwl@;R zp)2sj+0#@4kHAY$DIH$x+7PVFK!j1|cYj#s5!Zg&qdl{M0aH^PSl&1$f$gi)KWrp0g{G$AV9Y#5srq!nG0CTSYChWKJ=f_KfV3{^ppO(U207d00000 LNkvXXu0mjf5ilR7 literal 3852 zcmV+n5A*PeP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000CxNklc<%j+%>Vo&#s@Y95k!RxufOu}0F-_?yh}k;G2dSu-7kRJ^#Ks2izq$SCIR7RY(4>3D3Rz3E?%i7ds%kG@2orQ=39gax9@D@wZprD*FuCNreY~$%?Qhx&)+zEe|!WF1gK1ow)U3hua<`vc&m^Ph&M zM9M~EN>)SFFjX`sG$%x4NJa$WCAQDVU}5+&4ySC$R1*NPYIGWVQM5x zoO$YT4z%C_7jJA0Gr)r!if5-LrI?~>fU)bu;GdDT9}g%zfAR!R&qlOa7@5$!lSGJs zs!#&Nb$22fBEn=gWq$J(A+nkTAdGKL$w=!z>@tyDzp&YigO)t7vCea4yRPV(&GmUR;r3aU<{mAxmLA~)k zUJ712;ownXRRDQ1qb}x1R%+)W*0FQ@R`lHtpM5b$3~<5AlKz(;h|)=fAYw>PSYBHZ zz<7*CP)h-IYF`LMYAGSQ07=6h2+-|FgrlKwFMBYy>UNklgw(}?d(yuS0FH<^;2L8@d}b` z;8h47gy0r}-~m9~7+-=P0Rn=9VMB-2&S__+4^^35^l0s67ilFCA?l)|`=T?y{PVBu zYB4k3<#O*d`~L$#_O=0jc<;zWbv7WFp+$D=7lxAtMuEezs=Vbo#>DYyMKUNH z6lKv-fc)w|l(@D6pL~2|vsF#zg~R=lA~}X;?Sj5{lwLSl)Z|%Urw`*{fr((zFlR)8%)uy>w9ya+QCL?EY8YIk zcqM=rAp|JA5Q1>BtSN$HR0>Zk{T~OUgiPQ%O<$eg!k{fthMJ*QxbPCd{qNp3 zhcGHi)=f>lt|_yOTs$8h6l6X_R8TXL7@7<pU zF1!G6|Kr<;hv}-OZfkM}ds}@@<`o~lU*I#3;86urI%ga7`(-D2gqV!^szJ>tiooft zyH`W5nm88##9`C3hC$X%vhldb(^tKZd~o+RvqeKc2baOlsG#)l?HeVCaC}-LVhlg>~YQ}>=8U^!vYD4e;srj_@MpoZtn!Vcqe!VKEUH? z!+sx}!1lPu>3l_U!gjwP8vJ8Y5hLtv6;y3PqtPmz<2R3ogPGh(~ho*-S{U!MFNz zZ=B&5H4`~wo?O8?L!m3f$F|m$`$+V(QN}dH`>`a%~NdFqi&jEh&v(I63o_{w)woA|B zdCSei57-?Q>}wtw)-B4y~rtDD}VEc>sNmP;Kx7xT?ZkcW@2ihYDDcuZi+@P&rt*IT=tdO sq@(!t6{Gf5>#hEb{uk?={}-J88$3DT%!oGzBme*a07*qoM6N<$f~p9$!2kdN literal 4143 zcmV+~5YX?5P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000GANklaOlN?d(yuS0J!Mz^f2E2*E7`!2^J} zF}^f@1UuN^VA#-MwR75;=|fc}7d=`KU}%*=lLgUL6~!2Mejn{8@5aBW;LyJ$GvEy2K4 zh^iQ%+ojErUw!h$H^;zyU6TRF`#pNO=f+Wwfp-+Skb5EXf)fzA1YtkC^Zx5I@Zs%= z%~my;2-goviX1|ok%_Rl*f8t|swA{Aae7{n3BUv#kW+qN-7#D8tnmxOZ!U6S3eSc%jT3^HojZ9XF0j3_+8isw7~#*q{c3 zeo3o}nUU0Zrtvc5nA+9K@b0afwrOh19pnM>44$rPym#C<3OFa23Q3h#jM;M2ML#UN z9uSSFu-r5(*EJzzs2R!?0NnlFO>+pNqGZ$5)SH?z%Lw9m_oyKA8KQ!kk;Kqss2PJn zK}w1yqfW+RRinu$%be*&OEOUXPZd4~;O+-E5f8IFpBOL4$RBb||(JGzdx1y7) zctY>GCl~Mn-ovA5&A0?b*c3O(lxqkc(`=f%rVW5ssRm%CiqBbMsIjwCYSA;97{wrJY<=w)%IClGq!-+X%V$_`up?{CNd7yvvu;miZuE2sbf002ovPDHLkV1lt!%1r Date: Sun, 18 Apr 2021 19:29:56 +0200 Subject: [PATCH 04/13] Optimize imports --- survival/__init__.py | 11 ----------- survival/game_map.py | 2 -- survival/player.py | 2 -- 3 files changed, 15 deletions(-) diff --git a/survival/__init__.py b/survival/__init__.py index 35625fc..b0e66dc 100644 --- a/survival/__init__.py +++ b/survival/__init__.py @@ -1,21 +1,10 @@ import pygame from settings import SCREEN_WIDTH, SCREEN_HEIGHT -from survival import esper from survival.camera import Camera -from survival.components.camera_target_component import CameraTargetComponent -from survival.components.input_component import InputComponent -from survival.components.movement_component import MovementComponent -from survival.components.position_component import PositionComponent -from survival.components.sprite_component import SpriteComponent from survival.game_map import GameMap from survival.player_generator import PlayerGenerator from survival.resource_generator import ResourceGenerator -from survival.systems.camera_system import CameraSystem -from survival.systems.collision_system import CollisionSystem -from survival.systems.draw_system import DrawSystem -from survival.systems.input_system import InputSystem -from survival.systems.movement_system import MovementSystem from survival.world_generator import WorldGenerator if __name__ == '__main__': diff --git a/survival/game_map.py b/survival/game_map.py index dfc0365..d5b1fc1 100644 --- a/survival/game_map.py +++ b/survival/game_map.py @@ -1,6 +1,4 @@ -from survival.components.position_component import PositionComponent from survival.entity_layer import EntityLayer -from survival.player import Player from survival.tile_layer import TileLayer diff --git a/survival/player.py b/survival/player.py index a689663..995f5e6 100644 --- a/survival/player.py +++ b/survival/player.py @@ -2,8 +2,6 @@ from random import randint import pygame -from survival.image import Image - class Player: def __init__(self): From 927db0c18a82a45fcc831a32375606999ef3c28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sun, 18 Apr 2021 19:53:51 +0200 Subject: [PATCH 05/13] Introduce time flow --- survival/components/time_component.py | 20 ++++++++++++++++++++ survival/player_generator.py | 2 ++ survival/systems/time_system.py | 12 ++++++++++++ survival/world_generator.py | 2 ++ 4 files changed, 36 insertions(+) create mode 100644 survival/components/time_component.py create mode 100644 survival/systems/time_system.py diff --git a/survival/components/time_component.py b/survival/components/time_component.py new file mode 100644 index 0000000..e8bd962 --- /dev/null +++ b/survival/components/time_component.py @@ -0,0 +1,20 @@ +class TimeComponent: + def __init__(self, minute, hour, day, timer): + self.minute = minute + self.hour = hour + self.day = day + self.timer = timer + + def add_time(self, minutes): + self.minute += minutes + if self.minute >= 60: + temp = self.minute - 60 + self.hour += 1 + if self.hour >= 24: + temp2 = self.hour - 24 + self.day += 1 + self.hour = temp2 + self.minute = temp + + def __str__(self): + return f'Day {self.day}, {self.hour}:{self.minute}' diff --git a/survival/player_generator.py b/survival/player_generator.py index df7a317..4a24660 100644 --- a/survival/player_generator.py +++ b/survival/player_generator.py @@ -3,6 +3,7 @@ from survival.components.input_component import InputComponent from survival.components.movement_component import MovementComponent from survival.components.position_component import PositionComponent from survival.components.sprite_component import SpriteComponent +from survival.components.time_component import TimeComponent class PlayerGenerator: @@ -19,5 +20,6 @@ class PlayerGenerator: sprite = SpriteComponent('stevenson.png') sprite.set_scale(1) world.add_component(player, sprite) + world.add_component(player, TimeComponent(0, 0, 0, 0)) return player diff --git a/survival/systems/time_system.py b/survival/systems/time_system.py new file mode 100644 index 0000000..a1624bf --- /dev/null +++ b/survival/systems/time_system.py @@ -0,0 +1,12 @@ +from survival import esper +from survival.components.time_component import TimeComponent + + +class TimeSystem(esper.Processor): + def process(self, dt): + for ent, time in self.world.get_component(TimeComponent): + time.timer += dt + if time.timer > 1000: + time.add_time(1) + time.timer = 0 + print(time) diff --git a/survival/world_generator.py b/survival/world_generator.py index 02e75ff..f202497 100644 --- a/survival/world_generator.py +++ b/survival/world_generator.py @@ -4,6 +4,7 @@ from survival.systems.collision_system import CollisionSystem from survival.systems.draw_system import DrawSystem from survival.systems.input_system import InputSystem from survival.systems.movement_system import MovementSystem +from survival.systems.time_system import TimeSystem class WorldGenerator: @@ -15,5 +16,6 @@ class WorldGenerator: world.add_processor(MovementSystem(), priority=1) world.add_processor(CollisionSystem(game_map), priority=2) world.add_processor(DrawSystem(camera)) + world.add_processor(TimeSystem()) return world From a1f5b605f80bc750c2a7cb6f78f83eba0acfece9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sun, 18 Apr 2021 21:15:11 +0200 Subject: [PATCH 06/13] Add BFS algorithm --- survival/pathfinding.py | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 survival/pathfinding.py diff --git a/survival/pathfinding.py b/survival/pathfinding.py new file mode 100644 index 0000000..c979e79 --- /dev/null +++ b/survival/pathfinding.py @@ -0,0 +1,54 @@ +from collections import deque as Queue + + +def valid_neighbor(n, game_map, visited): + if n[0] < 0 or n[1] < 0 or n[0] >= game_map.width or n[1] >= game_map.height: + return False + if visited[n[0]][n[1]]: + return False + if game_map.is_colliding(n): + return False + + return True + + +def breadth_first_search(game_map, start, target): + visited = [[False for _ in range(game_map.width)] for _ in range(game_map.height)] + q = Queue() + came_from = dict() + came_from[start] = (-1, -1) + + q.append(start) + visited[start[0]][start[1]] = True + + while len(q) > 0: + cell = q.popleft() + + if cell == target: + break + + neighbors = [ + (cell[0] - 1, cell[1]), + (cell[0], cell[1] + 1), + (cell[0] + 1, cell[1]), + (cell[0], cell[1] - 1), + ] + + for neighbor in neighbors: + if valid_neighbor(neighbor, game_map, visited): + q.append(neighbor) + visited[neighbor[0]][neighbor[1]] = True + came_from[neighbor] = cell + + path = list() + current = target + if came_from.get(start, None) is not None: + path.append(start) + return path + + while current != start: + path.append(current) + current = came_from[current] + + path.append(start) + return path From 5b469611e64551506e04e347ef60ae2c4b56674a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 19 Apr 2021 00:07:40 +0200 Subject: [PATCH 07/13] Change movement to directional based one --- assets/stevenson.png | Bin 2048 -> 2021 bytes survival/components/moving_component.py | 7 ++--- survival/components/position_component.py | 4 +++ survival/enums.py | 27 +++++++++++++++++ survival/systems/collision_system.py | 16 ++++++---- survival/systems/draw_system.py | 1 + survival/systems/input_system.py | 9 +++--- survival/systems/movement_system.py | 35 +++++++++++----------- 8 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 survival/enums.py diff --git a/assets/stevenson.png b/assets/stevenson.png index 527dcb927faf87ae3cab7ce636909089bb899eed..9caa64f8681cdd60eb01d22c0b109253cade59f5 100644 GIT binary patch delta 2007 zcmV;|2PpV}5akb$B!8?)L_t(|oaI|vY+Oef{^s0v?fMqm>%H1(8x-Fxr4Vqc+vT-fQe4N1uG#Yn6dOlti08qA36PBPxVPb z8o_*lfMVpx7XZP<%Eh1XGS>}KTQD%e?R4GbZ|ZOS0WPIo<8O{b#_2jv$Mpqr0H%8M z=ocZ%{h+pu(SQ3s5987=czHK)*QcIAMV~|Q;*Z;#)HVL?JfcW=1n36;P85mzpTI!! z(5;21KjEpqKn@^Ez<~$9g2LtJK}{2Pot#0DIe_O0r86WCp5o8PA3lrxU!H0R5n02! z!!Pb&H75P!H^QI(q-`1}A3h5yHr%p_@A`r~^uSl&27e(0+T5Q(#X7%l&k@2#lTW|; zV%Ip^!T7gBQM%;)+tBR%o!aCDLXd|xUaLTx{UanX22qY9fAQBSzqE$6Kiu3iVY|Wk z4~6>{IDZsKp7_?gSa&Mx2AB<)EtpN(UtST&0am5oqmc%=JPl&bSCpR(no;=1(%LUk zwEx8U?|=UEGm#j6>eSP%`^?Gj!rCZxy$wt|vjMXOvq}5QD}t>8Tnmm>1*Ik&+ksQx zgiE?N2XlV2vc+#Bb^L3voH|z9lETpWttuuT`9VW}b*-Vl3plfK22Ve#Ll$GOYGsI$ z1lKZoo4)+Q5N|*xo%Y-i&kiti?gFJQg{o`FWq%Grk|ao?0Lyk@+bNiq1ywyyQ&&@b zefsk+oeae!kA3^M660U5ZeqQ>4z>~+89o3-k)f`fiN65U!r=9!|NL)gDiyANmivDH z@)4M(g_~<@5Jg~cXbgk>IRMM7KW#sid3FFzH{iMsUwbYUR0yKDm3UBIZd(o;8qf^` zqJI!xC~zHTeE!-3%Ed)I@Uf4hUbiqedkOtRNyhI>ydU(6ex^~=3{=ZH4ox3JEGFZP zS6{|ZVhW-xj>XbO&f)!Jjv#gl#Mv%=?sqJqEXMZ1oMkaFzY79CV%e4z~BJm56uCh*53v@kw~Bt@591k9!gBX zNM=8VhxVadUej_p770MS?uvMJ09DmdXLG&lKvkEa#QGpK4PaX^>ovYRWf&Ti*xqO7 ztn=BG@s|u0)r~UlKYjwWx`nG(E}>Ex;rwACYW+>Pt_w{qz^vTF!NbQ8kIA_B`hNvD zwOeLB&xY_70jjk+wB;ovQyKKd6bM3NJTRn`VuV5 z#7J@ycMJ@0{;<$1`V+}Ug12$UAg<^0kYxp)e5h_qB(#(J@U;}4$V5n%T6CBz2y z1>O#QzvyRJ(HlvAy>bK7M~*cjz+Yd4>sSEcs}Ii!ApAH$Hk;;iou;bDW;1-L)3i6s zg4rn0)MaEcX-Hx?%b@J!lz*hDsx>i@1R)aMnZFLlvXRKJRlp$U4-37ZpCOawSw1T3 z!&q2YgeXfGn%Iw2CJS~(VXIFqul4ze07b$ai+Q(htlTo%*gQyG7m}jDFjfc=0_`xr zCstQiEyiEEaRX&z9s)$}`D;oaMzaSve^}@R{b<;LUrU8|;F_BdM1L7B94LMLynK9o z%yZ|%R|H5WTlXi_@9`irefTKslw6FRC9SM8< z?97gHOx1dn{O&uC`G0Ca5ZuoEkDr~vE^~aVKY#VjpL(hmvBk|^tSvzi*a6s_S1oN|v%C@N4%}}1O+>{XrT%tK+QDJOzz!g>z3{li zqmd9|(z6?X6H)O;slT05>R{iYqaz%=4IwUhdL17G{hhgP_kSbbj^OifB*f9sVh=TU z<^Y6jos7Rt@d41^p#$_z6o>-sINJMNe`m)yDTyxpZbMY<=r-?O^K}#f_=%Wkj>Gqi z<3**v9i7-Ba0JL+n?ru;Ll7j9-xuyazrYeUk@r0M_9Q zlZ$UPwulL7l~9F^uYc|#n%m-7y7OQU82N0{HizZaPB pD4gJ3&hFy`&R!$PsBZzl{{Vf#V+A$pOppKo002ovPDHLkV1jG$uW$Eh5_xJf7pWpeN-+wti76g=1s(Ss8pWUN0 zT|+}40AETxUm+FZ(@(z})MRLVocVwM={*3-&;`1tp#X_O`Vv4?KKH5P{|jW#2;+oO zAW-xk_zGx}>GQupDUdRZwebA%3#3vl#dhM@PtCc&;V^HCOs=ycWRG?=(OC1H|?m10eeFNhbdK z3;>1>=*?#vYZ${1pTvl4srpp6y)X|w@XdD-LXa8z3x7&4A%s{}BQ#q=d@#~1t3}bb z!dFU>z5Ghcn0Hd8^KbW^1d$fzp~W`~WJdptp-0foC==&@%lxZz%>C(BC@1Pw?8x^5 z-D{To|0?7aF!V^vn0Jz)N03*_KGnX1DpUZ>7=NT(hDj6(As}x)AGV?hrR~R_tzIXN z{t$O@wtr>p^xO;Ny_Zo)c0G2gW!$ye_1GyClDzja>A4qHy@M*;DnPpU?h;Bl_@0kn zT1E<5G6&Z3hadS#nZGnw=C?>ny7*3ErTrf9!{5U7OU%?1h0W55=@UHrXckkC;1=i6 z4Fl;qDCzR`mwEt53=IXg7dQY$PM=dsD(q~ARDWU*hGAgn8m{N#d2t-q#kS9?_~kgd zrt#~q9bMNh3B*79I~9*NhhNd--~YXlO6*}_X_EpRRC`mS# zLx0!SKNYl_Uy=0v{%cpqna|(gfzN!FQpsg(^a7ndF;KFuFF%x&^fH+oOY>Rw?mI*z zV)FJIuhBEG9o^LXflNK-0tY}mHi+YROr|G9G8I#nWuj~462kW-#gcTA>{;E$X>Cr*zXQLf`=w)OX;nSyIK(tkK5hi!v9>FVyno}6fY0Dw0zFhC*N!T5~{ ztcXQ#Vh3A#wlY6Imr12kP+k=WsssZEfNf_fm6nmx$F`@iA{}Uz4)9!@QW2>Xxm*S- zvi{X^yZNU_jwk>reP(kuON;Z|fA|Q+lFQ{w7bp~Zku6vXODW0NlQ@N2?Ad>aXn(}y z{9EVni>qdyz>d_F0hWp-GE-?{@dOH;3#{T^d zi-qncGT&D&GX;RRyujX19>lUt-g^C2x}A9W1HxoBAKYH>3@|vzQpq7_r-{YmM6L1& z1aMrB<&s0ro-B_*s|gvP&HP&R4W!WYNI3&I4t+yAh(x30?8&Bb0S4j;0DnrwBG;}@ zU|JSihIY`~Hvquwbha*gb*%zir--KMl(&Sl*(`>+{zX{3`2e4P z`bD8>x|+>Tv5=qS@Po&29GB6H7l?Fi4ZR&(kEXNbBEM9)$-V=J${FBquONLFgbsBS zfKW#OB$Gq9o=?WMNhTBMntx8Ey}mDTT#t-BMIteTp|AfktlfNoUJsDsqR=qxA{|FT9+kX&%(M#`u24}>R zB;teh@B{01{8ef)|EXWSNpdJ&<;(4oef#f2D4)^){1YMG;EZ^(DePK(QF|T$l~U}x z_g<867`^l_H1Re7uYYm8E_;C|t=gY^pEx;k+c{>*eN_D62ang~Kp1_T`A?i2fjiIf z)&Bg=cYhhkUg%uud;Uu5Rn>D!o#xrF<~Ao^wvW$B+m%%KUihP7p#wlk$uS=HDvP9@rs z{#LorW`0Gj@?*U@4P^jb(^~dtkUOeP{EZTL(3mfwGeGj{7!%t+foA9=uZ}UfYww3Y z)C6g3|0>f0ZEK{?uSiKnYW#BD&xHzrm~~c|lau@rpWgV#9ysXvJ^fYxRL(tK$UBdMY)3x$Qv^X4Kc}|FLFc1$gQ} Q_W%F@07*qoM6N<$f?$05%m4rY diff --git a/survival/components/moving_component.py b/survival/components/moving_component.py index 0de446e..ec2e69e 100644 --- a/survival/components/moving_component.py +++ b/survival/components/moving_component.py @@ -1,5 +1,4 @@ class MovingComponent: - def __init__(self, direction, target): - self.direction = direction - self.movement_target = target - self.checked_collision = False + def __init__(self): + self.target = None + self.direction_vector = None diff --git a/survival/components/position_component.py b/survival/components/position_component.py index 5164100..fee7c98 100644 --- a/survival/components/position_component.py +++ b/survival/components/position_component.py @@ -1,4 +1,8 @@ +from survival.enums import Direction + + class PositionComponent: def __init__(self, pos, grid_pos): self.position = pos self.grid_position = grid_pos + self.direction = Direction.DOWN diff --git a/survival/enums.py b/survival/enums.py new file mode 100644 index 0000000..db44dec --- /dev/null +++ b/survival/enums.py @@ -0,0 +1,27 @@ +from enum import IntEnum + + +class Direction(IntEnum): + DOWN = 0 + LEFT = 1 + UP = 2 + RIGHT = 3 + + @staticmethod + def rotate_left(direction): + return Direction((direction - 1) % 4) + + @staticmethod + def rotate_right(direction): + return Direction((direction + 1) % 4) + + @staticmethod + def get_vector(direction): + if direction == Direction.UP: + return 0, -1 + elif direction == Direction.DOWN: + return 0, 1 + elif direction == Direction.LEFT: + return -1, 0 + elif direction == Direction.RIGHT: + return 1, 0 diff --git a/survival/systems/collision_system.py b/survival/systems/collision_system.py index 5a7055e..8be71a3 100644 --- a/survival/systems/collision_system.py +++ b/survival/systems/collision_system.py @@ -1,6 +1,9 @@ +import operator + from survival import esper from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent +from survival.enums import Direction class CollisionSystem(esper.Processor): @@ -9,17 +12,20 @@ class CollisionSystem(esper.Processor): def process(self, dt): for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent): - if moving.checked_collision: + if moving.target is not None: continue moving.checked_collision = True - if self.check_collision(moving.movement_target): - self.world.remove_component(ent, MovingComponent) + vector = Direction.get_vector(pos.direction) + moving.target = tuple(map(operator.add, vector, pos.grid_position)) + moving.direction_vector = vector + if self.check_collision(moving.target): + self.world.remove_component(ent, MovingComponent) else: - self.map.move_entity(pos.grid_position, moving.movement_target) - pos.grid_position = moving.movement_target + self.map.move_entity(pos.grid_position, moving.target) + pos.grid_position = moving.target def check_collision(self, pos): return self.map.is_colliding(pos) diff --git a/survival/systems/draw_system.py b/survival/systems/draw_system.py index cf70fe7..2cb35ae 100644 --- a/survival/systems/draw_system.py +++ b/survival/systems/draw_system.py @@ -10,4 +10,5 @@ class DrawSystem(esper.Processor): def process(self, dt): for ent, (sprite, pos) in self.world.get_components(SpriteComponent, PositionComponent): sprite.image.pos = pos.position + sprite.image.origin = (32 * pos.direction.value, 0) self.camera.draw(sprite.image) diff --git a/survival/systems/input_system.py b/survival/systems/input_system.py index c8d7cc8..6930035 100644 --- a/survival/systems/input_system.py +++ b/survival/systems/input_system.py @@ -4,6 +4,7 @@ from survival import esper from survival.components.input_component import InputComponent from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent +from survival.enums import Direction class InputSystem(esper.Processor): @@ -17,10 +18,8 @@ class InputSystem(esper.Processor): if self.world.has_component(ent, MovingComponent): continue if keys[pygame.K_LEFT]: - self.world.add_component(ent, MovingComponent([-1, 0], [pos.grid_position[0] - 1, pos.grid_position[1]])) + pos.direction = Direction.rotate_left(pos.direction) elif keys[pygame.K_RIGHT]: - self.world.add_component(ent, MovingComponent([1, 0], [pos.grid_position[0] + 1, pos.grid_position[1]])) - elif keys[pygame.K_DOWN]: - self.world.add_component(ent, MovingComponent([0, 1], [pos.grid_position[0], pos.grid_position[1] + 1])) + pos.direction = Direction.rotate_right(pos.direction) elif keys[pygame.K_UP]: - self.world.add_component(ent, MovingComponent([0, -1], [pos.grid_position[0], pos.grid_position[1] - 1])) + self.world.add_component(ent, MovingComponent()) diff --git a/survival/systems/movement_system.py b/survival/systems/movement_system.py index 70ad744..5f0d868 100644 --- a/survival/systems/movement_system.py +++ b/survival/systems/movement_system.py @@ -13,23 +13,22 @@ class MovementSystem(esper.Processor): for ent, (mov, pos, moving, sprite) in self.world.get_components(MovementComponent, PositionComponent, MovingComponent, SpriteComponent): - if moving.direction[0] != 0: - pos.position[0] += moving.direction[0] * mov.speed * dt / 100 - if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed: - pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32] - self.world.remove_component(ent, MovingComponent) - else: - pos.position[1] += moving.direction[1] * mov.speed * dt / 100 - if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed: - pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32] - self.world.remove_component(ent, MovingComponent) - if moving.direction[0] == 1: - sprite.image.origin = (96, 0) - elif moving.direction[0] == -1: - sprite.image.origin = (64, 0) - elif moving.direction[1] == 1: - sprite.image.origin = (0, 0) - else: - sprite.image.origin = (32, 0) + pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100 + pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100 + if abs(moving.target[0] * 32 - pos.position[0]) < 0.1 * mov.speed and abs( + pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed: + pos.position = [moving.target[0] * 32, moving.target[1] * 32] + self.world.remove_component(ent, MovingComponent) + + # if moving.direction[0] != 0: + # pos.position[0] += moving.direction[0] * mov.speed * dt / 100 + # if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed: + # pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32] + # self.world.remove_component(ent, MovingComponent) + # else: + # pos.position[1] += moving.direction[1] * mov.speed * dt / 100 + # if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed: + # pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32] + # self.world.remove_component(ent, MovingComponent) From 3463bd0415b0e11aee91a18f6f23067a2c5b4182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 19 Apr 2021 00:24:42 +0200 Subject: [PATCH 08/13] Check map bounds when checking collision --- survival/game_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/survival/game_map.py b/survival/game_map.py index d5b1fc1..b8563be 100644 --- a/survival/game_map.py +++ b/survival/game_map.py @@ -23,4 +23,4 @@ class GameMap: self.entity_layer.remove_entity(pos) def is_colliding(self, pos): - return self.entity_layer.is_colliding(pos) + return pos[0] < 0 or pos[0] >= self.width or pos[1] < 0 or pos[1] >= self.height or self.entity_layer.is_colliding(pos) From 4467ca555ea5576aaa7cfb2eb04a743f3f3c30a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 19 Apr 2021 01:41:02 +0200 Subject: [PATCH 09/13] Add pathfinding usage --- survival/components/pathfinding_component.py | 4 +++ survival/components/position_component.py | 6 ++++ survival/enums.py | 7 ++++ survival/pathfinding.py | 16 +++++---- survival/systems/collision_system.py | 1 - survival/systems/input_system.py | 17 +++++++--- .../systems/pathfinding_movement_system.py | 34 +++++++++++++++++++ survival/world_generator.py | 4 ++- 8 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 survival/components/pathfinding_component.py create mode 100644 survival/systems/pathfinding_movement_system.py diff --git a/survival/components/pathfinding_component.py b/survival/components/pathfinding_component.py new file mode 100644 index 0000000..71f7410 --- /dev/null +++ b/survival/components/pathfinding_component.py @@ -0,0 +1,4 @@ +class PathfindingComponent: + def __init__(self, target_pos): + self.target_grid_pos = (int(target_pos[0] / 32), int(target_pos[1] / 32)) + self.path = None diff --git a/survival/components/position_component.py b/survival/components/position_component.py index fee7c98..323cecb 100644 --- a/survival/components/position_component.py +++ b/survival/components/position_component.py @@ -6,3 +6,9 @@ class PositionComponent: self.position = pos self.grid_position = grid_pos self.direction = Direction.DOWN + + def rotate_left(self): + self.direction = Direction.rotate_left(self.direction) + + def rotate_right(self): + self.direction = Direction.rotate_right(self.direction) diff --git a/survival/enums.py b/survival/enums.py index db44dec..cefa661 100644 --- a/survival/enums.py +++ b/survival/enums.py @@ -25,3 +25,10 @@ class Direction(IntEnum): return -1, 0 elif direction == Direction.RIGHT: return 1, 0 + + @staticmethod + def from_vector(vector): + if vector[0] == 0: + return Direction.DOWN if vector[1] == 1 else Direction.UP + else: + return Direction.LEFT if vector[0] == -1 else Direction.RIGHT diff --git a/survival/pathfinding.py b/survival/pathfinding.py index c979e79..dfaa68b 100644 --- a/survival/pathfinding.py +++ b/survival/pathfinding.py @@ -13,10 +13,13 @@ def valid_neighbor(n, game_map, visited): def breadth_first_search(game_map, start, target): - visited = [[False for _ in range(game_map.width)] for _ in range(game_map.height)] + visited = [[False for _ in range(game_map.height)] for _ in range(game_map.width)] q = Queue() came_from = dict() - came_from[start] = (-1, -1) + start = tuple(start) + target = tuple(target) + + came_from[start] = None q.append(start) visited[start[0]][start[1]] = True @@ -42,13 +45,14 @@ def breadth_first_search(game_map, start, target): path = list() current = target - if came_from.get(start, None) is not None: - path.append(start) - return path while current != start: path.append(current) + if current not in came_from: + path.clear() + return path + current = came_from[current] - path.append(start) + path.reverse() return path diff --git a/survival/systems/collision_system.py b/survival/systems/collision_system.py index 8be71a3..5e0bea2 100644 --- a/survival/systems/collision_system.py +++ b/survival/systems/collision_system.py @@ -20,7 +20,6 @@ class CollisionSystem(esper.Processor): vector = Direction.get_vector(pos.direction) moving.target = tuple(map(operator.add, vector, pos.grid_position)) moving.direction_vector = vector - if self.check_collision(moving.target): self.world.remove_component(ent, MovingComponent) else: diff --git a/survival/systems/input_system.py b/survival/systems/input_system.py index 6930035..41b5225 100644 --- a/survival/systems/input_system.py +++ b/survival/systems/input_system.py @@ -3,23 +3,30 @@ import pygame from survival import esper from survival.components.input_component import InputComponent from survival.components.moving_component import MovingComponent +from survival.components.pathfinding_component import PathfindingComponent from survival.components.position_component import PositionComponent -from survival.enums import Direction class InputSystem(esper.Processor): - def __init__(self): - self.map = None + def __init__(self, camera): + self.camera = camera def process(self, dt): for ent, (inp, pos) in self.world.get_components(InputComponent, PositionComponent): keys = pygame.key.get_pressed() + mouse = pygame.mouse.get_pressed(3) + if mouse[0] == 1: + pos = pygame.mouse.get_pos() + pos = (pos[0] - self.camera.camera.left, pos[1] - self.camera.camera.top) + if self.world.has_component(ent, PathfindingComponent): + self.world.remove_component(ent, PathfindingComponent) + self.world.add_component(ent, PathfindingComponent(pos)) if self.world.has_component(ent, MovingComponent): continue if keys[pygame.K_LEFT]: - pos.direction = Direction.rotate_left(pos.direction) + pos.rotate_left() elif keys[pygame.K_RIGHT]: - pos.direction = Direction.rotate_right(pos.direction) + pos.rotate_right() elif keys[pygame.K_UP]: self.world.add_component(ent, MovingComponent()) diff --git a/survival/systems/pathfinding_movement_system.py b/survival/systems/pathfinding_movement_system.py new file mode 100644 index 0000000..617b4ed --- /dev/null +++ b/survival/systems/pathfinding_movement_system.py @@ -0,0 +1,34 @@ +from survival import esper +from survival.components.movement_component import MovementComponent +from survival.components.moving_component import MovingComponent +from survival.components.position_component import PositionComponent +from survival.enums import Direction +from survival.pathfinding import breadth_first_search +from survival.systems.input_system import PathfindingComponent + + +class PathfindingMovementSystem(esper.Processor): + def __init__(self, game_map): + self.game_map = game_map + pass + + def process(self, dt): + for ent, (pos, pathfinding, movement) in self.world.get_components(PositionComponent, PathfindingComponent, + MovementComponent): + if pathfinding.path is None: + pathfinding.path = breadth_first_search(self.game_map, pos.grid_position, pathfinding.target_grid_pos) + + if len(pathfinding.path) < 1: + self.world.remove_component(ent, PathfindingComponent) + continue + + if self.world.has_component(ent, MovingComponent): + continue + + target = pathfinding.path.pop(0) + vector = (target[0] - pos.grid_position[0], target[1] - pos.grid_position[1]) + direction = Direction.from_vector(vector) + if direction != pos.direction: + pos.direction = direction + + self.world.add_component(ent, MovingComponent()) diff --git a/survival/world_generator.py b/survival/world_generator.py index f202497..c7691b9 100644 --- a/survival/world_generator.py +++ b/survival/world_generator.py @@ -4,6 +4,7 @@ from survival.systems.collision_system import CollisionSystem from survival.systems.draw_system import DrawSystem from survival.systems.input_system import InputSystem from survival.systems.movement_system import MovementSystem +from survival.systems.pathfinding_movement_system import PathfindingMovementSystem from survival.systems.time_system import TimeSystem @@ -11,11 +12,12 @@ class WorldGenerator: def create_world(self, camera, game_map): world = esper.World() - world.add_processor(InputSystem()) + world.add_processor(InputSystem(camera)) world.add_processor(CameraSystem(camera)) world.add_processor(MovementSystem(), priority=1) world.add_processor(CollisionSystem(game_map), priority=2) world.add_processor(DrawSystem(camera)) world.add_processor(TimeSystem()) + world.add_processor(PathfindingMovementSystem(game_map), priority=3) return world From 4151f5996c74cd9a926cf0a71410421de20e59c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 19 Apr 2021 12:05:33 +0200 Subject: [PATCH 10/13] Add delay to direction change --- survival/components/direction_component.py | 12 +++++++++ survival/components/pathfinding_component.py | 1 + survival/components/position_component.py | 6 +++-- survival/settings.py | 1 + survival/systems/direction_system.py | 27 +++++++++++++++++++ survival/systems/input_system.py | 7 +++-- .../systems/pathfinding_movement_system.py | 14 +++++++--- survival/world_generator.py | 2 ++ 8 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 survival/components/direction_component.py create mode 100644 survival/systems/direction_system.py diff --git a/survival/components/direction_component.py b/survival/components/direction_component.py new file mode 100644 index 0000000..e768a48 --- /dev/null +++ b/survival/components/direction_component.py @@ -0,0 +1,12 @@ +from survival.enums import Direction + + +class DirectionChangeComponent: + def __init__(self, direction): + self.direction = direction + + def rotate_left(self): + self.direction = Direction.rotate_left(self.direction) + + def rotate_right(self): + self.direction = Direction.rotate_right(self.direction) diff --git a/survival/components/pathfinding_component.py b/survival/components/pathfinding_component.py index 71f7410..4dbe908 100644 --- a/survival/components/pathfinding_component.py +++ b/survival/components/pathfinding_component.py @@ -1,4 +1,5 @@ class PathfindingComponent: def __init__(self, target_pos): self.target_grid_pos = (int(target_pos[0] / 32), int(target_pos[1] / 32)) + self.current_target = None self.path = None diff --git a/survival/components/position_component.py b/survival/components/position_component.py index 323cecb..fc69965 100644 --- a/survival/components/position_component.py +++ b/survival/components/position_component.py @@ -1,4 +1,5 @@ from survival.enums import Direction +from survival.settings import DIRECTION_CHANGE_DELAY class PositionComponent: @@ -6,9 +7,10 @@ class PositionComponent: self.position = pos self.grid_position = grid_pos self.direction = Direction.DOWN + self.direction_change_timer = DIRECTION_CHANGE_DELAY def rotate_left(self): - self.direction = Direction.rotate_left(self.direction) + return Direction.rotate_left(self.direction) def rotate_right(self): - self.direction = Direction.rotate_right(self.direction) + return Direction.rotate_right(self.direction) diff --git a/survival/settings.py b/survival/settings.py index 577806c..fd6e84f 100644 --- a/survival/settings.py +++ b/survival/settings.py @@ -1,3 +1,4 @@ SCREEN_WIDTH = 1920 SCREEN_HEIGHT = 1080 RESOURCES_AMOUNT = 300 +DIRECTION_CHANGE_DELAY = 200 diff --git a/survival/systems/direction_system.py b/survival/systems/direction_system.py new file mode 100644 index 0000000..86d9f8f --- /dev/null +++ b/survival/systems/direction_system.py @@ -0,0 +1,27 @@ +from survival import esper +from survival.components.direction_component import DirectionChangeComponent +from survival.components.position_component import PositionComponent +from survival.settings import DIRECTION_CHANGE_DELAY + + +class DirectionSystem(esper.Processor): + def process(self, dt): + for ent, (pos, direction) in self.world.get_components(PositionComponent, DirectionChangeComponent): + if pos.direction_change_timer > 0: + pos.direction_change_timer -= dt + continue + + dir_left = pos.rotate_left() + dir_right = pos.rotate_right() + + pos.direction_change_timer = DIRECTION_CHANGE_DELAY + + if dir_left == direction.direction: + pos.direction = dir_left + elif dir_right == direction.direction: + pos.direction = dir_right + else: + pos.direction = dir_left + continue + + self.world.remove_component(ent, DirectionChangeComponent) diff --git a/survival/systems/input_system.py b/survival/systems/input_system.py index 41b5225..09282fb 100644 --- a/survival/systems/input_system.py +++ b/survival/systems/input_system.py @@ -1,6 +1,7 @@ import pygame from survival import esper +from survival.components.direction_component import DirectionChangeComponent from survival.components.input_component import InputComponent from survival.components.moving_component import MovingComponent from survival.components.pathfinding_component import PathfindingComponent @@ -25,8 +26,10 @@ class InputSystem(esper.Processor): if self.world.has_component(ent, MovingComponent): continue if keys[pygame.K_LEFT]: - pos.rotate_left() + if not self.world.has_component(ent, DirectionChangeComponent): + self.world.add_component(ent, DirectionChangeComponent(pos.rotate_left())) elif keys[pygame.K_RIGHT]: - pos.rotate_right() + if not self.world.has_component(ent, DirectionChangeComponent): + self.world.add_component(ent, DirectionChangeComponent(pos.rotate_right())) elif keys[pygame.K_UP]: self.world.add_component(ent, MovingComponent()) diff --git a/survival/systems/pathfinding_movement_system.py b/survival/systems/pathfinding_movement_system.py index 617b4ed..a40d5bc 100644 --- a/survival/systems/pathfinding_movement_system.py +++ b/survival/systems/pathfinding_movement_system.py @@ -1,4 +1,5 @@ from survival import esper +from survival.components.direction_component import DirectionChangeComponent from survival.components.movement_component import MovementComponent from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent @@ -18,17 +19,24 @@ class PathfindingMovementSystem(esper.Processor): if pathfinding.path is None: pathfinding.path = breadth_first_search(self.game_map, pos.grid_position, pathfinding.target_grid_pos) - if len(pathfinding.path) < 1: + if len(pathfinding.path) < 1 and pathfinding.current_target is None: self.world.remove_component(ent, PathfindingComponent) continue if self.world.has_component(ent, MovingComponent): continue - target = pathfinding.path.pop(0) + if pathfinding.current_target is None: + target = pathfinding.path.pop(0) + else: + target = pathfinding.current_target + vector = (target[0] - pos.grid_position[0], target[1] - pos.grid_position[1]) direction = Direction.from_vector(vector) if direction != pos.direction: - pos.direction = direction + pathfinding.current_target = target + self.world.add_component(ent, DirectionChangeComponent(direction)) + continue + pathfinding.current_target = None self.world.add_component(ent, MovingComponent()) diff --git a/survival/world_generator.py b/survival/world_generator.py index c7691b9..586564b 100644 --- a/survival/world_generator.py +++ b/survival/world_generator.py @@ -1,6 +1,7 @@ from survival import esper from survival.systems.camera_system import CameraSystem from survival.systems.collision_system import CollisionSystem +from survival.systems.direction_system import DirectionSystem from survival.systems.draw_system import DrawSystem from survival.systems.input_system import InputSystem from survival.systems.movement_system import MovementSystem @@ -19,5 +20,6 @@ class WorldGenerator: world.add_processor(DrawSystem(camera)) world.add_processor(TimeSystem()) world.add_processor(PathfindingMovementSystem(game_map), priority=3) + world.add_processor(DirectionSystem()) return world From d513223a6d08e03099a40baebc47bb805bf097ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Mon, 19 Apr 2021 17:20:40 +0200 Subject: [PATCH 11/13] Add graph search --- survival/components/position_component.py | 7 +- survival/graph_search.py | 88 +++++++++++++++++++ survival/systems/input_system.py | 5 +- .../systems/pathfinding_movement_system.py | 50 +++++++---- 4 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 survival/graph_search.py diff --git a/survival/components/position_component.py b/survival/components/position_component.py index fc69965..2838d6f 100644 --- a/survival/components/position_component.py +++ b/survival/components/position_component.py @@ -1,13 +1,12 @@ from survival.enums import Direction -from survival.settings import DIRECTION_CHANGE_DELAY class PositionComponent: - def __init__(self, pos, grid_pos): + def __init__(self, pos, grid_pos, direction=Direction.DOWN): self.position = pos self.grid_position = grid_pos - self.direction = Direction.DOWN - self.direction_change_timer = DIRECTION_CHANGE_DELAY + self.direction = direction + self.direction_change_timer = 0 def rotate_left(self): return Direction.rotate_left(self.direction) diff --git a/survival/graph_search.py b/survival/graph_search.py new file mode 100644 index 0000000..92054e7 --- /dev/null +++ b/survival/graph_search.py @@ -0,0 +1,88 @@ +from enum import Enum + +from survival import GameMap +from survival.components.position_component import PositionComponent +from survival.enums import Direction + + +class Action(Enum): + ROTATE_LEFT = 0 + ROTATE_RIGHT = 1 + MOVE = 2 + + +class State: + def __init__(self, position, direction): + self.position = position + self.direction = direction + + +class Node: + def __init__(self, state: State, parent=None, action=None): + self.state = state + self.parent = parent + self.action = action + + +def get_moved_position(position, direction): + vector = Direction.get_vector(direction) + return position[0] + vector[0], position[1] + vector[1] + + +def get_states(state: State, game_map: GameMap): + states = list() + + states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)))) + states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction)))) + + target_state = get_moved_position(state.position, state.direction) + if not game_map.is_colliding(target_state): + states.append((Action.MOVE, State(target_state, state.direction))) + + return states + + +def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple): + fringe = list() + explored = list() + + explored_states = set() + fringe_states = set() + + start = State(start.grid_position, start.direction) + fringe.append(Node(start)) + fringe_states.add((tuple(start.position), start.direction)) + + while True: + # No solutions found + if not any(fringe): + return [] + + node = fringe.pop(0) + fringe_states.remove((tuple(node.state.position), node.state.direction)) + + # Check goal + if node.state.position == goal: + actions = [node.action] + parent = node.parent + + while parent is not None: + if parent.action is not None: + actions.append(parent.action) + parent = parent.parent + + actions.reverse() + return actions + + explored.append(node) + explored_states.add((tuple(node.state.position), node.state.direction)) + + # Get all possible states + for state in get_states(node.state, game_map): + sub_state = (tuple(state[1].position), state[1].direction) + if sub_state not in fringe_states and sub_state not in explored_states: + new_node = Node(state=state[1], + parent=node, + action=state[0]) + fringe.append(new_node) + fringe_states.add((tuple(new_node.state.position), new_node.state.direction)) diff --git a/survival/systems/input_system.py b/survival/systems/input_system.py index 09282fb..d4b6c23 100644 --- a/survival/systems/input_system.py +++ b/survival/systems/input_system.py @@ -19,9 +19,8 @@ class InputSystem(esper.Processor): if mouse[0] == 1: pos = pygame.mouse.get_pos() pos = (pos[0] - self.camera.camera.left, pos[1] - self.camera.camera.top) - if self.world.has_component(ent, PathfindingComponent): - self.world.remove_component(ent, PathfindingComponent) - self.world.add_component(ent, PathfindingComponent(pos)) + if not self.world.has_component(ent, PathfindingComponent): + self.world.add_component(ent, PathfindingComponent(pos)) if self.world.has_component(ent, MovingComponent): continue diff --git a/survival/systems/pathfinding_movement_system.py b/survival/systems/pathfinding_movement_system.py index a40d5bc..551abd4 100644 --- a/survival/systems/pathfinding_movement_system.py +++ b/survival/systems/pathfinding_movement_system.py @@ -4,6 +4,7 @@ from survival.components.movement_component import MovementComponent from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent from survival.enums import Direction +from survival.graph_search import graph_search, Action from survival.pathfinding import breadth_first_search from survival.systems.input_system import PathfindingComponent @@ -17,26 +18,45 @@ class PathfindingMovementSystem(esper.Processor): for ent, (pos, pathfinding, movement) in self.world.get_components(PositionComponent, PathfindingComponent, MovementComponent): if pathfinding.path is None: - pathfinding.path = breadth_first_search(self.game_map, pos.grid_position, pathfinding.target_grid_pos) + pathfinding.path = graph_search(self.game_map, pos, pathfinding.target_grid_pos) - if len(pathfinding.path) < 1 and pathfinding.current_target is None: + if len(pathfinding.path) < 1: self.world.remove_component(ent, PathfindingComponent) continue - if self.world.has_component(ent, MovingComponent): + if self.world.has_component(ent, MovingComponent) or self.world.has_component(ent, DirectionChangeComponent): continue - if pathfinding.current_target is None: - target = pathfinding.path.pop(0) + action = pathfinding.path.pop(0) + + if action == Action.ROTATE_LEFT: + self.world.add_component(ent, DirectionChangeComponent(Direction.rotate_left(pos.direction))) + elif action == Action.ROTATE_RIGHT: + self.world.add_component(ent, DirectionChangeComponent(Direction.rotate_right(pos.direction))) else: - target = pathfinding.current_target + self.world.add_component(ent, MovingComponent()) - vector = (target[0] - pos.grid_position[0], target[1] - pos.grid_position[1]) - direction = Direction.from_vector(vector) - if direction != pos.direction: - pathfinding.current_target = target - self.world.add_component(ent, DirectionChangeComponent(direction)) - continue - - pathfinding.current_target = None - self.world.add_component(ent, MovingComponent()) + # if pathfinding.path is None: + # pathfinding.path = breadth_first_search(self.game_map, pos.grid_position, pathfinding.target_grid_pos) + # + # if len(pathfinding.path) < 1 and pathfinding.current_target is None: + # self.world.remove_component(ent, PathfindingComponent) + # continue + # + # if self.world.has_component(ent, MovingComponent): + # continue + # + # if pathfinding.current_target is None: + # target = pathfinding.path.pop(0) + # else: + # target = pathfinding.current_target + # + # vector = (target[0] - pos.grid_position[0], target[1] - pos.grid_position[1]) + # direction = Direction.from_vector(vector) + # if direction != pos.direction: + # pathfinding.current_target = target + # self.world.add_component(ent, DirectionChangeComponent(direction)) + # continue + # + # pathfinding.current_target = None + # self.world.add_component(ent, MovingComponent()) From 99934067dec3b1af97c237e7bd7e75ea0a80ff1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sat, 8 May 2021 22:15:20 +0200 Subject: [PATCH 12/13] Add building generator --- survival/building_generator.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 survival/building_generator.py diff --git a/survival/building_generator.py b/survival/building_generator.py new file mode 100644 index 0000000..bdfeacf --- /dev/null +++ b/survival/building_generator.py @@ -0,0 +1,18 @@ +from survival.components.collision_component import CollisionComponent +from survival.components.inventory_component import InventoryComponent +from survival.components.position_component import PositionComponent +from survival.components.sprite_component import SpriteComponent + + +class BuildingGenerator: + def create_home(self, world, game_map): + home = world.create_entity() + pos = PositionComponent([32, 32], [32, 32]) + world.add_component(home, pos) + world.add_component(home, InventoryComponent()) + + game_map.add_entity(home, pos) + sprite = SpriteComponent('stone.png') + sprite.set_scale(2) + world.add_component(home, sprite) + world.add_component(home, CollisionComponent()) From 4ceaf4904bc9e8a067e3686fd46e990c5d5573e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klupie=C4=87?= Date: Sat, 8 May 2021 22:15:26 +0200 Subject: [PATCH 13/13] Add inventory component --- survival/components/inventory_component.py | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 survival/components/inventory_component.py diff --git a/survival/components/inventory_component.py b/survival/components/inventory_component.py new file mode 100644 index 0000000..18d39f9 --- /dev/null +++ b/survival/components/inventory_component.py @@ -0,0 +1,24 @@ +class InventoryComponent: + def __init__(self, maxitems): + self.maxitems = maxitems + self.items = {} + + def addItem(self, item, count): + if item not in self.items: + self.items[item] = count + else: + self.items[item] = self.items[item] + count + if self.items[item] > self.maxitems: + self.items[item] = self.maxitems + + def removeItem(self, item, count): + if self.items: + self.items[item] = self.items[item] - count + if self.items[item] < 0: + self.items[item] = 0 + + def hasItem(self, item): + if self.items[item] != 0: + return True + else: + return False