From ab487c5e807dd8f419d63baa44d229d6e3970338 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Tue, 1 Jul 2014 19:32:03 -0400 Subject: [PATCH] Replaced native cursors with skinnable ones. - Added support for both the new (2013+) and old cursor styles. These can be toggled on/off in the configuration file. - Added cursor images by XinCrin (old style) and teinecthel (new style). Other changes: - Refactoring: Created a "Utils" module, containing methods and constants that didn't belong in the "Options" state and some duplicated drawing methods. - Mouse is now grabbed during gameplay. - Removed 'optionsChanged' switch, simplifying adding new options. Signed-off-by: Jeffrey Han --- CREDITS.md | 1 + res/cursor.png | Bin 1662 -> 5879 bytes res/cursor2.png | Bin 0 -> 6422 bytes res/cursormiddle.png | Bin 0 -> 565 bytes res/cursortrail.png | Bin 0 -> 2242 bytes res/cursortrail2.png | Bin 0 -> 4022 bytes src/itdelatrisu/opsu/GameScore.java | 2 +- src/itdelatrisu/opsu/OsuGroupNode.java | 12 +- src/itdelatrisu/opsu/OsuParser.java | 7 +- src/itdelatrisu/opsu/Utils.java | 369 ++++++++++++++++++ src/itdelatrisu/opsu/objects/Circle.java | 14 +- src/itdelatrisu/opsu/objects/Slider.java | 32 +- src/itdelatrisu/opsu/objects/Spinner.java | 3 +- src/itdelatrisu/opsu/states/Game.java | 22 +- .../opsu/states/GamePauseMenu.java | 8 +- src/itdelatrisu/opsu/states/GameRanking.java | 16 +- src/itdelatrisu/opsu/states/MainMenu.java | 14 +- src/itdelatrisu/opsu/states/MainMenuExit.java | 18 +- src/itdelatrisu/opsu/states/Options.java | 223 +++-------- src/itdelatrisu/opsu/states/SongMenu.java | 48 +-- 20 files changed, 523 insertions(+), 266 deletions(-) create mode 100644 res/cursor2.png create mode 100644 res/cursormiddle.png create mode 100644 res/cursortrail.png create mode 100644 res/cursortrail2.png create mode 100644 src/itdelatrisu/opsu/Utils.java diff --git a/CREDITS.md b/CREDITS.md index 94d8aca3..be12ed80 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -17,6 +17,7 @@ The images included in opsu! belong to their respective authors. * pictuga - "osu! web" * sherrie__fay * kouyang +* teinecthel Projects -------- diff --git a/res/cursor.png b/res/cursor.png index 79c21510dc832113f71926811ab0960e866fc37f..f8438108a9ca9bf0c0312b219f5574013e9b08e1 100644 GIT binary patch literal 5879 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 N002ovPDHLkV1lkTUZem3 literal 1662 zcmV-^27&pBP)bomMxJKqWfx(vs&{A9g8=f~2*EW#RHo~<) zla6&u#1DwvL_h?@f^}dmlPVVjI524h?yvi2O|(D{+=6dG>u};h%h5ED=-O)}WNY$h zc54x98CQikkSu~`ftE?*X$B;s8+;4C4luroa8)FrGtTTtAN>8&GeDsc^@0oL6G*23qvFV*_p{VJ9;f z&0#W!=^SQrn5K}WkOI_D*HB$TzJk?x*hK~Pz`-4$jj^b11Ji|65=0E+b>5rZOu)_p zc(xDo`*8OgcyI_04&eR*78#5IDkzt*T*BEg9Q^{v@8JCsRzMjzqJU?eQ>J58Rqz@u zAcmx=#6+LRuXjE8Bl!9WJbnUyeFTpW;h~vM7i8H)j4@gixzF?EDZG0PFZbZ5SMalg z-~Bcq`^@l3T~fP|B7F=PgX@AnVxaASe-Gb(3(p?IQ?pq7Nt~OrUOS3HLim`uy>sW_ z03Ph0o!#GuyEC}$;JFF$(fN=pTE{%7V-a(`?+wVn_ihg3L%90{9)AnZo{Id9{ryAA zGU4*l;CJ(xqHr>qEbP5|+0)b0vF|!6$O|YhLp-z|G>hpeVo+nSI7eDnG6ETebaPV} z9l%#l;4k07v+v;VW^eByd7e=evQv78t7!!99k*_c@04XyL$!p{C7kC_H8DkNQ0*Ez z<1iwoFR7Bov`ZnG!|V_qJc7q&u{fkGM^qJ`TxBU#)yT}}4<5nOui@bwW-g?#HQVk2 zA{ZqQNVjFyWFh)z`*8mdz7g-|RF&|#t1OL-Mz`-7^XCJ&KZj`si3=asS5)H1>(wqG z1QMgKU|}YcJ-jy`HTzA=dnilM#p3oJ+)iO^Vg+T35gVGwrmGE!q>zqbBIEIxw&jbL zDv~5g77LcSvGGYDaJ9W&Yf#pHIRWp%d%2<4?qC^IH~l=G8jD9=K(1xE!dm@e0!gB} zURNt9Yp8wjweDc4#|-p=4tjZ4L3IxQ^Lf5xJnn{;`s5N(jPdf}!{sGh)KK}|W~>9N z>xNMz(AChcAYa1SDZE3quZ?RZR=sHW+$wy5;o2|LHIDQGwe}sR$g4e&o`wOV5pcT3`_%fX+zQYD3 zBsd>lDu9i8SHXAC3P=KGuPl))bCM+KmD->riRx%n`qk=}m+<^w@WTuE_kZE{Gx$)1 zpl<6}iW@PYU9bRR5a0ERP;4X;1E#5tlB84TDp{{j&YR}v zpWufd;h!(y#asC86fQ2ID#Ckw3EBXiv7n0Ul}VG1)zu|6_#(EJC?@dmwt(UkPTs(q z1Gu-ixLC|!>VX2vb6B3j`(rqI3rELratSLscAIp}wPzFO3DYA3A&u zZ6r{;E0w$5%JR03=8p2**D>vybR5BTGtS9PQ;`@8)-qlfJA+AVrN(_^Fd6Ek^lez diff --git a/res/cursor2.png b/res/cursor2.png new file mode 100644 index 0000000000000000000000000000000000000000..5fef2fd7d90a2ea9d1a7c0d7cd626adb545ae8d7 GIT binary patch literal 6422 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 literal 0 HcmV?d00001 diff --git a/res/cursormiddle.png b/res/cursormiddle.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1e1b5be671bd7e6a71ffbcca8fe58d9c3b9693 GIT binary patch literal 565 zcmV-50?Pe~P)QcSXqdOjiSd!jVaj?Wi`uL%htz=ow8>kS&;9D*bP%yD2Xy@ z7{~dUW6E>i7-^<%@7;6G|99U#_nbT4vPj99NZFA{`H4v7&dW>9>Rr0wSfu_?q%myZ zCyw|7kt+0XM5NT?MPU`YBGnpLyZM}M+7oG2VVjQ5r3)EUQMZmBp3Jf1CvGE^ZbpY5 z4m^()FsPtT-&@unU%T|ZS4IBX{oAnGhW16GIv?T596NsEh)0JW4m^)7vywSFRM@A2 z$VwtJrO$D3{IIVi1%HHibm-xb3XhzyWmY7Buh~k=qzYm>;y~suQ@LT#IDUw;p(PaI%$=;6Tg?36ha-?kh>pkrb5;yvkt`lp!({0f{Q z-csS~yd9oLd5{y5ug@`CefA4Df0gcMk^YxHWUgKZ}00000NkvXXu0mjf D9!V3E literal 0 HcmV?d00001 diff --git a/res/cursortrail.png b/res/cursortrail.png new file mode 100644 index 0000000000000000000000000000000000000000..6379fedd215325e9571f71dc78cd00947f15a359 GIT binary patch literal 2242 zcmV;z2tD_SP)63ocNl zs8_y71yU^tkSTbX@px7*AhvcTQafF<2;Y+raZ)RfS&?fO_)|d1+}H-%i1fZRxR5U)Uu5onjtT&E$1iy z=5e0qb%f{l{!RS_1lD0U1+|wHD)`60EhSLyO>KqY*29$rUk&D){xY@3=pP%JjX>ZBhv{HC$#8KKkT$bDZxPE2TQ-SYmOPHtGcLz-?Z$Bz50ED?K?IKulx4Om^QGex)i8mO5gxJJSUrcOp$JEzt zA+^fdnXXv<&4@E6G;A>-A*vZyBNh1isjM}uRuk44 z6`Pfm*x^fw-Hbr5tkw6EqQ(^!+Z4b%lIm-G1EqJ;ZmM2%TPrjKHKXy0W*ASExv4!# zWez&#AX9=06WAS0SCn_CYaHETgS|(4(-RXl0Bt}tAr6d1Q)R#0?@!8lCmzTZ7aQGS z^>uqt4LFca1m}2Qs$^aJ`zw=N<%PMcjM2`)!yINxltj{=a->5U)U(FK4tGMU^QGi| zU!Q!}-z$%JQu2@|fr!dOzJ3JGITEtY78BcTU1Fm>q^@YWl;2Y`O#okr}VHJdXT@Y6N!0WIZ7FdlT|7fZwa?m8X3PdCHrV z?|EbL1kN4j12`hG(b^+xE!|?f1>j{}>T4VermhiNUhr`dEt6{V_hzT|czJFa&1L$q z?!9dzR_3G<73;lmZD&PN+vADLcc`d0Dw`|&lks;kqq6a zsyDP{dap8T=rNnizdBT~3f@`*i!1-D`f5eH*yIk&x2T#sC69UU$+K&|&0MSote?=y z%q0L{3IH5&f#B|_Jc%@F_V&udl^mG(zP(etQ3A^>38xjNNOpYCWC}jW&T8#{`PuXO zDbSt{vDw`L=uWv;iO3_qq&x$4&mles>=(X7=Hggf@B!SL$b4E6muKK2ClSX233 z2pj;gzc+J^N(Xu~pTb?v2AKCL*^KLdtH4Q|-J^M}z(Sp{(lQK2{vJEWc~_e0p>&9y zfUc)==+O5vp9Ka0n}Wj0%z5UsKb|?qeW?4%gwlQT7_L895s~%o9`UAL;&ERK?mvG@ zPliV0!z$>yD>f?aVuuozwG}aWkOPs>)!$~$L)r5Hzo5q$Jbd~C<}{qEN0sIH><`1bXQv%06 zh83O#@F}FjQFo6#s3c?)24jPFK-PMD#jov2b!{oV8imUceP|qak6A&_=+jWzlSED@ zkkb(~Lp}0sX9R@#KLcE}coS-H{R-N~3BO z@5`QMMZ1m@1SP327 z@{kMXcnyK^5VcGTc7^t+x|XG!jT0vboY~C2B0i<}V2ZF;R`BD9-2mI814ZOMq`*5m zB$Rc9;m0A_2&MPByJQ^|wkO1IY` zl-NRWI}|67qYLmMy#BEO=pOwT&gsP{B6bki1C`l_Gnd)r@xG%f&qJ3tV?TWz69BfS z@N72}cO}I(e^Ts1|6B(@uG4cJ{RMjr1(N}$ksbDi;~TA*FtAag&FTF#+Fgzr^EK_s zetKSd>Pa(~!k|`UL&rzO^`VzN z%pRJPWH$dHA*FRT^i zhqVHxgsByU1i(`kZx(170pR279vt-zP$R-2}gKk?l2J;DL!Gmz0!Q(v7naSuP&8WHy3p@k()U`a8e0}Y1Y&~y$pU3q| z$HH-W8rbK(2>H}i)9Bc7Ueodf?2FHTz54%ZefmH9Pr%HvxGipr+fQTr7gA6M6}xyR QfdBvi07*qoM6N<$g6Do*OaK4? literal 0 HcmV?d00001 diff --git a/res/cursortrail2.png b/res/cursortrail2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b644d0a941dfa28e761431f812590864073dd07 GIT binary patch literal 4022 zcmV;n4@vNeP)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} z000ExNkl)DQZ4 zWniUm^vR8HyiVyJo4)+{DE|nAlSy+u#`lJlJ3w%7zD?MIacA;;E0BEB5}d%;x&$yl32*LpfcPxmLM<`8>4_iqFK{)t(a$qtjb*y3kj)1zB zk{e%P!C3ls3gaD)ZJt-TX6t_g99)NQ=Cb&IaliM6bB@9JGlV_0d@|Qg`>n8 zZL9!Q94nHOJamhjWiGV?kewM8h=M!6!hSM;BSZ<}|X0t4l?TnipVDe9OoDA3lOcY_7Mc%Zzm~s5*x2ws<0%=|Bv4T9q zIMw3^a5VxKyAaXYz-o}~mU=xZ0`3*ftm^L^%RCqtdnTfD>gAKkQ@+bN`$PA7R{)>>`mNwK+QTH%sGgo zOC6XAJ&9CY9m%hu(9A5fhxCg;Cj)h==MkpW6m??g05?jUls#mbsHw`_%`2V=g;`BN zp5W)^atEU##8_pdN7B*eY)VwKrwVLA6{EG506-kK8tGn#`WfyF+tYX=RLuX74HM5S z;+@mUsieB0jlR#SWbQ^$i?XRX)6T%&WHl)kdlUlqTFiO8wghVd6s+vfXwme5Y@53} zavoEx)ax*2+PT@OC!=s0wbasCHL)P8Q{ZM0jwn+wgDIru_d3q?8V@dlqFy%BjSCM~ z06_@UVhlwR>q;w{Z$$K?4h!W0W!4E}rU$YZ5KlC=%6>`Y%o`DXuQ1k1S7_iOm;0E2 zKs9R;FP?j~iJpTd?CJ>3V&s1VdIJkx3=qcAX3Nk(oCpNCt|vw4>BQ**GOp^mFDG)If0T5(Ztc4U0Cpm6~!;kW=yFQbJq<2@^} zK-vR7BmREwZ-WG|Ob!tecu~^$}IU>gXr0{`3|By{^)#fehqt%TyP$3V=KA z7G*qjvfb9@*=wTRx`E$wGY%kn={tK8fG$~*h4a<1N(nTXj<}h9IFu=BjD;!aQPf!% zvufLZ*3p#QE1Klo(Go}=hIOsU=}e<1XT>RqWkWkK{7AzCBD1RK!HhfxROhaH`w~3N z-TZupI+B}$_~PWf01Xn0Zs=L`9`6BYAa|m-FGgW{ebg|>2#j`~<@3Nh1C-q13~uu# z8?2ITC4d?E&7Mo${dJQbVDvV{sO!?!`1mZ0Ca%N5JMn_y7O^ literal 0 HcmV?d00001 diff --git a/src/itdelatrisu/opsu/GameScore.java b/src/itdelatrisu/opsu/GameScore.java index 5b4e69fd..7ef362cb 100644 --- a/src/itdelatrisu/opsu/GameScore.java +++ b/src/itdelatrisu/opsu/GameScore.java @@ -509,7 +509,7 @@ public class GameScore { // header & "Ranking" text float rankingHeight = (rankingImage.getHeight() * 0.75f) + 3; - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRect(0, 0, width, rankingHeight); rankingImage.draw((width * 0.97f) - rankingImage.getWidth(), 0); diff --git a/src/itdelatrisu/opsu/OsuGroupNode.java b/src/itdelatrisu/opsu/OsuGroupNode.java index a9d3c9d4..83c67a13 100644 --- a/src/itdelatrisu/opsu/OsuGroupNode.java +++ b/src/itdelatrisu/opsu/OsuGroupNode.java @@ -18,8 +18,6 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.states.Options; - import java.util.ArrayList; import java.util.Comparator; @@ -128,21 +126,21 @@ public class OsuGroupNode implements Comparable { textColor = Color.white; } else { xOffset = bg.getWidth() * 0.05f; - bg.draw(x + xOffset, y, Options.COLOR_BLUE_BUTTON); + bg.draw(x + xOffset, y, Utils.COLOR_BLUE_BUTTON); } osu = osuFiles.get(osuFileIndex); } else { - bg.draw(x, y, Options.COLOR_ORANGE_BUTTON); + bg.draw(x, y, Utils.COLOR_ORANGE_BUTTON); osu = osuFiles.get(0); } float cx = x + (bg.getWidth() * 0.05f) + xOffset; float cy = y + (bg.getHeight() * 0.2f) - 3; - Options.FONT_MEDIUM.drawString(cx, cy, osu.title, textColor); - Options.FONT_DEFAULT.drawString(cx, cy + Options.FONT_MEDIUM.getLineHeight() - 4, String.format("%s // %s", osu.artist, osu.creator), textColor); + Utils.FONT_MEDIUM.drawString(cx, cy, osu.title, textColor); + Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 4, String.format("%s // %s", osu.artist, osu.creator), textColor); if (expanded) - Options.FONT_BOLD.drawString(cx, cy + Options.FONT_MEDIUM.getLineHeight() + Options.FONT_DEFAULT.getLineHeight() - 8, osu.version, textColor); + Utils.FONT_BOLD.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() - 8, osu.version, textColor); } /** diff --git a/src/itdelatrisu/opsu/OsuParser.java b/src/itdelatrisu/opsu/OsuParser.java index 6ff0f07b..89c2fddb 100644 --- a/src/itdelatrisu/opsu/OsuParser.java +++ b/src/itdelatrisu/opsu/OsuParser.java @@ -18,15 +18,12 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.states.Options; - import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; @@ -104,8 +101,6 @@ public class OsuParser { try (BufferedReader in = new BufferedReader(new FileReader(file))) { - // copy the default combo colors - osu.combo = Arrays.copyOf(Options.DEFAULT_COMBO, Options.DEFAULT_COMBO.length); // initialize timing point list osu.timingPoints = new ArrayList(); @@ -403,7 +398,7 @@ public class OsuParser { // if no custom colors, use the default color scheme if (osu.combo == null) - osu.combo = Options.DEFAULT_COMBO; + osu.combo = Utils.DEFAULT_COMBO; // add tags if (!tags.isEmpty()) { diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java new file mode 100644 index 00000000..4417187c --- /dev/null +++ b/src/itdelatrisu/opsu/Utils.java @@ -0,0 +1,369 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014 Jeffrey Han + * + * opsu! is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * opsu! is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with opsu!. If not, see . + */ + +package itdelatrisu.opsu; + +import itdelatrisu.opsu.states.Options; + +import java.awt.Font; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Cursor; +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.TrueTypeFont; +import org.newdawn.slick.imageout.ImageOut; +import org.newdawn.slick.state.StateBasedGame; +import org.newdawn.slick.util.Log; + +/** + * Contains miscellaneous utilities. + */ +public class Utils { + /** + * Game colors. + */ + public static final Color + COLOR_BLACK_ALPHA = new Color(0, 0, 0, 0.5f), + COLOR_BLUE_DIVIDER = new Color(49, 94, 237), + COLOR_BLUE_BACKGROUND = new Color(74, 130, 255), + COLOR_BLUE_BUTTON = new Color(50, 189, 237), + COLOR_ORANGE_BUTTON = new Color(230, 151, 87), + COLOR_GREEN_OBJECT = new Color(26, 207, 26), + COLOR_BLUE_OBJECT = new Color(46, 136, 248), + COLOR_RED_OBJECT = new Color(243, 48, 77), + COLOR_ORANGE_OBJECT = new Color(255, 200, 32); + + /** + * The default map colors, used when a map does not provide custom colors. + */ + public static final Color[] DEFAULT_COMBO = { + COLOR_GREEN_OBJECT, COLOR_BLUE_OBJECT, + COLOR_RED_OBJECT, COLOR_ORANGE_OBJECT + }; + + /** + * Game fonts. + */ + public static TrueTypeFont + FONT_DEFAULT, FONT_BOLD, + FONT_XLARGE, FONT_LARGE, FONT_MEDIUM, FONT_SMALL; + + /** + * Back button (shared by other states). + */ + private static GUIMenuButton backButton; + + /** + * Cursor image and trail. + */ + private static Image cursor, cursorTrail, cursorMiddle; + + /** + * Last cursor coordinates. + */ + private static int lastX = -1, lastY = -1; + + /** + * Stores all previous cursor locations to display a trail. + */ + private static LinkedList + cursorX = new LinkedList(), + cursorY = new LinkedList(); + + // game-related variables + private static GameContainer container; +// private static StateBasedGame game; + private static Input input; + + // This class should not be instantiated. + private Utils() {} + + /** + * Initializes game settings and class data. + * @param container the game container + * @param game the game object + * @throws SlickException + */ + public static void init(GameContainer container, StateBasedGame game) + throws SlickException { + Utils.container = container; +// Utils.game = game; + Utils.input = container.getInput(); + + // game settings + container.setTargetFrameRate(Options.getTargetFPS()); + container.setMusicVolume(Options.getMusicVolume()); + container.setShowFPS(false); + container.getInput().enableKeyRepeat(); + container.setAlwaysRender(true); + + // hide the cursor + try { + Cursor emptyCursor = new Cursor(1, 1, 0, 0, 1, BufferUtils.createIntBuffer(1), null); + container.setMouseCursor(emptyCursor, 0, 0); + } catch (LWJGLException e) { + Log.error("Failed to set the cursor.", e); + } + + // load cursor images + if (Options.isNewCursorEnabled()) { + // load new cursor type + try { + cursorMiddle = new Image("cursormiddle.png"); + cursor = new Image("cursor.png"); + cursorTrail = new Image("cursortrail.png"); + } catch (Exception e) { + // optional + } + } + if (cursorMiddle == null) { + // load old cursor type + cursor = new Image("cursor2.png"); + cursorTrail = new Image("cursortrail2.png"); + } + + // create fonts + int height = container.getHeight(); + float fontBase; + if (height <= 600) + fontBase = 9f; + else if (height < 800) + fontBase = 10f; + else if (height <= 900) + fontBase = 12f; + else + fontBase = 14f; + + Font font = new Font("Lucida Sans Unicode", Font.PLAIN, (int) (fontBase * 4 / 3)); + FONT_DEFAULT = new TrueTypeFont(font, false); + FONT_BOLD = new TrueTypeFont(font.deriveFont(Font.BOLD), false); + FONT_XLARGE = new TrueTypeFont(font.deriveFont(fontBase * 4), false); + FONT_LARGE = new TrueTypeFont(font.deriveFont(fontBase * 2), false); + FONT_MEDIUM = new TrueTypeFont(font.deriveFont(fontBase * 3 / 2), false); + FONT_SMALL = new TrueTypeFont(font.deriveFont(fontBase), false); + + // back button + Image back = new Image("menu-back.png"); + float scale = (height * 0.1f) / back.getHeight(); + back = back.getScaledCopy(scale); + backButton = new GUIMenuButton(back, + back.getWidth() / 2f, + height - (back.getHeight() / 2f)); + } + + /** + * Returns the 'back' GUIMenuButton. + */ + public static GUIMenuButton getBackButton() { return backButton; } + + /** + * Draws an image based on its center with a color filter. + * @param img the image to draw + * @param x the center x coordinate + * @param y the center y coordinate + * @param color the color filter to apply + */ + public static void drawCentered(Image img, float x, float y, Color color) { + img.draw(x - (img.getWidth() / 2f), y - (img.getHeight() / 2f), color); + } + + /** + * Draws an animation based on its center. + * @param anim the animation to draw + * @param x the center x coordinate + * @param y the center y coordinate + */ + public static void drawCentered(Animation anim, float x, float y) { + anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f)); + } + + /** + * Draws the cursor. + */ + public static void drawCursor() { + // TODO: use an image buffer + + int x = input.getMouseX(); + int y = input.getMouseY(); + + int removeCount = 0; + int FPSmod = (Options.getTargetFPS() / 60); + + // if middle exists, add all points between cursor movements + if (cursorMiddle != null) { + if (lastX < 0) { + lastX = x; + lastY = y; + return; + } + addCursorPoints(lastX, lastY, x, y); + lastX = x; + lastY = y; + + removeCount = (cursorX.size() / (6 * FPSmod)) + 1; + } + + // else, sample one point at a time + else { + cursorX.add(x); + cursorY.add(y); + + int max = 10 * FPSmod; + if (cursorX.size() > max) + removeCount = cursorX.size() - max; + } + + // remove points from the lists + for (int i = 0; i < removeCount && !cursorX.isEmpty(); i++) { + cursorX.remove(); + cursorY.remove(); + } + + // draw a fading trail + float alpha = 0f; + float t = 2f / cursorX.size(); + Iterator iterX = cursorX.iterator(); + Iterator iterY = cursorY.iterator(); + while (iterX.hasNext()) { + int cx = iterX.next(); + int cy = iterY.next(); + alpha += t; + cursorTrail.setAlpha(alpha); +// if (cx != x || cy != y) + cursorTrail.drawCentered(cx, cy); + } + cursorTrail.drawCentered(x, y); + + // draw the other components + cursor.drawCentered(x, y); + if (cursorMiddle != null) + cursorMiddle.drawCentered(x, y); + } + + /** + * Adds all points between (x1, y1) and (x2, y2) to the cursor point lists. + * @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java + */ + private static void addCursorPoints(int x1, int y1, int x2, int y2) { + // delta of exact value and rounded value of the dependent variable + int d = 0; + int dy = Math.abs(y2 - y1); + int dx = Math.abs(x2 - x1); + + int dy2 = (dy << 1); // slope scaling factors to avoid floating + int dx2 = (dx << 1); // point + int ix = x1 < x2 ? 1 : -1; // increment direction + int iy = y1 < y2 ? 1 : -1; + + int k = 5; // sample size + if (dy <= dx) { + for (int i = 0; ; i++) { + if (i == k) { + cursorX.add(x1); + cursorY.add(y1); + i = 0; + } + if (x1 == x2) + break; + x1 += ix; + d += dy2; + if (d > dx) { + y1 += iy; + d -= dx2; + } + } + } else { + for (int i = 0; ; i++) { + if (i == k) { + cursorX.add(x1); + cursorY.add(y1); + i = 0; + } + if (y1 == y2) + break; + y1 += iy; + d += dx2; + if (d > dy) { + x1 += ix; + d -= dy2; + } + } + } + } + + /** + * Draws the FPS at the bottom-right corner of the game container. + * If the option is not activated, this will do nothing. + */ + public static void drawFPS() { + if (!Options.isFPSCounterEnabled()) + return; + + String fps = String.format("FPS: %d", container.getFPS()); + FONT_DEFAULT.drawString( + container.getWidth() - 15 - FONT_DEFAULT.getWidth(fps), + container.getHeight() - 15 - FONT_DEFAULT.getHeight(fps), + fps, Color.white + ); + } + + /** + * Takes a screenshot. + * @return true if successful + */ + public static boolean takeScreenShot() { + // TODO: should this be threaded? + try { + // create the screenshot directory + if (!Options.SCREENSHOT_DIR.isDirectory()) { + if (!Options.SCREENSHOT_DIR.mkdir()) + return false; + } + + // create file name + SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss"); + String file = date.format(new Date()); + + SoundController.playSound(SoundController.SOUND_SHUTTER); + + // copy the screen + Image screen = new Image(container.getWidth(), container.getHeight()); + container.getGraphics().copyArea(screen, 0, 0); + ImageOut.write(screen, String.format("%s%sscreenshot_%s.%s", + Options.SCREENSHOT_DIR.getName(), File.separator, + file, Options.getScreenshotFormat()), false + ); + screen.destroy(); + } catch (SlickException e) { + Log.warn("Failed to take a screenshot.", e); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index a90616c8..167426e0 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -21,6 +21,7 @@ package itdelatrisu.opsu.objects; import itdelatrisu.opsu.GameScore; import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.OsuHitObject; +import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Options; @@ -120,21 +121,14 @@ public class Circle { if (timeDiff >= 0) { float approachScale = 1 + (timeDiff * 2f / game.getApproachTime()); - drawCentered(approachCircle.getScaledCopy(approachScale), hitObject.x, hitObject.y, color); - drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white); - drawCentered(hitCircle, hitObject.x, hitObject.y, color); + Utils.drawCentered(approachCircle.getScaledCopy(approachScale), hitObject.x, hitObject.y, color); + Utils.drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white); + Utils.drawCentered(hitCircle, hitObject.x, hitObject.y, color); score.drawSymbolNumber(hitObject.comboNumber, hitObject.x, hitObject.y, hitCircle.getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight()); } } - /** - * Draws an image based on its center with a color filter. - */ - private void drawCentered(Image img, float x, float y, Color color) { - img.draw(x - (img.getWidth() / 2f), y - (img.getHeight() / 2f), color); - } - /** * Calculates the circle hit result. * @param time the hit object time (difference between track time) diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index 1503650f..6a110483 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.GameScore; import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.OsuFile; import itdelatrisu.opsu.OsuHitObject; +import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Options; @@ -240,9 +241,9 @@ public class Slider { // draw overlay and hit circle for (int i = curveX.length - 1; i >= 0; i--) - drawCentered(hitCircleOverlay, curveX[i], curveY[i], Color.white); + Utils.drawCentered(hitCircleOverlay, curveX[i], curveY[i], Color.white); for (int i = curveX.length - 1; i >= 0; i--) - drawCentered(hitCircle, curveX[i], curveY[i], color); + Utils.drawCentered(hitCircle, curveX[i], curveY[i], color); } } @@ -312,12 +313,12 @@ public class Slider { // end circle int lastIndex = hitObject.sliderX.length - 1; - drawCentered(hitCircleOverlay, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], Color.white); - drawCentered(hitCircle, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], color); + Utils.drawCentered(hitCircleOverlay, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], Color.white); + Utils.drawCentered(hitCircle, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], color); // start circle - drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white); - drawCentered(hitCircle, hitObject.x, hitObject.y, color); + Utils.drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white); + Utils.drawCentered(hitCircle, hitObject.x, hitObject.y, color); if (sliderClicked) ; // don't draw current combo number if already clicked else @@ -335,12 +336,13 @@ public class Slider { if (timeDiff >= 0) { // approach circle float approachScale = 1 + (timeDiff * 2f / game.getApproachTime()); - drawCentered(Circle.getApproachCircle().getScaledCopy(approachScale), hitObject.x, hitObject.y, color); + Utils.drawCentered(Circle.getApproachCircle().getScaledCopy(approachScale), + hitObject.x, hitObject.y, color); } else { float[] c = bezier.pointAt(getT(trackPosition, false)); // slider ball - drawCentered(sliderBall, c[0], c[1]); + Utils.drawCentered(sliderBall, c[0], c[1]); // follow circle if (followCircleActive) @@ -348,20 +350,6 @@ public class Slider { } } - /** - * Draws an image based on its center with a color filter. - */ - private void drawCentered(Image img, float x, float y, Color color) { - img.draw(x - (img.getWidth() / 2f), y - (img.getHeight() / 2f), color); - } - - /** - * Draws an animation based on its center with a color filter. - */ - private static void drawCentered(Animation anim, float x, float y) { - anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f)); - } - /** * Calculates the slider hit result. * @param time the hit object time (difference between track time) diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java index 5086cff4..f982ea49 100644 --- a/src/itdelatrisu/opsu/objects/Spinner.java +++ b/src/itdelatrisu/opsu/objects/Spinner.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.GameScore; import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.OsuHitObject; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Options; @@ -127,7 +128,7 @@ public class Spinner { //spinnerOsuImage.drawCentered(width / 2, height / 4); // darken screen - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRect(0, 0, width, height); if (timeDiff > 0) diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index f758aeaf..d3a4c766 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -26,6 +26,7 @@ import itdelatrisu.opsu.OsuFile; import itdelatrisu.opsu.OsuHitObject; import itdelatrisu.opsu.OsuTimingPoint; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.objects.Circle; import itdelatrisu.opsu.objects.Slider; import itdelatrisu.opsu.objects.Spinner; @@ -264,8 +265,6 @@ public class Game extends BasicGameState { if (!osu.drawBG(width, height, 0.7f)) g.setBackground(Color.black); - Options.drawFPS(); - int trackPosition = MusicController.getPosition(); if (pauseTime > -1) // returning from pause screen trackPosition = pauseTime; @@ -313,6 +312,9 @@ public class Game extends BasicGameState { warningArrowL.draw(width * 0.75f, height * 0.75f); } } + + Utils.drawFPS(); + Utils.drawCursor(); return; } } @@ -413,7 +415,7 @@ public class Game extends BasicGameState { // returning from pause screen if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) { // darken the screen - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRect(0, 0, width, height); // draw glowing hit select circle and pulse effect @@ -425,6 +427,9 @@ public class Game extends BasicGameState { cursorCirclePulse.setAlpha(1f - pausePulse); cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY); } + + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -589,7 +594,7 @@ public class Game extends BasicGameState { mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY()); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } } @@ -647,6 +652,9 @@ public class Game extends BasicGameState { if (osu == null || osu.objects == null) throw new RuntimeException("Running game with no OsuFile loaded."); + // grab the mouse + container.setMouseGrabbed(true); + // restart the game if (restart != RESTART_FALSE) { // new game @@ -720,6 +728,12 @@ public class Game extends BasicGameState { } } + @Override + public void leave(GameContainer container, StateBasedGame game) + throws SlickException { + container.setMouseGrabbed(false); + } + /** * Skips the beginning of a track. * @return true if skipped, false otherwise diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index e49463c9..dc5e183a 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.GUIMenuButton; import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; @@ -117,13 +118,14 @@ public class GamePauseMenu extends BasicGameState { else g.setBackground(Color.black); - Options.drawFPS(); - // draw buttons if (Game.getRestart() != Game.RESTART_LOSE) continueButton.draw(); retryButton.draw(); backButton.draw(); + + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -149,7 +151,7 @@ public class GamePauseMenu extends BasicGameState { unPause(Game.RESTART_FALSE); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } } diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index cab08835..1c7ae4f6 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -24,6 +24,7 @@ import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.OsuFile; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import org.lwjgl.opengl.Display; import org.newdawn.slick.Color; @@ -100,7 +101,7 @@ public class GameRanking extends BasicGameState { // background if (!osu.drawBG(width, height, 0.7f)) - g.setBackground(Options.COLOR_BLACK_ALPHA); + g.setBackground(Utils.COLOR_BLACK_ALPHA); // ranking screen elements score.drawRankingElements(g, width, height); @@ -118,17 +119,18 @@ public class GameRanking extends BasicGameState { // header text g.setColor(Color.white); - Options.FONT_LARGE.drawString(10, 0, + Utils.FONT_LARGE.drawString(10, 0, String.format("%s - %s [%s]", osu.artist, osu.title, osu.version)); - Options.FONT_MEDIUM.drawString(10, Options.FONT_LARGE.getLineHeight() - 6, + Utils.FONT_MEDIUM.drawString(10, Utils.FONT_LARGE.getLineHeight() - 6, String.format("Beatmap by %s", osu.creator)); // buttons retryButton.draw(); exitButton.draw(); - Options.getBackButton().draw(); + Utils.getBackButton().draw(); - Options.drawFPS(); + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -150,7 +152,7 @@ public class GameRanking extends BasicGameState { game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } } @@ -170,7 +172,7 @@ public class GameRanking extends BasicGameState { } else if (exitButton.contains(x, y)) { SoundController.playSound(SoundController.SOUND_MENUBACK); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); - } else if (Options.getBackButton().contains(x, y)) { + } else if (Utils.getBackButton().contains(x, y)) { MusicController.pause(); MusicController.playAt(Game.getOsuFile().previewTime, true); SoundController.playSound(SoundController.SOUND_MENUBACK); diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index 30bd7600..95d81715 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -23,6 +23,7 @@ import itdelatrisu.opsu.MusicController; import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.OsuGroupNode; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import java.text.SimpleDateFormat; import java.util.Date; @@ -153,8 +154,8 @@ public class MainMenu extends BasicGameState { if (backgroundImage != null) backgroundImage.draw(); else - g.setBackground(Options.COLOR_BLUE_BACKGROUND); - g.setFont(Options.FONT_MEDIUM); + g.setBackground(Utils.COLOR_BLUE_BACKGROUND); + g.setFont(Utils.FONT_MEDIUM); int width = container.getWidth(); int height = container.getHeight(); @@ -173,7 +174,7 @@ public class MainMenu extends BasicGameState { musicPlay.draw(); musicNext.draw(); musicPrevious.draw(); - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRoundRect(width - 168, 54, 148, 5, 4); g.setColor(Color.white); if (!MusicController.isConverting()) @@ -181,7 +182,7 @@ public class MainMenu extends BasicGameState { 148f * MusicController.getPosition() / MusicController.getTrackLength(), 5, 4); // draw text - int lineHeight = Options.FONT_MEDIUM.getLineHeight(); + int lineHeight = Utils.FONT_MEDIUM.getLineHeight(); g.drawString(String.format("Loaded %d songs and %d beatmaps.", Opsu.groups.size(), Opsu.groups.getMapCount()), 25, 25); if (MusicController.isConverting()) @@ -203,7 +204,8 @@ public class MainMenu extends BasicGameState { new SimpleDateFormat("h:mm a").format(new Date())), 25, height - 25 - lineHeight); - Options.drawFPS(); + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -317,7 +319,7 @@ public class MainMenu extends BasicGameState { game.enterState(Opsu.STATE_MAINMENUEXIT); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } diff --git a/src/itdelatrisu/opsu/states/MainMenuExit.java b/src/itdelatrisu/opsu/states/MainMenuExit.java index d01c9ee7..2988cb15 100644 --- a/src/itdelatrisu/opsu/states/MainMenuExit.java +++ b/src/itdelatrisu/opsu/states/MainMenuExit.java @@ -20,6 +20,7 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GUIMenuButton; import itdelatrisu.opsu.Opsu; +import itdelatrisu.opsu.Utils; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; @@ -90,22 +91,23 @@ public class MainMenuExit extends BasicGameState { // draw text float c = container.getWidth() * 0.02f; - Options.FONT_LARGE.drawString(c, c, "Are you sure you want to exit opsu!?"); + Utils.FONT_LARGE.drawString(c, c, "Are you sure you want to exit opsu!?"); // draw buttons yesButton.draw(Color.green); noButton.draw(Color.red); - g.setFont(Options.FONT_XLARGE); + g.setFont(Utils.FONT_XLARGE); g.drawString("1. Yes", - yesButton.getX() - (Options.FONT_XLARGE.getWidth("1. Yes") / 2f), - yesButton.getY() - (Options.FONT_XLARGE.getHeight() / 2f) + yesButton.getX() - (Utils.FONT_XLARGE.getWidth("1. Yes") / 2f), + yesButton.getY() - (Utils.FONT_XLARGE.getHeight() / 2f) ); g.drawString("2. No", - noButton.getX() - (Options.FONT_XLARGE.getWidth("2. No") / 2f), - noButton.getY() - (Options.FONT_XLARGE.getHeight() / 2f) + noButton.getX() - (Utils.FONT_XLARGE.getWidth("2. No") / 2f), + noButton.getY() - (Utils.FONT_XLARGE.getHeight() / 2f) ); - Options.drawFPS(); + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -150,7 +152,7 @@ public class MainMenuExit extends BasicGameState { game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition(Color.black)); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } } diff --git a/src/itdelatrisu/opsu/states/Options.java b/src/itdelatrisu/opsu/states/Options.java index 37d67a9c..bcbcf89c 100644 --- a/src/itdelatrisu/opsu/states/Options.java +++ b/src/itdelatrisu/opsu/states/Options.java @@ -20,9 +20,8 @@ package itdelatrisu.opsu.states; import itdelatrisu.opsu.GUIMenuButton; import itdelatrisu.opsu.Opsu; -import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; -import java.awt.Font; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -39,8 +38,6 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.SlickException; -import org.newdawn.slick.TrueTypeFont; -import org.newdawn.slick.imageout.ImageOut; import org.newdawn.slick.state.BasicGameState; import org.newdawn.slick.state.StateBasedGame; import org.newdawn.slick.state.transition.EmptyTransition; @@ -85,41 +82,6 @@ public class Options extends BasicGameState { */ private static final String OPTIONS_FILE = ".opsu.cfg"; - /** - * Whether or not any changes were made to options. - * If false, the options file will not be modified. - */ - private static boolean optionsChanged = false; - - /** - * Game colors. - */ - public static final Color - COLOR_BLACK_ALPHA = new Color(0, 0, 0, 0.5f), - COLOR_BLUE_DIVIDER = new Color(49, 94, 237), - COLOR_BLUE_BACKGROUND = new Color(74, 130, 255), - COLOR_BLUE_BUTTON = new Color(50, 189, 237), - COLOR_ORANGE_BUTTON = new Color(230, 151, 87), - COLOR_GREEN_OBJECT = new Color(26, 207, 26), - COLOR_BLUE_OBJECT = new Color(46, 136, 248), - COLOR_RED_OBJECT = new Color(243, 48, 77), - COLOR_ORANGE_OBJECT = new Color(255, 200, 32); - - /** - * The default map colors, used when a map does not provide custom colors. - */ - public static final Color[] DEFAULT_COMBO = { - COLOR_GREEN_OBJECT, COLOR_BLUE_OBJECT, - COLOR_RED_OBJECT, COLOR_ORANGE_OBJECT - }; - - /** - * Game fonts. - */ - public static TrueTypeFont - FONT_DEFAULT, FONT_BOLD, - FONT_XLARGE, FONT_LARGE, FONT_MEDIUM, FONT_SMALL; - /** * Game mods. */ @@ -235,12 +197,12 @@ public class Options extends BasicGameState { /** * Port binding. */ - private static int port = 0; + private static int port = 49250; /** - * Back button (shared by other states). + * Whether or not to use the new cursor type. */ - private static GUIMenuButton backButton; + private static boolean newCursor = true; /** * Game option coordinate modifiers (for drawing). @@ -248,8 +210,8 @@ public class Options extends BasicGameState { private int textY, offsetY; // game-related variables - private static GameContainer container; - private static StateBasedGame game; + private GameContainer container; + private StateBasedGame game; private Input input; private int state; private boolean init = false; @@ -261,42 +223,17 @@ public class Options extends BasicGameState { @Override public void init(GameContainer container, StateBasedGame game) throws SlickException { - Options.container = container; - Options.game = game; + this.container = container; + this.game = game; this.input = container.getInput(); - // game settings - container.setTargetFrameRate(targetFPS[targetFPSindex]); - container.setMouseCursor("cursor.png", 16, 16); - container.setMusicVolume(getMusicVolume()); - container.setShowFPS(false); - container.getInput().enableKeyRepeat(); - container.setAlwaysRender(true); - - // create fonts - float fontBase; - if (container.getHeight() <= 600) - fontBase = 9f; - else if (container.getHeight() < 800) - fontBase = 10f; - else if (container.getHeight() <= 900) - fontBase = 12f; - else - fontBase = 14f; - - Font font = new Font("Lucida Sans Unicode", Font.PLAIN, (int) (fontBase * 4 / 3)); - FONT_DEFAULT = new TrueTypeFont(font, false); - FONT_BOLD = new TrueTypeFont(font.deriveFont(Font.BOLD), false); - FONT_XLARGE = new TrueTypeFont(font.deriveFont(fontBase * 4), false); - FONT_LARGE = new TrueTypeFont(font.deriveFont(fontBase * 2), false); - FONT_MEDIUM = new TrueTypeFont(font.deriveFont(fontBase * 3 / 2), false); - FONT_SMALL = new TrueTypeFont(font.deriveFont(fontBase), false); + Utils.init(container, game); int width = container.getWidth(); int height = container.getHeight(); // game option coordinate modifiers - textY = 10 + (FONT_XLARGE.getLineHeight() * 3 / 2); + textY = 10 + (Utils.FONT_XLARGE.getLineHeight() * 3 / 2); offsetY = (int) (((height * 0.8f) - textY) / OPTIONS_MAX); // game mods @@ -330,14 +267,6 @@ public class Options extends BasicGameState { for (int i = 0; i < modButtons.length; i++) modButtons[i].getImage().setAlpha(0.5f); - // back button - Image back = new Image("menu-back.png"); - float scale = (height * 0.1f) / back.getHeight(); - back = back.getScaledCopy(scale); - backButton = new GUIMenuButton(back, - back.getWidth() / 2f, - height - (back.getHeight() / 2f)); - game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition(Color.black)); } @@ -347,25 +276,25 @@ public class Options extends BasicGameState { if (!init) return; - g.setBackground(COLOR_BLACK_ALPHA); + g.setBackground(Utils.COLOR_BLACK_ALPHA); g.setColor(Color.white); int width = container.getWidth(); int height = container.getHeight(); // title - FONT_XLARGE.drawString( - (width / 2) - (FONT_XLARGE.getWidth("GAME OPTIONS") / 2), + Utils.FONT_XLARGE.drawString( + (width / 2) - (Utils.FONT_XLARGE.getWidth("GAME OPTIONS") / 2), 10, "GAME OPTIONS" ); - FONT_DEFAULT.drawString( - (width / 2) - (FONT_DEFAULT.getWidth("Click or drag an option to change it.") / 2), - 10 + FONT_XLARGE.getHeight(), "Click or drag an option to change it." + Utils.FONT_DEFAULT.drawString( + (width / 2) - (Utils.FONT_DEFAULT.getWidth("Click or drag an option to change it.") / 2), + 10 + Utils.FONT_XLARGE.getHeight(), "Click or drag an option to change it." ); // game options g.setLineWidth(1f); - g.setFont(FONT_LARGE); + g.setFont(Utils.FONT_LARGE); this.drawOption(g, OPTIONS_SCREEN_RESOLUTION, "Screen Resolution", String.format("%dx%d", resolutions[resolutionIndex][0], resolutions[resolutionIndex][1]), "Restart to apply resolution changes." @@ -375,7 +304,7 @@ public class Options extends BasicGameState { // "Restart to apply changes." // ); this.drawOption(g, OPTIONS_TARGET_FPS, "Frame Limiter", - String.format("%dfps", targetFPS[targetFPSindex]), + String.format("%dfps", getTargetFPS()), "Higher values may cause high CPU usage." ); this.drawOption(g, OPTIONS_MUSIC_VOLUME, "Music Volume", @@ -408,13 +337,14 @@ public class Options extends BasicGameState { ); // game mods - FONT_LARGE.drawString(width * 0.02f, height * 0.8f, "Game Mods:", Color.white); + Utils.FONT_LARGE.drawString(width * 0.02f, height * 0.8f, "Game Mods:", Color.white); for (int i = 0; i < modButtons.length; i++) modButtons[i].draw(); - backButton.draw(); + Utils.getBackButton().draw(); - drawFPS(); + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -433,7 +363,7 @@ public class Options extends BasicGameState { return; // back - if (backButton.contains(x, y)) { + if (Utils.getBackButton().contains(x, y)) { game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black)); return; } @@ -472,7 +402,7 @@ public class Options extends BasicGameState { // } if (isOptionClicked(OPTIONS_TARGET_FPS, y)) { targetFPSindex = (targetFPSindex + 1) % targetFPS.length; - container.setTargetFrameRate(targetFPS[targetFPSindex]); + container.setTargetFrameRate(getTargetFPS()); return; } if (isOptionClicked(OPTIONS_SCREENSHOT_FORMAT, y)) { @@ -545,7 +475,7 @@ public class Options extends BasicGameState { game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black)); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; } } @@ -560,14 +490,14 @@ public class Options extends BasicGameState { */ private void drawOption(Graphics g, int pos, String label, String value, String notes) { int width = container.getWidth(); - int textHeight = FONT_LARGE.getHeight(); + int textHeight = Utils.FONT_LARGE.getHeight(); float y = textY + (pos * offsetY); g.drawString(label, width / 50, y); g.drawString(value, width / 2, y); g.drawLine(0, y + textHeight, width, y + textHeight); if (notes != null) - FONT_SMALL.drawString(width / 50, y + textHeight, notes); + Utils.FONT_SMALL.drawString(width / 50, y + textHeight, notes); } /** @@ -577,12 +507,8 @@ public class Options extends BasicGameState { * @return true if clicked */ private boolean isOptionClicked(int pos, int y) { - if (y > textY + (offsetY * pos) - FONT_LARGE.getHeight() && - y < textY + (offsetY * pos) + FONT_LARGE.getHeight()) { - optionsChanged = true; - return true; - } - return false; + return (y > textY + (offsetY * pos) - Utils.FONT_LARGE.getHeight() && + y < textY + (offsetY * pos) + Utils.FONT_LARGE.getHeight()); } /** @@ -610,9 +536,10 @@ public class Options extends BasicGameState { public static Image getModImage(int mod) { return modButtons[mod].getImage(); } /** - * Returns the 'back' GUIMenuButton. + * Returns the target frame rate. + * @return the target FPS */ - public static GUIMenuButton getBackButton() { return backButton; } + public static int getTargetFPS() { return targetFPS[targetFPSindex]; } /** * Returns the default music volume. @@ -633,53 +560,10 @@ public class Options extends BasicGameState { public static int getMusicOffset() { return musicOffset; } /** - * Draws the FPS at the bottom-right corner of the game container. - * If the option is not activated, this will do nothing. + * Returns the screenshot file format. + * @return the file extension ("png", "jpg", "bmp") */ - public static void drawFPS() { - if (showFPS) { - String fps = String.format("FPS: %d", container.getFPS()); - FONT_DEFAULT.drawString( - container.getWidth() - 15 - FONT_DEFAULT.getWidth(fps), - container.getHeight() - 15 - FONT_DEFAULT.getHeight(fps), - fps, Color.white - ); - } - } - - /** - * Takes a screenshot. - * @return true if successful - */ - public static boolean takeScreenShot() { - // TODO: should this be threaded? - try { - // create the screenshot directory - if (!SCREENSHOT_DIR.isDirectory()) { - if (!SCREENSHOT_DIR.mkdir()) - return false; - } - - // create file name - SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss"); - String file = date.format(new Date()); - - SoundController.playSound(SoundController.SOUND_SHUTTER); - - // copy the screen - Image screen = new Image(container.getWidth(), container.getHeight()); - container.getGraphics().copyArea(screen, 0, 0); - ImageOut.write(screen, String.format("%s%sscreenshot_%s.%s", - SCREENSHOT_DIR.getName(), File.separator, - file, screenshotFormat[screenshotFormatIndex]), false - ); - screen.destroy(); - } catch (SlickException e) { - Log.warn("Failed to take a screenshot.", e); - return false; - } - return true; - } + public static String getScreenshotFormat() { return screenshotFormat[screenshotFormatIndex]; } /** * Returns the screen resolution. @@ -693,6 +577,12 @@ public class Options extends BasicGameState { // */ // public static boolean isFullscreen() { return fullscreen; } + /** + * Returns whether or not the FPS counter display is enabled. + * @return true if enabled + */ + public static boolean isFPSCounterEnabled() { return showFPS; } + /** * Returns whether or not hit lighting effects are enabled. * @return true if enabled @@ -709,14 +599,13 @@ public class Options extends BasicGameState { * Returns the port number to bind to. * @return the port */ - public static int getPort() { - if (port == 0) { - // choose a random port - port = 49250; - optionsChanged = true; // force file creation - } - return port; - } + public static int getPort() { return port; } + + /** + * Returns whether or not the new cursor type is enabled. + * @return true if enabled + */ + public static boolean isNewCursorEnabled() { return newCursor; } /** * Returns the current beatmap directory. @@ -730,15 +619,14 @@ public class Options extends BasicGameState { // search for directory for (int i = 0; i < BEATMAP_DIRS.length; i++) { beatmapDir = new File(BEATMAP_DIRS[i]); - if (beatmapDir.isDirectory()) { - optionsChanged = true; // force config file creation + if (beatmapDir.isDirectory()) return beatmapDir; - } } beatmapDir.mkdir(); // none found, create new directory return beatmapDir; } + /** * Reads user options from the options file, if it exists. */ @@ -746,9 +634,7 @@ public class Options extends BasicGameState { // if no config file, use default settings File file = new File(OPTIONS_FILE); if (!file.isFile()) { - optionsChanged = true; // force file creation saveOptions(); - optionsChanged = false; return; } @@ -816,6 +702,9 @@ public class Options extends BasicGameState { if (i > 0 && i <= 65535) port = i; break; + case "NewCursor": // custom + newCursor = Boolean.parseBoolean(value); + break; } } } catch (IOException e) { @@ -830,10 +719,6 @@ public class Options extends BasicGameState { * (Over)writes user options to a file. */ public static void saveOptions() { - // only overwrite when needed - if (!optionsChanged) - return; - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(OPTIONS_FILE), "utf-8"))) { // header @@ -873,6 +758,8 @@ public class Options extends BasicGameState { writer.newLine(); writer.write(String.format("Port = %d", port)); writer.newLine(); + writer.write(String.format("NewCursor = %b", newCursor)); // custom + writer.newLine(); writer.close(); } catch (IOException e) { Log.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE), e); diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 271473e8..a05b2f5e 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -26,6 +26,7 @@ import itdelatrisu.opsu.OsuGroupList; import itdelatrisu.opsu.OsuGroupNode; import itdelatrisu.opsu.OsuParser; import itdelatrisu.opsu.SoundController; +import itdelatrisu.opsu.Utils; import org.lwjgl.opengl.Display; import org.newdawn.slick.Color; @@ -170,13 +171,13 @@ public class SongMenu extends BasicGameState { searchResultString = "Type to search!"; searchIcon = new Image("search.png"); - float iconScale = Options.FONT_BOLD.getLineHeight() * 2f / searchIcon.getHeight(); + float iconScale = Utils.FONT_BOLD.getLineHeight() * 2f / searchIcon.getHeight(); searchIcon = searchIcon.getScaledCopy(iconScale); search = new TextField( - container, Options.FONT_DEFAULT, + container, Utils.FONT_DEFAULT, (int) tabX + searchIcon.getWidth(), (int) ((height * 0.15f) - (tab.getHeight() * 5 / 2f)), - (int) (buttonWidth / 2), Options.FONT_DEFAULT.getHeight() + (int) (buttonWidth / 2), Utils.FONT_DEFAULT.getHeight() ); search.setBackgroundColor(Color.transparent); search.setBorderColor(Color.transparent); @@ -205,9 +206,9 @@ public class SongMenu extends BasicGameState { // header setup float lowerBound = height * 0.15f; - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRect(0, 0, width, lowerBound); - g.setColor(Options.COLOR_BLUE_DIVIDER); + g.setColor(Utils.COLOR_BLUE_DIVIDER); g.setLineWidth(2f); g.drawLine(0, lowerBound, width, lowerBound); g.resetLineWidth(); @@ -220,17 +221,17 @@ public class SongMenu extends BasicGameState { String[] info = focusNode.getInfo(); g.setColor(Color.white); - Options.FONT_LARGE.drawString( + Utils.FONT_LARGE.drawString( musicNoteWidth + 5, -3, info[0]); - float y1 = -3 + Options.FONT_LARGE.getHeight() * 0.75f; - Options.FONT_DEFAULT.drawString( + float y1 = -3 + Utils.FONT_LARGE.getHeight() * 0.75f; + Utils.FONT_DEFAULT.drawString( musicNoteWidth + 5, y1, info[1]); - Options.FONT_BOLD.drawString( + Utils.FONT_BOLD.drawString( 5, Math.max(y1 + 4, musicNoteHeight - 3), info[2]); - Options.FONT_DEFAULT.drawString( - 5, musicNoteHeight + Options.FONT_BOLD.getLineHeight() - 9, info[3]); - Options.FONT_SMALL.drawString( - 5, musicNoteHeight + Options.FONT_BOLD.getLineHeight() + Options.FONT_DEFAULT.getLineHeight() - 13, info[4]); + Utils.FONT_DEFAULT.drawString( + 5, musicNoteHeight + Utils.FONT_BOLD.getLineHeight() - 9, info[3]); + Utils.FONT_SMALL.drawString( + 5, musicNoteHeight + Utils.FONT_BOLD.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() - 13, info[4]); } // song buttons @@ -248,17 +249,17 @@ public class SongMenu extends BasicGameState { for (int i = sortTabs.length - 1; i >= 0; i--) { sortTabs[i].getImage().setAlpha((i == currentSort) ? 1.0f : 0.7f); sortTabs[i].draw(); - float tabTextX = sortTabs[i].getX() - (Options.FONT_MEDIUM.getWidth(OsuGroupList.SORT_NAMES[i]) / 2); - Options.FONT_MEDIUM.drawString(tabTextX, tabTextY, OsuGroupList.SORT_NAMES[i], Color.white); + float tabTextX = sortTabs[i].getX() - (Utils.FONT_MEDIUM.getWidth(OsuGroupList.SORT_NAMES[i]) / 2); + Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, OsuGroupList.SORT_NAMES[i], Color.white); } // search - Options.FONT_BOLD.drawString( - search.getX(), search.getY() - Options.FONT_BOLD.getLineHeight(), + Utils.FONT_BOLD.drawString( + search.getX(), search.getY() - Utils.FONT_BOLD.getLineHeight(), searchResultString, Color.white ); searchIcon.draw(search.getX() - searchIcon.getWidth(), - search.getY() - Options.FONT_DEFAULT.getLineHeight()); + search.getY() - Utils.FONT_DEFAULT.getLineHeight()); g.setColor(Color.white); search.render(container, g); @@ -266,16 +267,17 @@ public class SongMenu extends BasicGameState { if (focusNode != null) { float scrollStartY = height * 0.16f; float scrollEndY = height * 0.82f; - g.setColor(Options.COLOR_BLACK_ALPHA); + g.setColor(Utils.COLOR_BLACK_ALPHA); g.fillRoundRect(width - 10, scrollStartY, 5, scrollEndY, 4); g.setColor(Color.white); g.fillRoundRect(width - 10, scrollStartY + (scrollEndY * startNode.index / Opsu.groups.size()), 5, 20, 4); } // back button - Options.getBackButton().draw(); + Utils.getBackButton().draw(); - Options.drawFPS(); + Utils.drawFPS(); + Utils.drawCursor(); } @Override @@ -343,7 +345,7 @@ public class SongMenu extends BasicGameState { return; // back - if (Options.getBackButton().contains(x, y)) { + if (Utils.getBackButton().contains(x, y)) { SoundController.playSound(SoundController.SOUND_MENUBACK); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); return; @@ -436,7 +438,7 @@ public class SongMenu extends BasicGameState { setFocus(Opsu.groups.getRandomNode(), -1, true); break; case Input.KEY_F12: - Options.takeScreenShot(); + Utils.takeScreenShot(); break; case Input.KEY_ENTER: if (focusNode != null)