From 2a9d4f4b56783749db4a02852d715d0c0d2c1755 Mon Sep 17 00:00:00 2001 From: tillvit Date: Thu, 21 Sep 2023 18:34:02 -0400 Subject: [PATCH] Add snap/quant keybinds, change note colors --- public/assets/noteskin/dance/lift/body.txt | 72 +++++------ public/assets/noteskin/dance/lift/parts.png | Bin 5191 -> 7785 bytes .../assets/noteskin/dance/lift/parts_old.png | Bin 0 -> 5573 bytes .../noteskin/dance/shader/lift_gradient.frag | 2 +- public/assets/noteskin/dance/tap/body.txt | 24 ++-- public/assets/noteskin/dance/tap/parts.png | Bin 5414 -> 5907 bytes .../assets/noteskin/dance/tap/parts_old.png | Bin 0 -> 4919 bytes src/chart/ChartManager.ts | 33 +++-- src/chart/ChartRenderer.ts | 6 +- src/chart/component/SnapContainer.ts | 12 +- src/chart/gameTypes/GameTypeRegistry.ts | 3 +- .../gameTypes/common/BasicNotedataParser.ts | 5 +- src/chart/gameTypes/dance/DanceNoteTexture.ts | 15 +-- src/chart/sm/NoteTypes.ts | 13 +- src/data/KeybindData.ts | 118 ++++++++++++++++++ src/data/MenubarData.ts | 4 + src/gui/widget/NoteLayoutWidget.ts | 10 +- src/gui/window/KeybindWindow.ts | 55 ++++++-- src/util/Ascii85.ts | 8 +- 19 files changed, 280 insertions(+), 100 deletions(-) create mode 100644 public/assets/noteskin/dance/lift/parts_old.png mode change 100755 => 100644 public/assets/noteskin/dance/tap/parts.png create mode 100644 public/assets/noteskin/dance/tap/parts_old.png diff --git a/public/assets/noteskin/dance/lift/body.txt b/public/assets/noteskin/dance/lift/body.txt index 461d0be4..8c53ed0b 100644 --- a/public/assets/noteskin/dance/lift/body.txt +++ b/public/assets/noteskin/dance/lift/body.txt @@ -1,9 +1,9 @@ 79 --9.000000 1.000000 0.500000 0.472100 --6.000000 25.000000 0.546875 0.855300 --9.000000 28.000000 0.500000 0.903200 -6.000000 25.000000 0.546875 0.855300 -9.000000 28.000000 0.500000 0.903200 +-9.000000 1.000000 0.375000 0.472100 +-6.000000 25.000000 0.421875 0.855300 +-9.000000 28.000000 0.375000 0.903200 +6.000000 25.000000 0.421875 0.855300 +9.000000 28.000000 0.375000 0.903200 -19.000000 14.000000 0.033500 0.417300 -19.000000 11.000000 0.033500 0.434600 -11.000000 6.000000 0.045700 0.466200 @@ -13,25 +13,25 @@ 19.000000 7.000000 0.094600 0.567900 19.000000 6.000000 0.094600 0.552000 25.000000 1.000000 0.094600 0.472100 -0.000000 -24.000000 0.562500 0.072900 -0.000000 -25.000000 0.546875 0.057000 -26.000000 1.000000 0.546875 0.472100 --6.000000 -5.000000 0.546875 0.376300 --19.000000 7.000000 0.562500 0.567900 --5.500000 -6.500000 0.546875 0.368300 -9.000000 1.000000 0.500000 0.472100 -19.000000 8.000000 0.546875 0.583900 -19.000000 11.000000 0.500000 0.631800 -0.000000 -28.000000 0.500000 0.009100 -29.000000 1.000000 0.500000 0.472100 --26.000000 1.000000 0.546875 0.472100 -5.500000 -6.500000 0.094600 0.392300 +0.000000 -24.000000 0.437500 0.072900 +0.000000 -25.000000 0.421875 0.057000 +26.000000 1.000000 0.421875 0.472100 +-6.000000 -5.000000 0.421875 0.376300 +-19.000000 7.000000 0.437500 0.567900 +-5.375000 -6.375000 0.421875 0.368300 +9.000000 1.000000 0.375000 0.472100 +19.000000 8.000000 0.421875 0.583900 +19.000000 11.000000 0.375000 0.631800 +0.000000 -28.000000 0.375000 0.009100 +29.000000 1.000000 0.375000 0.472100 +-26.000000 1.000000 0.421875 0.472100 +5.375000 -6.375000 0.094600 0.392300 5.000000 24.000000 0.094600 0.839400 5.000000 -8.000000 0.094600 0.408300 -5.500000 24.500000 0.562500 0.847300 --19.000000 8.000000 0.546875 0.583900 --19.000000 11.000000 0.500000 0.631800 --29.000000 1.000000 0.500000 0.472100 +5.375000 24.375000 0.437500 0.847300 +-19.000000 8.000000 0.421875 0.583900 +-19.000000 11.000000 0.375000 0.631800 +-29.000000 1.000000 0.375000 0.472100 11.000000 30.000000 0.079300 0.320000 -11.000000 30.000000 0.045700 0.320000 -11.000000 30.000000 0.045700 0.320000 @@ -48,36 +48,36 @@ 19.000000 14.000000 0.091500 0.417300 -32.000000 1.000000 0.013500 0.497200 32.000000 1.000000 0.111500 0.497200 --5.500000 -6.500000 0.094600 0.352400 +-5.375000 -6.375000 0.094600 0.352400 -19.000000 6.000000 0.094600 0.552000 -5.000000 -8.000000 0.094600 0.336400 19.000000 11.000000 0.091500 0.434600 -5.000000 24.000000 0.094600 0.839400 --5.500000 -6.500000 0.094600 0.392300 +-5.375000 -6.375000 0.094600 0.392300 -5.000000 -8.000000 0.094600 0.408300 --6.000000 -5.000000 0.546875 0.392300 +-6.000000 -5.000000 0.421875 0.392300 -29.000000 1.000000 0.017800 0.497200 -9.000000 1.000000 0.048800 0.495700 -19.000000 7.000000 0.094600 0.567900 0.000000 -24.000000 0.094600 0.072900 24.000000 1.000000 0.094600 0.472100 -5.500000 -6.500000 0.094600 0.352400 +5.375000 -6.375000 0.094600 0.352400 5.000000 -8.000000 0.094600 0.336400 -6.000000 -5.000000 0.546875 0.392300 -19.000000 7.000000 0.562500 0.567900 -5.500000 -6.500000 0.562500 0.352400 --25.000000 1.000000 0.562500 0.472100 -25.000000 1.000000 0.562500 0.472100 -6.000000 -5.000000 0.546875 0.376300 -5.500000 24.500000 0.094600 0.847300 --5.500000 24.500000 0.562500 0.847300 --5.500000 -6.500000 0.562500 0.400300 +6.000000 -5.000000 0.421875 0.392300 +19.000000 7.000000 0.437500 0.567900 +5.375000 -6.375000 0.437500 0.352400 +-25.000000 1.000000 0.437500 0.472100 +25.000000 1.000000 0.437500 0.472100 +6.000000 -5.000000 0.421875 0.376300 +5.375000 24.375000 0.094600 0.847300 +-5.375000 24.375000 0.437500 0.847300 +-5.375000 -6.375000 0.437500 0.400300 -9.000000 28.000000 0.048800 0.332200 0.000000 -28.000000 0.062500 0.675800 9.000000 28.000000 0.076200 0.332200 29.000000 1.000000 0.107200 0.497200 9.000000 1.000000 0.076200 0.495700 --5.500000 24.500000 0.094600 0.847300 +-5.375000 24.375000 0.094600 0.847300 90 0 1 2 2 3 4 diff --git a/public/assets/noteskin/dance/lift/parts.png b/public/assets/noteskin/dance/lift/parts.png index fa65bc449b5953037a6ea726931cc258102e6c57..da86b2a64ccede68720cb7c232e305dc9cec684e 100644 GIT binary patch literal 7785 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 SGVk~g)QcC4bt=wdBK`+d_uYN~ literal 5191 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 O!d-UzY#&?uvi=8(SxXlH diff --git a/public/assets/noteskin/dance/lift/parts_old.png b/public/assets/noteskin/dance/lift/parts_old.png new file mode 100644 index 0000000000000000000000000000000000000000..de13243a207a9af5efcbe9b9e45594ae62c17182 GIT binary patch literal 5573 zcmY*d2|QHo_rEjteQ7F7F;Q8{nssbr9bU^2p{!XGgUn#EjG@JzC2bPTA^PGF{d7gMn3j`Ot5IX<>Tqec_mjM7o z7XdgcGyS^j^tzeefY6r_dO-QWsTKOoT~B)xFLQH1nl7^fOdx)M;Rtf11`-08{*nRU z50LP`@@0_ZKNv6o#QFe?|6s1s+oQvnejXY9yJg4){f{vh{9iN($YuDi48ZAVfF+7j zNN?Bzjjy5sfaBQF2?8>+1n6v6eQfM8_U2|V_W+dab&miyPgyJ~@JI{L#KPzz$`f-P zibeVPqhVMrvA+;7x_s0vCkFisg7MW7vp2Vd>IVdQLRDntW#z@R*`ZLVW{`&$?6QH; zKkD>5EwLLIOdw27E;KY$HuREgK#;ebg1Wl8oV=o(qM{5PA%ni{kGYPO@kfjQP4ZtJ z15dPjkWV1SC%_+i#CzQ>AQ+=1CU#`#-{WsTF+N`Zv*eHdCoFn^az{_(6lCS){^h2t zY995%kU>74bjwHl+6tO~A^%_BKXx?bj*R~wXa0`#uU>kn+U%Ng{|=iryP)$N3jiDo zHZjn(!GgX!eRy!zgm1T}thi{!hdC_M(3k>9@bXLCRYE^CHU0K9YOYOUli?e0=t0}F z!?`ZLLiTX4Ilj3;@xIY7isE917(3fcIpVi|)b_c&`izlkN@++WIX1w%D(YY%r+~Wh z^g*{iEL&uxotW{vJ2Ff&dqfwfWWFW3L(U&c{~h>;OC)uCjapy+-26#LwcxU}<0Y!g zN@C1m5zkB`maHmzXuBAyBiFMtx0>$tbHSxIBOv4)^*PVE{;?dIb0H>*>Y27V=S`5g zabBCYK4^FuayUbfi{02Y)0x{A`(v$q|@`LDf6NUpZww{ zg7cmg3Jc6j?5lGZD+=z!13DGHecJKi50`wYCWEpKqA9bqH#gsBRKBl^ySU{Qf6MY> z_;t#IO_T?I4Y55Q7NpKncP15 zFsY6cOnZMXbVa*={#MDe7iCK4VTmcne+jQ9l0!-XvrEunD~h)wFc_iV=ANF|K+d^` zuB#daICt0jMo3?k0hprGWezFZw z1CdvSqpF$VyQlLnD4e!s*bL`YzOL9c*>zx&9J6$-P2D*_Df{^S6{G4&PYpy(zPhhx zsx;P3Q4lce*0dAIA)8vq*gCY4$o7miD%#Z*ofVy1!EBb8>z}nuQj??rh?Z#s+Z_6_ zp>qEE?Cq-NM#hdr_!^&camj7RGus7MhuO2jE@dW$Gv#q0LA}i3L_^8ml18Uzs``fH zs11@oZg6wc$Rv+Sb!`)XQ-g(|ImSkuLt0-Uy6HP&-XZxRsI_;K=a;U;HOSRos`q0+;w$%;7|3-M}%m+*U|ys+Dxg(1AQ@bSg~ z_HdIA;`)t7&vn?U`32S;7RohaEEsr8U(_E{c%e0ddY+P7*;uFV>2G-JmIOI$KWqAv zD$b!j=eNiAje1UFRLdWL?Bb*qbm$JNZEwLnRcfoqs4#H$F|>$d+0a-H_xVxD?CJ0V z=DmEj(Kpm8$E)q zRF6a^I%_P;!XboA@WpS3Kfkq)P>YlWV6x9+SJnbl@ZKD<*$R=b7*;{=dU zO?6BFXW2z<4p4g9|YuXTLXpC>Eajgaa@b`EmWs9g+~4s zK0MkWA3aNb>2WX+4tdHrQPT1VJVLz&=KB@piO*W7RcW?%ytdZprEN4~G^%a>;TO6C zMA(gnXOuUt*!!2vd;<@jz8;O1Zi&Z+yi!U4nI1%QPNc2dJuZJOiBo0@`~xFExG<<& zV1VAL?=$TELb3=sjv}b1@lbj=;hGdIZ=Dh+>)|-(L}qKhL$D`cYhUOP$YX;S2lsmM zBd?jsy^|Pie80g`oSRsMW*RIoB$ixJx;ZE~aYj99FcCNHlnOm8F+@nvLbw16;Prf; z{cF1_Cip82PW`Ic3Tr$$;zaH>gCK6#x9saSc$TdR7H{LAJb~XoN<}-p)e2e6b+*qX zR5W{sZQR=z?VKqEwS4=-Wx+Ef*SkuKFP9H*Mnb8!a59jb58kZ2@*`<*aE_K0qHjwa zBVF;i#gq<0J6chs!Lx?LM)VKK=H<>#OxjgG--UorfPnCKYsPEY5E1UDn*4r2vitfU zrI1|dswH(zt&F%6_)|9PBnMF0(Mix7(*Vt0M(_L};mL)id&ald> zP+@;mgqusElj&05HJ$kgs{`*_rk0$I{G3myLN89FLfqXghV}NgTKR|ei1SO;-3w%8 z{|@%PT3;I^&(7vx^xmJss&VsnC7TvBLt?*%xyDK&@Tu!4QZ=^*o(%@PJ0ds58zeLT z=DTh-*qA>A6gah@;)*Ewv7XK8yo0_eFG?XAL3s)SFAk|FF6+==H7U4&P91BV3eAhF zrEW;#i;N!g>F)Yj3v=xPe@Bpf(E=uN*)wSJlp-+At60z4gT@s)e4IrQ?x2Obux7QJ zCF%@<1-6l0?)6s)LKPbAN6xrmC9HupvajVh!q!NCKY29&i)q2E2M!(hM6!jspQ+6xIq%<2^dA+(DlVZazyXHDYBD`nP#Lr1nfA~9(Zs8)*$&1iFNL9 zS}`SW&Fh$w%1~#^w2|WgRVKqD+OvM#hb!$q3LV}FR*jNLIJ=5H`2&D%8q9NRhxbZ7ALqVG_#)Tryl zM(-?fCN-K17+ajCbkbI6XjR*#sh_+Ez|T@>Hw6;CRy~dy>zk~ITb0N|zef}DN#nA3 zG&`h=XB}LcOsCq+a;G}4oPlrtM%Ht;mz7El&8ECQEDvYgt1;{lz^bPc9>+OM! z$X$W!n23Dp>Ya;c&>IiE~fzY%F<$h7EXhHkMq*379`ii(DT62cuxbh63=WQ$0@kDkT2;mD;pb9i$aMi+0bswR(`dw#1 zVX7_kO-XRWh2_MNc3q#MyH+Is3Z_waz07>6bcHTvJOB2wfNtg%4KZD=q%bH>6u$?x zgt**ugBcZAj+VaPxK8O^*n8p2?ROr9im0gT$ILx8aRXIyy}A#7M_#P8V8aMjx}48& z$GbyTPf;CIs3YOqS;1=`JwsVPrG&NQQcbKVA7+IKSrl?qH$A2nc!IWm6S3zk&b@LI zo_bHcl6fmNwVXD05pO@OF?I+^2JC0EHRHmd&bvBlq4bKzigu5v4jw}N?DJFoqwHvI z0QFDLXqVpY<*r3C0*0Tv!B%-uS6>N$DEhC%8?aot+YgL~@S7hrHpCo*Gd}s?=^Xi@ z-QKU=4?N=?Pal3?Dtme%tUN&u>}iV2o4@H~IPCd-GD+|7jin|RBNCgE!mw4$6Tb6_ zw-Pk_w9$$X6Dl1T%wHMZJ!wVxc?v+r3#9&#$H{~3g=ba+;v#FsUoO^d&5P{ljXB;Z z{rx;6lP`6Lc6&DKL0Bl7>9{@DOii@~)6yNuwY0+8cN^#67*VYRmnNPTA9yfsRD*AG*me^dU-vz>iQLHMnr6N0qm$?0u&p|iqRQ;K`?)qIW58#U zIZ9dx0XRQARXQkAlOQQXg$?QEYTfQI+J$s3A1pzxy>i$7y!U%%euRj7RFvt}AK@ZA zbHV?TQ=NIb#U9x%Mzi9#B=4fHBJ|rhFlr@LYgKP!vd=ruReIUpP{vw&xe;kDH636i%>jWdJmLTVbEF2>&pCGD?x1yAjkss!M^4!aqG^6K& z6N772(@hP{$j75=8w!RkDcPJEazPN1NQEr+ag?jsdW4rvp)g^Ux7jNCO@1O>{{|y( z7tcY~IMGM5R{y7M&X2b@4|=|rE*ZC`l(F^f!1V!0bM=Lrz{K5fE}*JU5PsIVX4{DO zTZ9~L0$?P&$bexy;Cp1zZ}&YS+`2Oh%D10C-aAMc#a(SKDlYd46#^MB)fI1y0B=#i46aW4?Cdm&3dNx#mQ+lV!U30`;DuALP%0_;S* zFR_w3kQRpV8t?P?THbyJl3t#?RFMG4misW3s8=4-IZ$$i5c1WdmXnVDP=1WSpQ>^> zJT&-j6^AQ2D)L;MzxtWTB+jg@THLJ@*a|Lj9%uUe@=Pv->yvr`wXdR`DPJgRwAt55 zqZSO6N53b$PyIz&{Bisl;5ji?7h?{;D#NU&s7NM$<-G4TJ%~^O&+>WP8Jg|Dxdfq> zYXC~O@@#sT|hcr^%m&o3|VcA>E9#6!kPxJq&C zAjp7Ng0l2x1e z-wA9ErT?Pdqb!sOxLskY*hQ{wf3t)Yzdw8fE6ckd-QWH7Q=!( ztUhhddG7h6oR&bdgeh~-w$mx~p(&(xeI;2cdt9p9h%@4$rg?7mtIoMBx+N1g1d zps5vI;!ghkS6u)Y3e2$JtvZdrR*7=#qQqQR!Rb$a>FLj^K8gHwGDhvVD?$RK5!Q}O zbZNhA?vlFVMTO!#w@Bcu`Tn;zpPE$(MFimGDufGbGiTz4L z5Nmyb*-cNrAsdlt=SBi7(!;(oKmwHfNr{6?>Cj)>Ir6H??#{ac;jwU`)>?55A!V5c zGUzy2WSA=`XZrZLkd^Hie?BWpjW{j1Z=PV9bKHs2c;H)R7-DEZ7VVTEzW!1h)<%*~ zeF}aif2QN31OvK;_9rj=hS-=XRM=U(sgfCA~Q`XAxGoj!+@^^eXdAmGDTb&v<8ne%zGy8Kkv~=14Of! z@hAA2n4yunj)6vi##T$VH7K7n)x79ZyDf;9q&4gmy3Q`IT+vmHhaTpWgjIkhmA&fN(FKq9*-d=BZzgv}!}q zw@ey>iBSe8784h1a$D)6Nzaq|2mr1U^703g$}9(&Hh?gLb@C3jL|N>W+$1}Q+;P@Y zEi0RmcKL*YSFz%~OZ@`t!PQ1qt|zm=1yCG@owqXz;VTl5WsQGJJY<C?^Lw7p`+MK(kNZB?Ip=%6=Q`JNF9~KQ`a9T#*#Q7M3=QU^gU`Ap#mR#6B)?E1CRv5@- z`CA60Uyc*Eo1OvyS9CpP>2GOlq-O8sAy0AeI`1eS2{-q{J;C{RKyJ<>T8k^w`d-*!zROA)p6(qFSaX6fYuY;4Cx!%#g;LM(; zgp0qww;F*E7#JuYs3h;@>r7BQbm$O4fk+?{<(M9FewRG`DM50cetZ55@^>6PM?ZUC zS8sn;FHhXBxRmo=7yUIQBz_V7r~R3yW031VOrCy!4U0KI!Y>O!QC@-Yf5H4+o&F!# zFUy~>-{bm|PUBZFH8a;BM|UedR}V)|KW1uLD#{waS^mrLPo94Q4|}VJFxrm_5ohIl~b-#q`a{0sRjZ8cM0S4U<=f7R_5(SPaw^4B2zD(b(A{O1h* z)-tD1i(P~8pXH#%9st*<=HoF57%XsWK2!`{AuOq=QlWHOIJSg@l4mJF5^XvcE{oM znHjm+xjPwCQxg*_`RgNX*u<5(!I`6r*A7`Jd>Bz&jL6&{rm+5KgAc;C2ge0i(u?bPCS_4?_HTXfejt8?26amx#VIX!LL48d@9F*b+JNM(P7Dq95$ zxk{flXxDU7v}yfI;D_~t+6=>*%{FYfjmP#oeXQY3j^;kaoCCdKsQI4*k9FaUC&(jS zT^o-++KYZX_wwVnN67x?2Ue+1OXD&eyPjBR+61Pz)>QTGGhgEb(_vj~0@j zWgUHN$4-sc-OII}Z?MLnQ$gWq#wY%E7$Ra&VO1J#vz@A1s}N09w5JkcXk0_coEg<8?vy`%|!scWB1ZB1%=9cqsAms$X!Cf;WKd;Om+ zZov!nBJxJbx|F#c^2oLiNg+Y%=>v9CO{kW;}RApJ?^z50SI%^miV$END8s!>7Tx(==SF?Jr?$(_0Ie(0u68cVB z^azGtTqW3sFR>fd-=DjmJY-rxJ1NKA-M6(Xn&cAp!b5n`FiHy0wqCLmgI_Gjbh%!* zq!+IZ@(w)I*je&ka9hvpyn#+~fQW*%x3i5F3^?X1WaxmTWDg$JR^VbvL_KW5@lVPO(CrFR*%H-hfnEC{+vW_vs zEAaTBz^ijcUQVjM5++|CByqvUk{Kkw`}#+}Y?UJ_*@QNoZpxo&wB~@i1NsM1oSHC< z?4o)8*o1o_>FmeZZS>IqYA?Y|K;*LJJ$oE~qh9GG3dtUs_0zb&@^ z-nE2wATqfqR@Ey5s2^BuZKO6t=c?h~5Dsj9S9rM>vLR0_$IzZ=+vP`km@8cwM`v-- zo4On%J}|x)boX|nfN*9NSDzS*{5a0HQK_+m@dibPH%scqf9!*sDY%a!;9~-$n%u60 zaN^xX9tzBR4{16U$vs(p&`;&|1)V++{5IUvQCRCg?-+Bk_@+8GxS>%*SbZR`57 z;}rL+&!hW1Tw1e-7xXnp0vmWXO?X7Gd*{IqAHhxHSs@as2jX1T$@waI1dC9dlcie$ zqqns{kM+f*A2wXVL`R%OK~$%>+!{twS+%qI#2SAty!6g|jUe1A#1GDD`NUF^aA^Jm>;2nFu+ESJ-1g_X*Y{~8`*&!a#T?Y(yX3u(|E=1)S-S?*d@qD3k5v+Vm z{w!{crHZ8bsiRVGF2%R;P~yUgW@Yu&4U96|LfPA?T4cS`vK&SnN2HF+0l~Q!avreO z&dIazmc2RmPs1F%77taOL@|yJaA@s+N}eM(Wb1FT_J;uU$}P z+yIu+I4kbmOj1P!o2UhU;+25CR^V69ZFP?Ke4;wbgDBxM$&c$9JsY7DY!s!s5p9*O zC-vyqO%8#gkxS)8yseyJMIbwf_AdE`C?KC}z}Kz=bUY0=8*yZPboVN4S5~?& zmgK^qWCrxk?4}jx@ngV!6>jlic7Ibp!@BdX)eoF)kuNR2+myT1z#$^Nu@#-Ktvg`O zy_ibIh^@0o2zK1vdkk4tdyWmZv*jM6yiS>4&tHHl-OIA!N-sv(hYW7`g`#0sgwp&~ zB4(t#S?@0RMy9E2g>gZDWcl8hgWp+?;i$B{fDC1)DzxE0AF#_SX>b0n^F-ny61pM) zNdb?o+8t6xrV>|{GGBU_P#x>>SDxl^4ef9xi51v+dwE)=H8z+ljAZ0?D<5GW3E)%SQFYk5QP2zD+hUrI06Cqh}6n{WlDHOEmhj*@CCBILT z0*Z+!c9?nQ%lfPtmcB;R2+&v+-jG#jR8osbDgXwS!q@wJPMDy5cXwN81eW7%?7ocT zuD;~vQAfk53vo$Jlwkj)W@=uH60yJW1H&@W3OrMtI=``>S=}OEvFdqZARNw1_Etki z(LVEwYwy>E_e)Zbck}HcDdXb#q@(lkRc*ax*n+GQ4ztVPXxXGJ!GiKk3wr)0L~9$h zbC)RZ?GYb5Ye6YFEStNpFFZQwMFA*AY7+C5s^UG`4jhDD_-bbbWou!=iR;O=|HYd-NOV@jUhq7F2oKl>XwMUHr1RItl$bmSmgotR8@tk(P z0jFdNZs&2&G>Cd`NcPIVM@Dk(p-2lIaCPG zJYI+3kCA4#voEyWI6*MMLr-Klr)mINnVPgTl?>Y)-l6+C9DeITyG~C(Yx{sM3dTI< z*WWcf2msTIcFa9BB;4jMUA zLNK>=T<#66i}(t7VnN2j0rDHc$W93<0qSr=_z*kUkKCUAZSHi}mJ@LSB>=L#jVBaO zFlrJQ>;3nTIBSx8ID1tc6?d0kJVo9?9epFxr4+8?ZZ2j=@%L-81P=Zoaw!McKOaU< zFUV6dmo!v*o-7hMa2d}EpMTIC+pU+%%{T|mzRNj_FiPF+OtulB$2F@e+PH+RsJFrU z-oRvEXsrziVW9v&E+bDWBCn;?_bxR{TcJfz9;2g#K0oR&-)rPBax^pvKa0Ebbw7*n z%2Sj0(Kn2_ANYklYakM7WJ;lBo5;C#ymNgvSORGl1ks5#TTie`46L)nX$e_QaihP4|jTy1I2LfC*Pmq zfZ#7W!{YB&sAiDRiKW9auTQfqj^^)(yRv5lWB5|FcX2tSPy0;4e*J1^2RMll?LY6Ukm0sF>meWmnL>6c?%Ufm`gmSDc{93mE^qipb}=%Sr@YUW zIbT2H@918)!s*IwgN;20D=DTCJ9F!4BQH`GD<-}4O?`Ld(Sim$@2t0?Q2Z$7^yz~13 z`Se^B^~cK>kYF|Vk;d2vmsO4_Y-m1D@mrkiZMqdJQLnc*g0}s`DnX=_^R0G%Ui7Do zZ;%_6R$VJ|e4{FNV$N{RBk#WL!=TO$x;4BeR(4Q~#fe*(aL^=fk;@#4go1Ozgg{!dp$YE4@aK}+AFPV!Rxy9Z)i9hBYmTGy2PO>b~ z`WdI#TDQ(lv}x9uXlGp|Sc4D^diJt&JAUF?O%vC=5$2&Y?FX@oGnaRwA<%ekkR==8 zz*)9}vRrGQC_?fdKHRn5K7XQM`-h(%_w!w&Ql1fO(!o`7Ejv|>3jE%={Ft3J;Q>}5 zyR;{N3R>jG9;94ZdHA{Qr*95;g)?ZTBk$d}QSG~vkzmpwU?6DyS&1L-FCi5&-)n7& z278ok+(l!?q!0$~`}iuuwo^|R-l7j*aF(uGDd7jBa7q7fn~u3F;fKh)P-N8pNxRt~ z(L=?oO$s)r@Z^>Pkk3X!NCF~OPSZMENwU!shM=X2NFgQ8op(CJG9it);LRGw=lse7 z=hnphhMI#z!I_f_V+1n2!13Najy_h6(^%*N%w_3gM9}Cb8KBG=@cY+Hl^gyPh9jlx9~X zrN{x#nrL(9r*L@Y_4WIuC82B2&6ayDh(SP->m9-m*S zL%eI`im$|R4r$%QWt2xX77Gq2KJP!Z1) zK4P5`uWgKPZv0N~;pS`z&{I{MYzwo={~;U(yAfmat!ztr=nBR%z_>$PTq_3m*5Gap zxR<7+u2B`7_`X%VIkti=s@7tBqUxBHEgwe2D14R%m2L(jAw^|6%VKKyqpz;Wp!q-; zWB3P-W!AAN?eiBOw?a6{5vr>he+`H752n9um{UwM_fta6wOzVoiAMoiH9Ez_{pVOe%@m2ZOcgwbf1Gs zy(QyR>G`#1MM+=BOIil)qAKpjc4`@!b{6bOPGC1&Flb za(i`pZCgz}<~O9KY*{0vs7j*jpHz1VNs>es{lE<_Q~APhAw5TEn!vjkq_K6%0*vk# zp3xT7UGxEmi)KJ& z!cWrY2Twn(ws8y$Y1ahi9n44EyWg!R^KCGraNfLOaC_uI11(WIdtaNm4`)QnuDPi1 zPG{kC;VGX0+pg#wnqhBP4V)~90wC0W*>_s$OT}Q6Q3PorCkLT?g~EBt_JP_#y0Oz) zL6FX|fB8gHDkg_-E+Y}f3rktb5h#F5?F6GUUiOIw;@p!C@VSF_WxZ>DHGHT`ub+aJ e3#*PW26hMW!+IXZ6aDL-vEdODy+^v_$o~ONwM9k% delta 5368 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 wMQ@(mz&|e2U(M?)Xi1>k00000 literal 0 HcmV?d00001 diff --git a/src/chart/ChartManager.ts b/src/chart/ChartManager.ts index 007efd92..ea5b7cd3 100644 --- a/src/chart/ChartManager.ts +++ b/src/chart/ChartManager.ts @@ -32,6 +32,7 @@ import { TIMING_WINDOW_AUTOPLAY } from "./play/StandardTimingWindow" import { Chart } from "./sm/Chart" import { HoldNotedataEntry, + NoteType, Notedata, NotedataEntry, PartialHoldNotedataEntry, @@ -132,7 +133,6 @@ export class ChartManager { private holdEditing: (PartialHold | undefined)[] = [] private editNoteTypeIndex = 0 - private snapIndex = 0 private partialScroll = 0 private noteIndex = 0 private lastMetronomeDivision = -1 @@ -896,19 +896,30 @@ export class ChartManager { } previousSnap() { - this.snapIndex = (this.snapIndex - 1 + SNAPS.length) % SNAPS.length - Options.chart.snap = - SNAPS[this.snapIndex] == -1 ? 0 : 1 / SNAPS[this.snapIndex] + let curIndex = this.getSnapIndex() - 1 + curIndex = (curIndex + SNAPS.length) % SNAPS.length + Options.chart.snap = SNAPS[curIndex] == -1 ? 0 : 1 / SNAPS[curIndex] EventHandler.emit("snapChanged") } nextSnap() { - this.snapIndex = (this.snapIndex + 1 + SNAPS.length) % SNAPS.length - Options.chart.snap = - SNAPS[this.snapIndex] == -1 ? 0 : 1 / SNAPS[this.snapIndex] + let curIndex = this.getSnapIndex() + if ( + curIndex == SNAPS.length - 1 || + Math.abs(1 / Options.chart.snap - SNAPS[curIndex]) <= 0.0005 + ) { + curIndex++ + } + curIndex = (curIndex + SNAPS.length) % SNAPS.length + Options.chart.snap = SNAPS[curIndex] == -1 ? 0 : 1 / SNAPS[curIndex] EventHandler.emit("snapChanged") } + private getSnapIndex() { + if (Options.chart.snap == 0) return SNAPS.length - 1 + return SNAPS.findIndex(s => 1 / s <= Options.chart.snap) + } + private removeDuplicateBeats(arr: number[]): number[] { if (arr.length === 0) return arr const ret = [arr[0]] @@ -1086,7 +1097,7 @@ export class ChartManager { holdEdit.originalNote = { beat: beat, col: col, - type: this.getEditingNoteType(), + type: this.getEditingNoteType()!, } } this.getAssistTickIndex() @@ -1214,13 +1225,13 @@ export class ChartManager { (this.editNoteTypeIndex + 1 + numNoteTypes) % numNoteTypes } - getEditingNoteType(): string { + getEditingNoteType(): NoteType | null { return ( - this.loadedChart?.gameType.editNoteTypes[this.editNoteTypeIndex] ?? "" + this.loadedChart?.gameType.editNoteTypes[this.editNoteTypeIndex] ?? null ) } - setEditingNoteType(type: string) { + setEditingNoteType(type: NoteType) { if (!this.loadedChart) return const types = this.loadedChart?.gameType.editNoteTypes const index = types.indexOf(type) diff --git a/src/chart/ChartRenderer.ts b/src/chart/ChartRenderer.ts index 73ea23d1..c6c102ce 100644 --- a/src/chart/ChartRenderer.ts +++ b/src/chart/ChartRenderer.ts @@ -24,7 +24,7 @@ import { Waveform } from "./component/Waveform" import { Notefield } from "./gameTypes/base/Notefield" import { TimingWindow } from "./play/TimingWindow" import { Chart } from "./sm/Chart" -import { NotedataEntry } from "./sm/NoteTypes" +import { NoteType, NotedataEntry } from "./sm/NoteTypes" interface SelectionBounds { start: Point @@ -46,7 +46,7 @@ export class ChartRenderer extends Container< private lastMousePos?: Point private lastMouseBeat = -1 private lastMouseCol = -1 - private lastNoteType = "" + private lastNoteType: NoteType | null = null private editingCol = -1 private waveform: Waveform @@ -313,7 +313,7 @@ export class ChartRenderer extends Container< this.notefield.setGhostNote({ beat: snapBeat, col: this.lastMouseCol, - type: this.chartManager.getEditingNoteType(), + type: this.chartManager.getEditingNoteType()!, }) } } diff --git a/src/chart/component/SnapContainer.ts b/src/chart/component/SnapContainer.ts index 30dbfd21..757a45d8 100644 --- a/src/chart/component/SnapContainer.ts +++ b/src/chart/component/SnapContainer.ts @@ -10,17 +10,17 @@ const snapNumbers = { fill: ["#ffffff"], } -const SNAP_COLORS: { [key: number]: number } = { +export const QUANT_COLORS: { [key: number]: number } = { 4: 0xe74827, 8: 0x3d89f7, 12: 0xaa2df4, 16: 0x82e247, - 24: 0xaa2df4, + 24: 0xd82eab, 32: 0xeaa138, - 48: 0xaa2df4, + 48: 0xef8ceb, 64: 0x6be88e, - 96: 0x6be88e, - 192: 0x6be88e, + 96: 0x828282, + 192: 0x828282, } export class SnapContainer extends Container implements ChartRendererComponent { @@ -66,7 +66,7 @@ export class SnapContainer extends Container implements ChartRendererComponent { for (let i = 0; i < 2; i++) { const container = this.children[i] const square = container.children[0] as Graphics - square.tint = SNAP_COLORS[4 / Options.chart.snap] ?? 0x707070 + square.tint = QUANT_COLORS[4 / Options.chart.snap] ?? 0x707070 const text = container.children[1] as BitmapText text.text = "" + diff --git a/src/chart/gameTypes/GameTypeRegistry.ts b/src/chart/gameTypes/GameTypeRegistry.ts index dc38bc87..a93f88e0 100644 --- a/src/chart/gameTypes/GameTypeRegistry.ts +++ b/src/chart/gameTypes/GameTypeRegistry.ts @@ -1,4 +1,5 @@ import { ChartRenderer } from "../ChartRenderer" +import { NoteType } from "../sm/NoteTypes" import { GameLogic } from "./base/GameLogic" import { NotedataParser } from "./base/NotedataParser" import { Notefield } from "./base/Notefield" @@ -13,7 +14,7 @@ export interface GameType { gameLogic: GameLogic parser: NotedataParser notefield: new (renderer: ChartRenderer) => Notefield - editNoteTypes: string[] + editNoteTypes: NoteType[] flipColumns: { horizontal: number[] vertical: number[] diff --git a/src/chart/gameTypes/common/BasicNotedataParser.ts b/src/chart/gameTypes/common/BasicNotedataParser.ts index 30b3b9e4..788911d7 100644 --- a/src/chart/gameTypes/common/BasicNotedataParser.ts +++ b/src/chart/gameTypes/common/BasicNotedataParser.ts @@ -4,6 +4,7 @@ import { isHoldNote, Notedata, NotedataStats, + NoteType, PartialHoldNotedataEntry, PartialNotedata, PartialNotedataEntry, @@ -12,7 +13,7 @@ import { TimingData } from "../../sm/TimingData" import { NotedataParser } from "../base/NotedataParser" import { GameType } from "../GameTypeRegistry" -const NOTE_TYPE_LOOKUP: Record = { +const NOTE_TYPE_LOOKUP: Record = { "1": "Tap", "2": "Hold", "4": "Roll", @@ -21,7 +22,7 @@ const NOTE_TYPE_LOOKUP: Record = { L: "Lift", } -const NOTE_TYPE_LOOKUP_REV: Record = { +const NOTE_TYPE_LOOKUP_REV: Record = { Tap: "1", Hold: "2", Roll: "4", diff --git a/src/chart/gameTypes/dance/DanceNoteTexture.ts b/src/chart/gameTypes/dance/DanceNoteTexture.ts index b31451a9..31299a15 100644 --- a/src/chart/gameTypes/dance/DanceNoteTexture.ts +++ b/src/chart/gameTypes/dance/DanceNoteTexture.ts @@ -67,12 +67,12 @@ export class DanceNoteTexture { }) DanceNoteTexture.arrow_tex = RenderTexture.create({ width: 256, - height: 256, + height: 320, resolution: Options.performance.resolution, }) DanceNoteTexture.lift_tex = RenderTexture.create({ width: 256, - height: 256, + height: 320, resolution: Options.performance.resolution, }) DanceNoteTexture.mine_tex = RenderTexture.create({ @@ -124,7 +124,7 @@ export class DanceNoteTexture { this.arrow_frame = arrow_frame } { - for (let i = 0; i < 8; i++) { + for (let i = 0; i < 10; i++) { const shader_body = Shader.from( this.noop_vert, this.arrow_gradient_frag, @@ -146,7 +146,7 @@ export class DanceNoteTexture { } } { - for (let i = 0; i < 8; i++) { + for (let i = 0; i < 10; i++) { const shader_body = Shader.from( this.noop_vert, this.lift_gradient_frag, @@ -244,7 +244,7 @@ export class DanceNoteTexture { static setArrowTexTime(beat: number, second: number) { if (!this.loaded) return - for (let i = 0; i < 8; i++) { + for (let i = 0; i < 10; i++) { const tapShader: Mesh = DanceNoteTexture.arrow_container.getChildByName("body" + i)! tapShader.shader.uniforms.time = beat @@ -265,10 +265,7 @@ export class DanceNoteTexture { if (note.type == "Mine") { arrow.texture = DanceNoteTexture.mine_tex } else { - const i = Math.min( - getQuantIndex(timingData.getBeatOfMeasure(note.beat)), - 7 - ) + const i = getQuantIndex(timingData.getBeatOfMeasure(note.beat)) arrow.texture = new Texture( note.type == "Lift" ? DanceNoteTexture.lift_tex.baseTexture diff --git a/src/chart/sm/NoteTypes.ts b/src/chart/sm/NoteTypes.ts index e8ad4414..4678c8d3 100644 --- a/src/chart/sm/NoteTypes.ts +++ b/src/chart/sm/NoteTypes.ts @@ -2,10 +2,21 @@ export type Notedata = NotedataEntry[] export type PartialNotedata = PartialNotedataEntry[] +export const NOTE_TYPES = [ + "Tap", + "Hold", + "Roll", + "Mine", + "Lift", + "Fake", +] as const + +export type NoteType = (typeof NOTE_TYPES)[number] + export interface PartialTapNotedataEntry { beat: number col: number - type: string + type: NoteType } export interface PartialHoldNotedataEntry extends PartialTapNotedataEntry { diff --git a/src/data/KeybindData.ts b/src/data/KeybindData.ts index 8cd806e2..d184b580 100644 --- a/src/data/KeybindData.ts +++ b/src/data/KeybindData.ts @@ -766,6 +766,20 @@ export const KEYBIND_DATA: { [key: string]: Keybind } = { }) }, }, + convertNotesLifts: { + label: "Notes to lifts", + bindLabel: "Convert notes to lifts", + combos: [], + disabled: app => + app.chartManager.selection.notes.length == 0 || + app.chartManager.getMode() != EditMode.Edit, + callback: app => { + app.chartManager.modifySelection(note => { + note.type = "Lift" + return note + }) + }, + }, convertTapsFakes: { label: "Taps to fakes", bindLabel: "Convert taps to fakes", @@ -1097,4 +1111,108 @@ export const KEYBIND_DATA: { [key: string]: Keybind } = { Options.debug.showFPS = !Options.debug.showFPS }, }, + noteTypeTap: { + label: "Switch to Taps", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: app => { + app.chartManager.setEditingNoteType("Tap") + }, + }, + noteTypeLift: { + label: "Switch to Lifts", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: app => { + app.chartManager.setEditingNoteType("Lift") + }, + }, + noteTypeMine: { + label: "Switch to Mines", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: app => { + app.chartManager.setEditingNoteType("Mine") + }, + }, + noteTypeFake: { + label: "Switch to Fakes", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: app => { + app.chartManager.setEditingNoteType("Fake") + }, + }, + quant4: { + label: "Switch to 4ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 + }, + }, + quant8: { + label: "Switch to 8ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 2 + }, + }, + quant12: { + label: "Switch to 12ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 3 + }, + }, + quant16: { + label: "Switch to 16ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 4 + }, + }, + quant24: { + label: "Switch to 24ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 6 + }, + }, + quant32: { + label: "Switch to 32ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 8 + }, + }, + quant48: { + label: "Switch to 48ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 12 + }, + }, + quant96: { + label: "Switch to 96ths", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 24 + }, + }, + quant192: { + label: "Switch to 192nds", + combos: [], + disabled: app => !app.chartManager.chartView, + callback: () => { + Options.chart.snap = 1 / 48 + }, + }, } diff --git a/src/data/MenubarData.ts b/src/data/MenubarData.ts index eb366877..dc9d4a4f 100644 --- a/src/data/MenubarData.ts +++ b/src/data/MenubarData.ts @@ -329,6 +329,10 @@ export const MENUBAR_DATA: { [key: string]: MenuMain } = { type: "selection", id: "convertNotesMines", }, + { + type: "selection", + id: "convertNotesLifts", + }, { type: "selection", id: "convertTapsFakes", diff --git a/src/gui/widget/NoteLayoutWidget.ts b/src/gui/widget/NoteLayoutWidget.ts index c77a87db..6233c470 100644 --- a/src/gui/widget/NoteLayoutWidget.ts +++ b/src/gui/widget/NoteLayoutWidget.ts @@ -6,21 +6,17 @@ import { Texture, } from "pixi.js" import { EditMode } from "../../chart/ChartManager" +import { QUANT_COLORS } from "../../chart/component/SnapContainer" import { Chart } from "../../chart/sm/Chart" import { isHoldNote } from "../../chart/sm/NoteTypes" import { BetterRoundedRect } from "../../util/BetterRoundedRect" import { EventHandler } from "../../util/EventHandler" import { clamp, lerp, unlerp } from "../../util/Math" import { Options } from "../../util/Options" -import { destroyChildIf, getQuantIndex } from "../../util/Util" +import { destroyChildIf, getDivision } from "../../util/Util" import { Widget } from "./Widget" import { WidgetManager } from "./WidgetManager" -const QUANT_COLORS = [ - 0xe74827, 0x3d89f7, 0xaa2df4, 0x82e247, 0xaa2df4, 0xeaa138, 0xaa2df4, - 0x6be88e, 0x6be88e, 0x6be88e, -] - export class NoteLayoutWidget extends Widget { barContainer = new ParticleContainer( 1500, @@ -223,7 +219,7 @@ export class NoteLayoutWidget extends Widget { let t = unlerp(0, lastBeat, note.beat) if (Options.chart.CMod) t = unlerp(songOffset, lastSecond, note.second) obj.y = t * height - obj.tint = QUANT_COLORS[getQuantIndex(note.beat)] + obj.tint = QUANT_COLORS[getDivision(note.beat)] if (note.type == "Mine") obj.tint = 0x808080 childIndex++ if (isHoldNote(note)) { diff --git a/src/gui/window/KeybindWindow.ts b/src/gui/window/KeybindWindow.ts index 2293e9d0..72e7fbf8 100644 --- a/src/gui/window/KeybindWindow.ts +++ b/src/gui/window/KeybindWindow.ts @@ -8,11 +8,45 @@ import { Dropdown } from "../element/Dropdown" import { KeyComboWindow } from "./KeyComboWindow" import { Window } from "./Window" +interface KeybindInserts { + ids: string[] + after?: string +} + const KEYBIND_BLACKLIST = ["cut", "copy", "paste", "undo", "redo", "delete"] -const KEYBIND_INSERTS: Record = { - edit: ["previousNoteType", "nextNoteType"], - view: ["playback", "selectRegion"], - debug: ["showFPSCounter", "showDebugTimers"], +const KEYBIND_INSERTS: Record = { + edit: [ + { + ids: [ + "previousNoteType", + "nextNoteType", + "noteTypeTap", + "noteTypeMine", + "noteTypeFake", + "noteTypeLift", + "quant4", + "quant8", + "quant12", + "quant16", + "quant24", + "quant32", + "quant48", + "quant96", + "quant192", + ], + after: "mousePlacement", + }, + ], + view: [ + { + ids: ["playback", "selectRegion"], + }, + ], + debug: [ + { + ids: ["showFPSCounter", "showDebugTimers"], + }, + ], } export class KeybindWindow extends Window { @@ -154,10 +188,15 @@ export class KeybindWindow extends Window { }) Object.keys(KEYBIND_INSERTS).forEach(id => { if (GROUPS[id] === undefined) GROUPS[id] = [] - GROUPS[id].unshift(...KEYBIND_INSERTS[id]) - KEYBIND_INSERTS[id].forEach(option => { - const idx = missingKeybindTest.indexOf(option) - if (idx != -1) missingKeybindTest.splice(idx, 1) + KEYBIND_INSERTS[id].forEach(insert => { + const insertIndex = !insert.after + ? 0 + : GROUPS[id].findIndex(id => insert.after == id) + 1 ?? 0 + GROUPS[id].splice(insertIndex, 0, ...insert.ids) + insert.ids.forEach(option => { + const idx = missingKeybindTest.indexOf(option) + if (idx != -1) missingKeybindTest.splice(idx, 1) + }) }) }) KEYBIND_BLACKLIST.forEach(option => { diff --git a/src/util/Ascii85.ts b/src/util/Ascii85.ts index c00b0f5a..953f2baa 100644 --- a/src/util/Ascii85.ts +++ b/src/util/Ascii85.ts @@ -1,5 +1,5 @@ -import { PartialNotedata, isHoldNote } from "../chart/sm/NoteTypes" -import { TimingEventProperty, TimingEvent } from "../chart/sm/TimingTypes" +import { NoteType, PartialNotedata, isHoldNote } from "../chart/sm/NoteTypes" +import { TimingEvent, TimingEventProperty } from "../chart/sm/TimingTypes" import { roundDigit } from "./Math" const _a85chars = Array(85) @@ -151,6 +151,8 @@ export function packValue(value: number): number[] { return bytes } +const noteTypeOrder: NoteType[] = ["Hold", "Mine", "Roll", "Lift", "Fake"] + export function decodeNotes(data: string): PartialNotedata | undefined { if (data.startsWith("ArrowVortex:notes:")) { const decoded = a85decode(data.slice(18)) @@ -169,7 +171,7 @@ export function decodeNotes(data: string): PartialNotedata | undefined { const type = data_arr.shift() if (type == undefined || type > 4) continue - const noteType = ["Hold", "Mine", "Roll", "Lift", "Fake"][type] + const noteType = noteTypeOrder[type] if (start == end) { if (noteType == "Hold" || noteType == "Roll") continue noteList.push({