From 1c6962e75cca6603636287a16e60304c01497fd2 Mon Sep 17 00:00:00 2001 From: GridRL <165774503+GridRL@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:42:59 +0200 Subject: [PATCH 1/3] Adding characters sprites image --- assets/sprites_transparent.png | Bin 0 -> 19568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/sprites_transparent.png diff --git a/assets/sprites_transparent.png b/assets/sprites_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..88d674d122d7341a003f2af334cff9c327c04acb GIT binary patch literal 19568 zcmXV%by!pH!^Ss|?q+ld5~D^VqohN+1%`|UQ3R<8qd~eux)G2LX=w&ZgP?Rsw{*Pw zUcdLB?OfNcot-`Boaedk&wZarotG*^_;mOH0DwqMRZ$Q9E(ZWSiO0o8Z)wu1t)XAV z)fD9oyt58IK7aF6W!5u(h5Q$37;n71&4(V2ArLN$!;nj1bXYV59y{SsCKQ|6osCci z)g$FKVwQSHj>U`2+kH;|s7TlG=vQKjw3<-;9bji?Uz1^5+uiI95Awb|Ss&RS(L*(t zmhxWQr(TOkw=W-kTUk8h;OU#anlp9hF5bWNtM_b1bv2Ra(&^QNhllr{lFql1#S*bt z9e!xDd3~?_IFJ)`2`^|klJQ4KG7xXRmC(fhG_)b$NXn*vns8n(wDMLW`S#Y2N7etV z=J&y;8KSD5kAA+bzFq;3dg>W(tIo;do0pG9&d$y@#6bjKEoBfAGJ&_Zx9j>fERKgq zM{x=wvX~nLCX(QcXy;Aalr~CQOpi^^ft&g{Io);JDZ$vS+6=jX@EzzMT+>!q2@WhQ zI!>Fo>#{`H0vjARczbc~QS2A=Ohu3$v#5fBIDFlY!c%12}y zDl9l%@_Fmz*w~nD^Y0&YZz0%l{4Z0cHr@VRrI-l_`xsy=+f}v*SH%-CWYG)3rPq7N zd;frawWYO?z>YCd;hs0m8Zq?6@j`O=0zt<|4utU@zPl53>fDD@vZ7xbAWtnK3|~F` z^pM|ky#Fiv$o+UT*VTjavykka2~es2=|ZZQ`0$({>ha&+9FHUy@02r~b13y1f5v(- z$Tzn6HeGeFK$%W)=w zy?q$WTdQYjtcRfu8g>Y-q^ZF&R8~%IZU)eQCAML8PMCP&e0H&6k*3F~Va-|-aO1VO zI~0dR1o}hFo;ijdd-g91!K!v^jw_2@D|lX^)Mu>0D#{#hbVD^*>ybtrxw*OMbN2ow zJNdArt@)yK?}TDKwDlj*GX%aIDuk))IUE70tEUhd9Cf=wX8o9Ydd}( zO=c!^+FFCdO``GlK5WP1VUP)FmOOLMR;Ag>;{0UF7RyP*Pp`}_%4hV3=zs*H)^MoK z`xO7by^!bQHk7i{($EVh1|IuPb3JG>Qp4OFbXA8s&5S=GO_R zz569pF;NZ2qrbo-IdeQmTHQVI ziIfte+l7!6Qr~Y|CAmNh>&G$ae8C-03pGK=Er=>Jl{7i&yXrW@m)aPxxJ^b+Kew?Z>3!G5s>G|)TG*El=y0W@nUJWQ=3mS)N?_KcK1I1WS zG@4d{UDKxfr;XTJ8TbKy53WXe3;4Lw&Ch%4)mXwZ`XCv8DcvjtYF7BoB)&Ck^uI8R zssWjU4N78|B3ibZhI)eB45zg#IfQZA!9^kk90$uXGDxb{5 z2)|sWdJV-@$Ipu0Y93#V%|Au#YI<>qS=lO_Frgu_^sioFHS*V#;t=|Vm^q&r zQ8BH$Kx*c=`FC~Otzr7ocP9!7MUB`y_u1D5f^Fj5s5nuwNnQrnsgEvGl(7$GPi+5M z6Xa+M_C%zxpdencYxdWEctaviTDj;+j>2TKYR3dVY6%?6<%DnLm&cI6QjR?$s7+2q zw*7&xqSn1yIGJm$0Q8DS=Ce{tPo`x}J}O*Z_4f9@dq};0rDEbub%vkelbN2In(f(T zBW1zdR|FIcv>Y*aJQlAYD>`MeI#p}{7tn?eiyZ)|!5^8f7d{m(E;`raFw9tUOZ$0w ztY+w9F56cCehF79Z%2}hZ(7?mWG#%+bwc44?LF)|(Z= znydhUBa>x-s_}fjs++Fx-qn9{&n%3l!2A|!JhBkj_g?brNN|%>@w*`KDtaJ?>E!o; z^Inp4&l`w?B+NoQUUP@=>u}rZmAL>RR9u@4)*dBFT+Z%hbbR>np{m*-U!;OSz1bzM4 zRo&>L>?=w4sTM8j_8qfQ4trT)!wA;ImZz@!LY9r0cimd!JN5cQtBtd-(6~u8(zg4= zGT5+nkPoEMGPk4miyjWy-n6%kReetXq|p1eQl^ti25Qil+@ueOu%40SZkqO91~p*4 zDdJ)`S&j*bB|JN=Togd+{vFEwh3WAMYL3e|wzBg}a5c0tr7mmM(eYaeRF2&K&l-DP zsZN#W#kq8cth^O@zEvRo)9p`n$`YYsWte<^XZ1ZFpS&0&F~LZ%AHMMGgR66MhD%sp*SCKxww4UUP)0@GWk&TD*= zzc^qlgF(LYvUSbu-0Y_uco`P=4mu9)YilOJb;I%NSW9=C>?id?aso!yhXwu$&s_|P-rND%) zLfuwldPTqPAwZ-70=Yy2a}_@m)ASBbV~Q>yB6Z1p@!m&-pf>EoB>{{ka(c-jAa@2i zdt}7KSItbkmGAfJ)w&5;g-%bx5h!-Q zHIbFYrbV6x$W##tTu9s-F*C+71slgd4)6F_@GY5QxDI9MD;S0|4rsbRsbtvWCumjV zcE%p=&P!Kq)>sNgQq#aHYK<5e`S@YA$i|K_--yEtA42=a9)l`I#wymD2>n;J`R@)m zNIfOU*@i@V%GU|lH2KX(nDUVNpN<%x9t;@ z00j^cfC2#2Lp;aZL+3-4{=tzGjK!hTl{IDw?>^4}=%*1_>gB&PzKM({Q}y4!M%4Bz zjh{W4Pa0gVOv$@^!oCa>A+|loIr4=2w0$o9_btZ^iE|_xc~$UpZE2G({LAKcN~_K? z%kFsD*j~iRx+d&Z9b1O!EwNIVr}WoGtL}A&?K(I^g$E-`i6DXsLdKFfD-}^Z@O>D2 zgD`%Mfw`MnQ5PZCge=uTK;AaW6POZ>ouc4plNR4TWG8G+$k*URFSAidHdf1}UTz)H zr2hk0Y^MfYo@&R8Tm#eYF^GNo9`4N^$}b68=sAhQrxe3;*|asp76n#5dhOn0Kz(-I z%@;NlX;PMf=Y8p>V0K1%4M;3ad*p;A zlTK2M;6|tX#^o=}_G0I1C~{cv0EnLKr7w_OKZVVEnBpA_pw_@j*e`HrZ zAqmIK9;m$Pomqy=wS9$U zy50HVFC6_Qqie8P=O#I*YjNOjK07z!n7lH=(HMAr0T|;cBGIqkFu4E>`*kg+F?S)} z^=`^8FLnri?o-(AEKwn~AkVzJ$QkvLxXoQ2T^Z=pCGs$Sd2$+9I_YKP* z6YDTlnsFx)Ud@;adfo&g-!e7r=AQ*Gl9lXDu*rU?cq8~!T7~NI8lI?n8-L8W03c{i z-cA;;V-`JYJf~}T85!LGF~INgc`z8SPUe4cpl3q&lO8?O%OTCOsdwiw*{1t&(BGXt z{&SCEByhYB1fAbpn!$#PPJqW>{i<7ibH_eCUJ(DU@8elsCc3cg1 zJwitwFLV9Bpq}#vAJTLSi^r<$a2(>)K$`KpTVx+kNA7MfkF4rqbl7dEne&!q%kMV! z^I*ut(M>Dv&8vAQ+KmUaz1VLiv>K5^8x719q2B^51UTjGoo}_icGS^*M@j4Bt@?XE zcHUjwnP0E2hMT@njOjk5k?ev#8p*T=$R^3tC~TXd9DEbMb-p%+BtJ4UF`>dQkF-F{ zOI-qwm&=c-0sfEoMHuz%s>V&eV{T(z8Fd=dpYCMVCyt+uSi{rdea&3&IUPqpOLQhn(31W#qIy9kl*jprB<<>S?5m({su z-XIkbBJsaJMx~*T$|Yij%8$^DXBUp%=;O;K>d{!@?=R~2@vtCfZ9$bBwW2k3#_Djw z7%Jtjw#hYcMd24Q{P8Km)8Y|SFS4_WYw-u-XRz9rh2jjEN{vwsY>i*u#zY(XhA)4- zyE=V*^XAi?bByrNkjdsX(B;dI1vV#Jm)$!++;5&#qO>ITe?*!~gJosy{J-NPT&`?E_VdM&(Y2f@x0 z7lT9$CF3#$6_y~F5<$`Y!O)*&OX*rS0nydR5q2%21E3s&W>mg#lV4#;7s zx(2!Goc%jm&e@`Y)}Yvpo$HO*TP}(4*<=G|tH-bf1;iqTPyFLPL{A@&ULlQ|8(`BO zCCsYRZ?~gHpSX_B0(}Q@e2s-;R*Q$lN|qGnZU9eZn+#vtOac;ze!s1OOLxiTz_d59 z*x0KGQ(h0=+P8U<cNDfYt90I1le1r$loK`^4e$`Lb)4Xd-l5k6 z)4J%AdD`q$k-saYny=YEysM_#TCygXSXu>x{XYPWK6f=<`lMVEY~PP`kV=j!TA@~M zu^_zSQ3CJ9w`Soz{{k-nT1r=YoU|51W|tkR=%6-`LSNk>)e`kmJ{YfS13{O zl!S&mnwux$qazE=?HOlJpkpHLe0%N-Oyh;4aJSDR1%n1TtC`I|l)}O0HgZkCIt?@D z2c~}EF%r_Y?50cGpCqJ$ohhk?L89=Aj_>typN-x2Fjkg9EV2X^K6rDEjrh*Li)L^u zehVOXoW1!V)aZG-qMFe7G5cdK`^SO>rtT**eK^~-?7~+{_%YAIH=X6*(?}YmP_a3M zxGZVD)Y_~it{_a1*SJs_=EwXA@J+<^}hk+bZn%PoSE zxu(HKvu%ZJ;`vu`Z&tAJwI&U0AkK_{h&RIc?ZMEvk2T2YLfHFXytIZc5zB3W@J)Lo z<*XSiugmQ7$|>{o0}HW%yfPW|BLDP*znmQ-%$Dwi@6Fi7bs1sr8YYB)C%U3-j@N+q z?-f35&;00k6=sD<-?UGruT}jw09w(_!4=j^uG-kR)hx0nup0UP_8TF|NT9V>zS8#Z zsXar{S&C>E_oEMOG#Vej3OVj*(N)Pwa$jDVWq;O)0oJFmycD?dN^ypPb1&+34F6|~vGK@Jdk2inr zylwGo`>g|WaIdm|;1bGSs~7EU?PF9)bZU~x#wvIbRi2Udcar4k6a#Y!C@zczt9%VB*bh2P*OU7y)|xCi2)^{}}V2}`vO8xhyQQN_eBBC9#o z#jIPJo5VG{o`jH1gAeC1)rT|sI~0^ql+RUkyeAC4(8!rse<<4Od(P00>z!#Rrp!V? z-<`$7*D9y{vc*bnoTA8Jq|I-4 zwj!$SNqNUA{h0Q8=&Y%kS6mHJmz{WiN9AD73cqT#V!>!C+VXSv$kZ1r!0}zqYqn;a z(lKg5P=mz;_j2ra+D%@ZeXSk!!oBZYV?j-N548?-96{>B_Cc@nJw*|GgX@?4{TYO< zR&p#T!dtBu1pA8uEb5e}b*)IFU6DWN$+HOntjBY}8i-n-VEV1KPt#q&<0P)#9AxV0 zdw^%uyT~AXr{P!Ua(hZoQFfgJG+>^pEuv_^jtW6FoeH~C&tT2Qa{Sple}Z=}e#`&a zhi_R!-f#dEPatoW z=XcK>M5K#Dj;+^f(J>NKbhaQ>w&1#C$t|wi|JJS4xzDD$;?)Ip7 z(M)^BO!g~C5c~3T!s3)Y%N7&!NXyP{cW3)Qi;aH_vzYU`#;-M*)5{` zaT-q9y$=tT&XeRxgA3*nIgud+zY|u~g2X7U4NseRz7N%eMjFKkMKwvg3^( zv6HBlL8zz5I#YG_RI!G=XH2GXx3nf7gsY~}n@1(MdebDqNslF{^9mRqRDY)O*zEECpEsfl3-C&r$dwLtqbWOzYT&0IMa-m(_=R`Ra>=#6K zwk;<>LeUc-pQEq8+v?X@rnfvYi!xUD@4`K|*ZB|)dyQKH1d@A%!Sqkrb>C)%CF0PY zpJLVBcZOCsAMS-sBi@tl71L-Kz?_^nquZ&0Q$Li0zH;+cafz;fL zA1x`mS^2LWdc!LSXO!PZaHwE^b#aK1Yj6AY%j^R;La2}0`i7PyJA5mHvsTl(7Et^v zU-0T<5&2%1@Zp=0?8B6A$)RhQ9{HkUytqlL`7@juyFhQAr_VVidF2R}lL`}{6XcRc zorkUX#LR_XY+92Pc5PlJIzac-RkFoOpY!w{_BCBG-{>cj5D+*~3fPvbX-I9-DmTVzBg?az?VK-xD|P z;3N8JWfF~wQ97f>hvil`)l2xLj-k*qHRaYd6Cbs3G5HOM%i0}sl#Dh(%IRNLrj4L_ zxo|z;r1?EAhf>B0)I~Vi?4f`J(ua5OltvIg%Jg=5y?)B#$CIuHoAwQbJ48) zzj(2AID#KTKz88y^noa*u6-J=P0wR_?HaTpWwNm&j|!p%cglI-k2oZH#tEkn?kp}X zy);;jexHQ(a(lT_Ieyv0*I6tN0~NG-`pb98-Uto#OQRp9WsJHZqjqz^QCd`s_|5%4 zI(cJ*2idRxh-5I=>+z$pg*TE5aK6V?UlN+Wle{4>nrFFs=c}C%I!n)7)iF9 zxvOk{>ibNPig7kY;kh$7(q}S_xcU5VB^b(Nq3`$11(0@S;_1C zluSx)?fP2MbFYz~;8Y(?-L}^Cyx;b5$)MU)#yG8+x9! zZJHmz<6a?Vk1D@cPdsg|l^0mvPnuOstHo1(pr6LxeBRWsPgF+^V(K!cw}q7159!-i z^*GBsC(-F*vgG&`yd6U|d;NJ=@at-Z7&@O&2^m(ZOMM%Y5u0E(cBtpTNLerag?I#6 z{P*=i`9EydY|F=~E?XDi%_Eayosm~g-ZT41W7zgPU-OU(Vh!XABia(%yIJK!?(jh` zBDSi~U*wOkNnm(yuYGv@klp4V=C+vmR_&qPUl3J(U$92S(_KR}H#2y&J9>?tI=BMC z@X1(%4yft5KakLVhAgkN#R|awa=sD7R0B3)jv3}fmR8q}P26K7TuQu`Vn8`a>c(a( z4$EB<;HFDu%?ps07Y@zUD0n(s;_=#LU};QFZ9+ZM9YZ=0MALsw`<+wQxOf|_@;H~Q z?Cbjcfuj|((61wjM&VWH>8DSoEFmQjo6MDIq!YVR+>B|9{;3l=wNyQ`oZv3ksIIrS zciu$|y%<25FJ>_zTg6Gfv^JSqQaQNe4G30!(%w60DrfwZ{Eee`6@r)XHcJVOTPe64 zS?T&nrL;;`|5P-A{p3Dyzv>;knCEeEV^jX#lS63t)ON7*zaAt`w)6%gE%^nt*E7Q)Nk@JF9K$9^=M|fIH+3 zmjKVmC&brlZdr#75yO&seXzU@=x z_ZW>5o|vz1m<~@~tK{Y&=#IYInT5SFTHpmO+z zgXHO>CTKDeD;1ho96nuQ&F~{?4d6^SMidVlLbIS#vQ$2{wU2%`XQW}#48RUI9bES? zlbGPdoz>#x40TsSmWn|Y2)GTy0^cSi97jN*Xdr+&qnYn;yZD$VyRho0UfJ#sS7EsgwJRBOF9_+bSn5{mdhBg$ zh&`?Pm`G#DDw=WNCGrFv^${YR!m$t=o^?o0zvuzb$l7@ym=jIyC)ZcesF7SS01)Bw zMf1xI^L2SG4l@_v&>3N^h)Jucev~;|z)QN(mCfecx z9&fQ}pt5qzd6PYZNB=QRL}#0@?a-0v;bs5Jv^iF%dF7#vuHOgv&LLx7UIH050XG2tMspPx=f6w%nGLo1GrLcluB7~`x*3Um?E)n8y)HhcaCJIM zJD**}E2jO6zvmcnKhS;dRzp) z!>ba}ROK3ZB&A}G)6E1t(OfZzKk}Zs!wz~a?0o*EXLBsD|7F4n><2)~&F#i{nAsMs&!uF#d-P6#qA6~n zspZ-|kW%?DvLy>_(+72?n@<9YTT-$iUQ!PeOx^oenpMfmRhU$b#u-!_+jPO-Ufps$ zjVWs8*^(D%uOZ%2;A$I>^goNHZlq>vVu(ZP9x>SsZo|*f;uBL>zzROy$0e+|*=^h6 zx6Hy+ohz!U=nzR#=2;1|DLJIC9PZu)R^3HsEnNXPZR%Y4Cp-QfNhK|y>?Muh#?+vP zcck!OqL(U=mhFj*?@oQ0I+D<&hF@6Yc{VB6SNMpJ2+ix+;{49rbH~Y)P8k_J;bacX zYdQ&PTbyp{hcDCC+R7s==?TK;o!>1V%20j`qrL4`d2?OMy|T8+zj)R$D-)i-XY%0- z(9g-|xEl17tCEV^Lo#(DjC;orX{*aM9ECUyU)Q`E5fd0yL&Q;I4HMK48wDI-7RY>!u z8Ei)q;j3B&mHR7;pzeNxAuZ1~)zM%|lq=WQj5Z&APGT~^GxgkO5kg{g-XwXp6w%RN za)J#uYTKbn?OSFGHclL62acm`$#{Bot=8uc&Tv1i>a5RtB~uZxhuxnU&)sH44Afsu zXc~X*z|_UGp|5GDGhmD*q|=>qMQ0&;r}Rzn%u3;S4$(T*_9fwQM*(}h-QHlS%^$hU zD@)=I@}u@Srp~-A{F{vm8tZ%O>m%i-sI`*hn`hTv-lswNK=O&H`@^ZyibheUY%DWZ zcfD_BouVbvsNs^iYu(RzqH%kqm(_7`0{f13SJchL3{^W=y)WRtS5@x5r=o*^y{|$9 z^I8;3O0Gr^9mjA+E1(6?RKun(LPDi*kqJ7U$+2W$NbpCpXLPNeDUS-Tp~QrbJM^b? z<9c@CIKf-&iKdX$C6;SJlG>ln)a-q)oi^%U_L}KmQda8_N^re?rPm~0GoWB9RZXAG zMc_1Uuk#*w{A6RrNRk8hu0ME?Hb$iJ&FkZ}_QGO1^tbp$H|HUgQ(7 zYQCcQ1v>z}#(I&*6z5nUMmb_b@4@sq2Z;HgI;)Ah{yWGR3(R( zC(&CcLc=iSV{JyZsY*g0Q1hwZ*_#9#o);3Qdq-(HVaJg|lF(1do_W9FT}VTp`|RS; zbFLTBBPddCy?KF); z$bS<)mW3J_e4hWvjY^YDa-)}Xs6J7d{dFEz`f7}Q4>ixXOoMccvS7A1w<8yBTL0IZ-h}QU8t(T>kkp?WyJOkTx018Gd*TaUASz|gPJtpLK{n~ z2?*cj1lWZH2r9FuyNk1-U5VHN-Yw&gslEWqZZ-qAGy|I*xv6w8?5z^Zz^5#b!=UU z66#}r*>^D?023A>q7tK}c)X%n(s{tB_@5O@KkR%QGu||`Ts$d+c1L|v@h_31{>6q3 zZJ*Wqqb{(Fj@B6Me&VB2Q#Dgkdg{MxYU=6!dd}NosQcv1$S)wRpl8!mVebwwI=;pZ z$;=+be%5C7Ut?%3{``ZJ_Deh7DKA5nWEI4Z;q)QV%cr+kzD-@1ry3cpt6OGtm#XE}vuvlPD z-mIt}$?_T;^O@7NcskeQkaRX8>durTqN!g=e}49=9=mM*_h60aLybP%Y5CF;lwi*@ zk_}RXELa|`Y=8fBWTDR4h~t34KG#xOh2ay^QUnFa9{^Q{uJ#L;ohAieRztgcz2)|Ji(&o06w!eUvZ5X*IZ@bc&ckAFr%Fl&R^%4ok%jJKvP5bP;+V9PodBch z(Hy0uI9k4_7#Xy!*qU>f;UfO3bIx!vh~o@ix5JCnzNc&()bMG<_QU>>`g==|a=sMH`h}}L!m1zb`hw*D8j-vQWHs-*Xjeoej%oTUIyY$KG4)QHlL-c)+ z3uJWE=L1&MmtMM?x|RlLH-wwdm1O+lqsx_9Go4@jwzkQSPWiCKH{y=d3n`qfSD@`2 zRz#))tmcBycGYte&3(TmzNO~YaD%0UEDFX=gOzb{r0vKq8u%D#LPAfj=$0lv^GPu5 zzgL^OK<-o6?Kt^{d#qN*vFa0}bx)7s0%qN*`IJNYUp)WLw$FqTsVZ{{B+TC$Ax>8w z)G!Qie&MxcYjm{PLnTm|R{B@s+3G4%)sA0*JUa(q)p4-58_a)HZ-OA zsXt_f0rvPf3cw?h);df+K6N&nu z@wY-dK+CxlyoOmAfrLhq$U5fnu2U+u5RVL?*U7Wb=Lv5|8#%mF-kEUdkmeXa0--oi zohz^W^Ytas?1PbdyA?|mx+Y>CK|&h+A@=Je6KVe~JU)__2kp3uM;Tdfj=DY~kRSYO z!iHQ-BN=$BZn4bReoWnB)#OQ?EgT4RAheD61bRL%3&0jIeeq#2eN`#dKf06$c3&n- zi!Kwzm;Hc6-C6G;^}~K&D*SD1Jvd8@Gmc%rKHtdL6{IMTjWY_*uzDThh8%vsxPDW= zxiJKvP2$8)8{0@AO3M{N7GLLwIk2vGekBT`tBp97(@qm8_J5T1h0h>SJ5 z+?>a(vk&~YA&}DF4qASNZ*F%&DELPdKY%eifGEHdUHdvKhr;kac80kPoAl%ee#UC| z{Ah>2|Nqdj_lPFKBUCl%9Ikjr{nc-RoFFGUwa3&%!`cECs`{4D7eLbLcpz-~L zyI6sg%k)%8zqLmO?nFgbs2d7fxyTr{bzdoAlWElamU#VBpOXu8RJ(f4tC>LxeHw^i68 zi|gL){@6?V@u9{UGcAMWv&S6~vusuVKxd*CZs|ioRgLCX7@Xn%erm!(=PK;?kknRC z;vneV``3nKY?Zoi`Nxzi%$)&q;=i2dm^W!^CeK4g%puQ=tVN#Hdj0tv=6jWsg%0p6 zp31;95KdHfPPRsA%XGPiG&RZDm5(1a?lRo_9-hjuJXbJ(xMq6zCN~JsUVKI8 zlFOFLDwQ;y6xkwQSE}nc^2xKz5j!W2#PDcISp{UkIMCPfX|kj?;khI@8a4JMZeW9| z2F#POBt%|#cD$iQC-*AH{jsc%VS(HI^>Rr~{BSv>RV0#sCf2+BcmK_JZ4^7a=<+|n8x+#)hF zI`)z54vdA}U?6$vmv(*+ZSA2U(@lk;R;%u$O`p{N8Y^=0H#-DyMt3c1bACF{0O&~0>nK>E5&&tNTEHBEAt{w_o=Wy+9{eo`e|e?2r1@T zPZkisuMSf)-WSgCr?0y8KxJN&Ho((~j*VagI5kJwjn-?)cZMLvpWjQd$qanYmrHr_ zisza)s%^hf=j&Y&yI=fHlwcrUl|L+mR~_~!jZjYnMT32jL7Pl-K^`0;vJ@&UC0t~5 zT#ib1CS5UW1RlY%va-g%sW>_=J{Y^EcL&wsMmh8DG%i}0GY(B+RCbGFuW*I7I7(1p|B><#J| zAPCO)qPXEVdwu)-JqDdo*(WN#9KKg^;muU3nqC|xXTQ1p8#ssgLkk@QZRh8VD*% zOO`+Z35_^#g3TY~X4NqFdIBNBHe1JMP`T93vR@9s307V*h+|DRxm&VDgqlm@1Ai5< zy@G{C?aZoC=g{$0$F5+K&=i)3hUq1Nx5c==OabcX^0tRqC*Ia|R(u#N=Z?g9Qz-kT zca)Q*TlKBmRHD5NY%7+ns)z+gYvn5q?DlND{L$4iIA_^id|mH}hOy51DY=GazGniq zt?~4}{&K2sR6lPvqq5GCWJ`HVi%X?H1MEnK6eGZnB(#hPJsaQXIV+=`*D&j_P$j)M zWFSQ+1wU$Zsjg|sECqILz1nMH+pFa!8uZP&AQ>VlD@U{RV#D&1g~;)M23@58wUc`2 z4;-UL)~sP3H%IIlM#OCEY6fnZan^Vs)xTo02lI*NTck97=jK2CvOGu$X?Qq_nv=o1SLh=%^rBh zNaXzYyb}_1Kkm6yF$O&Cnq%qNFkF`UP4R7FU|-uOd9WA;0~=|i@;o#l^}&$Y)B3*WuM+YMAOX24RFYQSMh zlOGw9);i&A9%bl>qC@;cXXmMm8+FD~_xM>9zar3rVZf$(98TTM+y#YN)E-r8`dyRN z)yu_<3d+dQb7^lb+e1?8RK~B6!;brHizi3u!jKcCn0A;p+P02yg1#Dud9^WqjOe{2 zh+Z)-9fuzP!O{beSW9z7UY=1N)mhSv&1R7xV;>FdY%C8EWA19+<^-wMlxj-7he7;l zRMb!E%pDBh+92mEU3w7cyzQH!jM3Y{mKz3hHjUS6#r)}YYu z%c&S66`P{*&tj{EUrz$o)?ZvoOaVYOG%J#ipd;e59gVRHC~73JxVDTR=J?u@eMt2U zma6F&56UfyCGqrHJZ~tF0ET654}Lz0Yt&vhGyKSe&EE6QOL$71f*(>&V8|@bXNh-ZNg}e! z>^8*wirru*(>OVYvs7h{&b%A9b1ub?#l4m+wi>C)QOEFecglIySrXmSB9*Kc>ZTK{ zQ%-n)3QhTQ<9~|30{f0lqWx^_hKQ}Rd8D$RDStEnk4=Mwxti?(KvdV1EZ6Z3^a82Z z62=YP5!E>r;83QK8}e1R4jul|L!HhOi`9H;2Sdf*#XN-0`vbQ^zdM}ten&q~RExv% z_zO3{)78Mi$>qrvAVb=Tl-rhRWoRFmFd?kK8S@uEiV)Ixnz@d7ar^Q5WF*Z-zH%qr zP!2!3%jX0L)(-p6D9`e;)Y+W?6NNgDz%oclM$z1uCWq|W*C8edWJ)PkHqLBl`RZ3S zLSE8;v48*mEf^#hdCfqK(;BRiH<+j!rfWeXb}>%zlr1o4q9L`KAh;TUs=!MaM&+5? zvB58%G#vMHg7C{i>?z7(sbI0kO@C0G_re8DI7)U5mb#iMgT%kO?yn#y5Q z?Dhpjy9#LqIf5P<)DGh#gF&%!Xp4mADuO&m(F}CVI_hmQmPyx>KK2`<14zr|oD`p} zu*{`)^NnHJ@j{^OR%9+g&#tG(KPN=>FaxL+SFB1H zzFLJmC4JyL##8N*oI*(=-ZAe{&bOX^e$R!%>@Em~V85ntXS06#Vhh`u)nMy?2kRs@ z=CsRRbr}MazqfKfyfIYlOuKX9sh-*fX~MrPz~Z#xl)|^unXa=U$tR-N8nEXopVZ&Y zH%Yp+nKpdXx}+S|5r0BIQG0w-zUt>PPY&uzzW#<4EgM5Y7(39|S`f=WMdTn_Qo8K? znf3V(o}gpXrD)l6i4HPWNJ8`yB*e&CZTj_A&pTGspQH&VyOCtA!W)#9_OEjE`QfeR zR%Lw=O)tk%^*vwB~*wqtz7K<+o%<7fS%y zURCc!5{35f0Mk9cx>PGF&^P(e0){%s!(Wd3ekxD7LMv~UTIW+ear`CMWv_ErIeozm0JwvQthG%vHqr4)`%mzwXmzap5B~mn z;pvL~>C=f!-e7*Sbd6O%>~uGsM?SB@Jhz0O7z;C0-|D10^HSrOxDcN}%Pb!rb!bS@ zF`u7x8pDuRhS&IQ_DM$>xf?|Y6THL2vH>4mqHZ)2Lo^Qfn4&69gS5d5;27wr#^A;$ zt>8|Kg~CFWX$}_rcuMel;uUnC^SlTROftS*(lRkI#S*c#5`M&bN&$*KpBn1KfNUr0 zaRz7h+dF|f!xjQZu;qFC23jo(Dbumw3*;mu1Y!KQ_ff`pYH{)tr;A^c&)-S-f;0O` zzgw);-SsO+_$JfyjKsflN`)AgjlXM^3Lc6Ol)PS?%%ar#LnX;cs`nXxo7c22y!{hj2r;L%H>r7t0xP8&&5qAAUMq5I@u8gj&`MWEHHSr0e&-v%jZSRPzLTV4D*DV?SxYp8u0fGp? z|21*#@l3COd?U9UxzsQfO0}Zd$+cvXgHtK0Q;cCFBcWL_)t0R~PDO4hl$b8k$rgrj za*IWCHyqQ%Vi`eE;X27*w=?{6B<>4hEPq-Eq7#|4lP_yP5ui6QvT!bfMXw$a>g zQ5ZFJ7!>L}@9h}AH9!xPwV?<3W?3PxXF$qhFWEaAc>BE*t2V^|XLVm8 zPqs*s-ZV@&x#L5bN{=piU9D}0&tETXP|d=qfUV&=-Xtd3??@w*t61u5U_@Z(KELg}#cKAg?eQZ@vsy}S(7N?S5~s$Hxu~3qqz-#fTk){YF|{}RE`lBWUH2Gt zub)CHp`F7VoRsBNee(xy^LEdCo58f6_^s9Qg(cJUxs(p#blBm->gO*6xoBly zzAk|oNXF|r*7z%*bD#$@QlEv#$oDz@4#}C|Wh&o#@T|gck1D!DsH%A;HqwN}QzWp7 zdvJlt&S`y#kib7o>@?0=ZaCcguq-wod3XSeA7h9c;`Cpp$@lqQWNOu$qv9hv-`|)- zfdbhf{a=a$GCgNej`LSnmeAmyN(NqX* zH`GN4!f$)HVAWEURO}Ksc)!?D!SemRmu3iZVKGbys>ah83k`2a7IX@Dr`QT7Ba@h9HcFwP}K4V)k zH`cf~!F5^GPTGPDqr7psk{n!QZ~X`tt`7K+QxJ(vA_{dq z`=k0@ukubyW*n~n5blaZCZd&lepW0{3tqQm)V&zRTLj`3Y{ELJf6B9mv ze9Y|>Pf_0R$gj9AoXEG%@m9QYz6}1=$xW%Dp)s}vIP)0J5_GlVb`vC9Mf_D!(@KPjI#EF;lD zt~bAa)ta$Yd%ED{qorS>b-g3kmO5~Lz-Va-RSecwl#g_G6aJ8_ah2KomgSPMmcQ8m zok=*hJeLO$$7HDpC;?JFAdV-n<)v=^X2`i}PU<}U>5EUmTw-lgQ@B@o z#@xvw>*+52*BXYv76XLoR_DFzcUYaSquyj_Vs}qQ@{jpD*hT zeX6i}_m_I)Mf#+idU-ZP0~_JnJSeNMt|5)=NLXzDc*>k@zX6ATxPGQg{D)KpD3`2Hc)# zD*#1p)w=3q5`OnkD9Gk!e8SPi(V%wFM;!_=mZ~f+yxK1)2fmK#uqitt-Lc;;N2l`a zis&cpYK)hYC*k{(!CM6$p@SAQ|Ds&l~7|n=*V?UF33_~N_(S-TH>0Vx6;JlJ%n`EBw~e! zU0&*i?PQ&GsK&W^>;y1UqsZE!Oyz4OmJq%NkW*6C+nf*9 zxw7u;iEXS#+C8wYx9%n`)Db<}eK^vsz0E&%zdDL^w*+-i@I=Xn-BsS`!BAxN=B(SIIv0Y+@2r)UkW|`kLjA6xP z!9~PaeIsmk3nFx{`n0Fy%;xv!CK*Qkqw7dzjd1^j-!e-9)VDq&Wd6OBCOy}6g8O&a>Wn~*qNVfE> zun{xnU^njT`^9yp_U-k(4n$jiBUGR4>`cuV{5B03Hzx7wi}M?Y)1_W(8{#|J!q%#| zO%**ZcwiyGX@gbL;9?kW8pnC&H?efSMQ(bF<9XBYf~17qgEHHm+LjM2@nxB?5b+A> zt*GX{PoGlavO>^JK|K3gSN#`(#p-_b9#A*_(IIcuhdP)t_V8?ZD@6>}nV1&m(JdSE zGmI|MXmZ2w0Ks;_fCK(UgkQg@V15Is(?-e=3##yuOs#k-%mO7_OqZNRV%rXPFv#WL zDu`$BLYdM^Sw^R&d9VdvVbw<`nAa^ajL4=iYBz0@)b2tH@af6Co}iVCf-oR|P@nxO z|H8Hc7i5cYm$Waw%Za;M-)~1DGNbks4sMiVwDy>whij#9OFbYaj+o$$ef`x_`s=Mb zN%UZJJDnqBh+(?h%u0Wnp4DY>@@?w)??&O`iHOVG`(12+K?YbnFxABRT39zK0 zHwTLlOo8EyT9=xB#Xf;HunLnqcjNfi;L1(#*#TwLb4ZL8r3+LxvpEo;qVJtFV>MI6 zle1Y%8tyh8AxF76%Wx6w7S=Uk&gSQfsfV(Zt5ZvNZHhLNNA5%=006|tCeB2-$rEhu zV8W->7UvL1P;Jrt1RqueiRBxnTwjkK$>I+7=WZK>V;%NO%^e-T2d|A9VVgP~7K`)$oN zBEKm-*_@&L1Frsk?>}fg(gpfYvrLKd8iTOqC*a5GoS23lHQGD`NM+m>&1vkY}-jl-|@U!N(dr zv7*K%ThO%8qnK-;?_CDY%sw4~y-Qc7%pnA?Qz z`9hNFkwARKXP!ZJl62`tKgYCU>oMEHymAgj>=v+e{8Vo?^V3+1Cu~mx6R0TjjB2s}&VxXfg?#^$Vus8n)%O*W^ literal 0 HcmV?d00001 From 77e2b8003a0746f36939362f48063aaed708e130 Mon Sep 17 00:00:00 2001 From: GridRL <165774503+GridRL@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:45:45 +0200 Subject: [PATCH 2/3] Rebasing code to support data stacking and sprites --- index.html | 8 ++ visualizer_live.js | 311 +++++++++++++++++++++++++++++++++------------ 2 files changed, 241 insertions(+), 78 deletions(-) diff --git a/index.html b/index.html index 8087ad8..e48bc53 100644 --- a/index.html +++ b/index.html @@ -97,12 +97,20 @@ document.addEventListener('DOMContentLoaded', function() { let form = document.getElementById("userForm"); let stats = document.getElementById("statsDisplay"); + let filterBar = document.getElementById("fname"); + if (!!filterBar) filterBar.value=""; form.style.display = "none"; stats.style.display = "none"; document.addEventListener('keydown', function(event) { if ((event.key === 'f' || event.key === 'F') && document.activeElement.id !== 'fname') { form.style.display = form.style.display === 'none' ? 'block' : 'none'; statsDisplay.style.display = statsDisplay.style.display === 'none' ? 'block' : 'none'; + }else if ((event.key === 'd' || event.key === 'D') && document.activeElement.id !== 'fname') { + toggleSpriteDirections(); + }else if ((event.key === 's' || event.key === 'S') && document.activeElement.id !== 'fname') { + toggleAgentsPathStacking(); + }else if ((event.key === 'w' || event.key === 'W') && document.activeElement.id !== 'fname') { + wipeAgents(); } }); }); diff --git a/visualizer_live.js b/visualizer_live.js index 07080e7..8cb472b 100644 --- a/visualizer_live.js +++ b/visualizer_live.js @@ -16,6 +16,9 @@ let socket = null; let lastFrameTime = Date.now(); let curStats = {envs: 0, viewers: 0}; +let allowSpriteDirections = false; +let allowAgentsPathStacking = true; + let backgroundSharp = null; let backgroundSmooth = null; @@ -34,7 +37,6 @@ container.pivot.x = container.width / 2; container.pivot.y = container.height / 2; container.scale.set(0.1, 0.1); - app.stage.addChild(container); // add the view that Pixi created for you to the DOM @@ -47,15 +49,64 @@ function smoothstep(min, max, value) { return x*x*(3 - 2*x); } +const createLock = () => { + let lockStatus = false; + const release = () => { + lockStatus = false; + } + const acquire = () => { + if (lockStatus == true) + return false; + lockStatus = true; + return true; + } + return { + lockStatus: lockStatus, + acquire: acquire, + release: release, + }; +} + let userFilter = new RegExp(""); -let activeSprites = []; -function setUserFilter(value) { - userFilter = new RegExp(value); - activeSprites.forEach(obj => { - container.removeChild(obj.subContainer); // Remove sprite from the scene - obj.subContainer.destroy({ children: true }); // Optional: frees up memory used by the sprite +let activeAgents = {}; +let agentsLock = createLock(); + +function updateAgents(time){ + Object.entries(activeAgents).forEach(([phash, agent]) => { + agent.updatePath(time); }); - activeSprites = [] +} + +function wipeAgents(time){ + agentsLock.acquire(); + if(time!==undefined){ + activeAgents = Object.keys(activeAgents).reduce(function (filtered, phash) { + if (!activeAgents[phash].waitingDelete && activeAgents[phash].getRelativeTime(time) < animationDuration) filtered[phash] = activeAgents[phash]; + else activeAgents[phash].setPendingDelete(); + return filtered; + }, {}); + }else{ + activeAgents = Object.keys(activeAgents).reduce(function (filtered, phash) { + activeAgents[phash].setPendingDelete(); + return filtered; + }, {}); + } + agentsLock.release(); +} + +function setUserFilter(value) { + userFilter = new RegExp(value); + wipeAgents(); +} + +function toggleSpriteDirections(){ + allowSpriteDirections = !allowSpriteDirections; + wipeAgents(); +} + +function toggleAgentsPathStacking(){ + allowAgentsPathStacking = !allowAgentsPathStacking; + wipeAgents(); } app.view.addEventListener('wheel', (e) => { @@ -70,7 +121,7 @@ app.view.addEventListener('wheel', (e) => { // Calculate the point to scale around const point = new PIXI.Point(x, y); const localPoint = container.toLocal(point); - + // Scale the container container.scale.x *= scaleFactor; container.scale.y *= scaleFactor; @@ -157,7 +208,7 @@ app.view.addEventListener('touchmove', (e) => { }, { passive: false }); app.view.addEventListener('touchend', () => { - lastTouchDistance = null; + lastTouchDistance = null; }); // panning @@ -186,7 +237,7 @@ app.view.addEventListener('touchend', () => { }); -let coordConversionFunc = (coords) => [0,0]; +let coordConversionFunc = (coords) => [0,0,4]; fetch('assets/map_data.json') .then(response => response.json()) @@ -199,10 +250,12 @@ fetch('assets/map_data.json') if (MAP_DATA[coords[2]] !== undefined) { const mapX = MAP_DATA[coords[2]].coordinates[0]; const mapY = MAP_DATA[coords[2]].coordinates[1];//-vec2(217.5,221.5) - return [coords[0] + mapX - 217.5, coords[1] + mapY - 221.5]; + const maxMapX = Math.trunc(MAP_DATA[coords[2]].tileSize[0],16); + const maxMapY = Math.trunc(MAP_DATA[coords[2]].tileSize[1],16); + return [Math.max(0,Math.min(maxMapX,coords[0])) + mapX - 217.5, Math.max(0,Math.min(maxMapY,coords[1])) + mapY - 221.5, coords[3] || 4]; } else { console.warn(`No map coordiate location for id: ${coords[2]}`); - return [0,0]; + return [0,0,0]; } }; }) @@ -217,14 +270,29 @@ function getSpriteByCoords(x, y, baseTex) { return new PIXI.Texture(baseTex, new PIXI.Rectangle(sx, sy, width, height)); } +function getDirectionalSpritesById(id, sprite_x_count, baseTex) { + const sx = 0; + const sy = 16 * id; + const width = 16 * (sprite_x_count || 1); + const height = 16; + + return new PIXI.Texture(baseTex, new PIXI.Rectangle(sx, sy, width, height)); +} + +function getAgentHash(user,stackID){ + return user + "@" + stackID; +} + // "kanto_big_done1.png", - // "characters_transparent.png", + // "sprites_transparent.png", + // "characters_transparent.png", // OLD SPRITES, REMOVED // "characters_front.png" PIXI.Assets.load([ "assets/kanto_big_done1.png", - "assets/characters_transparent.png", - "assets/characters_front.png" + "assets/sprites_transparent.png", +// "assets/characters_transparent.png", +// "assets/characters_front.png" ]).then(() => { let baseTextureSmooth = new PIXI.BaseTexture("assets/kanto_big_done1.png", { @@ -245,7 +313,7 @@ PIXI.Assets.load([ container.addChild(backgroundSmooth); container.addChild(backgroundSharp); - // Function to initialize WebSocket connection + // Function to initialize WebSocket connection function initializeWebSocket(url) { const ws = new WebSocket(url); ws.onmessage = function(event) { @@ -257,7 +325,7 @@ PIXI.Assets.load([ } else { const path = data["coords"]; const meta = data["metadata"]; - console.log(meta); + ///console.log(meta); if (Date.now() - lastFrameTime < 2 * animationDuration) { startAnimationForPath(path, meta); } @@ -279,79 +347,166 @@ PIXI.Assets.load([ // Refresh WebSocket connection every 2 minutes (120000 milliseconds) setInterval(refreshWS, 120000); - let baseTextureChar = new PIXI.BaseTexture("assets/characters_transparent.png", { + let baseTextureChar = new PIXI.BaseTexture("assets/sprites_transparent.png", { scaleMode: PIXI.SCALE_MODES.NEAREST, }); - const charOffset = 1; // 1 index here gets sprite direction index - let textureChar = getSpriteByCoords(charOffset, 0, baseTextureChar); - - - function startAnimationForPath(path, meta) { + let texturesChars = []; + let texturesCharsDirectional = []; + + for(let i = 0; i < 73; ++i){ + texturesCharsDirectional.push(getDirectionalSpritesById(i, 4, baseTextureChar)); + texturesChars.push(getDirectionalSpritesById(i, 1, baseTextureChar)); + } - // Check if meta is defined and has a 'user' key - if (meta && meta.user !== undefined && typeof(meta.user) === "string") { - // Create a text label - const envID = meta.env_id !== undefined ? `-${meta.env_id}` : ""; - const extraInfo = meta.extra !== undefined ? ` ${meta.extra}` : ""; - const color = (meta.color && CSS.supports('color', meta.color)) ? meta.color : "0x000000"; - - const labelText = meta.user + envID + extraInfo; - if (userFilter.exec(labelText) !== null) { - const sprite = new PIXI.Sprite(textureChar); - //sprite.x = charOffset * 40; - sprite.anchor.set(0.5); - //sprite.scale.set(0.5); // Adjust scale as needed - const subContainer = new PIXI.Container(); - - subContainer.addChild(sprite); - const label = new PIXI.Text( - labelText, - { - fontFamily: 'Arial', - fontSize: 14, - fill: color, - align: 'center', - }); - label.x = sprite.x + sprite.width * 0.5; // Position the label next to the sprite - label.y -= sprite.height; // Adjust the label position as needed - subContainer.addChild(label); - container.addChild(subContainer); - - activeSprites.push({ subContainer, path, startTime: null }); + class Agent{ + constructor(user, envID, stackID, extraInfo, color, spriteID, path) { + this.user = user; + this.envID = Math.abs(parseInt(envID) || 0); + const curstackID = Math.abs(parseInt(stackID)); + this.stackID = isNaN(curstackID)? Math.floor(Math.random()*2048) : curstackID; + this.spriteID = 0; + this.dataBatchIdx = -1; + this.dataBatches = []; + this.waitingDelete = false; + this.animationDuration = animationDuration; + this.usingSpriteDirections = false; + this.sprite = null; + this.changeSprite(spriteID); + this.sprite.anchor.set(0.5); + this.subContainer = new PIXI.Container(); + this.subContainer.addChild(this.sprite); + this.label = new PIXI.Text(this.formatText(extraInfo), {fontFamily: 'Arial', fontSize: 14, fill: color, align: 'center'}); + this.label.x = this.sprite.x + this.sprite.width * 0.5; // Position the label next to the sprite + this.label.y -= this.sprite.height; // Adjust the label position as needed + this.subContainer.addChild(this.label); + container.addChild(this.subContainer); + this.appendBatch(path, null, null); + } + formatText(extraInfo){ + return this.user + "|" + this.envID + extraInfo; + } + changeText(extraInfo, color){ + if (this.waitingDelete) return + if (extraInfo) this.label.text = this.formatText(extraInfo); + if (color) this.label.style.fill = color; + } + allocateSprite(spriteID){ + self.usingSpriteDirections = allowSpriteDirections; + return self.usingSpriteDirections ? new PIXI.TilingSprite(texturesCharsDirectional[spriteID], 16, 16) : new PIXI.Sprite(texturesChars[spriteID]); + } + changeSprite(spriteID){ + this.spriteID = Math.abs(parseInt(spriteID) || 0) % texturesCharsDirectional.length; + this.sprite = this.allocateSprite(this.spriteID) + } + updateAnimationTime(){ + if (this.waitingDelete || !allowAgentsPathStacking || this.dataBatches[this.dataBatchIdx] === undefined){ + this.animationDuration = animationDuration; + }else{ + let batchSteps = Math.max(1, this.dataBatches[this.dataBatchIdx].path.length - 1) + let nextBatchesCount = Math.max(0,this.dataBatches.length - this.dataBatchIdx - 1); + let totalSteps = batchSteps + nextBatchesCount; + for(let i = this.dataBatchIdx+1; i 3) log2Steps += 1; + else if (nextBatchesCount > 7) log2Steps += 3; + if (log2Steps < 5) log2Steps = 1; + else if (log2Steps < 7) log2Steps = 2; + this.animationDuration = 400 * batchSteps / log2Steps; } } - - } - - function animate(time) { - activeSprites.forEach(obj => { - if (!obj.startTime) obj.startTime = time; - const timeDelta = time - obj.startTime; - const progress = Math.min(timeDelta / animationDuration, 1); - + appendBatch(path, extraInfo, color){ + if (!this.waitingDelete && path !== undefined){ + if (this.dataBatchIdx < 0) this.dataBatchIdx = 0; + if ((this.dataBatches.length - this.dataBatchIdx) < 10){ + if (path.length < 2048) path=path.slice(0, 2048); + this.dataBatches.push({path, extraInfo, color, startTime: null}); + this.updateAnimationTime(); + return true; + } + } + return false; + } + getRelativeTime(time){ + return this.dataBatches[this.dataBatchIdx] !== undefined ? (time - (this.dataBatches[this.dataBatchIdx].startTime || time)) || 0 : this.animationDuration + 1; + } + setPendingDelete(){ + container.removeChild(this.subContainer); // Remove sprite from the scene + this.subContainer.destroy({ children: true }); // Optional: frees up memory used by the sprite + this.waitingDelete = true; + } + updatePath(time){ + if (this.waitingDelete) return + if (!this.dataBatches[this.dataBatchIdx].startTime) this.dataBatches[this.dataBatchIdx].startTime = time; + const timeDelta = time - this.dataBatches[this.dataBatchIdx].startTime; + const progress = Math.min(timeDelta / this.animationDuration, 1); // Calculate the current position - const currentIndex = Math.floor(progress * (obj.path.length - 1)); - const nextIndex = Math.min(currentIndex + 1, obj.path.length - 1); - const pointProgress = (progress * (obj.path.length - 1)) - currentIndex; - - const currentPoint = coordConversionFunc(obj.path[currentIndex]); - const nextPoint = coordConversionFunc(obj.path[nextIndex]); - obj.subContainer.x = 16*(currentPoint[0] + (nextPoint[0] - currentPoint[0]) * pointProgress); - obj.subContainer.y = 16*(currentPoint[1] + (nextPoint[1] - currentPoint[1]) * pointProgress); - + const currentIndex = Math.floor(progress * (this.dataBatches[this.dataBatchIdx].path.length - 1)); + const nextIndex = Math.min(currentIndex + 1, this.dataBatches[this.dataBatchIdx].path.length - 1); + const pointProgress = (progress * (this.dataBatches[this.dataBatchIdx].path.length - 1)) - currentIndex; + + const currentPoint = coordConversionFunc(this.dataBatches[this.dataBatchIdx].path[currentIndex]); + const nextPoint = coordConversionFunc(this.dataBatches[this.dataBatchIdx].path[nextIndex]); + const deltaPoints = [nextPoint[0] - currentPoint[0], nextPoint[1] - currentPoint[1]]; + const absDeltaPoints = [Math.abs(deltaPoints[0]), Math.abs(deltaPoints[1])]; + // Hide subContainer when warping, to prevent fast and noisy movements + const visible = Math.max(absDeltaPoints[0], absDeltaPoints[1]) < 1.5; + this.subContainer.visible = visible; + if (visible){ + if (this.usingSpriteDirections){ + let direction = Math.abs(parseInt(nextPoint[2]) || 0); + if (direction == 4) { + direction = progress >= 1 ? 0 : (absDeltaPoints[1] > 0.5 || absDeltaPoints[0] < 0.5 ? (deltaPoints[1] < -0.5 ? 1 : 0) : (deltaPoints[0] > 0.5 ? 3 : 2)); + } + this.subContainer.children[0].tilePosition.x = -16 * (direction % 4); + } + this.subContainer.x = 16 * (currentPoint[0] + deltaPoints[0] * pointProgress); + this.subContainer.y = 16 * (currentPoint[1] + deltaPoints[1] * pointProgress); + } + if (progress >= 1) { - container.removeChild(obj.subContainer); // Remove sprite from the scene - obj.subContainer.destroy({ children: true }); // Optional: frees up memory used by the sprite + this.dataBatchIdx+=1; + if (this.dataBatches[this.dataBatchIdx] === undefined){ + this.setPendingDelete(); + }else{ + this.dataBatches[this.dataBatchIdx].path.unshift(this.dataBatches[this.dataBatchIdx - 1].path.slice(-1)[0]); + this.changeText(this.dataBatches[this.dataBatchIdx].extraInfo, this.dataBatches[this.dataBatchIdx].color); + } + delete this.dataBatches[this.dataBatchIdx-1]; + this.updateAnimationTime(); } + } + } - }); + function startAnimationForPath(path, meta) { + // Check if meta is defined and has ['user', 'env_id'] keys + if (meta && meta.user !== undefined && typeof(meta.user) === "string" && meta.user.length > 1){ + const envID = meta.env_id !== undefined ? Math.abs(parseInt(meta.env_id)) : NaN; + const invalidEnvID = isNaN(envID); + const stackID = !invalidEnvID && allowAgentsPathStacking ? envID : Math.floor(Math.random()*2048); + const phash = getAgentHash(meta.user, stackID); + if (userFilter.exec(phash) !== null) { + console.log(meta); + const extraInfo = meta.extra !== undefined ? ` ${meta.extra}` : ""; + const color = (meta.color && CSS.supports('color', meta.color)) ? meta.color : "0x000000"; + agentsLock.acquire(); + if (activeAgents[phash] === undefined || activeAgents[phash].waitingDelete) { + const spriteID = meta && meta.sprite_id !== undefined && meta.sprite_id >= 0 && meta.sprite_id < 73 ? parseInt(meta.sprite_id) : 0; + let agent = new Agent(meta.user, invalidEnvID ? 0 : envID, stackID, extraInfo, color, spriteID, path); + activeAgents[phash]=agent; + } else activeAgents[phash].appendBatch(path, extraInfo, color); + agentsLock.release(); + } + }; + } - // Remove sprites that have completed their animation - activeSprites = activeSprites.filter(obj => (time - obj.startTime) < animationDuration); + function animate(time) { + updateAgents(time); + wipeAgents(time); lastFrameTime = Date.now(); requestAnimationFrame(animate); } - requestAnimationFrame(animate); }); From 9ccfa610548d37b57bf345b6aa67309134679517 Mon Sep 17 00:00:00 2001 From: GridRL <165774503+GridRL@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:33:05 +0200 Subject: [PATCH 3/3] Fixing typo on directional changes --- visualizer_live.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/visualizer_live.js b/visualizer_live.js index 8cb472b..40d6ef3 100644 --- a/visualizer_live.js +++ b/visualizer_live.js @@ -370,7 +370,7 @@ PIXI.Assets.load([ this.dataBatches = []; this.waitingDelete = false; this.animationDuration = animationDuration; - this.usingSpriteDirections = false; + this.usingSpriteDirections = true; this.sprite = null; this.changeSprite(spriteID); this.sprite.anchor.set(0.5); @@ -392,24 +392,24 @@ PIXI.Assets.load([ if (color) this.label.style.fill = color; } allocateSprite(spriteID){ - self.usingSpriteDirections = allowSpriteDirections; - return self.usingSpriteDirections ? new PIXI.TilingSprite(texturesCharsDirectional[spriteID], 16, 16) : new PIXI.Sprite(texturesChars[spriteID]); + this.usingSpriteDirections = allowSpriteDirections; + return this.usingSpriteDirections ? new PIXI.TilingSprite(texturesCharsDirectional[spriteID], 16, 16) : new PIXI.Sprite(texturesChars[spriteID]); } changeSprite(spriteID){ this.spriteID = Math.abs(parseInt(spriteID) || 0) % texturesCharsDirectional.length; - this.sprite = this.allocateSprite(this.spriteID) + this.sprite = this.allocateSprite(this.spriteID); } updateAnimationTime(){ if (this.waitingDelete || !allowAgentsPathStacking || this.dataBatches[this.dataBatchIdx] === undefined){ this.animationDuration = animationDuration; }else{ - let batchSteps = Math.max(1, this.dataBatches[this.dataBatchIdx].path.length - 1) + let batchSteps = Math.max(1, this.dataBatches[this.dataBatchIdx].path.length - 1); let nextBatchesCount = Math.max(0,this.dataBatches.length - this.dataBatchIdx - 1); let totalSteps = batchSteps + nextBatchesCount; for(let i = this.dataBatchIdx+1; i 3) log2Steps += 1; else if (nextBatchesCount > 7) log2Steps += 3; if (log2Steps < 5) log2Steps = 1; @@ -455,11 +455,12 @@ PIXI.Assets.load([ const visible = Math.max(absDeltaPoints[0], absDeltaPoints[1]) < 1.5; this.subContainer.visible = visible; if (visible){ - if (this.usingSpriteDirections){ + if (this.usingSpriteDirections&&this.subContainer.children[0].tilePosition!==undefined){ let direction = Math.abs(parseInt(nextPoint[2]) || 0); if (direction == 4) { direction = progress >= 1 ? 0 : (absDeltaPoints[1] > 0.5 || absDeltaPoints[0] < 0.5 ? (deltaPoints[1] < -0.5 ? 1 : 0) : (deltaPoints[0] > 0.5 ? 3 : 2)); } + console.log(this.usingSpriteDirections); this.subContainer.children[0].tilePosition.x = -16 * (direction % 4); } this.subContainer.x = 16 * (currentPoint[0] + deltaPoints[0] * pointProgress);