diff --git a/CREDITS.md b/CREDITS.md index d528d86e..a3b0d880 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -21,6 +21,7 @@ The images included in opsu! belong to their respective authors. * kouyang * teinecthel * Font Awesome by Dave Gandy - http://fontawesome.io +* Chaaoos/C0N - the opsu!dance logo Projects -------- @@ -32,5 +33,4 @@ The following projects were referenced in creating opsu!: Theme Song ---------- -The theme song is "On the Bach" by Jingle Punks, from the [YouTube Audio Library] -(https://www.youtube.com/audiolibrary/music). +The theme song is "On the Bach" by Jingle Punks, from the [YouTube Audio Library](https://www.youtube.com/audiolibrary/music). diff --git a/README.md b/README.md index b16118d5..a6df6f85 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Table of contents** -* [What](#What) - [Why](#why) - [Downloads](#downloads) - [Running](#running) - [Building a JAR](#building-a-jar) - [Credits](#credits) - [License](#license) +* [What](#what) - [Why](#why) - [Downloads](#downloads) - [Running](#running) - [Building a JAR](#building-a-jar) - [Credits](#credits) - [License](#license) What ---- @@ -38,10 +38,7 @@ You should know how to do this. It's recommended to use a working directory like ### Using apache ant -Resolve dependencies first, use either: - -* `ant ivyresolve` to download dependencies using apache ivy -* `ant mvnresolve` to download dependencies using apache mvn +Resolve dependencies first by doing `ant mvnresolve` or `mvn initialize` Then do `ant run` diff --git a/build.xml b/build.xml index b24b89de..938decb3 100644 --- a/build.xml +++ b/build.xml @@ -1,9 +1,8 @@ - + - @@ -21,14 +20,12 @@ ant clean --> clean the ant working dir ant cleanlib --> clean the lib folder -ant ivyresolve --> resolve dependencies using ivy ant mvnresolve --> resolve dependencies using mvn ant compile --> compile the code ant run --> prepare to run and run ant jar --> package a jar -resolve dependencies first -(using either mvnresolve or ivyresolve), +resolve dependencies first (mvnresolve) then run (code is compiled automatically when you run) @@ -40,12 +37,6 @@ then run (code is compiled automatically when you run) - - - - - - @@ -119,9 +110,6 @@ then run (code is compiled automatically when you run) - - - diff --git a/ivy.xml b/ivy.xml deleted file mode 100644 index 1573a486..00000000 --- a/ivy.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9a19f00a..8ddbaf43 100644 --- a/pom.xml +++ b/pom.xml @@ -8,12 +8,12 @@ opsu-dance 0.5.0-SNAPSHOT + target/Natives UTF-8 ${project.version} ${maven.build.timestamp} yyyy-MM-dd HH:mm yugecin.opsudance.core.Entrypoint - false src @@ -147,10 +147,6 @@ ${mainClassName} - ${XDG} - OpenAL32.dll,OpenAL64.dll,lwjgl.dll,lwjgl64.dll - liblwjgl.so,liblwjgl64.so,libopenal.so,libopenal64.so - liblwjgl.dylib,openal.dylib @@ -236,11 +232,6 @@ jna-platform 4.1.0 - - org.apache.maven - maven-artifact - 3.3.3 - org.apache.commons commons-compress @@ -263,4 +254,4 @@ - \ No newline at end of file + diff --git a/res/epiwarning.pdn b/res/epiwarning.pdn new file mode 100644 index 00000000..07b2daa7 Binary files /dev/null and b/res/epiwarning.pdn differ diff --git a/res/icon16.png b/res/icon16.png index c6e71e06..39db77a7 100644 Binary files a/res/icon16.png and b/res/icon16.png differ diff --git a/res/icon32.png b/res/icon32.png index 412f623d..12176ee0 100644 Binary files a/res/icon32.png and b/res/icon32.png differ diff --git a/res/icon48dp.png b/res/icon48dp.png deleted file mode 100644 index 60c770cb..00000000 Binary files a/res/icon48dp.png and /dev/null differ diff --git a/res/logo.png b/res/logo.png deleted file mode 100644 index 3f59daec..00000000 Binary files a/res/logo.png and /dev/null differ diff --git a/res/logo2piece.png b/res/logo2piece.png new file mode 100644 index 00000000..1fae6bdd Binary files /dev/null and b/res/logo2piece.png differ diff --git a/res/logo2piece2.png b/res/logo2piece2.png deleted file mode 100644 index 35484e97..00000000 Binary files a/res/logo2piece2.png and /dev/null differ diff --git a/res/logo2pulse.png b/res/logo2pulse.png new file mode 100644 index 00000000..e37d38ec Binary files /dev/null and b/res/logo2pulse.png differ diff --git a/res/menu-back.png b/res/menu-back.png index e3678753..34f690b7 100644 Binary files a/res/menu-back.png and b/res/menu-back.png differ diff --git a/res/menu-background.jpg b/res/menu-background.jpg deleted file mode 100644 index 92bcd310..00000000 Binary files a/res/menu-background.jpg and /dev/null differ diff --git a/res/menu-background.pdn b/res/menu-background.pdn new file mode 100644 index 00000000..3dddfaec Binary files /dev/null and b/res/menu-background.pdn differ diff --git a/res/menu-background.png b/res/menu-background.png new file mode 100644 index 00000000..da2dccd9 Binary files /dev/null and b/res/menu-background.png differ diff --git a/res/menu-exit.png b/res/menu-exit.png index 79a484b7..8faf6a19 100644 Binary files a/res/menu-exit.png and b/res/menu-exit.png differ diff --git a/res/menu-exit2.png b/res/menu-exit2.png deleted file mode 100644 index 7acd9900..00000000 Binary files a/res/menu-exit2.png and /dev/null differ diff --git a/res/menu-options-1.pdn b/res/menu-options-1.pdn new file mode 100644 index 00000000..25d63320 Binary files /dev/null and b/res/menu-options-1.pdn differ diff --git a/res/menu-options-2.pdn b/res/menu-options-2.pdn new file mode 100644 index 00000000..edeeb5c8 Binary files /dev/null and b/res/menu-options-2.pdn differ diff --git a/res/menu-options-3.pdn b/res/menu-options-3.pdn new file mode 100644 index 00000000..457260e7 Binary files /dev/null and b/res/menu-options-3.pdn differ diff --git a/res/menu-options-background.pdn b/res/menu-options-background.pdn new file mode 100644 index 00000000..e51aa575 Binary files /dev/null and b/res/menu-options-background.pdn differ diff --git a/res/menu-options.pdn b/res/menu-options.pdn new file mode 100644 index 00000000..3e611d88 Binary files /dev/null and b/res/menu-options.pdn differ diff --git a/res/menu-options.png b/res/menu-options.png new file mode 100644 index 00000000..90ce46e6 Binary files /dev/null and b/res/menu-options.png differ diff --git a/res/menu-play.png b/res/menu-play.png index 4ecd2c72..6bab54c4 100644 Binary files a/res/menu-play.png and b/res/menu-play.png differ diff --git a/res/menu-play2.png b/res/menu-play2.png deleted file mode 100644 index 0ba86471..00000000 Binary files a/res/menu-play2.png and /dev/null differ diff --git a/res/music-next.png b/res/music-next.png index f600ef30..5838b17d 100644 Binary files a/res/music-next.png and b/res/music-next.png differ diff --git a/res/music-np-bg-black.png b/res/music-np-bg-black.png new file mode 100644 index 00000000..5c83f2d0 Binary files /dev/null and b/res/music-np-bg-black.png differ diff --git a/res/music-np-bg-white.png b/res/music-np-bg-white.png new file mode 100644 index 00000000..fd1f7005 Binary files /dev/null and b/res/music-np-bg-white.png differ diff --git a/res/music-np.pdn b/res/music-np.pdn new file mode 100644 index 00000000..ffacaef9 Binary files /dev/null and b/res/music-np.pdn differ diff --git a/res/music-np.png b/res/music-np.png new file mode 100644 index 00000000..a1b406cf Binary files /dev/null and b/res/music-np.png differ diff --git a/res/music-pause.png b/res/music-pause.png index 1df82839..f61d58bd 100644 Binary files a/res/music-pause.png and b/res/music-pause.png differ diff --git a/res/music-play.png b/res/music-play.png index 5cf7d9c4..7ff5ed28 100644 Binary files a/res/music-play.png and b/res/music-play.png differ diff --git a/res/music-previous.png b/res/music-previous.png index 799f405c..1e0cec9c 100644 Binary files a/res/music-previous.png and b/res/music-previous.png differ diff --git a/res/music-stop.png b/res/music-stop.png new file mode 100644 index 00000000..988a4271 Binary files /dev/null and b/res/music-stop.png differ diff --git a/res/options-background.png b/res/options-background.png deleted file mode 100644 index 10612e32..00000000 Binary files a/res/options-background.png and /dev/null differ diff --git a/res/playfield.png b/res/playfield.png index e7f45899..34f690b7 100644 Binary files a/res/playfield.png and b/res/playfield.png differ diff --git a/res/selection-mode-over.png b/res/selection-mode-over.png new file mode 100644 index 00000000..1833f567 Binary files /dev/null and b/res/selection-mode-over.png differ diff --git a/res/selection-mode.png b/res/selection-mode.png new file mode 100644 index 00000000..cd2a9c1f Binary files /dev/null and b/res/selection-mode.png differ diff --git a/res/selection-mods-over.png b/res/selection-mods-over.png index 890daa54..37fd4d2a 100644 Binary files a/res/selection-mods-over.png and b/res/selection-mods-over.png differ diff --git a/res/selection-mods.png b/res/selection-mods.png index 53078a2e..743bd47b 100644 Binary files a/res/selection-mods.png and b/res/selection-mods.png differ diff --git a/res/selection-options-over.png b/res/selection-options-over.png index ab5f95e5..b252e5a9 100644 Binary files a/res/selection-options-over.png and b/res/selection-options-over.png differ diff --git a/res/selection-options.png b/res/selection-options.png index f94051fe..cc9e730d 100644 Binary files a/res/selection-options.png and b/res/selection-options.png differ diff --git a/res/selection-random-over.png b/res/selection-random-over.png index 9daf6072..adc6a1e8 100644 Binary files a/res/selection-random-over.png and b/res/selection-random-over.png differ diff --git a/res/selection-random.png b/res/selection-random.png index 8afd55d3..11d7698c 100644 Binary files a/res/selection-random.png and b/res/selection-random.png differ diff --git a/res/selection-selectoptions-over.png b/res/selection-selectoptions-over.png deleted file mode 100644 index 27af71de..00000000 Binary files a/res/selection-selectoptions-over.png and /dev/null differ diff --git a/res/selection-selectoptions.png b/res/selection-selectoptions.png deleted file mode 100644 index ab60811f..00000000 Binary files a/res/selection-selectoptions.png and /dev/null differ diff --git a/src/awlex/ospu/FakeGameObject.java b/src/awlex/ospu/FakeGameObject.java index 0ce5c963..b474acc2 100644 --- a/src/awlex/ospu/FakeGameObject.java +++ b/src/awlex/ospu/FakeGameObject.java @@ -10,7 +10,7 @@ import itdelatrisu.opsu.objects.curves.Vec2f; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; /** * This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects. @@ -24,8 +24,8 @@ public class FakeGameObject extends GameObject { public FakeGameObject() { this.start = new Vec2f(); this.end = new Vec2f(); - this.start.x = this.end.x = displayContainer.width / 2; - this.start.y = this.end.y = displayContainer.height / 2; + this.start.x = this.end.x = width2; + this.start.y = this.end.y = height2; } public FakeGameObject(GameObject start, GameObject end) { diff --git a/src/awlex/ospu/movers/CombinedSpiralMover.java b/src/awlex/ospu/movers/CombinedSpiralMover.java index 04729047..b44dd178 100644 --- a/src/awlex/ospu/movers/CombinedSpiralMover.java +++ b/src/awlex/ospu/movers/CombinedSpiralMover.java @@ -131,6 +131,6 @@ public class CombinedSpiralMover extends Mover { } private boolean checkBounds(double[] pos) { - return 0 < pos[0] && pos[0] < displayContainer.width && 0 < pos[1] && pos[1] < displayContainer.height; + return 0 < pos[0] && pos[0] < width && 0 < pos[1] && pos[1] < height; } } diff --git a/src/awlex/ospu/movers/factories/SpiralMoverFactory.java b/src/awlex/ospu/movers/factories/SpiralMoverFactory.java index f24a527c..f7e142c7 100644 --- a/src/awlex/ospu/movers/factories/SpiralMoverFactory.java +++ b/src/awlex/ospu/movers/factories/SpiralMoverFactory.java @@ -8,7 +8,7 @@ import yugecin.opsudance.movers.Mover; import awlex.ospu.movers.SpiralToMover; import yugecin.opsudance.movers.factories.MoverFactory; -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; /** * Created by Alex Wieser on 09.10.2016. @@ -88,7 +88,7 @@ public class SpiralMoverFactory implements MoverFactory { * @return */ private boolean checkBounds(double[] pos) { - return 0 < pos[0] && pos[0] < displayContainer.width && 0 < pos[1] && pos[1] < displayContainer.height; + return 0 < pos[0] && pos[0] < width && 0 < pos[1] && pos[1] < height; } @Override diff --git a/src/awlex/ospu/spinners/SpiralSpinner.java b/src/awlex/ospu/spinners/SpiralSpinner.java index 50fa9295..20104fd9 100644 --- a/src/awlex/ospu/spinners/SpiralSpinner.java +++ b/src/awlex/ospu/spinners/SpiralSpinner.java @@ -43,18 +43,16 @@ public class SpiralSpinner extends Spinner { double ang; double rad; for (int i = 0; i < SIZE / 2; i++) { - MAX_RAD = (int) (displayContainer.height * .35); + MAX_RAD = (int) (height * .35); ang = (DENSITY * (Math.PI / SIZE) * i); rad = (MAX_RAD / (SIZE / 2)) * i; - int offsetX = displayContainer.width / 2; - int offsetY = displayContainer.height / 2; points[SIZE / 2 - 1 - i] = new double[]{ - offsetX + rad * Math.cos(ang), - offsetY + rad * Math.sin(ang) + width2 + rad * Math.cos(ang), + height2 + rad * Math.sin(ang) }; points[SIZE / 2 + i] = new double[]{ - offsetX + rad * (Math.cos(ang) * Math.cos(Math.PI) - Math.sin(ang) * Math.sin(Math.PI)), - offsetY + rad * -Math.sin(ang) + width2 + rad * (Math.cos(ang) * Math.cos(Math.PI) - Math.sin(ang) * Math.sin(Math.PI)), + height2 + rad * -Math.sin(ang) }; } } @@ -84,12 +82,12 @@ public class SpiralSpinner extends Spinner { } private void rotatePointAroundCenter(double[] point, double beta) { - double angle = Math.atan2(point[1] - displayContainer.height / 2, point[0] - displayContainer.width / 2); - double rad = Utils.distance(point[0], point[1], displayContainer.width / 2, displayContainer.height / 2); + double angle = Math.atan2(point[1] - height2, point[0] - width2); + double rad = Utils.distance(point[0], point[1], width2, height2); //rotationMatrix - point[0] = displayContainer.width / 2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta)); - point[1] = displayContainer.height / 2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta)); + point[0] = width2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta)); + point[1] = height2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta)); } diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index c45c8945..be9fe557 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -603,8 +603,6 @@ public class GameData { */ @SuppressWarnings("deprecation") public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObject, float alpha) { - int width = displayContainer.width; - int height = displayContainer.height; boolean relaxAutoPilot = (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); int margin = (int) (width * 0.008f); float uiScale = GameImage.getUIscale(); @@ -796,9 +794,6 @@ public class GameData { * @param beatmap the beatmap */ public void drawRankingElements(Graphics g, Beatmap beatmap) { - int width = displayContainer.width; - int height = displayContainer.height; - // TODO Version 2 skins float rankingHeight = 75; float scoreTextScale = 1.0f; @@ -863,7 +858,7 @@ public class GameData { if (comboMax == fullObjectCount) { GameImage.RANKING_PERFECT.getImage().draw( width * 0.08f, - (height * 0.99f) - GameImage.RANKING_PERFECT.getImage().getHeight() + (height * 0.99f) - GameImage.RANKING_PERFECT.getHeight() ); } @@ -911,7 +906,7 @@ public class GameData { if (hitResult.hitResultType == HitObjectType.SPINNER && hitResult.result != HIT_MISS) { Image spinnerOsu = GameImage.SPINNER_OSU.getImage(); spinnerOsu.setAlpha(hitResult.alpha); - spinnerOsu.drawCentered(displayContainer.width / 2, displayContainer.height / 4); + spinnerOsu.drawCentered(width2, height / 4); spinnerOsu.setAlpha(1f); } else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS && // hit lighting @@ -1185,7 +1180,7 @@ public class GameData { // combo burst if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) { int leftX = 0; - int rightX = displayContainer.width - comboBurstImages[comboBurstIndex].getWidth(); + int rightX = width - comboBurstImages[comboBurstIndex].getWidth(); if (comboBurstX < leftX) { comboBurstX += (delta / 2f) * GameImage.getUIscale(); if (comboBurstX > leftX) @@ -1246,7 +1241,7 @@ public class GameData { } comboBurstAlpha = 0.8f; if ((comboBurstIndex % 2) == 0) { - comboBurstX = displayContainer.width; + comboBurstX = width; } else { comboBurstX = comboBurstImages[0].getWidth() * -1; } diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index 68002fc5..183335ab 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Fonts; import java.io.File; @@ -31,10 +30,11 @@ import org.newdawn.slick.SlickException; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; import yugecin.opsudance.core.errorhandling.ErrorHandler; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.skinning.SkinService; import yugecin.opsudance.utils.SlickUtil; +import static itdelatrisu.opsu.ui.Colors.*; +import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; /** @@ -90,7 +90,9 @@ public enum GameImage { PLAYFIELD ("playfield", "png|jpg", false, false) { @Override protected Image process_sub(Image img, int w, int h) { - img.setAlpha(0.7f); + if (img.getWidth() == 1 && img.getHeight() == 1) { + img = MENU_BG.getImage().getFlippedCopy(/*h*/ false, /*v*/ true); + } return img.getScaledCopy(w, h); } }, @@ -230,14 +232,14 @@ public enum GameImage { MOD_AUTOPILOT ("selection-mod-relax2", "png", false, false), // Selection Buttons + SELECTION_MODE ("selection-mode", "png", false, false), + SELECTION_MODE_OVERLAY ("selection-mode-over", "png", false, false), SELECTION_MODS ("selection-mods", "png", false, false), SELECTION_MODS_OVERLAY ("selection-mods-over", "png", false, false), SELECTION_RANDOM ("selection-random", "png", false, false), SELECTION_RANDOM_OVERLAY ("selection-random-over", "png", false, false), SELECTION_OPTIONS ("selection-options", "png", false, false), SELECTION_OPTIONS_OVERLAY ("selection-options-over", "png", false, false), - SELECTION_OTHER_OPTIONS ("selection-selectoptions", "png", false, false), - SELECTION_OTHER_OPTIONS_OVERLAY ("selection-selectoptions-over", "png", false, false), // Replay Speed Buttons REPLAY_PLAYBACK_NORMAL ("playback-normal", "png", false, false), @@ -299,25 +301,37 @@ public enum GameImage { MENU_LOGO ("logo2", "png", false, true) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy(0.8f); + return img.getScaledCopy(0.75f); } }, - MENU_LOGO_PIECE ("logo2piece2", "png", false, true) { + MENU_LOGO_PIECE ("logo2piece", "png", false, true) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy(0.8f); + return img.getScaledCopy(0.75f); } }, - MENU_PLAY ("menu-play2", "png", false, false) { + MENU_LOGO_PULSE ("logo2pulse", "png", false, true) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy(0.8f); + return img.getScaledCopy(0.75f); } }, - MENU_EXIT ("menu-exit2", "png", false, false) { + MENU_PLAY ("menu-play", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy(0.8f); + return img.getScaledCopy(0.75f); + } + }, + MENU_EXIT ("menu-exit", "png", false, false) { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(0.75f); + } + }, + MENU_OPTIONS ("menu-options", "png", false, false) { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(0.75f); } }, MENU_BUTTON_MID ("button-middle", "png", false, false), @@ -326,21 +340,25 @@ public enum GameImage { STAR ("star", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.16f) / img.getHeight()); + return img.getScaledCopy((MENU_BUTTON_BG.getHeight() * 0.16f) / img.getHeight()); } }, STAR2 ("star2", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.33f) / img.getHeight()); + return img.getScaledCopy((MENU_BUTTON_BG.getHeight() * 0.33f) / img.getHeight()); } }, // Music Player Buttons MUSIC_PLAY ("music-play", "png", false, false), MUSIC_PAUSE ("music-pause", "png", false, false), + MUSIC_STOP ("music-stop", "png", false, false), MUSIC_NEXT ("music-next", "png", false, false), MUSIC_PREVIOUS ("music-previous", "png", false, false), + MUSIC_NOWPLAYING ("music-np", "png", false, false), + MUSIC_NOWPLAYING_BG_BLACK ("music-np-bg-black", "png", false, false), + MUSIC_NOWPLAYING_BG_WHITE ("music-np-bg-white", "png", false, false), DOWNLOADS ("downloads", "png", false, false) { @Override @@ -386,13 +404,6 @@ public enum GameImage { return img.getScaledCopy((h / 14f) / img.getHeight()); } }, - OPTIONS_BG ("options-background", "png|jpg", false, true) { - @Override - protected Image process_sub(Image img, int w, int h) { - img.setAlpha(0.7f); - return img.getScaledCopy(w, h); - } - }, CHEVRON_DOWN ("chevron-down", "png", false, false), CHEVRON_RIGHT ("chevron-right", "png", false, false), @@ -646,6 +657,30 @@ public enum GameImage { return (skinImage != null) ? skinImage : defaultImage; } + public int getHeight() { + return getImage().getHeight(); + } + + public int getWidth() { + return getImage().getWidth(); + } + + /** + * Returns the image associated with this resource, with a scale applied. + * The beatmap skin image takes priority over the default image. + */ + public Image getScaledImage(float scale) { + return this.getImage().getScaledCopy(scale); + } + + /** + * Returns the image associated with this resource, with a scale applied. + * The beatmap skin image takes priority over the default image. + */ + public Image getScaledImage(int width, int height) { + return this.getImage().getScaledCopy(width, height); + } + /** * Returns an Animation based on the image array. * If no image array exists, returns the single image as an animation. @@ -743,7 +778,7 @@ public enum GameImage { String err = String.format("Could not find default image '%s'.", filename); Log.warn(err); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } /** @@ -806,8 +841,7 @@ public enum GameImage { img = img.getScaledCopy(0.5f); list.add(img); } catch (SlickException e) { - BubNotifListener.EVENT.make().onBubNotif( - String.format("Failed to set image '%s'.", name), Colors.BUB_RED); + bubNotifs.sendf(BUB_RED, "Failed to set image '%s'.", name); break; } } @@ -834,8 +868,7 @@ public enum GameImage { img = img.getScaledCopy(0.5f); return img; } catch (SlickException e) { - BubNotifListener.EVENT.make().onBubNotif( - String.format("Failed to set image '%s'.", filename), Colors.BUB_RED); + bubNotifs.sendf(BUB_RED, "Failed to set image '%s'.", filename); } } return null; diff --git a/src/itdelatrisu/opsu/GameMod.java b/src/itdelatrisu/opsu/GameMod.java index 8449db60..7a0ed8e5 100644 --- a/src/itdelatrisu/opsu/GameMod.java +++ b/src/itdelatrisu/opsu/GameMod.java @@ -100,7 +100,7 @@ public enum GameMod { */ public void init(int width, int height) { float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f; - float offsetY = GameImage.MOD_EASY.getImage().getHeight() * 1.5f; + float offsetY = GameImage.MOD_EASY.getHeight() * 1.5f; this.x = width / 30f; this.y = multY + Fonts.LARGE.getLineHeight() * 3f + offsetY * index; } @@ -193,7 +193,7 @@ public enum GameMod { // create buttons float baseX = Category.EASY.getX() + Fonts.LARGE.getWidth(Category.EASY.getName()) * 1.25f; - float offsetX = GameImage.MOD_EASY.getImage().getWidth() * 2.1f; + float offsetX = GameImage.MOD_EASY.getWidth() * 2.1f; for (GameMod mod : GameMod.values()) { Image img = mod.image.getImage(); mod.button = new MenuButton(img, diff --git a/src/itdelatrisu/opsu/NativeLoader.java b/src/itdelatrisu/opsu/NativeLoader.java index 840fc9b5..c6338619 100644 --- a/src/itdelatrisu/opsu/NativeLoader.java +++ b/src/itdelatrisu/opsu/NativeLoader.java @@ -19,7 +19,6 @@ package itdelatrisu.opsu; import org.newdawn.slick.util.Log; -import yugecin.opsudance.utils.ManifestWrapper; import java.io.File; import java.io.IOException; @@ -50,42 +49,32 @@ public class NativeLoader { * Unpacks natives for the current operating system to the natives directory. * @throws IOException if an I/O exception occurs */ - public static void loadNatives(JarFile jarfile, ManifestWrapper manifest) throws IOException { + public static void loadNatives(JarFile jarfile) throws IOException { if (!config.NATIVE_DIR.exists() && !config.NATIVE_DIR.mkdir()) { String msg = String.format("Could not create folder '%s'", config.NATIVE_DIR.getAbsolutePath()); throw new RuntimeException(msg); } - String osName = System.getProperty("os.name"); - String nativekey = null; + final String osName = System.getProperty("os.name"); + final String[] files; if (osName.startsWith("Win")) { - nativekey = "WinNatives"; + files = new String[] { "OpenAL32.dll", "OpenAL64.dll", "lwjgl.dll", "lwjgl64.dll" }; } else if (osName.startsWith("Linux")) { - nativekey = "NixNatives"; + files = new String[] { "liblwjgl.so", "liblwjgl64.so", "libopenal.so", "libopenal64.so" }; } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { - nativekey = "MacNatives"; - } - - if (nativekey == null) { + files = new String[] { "liblwjgl.dylib", "openal.dylib" }; + } else { Log.warn("Cannot determine natives for os " + osName); return; } - String natives = manifest.valueOrDefault(null, nativekey, null); - if (natives == null) { - String msg = String.format("No entry for '%s' in manifest, jar is badly packed or damaged", - nativekey); - throw new RuntimeException(msg); - } - - String[] nativefiles = natives.split(","); - for (String nativefile : nativefiles) { - File unpackedFile = new File(config.NATIVE_DIR, nativefile); + for (String file : files) { + File unpackedFile = new File(config.NATIVE_DIR, file); if (unpackedFile.exists()) { continue; } - Utils.unpackFromJar(jarfile, unpackedFile, nativefile); + Utils.unpackFromJar(jarfile, unpackedFile, file); } } diff --git a/src/itdelatrisu/opsu/ScoreData.java b/src/itdelatrisu/opsu/ScoreData.java index b873672f..28ed044e 100644 --- a/src/itdelatrisu/opsu/ScoreData.java +++ b/src/itdelatrisu/opsu/ScoreData.java @@ -101,7 +101,7 @@ public class ScoreData implements Comparable { baseX = containerWidth * 0.01f; baseY = topY; buttonWidth = containerWidth * 0.4f; - float gradeHeight = GameImage.MENU_BUTTON_BG.getImage().getHeight() * 0.45f; + float gradeHeight = GameImage.MENU_BUTTON_BG.getHeight() * 0.45f; buttonHeight = Math.max(gradeHeight, Fonts.DEFAULT.getLineHeight() * 3.03f); buttonOffset = buttonHeight + gradeHeight / 10f; buttonAreaHeight = (SongMenu.MAX_SCORE_BUTTONS - 1) * buttonOffset + buttonHeight; diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index cbebead9..c3a49687 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -464,8 +464,8 @@ public class Utils { */ public static int getRegion(double x, double y) { int q = 0; - if (y < displayContainer.height / 2d) q = 2; - if (x < displayContainer.width / 2d) q |= 1; + if (y < height2) q = 2; + if (x < width2) q |= 1; return q; } @@ -483,24 +483,24 @@ public class Utils { */ public static float[] mirrorPoint(float x, float y) { - double dx = x - displayContainer.width / 2d; - double dy = y - displayContainer.height / 2d; + double dx = x - width2; + double dy = y - height2; double ang = Math.atan2(dy, dx); double d = -Math.sqrt(dx * dx + dy * dy); return new float[]{ - (float) (displayContainer.width / 2d + Math.cos(ang) * d), - (float) (displayContainer.height / 2d + Math.sin(ang) * d) + (float) (width2 + Math.cos(ang) * d), + (float) (height2 + Math.sin(ang) * d) }; } public static float[] mirrorPoint(float x, float y, float degrees) { - double dx = x - displayContainer.width / 2d; - double dy = y - displayContainer.height / 2d; + double dx = x - width2; + double dy = y - height2; double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d); double d = Math.sqrt(dx * dx + dy * dy); return new float[]{ - (float) (displayContainer.width / 2d + Math.cos(ang) * d), - (float) (displayContainer.height / 2d + Math.sin(ang) * d) + (float) (width2 + Math.cos(ang) * d), + (float) (height2 + Math.sin(ang) * d) }; } diff --git a/src/itdelatrisu/opsu/audio/MusicController.java b/src/itdelatrisu/opsu/audio/MusicController.java index eee4135a..8d957ed8 100644 --- a/src/itdelatrisu/opsu/audio/MusicController.java +++ b/src/itdelatrisu/opsu/audio/MusicController.java @@ -22,6 +22,7 @@ import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.states.Game; +import yugecin.opsudance.options.Options; import java.io.File; import java.io.IOException; @@ -33,7 +34,6 @@ import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; -import itdelatrisu.opsu.ui.Colors; import org.lwjgl.BufferUtils; import org.lwjgl.openal.AL; import org.lwjgl.openal.AL10; @@ -45,10 +45,10 @@ import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; import org.tritonus.share.sampled.file.TAudioFileFormat; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; +import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; /** @@ -103,8 +103,7 @@ public class MusicController { if (lastBeatmap == null || !beatmap.audioFilename.equals(lastBeatmap.audioFilename)) { final File audioFile = beatmap.audioFilename; if (!audioFile.isFile() && !ResourceLoader.resourceExists(audioFile.getPath())) { - BarNotifListener.EVENT.make().onBarNotif(String.format("Could not find track '%s'.", - audioFile.getName())); + barNotifs.sendf("Could not find track '%s'.", audioFile.getName()); return; } @@ -159,7 +158,7 @@ public class MusicController { } catch (Exception e) { String err = String.format("Could not play track '%s'.", file.getName()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } } @@ -219,6 +218,17 @@ public class MusicController { return (float) ((((trackPosition - beatTime) * 100.0) % beatLength) / beatLength); } + /** + * gets the current beat length + * @return + */ + public static Float getBeatLength() { + if (!updateTimingPoint()) + return null; + + return lastTimingPoint.getBeatLength(); + } + /** * Gets the progress of the current measure. * @return a measure progress value [0,1) where 0 marks the start of the measure and @@ -349,10 +359,14 @@ public class MusicController { * If no track is loaded, 0 will be returned. */ public static int getPosition() { + int offset = OPTION_MUSIC_OFFSET.val; + if (lastBeatmap != null) + offset += lastBeatmap.localMusicOffset; + if (isPlaying()) - return (int) (player.getPosition() * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset); + return (int) (player.getPosition() * 1000 + offset); else if (isPaused()) - return Math.max((int) (pauseTime * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset), 0); + return Math.max((int) (pauseTime * 1000 + offset), 0); else return 0; } diff --git a/src/itdelatrisu/opsu/audio/SoundController.java b/src/itdelatrisu/opsu/audio/SoundController.java index 659b9fbb..2f62d3d2 100644 --- a/src/itdelatrisu/opsu/audio/SoundController.java +++ b/src/itdelatrisu/opsu/audio/SoundController.java @@ -36,15 +36,14 @@ import javax.sound.sampled.DataLine; import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.SlickException; import org.newdawn.slick.util.ResourceLoader; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.options.Configuration; import yugecin.opsudance.skinning.SkinService; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; +import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; /** @@ -220,8 +219,8 @@ public class SoundController { // menu and game sounds for (SoundEffect s : SoundEffect.values()) { if ((currentFileName = getSoundFileName(s.getFileName())) == null) { - BubNotifListener.EVENT.make().onBubNotif( - "Could not find sound file " + s.getFileName(), Colors.BUB_ORANGE); + final String name = s.getFileName(); + bubNotifs.send(BUB_ORANGE, "Could not find sound file " + name); continue; } MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3")); @@ -240,8 +239,10 @@ public class SoundController { for (HitSound s : HitSound.values()) { String filename = String.format("%s-%s", ss.getName(), s.getFileName()); if ((currentFileName = getSoundFileName(filename)) == null) { - BubNotifListener.EVENT.make().onBubNotif( - "Could not find hit sound file " + filename, Colors.BUB_ORANGE); + bubNotifs.send( + BUB_ORANGE, + "Could not find hit sound file " + filename + ); continue; } MultiClip newClip = loadClip(currentFileName, false); @@ -398,8 +399,7 @@ public class SoundController { @Override public void error() { - BarNotifListener.EVENT.make().onBarNotif( - "Failed to download track preview"); + barNotifs.send("Failed to download track preview"); } }); try { diff --git a/src/itdelatrisu/opsu/beatmap/Beatmap.java b/src/itdelatrisu/opsu/beatmap/Beatmap.java index acb24feb..dac22ad1 100644 --- a/src/itdelatrisu/opsu/beatmap/Beatmap.java +++ b/src/itdelatrisu/opsu/beatmap/Beatmap.java @@ -89,6 +89,9 @@ public class Beatmap implements Comparable { /** The last time this beatmap was played (timestamp). */ public long lastPlayed = 0; + /** The local music offset. */ + public int localMusicOffset = 0; + /** * [General] */ diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java index 7550d0af..1e31d024 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java @@ -31,13 +31,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.Color; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.Nullable; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.skinning.SkinService; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -890,7 +889,7 @@ public class BeatmapParser { private static void logAndShowErrorNotification(Exception e, String message, Object... formatArgs) { message = String.format(message, formatArgs); Log.error(message, e); - BubNotifListener.EVENT.make().onBubNotif(message, Colors.BUB_RED); + bubNotifs.send(BUB_RED, message); } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java index 1cb1d72c..2e6ea9db 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java @@ -21,8 +21,6 @@ package itdelatrisu.opsu.beatmap; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.db.BeatmapDB; -import itdelatrisu.opsu.ui.Colors; -import yugecin.opsudance.events.BubNotifListener; import java.io.File; import java.io.IOException; @@ -36,6 +34,9 @@ import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static itdelatrisu.opsu.ui.Colors.*; +import static yugecin.opsudance.core.InstanceContainer.*; + /** * Indexed, expanding, doubly-linked list data type for song groups. */ @@ -215,7 +216,7 @@ public class BeatmapSetList { try { Utils.deleteToTrash(dir); } catch (IOException e) { - BubNotifListener.EVENT.make().onBubNotif("Could not delete song group", Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, "Could not delete song group"); } if (ws != null) ws.resume(); @@ -271,7 +272,7 @@ public class BeatmapSetList { try { Utils.deleteToTrash(file); } catch (IOException e) { - BubNotifListener.EVENT.make().onBubNotif("Could not delete song", Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, "Could not delete song"); } if (ws != null) ws.resume(); diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java b/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java index b49acd37..8b36f570 100644 --- a/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java +++ b/src/itdelatrisu/opsu/beatmap/BeatmapWatchService.java @@ -38,11 +38,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -101,7 +99,7 @@ public class BeatmapWatchService { ws.register(config.beatmapDir.toPath()); } catch (IOException e) { Log.error("Could not create watch service", e); - BubNotifListener.EVENT.make().onBubNotif("Could not create watch service", Colors.BUB_RED); + bubNotifs.send(BUB_RED, "Could not create watch service"); return; } @@ -124,7 +122,7 @@ public class BeatmapWatchService { } catch (IOException e) { String msg = "An I/O exception occurred while closing the previous watch service."; Log.error(msg, e); - BarNotifListener.EVENT.make().onBarNotif(msg); + barNotifs.send(msg); ws = null; } } diff --git a/src/itdelatrisu/opsu/beatmap/HitObject.java b/src/itdelatrisu/opsu/beatmap/HitObject.java index 0503a85a..be30b7bc 100644 --- a/src/itdelatrisu/opsu/beatmap/HitObject.java +++ b/src/itdelatrisu/opsu/beatmap/HitObject.java @@ -240,7 +240,7 @@ public class HitObject { String[] edgeHitSoundTokens = tokens[8].split("\\|"); this.edgeHitSound = new byte[edgeHitSoundTokens.length]; for (int j = 0; j < edgeHitSoundTokens.length; j++) - edgeHitSound[j] = Byte.parseByte(edgeHitSoundTokens[j]); + edgeHitSound[j] = (byte) Short.parseShort(edgeHitSoundTokens[j]); } if (tokens.length > 9) { String[] edgeAdditionTokens = tokens[9].split("\\|"); diff --git a/src/itdelatrisu/opsu/beatmap/OszUnpacker.java b/src/itdelatrisu/opsu/beatmap/OszUnpacker.java index 05e7182a..a31ed064 100644 --- a/src/itdelatrisu/opsu/beatmap/OszUnpacker.java +++ b/src/itdelatrisu/opsu/beatmap/OszUnpacker.java @@ -23,12 +23,11 @@ import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; -import itdelatrisu.opsu.ui.Colors; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.InstanceContainer.*; /** @@ -97,7 +96,7 @@ public class OszUnpacker { } catch (ZipException e) { String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } } diff --git a/src/itdelatrisu/opsu/db/BeatmapDB.java b/src/itdelatrisu/opsu/db/BeatmapDB.java index 745271db..e0d4ca8e 100644 --- a/src/itdelatrisu/opsu/db/BeatmapDB.java +++ b/src/itdelatrisu/opsu/db/BeatmapDB.java @@ -20,6 +20,7 @@ package itdelatrisu.opsu.db; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.BeatmapParser; +import yugecin.opsudance.core.errorhandling.ErrorHandler; import java.io.File; import java.sql.Connection; @@ -47,7 +48,7 @@ public class BeatmapDB { * This value should be changed whenever the database format changes. * Add any update queries to the {@link #getUpdateQueries(int)} method. */ - private static final int DATABASE_VERSION = 20161222; + private static final int DATABASE_VERSION = 20161225; /** * Returns a list of SQL queries to apply, in order, to update from @@ -64,6 +65,10 @@ public class BeatmapDB { list.add("ALTER TABLE beatmaps ADD COLUMN lastPlayed INTEGER"); list.add("UPDATE beatmaps SET dateAdded = 0, favorite = 0, playCount = 0, lastPlayed = 0"); } + if (version < 20161225) { + list.add("ALTER TABLE beatmaps ADD COLUMN localOffset INTEGER"); + list.add("UPDATE beatmaps SET localOffset = 0"); + } /* add future updates here */ @@ -85,7 +90,7 @@ public class BeatmapDB { /** Query statements. */ private static PreparedStatement insertStmt, selectStmt, deleteMapStmt, deleteGroupStmt, - setStarsStmt, updatePlayStatsStmt, setFavoriteStmt, updateSizeStmt; + setStarsStmt, updatePlayStatsStmt, setFavoriteStmt, setLocalOffsetStmt, updateSizeStmt; /** Current size of beatmap cache table. */ private static int cacheSize = -1; @@ -121,7 +126,7 @@ public class BeatmapDB { "INSERT INTO beatmaps VALUES (" + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?," + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?," + - "?, ?, ?, ?, ?, ?" + + "?, ?, ?, ?, ?, ?, ?" + ")" ); selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?"); @@ -130,6 +135,7 @@ public class BeatmapDB { setStarsStmt = connection.prepareStatement("UPDATE beatmaps SET stars = ? WHERE dir = ? AND file = ?"); updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?"); setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?"); + setLocalOffsetStmt = connection.prepareStatement("UPDATE beatmaps SET localOffset = ? WHERE dir = ? AND file = ?"); } catch (SQLException e) { explode("Failed to prepare beatmap statements.", e, DEFAULT_OPTIONS); } @@ -153,7 +159,7 @@ public class BeatmapDB { "mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " + "bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT, " + "md5hash TEXT, stars REAL, " + - "dateAdded INTEGER, favorite BOOLEAN, playCount INTEGER, lastPlayed INTEGER" + + "dateAdded INTEGER, favorite BOOLEAN, playCount INTEGER, lastPlayed INTEGER, localOffset INTEGER" + "); " + "CREATE TABLE IF NOT EXISTS info (" + "key TEXT NOT NULL UNIQUE, value TEXT" + @@ -402,6 +408,7 @@ public class BeatmapDB { stmt.setBoolean(44, beatmap.favorite); stmt.setInt(45, beatmap.playCount); stmt.setLong(46, beatmap.lastPlayed); + stmt.setInt(47, beatmap.localMusicOffset); } catch (SQLException e) { throw e; } catch (Exception e) { @@ -549,6 +556,7 @@ public class BeatmapDB { beatmap.favorite = rs.getBoolean(44); beatmap.playCount = rs.getInt(45); beatmap.lastPlayed = rs.getLong(46); + beatmap.localMusicOffset = rs.getInt(47); } catch (SQLException e) { throw e; } catch (Exception e) { @@ -694,6 +702,24 @@ public class BeatmapDB { } } + /** + * Updates the local music offset for a beatmap in the database. + * @param beatmap the beatmap + */ + public static void updateLocalOffset(Beatmap beatmap) { + if (connection == null) + return; + try { + setLocalOffsetStmt.setInt(1, beatmap.localMusicOffset); + setLocalOffsetStmt.setString(2, beatmap.getFile().getParentFile().getName()); + setLocalOffsetStmt.setString(3, beatmap.getFile().getName()); + setLocalOffsetStmt.executeUpdate(); + } catch (SQLException e) { + ErrorHandler.explode(String.format("Failed to update local music offset for beatmap '%s' in database.", + beatmap.toString()), e, ErrorHandler.DEFAULT_OPTIONS); + } + } + /** * Closes the connection to the database. */ diff --git a/src/itdelatrisu/opsu/downloads/Download.java b/src/itdelatrisu/opsu/downloads/Download.java index 1b65d5d3..35f59134 100644 --- a/src/itdelatrisu/opsu/downloads/Download.java +++ b/src/itdelatrisu/opsu/downloads/Download.java @@ -33,10 +33,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; +import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; /** @@ -220,7 +220,7 @@ public class Download { else if (redirectCount > MAX_REDIRECTS) error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS); if (error != null) { - BubNotifListener.EVENT.make().onBubNotif(error, Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, error); throw new IOException(); } diff --git a/src/itdelatrisu/opsu/downloads/DownloadNode.java b/src/itdelatrisu/opsu/downloads/DownloadNode.java index e5625306..e5899642 100644 --- a/src/itdelatrisu/opsu/downloads/DownloadNode.java +++ b/src/itdelatrisu/opsu/downloads/DownloadNode.java @@ -33,9 +33,8 @@ import java.io.File; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -132,7 +131,7 @@ public class DownloadNode { * @param index the index (to offset the button from the topmost button) */ public static boolean resultIconContains(float cx, float cy, int index) { - int iconWidth = GameImage.MUSIC_PLAY.getImage().getWidth(); + int iconWidth = GameImage.MUSIC_PLAY.getWidth(); float x = buttonBaseX + buttonWidth * 0.001f; float y = buttonBaseY + (index * buttonOffset) + buttonHeight / 2f; return ((cx > x && cx < x + iconWidth) && @@ -187,7 +186,7 @@ public class DownloadNode { * @param index the index (to offset the button from the topmost button) */ public static boolean downloadIconContains(float cx, float cy, int index) { - int iconWidth = GameImage.DELETE.getImage().getWidth(); + int iconWidth = GameImage.DELETE.getWidth(); float edgeX = infoBaseX + infoWidth * 0.985f; float y = infoBaseY + (index * infoHeight); float marginY = infoHeight * 0.04f; @@ -280,14 +279,12 @@ public class DownloadNode { download.setListener(new DownloadListener() { @Override public void completed() { - BarNotifListener.EVENT.make().onBarNotif( - String.format("Download complete: %s", getTitle())); + barNotifs.sendf("Download complete: %s", getTitle()); } @Override public void error() { - BarNotifListener.EVENT.make().onBarNotif( - "Download failed due to a connection error."); + barNotifs.send("Download failed due to a connection error."); } }); this.download = download; @@ -409,9 +406,10 @@ public class DownloadNode { public void drawDownload(Graphics g, float position, int id, boolean hover) { Download download = this.download; // in case clearDownload() is called asynchronously if (download == null) { - BubNotifListener.EVENT.make().onBubNotif( - "Trying to draw download information for button without Download object", - Colors.BUB_ORANGE); + bubNotifs.send( + BUB_ORANGE, + "Trying to draw download information for button without Download object" + ); return; } diff --git a/src/itdelatrisu/opsu/downloads/Updater.java b/src/itdelatrisu/opsu/downloads/Updater.java index f0d95efd..5a199f82 100644 --- a/src/itdelatrisu/opsu/downloads/Updater.java +++ b/src/itdelatrisu/opsu/downloads/Updater.java @@ -32,11 +32,10 @@ import java.util.Date; import java.util.Locale; import java.util.Properties; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; import yugecin.opsudance.core.Constants; -import yugecin.opsudance.events.BarNotifListener; +import yugecin.opsudance.utils.SimpleVersion; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; import static yugecin.opsudance.core.InstanceContainer.*; @@ -92,7 +91,7 @@ public class Updater { private Status status; /** The current and latest versions. */ - private DefaultArtifactVersion currentVersion, latestVersion; + private SimpleVersion currentVersion, latestVersion; /** The version information if the program was just updated. */ private String updatedFromVersion, updatedToVersion; @@ -107,7 +106,7 @@ public class Updater { if (currentVersion == null) { return "unknown version"; } - return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion(); + return currentVersion.toString(); } public Updater() { @@ -182,13 +181,13 @@ public class Updater { * @param props the set of properties * @return the version, or null if not found */ - private DefaultArtifactVersion getVersion(Properties props) { - String version = props.getProperty("version"); - if (version == null || version.equals("${pom.version}")) { - status = Status.INTERNAL_ERROR; - return null; - } else - return new DefaultArtifactVersion(version); + private SimpleVersion getVersion(Properties props) { + final String version = props.getProperty("version"); + if (version != null && !version.equals("${pom.version}")) { + return SimpleVersion.parse(version); + } + this.status = Status.INTERNAL_ERROR; + return null; } /** @@ -196,7 +195,7 @@ public class Updater { * @throws IOException if an I/O exception occurs */ public void checkForUpdates() throws IOException { - if (status != Status.INITIAL || config.USE_XDG) + if (status != Status.INITIAL) return; status = Status.CHECKING; @@ -241,14 +240,13 @@ public class Updater { @Override public void completed() { status = Status.UPDATE_DOWNLOADED; - BarNotifListener.EVENT.make().onBarNotif("Update has finished downloading"); + barNotifs.send("Update has finished downloading"); } @Override public void error() { status = Status.CONNECTION_ERROR; - BarNotifListener.EVENT.make().onBarNotif( - "Update failed due to a connection error."); + barNotifs.send("Update failed due to a connection error."); } }); } diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index c2c5f6d3..621ee757 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -370,13 +370,13 @@ public class Slider extends GameObject { float followCircleScale = 1f + (tickExpandTime / (float) TICK_EXPAND_TIME) * 0.1f; float followAlpha = 1f; if (followCircleActive && followExpandTime < FOLLOW_EXPAND_TIME) { - followExpandTime += displayContainer.renderDelta; + followExpandTime += renderDelta; followCircleScale *= 0.5f; float progress = AnimationEquation.OUT_QUAD.calc((float) followExpandTime / FOLLOW_EXPAND_TIME); followCircleScale = followCircleScale + followCircleScale * progress; followAlpha = progress; } else if (!followCircleActive) { - followExpandTime -= displayContainer.renderDelta; + followExpandTime -= renderDelta; if (followExpandTime > FOLLOW_SHRINK_TIME) { followExpandTime = FOLLOW_SHRINK_TIME; } @@ -391,7 +391,7 @@ public class Slider extends GameObject { float oldAlphaBlack = Colors.BLACK_ALPHA.a; Colors.BLACK_ALPHA.a = 0.75f; g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, 0, displayContainer.width, displayContainer.height); + g.fillRect(0, 0, width, height); Colors.BLACK_ALPHA.a = oldAlphaBlack; } } diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java index 0e868ab5..7b8a5ccd 100644 --- a/src/itdelatrisu/opsu/objects/Spinner.java +++ b/src/itdelatrisu/opsu/objects/Spinner.java @@ -34,15 +34,14 @@ import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.skinning.SkinService; +import static yugecin.opsudance.core.InstanceContainer.*; + /** * Data type representing a spinner object. */ public class Spinner extends GameObject { - /** Container dimensions. */ - private static int width, height; /** The map's overall difficulty value. */ private static float overallDifficulty = 5f; @@ -112,9 +111,7 @@ public class Spinner extends GameObject { * Initializes the Spinner data type with images and dimensions. * @param difficulty the map's overall difficulty value */ - public static void init(DisplayContainer displayContainer, float difficulty) { - width = displayContainer.width; - height = displayContainer.height; + public static void init(float difficulty) { overallDifficulty = difficulty; } @@ -207,7 +204,7 @@ public class Spinner extends GameObject { // rpm Image rpmImg = GameImage.SPINNER_RPM.getImage(); rpmImg.setAlpha(alpha); - rpmImg.drawCentered(width / 2f, height - rpmImg.getHeight() / 2f); + rpmImg.drawCentered(width2, height - rpmImg.getHeight() / 2f); if (timeDiff < 0) data.drawSymbolString(Integer.toString(drawnRPM), (width + rpmImg.getWidth() * 0.95f) / 2f, height - data.getScoreSymbolImage('0').getHeight() * 1.025f, 1f, 1f, true); @@ -225,21 +222,21 @@ public class Spinner extends GameObject { // main spinner elements GameImage.SPINNER_CIRCLE.getImage().setAlpha(alpha); GameImage.SPINNER_CIRCLE.getImage().setRotation(drawRotation * 360f); - GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2); + GameImage.SPINNER_CIRCLE.getImage().drawCentered(width2, height2); if (!GameMod.HIDDEN.isActive()) { float approachScale = 1 - Utils.clamp(((float) timeDiff / (hitObject.getTime() - hitObject.getEndTime())), 0f, 1f); Image approachCircleScaled = GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(approachScale); approachCircleScaled.setAlpha(alpha); - approachCircleScaled.drawCentered(width / 2, height / 2); + approachCircleScaled.drawCentered(width2, height2); } GameImage.SPINNER_SPIN.getImage().setAlpha(alpha); - GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4); + GameImage.SPINNER_SPIN.getImage().drawCentered(width2, height * 3 / 4); if (spinnerComplete) { - GameImage.SPINNER_CLEAR.getImage().drawCentered(width / 2, height / 4); + GameImage.SPINNER_CLEAR.getImage().drawCentered(width2, height / 4); int extraRotations = (int) (rotations - rotationsNeeded); if (extraRotations > 0) - data.drawSymbolNumber(extraRotations * 1000, width / 2, height * 2 / 3, 1f, 1f); + data.drawSymbolNumber(extraRotations * 1000, width2, height * 2 / 3, 1f, 1f); } } @@ -261,14 +258,14 @@ public class Spinner extends GameObject { else result = GameData.HIT_MISS; - data.sendHitResult(hitObject.getEndTime(), result, width / 2, height / 2, + data.sendHitResult(hitObject.getEndTime(), result, width2, height2, Color.transparent, true, hitObject, HitObjectType.SPINNER, true, 0, null, false); return result; } @Override public boolean mousePressed(int x, int y, int trackPosition) { - lastAngle = (float) Math.atan2(x - (height / 2), y - (width / 2)); + lastAngle = (float) Math.atan2(x - height2, y - width2); return false; } @@ -297,7 +294,7 @@ public class Spinner extends GameObject { angleDiff = delta * SPUN_OUT_MULTIPLIER; isSpinning = true; } else { - float angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2)); + float angle = (float) Math.atan2(mouseY - height2, mouseX - width2); // set initial angle to current mouse position to skip first click if (!isSpinning && (keyPressed || GameMod.RELAX.isActive())) { diff --git a/src/itdelatrisu/opsu/objects/curves/Curve.java b/src/itdelatrisu/opsu/objects/curves/Curve.java index cd21a525..bf1f5353 100644 --- a/src/itdelatrisu/opsu/objects/curves/Curve.java +++ b/src/itdelatrisu/opsu/objects/curves/Curve.java @@ -98,13 +98,13 @@ public abstract class Curve { * @param circleDiameter the circle diameter * @param borderColor the curve border color */ - public static void init(int width, int height, float circleDiameter, Color borderColor) { + public static void init(float circleDiameter, Color borderColor) { Curve.borderColor = borderColor; ContextCapabilities capabilities = GLContext.getCapabilities(); mmsliderSupported = capabilities.OpenGL30; if (mmsliderSupported) { - CurveRenderState.init(width, height, circleDiameter); + CurveRenderState.init(circleDiameter); } else if (SkinService.skin.getSliderStyle() != Skin.STYLE_PEPPYSLIDER) { Log.warn("New slider style requires OpenGL 3.0."); } diff --git a/src/itdelatrisu/opsu/render/CurveRenderState.java b/src/itdelatrisu/opsu/render/CurveRenderState.java index ebf365e3..c69aa034 100644 --- a/src/itdelatrisu/opsu/render/CurveRenderState.java +++ b/src/itdelatrisu/opsu/render/CurveRenderState.java @@ -48,8 +48,6 @@ import static yugecin.opsudance.options.Options.*; * @author Bigpet {@literal } */ public class CurveRenderState { - /** The width and height of the display container this curve gets drawn into. */ - protected static int containerWidth, containerHeight; /** Thickness of the curve. */ protected static int scale; @@ -79,14 +77,9 @@ public class CurveRenderState { /** * Set the width and height of the container that Curves get drawn into. * Should be called before any curves are drawn. - * @param width the container width - * @param height the container height * @param circleDiameter the circle diameter */ - public static void init(int width, int height, float circleDiameter) { - containerWidth = width; - containerHeight = height; - + public static void init(float circleDiameter) { // equivalent to what happens in Slider.init() scale = (int) (circleDiameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480) //scale = scale * 118 / 128; //for curves exactly as big as the sliderball @@ -410,8 +403,6 @@ public class CurveRenderState { x1 = m[0]; y1 = m[1]; } - float divx = containerWidth / 2.0f; - float divy = containerHeight / 2.0f; float offx = -1.0f; float offy = 1.0f; float radius = scale / 2; @@ -419,8 +410,8 @@ public class CurveRenderState { for (int i = 0; i < NewCurveStyleState.unitCone.length / 6; ++i) { buff.put(NewCurveStyleState.unitCone[i * 6 + 0]); buff.put(NewCurveStyleState.unitCone[i * 6 + 1]); - buff.put(offx + (x1 + radius * NewCurveStyleState.unitCone[i * 6 + 2]) / divx); - buff.put(offy - (y1 + radius * NewCurveStyleState.unitCone[i * 6 + 3]) / divy); + buff.put(offx + (x1 + radius * NewCurveStyleState.unitCone[i * 6 + 2]) / width2); + buff.put(offy - (y1 + radius * NewCurveStyleState.unitCone[i * 6 + 3]) / height2); buff.put(NewCurveStyleState.unitCone[i * 6 + 4]); buff.put(NewCurveStyleState.unitCone[i * 6 + 5]); } diff --git a/src/itdelatrisu/opsu/replay/Replay.java b/src/itdelatrisu/opsu/replay/Replay.java index 9296f55e..6550e96e 100644 --- a/src/itdelatrisu/opsu/replay/Replay.java +++ b/src/itdelatrisu/opsu/replay/Replay.java @@ -39,13 +39,12 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import itdelatrisu.opsu.ui.Colors; import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; import org.newdawn.slick.util.Log; import lzma.streams.LzmaOutputStream; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; import static yugecin.opsudance.core.InstanceContainer.*; @@ -275,7 +274,7 @@ public class Replay { public void save() { // create replay directory if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) { - BubNotifListener.EVENT.make().onBubNotif("Failed to create replay directory", Colors.BUB_RED); + bubNotifs.send(BUB_RED, "Failed to create replay directory"); return; } diff --git a/src/itdelatrisu/opsu/replay/ReplayImporter.java b/src/itdelatrisu/opsu/replay/ReplayImporter.java index 84b0a44e..3d002bf1 100644 --- a/src/itdelatrisu/opsu/replay/ReplayImporter.java +++ b/src/itdelatrisu/opsu/replay/ReplayImporter.java @@ -28,10 +28,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.InstanceContainer.*; /** @@ -69,7 +68,7 @@ public class ReplayImporter { if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) { String err = String.format("Failed to create replay directory '%s'.", config.replayDir.getAbsolutePath()); Log.error(err); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); return; } @@ -83,7 +82,7 @@ public class ReplayImporter { moveToFailedDirectory(file); String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); continue; } Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash); @@ -102,7 +101,7 @@ public class ReplayImporter { moveToFailedDirectory(file); String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()); Log.error(err); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } } diff --git a/src/itdelatrisu/opsu/skins/SkinLoader.java b/src/itdelatrisu/opsu/skins/SkinLoader.java index 799a2da2..6c45e747 100644 --- a/src/itdelatrisu/opsu/skins/SkinLoader.java +++ b/src/itdelatrisu/opsu/skins/SkinLoader.java @@ -29,10 +29,11 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedList; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.Color; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; + +import static itdelatrisu.opsu.ui.Colors.*; +import static yugecin.opsudance.core.InstanceContainer.*; /** * Loads skin configuration files. @@ -293,7 +294,7 @@ public class SkinLoader { } catch (IOException e) { String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } return skin; diff --git a/src/itdelatrisu/opsu/states/ButtonMenu.java b/src/itdelatrisu/opsu/states/ButtonMenu.java index acc66611..57e3b9f0 100644 --- a/src/itdelatrisu/opsu/states/ButtonMenu.java +++ b/src/itdelatrisu/opsu/states/ButtonMenu.java @@ -181,7 +181,7 @@ public class ButtonMenu extends BaseOpsuState { @Override protected float getBaseY() { - return displayContainer.height * 2f / 3; + return height * 2f / 3; } @Override @@ -203,10 +203,9 @@ public class ButtonMenu extends BaseOpsuState { float mult = GameMod.getScoreMultiplier(); String multString = String.format("Score Multiplier: %.2fx", mult); Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red; - float multY = Fonts.LARGE.getLineHeight() * 2 + displayContainer.height * 0.06f; - Fonts.LARGE.drawString( - (displayContainer.width - Fonts.LARGE.getWidth(multString)) / 2f, - multY, multString, multColor); + float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f; + final float multX = width2 - Fonts.LARGE.getWidth(multString) / 2f; + Fonts.LARGE.drawString(multX, multY, multString, multColor); // category text for (GameMod.Category category : GameMod.Category.values()) { @@ -227,14 +226,14 @@ public class ButtonMenu extends BaseOpsuState { super.preRenderUpdate(); GameMod hoverMod = null; for (GameMod mod : GameMod.values()) { - mod.hoverUpdate(displayContainer.renderDelta, mod.isActive()); - if (hoverMod == null && mod.contains(displayContainer.mouseX, displayContainer.mouseY)) + mod.hoverUpdate(renderDelta, mod.isActive()); + if (hoverMod == null && mod.contains(mouseX, mouseY)) hoverMod = mod; } // tooltips if (hoverMod != null) { - UI.updateTooltip(displayContainer.renderDelta, hoverMod.getDescription(), true); + UI.updateTooltip(renderDelta, hoverMod.getDescription(), true); } } @@ -296,13 +295,12 @@ public class ButtonMenu extends BaseOpsuState { * Initializes the menu state. */ public void revalidate(Image button, Image buttonL, Image buttonR) { - float center = displayContainer.width / 2; float baseY = getBaseY(); float offsetY = button.getHeight() * 1.25f; menuButtons = new MenuButton[buttons.length]; for (int i = 0; i < buttons.length; i++) { - MenuButton b = new MenuButton(button, buttonL, buttonR, center, baseY + (i * offsetY)); + MenuButton b = new MenuButton(button, buttonL, buttonR, width2, baseY + (i * offsetY)); b.setText(String.format("%d. %s", i + 1, buttons[i].getText()), Fonts.XLARGE, Color.white); b.setHoverFade(); menuButtons[i] = b; @@ -313,7 +311,7 @@ public class ButtonMenu extends BaseOpsuState { * Returns the base Y coordinate for the buttons. */ protected float getBaseY() { - float baseY = displayContainer.height * 0.2f; + float baseY = height * 0.2f; baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight()); return baseY; } @@ -325,7 +323,7 @@ public class ButtonMenu extends BaseOpsuState { public void render(Graphics g) { // draw title if (actualTitle != null) { - float marginX = displayContainer.width * 0.015f, marginY = displayContainer.height * 0.01f; + float marginX = width * 0.015f, marginY = height * 0.01f; int lineHeight = Fonts.LARGE.getLineHeight(); for (int i = 0, size = actualTitle.size(); i < size; i++) Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white); @@ -342,15 +340,16 @@ public class ButtonMenu extends BaseOpsuState { * Updates the menu state. */ public void preRenderUpdate() { - float center = displayContainer.width / 2f; - boolean centerOffsetUpdated = centerOffset.update(displayContainer.renderDelta); + boolean centerOffsetUpdated = centerOffset.update(renderDelta); float centerOffsetX = centerOffset.getValue(); + final float[] offsets = { centerOffsetX, - centerOffsetX }; for (int i = 0; i < buttons.length; i++) { - menuButtons[i].hoverUpdate(displayContainer.renderDelta, displayContainer.mouseX, displayContainer.mouseY); + menuButtons[i].hoverUpdate(renderDelta, mouseX, mouseY); // move button to center - if (centerOffsetUpdated) - menuButtons[i].setX((i % 2 == 0) ? center + centerOffsetX : center - centerOffsetX); + if (centerOffsetUpdated) { + menuButtons[i].setX(width2 + offsets[i & 1]); + } } } @@ -394,18 +393,17 @@ public class ButtonMenu extends BaseOpsuState { * Processes a state enter request. */ public void enter() { - float center = displayContainer.width / 2f; - float centerOffsetX = displayContainer.width * OFFSET_WIDTH_RATIO; + float centerOffsetX = width * OFFSET_WIDTH_RATIO; centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE); for (int i = 0; i < buttons.length; i++) { - menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1)); + menuButtons[i].setX(width2 + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1)); menuButtons[i].resetHover(); } // create title string list actualTitle = new ArrayList<>(); String[] title = getTitle(); - int maxLineWidth = (int) (displayContainer.width * 0.96f); + int maxLineWidth = (int) (width * 0.96f); for (String aTitle : title) { // wrap text if too long if (Fonts.LARGE.getWidth(aTitle) > maxLineWidth) { @@ -601,7 +599,7 @@ public class ButtonMenu extends BaseOpsuState { // initialize buttons Image button = GameImage.MENU_BUTTON_MID.getImage(); - button = button.getScaledCopy(displayContainer.width / 2, button.getHeight()); + button = button.getScaledCopy(width2, button.getHeight()); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); for (MenuState ms : MenuState.values()) { @@ -624,7 +622,7 @@ public class ButtonMenu extends BaseOpsuState { public void preRenderUpdate() { super.preRenderUpdate(); - UI.update(displayContainer.renderDelta); + UI.update(renderDelta); MusicController.loopTrackIfEnded(false); menuState.preRenderUpdate(); } diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java index 23fe14b8..b783b300 100644 --- a/src/itdelatrisu/opsu/states/DownloadsMenu.java +++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java @@ -53,7 +53,6 @@ import org.newdawn.slick.SlickException; import org.newdawn.slick.gui.TextField; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.state.ComplexOpsuState; -import yugecin.opsudance.events.BarNotifListener; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.core.InstanceContainer.*; @@ -150,9 +149,6 @@ public class DownloadsMenu extends ComplexOpsuState { /** Beatmap set ID of the current beatmap being previewed, or -1 if none. */ private int previewID = -1; - /** The bar notification to send upon entering the state. */ - private String barNotificationOnLoad; - /** Search query, executed in {@code queryThread}. */ private SearchQuery searchQuery; @@ -273,13 +269,12 @@ public class DownloadsMenu extends ComplexOpsuState { if (this.importedNode == null) { return; } - String msg; + if (dirs.length == 1) { - msg = "Imported 1 new song."; - } else { - msg = String.format("Imported %d new songs.", dirs.length); + barNotifs.send("Imported 1 new song."); + return; } - BarNotifListener.EVENT.make().onBarNotif(msg); + barNotifs.sendf("Imported %d new songs.", dirs.length); } } @@ -298,8 +293,6 @@ public class DownloadsMenu extends ComplexOpsuState { components.clear(); - int width = displayContainer.width; - int height = displayContainer.height; int baseX = (int) (width * 0.024f); int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight()); int searchWidth = (int) (width * 0.3f); @@ -371,7 +364,7 @@ public class DownloadsMenu extends ComplexOpsuState { // dropdown menu int serverWidth = (int) (width * 0.12f); int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth; - serverMenu = new DropdownMenu(displayContainer, SERVERS, x, searchY, serverWidth) { + serverMenu = new DropdownMenu(SERVERS, x, searchY, serverWidth) { @Override public void itemSelected(int index, DownloadServer item) { resultList = null; @@ -414,7 +407,7 @@ public class DownloadsMenu extends ComplexOpsuState { GameImage.SEARCH_BG.getImage().draw(); // title - Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white); + Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white); // search g.setColor(Color.white); @@ -440,7 +433,7 @@ public class DownloadsMenu extends ComplexOpsuState { if (index >= nodes.length) break; nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(), - DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(), + DownloadNode.resultContains(mouseX, mouseY - offset, i) && !serverMenu.isHovered(), (index == focusResult), (previewID == nodes[index].getID())); } g.clearClip(); @@ -451,9 +444,9 @@ public class DownloadsMenu extends ComplexOpsuState { // pages if (nodes.length > 0) { - float baseX = displayContainer.width * 0.024f; - float buttonY = displayContainer.height * 0.2f; - float buttonWidth = displayContainer.width * 0.7f; + float baseX = width * 0.024f; + float buttonY = height * 0.2f; + float buttonWidth = width * 0.7f; Fonts.BOLD.drawString( baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f, buttonY - Fonts.BOLD.getLineHeight() * 1.3f, @@ -467,11 +460,10 @@ public class DownloadsMenu extends ComplexOpsuState { } // downloads - float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y; + float downloadsX = width * 0.75f, downloadsY = search.y; g.setColor(Colors.BLACK_BG_NORMAL); - g.fillRect(downloadsX, downloadsY, - displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f); - Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white); + g.fillRect(downloadsX, downloadsY, width * 0.25f, height - downloadsY * 2f); + Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white); int downloadsSize = DownloadList.get().size(); if (downloadsSize > 0) { int maxDownloadsShown = DownloadNode.maxDownloadsShown(); @@ -487,7 +479,7 @@ public class DownloadsMenu extends ComplexOpsuState { if (node == null) break; node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index, - DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i)); + DownloadNode.downloadContains(mouseX, mouseY - offset, i)); } g.clearClip(); @@ -510,15 +502,13 @@ public class DownloadsMenu extends ComplexOpsuState { if (importThread != null) { // darken the screen g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, 0, displayContainer.width, displayContainer.height); + g.fillRect(0, 0, width, height); UI.drawLoadingProgress(g); + } else { + backButton.draw(g); } - // back button - else - UI.getBackButton().draw(g); - UI.draw(g); } @@ -526,7 +516,7 @@ public class DownloadsMenu extends ComplexOpsuState { public void preRenderUpdate() { super.preRenderUpdate(); - int delta = displayContainer.renderDelta; + int delta = renderDelta; UI.update(delta); if (importThread == null) MusicController.loopTrackIfEnded(false); @@ -547,9 +537,7 @@ public class DownloadsMenu extends ComplexOpsuState { } importThread = null; } - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); + backButton.hoverUpdate(); prevPage.hoverUpdate(delta, mouseX, mouseY); nextPage.hoverUpdate(delta, mouseX, mouseY); clearButton.hoverUpdate(delta, mouseX, mouseY); @@ -619,7 +607,7 @@ public class DownloadsMenu extends ComplexOpsuState { } // back - if (UI.getBackButton().contains(x, y)) { + if (backButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); displayContainer.switchState(mainmenuState); return true; @@ -686,7 +674,7 @@ public class DownloadsMenu extends ComplexOpsuState { if (playing) previewID = node.getID(); } catch (SlickException e) { - BarNotifListener.EVENT.make().onBarNotif("Failed to load track preview. See log for details."); + barNotifs.send("Failed to load track preview. See log for details."); Log.error(e); } } @@ -709,7 +697,7 @@ public class DownloadsMenu extends ComplexOpsuState { if (!DownloadList.get().contains(node.getID())) { node.createDownload(serverMenu.getSelectedItem()); if (node.getDownload() == null) { - BarNotifListener.EVENT.make().onBarNotif("The download could not be started"); + barNotifs.send("The download could not be started"); } else { DownloadList.get().addNode(node); node.getDownload().start(); @@ -856,7 +844,7 @@ public class DownloadsMenu extends ComplexOpsuState { } int shift = (newValue < 0) ? 1 : -1; - scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift); + scrollLists(mouseX, mouseY, shift); return true; } @@ -950,10 +938,6 @@ public class DownloadsMenu extends ComplexOpsuState { startDownloadIndexPos.setPosition(0); pageDir = Page.RESET; previewID = -1; - if (barNotificationOnLoad != null) { - BarNotifListener.EVENT.make().onBarNotif(barNotificationOnLoad); - barNotificationOnLoad = null; - } } @Override @@ -994,10 +978,4 @@ public class DownloadsMenu extends ComplexOpsuState { else if (DownloadNode.downloadAreaContains(cx, cy)) startDownloadIndexPos.scrollOffset(shift * DownloadNode.getInfoHeight()); } - - /** - * Sends a bar notification upon entering the state. - * @param s the notification string - */ - public void notifyOnLoad(String s) { barNotificationOnLoad = s; } } diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index ca6697c6..e71fa0ad 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -24,6 +24,7 @@ import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.beatmap.Beatmap; +import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.db.BeatmapDB; @@ -60,9 +61,6 @@ import org.newdawn.slick.SlickException; import org.newdawn.slick.util.Log; import yugecin.opsudance.*; import yugecin.opsudance.core.state.ComplexOpsuState; -import yugecin.opsudance.core.state.specialstates.BubNotifState; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.options.OptionGroups; import yugecin.opsudance.sbv2.MoveStoryboard; @@ -71,6 +69,8 @@ import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.StoryboardOverlay; import yugecin.opsudance.utils.GLHelper; +import static itdelatrisu.opsu.GameImage.*; +import static itdelatrisu.opsu.ui.Colors.*; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.core.InstanceContainer.*; @@ -268,8 +268,6 @@ public class Game extends ComplexOpsuState { /** Music position bar coordinates and dimensions (for replay seeking). */ private float musicBarX, musicBarY, musicBarWidth, musicBarHeight; - public static int currentMapMusicOffset; - private int mirrorFrom; private int mirrorTo; @@ -320,9 +318,9 @@ public class Game extends ComplexOpsuState { public Game() { super(); mirrorCursor = new Cursor(true); - this.moveStoryboardOverlay = new MoveStoryboard(displayContainer); - this.optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.storyboardOptions); - this.storyboardOverlay = new StoryboardOverlay(displayContainer, moveStoryboardOverlay, optionsOverlay, this); + this.moveStoryboardOverlay = new MoveStoryboard(); + this.optionsOverlay = new OptionsOverlay(OptionGroups.storyboardOptions); + this.storyboardOverlay = new StoryboardOverlay(moveStoryboardOverlay, optionsOverlay, this); storyboardOverlay.show(); moveStoryboardOverlay.show(); optionsOverlay.setListener(storyboardOverlay); @@ -334,25 +332,26 @@ public class Game extends ComplexOpsuState { // create offscreen graphics try { - offscreen = new Image(displayContainer.width, displayContainer.height); + offscreen = new Image(width, height); gOffscreen = offscreen.getGraphics(); gOffscreen.setBackground(Color.black); } catch (SlickException e) { Log.error("could not create offscreen graphics", e); - BubNotifListener.EVENT.make().onBubNotif( - "Exception while creating offscreen graphics. See logfile for details.", - Colors.BUB_RED); + bubNotifs.send( + BUB_RED, + "Exception while creating offscreen graphics. See logfile for details." + ); } // initialize music position bar location - musicBarX = displayContainer.width * 0.01f; - musicBarY = displayContainer.height * 0.05f; - musicBarWidth = Math.max(displayContainer.width * 0.005f, 7); - musicBarHeight = displayContainer.height * 0.9f; + musicBarX = width * 0.01f; + musicBarY = height * 0.05f; + musicBarWidth = Math.max(width * 0.005f, 7); + musicBarHeight = height * 0.9f; // initialize scoreboard star stream - scoreboardStarStream = new StarStream(0, displayContainer.height * 2f / 3f, displayContainer.width / 4, 0, 0); - scoreboardStarStream.setPositionSpread(displayContainer.height / 20f); + scoreboardStarStream = new StarStream(0, height * 2f / 3f, width / 4, 0, 0); + scoreboardStarStream.setPositionSpread(height / 20f); scoreboardStarStream.setDirectionSpread(10f); scoreboardStarStream.setDurationSpread(700, 100); @@ -386,12 +385,9 @@ public class Game extends ComplexOpsuState { @Override public void render(Graphics g) { - int width = displayContainer.width; - int height = displayContainer.height; - int trackPosition = MusicController.getPosition(); if (isLeadIn()) { - trackPosition -= leadInTime - currentMapMusicOffset - OPTION_MUSIC_OFFSET.val; + trackPosition -= leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset; } if (pauseTime > -1) // returning from pause screen trackPosition = pauseTime; @@ -444,7 +440,7 @@ public class Game extends ComplexOpsuState { if (GameMod.FLASHLIGHT.isActive()) { // render hit objects offscreen Graphics.setCurrent(gOffscreen); - int trackPos = (isLeadIn()) ? (leadInTime - OPTION_MUSIC_OFFSET.val) * -1 : trackPosition; + int trackPos = (isLeadIn()) ? (leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset) * -1 : trackPosition; drawHitObjects(gOffscreen, trackPos); // restore original graphics context @@ -454,23 +450,23 @@ public class Game extends ComplexOpsuState { // draw alpha map around cursor g.setDrawMode(Graphics.MODE_ALPHA_MAP); g.clearAlphaMap(); - int mouseX, mouseY; + int mx, my; if (pauseTime > -1 && pausedMousePosition != null) { - mouseX = (int) pausedMousePosition.x; - mouseY = (int) pausedMousePosition.y; + mx = (int) pausedMousePosition.x; + my = (int) pausedMousePosition.y; } else if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { - mouseX = (int) autoMousePosition.x; - mouseY = (int) autoMousePosition.y; + mx = (int) autoMousePosition.x; + my = (int) autoMousePosition.y; } else if (isReplay) { - mouseX = replayX; - mouseY = replayY; + mx = replayX; + my = replayY; } else { - mouseX = displayContainer.mouseX; - mouseY = displayContainer.mouseY; + mx = mouseX; + my = mouseY; } int alphaRadius = flashlightRadius * 256 / 215; - int alphaX = mouseX - alphaRadius / 2; - int alphaY = mouseY - alphaRadius / 2; + int alphaX = mx - alphaRadius / 2; + int alphaY = my - alphaRadius / 2; GameImage.ALPHA_MAP.getImage().draw(alphaX, alphaY, alphaRadius, alphaRadius); // blend offscreen image @@ -557,8 +553,8 @@ public class Game extends ComplexOpsuState { // show retries if (retries >= 2 && timeDiff >= -1000) { int retryHeight = Math.max( - GameImage.SCOREBAR_BG.getImage().getHeight(), - GameImage.SCOREBAR_KI.getImage().getHeight() + GameImage.SCOREBAR_BG.getHeight(), + GameImage.SCOREBAR_KI.getHeight() ); float oldAlpha = Colors.WHITE_FADE.a; if (timeDiff < -500) @@ -571,8 +567,8 @@ public class Game extends ComplexOpsuState { Colors.WHITE_FADE.a = oldAlpha; } - if (isLeadIn()) - trackPosition = (leadInTime - OPTION_MUSIC_OFFSET.val) * -1; // render approach circles during song lead-in + if (isLeadIn()) // render approach circles during song lead-in + trackPosition = (leadInTime - OPTION_MUSIC_OFFSET.val - beatmap.localMusicOffset) * -1; // countdown if (beatmap.countdown > 0) { @@ -594,7 +590,7 @@ public class Game extends ComplexOpsuState { } } if (timeDiff < 1500 * speedModifier) { - GameImage.COUNTDOWN_2.getImage().draw(width - GameImage.COUNTDOWN_2.getImage().getWidth(), 0); + COUNTDOWN_2.getImage().draw(width - COUNTDOWN_2.getWidth(), 0); if (!countdown2Sound) { SoundController.playSound(SoundEffect.COUNT2); countdown2Sound = true; @@ -641,7 +637,7 @@ public class Game extends ComplexOpsuState { float animation = AnimationEquation.IN_OUT_QUAD.calc( Utils.clamp((trackPosition - lastRankUpdateTime) / SCOREBOARD_ANIMATION_TIME, 0f, 1f) ); - int scoreboardPosition = 2 * displayContainer.height / 3; + int scoreboardPosition = 2 * height / 3; // draw star stream behind the scores scoreboardStarStream.draw(); @@ -682,7 +678,7 @@ public class Game extends ComplexOpsuState { // draw music position bar (for replay seeking) if (isReplay && OPTION_REPLAY_SEEKING.state) { - g.setColor((musicPositionBarContains(displayContainer.mouseX, displayContainer.mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL); + g.setColor((musicPositionBarContains(mouseX, mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL); g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); if (!isLeadIn()) { g.setColor(MUSICBAR_FILL); @@ -698,8 +694,8 @@ public class Game extends ComplexOpsuState { g.fillRect(0, 0, width, height); // draw glowing hit select circle and pulse effect - int circleDiameter = GameImage.HITCIRCLE.getImage().getWidth(); - Image cursorCircle = GameImage.HITCIRCLE_SELECT.getImage().getScaledCopy(circleDiameter, circleDiameter); + int circleDiameter = HITCIRCLE.getWidth(); + Image cursorCircle = HITCIRCLE_SELECT.getScaledImage(circleDiameter, circleDiameter); cursorCircle.setAlpha(1.0f); cursorCircle.drawCentered(pausedMousePosition.x, pausedMousePosition.y); Image cursorCirclePulse = cursorCircle.getScaledCopy(1f + pausePulse); @@ -718,6 +714,15 @@ public class Game extends ComplexOpsuState { displayContainer.cursor.draw(Utils.isGameKeyPressed()); } + super.render(g); + + if (OPTION_DANCE_ENABLE_SB.state) { + optionsOverlay.render(g); + if (optionsOverlay.isActive()) { + backButton.draw(g); + } + } + UI.draw(g); //g.setColor(new Color(0.2f, 0.2f, 0.2f)); @@ -736,15 +741,20 @@ public class Game extends ComplexOpsuState { replayPlayback.render(displayContainer.renderDelta, g, ypos, trackPosition); //} } - - super.render(g); } @Override public void preRenderUpdate() { super.preRenderUpdate(); - int delta = displayContainer.renderDelta; + if (OPTION_DANCE_ENABLE_SB.state) { + optionsOverlay.preRenderUpdate(); + if (optionsOverlay.isActive()) { + backButton.hoverUpdate(); + } + } + + int delta = renderDelta; UI.update(delta); Pippi.update(delta); @@ -752,8 +762,6 @@ public class Game extends ComplexOpsuState { epiImgTime -= delta; } yugecin.opsudance.spinners.Spinner.update(delta); - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; skipButton.hoverUpdate(delta, mouseX, mouseY); if (isReplay || GameMod.AUTO.isActive()) playbackSpeed.getButton().hoverUpdate(delta, mouseX, mouseY); @@ -929,16 +937,16 @@ public class Game extends ComplexOpsuState { } else if (GameMod.AUTO.isActive()) { displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) { - double dx = autoMousePosition.x - displayContainer.width / 2d; - double dy = autoMousePosition.y - displayContainer.height / 2d; + double dx = autoMousePosition.x - width2; + double dy = autoMousePosition.y - height2; double d = Math.sqrt(dx * dx + dy * dy); double a = Math.atan2(dy, dx) + Math.PI; - mirrorCursor.setCursorPosition(displayContainer.delta, (int) (Math.cos(a) * d + displayContainer.width / 2), (int) (Math.sin(a) * d + displayContainer.height / 2)); + mirrorCursor.setCursorPosition(displayContainer.delta, (int) (Math.cos(a) * d + width2), (int) (Math.sin(a) * d + height2)); } } else if (GameMod.AUTOPILOT.isActive()) { displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); } else { - displayContainer.cursor.setCursorPosition(displayContainer.delta, displayContainer.mouseX, displayContainer.mouseY); + displayContainer.cursor.setCursorPosition(displayContainer.delta, mouseX, mouseY); } } @@ -1088,7 +1096,6 @@ public class Game extends ComplexOpsuState { if (restart != Restart.LOSE) { // update objects (loop in unlikely event of any skipped indexes) boolean keyPressed = keys != ReplayFrame.KEY_NONE; - boolean skippedObject = false; while (objectIndex < gameObjects.length && trackPosition > beatmap.objects[objectIndex].getTime()) { // check if we've already passed the next object's start time boolean overlap = (objectIndex + 1 < gameObjects.length && @@ -1096,7 +1103,6 @@ public class Game extends ComplexOpsuState { // update hit object and check completion status if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition)) { - skippedObject = true; objectIndex++; // done, so increment object index storyboardOverlay.updateIndex(objectIndex); if (objectIndex >= mirrorTo) { @@ -1119,6 +1125,10 @@ public class Game extends ComplexOpsuState { @Override public boolean keyPressed(int key, char c) { + if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.keyPressed(key, c)) { + return true; + } + if (super.keyPressed(key, c)) { return true; } @@ -1128,8 +1138,6 @@ public class Game extends ComplexOpsuState { } int trackPosition = MusicController.getPosition(); - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; // game keys if (!Keyboard.isRepeatEvent()) { @@ -1189,7 +1197,7 @@ public class Game extends ComplexOpsuState { if (0 <= time && time < 3600) { OPTION_CHECKPOINT.setValue(time); SoundController.playSound(SoundEffect.MENUCLICK); - BarNotifListener.EVENT.make().onBarNotif("Checkpoint saved."); + barNotifs.send("Checkpoint saved."); } } break; @@ -1201,7 +1209,7 @@ public class Game extends ComplexOpsuState { break; // invalid checkpoint loadCheckpoint(checkpoint); SoundController.playSound(SoundEffect.MENUHIT); - BarNotifListener.EVENT.make().onBarNotif("Checkpoint loaded."); + barNotifs.send("Checkpoint loaded."); } break; case KEY_F: @@ -1242,14 +1250,13 @@ public class Game extends ComplexOpsuState { } OPTION_DANCE_MIRROR.toggle(); break; + case KEY_SUBTRACT: case KEY_MINUS: - currentMapMusicOffset += 5; - BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset); + adjustLocalMusicOffset(-5); break; } - if (key == KEY_ADD || c == '+') { - currentMapMusicOffset -= 5; - BarNotifListener.EVENT.make().onBarNotif("Current map offset: " + currentMapMusicOffset); + if (key == KEY_EQUALS || key == KEY_ADD || c == '+') { + adjustLocalMusicOffset(5); } return true; @@ -1257,14 +1264,21 @@ public class Game extends ComplexOpsuState { @Override public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { - if (super.mouseDragged(oldx, oldy, newx, newy)) { + if (OPTION_DANCE_ENABLE_SB.state && + optionsOverlay.mouseDragged(oldx, oldy, newx, newy)) + { return true; } - return true; + + return super.mouseDragged(oldx, oldy, newx, newy); } @Override public boolean mousePressed(int button, int x, int y) { + if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mousePressed(button, x, y)) { + return true; + } + if (super.mousePressed(button, x, y)) { return true; } @@ -1340,7 +1354,7 @@ public class Game extends ComplexOpsuState { // returning from pause screen if (pauseTime > -1) { double distance = Math.hypot(pausedMousePosition.x - x, pausedMousePosition.y - y); - int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2; + int circleRadius = GameImage.HITCIRCLE.getWidth() / 2; if (distance < circleRadius) { // unpause the game pauseTime = -1; @@ -1370,6 +1384,10 @@ public class Game extends ComplexOpsuState { @Override public boolean mouseReleased(int button, int x, int y) { + if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mouseReleased(button, x, y)) { + return true; + } + if (super.mouseReleased(button, x, y)) { return true; } @@ -1399,6 +1417,10 @@ public class Game extends ComplexOpsuState { @Override public boolean keyReleased(int key, char c) { + if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.keyReleased(key, c)) { + return true; + } + if (super.keyReleased(key, c)) { return true; } @@ -1436,6 +1458,10 @@ public class Game extends ComplexOpsuState { @Override public boolean mouseWheelMoved(int newValue) { + if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mouseWheelMoved(newValue)) { + return true; + } + if (super.mouseWheelMoved(newValue)) { return true; } @@ -1453,11 +1479,9 @@ public class Game extends ComplexOpsuState { public void enter() { overlays.clear(); if (OPTION_DANCE_ENABLE_SB.state) { - overlays.add(optionsOverlay); overlays.add(moveStoryboardOverlay); overlays.add(storyboardOverlay); storyboardOverlay.onEnter(); - optionsOverlay.revalidate(); } super.enter(); @@ -1523,8 +1547,9 @@ public class Game extends ComplexOpsuState { } if (beatmap == null || beatmap.objects == null) { - BubNotifListener.EVENT.make().onBubNotif("Game was running without a beatmap", Colors.BUB_RED); + bubNotifs.send(BUB_RED, "Game was running without a beatmap"); displayContainer.switchStateInstantly(songMenuState); + return; } Dancer.instance.reset(); @@ -1552,10 +1577,9 @@ public class Game extends ComplexOpsuState { epiImgTime = OPTION_EPILEPSY_WARNING.val * 100; if (epiImgTime > 0) { epiImg = GameImage.EPILEPSY_WARNING.getImage(); - float desWidth = displayContainer.width / 2; - epiImg = epiImg.getScaledCopy(desWidth / epiImg.getWidth()); - epiImgX = (displayContainer.width - epiImg.getWidth()) / 2; - epiImgY = (displayContainer.height - epiImg.getHeight()) / 2; + epiImg = epiImg.getScaledCopy(width2 / epiImg.getWidth()); + epiImgX = width2 - epiImg.getWidth() / 2; + epiImgY = height2 - epiImg.getHeight() / 2; } // load mods @@ -1630,7 +1654,7 @@ public class Game extends ComplexOpsuState { } catch (Exception e) { String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString()); Log.error(message, e); - BubNotifListener.EVENT.make().onBubNotif(message, Colors.BUB_RED); + bubNotifs.send(BUB_RED, message); gameObjects[i] = new DummyObject(hitObject); } } @@ -1657,8 +1681,8 @@ public class Game extends ComplexOpsuState { // load replay frames if (isReplay) { // load initial data - replayX = displayContainer.width / 2; - replayY = displayContainer.height / 2; + replayX = width2; + replayY = height2; replayKeyPressed = false; replaySkipTime = -1; for (replayIndex = 0; replayIndex < replay.frames.length; replayIndex++) { @@ -1680,7 +1704,7 @@ public class Game extends ComplexOpsuState { lastKeysPressed = ReplayFrame.KEY_NONE; replaySkipTime = -1; replayFrames = new LinkedList<>(); - replayFrames.add(new ReplayFrame(0, 0, displayContainer.mouseX, displayContainer.mouseY, 0)); + replayFrames.add(new ReplayFrame(0, 0, mouseX, mouseY, 0)); } for (int i = 0; i < gameObjects.length; i++) { @@ -1698,6 +1722,10 @@ public class Game extends ComplexOpsuState { scoreboardVisible = true; currentScoreboardAlpha = 0f; + // using local offset? + if (beatmap.localMusicOffset != 0) + barNotifs.send(String.format("Using local beatmap offset (%dms)", beatmap.localMusicOffset)); + // needs to play before setting position to resume without lag later MusicController.play(false); MusicController.setPosition(0); @@ -1773,6 +1801,8 @@ public class Game extends ComplexOpsuState { storyboardOverlay.onLeave(); } + optionsOverlay.hide(); + isInGame = false; // container.setMouseGrabbed(false); skippedToCheckpoint = false; @@ -1796,6 +1826,18 @@ public class Game extends ComplexOpsuState { GameMod.loadModState(previousMods); } + /** + * Adjusts the beatmap's local music offset. + * @param sign the sign (multiplier) + */ + public void adjustLocalMusicOffset(int amount) { + int newOffset = beatmap.localMusicOffset + amount; + barNotifs.send(String.format("Local beatmap offset set to %dms", newOffset)); + if (beatmap.localMusicOffset != newOffset) { + beatmap.localMusicOffset = newOffset; + BeatmapDB.updateLocalOffset(beatmap); + } + } public void addMergedSliderPointsToRender(int from, int to) { knorkesliders.addRange(from, to); } @@ -1850,7 +1892,7 @@ public class Game extends ComplexOpsuState { } if (lastObjectIndex != -1 && !beatmap.objects[index].isNewCombo()) { // calculate points - final int followPointInterval = displayContainer.height / 14; + final int followPointInterval = height / 14; int lastObjectEndTime = gameObjects[lastObjectIndex].getEndTime() + 1; int objectStartTime = beatmap.objects[index].getTime(); Vec2f startPoint = gameObjects[lastObjectIndex].getPointAt(lastObjectEndTime); @@ -1858,7 +1900,7 @@ public class Game extends ComplexOpsuState { float xDiff = endPoint.x - startPoint.x; float yDiff = endPoint.y - startPoint.y; float dist = (float) Math.hypot(xDiff, yDiff); - int numPoints = (int) ((dist - GameImage.HITCIRCLE.getImage().getWidth()) / followPointInterval); + int numPoints = (int) ((dist - GameImage.HITCIRCLE.getWidth()) / followPointInterval); if (numPoints > 0) { // set the image angle Image followPoint = GameImage.FOLLOWPOINT.getImage(); @@ -1911,7 +1953,7 @@ public class Game extends ComplexOpsuState { gameObj.draw(g, trackPosition, false); if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive() && idx < mirrorTo && idx >= mirrorFrom) { g.pushTransform(); - g.rotate(displayContainer.width / 2f, displayContainer.height / 2f, 180f); + g.rotate(width2, height2, 180f); gameObj.draw(g, trackPosition, true); g.popTransform(); } @@ -1939,7 +1981,7 @@ public class Game extends ComplexOpsuState { g.pushTransform(); // translate and rotate the object - g.translate(0, dt * dt * displayContainer.height); + g.translate(0, dt * dt * height); Vec2f rotationCenter = gameObj.getPointAt((beatmap.objects[idx].getTime() + beatmap.objects[idx].getEndTime()) / 2); g.rotate(rotationCenter.x, rotationCenter.y, rotSpeed * dt); gameObj.draw(g, trackPosition, false); @@ -1954,15 +1996,12 @@ public class Game extends ComplexOpsuState { * @param beatmap the beatmap to load */ public void loadBeatmap(Beatmap beatmap) { - if (this.beatmap == null || this.beatmap.beatmapID != beatmap.beatmapID) { - currentMapMusicOffset = 0; - } this.beatmap = beatmap; Display.setTitle(String.format("opsu!dance - %s", beatmap.toString())); if (beatmap.breaks == null) { BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY); } - beatmapParser.parseHitObjects(beatmap); + BeatmapParser.parseHitObjects(beatmap); HitSound.setDefaultSampleSet(beatmap.sampleSet); } @@ -1993,7 +2032,7 @@ public class Game extends ComplexOpsuState { lastReplayTime = 0; autoMousePosition = new Vec2f(); autoMousePressed = false; - flashlightRadius = displayContainer.height * 2 / 3; + flashlightRadius = height * 2 / 3; if (scoreboardStarStream != null) { scoreboardStarStream.clear(); } @@ -2045,10 +2084,10 @@ public class Game extends ComplexOpsuState { // skip button if (GameImage.SKIP.getImages() != null) { Animation skip = GameImage.SKIP.getAnimation(120); - skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f)); + skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f)); } else { Image skip = GameImage.SKIP.getImage(); - skipButton = new MenuButton(skip, displayContainer.width - skip.getWidth() / 2f, displayContainer.height - (skip.getHeight() / 2f)); + skipButton = new MenuButton(skip, width - skip.getWidth() / 2f, height - (skip.getHeight() / 2f)); } skipButton.setHoverAnimationDuration(350); skipButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); @@ -2093,12 +2132,12 @@ public class Game extends ComplexOpsuState { // initialize objects gameObjectRenderer.initForGame(data, diameter); Slider.init(gameObjectRenderer.circleDiameter, beatmap); - Spinner.init(displayContainer, overallDifficulty); + Spinner.init(overallDifficulty); Color sliderBorderColor = SkinService.skin.getSliderBorderColor(); if (!OPTION_IGNORE_BEATMAP_SKINS.state && beatmap.getSliderBorderColor() != null) { sliderBorderColor = beatmap.getSliderBorderColor(); } - Curve.init(displayContainer.width, displayContainer.height, diameter, sliderBorderColor); + Curve.init(diameter, sliderBorderColor); // approachRate (hit object approach time) if (approachRate < 5) @@ -2214,15 +2253,16 @@ public class Game extends ComplexOpsuState { if (replay == null) { this.isReplay = false; this.replay = null; - } else { - if (replay.frames == null) { - BubNotifListener.EVENT.make().onBubNotif("Attempting to set a replay with no frames.", - Colors.BUB_ORANGE); - return; - } - this.isReplay = true; - this.replay = replay; + return; } + + if (replay.frames == null) { + bubNotifs.send(BUB_ORANGE, "Attempting to set a replay with no frames."); + return; + } + + this.isReplay = true; + this.replay = replay; } /** @@ -2316,25 +2356,25 @@ public class Game extends ComplexOpsuState { if (isLeadIn()) { // lead-in: expand area float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f); - flashlightRadius = displayContainer.width - (int) ((displayContainer.width - (displayContainer.height * 2 / 3)) * progress); + flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress); } else if (firstObject) { // before first object: shrink area int timeDiff = beatmap.objects[0].getTime() - trackPosition; - flashlightRadius = displayContainer.width; + flashlightRadius = width; if (timeDiff < approachTime) { float progress = (float) timeDiff / approachTime; - flashlightRadius -= (displayContainer.width - (displayContainer.height * 2 / 3)) * (1 - progress); + flashlightRadius -= (width - (height * 2 / 3)) * (1 - progress); } } else { // gameplay: size based on combo int targetRadius; int combo = data.getComboStreak(); if (combo < 100) - targetRadius = displayContainer.height * 2 / 3; + targetRadius = height * 2 / 3; else if (combo < 200) - targetRadius = displayContainer.height / 2; + targetRadius = height2; else - targetRadius = displayContainer.height / 3; + targetRadius = height / 3; if (beatmap.breaks != null && breakIndex < beatmap.breaks.size() && breakTime > 0) { // breaks: expand at beginning, shrink at end flashlightRadius = targetRadius; @@ -2346,11 +2386,11 @@ public class Game extends ComplexOpsuState { progress = (float) (trackPosition - breakTime) / approachTime; else if (endTime - trackPosition < approachTime) progress = (float) (endTime - trackPosition) / approachTime; - flashlightRadius += (displayContainer.width - flashlightRadius) * progress; + flashlightRadius += (width - flashlightRadius) * progress; } } else if (flashlightRadius != targetRadius) { // radius size change - float radiusDiff = displayContainer.height * delta / 2000f; + float radiusDiff = height * delta / 2000f; if (flashlightRadius > targetRadius) { flashlightRadius -= radiusDiff; if (flashlightRadius < targetRadius) diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index dc92a060..a320f3ca 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -32,6 +32,7 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; import yugecin.opsudance.core.state.BaseOpsuState; +import static itdelatrisu.opsu.GameImage.*; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -73,11 +74,11 @@ public class GamePauseMenu extends BaseOpsuState { @Override public void preRenderUpdate() { - int delta = displayContainer.renderDelta; + int delta = renderDelta; UI.update(delta); - continueButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); - retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); - backButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + continueButton.hoverUpdate(delta, mouseX, mouseY); + retryButton.hoverUpdate(delta, mouseX, mouseY); + backButton.hoverUpdate(delta, mouseX, mouseY); } @Override @@ -89,9 +90,9 @@ public class GamePauseMenu extends BaseOpsuState { // game keys if (!Keyboard.isRepeatEvent()) { if (key == OPTION_KEY_LEFT.intval) { - mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); + mousePressed(Input.MOUSE_LEFT_BUTTON, mouseX, mouseY); } else if (key == OPTION_KEY_RIGHT.intval) { - mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); + mousePressed(Input.MOUSE_RIGHT_BUTTON, mouseX, mouseY); } } @@ -116,6 +117,15 @@ public class GamePauseMenu extends BaseOpsuState { return true; } + if (key == KEY_SUBTRACT || key == KEY_MINUS) { + gameState.adjustLocalMusicOffset(-5); + return true; + } + if (key == KEY_EQUALS || key == KEY_ADD || c == '+') { + gameState.adjustLocalMusicOffset(5); + return true; + } + return false; } @@ -193,9 +203,9 @@ public class GamePauseMenu extends BaseOpsuState { */ public void loadImages() { // initialize buttons - continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), displayContainer.width / 2f, displayContainer.height * 0.25f); - retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), displayContainer.width / 2f, displayContainer.height * 0.5f); - backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), displayContainer.width / 2f, displayContainer.height * 0.75f); + continueButton = new MenuButton(PAUSE_CONTINUE.getImage(), width2, height * 0.25f); + retryButton = new MenuButton(PAUSE_RETRY.getImage(), width2, height2); + backButton = new MenuButton(PAUSE_BACK.getImage(), width2, height * 0.75f); final int buttonAnimationDuration = 300; continueButton.setHoverAnimationDuration(buttonAnimationDuration); retryButton.setHoverAnimationDuration(buttonAnimationDuration); diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index eb069a0e..29bd9d3e 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -38,7 +38,6 @@ import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.state.BaseOpsuState; -import yugecin.opsudance.events.BarNotifListener; import static yugecin.opsudance.core.InstanceContainer.*; @@ -67,10 +66,10 @@ public class GameRanking extends BaseOpsuState { // buttons Image retry = GameImage.PAUSE_RETRY.getImage(); Image replay = GameImage.PAUSE_REPLAY.getImage(); - replayY = (displayContainer.height * 0.985f) - replay.getHeight() / 2f; + replayY = height * 0.985f - replay.getHeight() / 2f; retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f); - retryButton = new MenuButton(retry, displayContainer.width - (retry.getWidth() / 2f), retryY); - replayButton = new MenuButton(replay, displayContainer.width - (replay.getWidth() / 2f), replayY); + retryButton = new MenuButton(retry, width - (retry.getWidth() / 2f), retryY); + replayButton = new MenuButton(replay, width - (replay.getWidth() / 2f), replayY); retryButton.setHoverFade(); replayButton.setHoverFade(); } @@ -80,7 +79,7 @@ public class GameRanking extends BaseOpsuState { Beatmap beatmap = MusicController.getBeatmap(); // background - if (!beatmap.drawBackground(displayContainer.width, displayContainer.height, 0.7f, true)) { + if (!beatmap.drawBackground(width, height, 0.7f, true)) { GameImage.PLAYFIELD.getImage().draw(0, 0); } @@ -91,7 +90,7 @@ public class GameRanking extends BaseOpsuState { replayButton.draw(); if (data.isGameplay() && !GameMod.AUTO.isActive()) retryButton.draw(); - UI.getBackButton().draw(g); + backButton.draw(g); UI.draw(g); @@ -100,15 +99,15 @@ public class GameRanking extends BaseOpsuState { @Override public void preRenderUpdate() { - int delta = displayContainer.renderDelta; + int delta = renderDelta; UI.update(delta); - replayButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + replayButton.hoverUpdate(delta, mouseX, mouseY); if (data.isGameplay()) { - retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + retryButton.hoverUpdate(delta, mouseX, mouseY); } else { MusicController.loopTrackIfEnded(true); } - UI.getBackButton().hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY); + backButton.hoverUpdate(); } @Override @@ -140,7 +139,7 @@ public class GameRanking extends BaseOpsuState { } // back to menu - if (UI.getBackButton().contains(x, y)) { + if (backButton.contains(x, y)) { returnToSongMenu(); return true; } @@ -157,14 +156,14 @@ public class GameRanking extends BaseOpsuState { gameState.setRestart((data.isGameplay()) ? Game.Restart.REPLAY : Game.Restart.NEW); returnToGame = true; } catch (FileNotFoundException e) { - BarNotifListener.EVENT.make().onBarNotif("Replay file not found."); + barNotifs.send("Replay file not found."); } catch (IOException e) { Log.error("Failed to load replay data.", e); - BarNotifListener.EVENT.make().onBarNotif( - "Failed to load replay data. See log for details."); + barNotifs.send("Failed to load replay data. See log for details."); } - } else - BarNotifListener.EVENT.make().onBarNotif("Replay file not found."); + } else { + barNotifs.send("Replay file not found."); + } } // retry diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index c711e05b..96fc42c7 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -37,20 +37,31 @@ import java.awt.Desktop; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; import java.util.Stack; import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.GL11; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.Constants; +import yugecin.opsudance.core.Entrypoint; +import yugecin.opsudance.core.InstanceContainer; import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.events.BubNotifListener; +import yugecin.opsudance.ui.ImagePosition; +import static itdelatrisu.opsu.GameImage.*; +import static itdelatrisu.opsu.ui.Colors.*; +import static itdelatrisu.opsu.ui.animations.AnimationEquation.*; +import static java.awt.Desktop.Action.*; +import static java.lang.Math.*; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -63,13 +74,15 @@ import static yugecin.opsudance.options.Options.*; public class MainMenu extends BaseOpsuState { /** Idle time, in milliseconds, before returning the logo to its original position. */ - private static final short LOGO_IDLE_DELAY = 10000; + private static final short LOGO_IDLE_DELAY = 6000; /** Max alpha level of the menu background. */ private static final float BG_MAX_ALPHA = 0.9f; + + private float barHeight; - /** Logo button that reveals other buttons on click. */ - private MenuButton logo; + private ImagePosition logo; + private AnimatedValue logoHover; /** Logo states. */ private enum LogoState { DEFAULT, OPENING, OPEN, CLOSING } @@ -81,16 +94,27 @@ public class MainMenu extends BaseOpsuState { private int logoTimer = 0; /** Logo horizontal offset for opening and closing actions. */ - private AnimatedValue logoOpen, logoClose; + private AnimatedValue logoPosition; + private float logoPositionOffsetX; + + private int lastMouseX; + private int lastMouseY; + + private AnimatedValue logoClickScale; + private AnimatedValue buttonAnimation; + private int buttonsX; + private AnimatedValue[] buttonAnimations; + private ImagePosition[] buttonPositions; /** Logo button alpha levels. */ private AnimatedValue logoButtonAlpha; - - /** Main "Play" and "Exit" buttons. */ - private MenuButton playButton, exitButton; + + /** Now playing position vlaue. */ + private final AnimatedValue nowPlayingPosition; /** Music control buttons. */ - private MenuButton musicPlay, musicPause, musicNext, musicPrevious; + private MenuButton musicPlay, musicPause, musicStop, musicNext, musicPrev; + private MenuButton[] musicButtons = new MenuButton[5]; /** Button linking to Downloads menu. */ private MenuButton downloadsButton; @@ -104,8 +128,9 @@ public class MainMenu extends BaseOpsuState { /** Buttons for installing updates. */ private MenuButton updateButton, restartButton; - /** Application start time, for drawing the total running time. */ - private long programStartTime; + private int textMarginX; + private int textTopMarginY; + private int textLineHeight; /** Indexes of previous songs. */ private Stack previous; @@ -117,117 +142,131 @@ public class MainMenu extends BaseOpsuState { private boolean enterNotification = false; /** Music position bar coordinates and dimensions. */ - private float musicBarX, musicBarY, musicBarWidth, musicBarHeight; + private int musicBarX, musicBarY, musicBarWidth, musicBarHeight; /** Last measure progress value. */ private float lastMeasureProgress = 0f; /** The star fountain. */ private StarFountain starFountain; + + /** Time format used to show running time. */ + private final SimpleDateFormat timeFormat; + + private LinkedList pulseData = new LinkedList<>(); + private float lastPulseProgress; + + public MainMenu() { + this.nowPlayingPosition = new AnimatedValue(1000, 0, 0, OUT_QUART); + this.logoClickScale = new AnimatedValue(300, .9f, 1f, OUT_QUAD); + this.logoHover = new AnimatedValue(350, 1f, 1.096f, IN_OUT_EXPO); + this.logoPosition = new AnimatedValue(1, 0, 1, AnimationEquation.OUT_QUAD); + this.logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); + this.buttonAnimation = new AnimatedValue(1, 0f, 1f, OUT_QUAD); + this.buttonAnimations = new AnimatedValue[3]; + for (int i = 0; i < 3; i++) { + this.buttonAnimations[i] = new AnimatedValue(1, 0f, 1f, LINEAR); + } + this.buttonPositions = new ImagePosition[3]; + this.timeFormat = new SimpleDateFormat("HH:mm"); + this.previous = new Stack<>(); + } @Override protected void revalidate() { - programStartTime = System.currentTimeMillis(); - previous = new Stack<>(); - // initialize menu buttons - Image logoImg = GameImage.MENU_LOGO.getImage(); - Image playImg = GameImage.MENU_PLAY.getImage(); - Image exitImg = GameImage.MENU_EXIT.getImage(); - float exitOffset = (playImg.getWidth() - exitImg.getWidth()) / 3f; - logo = new MenuButton(logoImg, displayContainer.width / 2f, displayContainer.height / 2f); - playButton = new MenuButton(playImg, - displayContainer.width * 0.75f, (displayContainer.height / 2) - (logoImg.getHeight() / 5f) - ); - exitButton = new MenuButton(exitImg, - displayContainer.width * 0.75f - exitOffset, (displayContainer.height / 2) + (exitImg.getHeight() / 2f) - ); - final int logoAnimationDuration = 350; - logo.setHoverAnimationDuration(logoAnimationDuration); - playButton.setHoverAnimationDuration(logoAnimationDuration); - exitButton.setHoverAnimationDuration(logoAnimationDuration); - final AnimationEquation logoAnimationEquation = AnimationEquation.IN_OUT_BACK; - logo.setHoverAnimationEquation(logoAnimationEquation); - playButton.setHoverAnimationEquation(logoAnimationEquation); - exitButton.setHoverAnimationEquation(logoAnimationEquation); - final float logoHoverScale = 1.08f; - logo.setHoverExpand(logoHoverScale); - playButton.setHoverExpand(logoHoverScale); - exitButton.setHoverExpand(logoHoverScale); + this.barHeight = height * 0.1125f; + + this.textMarginX = (int) (width * 0.015f); + this.textTopMarginY = (int) (height * 0.01f); + this.textLineHeight = (int) (Fonts.MEDIUM.getLineHeight() * 0.925f); // initialize music buttons - int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth(); - int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight(); - musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f); - musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f); - musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), displayContainer.width - musicWidth, musicHeight / 1.5f); - musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), displayContainer.width - (3 * musicWidth), musicHeight / 1.5f); - musicPlay.setHoverExpand(1.5f); - musicPause.setHoverExpand(1.5f); - musicNext.setHoverExpand(1.5f); - musicPrevious.setHoverExpand(1.5f); + final int musicSize = (int) (this.textLineHeight * 0.8f); + final float musicScale = (float) musicSize / MUSIC_STOP.getWidth(); + final int musicSpacing = (int) (musicSize * 0.8f) + musicSize; // (center to center) + int x = width - this.textMarginX - musicSize / 2; + int y = this.textLineHeight * 2 + this.textLineHeight / 2; + this.musicNext = new MenuButton(MUSIC_NEXT.getScaledImage(musicScale), x, y); + x -= musicSpacing; + this.musicStop = new MenuButton(MUSIC_STOP.getScaledImage(musicScale), x, y); + x -= musicSpacing; + this.musicPause = new MenuButton(MUSIC_PAUSE.getScaledImage(musicScale), x, y); + x -= musicSpacing; + this.musicPlay = new MenuButton(MUSIC_PLAY.getScaledImage(musicScale), x, y); + x -= musicSpacing; + this.musicPrev = new MenuButton(MUSIC_PREVIOUS.getScaledImage(musicScale), x, y); + this.musicButtons[0] = this.musicPrev; + this.musicButtons[1] = this.musicPlay; + this.musicButtons[2] = this.musicPause; + this.musicButtons[3] = this.musicStop; + this.musicButtons[4] = this.musicNext; + for (MenuButton b : this.musicButtons) { + b.setHoverExpand(1.15f); + } // initialize music position bar location - musicBarX = displayContainer.width - musicWidth * 3.5f; - musicBarY = musicHeight * 1.25f; - musicBarWidth = musicWidth * 3f; - musicBarHeight = musicHeight * 0.11f; + this.musicBarX = x - musicSize / 2; + this.musicBarY = y + musicSize; + this.musicBarWidth = musicSize + musicSpacing * 4; + this.musicBarHeight = (int) (musicSize * 0.3f); // initialize downloads button Image dlImg = GameImage.DOWNLOADS.getImage(); - downloadsButton = new MenuButton(dlImg, displayContainer.width - dlImg.getWidth() / 2f, displayContainer.height / 2f); + downloadsButton = new MenuButton(dlImg, width - dlImg.getWidth() / 2f, height2); downloadsButton.setHoverAnimationDuration(350); downloadsButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); downloadsButton.setHoverExpand(1.03f, Expand.LEFT); - // initialize repository button - float startX = displayContainer.width * 0.997f, startY = displayContainer.height * 0.997f; - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { // only if a webpage can be opened - Image repoImg; - repoImg = GameImage.REPOSITORY.getImage(); - repoButton = new MenuButton(repoImg, - startX - repoImg.getWidth() * 2.5f, startY - repoImg.getHeight() - ); - repoButton.setHoverAnimationDuration(350); - repoButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); - repoButton.setHoverExpand(); - repoImg = GameImage.REPOSITORY.getImage(); - danceRepoButton = new MenuButton(repoImg, - startX - repoImg.getWidth(), startY - repoImg.getHeight() - ); - danceRepoButton.setHoverAnimationDuration(350); - danceRepoButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); - danceRepoButton.setHoverExpand(); + // initialize repository button (only if a webpage can be opened) + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(BROWSE)) { + final Image repoImg = GameImage.REPOSITORY.getImage(); + float repoX = this.textMarginX + repoImg.getWidth() / 2; + final float repoY = height - this.barHeight / 2; + repoButton = new MenuButton(repoImg, repoX, repoY); + repoButton.setHoverAnimationDuration(100); + repoButton.setHoverExpand(1.1f); + repoX += repoImg.getWidth() * 1.5f; + danceRepoButton = new MenuButton(repoImg, repoX, repoY); + danceRepoButton.setHoverAnimationDuration(100); + danceRepoButton.setHoverExpand(1.1f); } // initialize update buttons - float updateX = displayContainer.width / 2f, updateY = displayContainer.height * 17 / 18f; - Image downloadImg = GameImage.DOWNLOAD.getImage(); - updateButton = new MenuButton(downloadImg, updateX, updateY); + final float updateY = height * 17 / 18f; + final Image downloadImg = GameImage.DOWNLOAD.getImage(); + updateButton = new MenuButton(downloadImg, width2, updateY); updateButton.setHoverAnimationDuration(400); updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD); updateButton.setHoverExpand(1.1f); - Image updateImg = GameImage.UPDATE.getImage(); - restartButton = new MenuButton(updateImg, updateX, updateY); + final Image updateImg = GameImage.UPDATE.getImage(); + restartButton = new MenuButton(updateImg, width2, updateY); restartButton.setHoverAnimationDuration(2000); restartButton.setHoverAnimationEquation(AnimationEquation.LINEAR); restartButton.setHoverRotate(360); // initialize star fountain - starFountain = new StarFountain(displayContainer.width, displayContainer.height); + starFountain = new StarFountain(width, height); - // logo animations - float centerOffsetX = displayContainer.width / 6.5f; - logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD); - logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD); - logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); + // logo & buttons + this.logo = new ImagePosition(MENU_LOGO.getImage()); + this.logoPositionOffsetX = 0.35f * MENU_LOGO.getHeight(); + this.logoPosition.setValues(0, logoPositionOffsetX); + this.buttonsX = width2 - MENU_OPTIONS.getWidth() / 2; + this.buttonPositions[0] = new ImagePosition(MENU_PLAY.getImage()); + this.buttonPositions[1] = new ImagePosition(MENU_OPTIONS.getImage()); + this.buttonPositions[2] = new ImagePosition(MENU_EXIT.getImage()); + final int basey = height2 - MENU_OPTIONS.getHeight() / 2; + final float yoffset = MENU_LOGO.getHeight() * 0.196378f; + for (int i = 0; i < 3; i++) { + this.buttonPositions[i].width = MENU_OPTIONS.getWidth(); + this.buttonPositions[i].y = (int) (basey + (i - 1f) * yoffset); + this.buttonPositions[i].height = MENU_OPTIONS.getHeight(); + } } @Override public void render(Graphics g) { - int width = displayContainer.width; - int height = displayContainer.height; - // draw background Beatmap beatmap = MusicController.getBeatmap(); if (OPTION_DYNAMIC_BACKGROUND.state && @@ -241,75 +280,167 @@ public class MainMenu extends BaseOpsuState { // top/bottom horizontal bars float oldAlpha = Colors.BLACK_ALPHA.a; - Colors.BLACK_ALPHA.a = 0.2f; + Colors.BLACK_ALPHA.a = 0.4f; g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, 0, width, height / 9f); - g.fillRect(0, height * 8 / 9f, width, height / 9f); + g.fillRect(0, 0, width, this.barHeight); + g.fillRect(0, height - this.barHeight, width, this.barHeight); Colors.BLACK_ALPHA.a = oldAlpha; // draw star fountain - starFountain.draw(); + if (OPTION_STARFOUNTAINS.state) { + starFountain.draw(); + } // draw downloads button downloadsButton.draw(); + + // calculate scale stuff for logo + final float clickScale = this.logoClickScale.getValue(); + Float beatPosition = MusicController.getBeatProgress(); + Float beatLength = MusicController.getBeatLength(); + final boolean renderPiece = beatPosition != null; + if (beatPosition == null || beatLength == null) { + beatPosition = System.currentTimeMillis() % 1000 / 1000f; + beatLength = 1000f; + } + final float hoverScale = this.logoHover.getValue(); + if (beatPosition < this.lastPulseProgress) { + this.pulseData.add(new PulseData((int) (beatPosition*beatLength), hoverScale)); + } + this.lastPulseProgress = beatPosition; + final float smoothExpandProgress; + if (beatPosition < 0.05f) { + smoothExpandProgress = 1f - IN_CUBIC.calc(beatPosition / 0.05f); + } else { + smoothExpandProgress = (beatPosition - 0.05f) / 0.95f; + } + final float logoScale = (0.9726f + smoothExpandProgress * 0.0274f) * clickScale; + final float totalLogoScale = hoverScale * logoScale; + + // pulse ripples + final Color logoColor; + if (OPTION_COLOR_MAIN_MENU_LOGO.state) { + logoColor = Cursor.lastCursorColor; + } else { + logoColor = Color.white; + } + for (PulseData pd : this.pulseData) { + final float progress = OUT_CUBIC.calc(pd.position / 1000f); + final float scale = (pd.initialScale + (0.432f * progress)) * clickScale; + final Image p = MENU_LOGO_PULSE.getScaledImage(scale); + p.setAlpha(0.15f * (1f - IN_QUAD.calc(progress))); + p.drawCentered(this.logo.middleX(), this.logo.middleY(), logoColor); + } // draw buttons - if (logoState == LogoState.OPEN || logoState == LogoState.CLOSING) { - playButton.draw(); - exitButton.draw(); + final float buttonProgress = this.buttonAnimation.getValue(); + if (this.logoState != LogoState.DEFAULT && buttonProgress > 0f) { + final int btnwidth = MENU_OPTIONS.getWidth(); + final float btnhalfheight = MENU_OPTIONS.getHeight() / 2f; + final int basey = height2; + final int x = (int) (this.buttonsX + btnwidth * 0.375f * buttonProgress); + final Color col = new Color(logoColor); + final Image[] imgs = { + MENU_PLAY.getImage(), + MENU_OPTIONS.getImage(), + MENU_EXIT.getImage() + }; + final float circleradius = MENU_LOGO.getHeight() * 0.44498f; + final float yoffset = MENU_LOGO.getHeight() * 0.196378f; + final float cr = circleradius * totalLogoScale; + for (int i = 0; i < 3; i++) { + final float hoverprogress = this.buttonAnimations[i].getValue(); + final int bx = x + (int) (btnwidth * 0.075f * hoverprogress); + this.buttonPositions[i].x = bx; + final float yoff = (i - 1f) * yoffset; + final double cliptop = cr * cos(asin((yoff - btnhalfheight) / cr)); + final double clipbot = cr * cos(asin((yoff + btnhalfheight) / cr)); + final float clipxstart = bx - this.logo.middleX(); + final int ct = (int) (cliptop - clipxstart); + final int cb = (int) (clipbot - clipxstart); + final int y = (int) (basey + yoff); + col.a = buttonProgress * 0.85f + hoverprogress * 0.15f; + this.drawMenuButton(imgs[i], bx, y, ct, cb, col); + } } - // draw logo (pulsing) - Color color = OPTION_COLOR_MAIN_MENU_LOGO.state ? Cursor.lastCursorColor : Color.white; - Float position = MusicController.getBeatProgress(); - boolean renderPiece = position != null; - if (position == null) { - position = System.currentTimeMillis() % 1000 / 1000f; - } - float scale = 1f + position * 0.05f; - logo.draw(color, scale); + // draw logo + this.logo.scale(logoScale); + this.logo.draw(logoColor); if (renderPiece) { - Image piece = GameImage.MENU_LOGO_PIECE.getImage().getScaledCopy(logo.getLastScale()); - piece.rotate(position * 360); - piece.drawCentered(logo.getX(), logo.getY(), color); + final Image piece = MENU_LOGO_PIECE.getScaledImage(hoverScale * logoScale); + piece.rotate(beatPosition * 360f); + piece.drawCentered(this.logo.middleX(), this.logo.middleY(), logoColor); } - float ghostScale = logo.getLastScale() / scale * 1.05f; - Image ghostLogo = GameImage.MENU_LOGO.getImage().getScaledCopy(ghostScale); - ghostLogo.drawCentered(logo.getX(), logo.getY(), Colors.GHOST_LOGO); + final float ghostScale = hoverScale * 1.0186f - smoothExpandProgress * 0.0186f; + Image ghostLogo = MENU_LOGO.getScaledImage(ghostScale * clickScale); + ghostLogo.setAlpha(0.25f); + ghostLogo.drawCentered(this.logo.middleX(), this.logo.middleY(), logoColor); + + // now playing + final Image np = GameImage.MUSIC_NOWPLAYING.getImage(); + final String trackText; + if (beatmap != null) { + if (OPTION_SHOW_UNICODE.state) { + Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode); + Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.artistUnicode); + } + trackText = beatmap.getArtist() + ": " + beatmap.getTitle(); + } else { + trackText = "Loading..."; + } + final float textWidth = Fonts.MEDIUM.getWidth(trackText); + final float npheight = Fonts.MEDIUM.getLineHeight() * 1.15f; + final float npscale = npheight / np.getHeight(); + final float npwidth = np.getWidth() * npscale; + float totalWidth = textMarginX + textWidth + npwidth; + if (this.nowPlayingPosition.getMax() != totalWidth) { + final float current = this.nowPlayingPosition.getValue(); + this.nowPlayingPosition.setValues(current, totalWidth); + this.nowPlayingPosition.setTime(0); + } + final float npimgx = width - this.nowPlayingPosition.getValue(); + final float npx = npimgx + npwidth; + MUSIC_NOWPLAYING_BG_BLACK.getImage().draw(npx, 0, width - npx, npheight); + MUSIC_NOWPLAYING_BG_WHITE.getImage().draw(npimgx, npheight, width - npimgx, 2); + np.draw(npimgx, 0, npscale); + Fonts.MEDIUM.drawString(npx, 0, trackText); // draw music buttons - if (MusicController.isPlaying()) - musicPause.draw(); - else - musicPlay.draw(); - musicNext.draw(); - musicPrevious.draw(); + for (MenuButton b : this.musicButtons) { + b.draw(); + } // draw music position bar - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; g.setColor((musicPositionBarContains(mouseX, mouseY)) ? Colors.BLACK_BG_HOVER : Colors.BLACK_BG_NORMAL); - g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); - g.setColor(Color.white); + g.fillRect(this.musicBarX, this.musicBarY, this.musicBarWidth, this.musicBarHeight); + g.setColor(Colors.WHITE_ALPHA_75); if (!MusicController.isTrackLoading() && beatmap != null) { - float musicBarPosition = Math.min((float) MusicController.getPosition() / MusicController.getDuration(), 1f); - g.fillRoundRect(musicBarX, musicBarY, musicBarWidth * musicBarPosition, musicBarHeight, 4); + final float trackpos = MusicController.getPosition(); + final float tracklen = MusicController.getDuration(); + final float barwidth = musicBarWidth * Math.min(trackpos / tracklen, 1f); + g.fillRect(this.musicBarX, this.musicBarY, barwidth, this.musicBarHeight); } // draw repository buttons if (repoButton != null) { String text; int fheight, fwidth; + float x, y; repoButton.draw(); text = "opsu!"; fheight = Fonts.SMALL.getLineHeight(); fwidth = Fonts.SMALL.getWidth(text); - Fonts.SMALL.drawString(repoButton.getX() - fwidth / 2, repoButton.getY() - repoButton.getImage().getHeight() / 2 - fheight, text, Color.white); + x = repoButton.getX() - fwidth / 2; + y = repoButton.getY() - repoButton.getImage().getHeight() / 2 - fheight; + Fonts.SMALL.drawString(x, y, text, Color.white); danceRepoButton.draw(); text = "opsu!dance"; fheight = Fonts.SMALL.getLineHeight(); fwidth = Fonts.SMALL.getWidth(text); - Fonts.SMALL.drawString(danceRepoButton.getX() - fwidth / 2, repoButton.getY() - repoButton.getImage().getHeight() / 2 - fheight, text, Color.white); + x = danceRepoButton.getX() - fwidth / 2; + y = danceRepoButton.getY() - repoButton.getImage().getHeight() / 2 - fheight; + Fonts.SMALL.drawString(x, y, text, Color.white); } // draw update button @@ -323,43 +454,63 @@ public class MainMenu extends BaseOpsuState { } // draw text - float marginX = width * 0.015f, topMarginY = height * 0.01f, bottomMarginY = height * 0.015f; g.setFont(Fonts.MEDIUM); - float lineHeight = Fonts.MEDIUM.getLineHeight() * 0.925f; - g.drawString(String.format("Loaded %d songs and %d beatmaps.", - BeatmapSetList.get().getMapSetCount(), BeatmapSetList.get().getMapCount()), marginX, topMarginY); - if (MusicController.isTrackLoading()) { - g.drawString("Track loading...", marginX, topMarginY + lineHeight); - } else if (MusicController.trackExists()) { - if (OPTION_SHOW_UNICODE.state) { - Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode); - Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.artistUnicode); - } - g.drawString((MusicController.isPlaying()) ? "Now Playing:" : "Paused:", marginX, topMarginY + lineHeight); - g.drawString(String.format("%s: %s", beatmap.getArtist(), beatmap.getTitle()), marginX + 25, topMarginY + (lineHeight * 2)); + g.setColor(Color.white); + String txt = String.format( + "You have %d beatmaps (%d songs) available!", + BeatmapSetList.get().getMapCount(), + BeatmapSetList.get().getMapSetCount() + ); + g.drawString(txt, textMarginX, textTopMarginY); + txt = String.format( + "%s has been running for %s.", + Constants.PROJECT_NAME, + Utils.getTimeString((int) (Entrypoint.runtime() / 1000L)) + ); + g.drawString(txt, textMarginX, textTopMarginY + textLineHeight); + txt = String.format( + "It is currently %s.", + this.timeFormat.format(new Date()) + ); + g.drawString(txt, textMarginX, textTopMarginY + textLineHeight * 2); + + optionsOverlay.render(g); + if (optionsOverlay.isActive()) { + backButton.draw(g); } - g.drawString(String.format("opsu! has been running for %s.", - Utils.getTimeString((int) (System.currentTimeMillis() - programStartTime) / 1000)), - marginX, height - bottomMarginY - (lineHeight * 2)); - g.drawString(String.format("It is currently %s.", - new SimpleDateFormat("h:mm a").format(new Date())), - marginX, height - bottomMarginY - lineHeight); UI.draw(g); } @Override public void preRenderUpdate() { - int delta = displayContainer.renderDelta; + optionsOverlay.preRenderUpdate(); + if (optionsOverlay.isActive()) { + backButton.hoverUpdate(); + } + + int delta = renderDelta; + + int mouseX = InstanceContainer.mouseX; + int mouseY = InstanceContainer.mouseY; + if (optionsOverlay.containsMouse()) { + // dirty hack to not show elements underneath options overlay as hovered + mouseX = -mouseX; + mouseY = -mouseY; + } + + final Iterator pulseDataIter = this.pulseData.iterator(); + while (pulseDataIter.hasNext()) { + final PulseData pd = pulseDataIter.next(); + pd.position += delta; + if (pd.position > 1000) { + pulseDataIter.remove(); + } + } UI.update(delta); if (MusicController.trackEnded()) nextTrack(false); // end of track: go to next track - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - logo.hoverUpdate(delta, mouseX, mouseY, 0.25f); - playButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); - exitButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); if (repoButton != null) { repoButton.hoverUpdate(delta, mouseX, mouseY); danceRepoButton.hoverUpdate(delta, mouseX, mouseY); @@ -369,15 +520,12 @@ public class MainMenu extends BaseOpsuState { restartButton.autoHoverUpdate(delta, false); } downloadsButton.hoverUpdate(delta, mouseX, mouseY); - // ensure only one button is in hover state at once - boolean noHoverUpdate = musicPositionBarContains(mouseX, mouseY); - boolean contains = musicPlay.contains(mouseX, mouseY); - musicPlay.hoverUpdate(delta, !noHoverUpdate && contains); - musicPause.hoverUpdate(delta, !noHoverUpdate && contains); - noHoverUpdate |= contains; - musicNext.hoverUpdate(delta, !noHoverUpdate && musicNext.contains(mouseX, mouseY)); - musicPrevious.hoverUpdate(delta, !noHoverUpdate && musicPrevious.contains(mouseX, mouseY)); - starFountain.update(delta); + for (MenuButton b : this.musicButtons) { + b.hoverUpdate(delta, b.contains(mouseX, mouseY)); + } + if (OPTION_STARFOUNTAINS.state) { + starFountain.update(delta); + } // window focus change: increase/decrease theme song volume if (MusicController.isThemePlaying() && @@ -392,83 +540,125 @@ public class MainMenu extends BaseOpsuState { // check measure progress Float measureProgress = MusicController.getMeasureProgress(2); if (measureProgress != null) { - if (measureProgress < lastMeasureProgress) + if (OPTION_STARFOUNTAINS.state && measureProgress < lastMeasureProgress) starFountain.burst(true); lastMeasureProgress = measureProgress; } // buttons - int centerX = displayContainer.width / 2; - float currentLogoButtonAlpha; + this.logo.width = MENU_LOGO.getWidth(); + this.logo.height = MENU_LOGO.getHeight(); + this.logo.x = width2 - this.logo.width / 2; + this.logo.y = height2 - this.logo.height / 2; + if (this.logoState != LogoState.DEFAULT) { + this.logo.x -= (int) this.logoPosition.getValue(); + } switch (logoState) { case DEFAULT: break; case OPENING: - if (logoOpen.update(delta)) // shifting to left - logo.setX(centerX - logoOpen.getValue()); - else { + if (logoPosition.update(delta)) { + this.buttonAnimation.update(delta); + } else { + this.buttonAnimation.setTime(this.buttonAnimation.getDuration()); logoState = LogoState.OPEN; logoTimer = 0; logoButtonAlpha.setTime(0); } break; case OPEN: - if (logoButtonAlpha.update(delta)) { // fade in buttons - currentLogoButtonAlpha = logoButtonAlpha.getValue(); - playButton.getImage().setAlpha(currentLogoButtonAlpha); - exitButton.getImage().setAlpha(currentLogoButtonAlpha); - } else if (logoTimer >= LOGO_IDLE_DELAY) { // timer over: shift back to center - logoState = LogoState.CLOSING; - logoClose.setTime(0); - logoTimer = 0; - } else // increment timer - logoTimer += delta; + logoButtonAlpha.update(delta); + if (this.lastMouseX != mouseX || this.lastMouseY != mouseY) { + this.logoTimer = 0; + this.lastMouseX = mouseX; + this.lastMouseY = mouseY; + } else { + this.logoTimer += delta; + if (this.logoTimer >= LOGO_IDLE_DELAY) { + this.closeLogo(); + } + } break; case CLOSING: - if (logoButtonAlpha.update(-delta)) { // fade out buttons - currentLogoButtonAlpha = logoButtonAlpha.getValue(); - playButton.getImage().setAlpha(currentLogoButtonAlpha); - exitButton.getImage().setAlpha(currentLogoButtonAlpha); + logoButtonAlpha.update(-delta); + if (logoPosition.update(-delta)) { + this.buttonAnimation.update(-delta); + } else { + this.logoState = LogoState.DEFAULT; + this.buttonAnimation.setTime(0); } - if (logoClose.update(delta)) // shifting to right - logo.setX(centerX - logoClose.getValue()); break; } + this.logoClickScale.update(delta); + final boolean logoHovered = this.logo.contains(mouseX, mouseY, 0.25f); + if (logoHovered) { + this.logoHover.update(delta); + } else { + this.logoHover.update(-delta); + } + final float hoverScale = this.logoHover.getValue(); + if (hoverScale != 1f) { + this.logo.scale(hoverScale); + } + for (int i = 0; i < 3; i++) { + final ImagePosition pos = this.buttonPositions[i]; + final AnimatedValue anim = this.buttonAnimations[i]; + if (!logoHovered && pos.contains(mouseX, mouseY, 0.25f)) { + if (anim.getDuration() != 500) { + anim.change(500, 0f, 1f, OUT_ELASTIC); + continue; + } + anim.update(delta); + continue; + } + + if (anim.getDuration() != 350) { + anim.change(350, 0f, 1f, IN_QUAD); + continue; + } + anim.update(-delta); + } // tooltips if (musicPositionBarContains(mouseX, mouseY)) UI.updateTooltip(delta, "Click to seek to a specific point in the song.", false); + else if (musicPrev.contains(mouseX, mouseY)) + UI.updateTooltip(delta, "Previous track", false); else if (musicPlay.contains(mouseX, mouseY)) - UI.updateTooltip(delta, (MusicController.isPlaying()) ? "Pause" : "Play", false); + UI.updateTooltip(delta, "Play", false); + else if (musicPause.contains(mouseX, mouseY)) + UI.updateTooltip(delta, "Pause", false); + else if (musicStop.contains(mouseX, mouseY)) + UI.updateTooltip(delta, "Stop", false); else if (musicNext.contains(mouseX, mouseY)) UI.updateTooltip(delta, "Next track", false); - else if (musicPrevious.contains(mouseX, mouseY)) - UI.updateTooltip(delta, "Previous track", false); else if (updater.showButton()) { Updater.Status status = updater.getStatus(); if (((status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) && updateButton.contains(mouseX, mouseY)) || (status == Updater.Status.UPDATE_DOWNLOADED && restartButton.contains(mouseX, mouseY))) UI.updateTooltip(delta, status.getDescription(), true); } + + nowPlayingPosition.update(delta); } @Override public void enter() { super.enter(); - logo.setX(displayContainer.width / 2); - logoOpen.setTime(0); - logoClose.setTime(0); + logoPosition.setTime(0); logoButtonAlpha.setTime(0); - logoTimer = 0; + nowPlayingPosition.setTime(0); logoState = LogoState.DEFAULT; + this.logoClickScale.setTime(this.logoClickScale.getDuration()); + this.buttonAnimation.setTime(0); UI.enter(); if (!enterNotification) { if (updater.getStatus() == Updater.Status.UPDATE_AVAILABLE) { - BarNotifListener.EVENT.make().onBarNotif("An opsu! update is available."); + barNotifs.send("An opsu! update is available."); } else if (updater.justUpdated()) { - BarNotifListener.EVENT.make().onBarNotif("opsu! is now up to date!"); + barNotifs.send("opsu! is now up to date!"); } enterNotification = true; } @@ -478,22 +668,11 @@ public class MainMenu extends BaseOpsuState { starFountain.clear(); // reset button hover states if mouse is not currently hovering over the button - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - if (!logo.contains(mouseX, mouseY, 0.25f)) - logo.resetHover(); - if (!playButton.contains(mouseX, mouseY, 0.25f)) - playButton.resetHover(); - if (!exitButton.contains(mouseX, mouseY, 0.25f)) - exitButton.resetHover(); - if (!musicPlay.contains(mouseX, mouseY)) - musicPlay.resetHover(); - if (!musicPause.contains(mouseX, mouseY)) - musicPause.resetHover(); - if (!musicNext.contains(mouseX, mouseY)) - musicNext.resetHover(); - if (!musicPrevious.contains(mouseX, mouseY)) - musicPrevious.resetHover(); + for (MenuButton b : this.musicButtons) { + if (!b.contains(mouseX, mouseY)) { + b.resetHover(); + } + } if (repoButton != null && !repoButton.contains(mouseX, mouseY)) repoButton.resetHover(); if (danceRepoButton != null && !danceRepoButton.contains(mouseX, mouseY)) @@ -513,35 +692,24 @@ public class MainMenu extends BaseOpsuState { @Override public boolean mousePressed(int button, int x, int y) { + if (optionsOverlay.mousePressed(button, x, y)) { + return true; + } + // check mouse button if (button == Input.MOUSE_MIDDLE_BUTTON) return false; // music position bar - if (MusicController.isPlaying()) { - if (musicPositionBarContains(x, y)) { - lastMeasureProgress = 0f; - float pos = (x - musicBarX) / musicBarWidth; - MusicController.setPosition((int) (pos * MusicController.getDuration())); - return true; - } + if (MusicController.isPlaying() && musicPositionBarContains(x, y)) { + this.lastMeasureProgress = 0f; + float pos = (float) (x - this.musicBarX) / this.musicBarWidth; + MusicController.setPosition((int) (pos * MusicController.getDuration())); + return true; } // music button actions - if (musicPlay.contains(x, y)) { - if (MusicController.isPlaying()) { - MusicController.pause(); - BarNotifListener.EVENT.make().onBarNotif("Pause"); - } else if (!MusicController.isTrackLoading()) { - MusicController.resume(); - BarNotifListener.EVENT.make().onBarNotif("Play"); - } - return true; - } else if (musicNext.contains(x, y)) { - nextTrack(true); - BarNotifListener.EVENT.make().onBarNotif(">> Next"); - return true; - } else if (musicPrevious.contains(x, y)) { + if (musicPrev.contains(x, y)) { lastMeasureProgress = 0f; if (!previous.isEmpty()) { songMenuState.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false); @@ -551,7 +719,34 @@ public class MainMenu extends BaseOpsuState { } else { MusicController.setPosition(0); } - BarNotifListener.EVENT.make().onBarNotif("<< Previous"); + barNotifs.send("<< Previous"); + return true; + } else if (musicPlay.contains(x, y)) { + if (MusicController.isPlaying()) { + lastMeasureProgress = 0f; + MusicController.setPosition(0); + } else if (!MusicController.isTrackLoading()) { + MusicController.resume(); + } + barNotifs.send("Play"); + return true; + } else if (musicPause.contains(x, y)) { + if (MusicController.isPlaying()) { + MusicController.pause(); + barNotifs.send("Pause"); + } else if (!MusicController.isTrackLoading()) { + MusicController.resume(); + barNotifs.send("Unpause"); + } + } else if (musicStop.contains(x, y)) { + if (MusicController.isPlaying()) { + MusicController.setPosition(0); + MusicController.pause(); + } + barNotifs.send("Stop Playing"); + } else if (musicNext.contains(x, y)) { + nextTrack(true); + barNotifs.send(">> Next"); return true; } @@ -567,11 +762,10 @@ public class MainMenu extends BaseOpsuState { try { Desktop.getDesktop().browse(Constants.REPOSITORY_URI); } catch (UnsupportedOperationException e) { - BarNotifListener.EVENT.make().onBarNotif( - "The repository web page could not be opened."); + barNotifs.send("The repository web page could not be opened."); } catch (IOException e) { Log.error("could not browse to repo", e); - BubNotifListener.EVENT.make().onBubNotif("Could not browse to repo", Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, "Could not browse to repo"); } return true; } @@ -580,11 +774,10 @@ public class MainMenu extends BaseOpsuState { try { Desktop.getDesktop().browse(Constants.DANCE_REPOSITORY_URI); } catch (UnsupportedOperationException e) { - BarNotifListener.EVENT.make().onBarNotif( - "The repository web page could not be opened."); + barNotifs.send("The repository web page could not be opened."); } catch (IOException e) { Log.error("could not browse to repo", e); - BubNotifListener.EVENT.make().onBubNotif("Could not browse to repo", Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, "Could not browse to repo"); } return true; } @@ -608,36 +801,42 @@ public class MainMenu extends BaseOpsuState { } } - // start moving logo (if clicked) + final boolean logoHovered = this.logo.contains(x, y, 0.25f); if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) { - if (logo.contains(x, y, 0.25f)) { - logoState = LogoState.OPENING; - logoOpen.setTime(0); - logoTimer = 0; - playButton.getImage().setAlpha(0f); - exitButton.getImage().setAlpha(0f); + if (logoHovered) { + this.openLogo(); SoundController.playSound(SoundEffect.MENUHIT); + this.logoClickScale.setTime(0); return true; } - } - - // other button actions (if visible) - else if (logoState == LogoState.OPEN || logoState == LogoState.OPENING) { - if (logo.contains(x, y, 0.25f) || playButton.contains(x, y, 0.25f)) { + } else { + if (logoHovered || this.buttonPositions[0].contains(x, y, 0.25f)) { + this.logoClickScale.setTime(0); SoundController.playSound(SoundEffect.MENUHIT); enterSongMenu(); return true; - } else if (exitButton.contains(x, y, 0.25f)) { + } + + if (this.buttonPositions[1].contains(x, y, 0.25f)) { + if (!optionsOverlay.isActive()) { + SoundController.playSound(SoundEffect.MENUHIT); + optionsOverlay.show(); + } + return true; + } + + if (this.buttonPositions[2].contains(x, y, 0.25f)) { displayContainer.exitRequested = true; return true; } } + return false; } @Override public boolean mouseWheelMoved(int newValue) { - if (super.mouseWheelMoved(newValue)) { + if (optionsOverlay.mouseWheelMoved(newValue)) { return true; } @@ -647,17 +846,15 @@ public class MainMenu extends BaseOpsuState { @Override public boolean keyPressed(int key, char c) { - if (super.keyPressed(key, c)) { + if (optionsOverlay.keyPressed(key, c)) { return true; } switch (key) { case KEY_ESCAPE: case KEY_Q: - if (logoTimer > 0) { - logoState = LogoState.CLOSING; - logoClose.setTime(0); - logoTimer = 0; + if (logoState == LogoState.OPEN || logoState == LogoState.OPENING) { + this.closeLogo(); break; } buttonState.setMenuState(MenuState.EXIT); @@ -666,13 +863,10 @@ public class MainMenu extends BaseOpsuState { case KEY_P: SoundController.playSound(SoundEffect.MENUHIT); if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) { - logoState = LogoState.OPENING; - logoOpen.setTime(0); - logoTimer = 0; - playButton.getImage().setAlpha(0f); - exitButton.getImage().setAlpha(0f); - } else + this.openLogo(); + } else { enterSongMenu(); + } return true; case KEY_D: SoundController.playSound(SoundEffect.MENUHIT); @@ -687,9 +881,28 @@ public class MainMenu extends BaseOpsuState { case KEY_DOWN: UI.changeVolume(-1); return true; + case KEY_O: + if (input.isControlDown()) { + optionsOverlay.show(); + } } return false; } + + @Override + public boolean keyReleased(int key, char c) { + return optionsOverlay.keyReleased(key, c); + } + + @Override + public boolean mouseReleased(int button, int x, int y) { + return optionsOverlay.mouseReleased(button, x, y); + } + + @Override + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + return optionsOverlay.mouseDragged(oldx, oldy, newx, newy); + } /** * Returns true if the coordinates are within the music position bar bounds. @@ -730,11 +943,69 @@ public class MainMenu extends BaseOpsuState { * Enters the song menu, or the downloads menu if no beatmaps are loaded. */ private void enterSongMenu() { + if (optionsOverlay.isActive()) { + optionsOverlay.hide(); + } OpsuState state = songMenuState; if (BeatmapSetList.get().getMapSetCount() == 0) { - downloadState.notifyOnLoad("Download some beatmaps to get started!"); + barNotifs.send("Download some beatmaps to get started!"); state = downloadState; } displayContainer.switchState(state); } + + private void openLogo() { + buttonAnimation.change(300, 0f, 1f, OUT_QUAD); + logoPosition.change(300, 0, logoPositionOffsetX, OUT_CUBIC); + logoState = LogoState.OPENING; + } + + private void closeLogo() { + buttonAnimation.change(500, 0f, 1f, OUT_QUAD); + logoPosition.change(1800, 0, logoPositionOffsetX, IN_QUAD); + logoState = LogoState.CLOSING; + } + + private void drawMenuButton( + Image img, + int x, + int y, + int clipxtop, + int clipxbot, + Color col) + { + col.bind(); + final Texture t = img.getTexture(); + t.bind(); + + final int width = img.getWidth(); + final int height = img.getHeight(); + final float twidth = t.getWidth(); + final float theight = t.getHeight(); + y -= height / 2; + + final float texXtop = clipxtop > 0 ? (float) clipxtop / width * twidth : 0f; + final float texXbot = clipxbot > 0 ? (float) clipxbot / width * twidth : 0f; + + GL11.glBegin(SGL.GL_QUADS); + GL11.glTexCoord2f(texXtop, 0); + GL11.glVertex3i(x + clipxtop, y, 0); + GL11.glTexCoord2f(twidth, 0); + GL11.glVertex3i(x + width, y, 0); + GL11.glTexCoord2f(twidth, theight); + GL11.glVertex3i(x + width, y + height, 0); + GL11.glTexCoord2f(texXbot, theight); + GL11.glVertex3i(x + clipxbot, y + height, 0); + GL11.glEnd(); + } + + private static class PulseData { + private int position; + private float initialScale; + + private PulseData(int position, float initialScale) { + this.position = position; + this.initialScale = initialScale; + } + } } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 3bb48625..70bf2129 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -48,6 +48,7 @@ import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimationEquation; +import java.awt.Point; import java.io.File; import java.nio.file.Path; import java.nio.file.StandardWatchEventKinds; @@ -63,11 +64,11 @@ import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.SpriteSheet; import org.newdawn.slick.gui.TextField; -import yugecin.opsudance.core.state.ComplexOpsuState; -import yugecin.opsudance.events.BarNotifListener; -import yugecin.opsudance.options.OptionGroups; -import yugecin.opsudance.ui.OptionsOverlay; +import yugecin.opsudance.core.InstanceContainer; +import yugecin.opsudance.core.state.ComplexOpsuState; + +import static itdelatrisu.opsu.GameImage.*; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -99,7 +100,7 @@ public class SongMenu extends ComplexOpsuState { private static final int SEARCH_TRANSITION_TIME = 250; /** Line width of the header/footer divider. */ - private static final int DIVIDER_LINE_WIDTH = 4; + private static final int DIVIDER_LINE_WIDTH = 3; /** Song node class representing an BeatmapSetNode and file index. */ private static class SongNode { @@ -161,7 +162,7 @@ public class SongMenu extends ComplexOpsuState { private BeatmapSetNode hoverIndex = null; /** The selection buttons. */ - private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton; + private MenuButton selectModeButton, selectModsButton, selectRandomButton, selectMapOptionsButton; /** The search textfield. */ private TextField searchTextField; @@ -311,12 +312,8 @@ public class SongMenu extends ComplexOpsuState { /** Sort order dropdown menu. */ private DropdownMenu sortMenu; - private final OptionsOverlay optionsOverlay; - public SongMenu() { super(); - optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions); - overlays.add(optionsOverlay); } @Override @@ -325,26 +322,27 @@ public class SongMenu extends ComplexOpsuState { components.clear(); + final float footerHeight = height * 0.116666666666f; + // header/footer coordinates - headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() + + headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getHeight() + Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() + Fonts.SMALL.getLineHeight(); - footerY = displayContainer.height - GameImage.SELECTION_MODS.getImage().getHeight(); + footerY = height - footerHeight; // footer logo coordinates - float footerHeight = displayContainer.height - footerY; footerLogoSize = footerHeight * 3.25f; Image logo = GameImage.MENU_LOGO.getImage(); logo = logo.getScaledCopy(footerLogoSize / logo.getWidth()); - footerLogoButton = new MenuButton(logo, displayContainer.width - footerHeight * 0.8f, displayContainer.height - footerHeight * 0.65f); + footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f); footerLogoButton.setHoverAnimationDuration(1); footerLogoButton.setHoverExpand(1.2f); // initialize sorts - int sortWidth = (int) (displayContainer.width * 0.12f); - int posX = (int) (displayContainer.width * 0.87f); - int posY = (int) (headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f); - sortMenu = new DropdownMenu(displayContainer, BeatmapSortOrder.values(), posX, posY, sortWidth) { + int sortWidth = (int) (width * 0.12f); + int posX = (int) (width * 0.87f); + int posY = (int) (headerY - GameImage.MENU_TAB.getHeight() * 2.25f); + sortMenu = new DropdownMenu(BeatmapSortOrder.values(), posX, posY, sortWidth) { @Override public void itemSelected(int index, BeatmapSortOrder item) { BeatmapSortOrder.set(item); @@ -373,25 +371,25 @@ public class SongMenu extends ComplexOpsuState { // initialize group tabs for (BeatmapGroup group : BeatmapGroup.values()) - group.init(displayContainer.width, headerY - DIVIDER_LINE_WIDTH / 2); + group.init(width, headerY - DIVIDER_LINE_WIDTH / 2); // initialize score data buttons - ScoreData.init(displayContainer.width, headerY + displayContainer.height * 0.01f); + ScoreData.init(width, headerY + height * 0.01f); // song button background & graphics context Image menuBackground = GameImage.MENU_BUTTON_BG.getImage(); // song button coordinates - buttonX = displayContainer.width * 0.6f; + buttonX = width * 0.6f; //buttonY = headerY; buttonWidth = menuBackground.getWidth(); buttonHeight = menuBackground.getHeight(); buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS; // search - int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); + int textFieldX = (int) (width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2); - searchTextField = new TextField(Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) { + searchTextField = new TextField(Fonts.BOLD, textFieldX, textFieldY, (int) (width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) { @Override public boolean isFocusable() { return false; @@ -405,33 +403,23 @@ public class SongMenu extends ComplexOpsuState { components.add(searchTextField); // selection buttons - Image selectionMods = GameImage.SELECTION_MODS.getImage(); - int selectButtonsWidth = selectionMods.getWidth(); - int selectButtonsHeight = selectionMods.getHeight(); - if (selectButtonsHeight < 20) { - selectButtonsHeight = 100; - } - if (selectButtonsWidth < 20) { - selectButtonsWidth = 100; - } - float selectX = displayContainer.width * 0.183f + selectButtonsWidth / 2f; - float selectY = displayContainer.height - selectButtonsHeight / 2f; - float selectOffset = selectButtonsWidth * 1.05f; - selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(), - selectX, selectY); - selectRandomButton = new MenuButton(GameImage.SELECTION_RANDOM_OVERLAY.getImage(), - selectX + selectOffset, selectY); - selectMapOptionsButton = new MenuButton(GameImage.SELECTION_OPTIONS_OVERLAY.getImage(), - selectX + selectOffset * 2f, selectY); - selectOptionsButton = new MenuButton(GameImage.SELECTION_OTHER_OPTIONS_OVERLAY.getImage(), - selectX + selectOffset * 3f, selectY); + // TODO: the origin should be bottomleft or something + float selectX = width * (isWidescreen ? 0.164f : 0.1875f); + final float footerButtonWidth = footerHeight * 0.84f; + selectModeButton = new MenuButton(SELECTION_MODE_OVERLAY, selectX, footerY); + selectX += footerHeight + 2; + selectModsButton = new MenuButton(SELECTION_MODS_OVERLAY, selectX, footerY); + selectX += footerButtonWidth; + selectRandomButton = new MenuButton(SELECTION_RANDOM_OVERLAY, selectX, footerY); + selectX += footerButtonWidth; + selectMapOptionsButton = new MenuButton(SELECTION_OPTIONS_OVERLAY, selectX, footerY); + selectModeButton.setHoverFade(0f); selectModsButton.setHoverFade(0f); selectRandomButton.setHoverFade(0f); selectMapOptionsButton.setHoverFade(0f); - selectOptionsButton.setHoverFade(0f); // loader - int loaderDim = GameImage.MENU_MUSICNOTE.getImage().getWidth(); + int loaderDim = GameImage.MENU_MUSICNOTE.getWidth(); SpriteSheet spr = new SpriteSheet(GameImage.MENU_LOADER.getImage(), loaderDim, loaderDim); loader = new Animation(spr, 50); @@ -446,14 +434,13 @@ public class SongMenu extends ComplexOpsuState { if (!displayContainer.isInState(SongMenu.class)) { return; } - BarNotifListener.EVENT.make().onBarNotif( - "Changed is Songs folder detected. Hit F5 to refresh."); + barNotifs.send("Changes in Songs folder detected. Hit F5 to refresh."); } }); // star stream - starStream = new StarStream(displayContainer.width, (displayContainer.height - GameImage.STAR.getImage().getHeight()) / 2, -displayContainer.width, 0, MAX_STREAM_STARS); - starStream.setPositionSpread(displayContainer.height / 20f); + starStream = new StarStream(width, height2 - GameImage.STAR.getImage().getHeight() / 2, -width, 0, MAX_STREAM_STARS); + starStream.setPositionSpread(height / 20f); starStream.setDirectionSpread(10f); } @@ -461,11 +448,6 @@ public class SongMenu extends ComplexOpsuState { public void render(Graphics g) { g.setBackground(Color.black); - int width = displayContainer.width; - int height = displayContainer.height; - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - // background if (focusNode != null) { Beatmap focusNodeBeatmap = focusNode.getSelectedBeatmap(); @@ -542,14 +524,12 @@ public class SongMenu extends ComplexOpsuState { } // top/bottom bars - g.setColor(Colors.BLACK_ALPHA); + g.setColor(Color.black); g.fillRect(0, 0, width, headerY); g.fillRect(0, footerY, width, height - footerY); g.setColor(Colors.BLUE_DIVIDER); - g.setLineWidth(DIVIDER_LINE_WIDTH); - g.drawLine(0, headerY, width, headerY); - g.drawLine(0, footerY, width, footerY); - g.resetLineWidth(); + g.fillRect(0, headerY, width, DIVIDER_LINE_WIDTH); + g.fillRect(0, footerY, width, DIVIDER_LINE_WIDTH); // footer logo (pulsing) Float position = MusicController.getBeatProgress(); @@ -563,10 +543,8 @@ public class SongMenu extends ComplexOpsuState { footerLogoButton.draw(Color.white, 1f - expand); Image ghostLogo = GameImage.MENU_LOGO.getImage(); ghostLogo = ghostLogo.getScaledCopy((1f + expand) * footerLogoSize / ghostLogo.getWidth()); - float oldGhostAlpha = Colors.GHOST_LOGO.a; - Colors.GHOST_LOGO.a *= (1f - position); - ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY(), Colors.GHOST_LOGO); - Colors.GHOST_LOGO.a = oldGhostAlpha; + ghostLogo.setAlpha(0.25f * (1f - position)); + ghostLogo.drawCentered(footerLogoButton.getX(), footerLogoButton.getY()); } // header @@ -636,14 +614,19 @@ public class SongMenu extends ComplexOpsuState { } // selection buttons - GameImage.SELECTION_MODS.getImage().drawCentered(selectModsButton.getX(), selectModsButton.getY()); + Point c; + c = selectModeButton.bottomLeft(); + SELECTION_MODE.getImage().draw(c.x, c.y - SELECTION_MODE.getHeight()); + selectModeButton.draw(); + c = selectModsButton.bottomLeft(); + SELECTION_MODS.getImage().draw(c.x, c.y - SELECTION_MODS.getHeight()); selectModsButton.draw(); - GameImage.SELECTION_RANDOM.getImage().drawCentered(selectRandomButton.getX(), selectRandomButton.getY()); + c = selectRandomButton.bottomLeft(); + SELECTION_RANDOM.getImage().draw(c.x, c.y - SELECTION_RANDOM.getHeight()); selectRandomButton.draw(); - GameImage.SELECTION_OPTIONS.getImage().drawCentered(selectMapOptionsButton.getX(), selectMapOptionsButton.getY()); + c = selectMapOptionsButton.bottomLeft(); + SELECTION_OPTIONS.getImage().draw(c.x, c.y - SELECTION_OPTIONS.getHeight()); selectMapOptionsButton.draw(); - GameImage.SELECTION_OTHER_OPTIONS.getImage().drawCentered(selectOptionsButton.getX(), selectOptionsButton.getY()); - selectOptionsButton.draw(); // group tabs BeatmapGroup currentGroup = BeatmapGroup.current(); @@ -701,11 +684,11 @@ public class SongMenu extends ComplexOpsuState { g.fillRect(0, 0, width, height); UI.drawLoadingProgress(g); + } else { + optionsOverlay.render(g); + backButton.draw(g); } - // back button - else - UI.getBackButton().draw(g); UI.draw(g); @@ -715,8 +698,18 @@ public class SongMenu extends ComplexOpsuState { @Override public void preRenderUpdate() { super.preRenderUpdate(); + + optionsOverlay.preRenderUpdate(); - int delta = displayContainer.renderDelta; + int mouseX = InstanceContainer.mouseX; + int mouseY = InstanceContainer.mouseY; + if (optionsOverlay.containsMouse()) { + // dirty hack to not show elements underneath options overlay as hovered + mouseX = -mouseX; + mouseY = -mouseY; + } + + int delta = renderDelta; UI.update(delta); if (reloadThread == null) MusicController.loopTrackIfEnded(true); @@ -732,13 +725,11 @@ public class SongMenu extends ComplexOpsuState { MusicController.playThemeSong(config.themeBeatmap); reloadThread = null; } - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); + backButton.hoverUpdate(); + selectModeButton.hoverUpdate(delta, mouseX, mouseY); selectModsButton.hoverUpdate(delta, mouseX, mouseY); selectRandomButton.hoverUpdate(delta, mouseX, mouseY); selectMapOptionsButton.hoverUpdate(delta, mouseX, mouseY); - selectOptionsButton.hoverUpdate(delta, mouseX, mouseY); footerLogoButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); // beatmap menu timer @@ -873,6 +864,10 @@ public class SongMenu extends ComplexOpsuState { if (super.mousePressed(button, x, y)) { return true; } + + if (optionsOverlay.mousePressed(button, x, y)) { + return true; + } if (button == Input.MOUSE_MIDDLE_BUTTON) { return false; @@ -892,6 +887,10 @@ public class SongMenu extends ComplexOpsuState { if (super.mouseReleased(button, x, y)) { return true; } + + if (optionsOverlay.mouseReleased(button, x, y)) { + return true; + } if (button == Input.MOUSE_MIDDLE_BUTTON) { return false; @@ -908,14 +907,17 @@ public class SongMenu extends ComplexOpsuState { return true; } - if (UI.getBackButton().contains(x, y)) { + if (backButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); displayContainer.switchState(mainmenuState); return true; } // selection buttons - if (selectModsButton.contains(x, y)) { + if (selectModeButton.contains(x, y)) { + barNotifs.send("There are no other modes available."); + return true; + } else if (selectModsButton.contains(x, y)) { this.keyPressed(KEY_F1, '\0'); return true; } else if (selectRandomButton.contains(x, y)) { @@ -924,10 +926,6 @@ public class SongMenu extends ComplexOpsuState { } else if (selectMapOptionsButton.contains(x, y)) { this.keyPressed(KEY_F3, '\0'); return true; - } else if (selectOptionsButton.contains(x, y)) { - SoundController.playSound(SoundEffect.MENUHIT); - optionsOverlay.show(); - return true; } // group tabs @@ -955,7 +953,7 @@ public class SongMenu extends ComplexOpsuState { setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) { - BarNotifListener.EVENT.make().onBarNotif(group.getEmptyMessage()); + barNotifs.send(group.getEmptyMessage()); } return true; } @@ -1038,6 +1036,10 @@ public class SongMenu extends ComplexOpsuState { if (super.keyPressed(key, c)) { return true; } + + if (optionsOverlay.keyPressed(key, c)) { + return true; + } // block input if ((reloadThread != null && key != KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) { @@ -1192,6 +1194,10 @@ public class SongMenu extends ComplexOpsuState { if (super.mouseDragged(oldx, oldy, newx, newy)) { return true; } + + if (optionsOverlay.mouseDragged(oldx, oldy, newx, newy)) { + return true; + } if (isInputBlocked()) { return true; @@ -1225,6 +1231,10 @@ public class SongMenu extends ComplexOpsuState { if (super.mouseWheelMoved(newValue)) { return true; } + + if (optionsOverlay.mouseWheelMoved(newValue)) { + return true; + } if (isInputBlocked()) { return true; @@ -1242,16 +1252,25 @@ public class SongMenu extends ComplexOpsuState { changeIndex(shift); return false; } + + @Override + public boolean keyReleased(int key, char c) { + if (super.keyReleased(key, c)) { + return true; + } + + return optionsOverlay.keyReleased(key, c); + } @Override public void enter() { super.enter(); UI.enter(); + selectModeButton.resetHover(); selectModsButton.resetHover(); selectRandomButton.resetHover(); selectMapOptionsButton.resetHover(); - selectOptionsButton.resetHover(); hoverOffset.setTime(0); hoverIndex = null; isScrollingToFocusNode = false; @@ -1761,7 +1780,7 @@ public class SongMenu extends ComplexOpsuState { Beatmap beatmap = MusicController.getBeatmap(); if (focusNode == null || beatmap != focusNode.getSelectedBeatmap()) { - BarNotifListener.EVENT.make().onBarNotif("Unable to load the beatmap audio."); + barNotifs.send("Unable to load the beatmap audio."); return; } diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index eba0f109..44800dda 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -22,13 +22,12 @@ import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.beatmap.BeatmapSetList; +import itdelatrisu.opsu.ui.Cursor; import itdelatrisu.opsu.ui.UI; import org.lwjgl.input.Keyboard; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.state.BaseOpsuState; @@ -65,9 +64,6 @@ public class Splash extends BaseOpsuState { return; } - System.out.println( - Renderer.get().getClass() - ); inited = true; thread = new Thread() { @Override @@ -111,7 +107,13 @@ public class Splash extends BaseOpsuState { @Override public void render(Graphics g) { g.setBackground(Color.black); - GameImage.MENU_LOGO.getImage().drawCentered(displayContainer.width / 2, displayContainer.height / 2); + final Color col; + if (OPTION_COLOR_MAIN_MENU_LOGO.state) { + col = Cursor.lastCursorColor; + } else { + col = Color.white; + } + GameImage.MENU_LOGO.getImage().drawCentered(width2, height2, col); UI.drawLoadingProgress(g); } diff --git a/src/itdelatrisu/opsu/ui/Colors.java b/src/itdelatrisu/opsu/ui/Colors.java index cd17f0fd..51477fc9 100644 --- a/src/itdelatrisu/opsu/ui/Colors.java +++ b/src/itdelatrisu/opsu/ui/Colors.java @@ -28,6 +28,7 @@ public class Colors { BLACK_ALPHA = new Color(0, 0, 0, 0.5f), BLACK_ALPHA_75 = new Color(0, 0, 0, 0.75f), BLACK_ALPHA_85 = new Color(0, 0, 0, 0.85f), + WHITE_ALPHA_75 = new Color(1, 1, 1, 0.75f), WHITE_ALPHA = new Color(255, 255, 255, 0.5f), BLUE_DIVIDER = new Color(49, 94, 237), BLUE_BACKGROUND = new Color(74, 130, 255), @@ -53,8 +54,7 @@ public class Colors { BUB_WHITE = new Color(220, 220, 220), BUB_PURPLE = new Color(94, 46, 149), BUB_RED = new Color(141, 49, 16), - BUB_ORANGE = new Color(138, 72, 51), - GHOST_LOGO = new Color(1.0f, 1.0f, 1.0f, 0.25f); + BUB_ORANGE = new Color(138, 72, 51); // This class should not be instantiated. private Colors() {} diff --git a/src/itdelatrisu/opsu/ui/Cursor.java b/src/itdelatrisu/opsu/ui/Cursor.java index 0cc0dc02..44e8e30f 100644 --- a/src/itdelatrisu/opsu/ui/Cursor.java +++ b/src/itdelatrisu/opsu/ui/Cursor.java @@ -30,6 +30,7 @@ import yugecin.opsudance.Dancer; import yugecin.opsudance.skinning.SkinService; import static yugecin.opsudance.options.Options.*; +import static yugecin.opsudance.core.InstanceContainer.*; /** * Updates and draws the cursor. @@ -263,8 +264,8 @@ public class Cursor { * If the old style cursor is being used, this will do nothing. * @param delta the delta interval since the last call */ - public void updateAngle(int delta) { - cursorAngle += delta / 40f; + public void updateAngle() { + cursorAngle += renderDelta / 40f; cursorAngle %= 360; } diff --git a/src/itdelatrisu/opsu/ui/DropdownMenu.java b/src/itdelatrisu/opsu/ui/DropdownMenu.java index ec56dfd4..317aded0 100644 --- a/src/itdelatrisu/opsu/ui/DropdownMenu.java +++ b/src/itdelatrisu/opsu/ui/DropdownMenu.java @@ -29,15 +29,14 @@ import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; import org.newdawn.slick.Input; import org.newdawn.slick.UnicodeFont; -import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.components.Component; +import static yugecin.opsudance.core.InstanceContainer.*; + public class DropdownMenu extends Component { private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f; - private final DisplayContainer displayContainer; - private E[] items; private String[] itemNames; private int selectedItemIndex; @@ -61,8 +60,7 @@ public class DropdownMenu extends Component { private Image chevronDown; private Image chevronRight; - public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) { - this.displayContainer = displayContainer; + public DropdownMenu(E[] items, int x, int y, int width) { init(items, x, y, width); } @@ -143,13 +141,13 @@ public class DropdownMenu extends Component { @Override public void render(Graphics g) { - int delta = displayContainer.renderDelta; + int delta = renderDelta; // update animation expandProgress.update((expanded) ? delta : -delta * 2); // get parameters - int idx = getIndexAt(displayContainer.mouseY); + int idx = getIndexAt(mouseY); float t = expandProgress.getValue(); if (expanded) { t = AnimationEquation.OUT_CUBIC.calc(t); @@ -245,7 +243,7 @@ public class DropdownMenu extends Component { return; } - int idx = getIndexAt(displayContainer.mouseY); + int idx = getIndexAt(mouseY); if (idx == -2) { this.expanded = false; return; diff --git a/src/itdelatrisu/opsu/ui/MenuButton.java b/src/itdelatrisu/opsu/ui/MenuButton.java index a155d270..bc26188c 100644 --- a/src/itdelatrisu/opsu/ui/MenuButton.java +++ b/src/itdelatrisu/opsu/ui/MenuButton.java @@ -18,10 +18,13 @@ package itdelatrisu.opsu.ui; +import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimationEquation; +import java.awt.Point; + import org.newdawn.slick.Animation; import org.newdawn.slick.Color; import org.newdawn.slick.Font; @@ -98,9 +101,6 @@ public class MenuButton { /** The default max rotation angle of the button. */ private static final float DEFAULT_ANGLE_MAX = 30f; - /** The last scale at which the button was drawn. */ - private float lastScale = 1f; - /** * Creates a new button from an Image. * @param img the image @@ -115,6 +115,14 @@ public class MenuButton { this.yRadius = img.getHeight() / 2f; } + public MenuButton(GameImage img, float topleftX, float topleftY) { + this.img = img.getImage(); + this.xRadius = img.getWidth() / 2f; + this.yRadius = img.getHeight() / 2f; + this.x = topleftX + this.xRadius; + this.y = topleftY + this.yRadius; + } + /** * Creates a new button from a 3-part Image. * @param imgCenter the center image @@ -168,11 +176,20 @@ public class MenuButton { * Returns the center y coordinate. */ public float getY() { return y; } + + public Point bottomLeft() { + return new Point((int) (x - xRadius), (int) (y + yRadius)); + } /** - * Returns the last scale at which the button was drawn. + * Returns the scale multiplier, from the hover effect, used to draw the button. */ - public float getLastScale() { return lastScale; } + public float getCurrentHoverExpandValue() { + if ((hoverEffect & EFFECT_EXPAND) == 0) { + return 1f; + } + return scale.getValue(); + } /** * Sets text to draw in the middle of the button. @@ -229,7 +246,6 @@ public class MenuButton { xScaleOffset = image.getWidth() / 2f - xRadius; yScaleOffset = image.getHeight() / 2f - yRadius; } - lastScale = scaleOverride; if (hoverEffect == 0) image.draw(x - xRadius, y - yRadius, filter); else { @@ -243,7 +259,6 @@ public class MenuButton { xScaleOffset = image.getWidth() / 2f - xRadius; yScaleOffset = image.getHeight() / 2f - yRadius; } - lastScale *= scale.getValue(); } } if ((hoverEffect & EFFECT_FADE) > 0) diff --git a/src/itdelatrisu/opsu/ui/UI.java b/src/itdelatrisu/opsu/ui/UI.java index 8acfb1cf..df4c5e55 100644 --- a/src/itdelatrisu/opsu/ui/UI.java +++ b/src/itdelatrisu/opsu/ui/UI.java @@ -22,27 +22,21 @@ import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.beatmap.BeatmapParser; -import itdelatrisu.opsu.beatmap.OszUnpacker; -import itdelatrisu.opsu.replay.ReplayImporter; import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.ui.BackButton; import static yugecin.opsudance.options.Options.*; +import static yugecin.opsudance.core.InstanceContainer.*; /** * Draws common UI components. */ public class UI { - /** Back button. */ - private static BackButton backButton; - /** Time to show volume image, in milliseconds. */ private static final int VOLUME_DISPLAY_TIME = 1500; @@ -58,20 +52,9 @@ public class UI { /** The alpha level of the current tooltip (if any). */ private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); - // game-related variables - private static DisplayContainer displayContainer; - // This class should not be instantiated. private UI() {} - /** - * Initializes UI data. - */ - public static void init(DisplayContainer displayContainer) { - UI.displayContainer = displayContainer; - backButton = new BackButton(displayContainer); - } - /** * Updates all UI components by a delta interval. * @param delta the delta interval since the last call. @@ -93,15 +76,9 @@ public class UI { * Resets the necessary UI components upon entering a state. */ public static void enter() { - backButton.resetHover(); resetTooltip(); } - /** - * Returns the 'menu-back' MenuButton. - */ - public static BackButton getBackButton() { return backButton; } - /** * Draws a tab image and text centered at a location. * @param x the center x coordinate @@ -145,13 +122,13 @@ public class UI { else if (ratio >= 0.9f) xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f)); - img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f); + img.drawCentered(width - img.getWidth() / 2f + xOffset, height2); float barHeight = img.getHeight() * 0.9f; float volume = OPTION_MASTER_VOLUME.val / 100f; g.setColor(Color.white); g.fillRoundRect( - displayContainer.width - (img.getWidth() * 0.368f) + xOffset, - (displayContainer.height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), + width - (img.getWidth() * 0.368f) + xOffset, + height2 - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), img.getWidth() * 0.15f, barHeight * volume, 3 ); } @@ -193,33 +170,25 @@ public class UI { int progress; // determine current action - // - /* - TODO - if ((file = OszUnpacker.getCurrentFileName()) != null) { + if ((file = oszunpacker.getCurrentFileName()) != null) { text = "Unpacking new beatmaps..."; - progress = OszUnpacker.getUnpackerProgress(); - } else if ((file = BeatmapParser.getCurrentFileName()) != null) { - text = (BeatmapParser.getStatus() == BeatmapParser.Status.INSERTING) ? + progress = oszunpacker.getUnpackerProgress(); + } else if ((file = beatmapParser.getCurrentFileName()) != null) { + text = (beatmapParser.getStatus() == BeatmapParser.Status.INSERTING) ? "Updating database..." : "Loading beatmaps..."; - progress = BeatmapParser.getParserProgress(); - } else if ((file = ReplayImporter.getCurrentFileName()) != null) { + progress = beatmapParser.getParserProgress(); + } else if ((file = replayImporter.getCurrentFileName()) != null) { text = "Importing replays..."; - progress = ReplayImporter.getLoadingProgress(); + progress = replayImporter.getLoadingProgress(); } else if ((file = SoundController.getCurrentFileName()) != null) { text = "Loading sounds..."; progress = SoundController.getLoadingProgress(); } else return; - */ - - if (true) { - return; // TODO - } // draw loading info - float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f; - float lineY = displayContainer.height - marginY; + float marginX = width * 0.02f, marginY = height * 0.02f; + float lineY = height - marginY; int lineOffsetY = Fonts.MEDIUM.getLineHeight(); if (OPTION_LOAD_VERBOSE.state) { // verbose: display percentages and file names @@ -232,7 +201,7 @@ public class UI { Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white); g.setColor(Color.white); g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f), - (displayContainer.width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 + (width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 ); } } @@ -256,7 +225,7 @@ public class UI { float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight, Color bgColor, Color scrollbarColor, boolean right ) { - float scrollbarWidth = displayContainer.width * 0.00347f; + float scrollbarWidth = width * 0.00347f; float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength; float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown)); float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0); @@ -292,8 +261,8 @@ public class UI { if (tooltipAlpha.getTime() == 0 || tooltip == null) return; - int margin = displayContainer.width / 100, textMarginX = 2; - int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2; + int margin = width / 100, textMarginX = 2; + int offset = GameImage.CURSOR_MIDDLE.getWidth() / 2; int lineHeight = Fonts.SMALL.getLineHeight(); int textWidth = textMarginX * 2, textHeight = lineHeight; if (tooltipNewlines) { @@ -310,14 +279,14 @@ public class UI { textWidth += Fonts.SMALL.getWidth(tooltip); // get drawing coordinates - int x = displayContainer.mouseX + offset; - int y = displayContainer.mouseY + offset; - if (x + textWidth > displayContainer.width - margin) - x = displayContainer.width - margin - textWidth; + int x = mouseX + offset; + int y = mouseY + offset; + if (x + textWidth > width - margin) + x = width - margin - textWidth; else if (x < margin) x = margin; - if (y + textHeight > displayContainer.height - margin) - y = displayContainer.height - margin - textHeight; + if (y + textHeight > height - margin) + y = height - margin - textHeight; else if (y < margin) y = margin; diff --git a/src/itdelatrisu/opsu/ui/animations/AnimatedValue.java b/src/itdelatrisu/opsu/ui/animations/AnimatedValue.java index da8bd8d5..3b385ded 100644 --- a/src/itdelatrisu/opsu/ui/animations/AnimatedValue.java +++ b/src/itdelatrisu/opsu/ui/animations/AnimatedValue.java @@ -57,6 +57,41 @@ public class AnimatedValue { this.diff = max - min; this.eqn = eqn; } + + public void change(int duration, float min, float max, AnimationEquation eqn) { + float progress = (float) this.time / this.duration; + if (this.eqn != eqn) { + if (this.time != 0 && this.time != this.duration) { + progress = eqn.uncalc(this.eqn.calc(progress)); + } + this.eqn = eqn; + } + this.duration = duration; + this.time = (int) (this.duration * progress); + this.base = min; + this.diff = max - min; + this.updateValue(); + } + + /** + * Change the from and to values + * @param min start value + * @param max end value + */ + public void setValues(float min, float max) { + this.base = min; + this.value = min; + this.diff = max - min; + this.updateValue(); + } + + /** + * Gets the max (end) value + * @return the max (end) value + */ + public float getMax() { + return this.base + this.diff; + } /** * Returns the current value. diff --git a/src/itdelatrisu/opsu/ui/animations/AnimationEquation.java b/src/itdelatrisu/opsu/ui/animations/AnimationEquation.java index 146be941..db78fd1e 100644 --- a/src/itdelatrisu/opsu/ui/animations/AnimationEquation.java +++ b/src/itdelatrisu/opsu/ui/animations/AnimationEquation.java @@ -312,4 +312,19 @@ public enum AnimationEquation { * @return the new {@code t} value [0,1] */ public abstract float calc(float t); + + public float uncalc(float x) { + float min = 0f; + float max = 1f; + // 7 iterations is already decent, do we need 8? + for (int i = 0; i < 8; i++) { + float pos = (min + max) / 2f; + if (this.calc(pos) > x) { + max = pos; + } else { + min = pos; + } + } + return (min + max) / 2f; + } } diff --git a/src/org/newdawn/slick/Input.java b/src/org/newdawn/slick/Input.java index b340588b..673ca2cd 100644 --- a/src/org/newdawn/slick/Input.java +++ b/src/org/newdawn/slick/Input.java @@ -43,7 +43,7 @@ import static org.lwjgl.input.Keyboard.*; * * @author kevin */ -@SuppressWarnings({"rawtypes", "unchecked", "unused"}) +@SuppressWarnings({"unused"}) public class Input { /** A helper for left ALT */ @@ -98,8 +98,6 @@ public class Input { protected char[] keys = new char[1024]; /** True if the key has been pressed since last queries */ protected boolean[] pressed = new boolean[1024]; - /** The time since the next key repeat to be fired for the key */ - protected long[] nextRepeat = new long[1024]; /** The listeners to notify of key events */ protected ArrayList keyListeners = new ArrayList<>(); @@ -113,13 +111,6 @@ public class Input { /** True if the display is active */ private boolean displayActive = true; - /** True if key repeat is enabled */ - private boolean keyRepeat; - /** The initial delay for key repeat starts */ - private int keyRepeatInitial; - /** The interval of key repeat */ - private int keyRepeatInterval; - /** The clicked button */ private int clickButton; @@ -344,7 +335,6 @@ public class Input { keys[eventKey] = Keyboard.getEventCharacter(); pressed[eventKey] = true; - nextRepeat[eventKey] = System.currentTimeMillis() + keyRepeatInitial; for (KeyListener listener : keyListeners) { if (listener.keyPressed(eventKey, Keyboard.getEventCharacter())) { @@ -353,7 +343,6 @@ public class Input { } } else { int eventKey = Keyboard.getEventKey(); - nextRepeat[eventKey] = 0; for (KeyListener listener : keyListeners) { if (listener.keyReleased(eventKey, keys[eventKey])) { @@ -424,21 +413,6 @@ public class Input { lastMouseY = getMouseY(); } } - - if (keyRepeat) { - for (int i=0;i<1024;i++) { - if (pressed[i] && (nextRepeat[i] != 0)) { - if (System.currentTimeMillis() > nextRepeat[i]) { - nextRepeat[i] = System.currentTimeMillis() + keyRepeatInterval; - for (KeyListener listener : keyListeners) { - if (listener.keyPressed(i, keys[i])) { - break; - } - } - } - } - } - } if (Display.isCreated()) { displayActive = Display.isActive(); diff --git a/src/yugecin/opsudance/Dancer.java b/src/yugecin/opsudance/Dancer.java index 944df3d1..4f5ddeb2 100644 --- a/src/yugecin/opsudance/Dancer.java +++ b/src/yugecin/opsudance/Dancer.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,7 +35,6 @@ import yugecin.opsudance.movers.factories.*; import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController; import yugecin.opsudance.movers.slidermovers.InheritedSliderMoverController; import yugecin.opsudance.movers.slidermovers.SliderMoverController; -import yugecin.opsudance.render.GameObjectRenderer; import yugecin.opsudance.spinners.*; import java.awt.*; @@ -252,8 +251,8 @@ public class Dancer { } } Pippi.dance(time, c, isCurrentLazySlider); - x = Utils.clamp(x, 10, displayContainer.width - 10); - y = Utils.clamp(y, 10, displayContainer.height - 10); + x = Utils.clamp(x, 10, width - 10); + y = Utils.clamp(y, 10, height - 10); } private void createNewMover() { diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java index f9ee69a4..2fed84d0 100644 --- a/src/yugecin/opsudance/core/DisplayContainer.java +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,8 @@ import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.render.CurveRenderState; +import itdelatrisu.opsu.render.FrameBufferCache; import itdelatrisu.opsu.replay.PlaybackSpeed; -import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Cursor; import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.UI; @@ -43,16 +43,16 @@ import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.util.Log; import yugecin.opsudance.core.errorhandling.ErrorDumpable; import yugecin.opsudance.core.state.OpsuState; -import yugecin.opsudance.core.state.specialstates.BarNotificationState; -import yugecin.opsudance.core.state.specialstates.BubNotifState; -import yugecin.opsudance.core.state.specialstates.FpsRenderState; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.events.ResolutionChangedListener; import yugecin.opsudance.events.SkinChangedListener; +import yugecin.opsudance.ui.BackButton; import yugecin.opsudance.utils.GLHelper; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.Entrypoint.sout; import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; @@ -60,33 +60,23 @@ import static yugecin.opsudance.options.Options.*; /** * based on org.newdawn.slick.AppGameContainer */ -public class DisplayContainer implements ErrorDumpable, ResolutionChangedListener, SkinChangedListener { +public class DisplayContainer implements ErrorDumpable, SkinChangedListener { private static SGL GL = Renderer.get(); - private FpsRenderState fpsState; - private BarNotificationState barNotifState; - private BubNotifState bubNotifState; - private OpsuState state; public final DisplayMode nativeDisplayMode; private Graphics graphics; - public int width; - public int height; - - public int mouseX; - public int mouseY; - private int targetUpdatesPerSecond; public int targetUpdateInterval; private int targetRendersPerSecond; public int targetRenderInterval; public int targetBackgroundRenderInterval; - public int renderDelta; + private boolean rendering; public int delta; public boolean exitRequested; @@ -104,53 +94,22 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene public final Cursor cursor; public boolean drawCursor; + + private final List resolutionChangedListeners; - class Transition { - int in; - int out; - int total; - int progress = -1; - OpsuState nextstate; - Color OVERLAY = new Color(Color.black); + private int tIn; + private int tOut; + private int tProgress = -1; + private OpsuState tNextState; + private final Color tOVERLAY = new Color(Color.black); - public void update() { - if (progress == -1) { - return; - } - progress += delta; - if (progress > out && nextstate != null) { - switchStateInstantly(nextstate); - nextstate = null; - } - if (progress > total) { - progress = -1; - } - } - - public void render(Graphics graphics) { - if (progress == -1) { - return; - } - int relprogress = progress; - int reltotal = out; - if (progress > out) { - reltotal = in; - relprogress = total - progress; - } - OVERLAY.a = (float) relprogress / reltotal; - graphics.setColor(OVERLAY); - graphics.fillRect(0, 0, width, height); - } - } - - private final Transition transition = new Transition(); - - public DisplayContainer() { + public DisplayContainer() + { + this.resolutionChangedListeners = new ArrayList<>(); this.cursor = new Cursor(); drawCursor = true; - ResolutionChangedListener.EVENT.addListener(this); - SkinChangedListener.EVENT.addListener(this); + skinservice.addSkinChangedListener(this); this.nativeDisplayMode = Display.getDisplayMode(); targetBackgroundRenderInterval = 41; // ~24 fps @@ -159,10 +118,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene renderDelta = 1; } - @Override - public void onResolutionChanged(int w, int h) { - destroyImages(); - reinit(); + public void addResolutionChangedListener(ResolutionChangedListener l) + { + this.resolutionChangedListeners.add(l); } @Override @@ -186,12 +144,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene } } + backButton = new BackButton(); + // TODO clean this up GameMod.init(width, height); PlaybackSpeed.init(width, height); HitObject.init(width, height); DownloadNode.init(width, height); - UI.init(this); } public void setUPS(int ups) { @@ -205,13 +164,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene } public void init(OpsuState startingState) { - setUPS(OPTION_TARGET_UPS.val); + setUPS(targetUPS[OPTION_TARGET_UPS.val]); setFPS(targetFPS[targetFPSIndex]); - fpsState = new FpsRenderState(); - bubNotifState = new BubNotifState(); - barNotifState = new BarNotificationState(); - state = startingState; state.enter(); } @@ -228,8 +183,18 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene mouseX = input.getMouseX(); mouseY = input.getMouseY(); - transition.update(); - fpsState.update(); + // state transition + if (tProgress != -1) { + tProgress += delta; + if (tProgress > tOut && tNextState != null) { + switchStateInstantly(tNextState); + tNextState = null; + } + if (tProgress > tIn + tOut) { + tProgress = -1; + } + } + fpsDisplay.update(); state.update(); if (drawCursor) { @@ -244,6 +209,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene } if (timeSinceLastRender >= maxRenderInterval) { + rendering = true; GL.glClear(SGL.GL_COLOR_BUFFER_BIT); /* @@ -257,32 +223,44 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene state.preRenderUpdate(); state.render(graphics); - fpsState.render(graphics); - bubNotifState.render(graphics); - barNotifState.render(graphics); + fpsDisplay.render(graphics); + bubNotifs.render(graphics); + barNotifs.render(graphics); - cursor.updateAngle(renderDelta); + cursor.updateAngle(); if (drawCursor) { cursor.draw(Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON) || Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON)); } UI.drawTooltip(graphics); - transition.render(graphics); + // transition + if (tProgress != -1) { + if (tProgress > tOut) { + tOVERLAY.a = 1f - (tProgress - tOut) / (float) tIn; + } else { + tOVERLAY.a = tProgress / (float) tOut; + } + graphics.setColor(tOVERLAY); + graphics.fillRect(0, 0, width, height); + } timeSinceLastRender = 0; Display.update(false); GL11.glFlush(); + rendering = false; } Display.processMessages(); - Display.sync(targetUpdatesPerSecond); + if (targetUpdatesPerSecond >= 60) { + Display.sync(targetUpdatesPerSecond); + } } } public void setup() throws Exception { - width = height = -1; + width = height = width2 = height2 = -1; Display.setTitle("opsu!dance"); setupResolutionOptionlist(nativeDisplayMode.getWidth(), nativeDisplayMode.getHeight()); updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString()); @@ -318,6 +296,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene GameImage.destroyImages(); GameData.Grade.destroyImages(); Beatmap.destroyBackgroundImageCache(); + FrameBufferCache.shutdown(); } public void teardownAL() { @@ -342,13 +321,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene return true; } if (DownloadList.get().hasActiveDownloads()) { - BubNotifListener.EVENT.make().onBubNotif(DownloadList.EXIT_CONFIRMATION, Colors.BUB_RED); + bubNotifs.send(BUB_RED, DownloadList.EXIT_CONFIRMATION); exitRequested = false; exitconfirmation = System.currentTimeMillis(); return false; } if (updater.getStatus() == Updater.Status.UPDATE_DOWNLOADING) { - BubNotifListener.EVENT.make().onBubNotif(Updater.EXIT_CONFIRMATION, Colors.BUB_PURPLE); + bubNotifs.send(BUB_PURPLE, Updater.EXIT_CONFIRMATION); exitRequested = false; exitconfirmation = System.currentTimeMillis(); return false; @@ -372,6 +351,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene width = Integer.parseInt(res[0]); height = Integer.parseInt(res[1]); } + + updateDisplayMode(width, height); + } + + public void updateDisplayMode(int width, int height) { + int screenWidth = nativeDisplayMode.getWidth(); + int screenHeight = nativeDisplayMode.getHeight(); // check for larger-than-screen dimensions if (!OPTION_ALLOW_LARGER_RESOLUTIONS.state && (screenWidth < width || screenHeight < height)) { @@ -387,34 +373,35 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene try { setDisplayMode(width, height, OPTION_FULLSCREEN.state); } catch (Exception e) { - BubNotifListener.EVENT.make().onBubNotif("Failed to change resolution", Colors.BUB_RED); + bubNotifs.send(BUB_RED, "Failed to change display mode"); Log.error("Failed to set display mode.", e); } } - public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { - if (this.width == width && this.height == height) { - Display.setFullscreen(fullscreen); - return; - } - + public void setDisplayMode(int w, int h, boolean fullscreen) throws Exception { DisplayMode displayMode = null; if (fullscreen) { - displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height); + final int bpp = this.nativeDisplayMode.getBitsPerPixel(); + final int freq = this.nativeDisplayMode.getFrequency(); + displayMode = GLHelper.findFullscreenDisplayMode(bpp, freq, w, h); } if (displayMode == null) { - displayMode = new DisplayMode(width, height); + displayMode = new DisplayMode(w, h); if (fullscreen) { fullscreen = false; - String msg = String.format("Fullscreen mode is not supported for %sx%s", width, height); + String msg = "Fullscreen mode is not supported for %sx%s"; + msg = String.format(msg, w, h); Log.warn(msg); - BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_ORANGE); + bubNotifs.send(BUB_ORANGE, msg); } } - this.width = displayMode.getWidth(); - this.height = displayMode.getHeight(); + width = displayMode.getWidth(); + height = displayMode.getHeight(); + width2 = width / 2; + height2 = height / 2; + isWidescreen = width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3 Display.setDisplayMode(displayMode); Display.setFullscreen(fullscreen); @@ -439,7 +426,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene input = new Input(height); input.enableKeyRepeat(); input.addListener(new GlobalInputListener()); - input.addMouseListener(bubNotifState); + input.addMouseListener(bubNotifs); } input.addListener(state); @@ -448,7 +435,15 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene GameImage.init(width, height); Fonts.init(); - ResolutionChangedListener.EVENT.make().onResolutionChanged(width, height); + destroyImages(); + reinit(); + + barNotifs.onResolutionChanged(width, height); + bubNotifs.onResolutionChanged(width, height); + fpsDisplay.onResolutionChanged(width, height); + for (ResolutionChangedListener l : this.resolutionChangedListeners) { + l.onResolutionChanged(width, height); + } } public void resetCursor() { @@ -466,10 +461,6 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene return (Sys.getTime() * 1000) / Sys.getTimerResolution(); } - public boolean isWidescreen() { - return width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3 - } - @Override public void writeErrorDump(StringWriter dump) { dump.append("> DisplayContainer dump\n"); @@ -487,22 +478,32 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene } public void switchState(OpsuState state) { - switchState(state, 200, 300); + switchState(state, 150, 250); } public void switchState(OpsuState newstate, int outtime, int intime) { - if (transition.progress != -1) { + if (tProgress != -1 && tProgress <= tOut) { return; } + if (outtime == 0) { switchStateInstantly(newstate); newstate = null; + } else { + input.removeListener(this.state); } - transition.nextstate = newstate; - transition.total = transition.in = intime; - transition.out = outtime; - transition.total += outtime; - transition.progress = 0; + + if (tProgress == -1) { + tProgress = 0; + } else { + // we were in a transition (out state), so start from the time + // that was already spent transitioning in + tProgress = (int) (((1f - (tProgress - tOut) / (float) tIn)) * outtime); + } + + tNextState = newstate; + tIn = intime; + tOut = outtime; } public void switchStateInstantly(OpsuState state) { @@ -511,6 +512,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene this.state = state; this.state.enter(); input.addListener(this.state); + backButton.resetHover(); + if (this.rendering) { + // state might be changed in preRenderUpdate, + // in that case the new state will be rendered without having + // preRenderUpdate being called first, so do that now + this.state.preRenderUpdate(); + } } } diff --git a/src/yugecin/opsudance/core/GlobalInputListener.java b/src/yugecin/opsudance/core/GlobalInputListener.java index 4e57f1b2..7a15bde0 100644 --- a/src/yugecin/opsudance/core/GlobalInputListener.java +++ b/src/yugecin/opsudance/core/GlobalInputListener.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.ui.UI; import org.newdawn.slick.Input; import org.newdawn.slick.InputListener; -import yugecin.opsudance.events.BarNotifListener; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.core.InstanceContainer.*; @@ -38,8 +37,8 @@ public class GlobalInputListener implements InputListener { public boolean keyReleased(int key, char c) { if (key == KEY_F7) { OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length); - BarNotifListener.EVENT.make().onBarNotif(String.format("Frame limiter: %s", - OPTION_TARGET_FPS.getValueString())); + final String value = OPTION_TARGET_FPS.getValueString(); + barNotifs.sendf("Frame limiter: %s", value); return true; } if (key == KEY_F10) { diff --git a/src/yugecin/opsudance/core/InstanceContainer.java b/src/yugecin/opsudance/core/InstanceContainer.java index a2cb6c75..ea53fd90 100644 --- a/src/yugecin/opsudance/core/InstanceContainer.java +++ b/src/yugecin/opsudance/core/InstanceContainer.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,11 +26,17 @@ import itdelatrisu.opsu.states.*; import org.newdawn.slick.Input; import org.newdawn.slick.util.FileSystemLocation; import org.newdawn.slick.util.ResourceLoader; + +import yugecin.opsudance.core.state.specialstates.BarNotificationState; +import yugecin.opsudance.core.state.specialstates.BubNotifState; +import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.options.Configuration; +import yugecin.opsudance.options.OptionGroups; import yugecin.opsudance.options.OptionsService; import yugecin.opsudance.render.GameObjectRenderer; import yugecin.opsudance.skinning.SkinService; -import yugecin.opsudance.utils.ManifestWrapper; +import yugecin.opsudance.ui.BackButton; +import yugecin.opsudance.ui.OptionsOverlay; import java.io.File; import java.io.IOException; @@ -51,10 +57,17 @@ public class InstanceContainer { public static BeatmapParser beatmapParser; public static Updater updater; + public static BackButton backButton; public static DisplayContainer displayContainer; public static Input input; public static GameObjectRenderer gameObjectRenderer; + + public static BarNotificationState barNotifs; + public static BubNotifState bubNotifs; + public static FpsRenderState fpsDisplay; + + public static OptionsOverlay optionsOverlay; public static Splash splashState; public static MainMenu mainmenuState; @@ -64,17 +77,21 @@ public class InstanceContainer { public static Game gameState; public static GameRanking gameRankingState; public static GamePauseMenu pauseState; + + public static int width, width2, height, height2; + public static boolean isWidescreen; + public static int mouseX, mouseY; + public static int renderDelta; public static void kickstart() { updater = new Updater(); env = new Environment(); JarFile jarfile = getJarfile(); - ManifestWrapper manifest = new ManifestWrapper(getJarManifest(jarfile)); - config = new Configuration(manifest); + config = new Configuration(); if (jarfile != null) { try { - NativeLoader.loadNatives(jarfile, manifest); + NativeLoader.loadNatives(jarfile); } catch (IOException e) { String msg = String.format("Could not unpack native(s): %s", e.getMessage()); throw new RuntimeException(msg, e); @@ -94,9 +111,15 @@ public class InstanceContainer { updater = new Updater(); displayContainer = new DisplayContainer(); + + barNotifs = new BarNotificationState(); + bubNotifs = new BubNotifState(); + fpsDisplay = new FpsRenderState(); gameObjectRenderer = new GameObjectRenderer(); + optionsOverlay = new OptionsOverlay(OptionGroups.normalOptions); + splashState = new Splash(); mainmenuState = new MainMenu(); buttonState = new ButtonMenu(); diff --git a/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java index 2a3ef34f..d8627635 100644 --- a/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java +++ b/src/yugecin/opsudance/core/errorhandling/ErrorHandler.java @@ -24,6 +24,11 @@ import yugecin.opsudance.utils.MiscUtils; import javax.swing.*; import java.awt.Desktop; +import java.awt.Dialog.ModalityType; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Window; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -94,30 +99,15 @@ public class ErrorHandler { } JLabel message = new JLabel(messageText); - JTextArea textArea = new JTextArea(15, 100); - textArea.setEditable(false); - textArea.setBackground(UIManager.getColor("Panel.background")); - textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - textArea.setTabSize(2); - textArea.setLineWrap(false); - textArea.setWrapStyleWord(true); - textArea.setText(messageBody); - ActionListener reportAction = new ActionListener() { @Override public void actionPerformed(ActionEvent event) { - try { - URI url = createGithubIssueUrl(customMessage, cause, errorDump); - Desktop.getDesktop().browse(url); - } catch (IOException e) { - Log.warn("Could not open browser to report issue", e); - JOptionPane.showMessageDialog(null, "whoops could not launch a browser", - "errorception", JOptionPane.ERROR_MESSAGE); - } + Window parent = SwingUtilities.getWindowAncestor(message); + showCreateIssueDialog(parent, errorDump, customMessage, cause); } }; - Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), + Object[] messageComponents = new Object[] { message, readonlyTextarea(messageBody), createViewLogButton(), createReportButton(flags, reportAction) }; String[] buttons; @@ -181,8 +171,71 @@ public class ErrorHandler { button.setEnabled(false); return button; } + + private static void showCreateIssueDialog( + Window parent, + String errorDump, + String customMessage, + Throwable cause) + { + final String dump = createIssueDump(customMessage, cause, errorDump); - private static URI createGithubIssueUrl(String customMessage, Throwable cause, String errorDump) { + final String title = "report error"; + JDialog d = new JDialog(parent, title, ModalityType.APPLICATION_MODAL); + d.setLayout(new GridBagLayout()); + final GridBagConstraints c = new GridBagConstraints(); + c.gridx = 1; + c.gridy = 1; + c.weightx = 1d; + c.weighty = 0d; + c.fill = GridBagConstraints.BOTH; + c.insets = new Insets(4, 8, 4, 8); + + d.add(new JLabel( + "Copy the text in the box below.
" + + "Then click the button below.
" + + "Your browser should open a page where you can report the issue.
" + + "Please paste the dump below in the issue box." + ), c); + + c.gridy++; + c.weighty = 1d; + d.add(readonlyTextarea(dump), c); + c.gridy++; + c.weighty = c.weightx = 0d; + c.fill = GridBagConstraints.NONE; + JButton btn = new JButton("Report"); + btn.addActionListener(e -> { + try { + URI url = createGithubIssueUrl(customMessage, cause); + Desktop.getDesktop().browse(url); + d.dispose(); + } catch (IOException t) { + Log.warn("Could not open browser to report issue", t); + JOptionPane.showMessageDialog(null, "whoops could not launch a browser", + "errorception", JOptionPane.ERROR_MESSAGE); + } + }); + d.add(btn, c); + d.pack(); + d.setLocationRelativeTo(parent); + d.setVisible(true); + } + + private static URI createGithubIssueUrl(String customMessage, Throwable cause) { + String issueTitle = ""; + String issueBody = ""; + try { + issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + ": " + + customMessage, "UTF-8"); + issueBody = URLEncoder.encode("PASTE THE DUMP HERE", "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e); + } + return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody)); + } + + private static String createIssueDump(String customMessage, Throwable cause, String errorDump) { StringWriter dump = new StringWriter(); dump.append(customMessage).append("\n"); @@ -203,25 +256,19 @@ public class ErrorHandler { dump.append("**info dump**").append('\n'); dump.append("```\n").append(errorDump).append("\n```\n\n"); - - String issueTitle = ""; - String issueBody = ""; - try { - issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " + - customMessage, "UTF-8"); - issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8"); - } catch (UnsupportedEncodingException e) { - Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e); - } - return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody)); + return dump.toString(); } - private static String truncateGithubIssueBody(String body) { - if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) { - return body; - } - Log.warn("error dump too long to fit into github issue url, truncating"); - return body.substring(0, 1640) + "** TRUNCATED **\n```"; + private static JComponent readonlyTextarea(String contents) { + JTextArea textArea = new JTextArea(15, 100); + textArea.setEditable(false); + textArea.setBackground(UIManager.getColor("Panel.background")); + textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + textArea.setTabSize(2); + textArea.setLineWrap(false); + textArea.setWrapStyleWord(true); + textArea.setFont(new JLabel().getFont()); + textArea.setText(contents); + return new JScrollPane(textArea); } - } diff --git a/src/yugecin/opsudance/core/events/Event.java b/src/yugecin/opsudance/core/events/Event.java deleted file mode 100644 index 63160e02..00000000 --- a/src/yugecin/opsudance/core/events/Event.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.events; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.LinkedList; - -@SuppressWarnings("unchecked") -public class Event { - - private final Class type; - private final LinkedList listeners; - - public Event(Class type) { - this.type = type; - this.listeners = new LinkedList<>(); - } - - public void addListener(T listener) { - this.listeners.add(listener); - } - - public T make() { - return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - for (T listener : listeners) { - method.invoke(listener, args); - } - return null; - } - }); - } - -} diff --git a/src/yugecin/opsudance/core/inject/Binder.java b/src/yugecin/opsudance/core/inject/Binder.java deleted file mode 100644 index 16f33180..00000000 --- a/src/yugecin/opsudance/core/inject/Binder.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.inject; - -public interface Binder { - - void asEagerSingleton(); - void asLazySingleton(); - void to(Class type); -} diff --git a/src/yugecin/opsudance/core/inject/Inject.java b/src/yugecin/opsudance/core/inject/Inject.java deleted file mode 100644 index 04fc897a..00000000 --- a/src/yugecin/opsudance/core/inject/Inject.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.inject; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) public @interface Inject { -} diff --git a/src/yugecin/opsudance/core/inject/Injector.java b/src/yugecin/opsudance/core/inject/Injector.java deleted file mode 100644 index 9ddee01d..00000000 --- a/src/yugecin/opsudance/core/inject/Injector.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.inject; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.ListIterator; - -@SuppressWarnings("unchecked") -public abstract class Injector implements InstanceContainer, Binder { - - private final HashMap, Object> instances; - private final LinkedList> lazyInstances; - - private Class lastType; - - public Injector() { - instances = new HashMap<>(); - lazyInstances = new LinkedList<>(); - instances.put(InstanceContainer.class, this); - configure(); - } - - protected abstract void configure(); - - public final T provide(Class type) { - Object instance = instances.get(type); - if (instance != null) { - return (T) instance; - } - ListIterator> iter = lazyInstances.listIterator(); - while (iter.hasNext()) { - Class l = iter.next(); - if (l == type) { - iter.remove(); - instance = createInstance(type); - instances.put(type, instance); - return (T) instance; - } - } - return createInstance(type); - } - - private T createInstance(Class type) { - Constructor[] constructors = type.getDeclaredConstructors(); - if (constructors.length == 0) { - throw new RuntimeException("Cannot provide " + type.getSimpleName()); - } - Constructor constructor = constructors[0]; - Class[] parameterTypes = constructor.getParameterTypes(); - Object[] params = new Object[parameterTypes.length]; - for (int i = parameterTypes.length - 1; i >= 0; i--) { - params[i] = provide(parameterTypes[i]); - } - try { - return injectFields((T) constructor.newInstance(params), type); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - private T injectFields(T object, Class type) { - do { - for (Field f : type.getDeclaredFields()) { - if (f.getDeclaredAnnotation(Inject.class) == null) { - continue; - } - boolean accessible = f.isAccessible(); - if (!accessible) { - f.setAccessible(true); - } - try { - f.set(object, provide(f.getType())); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - if (!accessible) { - f.setAccessible(false); - } - } - type = type.getSuperclass(); - } while (type != null); - return object; - } - - @Override - public T injectFields(T obj) { - return injectFields(obj, obj.getClass()); - } - - public final Binder bind(Class type) { - lastType = type; - return this; - } - - @Override - public final void asEagerSingleton() { - instances.put(lastType, createInstance(lastType)); - } - - @Override - public final void asLazySingleton() { - lazyInstances.add(lastType); - } - - @Override - public final void to(Class type) { - instances.put(lastType, createInstance(type)); - } - -} diff --git a/src/yugecin/opsudance/core/inject/InstanceContainer.java b/src/yugecin/opsudance/core/inject/InstanceContainer.java deleted file mode 100644 index 77ba1be7..00000000 --- a/src/yugecin/opsudance/core/inject/InstanceContainer.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.inject; - -public interface InstanceContainer { - - T provide(Class type); - T injectFields(T instance); - -} diff --git a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java b/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java deleted file mode 100644 index ba100f31..00000000 --- a/src/yugecin/opsudance/core/inject/OpsuDanceInjector.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core.inject; - -import itdelatrisu.opsu.beatmap.BeatmapParser; -import itdelatrisu.opsu.beatmap.OszUnpacker; -import itdelatrisu.opsu.downloads.Updater; -import itdelatrisu.opsu.replay.ReplayImporter; -import itdelatrisu.opsu.states.*; -import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.state.specialstates.BarNotificationState; -import yugecin.opsudance.core.state.specialstates.BubNotifState; -import yugecin.opsudance.core.state.specialstates.FpsRenderState; -import yugecin.opsudance.core.errorhandling.ErrorHandler; -import yugecin.opsudance.options.Configuration; -import yugecin.opsudance.options.OptionsService; -import yugecin.opsudance.render.GameObjectRenderer; -import yugecin.opsudance.skinning.SkinService; - -public class OpsuDanceInjector extends Injector { - - protected void configure() { - bind(Configuration.class).asEagerSingleton(); - - bind(OptionsService.class).asLazySingleton(); - bind(ReplayImporter.class).asLazySingleton(); - bind(OszUnpacker.class).asLazySingleton(); - bind(BeatmapParser.class).asLazySingleton(); - bind(Updater.class).asLazySingleton(); - bind(SkinService.class).asEagerSingleton(); - - //bind(PreStartupInitializer.class).asEagerSingleton(); - bind(DisplayContainer.class).asEagerSingleton(); - - bind(ErrorHandler.class).asEagerSingleton(); - - bind(FpsRenderState.class).asEagerSingleton(); - bind(BarNotificationState.class).asEagerSingleton(); - bind(BubNotifState.class).asEagerSingleton(); - - bind(GameObjectRenderer.class).asEagerSingleton(); - - bind(Splash.class).asEagerSingleton(); - bind(MainMenu.class).asEagerSingleton(); - bind(ButtonMenu.class).asEagerSingleton(); - bind(SongMenu.class).asEagerSingleton(); - bind(DownloadsMenu.class).asEagerSingleton(); - bind(Game.class).asEagerSingleton(); - bind(GameRanking.class).asEagerSingleton(); - bind(GamePauseMenu.class).asEagerSingleton(); - } - -} diff --git a/src/yugecin/opsudance/core/state/BaseOpsuState.java b/src/yugecin/opsudance/core/state/BaseOpsuState.java index 893a4853..01ed84d2 100644 --- a/src/yugecin/opsudance/core/state/BaseOpsuState.java +++ b/src/yugecin/opsudance/core/state/BaseOpsuState.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,11 +19,15 @@ package yugecin.opsudance.core.state; import org.newdawn.slick.Graphics; import yugecin.opsudance.events.ResolutionChangedListener; +import yugecin.opsudance.events.SkinChangedListener; import java.io.StringWriter; -public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener { +import static yugecin.opsudance.core.InstanceContainer.*; +public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener, + SkinChangedListener +{ /** * state is dirty when resolution or skin changed but hasn't rendered yet */ @@ -31,7 +35,8 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListe private boolean isCurrentState; public BaseOpsuState() { - ResolutionChangedListener.EVENT.addListener(this); + displayContainer.addResolutionChangedListener(this); + skinservice.addSkinChangedListener(this); } protected void revalidate() { @@ -48,9 +53,18 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListe @Override public void render(Graphics g) { } + + @Override + public void onSkinChanged(String name) { + makeDirty(); + } @Override public void onResolutionChanged(int w, int h) { + makeDirty(); + } + + private void makeDirty() { if (isCurrentState) { revalidate(); return; diff --git a/src/yugecin/opsudance/core/state/ComplexOpsuState.java b/src/yugecin/opsudance/core/state/ComplexOpsuState.java index 888c6938..d7a70e27 100644 --- a/src/yugecin/opsudance/core/state/ComplexOpsuState.java +++ b/src/yugecin/opsudance/core/state/ComplexOpsuState.java @@ -130,7 +130,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState { public void preRenderUpdate() { super.preRenderUpdate(); for (Component component : components) { - component.updateHover(displayContainer.mouseX, displayContainer.mouseY); + component.updateHover(mouseX, mouseY); component.preRenderUpdate(); } for (OverlayOpsuState overlay : overlays) { diff --git a/src/yugecin/opsudance/core/state/OverlayOpsuState.java b/src/yugecin/opsudance/core/state/OverlayOpsuState.java index 849b4567..c81d2f79 100644 --- a/src/yugecin/opsudance/core/state/OverlayOpsuState.java +++ b/src/yugecin/opsudance/core/state/OverlayOpsuState.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,10 @@ public abstract class OverlayOpsuState implements OpsuState { public void show() { acceptInput = active = true; } + + public boolean isActive() { + return this.active; + } @Override public final void update() { diff --git a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java index dfb24d2c..0aeeb4ed 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BarNotificationState.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,17 +21,17 @@ import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.events.BarNotifListener; import yugecin.opsudance.events.ResolutionChangedListener; +import java.util.Formatter; import java.util.List; -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; -public class BarNotificationState implements BarNotifListener, ResolutionChangedListener { +public class BarNotificationState implements ResolutionChangedListener { private final int IN_TIME = 200; - private final int DISPLAY_TIME = 5700 + IN_TIME; + private final int DISPLAY_TIME = 2700 + IN_TIME; private final int OUT_TIME = 200; private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME; @@ -51,21 +51,19 @@ public class BarNotificationState implements BarNotifListener, ResolutionChanged this.bgcol = new Color(Color.black); this.textCol = new Color(Color.white); this.timeShown = TOTAL_TIME; - BarNotifListener.EVENT.addListener(this); - ResolutionChangedListener.EVENT.addListener(this); } public void render(Graphics g) { if (timeShown >= TOTAL_TIME) { return; } - timeShown += displayContainer.renderDelta; + timeShown += renderDelta; processAnimations(); g.setColor(bgcol); - g.fillRect(0, displayContainer.height / 2 - barHalfHeight, displayContainer.width, barHalfHeight * 2); + g.fillRect(0, height2 - barHalfHeight, width, barHalfHeight * 2); int y = textY; for (String line : lines) { - Fonts.LARGE.drawString((displayContainer.width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol); + Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol); y += Fonts.LARGE.getLineHeight(); } } @@ -90,14 +88,18 @@ public class BarNotificationState implements BarNotifListener, ResolutionChanged } private void calculatePosition() { - this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (displayContainer.width * 0.96f), true); + this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (width * 0.96f), true); int textHeight = (int) (Fonts.LARGE.getLineHeight() * (lines.size() + 0.5f)); - textY = (displayContainer.height - textHeight) / 2 + (int) (Fonts.LARGE.getLineHeight() / 5f); + textY = (height - textHeight) / 2 + (int) (Fonts.LARGE.getLineHeight() / 5f); barHalfTargetHeight = textHeight / 2; } + + @SuppressWarnings("resource") + public void sendf(String format, Object... args) { + this.send(new Formatter().format(format, args).toString()); + } - @Override - public void onBarNotif(String message) { + public void send(String message) { this.message = message; calculatePosition(); timeShown = 0; diff --git a/src/yugecin/opsudance/core/state/specialstates/BubNotifState.java b/src/yugecin/opsudance/core/state/specialstates/BubNotifState.java index 34cefd0f..1e427cec 100644 --- a/src/yugecin/opsudance/core/state/specialstates/BubNotifState.java +++ b/src/yugecin/opsudance/core/state/specialstates/BubNotifState.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,16 +22,16 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import org.newdawn.slick.MouseListener; -import yugecin.opsudance.events.BubNotifListener; import yugecin.opsudance.events.ResolutionChangedListener; +import java.util.Formatter; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import static yugecin.opsudance.core.InstanceContainer.*; -public class BubNotifState implements MouseListener, BubNotifListener, ResolutionChangedListener { +public class BubNotifState implements MouseListener, ResolutionChangedListener { public static final int IN_TIME = 633; public static final int DISPLAY_TIME = 7000 + IN_TIME; @@ -46,8 +46,6 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio public BubNotifState() { this.bubbles = new LinkedList<>(); this.addAnimationTime = IN_TIME; - BubNotifListener.EVENT.addListener(this); - ResolutionChangedListener.EVENT.addListener(this); } public void render(Graphics g) { @@ -55,7 +53,7 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio if (!iter.hasNext()) { return; } - addAnimationTime += displayContainer.renderDelta; + addAnimationTime += renderDelta; if (addAnimationTime > IN_TIME) { finishAddAnimation(); } @@ -65,7 +63,7 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio if (animateUp && addAnimationTime < IN_TIME) { next.y = next.baseY - (int) (addAnimationHeight * AnimationEquation.OUT_QUINT.calc((float) addAnimationTime / IN_TIME)); } - if (next.render(g, displayContainer.mouseX, displayContainer.mouseY, displayContainer.renderDelta)) { + if (next.render(g, mouseX, mouseY, renderDelta)) { iter.remove(); } animateUp = true; @@ -74,10 +72,10 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio private void calculatePositions() { // if width is 0, attempting to wrap it will result in infinite loop - Notification.width = Math.max(50, (int) (displayContainer.width * 0.1703125f)); - Notification.baseLine = (int) (displayContainer.height * 0.9645f); - Notification.paddingY = (int) (displayContainer.height * 0.0144f); - Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01); + Notification.width = Math.max(50, (int) (width * 0.1703125f)); + Notification.baseLine = (int) (height * 0.9645f); + Notification.paddingY = (int) (height * 0.0144f); + Notification.finalX = width - Notification.width - (int) (width * 0.01); Notification.fontPaddingX = (int) (Notification.width * 0.02f); Notification.fontPaddingY = (int) (Fonts.SMALLBOLD.getLineHeight() / 4f); Notification.lineHeight = Fonts.SMALLBOLD.getLineHeight(); @@ -111,8 +109,12 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio addAnimationTime = IN_TIME; } - @Override - public void onBubNotif(String message, Color borderColor) { + @SuppressWarnings("resource") + public void sendf(Color borderColor, String format, Object... args) { + this.send(borderColor, new Formatter().format(format, args).toString()); + } + + public void send(Color borderColor, String message) { finishAddAnimation(); Notification newBubble = new Notification(message, borderColor); bubbles.add(0, newBubble); diff --git a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java index 5d41a5b5..9186db35 100644 --- a/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java +++ b/src/yugecin/opsudance/core/state/specialstates/FpsRenderState.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ import yugecin.opsudance.events.ResolutionChangedListener; import yugecin.opsudance.utils.FPSMeter; import static yugecin.opsudance.options.Options.*; -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; public class FpsRenderState implements ResolutionChangedListener { @@ -42,7 +42,6 @@ public class FpsRenderState implements ResolutionChangedListener { public FpsRenderState() { fpsMeter = new FPSMeter(10); upsMeter = new FPSMeter(10); - ResolutionChangedListener.EVENT.addListener(this); } public void update() { @@ -50,7 +49,7 @@ public class FpsRenderState implements ResolutionChangedListener { } public void render(Graphics g) { - fpsMeter.update(displayContainer.renderDelta); + fpsMeter.update(renderDelta); if (!OPTION_SHOW_FPS.state) { return; } @@ -61,7 +60,7 @@ public class FpsRenderState implements ResolutionChangedListener { } private String getText(int value, String unit) { - if (OPTION_USE_FPS_DELTAS.state) { + if (OPTION_USE_FPS_DELTAS.state || value > 1000) { return String.format("%.2fms", 1000f / value); } return value + " " + unit; @@ -91,8 +90,8 @@ public class FpsRenderState implements ResolutionChangedListener { @Override public void onResolutionChanged(int w, int h) { singleHeight = Fonts.SMALL.getLineHeight(); - x = displayContainer.width - 3; - y = displayContainer.height - 3 - singleHeight - 10; + x = width - 3; + y = height - 3 - singleHeight - 10; } } diff --git a/src/yugecin/opsudance/events/BarNotifListener.java b/src/yugecin/opsudance/events/BarNotifListener.java deleted file mode 100644 index ed798e32..00000000 --- a/src/yugecin/opsudance/events/BarNotifListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.events; - -import yugecin.opsudance.core.events.Event; - -public interface BarNotifListener { - - Event EVENT = new Event<>(BarNotifListener.class); - - void onBarNotif(String message); - -} diff --git a/src/yugecin/opsudance/events/BubNotifListener.java b/src/yugecin/opsudance/events/BubNotifListener.java deleted file mode 100644 index 2990d676..00000000 --- a/src/yugecin/opsudance/events/BubNotifListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.events; - -import org.newdawn.slick.Color; -import yugecin.opsudance.core.events.Event; - -@SuppressWarnings({"UnnecessaryInterfaceModifier", "unused"}) -public interface BubNotifListener { - - Event EVENT = new Event<>(BubNotifListener.class); - - void onBubNotif(String message, Color borderColor); - -} diff --git a/src/yugecin/opsudance/events/ResolutionChangedListener.java b/src/yugecin/opsudance/events/ResolutionChangedListener.java index c0311f0d..f00c9e4f 100644 --- a/src/yugecin/opsudance/events/ResolutionChangedListener.java +++ b/src/yugecin/opsudance/events/ResolutionChangedListener.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +17,7 @@ */ package yugecin.opsudance.events; -import yugecin.opsudance.core.events.Event; - -public interface ResolutionChangedListener { - - Event EVENT = new Event<>(ResolutionChangedListener.class); - +public interface ResolutionChangedListener +{ void onResolutionChanged(int w, int h); - } diff --git a/src/yugecin/opsudance/events/SkinChangedListener.java b/src/yugecin/opsudance/events/SkinChangedListener.java index 273fa1b6..76eb7e99 100644 --- a/src/yugecin/opsudance/events/SkinChangedListener.java +++ b/src/yugecin/opsudance/events/SkinChangedListener.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +17,7 @@ */ package yugecin.opsudance.events; -import yugecin.opsudance.core.events.Event; - -public interface SkinChangedListener { - - Event EVENT = new Event<>(SkinChangedListener.class); - - void onSkinChanged(String stringName); - +public interface SkinChangedListener +{ + void onSkinChanged(String name); } diff --git a/src/yugecin/opsudance/movers/CircleMover.java b/src/yugecin/opsudance/movers/CircleMover.java index e9e3bda4..4e76c4c2 100644 --- a/src/yugecin/opsudance/movers/CircleMover.java +++ b/src/yugecin/opsudance/movers/CircleMover.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -64,7 +64,7 @@ public class CircleMover extends Mover { double a = ang + SOME_CONSTANT * t; pos[0] = (startX + (endX - startX) * t) - middlexoffset - Math.cos(a) * radius; pos[1] = (startY + (endY - startY) * t) - middleyoffset - Math.sin(a) * radius; - if (pos[0] < 0 || displayContainer.width < pos[0] || pos[1] < 0 || displayContainer.height < pos[1]) { + if (pos[0] < 0 || width < pos[0] || pos[1] < 0 || height < pos[1]) { pass = false; break; } diff --git a/src/yugecin/opsudance/movers/ExgonMover.java b/src/yugecin/opsudance/movers/ExgonMover.java index 6c7f200b..1eb3e57d 100644 --- a/src/yugecin/opsudance/movers/ExgonMover.java +++ b/src/yugecin/opsudance/movers/ExgonMover.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,8 +44,8 @@ public class ExgonMover extends Mover { pos[0] = endX; pos[1] = endY; } else { - pos[0] = randgen.nextInt(displayContainer.width); - pos[1] = randgen.nextInt(displayContainer.height); + pos[0] = randgen.nextInt(width); + pos[1] = randgen.nextInt(height); } } return pos; diff --git a/src/yugecin/opsudance/movers/factories/AutoMoverFactory.java b/src/yugecin/opsudance/movers/factories/AutoMoverFactory.java index 333a894a..b9dce873 100644 --- a/src/yugecin/opsudance/movers/factories/AutoMoverFactory.java +++ b/src/yugecin/opsudance/movers/factories/AutoMoverFactory.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -102,8 +102,8 @@ public class AutoMoverFactory implements MoverFactory { } private boolean checkBounds( double[] pos ) { - return 0 < pos[0] && pos[0] < displayContainer.width - displayContainer.width / 8 && - 0 < pos[1] && pos[1] < displayContainer.height - displayContainer.height / 8; + return 0 < pos[0] && pos[0] < width - width / 8 && + 0 < pos[1] && pos[1] < height - height / 8; } @Override diff --git a/src/yugecin/opsudance/options/Configuration.java b/src/yugecin/opsudance/options/Configuration.java index 989d99fc..c0c33a35 100644 --- a/src/yugecin/opsudance/options/Configuration.java +++ b/src/yugecin/opsudance/options/Configuration.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,13 +24,10 @@ import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.TimingPoint; -import itdelatrisu.opsu.ui.Colors; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; -import yugecin.opsudance.utils.ManifestWrapper; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -42,13 +39,12 @@ import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static yugecin.opsudance.core.errorhandling.ErrorHandler.*; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.options.Options.*; import static yugecin.opsudance.core.InstanceContainer.*; public class Configuration { - public final boolean USE_XDG; public final File CONFIG_DIR; public final File DATA_DIR; public final File CACHE_DIR; @@ -73,12 +69,10 @@ public class Configuration { public File replayImportDir; public File skinRootDir; - public Configuration(ManifestWrapper jarmanifest) { - USE_XDG = jarmanifest.valueOrDefault(null, "Use-XDG", "").equalsIgnoreCase("true"); - - CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config"); - DATA_DIR = getXDGBaseDir("XDG_DATA_HOME", ".local/share"); - CACHE_DIR = getXDGBaseDir("XDG_CACHE_HOME", ".cache"); + public Configuration() { + CONFIG_DIR = env.workingdir; + DATA_DIR = env.workingdir; + CACHE_DIR = env.workingdir; BEATMAP_DIR = new File(DATA_DIR, "Songs/"); SKIN_ROOT_DIR = new File(DATA_DIR, "Skins/"); @@ -155,7 +149,7 @@ public class Configuration { } if (!defaultDir.isDirectory() && !defaultDir.mkdir()) { String msg = String.format("Failed to create %s directory at '%s'.", kind, defaultDir.getAbsolutePath()); - BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_RED); + bubNotifs.send(BUB_RED, msg); } return defaultDir; } @@ -175,39 +169,6 @@ public class Configuration { return loadDirectory(dir, defaultDir, kind); } - /** - * Returns the directory based on the XDG base directory specification for - * Unix-like operating systems, only if the "XDG" flag is enabled. - * @param envvar the environment variable to check (XDG_*_*) - * @param fallback the fallback directory relative to ~home - * @return the XDG base directory, or the working directory if unavailable - */ - private File getXDGBaseDir(String envvar, String fallback) { - if (!USE_XDG) { - return env.workingdir; - } - - String OS = System.getProperty("os.name").toLowerCase(); - if (OS.indexOf("nix") == -1 && OS.indexOf("nux") == -1 && OS.indexOf("aix") == -1){ - return env.workingdir; - } - - String rootPath = System.getenv(envvar); - if (rootPath == null) { - String home = System.getProperty("user.home"); - if (home == null) { - return new File("./"); - } - rootPath = String.format("%s/%s", home, fallback); - } - File dir = new File(rootPath, "opsu"); - if (!dir.isDirectory() && !dir.mkdir()) { - explode(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), - new Exception("empty"), PREVENT_REPORT); - } - return dir; - } - /** * @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots */ @@ -215,9 +176,11 @@ public class Configuration { // TODO: get a decent place for this // create the screenshot directory if (!screenshotDir.isDirectory() && !screenshotDir.mkdir()) { - BubNotifListener.EVENT.make().onBubNotif( - String.format( "Failed to create screenshot directory at '%s'.", - screenshotDir.getAbsolutePath()), Colors.BUB_RED); + bubNotifs.sendf( + BUB_RED, + "Failed to create screenshot directory at '%s'.", + screenshotDir.getAbsolutePath() + ); return; } @@ -251,13 +214,13 @@ public class Configuration { } } ImageIO.write(image, OPTION_SCREENSHOT_FORMAT.getValueString().toLowerCase(), file); - BubNotifListener.EVENT.make().onBubNotif("Created " + fileName, - Colors.BUB_PURPLE); + bubNotifs.send(BUB_PURPLE, "Created " + fileName); } catch (Exception e) { Log.error("Could not take screenshot", e); - BubNotifListener.EVENT.make().onBubNotif( - "Failed to take a screenshot. See log file for details", - Colors.BUB_PURPLE); + bubNotifs.send( + BUB_PURPLE, + "Failed to take a screenshot. See log file for details" + ); } } }.start(); diff --git a/src/yugecin/opsudance/options/ListOption.java b/src/yugecin/opsudance/options/ListOption.java index 5c0c5808..52c9f1bc 100644 --- a/src/yugecin/opsudance/options/ListOption.java +++ b/src/yugecin/opsudance/options/ListOption.java @@ -19,6 +19,8 @@ package yugecin.opsudance.options; public abstract class ListOption extends Option { + public Runnable observer; + public ListOption(String name, String configurationName, String description) { super(name, configurationName, description); } @@ -30,4 +32,8 @@ public abstract class ListOption extends Option { public abstract Object[] getListItems(); public abstract void clickListItem(int index); + protected final void onChange() { + observer.run(); + } + } diff --git a/src/yugecin/opsudance/options/NumericOption.java b/src/yugecin/opsudance/options/NumericOption.java index 84a83dc2..7bf4fc71 100644 --- a/src/yugecin/opsudance/options/NumericOption.java +++ b/src/yugecin/opsudance/options/NumericOption.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,8 +18,9 @@ package yugecin.opsudance.options; import itdelatrisu.opsu.Utils; -import itdelatrisu.opsu.ui.Colors; -import yugecin.opsudance.events.BubNotifListener; + +import static itdelatrisu.opsu.ui.Colors.*; +import static yugecin.opsudance.core.InstanceContainer.*; public class NumericOption extends Option { @@ -53,8 +54,7 @@ public class NumericOption extends Option { try { val = Utils.clamp(Integer.parseInt(s), min, max); } catch (Exception ignored) { - BubNotifListener.EVENT.make().onBubNotif("Failed to parse " + configurationName + " option", - Colors.BUB_RED); + bubNotifs.send(BUB_RED, "Failed to parse '" + configurationName + "' option"); } } diff --git a/src/yugecin/opsudance/options/Option.java b/src/yugecin/opsudance/options/Option.java index 76acf4f0..7f31a142 100644 --- a/src/yugecin/opsudance/options/Option.java +++ b/src/yugecin/opsudance/options/Option.java @@ -75,6 +75,14 @@ public class Option { return false; } filtered = !name.toLowerCase().contains(searchString) && !description.toLowerCase().contains(searchString); + if (this instanceof ListOption) { + for (Object itm : ((ListOption) this).getListItems()) { + if (itm != null && itm.toString().toLowerCase().contains(searchString)) { + filtered = false; + return false; + } + } + } return filtered; } diff --git a/src/yugecin/opsudance/options/OptionGroups.java b/src/yugecin/opsudance/options/OptionGroups.java index 0df29f34..de2ff0c2 100644 --- a/src/yugecin/opsudance/options/OptionGroups.java +++ b/src/yugecin/opsudance/options/OptionGroups.java @@ -42,6 +42,7 @@ public class OptionGroups { OPTION_TARGET_FPS, OPTION_SHOW_FPS, OPTION_USE_FPS_DELTAS, + OPTION_STARFOUNTAINS, OPTION_SCREENSHOT_FORMAT, }), new OptionTab("SLIDER OPTIONS", new Option[]{ @@ -181,7 +182,8 @@ public class OptionGroups { }; public static final OptionTab[] storyboardOptions = new OptionTab[] { - new OptionTab("Gameplay", new Option[] { + new OptionTab("Gameplay", GameImage.MENU_NAV_GAMEPLAY), + new OptionTab("GENERAL", new Option[] { OPTION_BACKGROUND_DIM, OPTION_DANCE_REMOVE_BG, OPTION_SNAKING_SLIDERS, @@ -192,12 +194,14 @@ public class OptionGroups { OPTION_SHOW_PERFECT_HIT, OPTION_SHOW_FOLLOW_POINTS, }), - new OptionTab("Input", new Option[] { + new OptionTab("Input", GameImage.MENU_NAV_INPUT), + new OptionTab("INPUT", new Option[] { OPTION_CURSOR_SIZE, OPTION_NEW_CURSOR, OPTION_DISABLE_CURSOR }), - new OptionTab("Dance", new Option[] { + new OptionTab("Dance", GameImage.MENU_NAV_DANCE), + new OptionTab("MOVER", new Option[]{ OPTION_DANCE_MOVER, OPTION_DANCE_EXGON_DELAY, OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS, @@ -206,36 +210,56 @@ public class OptionGroups { OPTION_DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR, OPTION_DANCE_MOVER_DIRECTION, OPTION_DANCE_SLIDER_MOVER_TYPE, + }), + new OptionTab("SPINNER", new Option[]{ OPTION_DANCE_SPINNER, OPTION_DANCE_SPINNER_DELAY, + }), + new OptionTab("SLIDER OPTIONS", new Option[]{ OPTION_DANCE_LAZY_SLIDERS, - OPTION_DANCE_CIRCLE_STREAMS, - OPTION_DANCE_ONLY_CIRCLE_STACKS, OPTION_DANCE_CIRLCE_IN_SLOW_SLIDERS, OPTION_DANCE_CIRLCE_IN_LAZY_SLIDERS, + }), + new OptionTab("CIRCLE MOVEMENTS", new Option[]{ + OPTION_DANCE_CIRCLE_STREAMS, + OPTION_DANCE_ONLY_CIRCLE_STACKS, + }), + new OptionTab("MIRROR", new Option[] { OPTION_DANCE_MIRROR, }), - new OptionTab("Dance display", new Option[] { + new OptionTab("Advanced Display", GameImage.MENU_NAV_ADVANCED), + new OptionTab("OBJECTS", new Option[]{ OPTION_DANCE_DRAW_APPROACH, OPTION_DANCE_OBJECT_COLOR_OVERRIDE, OPTION_DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED, OPTION_DANCE_RGB_OBJECT_INC, + OPTION_DANCE_HIDE_OBJECTS, + }), + new OptionTab("CURSOR", new Option[]{ OPTION_DANCE_CURSOR_COLOR_OVERRIDE, OPTION_DANCE_CURSOR_MIRROR_COLOR_OVERRIDE, OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL, OPTION_DANCE_RGB_CURSOR_INC, OPTION_DANCE_CURSOR_TRAIL_OVERRIDE, - OPTION_DANCE_HIDE_OBJECTS, - OPTION_DANCE_HIDE_UI, }), - new OptionTab ("Pippi", new Option[] { + new OptionTab("MISC", new Option[] { + OPTION_DANCE_HIDE_UI, + OPTION_DANCE_REMOVE_BG, + OPTION_DANCE_ENABLE_SB, + }), + new OptionTab ("Pippi", GameImage.MENU_NAV_PIPPI), + new OptionTab ("GENERAL", new Option[]{ OPTION_PIPPI_ENABLE, OPTION_PIPPI_RADIUS_PERCENT, + }), + new OptionTab ("ANGLE MULTIPLIERS", new Option[]{ OPTION_PIPPI_ANGLE_INC_MUL, OPTION_PIPPI_ANGLE_INC_MUL_SLIDER, + }), + new OptionTab ("MISC", new Option[] { OPTION_PIPPI_SLIDER_FOLLOW_EXPAND, OPTION_PIPPI_PREVENT_WOBBLY_STREAMS, - }) + }), }; } diff --git a/src/yugecin/opsudance/options/Options.java b/src/yugecin/opsudance/options/Options.java index 17f3cf92..3806c671 100644 --- a/src/yugecin/opsudance/options/Options.java +++ b/src/yugecin/opsudance/options/Options.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,6 @@ import org.newdawn.slick.SlickException; import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.util.Log; import yugecin.opsudance.*; -import yugecin.opsudance.events.BarNotifListener; import yugecin.opsudance.movers.factories.ExgonMoverFactory; import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory; import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController; @@ -128,6 +127,8 @@ public class Options { public static final ToggleOption OPTION_NOSINGLEINSTANCE = new ToggleOption("-", "NoSingleInstance", "-", false); + public static final ToggleOption OPTION_STARFOUNTAINS = new ToggleOption("Star fountains in main menu", "StarFountains", "Show star bursts in main menu", true); + // in-game options public static final ListOption OPTION_SCREEN_RESOLUTION = new ListOption("Screen Resolution", "ScreenResolution", "Change the size of the game.") { private final String[] resolutions = { @@ -167,6 +168,7 @@ public class Options { public void clickListItem(int index){ idx = index; displayContainer.updateDisplayMode(resolutions[idx]); + this.onChange(); } @Override @@ -185,7 +187,14 @@ public class Options { }; public static final ToggleOption OPTION_ALLOW_LARGER_RESOLUTIONS = new ToggleOption("Allow large resolutions", "AllowLargeRes", "Allow resolutions larger than the native resolution", false); - public static final ToggleOption OPTION_FULLSCREEN = new ToggleOption("Fullscreen Mode", "Fullscreen", "Restart to apply changes.", false); + public static final ToggleOption OPTION_FULLSCREEN = new ToggleOption("Fullscreen Mode", "Fullscreen", "Fullscreen mode", false) { + @Override + public void toggle() + { + super.toggle(); + displayContainer.updateDisplayMode(width, height); + } + }; public static final ListOption OPTION_SKIN = new ListOption("Skin", "Skin", "Change how the game looks.") { @Override @@ -202,6 +211,7 @@ public class Options { public void clickListItem(int index){ skinservice.usedSkinName = skinservice.availableSkinDirectories[index]; skinservice.reloadSkin(); + this.onChange(); } @Override @@ -215,16 +225,34 @@ public class Options { } }; - public static final NumericOption OPTION_TARGET_UPS = new NumericOption("target UPS", "targetUPS", "Higher values result in less input lag and smoother cursor trail, but may cause high CPU usage.", 480, 20, 1000) { + public static final int[] targetUPS = { 60, 120, 240, 480, 960, 1000, -1 }; + + public static final NumericOption OPTION_TARGET_UPS = new NumericOption("target UPS", "targetUPS", "Higher values result in less input lag and smoother cursor trail, but may cause high CPU usage.", 2, 0, targetUPS.length - 1) { @Override public String getValueString () { - return String.format("%dups", val); + if (targetUPS[val] == -1) { + return "unlimited"; + } + return String.valueOf(targetUPS[val]); } @Override - public void setValue ( int value){ + public void setValue(int value) { + if (value < 0 || targetUPS.length <= value) { + return; + } + final int ups = targetUPS[value]; + final int fps = targetFPS[targetFPSIndex]; super.setValue(value); - displayContainer.setUPS(value); + displayContainer.setUPS(ups); + if (ups != -1 && fps > ups) { + for (int i = targetFPSIndex - 1; i >= 0; i--) { + if (targetFPS[i] >= ups) { + OPTION_TARGET_FPS.clickListItem(i); + break; + } + } + } } }; @@ -256,7 +284,17 @@ public class Options { @Override public void clickListItem(int index){ targetFPSIndex = index; - displayContainer.setFPS(targetFPS[targetFPSIndex]); + int fps = targetFPS[targetFPSIndex]; + displayContainer.setFPS(fps); + if (targetUPS[OPTION_TARGET_UPS.val] < fps) { + for (int i = 0; i < targetUPS.length; i++) { + if (targetUPS[i] >= fps) { + OPTION_TARGET_UPS.setValue(i); + break; + } + } + } + this.onChange(); } @Override @@ -318,6 +356,7 @@ public class Options { @Override public void clickListItem(int index){ this.index = index; + this.onChange(); } @Override @@ -386,7 +425,7 @@ public class Options { public static final NumericOption OPTION_EFFECT_VOLUME = new NumericOption("Effects", "VolumeEffect", "Volume of menu and game sounds.", 70, 0, 100); public static final NumericOption OPTION_HITSOUND_VOLUME = new NumericOption("Hit Sounds", "VolumeHitSound", "Volume of hit sounds.", 30, 0, 100); - public static final NumericOption OPTION_MUSIC_OFFSET = new NumericOption("Music Offset", "Offset", "Adjust this value if hit objects are out of sync.", -75, -500, 500) { + public static final NumericOption OPTION_MUSIC_OFFSET = new NumericOption("Global Music Offset", "Offset", "Adjust this value if hit objects are out of sync.", -75, -500, 500) { @Override public String getValueString () { return String.format("%dms", val); @@ -439,7 +478,7 @@ public class Options { public static final ToggleOption OPTION_DISABLE_MOUSE_BUTTONS = new ToggleOption("Disable mouse buttons in play mode", "MouseDisableButtons", "This option will disable all mouse buttons. Specifically for people who use their keyboard to click.", false) { @Override public void toggle() { - BarNotifListener.EVENT.make().onBarNotif(state ? + barNotifs.send(state ? "Mouse buttons are disabled." : "Mouse buttons are enabled."); } }; @@ -608,10 +647,11 @@ public class Options { public void clickListItem(int index){ if (Game.isInGame && Dancer.moverFactories[index] instanceof PolyMoverFactory) { // TODO remove this when #79 is fixed - BarNotifListener.EVENT.make().onBarNotif("This mover is disabled in the storyboard right now"); + barNotifs.send("This mover is disabled in the storyboard right now"); return; } Dancer.instance.setMoverFactoryIndex(index); + this.onChange(); } @Override @@ -701,6 +741,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.moverDirection = MoverDirection.values()[index]; + this.onChange(); } @Override @@ -731,6 +772,7 @@ public class Options { public void clickListItem(int index){ val = index; Dancer.sliderMoverController = Dancer.sliderMovers[index]; + this.onChange(); } @Override @@ -753,6 +795,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.instance.setSpinnerIndex(index); + this.onChange(); } @Override @@ -797,6 +840,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.colorOverride = ObjectColorOverrides.values()[index]; + this.onChange(); } @Override @@ -824,6 +868,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.colorMirrorOverride = ObjectColorOverrides.values()[index]; + this.onChange(); } @Override @@ -858,6 +903,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.cursorColorOverride = CursorColorOverrides.values()[index]; + this.onChange(); } @Override @@ -885,6 +931,7 @@ public class Options { @Override public void clickListItem(int index){ Dancer.cursorColorMirrorOverride = CursorColorOverrides.values()[index]; + this.onChange(); } @Override diff --git a/src/yugecin/opsudance/options/OptionsService.java b/src/yugecin/opsudance/options/OptionsService.java index b4fe3ee9..d0d972a0 100644 --- a/src/yugecin/opsudance/options/OptionsService.java +++ b/src/yugecin/opsudance/options/OptionsService.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,15 +17,14 @@ */ package yugecin.opsudance.options; -import itdelatrisu.opsu.ui.Colors; import org.newdawn.slick.util.Log; -import yugecin.opsudance.events.BubNotifListener; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; +import static itdelatrisu.opsu.ui.Colors.*; import static yugecin.opsudance.core.InstanceContainer.*; /** @@ -80,7 +79,7 @@ public class OptionsService { } catch (IOException e) { String err = String.format("Failed to read option file '%s'.", config.OPTIONS_FILE.getAbsolutePath()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } config.loadDirectories(); } @@ -109,7 +108,7 @@ public class OptionsService { } catch (IOException e) { String err = String.format("Failed to write to file '%s'.", config.OPTIONS_FILE.getAbsolutePath()); Log.error(err, e); - BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED); + bubNotifs.send(BUB_RED, err); } } diff --git a/src/yugecin/opsudance/render/GameObjectRenderer.java b/src/yugecin/opsudance/render/GameObjectRenderer.java index 04edeaa1..d07361bd 100644 --- a/src/yugecin/opsudance/render/GameObjectRenderer.java +++ b/src/yugecin/opsudance/render/GameObjectRenderer.java @@ -28,6 +28,7 @@ import org.newdawn.slick.Color; import org.newdawn.slick.Image; import yugecin.opsudance.skinning.SkinService; +import static itdelatrisu.opsu.GameImage.*; import static yugecin.opsudance.options.Options.*; public class GameObjectRenderer { @@ -88,7 +89,8 @@ public class GameObjectRenderer { public void renderComboNumberOnly(float x, float y, int number, float alpha) { if (number > 0) { - gameData.drawSymbolNumber(number, x, y, GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / gameData.getDefaultSymbolImage(0).getHeight(), alpha); + float scale = HITCIRCLE.getWidth() * 0.40f / gameData.getDefaultSymbolImage(0).getHeight(); + gameData.drawSymbolNumber(number, x, y, scale, alpha); } } diff --git a/src/yugecin/opsudance/sbv2/MoveStoryboard.java b/src/yugecin/opsudance/sbv2/MoveStoryboard.java index d7fd084e..73093277 100644 --- a/src/yugecin/opsudance/sbv2/MoveStoryboard.java +++ b/src/yugecin/opsudance/sbv2/MoveStoryboard.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,7 @@ import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OverlayOpsuState; -import yugecin.opsudance.events.BarNotifListener; import yugecin.opsudance.sbv2.movers.CubicStoryboardMover; import yugecin.opsudance.sbv2.movers.LinearStoryboardMover; import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover; @@ -35,9 +33,9 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -public class MoveStoryboard extends OverlayOpsuState{ +import static yugecin.opsudance.core.InstanceContainer.*; - private final DisplayContainer displayContainer; +public class MoveStoryboard extends OverlayOpsuState{ private SimpleButton btnAddLinear; private SimpleButton btnAddQuadratic; @@ -56,8 +54,7 @@ public class MoveStoryboard extends OverlayOpsuState{ private int trackPosition; - public MoveStoryboard(DisplayContainer displayContainer) { - this.displayContainer = displayContainer; + public MoveStoryboard() { dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class[]{StoryboardMove.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -70,12 +67,12 @@ public class MoveStoryboard extends OverlayOpsuState{ public void revalidate() { super.revalidate(); - btnAddLinear = new SimpleButton(displayContainer.width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAddQuadratic = new SimpleButton(displayContainer.width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAddCubic = new SimpleButton(displayContainer.width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); - btnAnimLin = new SimpleButton(displayContainer.width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange); - btnAnimMid = new SimpleButton(displayContainer.width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange); - btnAnimCub = new SimpleButton(displayContainer.width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange); + btnAddLinear = new SimpleButton(width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAddQuadratic = new SimpleButton(width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAddCubic = new SimpleButton(width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON); + btnAnimLin = new SimpleButton(width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange); + btnAnimMid = new SimpleButton(width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange); + btnAnimCub = new SimpleButton(width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange); } /** @@ -97,8 +94,8 @@ public class MoveStoryboard extends OverlayOpsuState{ @Override protected void onPreRenderUpdate() { - int x = displayContainer.mouseX; - int y = displayContainer.mouseY; + int x = mouseX; + int y = mouseY; btnAddLinear.update(x, y); btnAddQuadratic.update(x, y); btnAddCubic.update(x, y); @@ -106,7 +103,7 @@ public class MoveStoryboard extends OverlayOpsuState{ btnAnimMid.update(x, y); btnAnimCub.update(x, y); if (moves[objectIndex] != null) { - moves[objectIndex].update(displayContainer.renderDelta, x, y); + moves[objectIndex].update(renderDelta, x, y); } } @@ -185,11 +182,11 @@ public class MoveStoryboard extends OverlayOpsuState{ private StoryboardMove getCurrentMoveOrCreateNew() { if (gameObjects[objectIndex].isSlider() && trackPosition > gameObjects[objectIndex].getTime() && trackPosition < gameObjects[objectIndex].getEndTime()) { - BarNotifListener.EVENT.make().onBarNotif("Wait until the slider ended"); + barNotifs.send("Wait until the slider ended"); return dummyMove; } if (moves[objectIndex] == null) { - return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, displayContainer.width); + return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, width); } return moves[objectIndex]; } diff --git a/src/yugecin/opsudance/skinning/SkinService.java b/src/yugecin/opsudance/skinning/SkinService.java index 814f736d..a2541e35 100644 --- a/src/yugecin/opsudance/skinning/SkinService.java +++ b/src/yugecin/opsudance/skinning/SkinService.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,21 +26,39 @@ import org.newdawn.slick.util.ResourceLoader; import yugecin.opsudance.events.SkinChangedListener; import java.io.File; +import java.util.ArrayList; +import java.util.List; import static yugecin.opsudance.core.InstanceContainer.*; + /** * @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java */ -public class SkinService { +public class SkinService +{ + private final List skinChangedListeners; public String[] availableSkinDirectories; public String usedSkinName = "Default"; public static Skin skin; + + public SkinService() + { + this.skinChangedListeners = new ArrayList<>(); + } + + public void addSkinChangedListener(SkinChangedListener l) + { + this.skinChangedListeners.add(l); + } - public void reloadSkin() { + public void reloadSkin() + { loadSkin(); SoundController.init(); - SkinChangedListener.EVENT.make().onSkinChanged(usedSkinName); + for (SkinChangedListener l : this.skinChangedListeners) { + l.onSkinChanged(this.usedSkinName); + } } /** diff --git a/src/yugecin/opsudance/spinners/ApproachCircleSpinner.java b/src/yugecin/opsudance/spinners/ApproachCircleSpinner.java index f25e5d4b..b4a46d5a 100644 --- a/src/yugecin/opsudance/spinners/ApproachCircleSpinner.java +++ b/src/yugecin/opsudance/spinners/ApproachCircleSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,10 +38,10 @@ public class ApproachCircleSpinner extends Spinner { ang += 15; } - double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS); + double rad = width / 4.0f * (1d - Spinner.PROGRESS); - point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI); - point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI); + point[0] = width2 + rad * Math.sin(ang / 180d * Math.PI); + point[1] = height2 - rad * Math.cos(ang / 180d * Math.PI); return point; } diff --git a/src/yugecin/opsudance/spinners/BeamSpinner.java b/src/yugecin/opsudance/spinners/BeamSpinner.java index d0908bae..bbf8bb29 100644 --- a/src/yugecin/opsudance/spinners/BeamSpinner.java +++ b/src/yugecin/opsudance/spinners/BeamSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,30 +41,30 @@ public class BeamSpinner extends Spinner { index = ++index % 4; final int MOD = 60; - point[0] = displayContainer.width / 2d; - point[1] = displayContainer.height / 2d; + point[0] = width2; + point[1] = height2; - if( index == 0 ) { - add( MOD, 90 ); - add( MOD, 180 ); - } else if( index == 1 ) { - add( MOD, 90 ); - add( displayContainer.height / 2 * 0.8d, 0 ); - } else if( index == 2 ) { - add( MOD, -90 ); - add( displayContainer.height / 2 * 0.8d, 0 ); - } else if( index == 3 ) { - add( MOD, -90 ); - add( MOD, 180 ); + if (index == 0) { + add(MOD, 90); + add(MOD, 180); + } else if (index == 1) { + add(MOD, 90); + add(height2 * 0.8d, 0); + } else if (index == 2) { + add(MOD, -90); + add(height2 * 0.8d, 0); + } else if (index == 3) { + add(MOD, -90); + add(MOD, 180); ang += 0.3; } return point; } - private void add( double rad, double ang ) { - point[0] += rad * Math.cos( (this.ang + ang) / 180d * Math.PI); - point[1] -= rad * Math.sin( (this.ang + ang) / 180d * Math.PI); + private void add (double rad, double ang ) { + point[0] += rad * Math.cos((this.ang + ang) / 180d * Math.PI); + point[1] -= rad * Math.sin((this.ang + ang) / 180d * Math.PI); } @Override diff --git a/src/yugecin/opsudance/spinners/CircleSpinner.java b/src/yugecin/opsudance/spinners/CircleSpinner.java index b014d406..ba519d63 100644 --- a/src/yugecin/opsudance/spinners/CircleSpinner.java +++ b/src/yugecin/opsudance/spinners/CircleSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,10 +36,10 @@ public class CircleSpinner extends Spinner { ang += 15; } - double rad = displayContainer.width / 4.0f; + double rad = width / 4.0f; - point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI); - point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI); + point[0] = width2 + rad * Math.sin(ang / 180d * Math.PI); + point[1] = height2 - rad * Math.cos(ang / 180d * Math.PI); return point; } diff --git a/src/yugecin/opsudance/spinners/CubeSpinner.java b/src/yugecin/opsudance/spinners/CubeSpinner.java index 622cbf0f..baa3b1b3 100644 --- a/src/yugecin/opsudance/spinners/CubeSpinner.java +++ b/src/yugecin/opsudance/spinners/CubeSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ */ package yugecin.opsudance.spinners; -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; public class CubeSpinner extends Spinner { @@ -90,10 +90,10 @@ public class CubeSpinner extends Spinner { x *= 3.0d / ( z + 3.0d + 5.0d + 0.5 ); y *= 3.0d / ( z + 3.0d + 5.0d + 0.5 ); - double scale = displayContainer.width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI)); + double scale = width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI)); //double scale = Options.width / (3.0f + -1f * ((float)(Options.s.ElapsedMilliseconds % Options.beatTimeMs)/(float)Options.beatTimeMs)); - point[0] = (int) ( displayContainer.width / 2.0f + scale * x ); - point[1] = (int) ( displayContainer.height / 2.0f - scale * y ); + point[0] = (int) (width2 + scale * x ); + point[1] = (int) (height2 - scale * y ); return point; } diff --git a/src/yugecin/opsudance/spinners/DonutSpinner.java b/src/yugecin/opsudance/spinners/DonutSpinner.java index 2d65fec2..1bb7e490 100644 --- a/src/yugecin/opsudance/spinners/DonutSpinner.java +++ b/src/yugecin/opsudance/spinners/DonutSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,10 +38,10 @@ public class DonutSpinner extends Spinner { ang += 15; } - double rad = displayContainer.width / 4.0f; + double rad = width / 4.0f; - point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang); - point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang); + point[0] = width2 + rad * Math.sin(ang); + point[1] = height2 - rad * Math.cos(ang); return point; } diff --git a/src/yugecin/opsudance/spinners/FivePointStarApproachSpinner.java b/src/yugecin/opsudance/spinners/FivePointStarApproachSpinner.java index b1f55d48..f3da8ea6 100644 --- a/src/yugecin/opsudance/spinners/FivePointStarApproachSpinner.java +++ b/src/yugecin/opsudance/spinners/FivePointStarApproachSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,12 +39,12 @@ public class FivePointStarApproachSpinner extends Spinner { odd = !odd; } - double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS); + double rad = width / 4.0f * (1d - Spinner.PROGRESS); if (!odd) { rad /= 3d; } - point[0] = displayContainer.width / 2d + Math.cos(ang) * rad; - point[1] = displayContainer.height / 2d + Math.sin(ang) * rad; + point[0] = width2 + Math.cos(ang) * rad; + point[1] = height2 + Math.sin(ang) * rad; return point; } diff --git a/src/yugecin/opsudance/spinners/FivePointStarSpinner.java b/src/yugecin/opsudance/spinners/FivePointStarSpinner.java index d9eb8377..4e40eb3d 100644 --- a/src/yugecin/opsudance/spinners/FivePointStarSpinner.java +++ b/src/yugecin/opsudance/spinners/FivePointStarSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,11 +24,9 @@ public class FivePointStarSpinner extends Spinner { @Override public void init() { double[][] points = new double[10][]; - double midx = displayContainer.width / 2d; - double midy = displayContainer.height / 2d; double angleIncRads = Math.PI * 36d / 180d; double ang = -Math.PI / 2d; - double maxrad = displayContainer.width / 4d; + double maxrad = width / 4d; double minrad = maxrad / 3d; for (int i = 0; i < 10; i++) { double rad = maxrad; @@ -36,8 +34,8 @@ public class FivePointStarSpinner extends Spinner { rad = minrad; } points[i] = new double[] { - midx + Math.cos(ang) * rad, - midy + Math.sin(ang) * rad + width2 + Math.cos(ang) * rad, + height2 + Math.sin(ang) * rad }; ang += angleIncRads; } diff --git a/src/yugecin/opsudance/spinners/HalfCircleSpinner.java b/src/yugecin/opsudance/spinners/HalfCircleSpinner.java index 18d08597..429e6bdb 100644 --- a/src/yugecin/opsudance/spinners/HalfCircleSpinner.java +++ b/src/yugecin/opsudance/spinners/HalfCircleSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,7 @@ */ package yugecin.opsudance.spinners; -import yugecin.opsudance.options.Options; - -import static yugecin.opsudance.core.InstanceContainer.displayContainer; +import static yugecin.opsudance.core.InstanceContainer.*; public class HalfCircleSpinner extends Spinner { @@ -47,8 +45,8 @@ public class HalfCircleSpinner extends Spinner { skipang += 359; } - point[0] = displayContainer.width / 2.0d + displayContainer.height / 2 * 0.8d * Math.cos(ang/180d*Math.PI); - point[1] = displayContainer.height / 2.0d + displayContainer.height / 2 * 0.8d * Math.sin(ang/180d*Math.PI); + point[0] = width2 + height2 * 0.8d * Math.cos(ang/180d*Math.PI); + point[1] = height2 + height2 * 0.8d * Math.sin(ang/180d*Math.PI); return point; } diff --git a/src/yugecin/opsudance/spinners/IlluminatiSpinner.java b/src/yugecin/opsudance/spinners/IlluminatiSpinner.java index e4c946bd..cb9201bd 100644 --- a/src/yugecin/opsudance/spinners/IlluminatiSpinner.java +++ b/src/yugecin/opsudance/spinners/IlluminatiSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,9 +24,9 @@ public class IlluminatiSpinner extends Spinner { @Override public void init() { init( new double[][] { - new double[] { displayContainer.width / 2d - 120, displayContainer.height / 2d + 80 }, - new double[] { displayContainer.width / 2d, displayContainer.height / 2d - 160 }, - new double[] { displayContainer.width / 2d + 120, displayContainer.height / 2d + 80 } + new double[] { width2 - 120, height2 + 80 }, + new double[] { width2, height2 - 160 }, + new double[] { width2 + 120, height2 + 80 } } ); } diff --git a/src/yugecin/opsudance/spinners/LessThanThreeSpinner.java b/src/yugecin/opsudance/spinners/LessThanThreeSpinner.java index 4f099583..8c27d5b2 100644 --- a/src/yugecin/opsudance/spinners/LessThanThreeSpinner.java +++ b/src/yugecin/opsudance/spinners/LessThanThreeSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ */ package yugecin.opsudance.spinners; +import static java.lang.Math.*; import static yugecin.opsudance.core.InstanceContainer.*; public class LessThanThreeSpinner extends Spinner { @@ -24,9 +25,7 @@ public class LessThanThreeSpinner extends Spinner { private int angle = 0; @Override - public void init() - { - + public void init() { } @Override @@ -37,15 +36,12 @@ public class LessThanThreeSpinner extends Spinner { } if( angle > 360 ) angle = 0; double theta = angle / 180d * Math.PI; - double[] pos = new double[] { - displayContainer.width / 2d, - displayContainer.height / 2d - }; + double[] pos = { width2, height2 }; - double r = 2 - 2 * Math.sin( theta ) + Math.sin( theta ) * Math.sqrt( Math.abs( Math.cos( theta ) ) ) / ( Math.sin( theta ) + 1.4 ); + double r = 2 - 2 * sin(theta) + sin(theta) * sqrt(abs(cos(theta))) / (sin(theta) + 1.4); - pos[0] += Math.cos( theta ) * r * 100; - pos[1] -= Math.sin( theta ) * r * 100 + 100; + pos[0] += Math.cos(theta) * r * 100; + pos[1] -= Math.sin(theta) * r * 100 + 100; return pos; } diff --git a/src/yugecin/opsudance/spinners/RektCircleSpinner.java b/src/yugecin/opsudance/spinners/RektCircleSpinner.java index c8c2341a..fcd83616 100644 --- a/src/yugecin/opsudance/spinners/RektCircleSpinner.java +++ b/src/yugecin/opsudance/spinners/RektCircleSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public class RektCircleSpinner extends Spinner { @Override public void init() { index = 0; - size = displayContainer.height * 0.8d; + size = height * 0.8d; point = new double[2]; } @@ -42,20 +42,20 @@ public class RektCircleSpinner extends Spinner { final int INC = 50; if( index == 0 ) { - point[0] = displayContainer.width / 2d + size / 2d - pos; - point[1] = displayContainer.height / 2d - size / 2d; + point[0] = width2 + size / 2d - pos; + point[1] = height2 - size / 2d; index++; } else if( index == 1 ) { - point[0] = displayContainer.width / 2 - size / 2; - point[1] = displayContainer.height / 2 - size / 2 + pos; + point[0] = width2 - size / 2; + point[1] = height2 - size / 2 + pos; index++; } else if( index == 2 ) { - point[0] = displayContainer.width / 2 - size / 2 + pos; - point[1] = displayContainer.height / 2 + size / 2; + point[0] = width2 - size / 2 + pos; + point[1] = height2 + size / 2; index++; } else if( index == 3 ) { - point[0] = displayContainer.width / 2 + size / 2; - point[1] = displayContainer.height / 2 + size / 2 - pos; + point[0] = width2 + size / 2; + point[1] = height2 + size / 2 - pos; pos += INC; if( pos > size ) { pos = INC; diff --git a/src/yugecin/opsudance/spinners/RektSpinner.java b/src/yugecin/opsudance/spinners/RektSpinner.java index d1f75ca0..7cb6e616 100644 --- a/src/yugecin/opsudance/spinners/RektSpinner.java +++ b/src/yugecin/opsudance/spinners/RektSpinner.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,11 +25,11 @@ public class RektSpinner extends Spinner { public void init() { init(new double[][] { { 10, 10 }, - { displayContainer.width / 2d, 10 }, - { displayContainer.width - 10, 10 }, - { displayContainer.width - 10, displayContainer.height - 10 }, - { displayContainer.width / 2d, displayContainer.height - 10 }, - { 10, displayContainer.height - 10 } + { width2, 10 }, + { width - 10, 10 }, + { width - 10, height - 10 }, + { width2, height - 10 }, + { 10, height - 10 } }); } diff --git a/src/yugecin/opsudance/ui/BackButton.java b/src/yugecin/opsudance/ui/BackButton.java index c9a28ef1..fdb36fa2 100644 --- a/src/yugecin/opsudance/ui/BackButton.java +++ b/src/yugecin/opsudance/ui/BackButton.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin + * Copyright (C) 2017-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,8 @@ import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.*; -import yugecin.opsudance.core.DisplayContainer; + +import static yugecin.opsudance.core.InstanceContainer.*; public class BackButton { @@ -80,7 +81,7 @@ public class BackButton { /** The real button with, determined by the size and animations. */ private int realButtonWidth; - public BackButton(DisplayContainer container) { + public BackButton() { if (!GameImage.MENU_BACK.hasGameSkinImage()) { backButton = null; textWidth = Fonts.MEDIUM.getWidth("back"); @@ -90,7 +91,7 @@ public class BackButton { paddingY *= 0.736f; paddingX = paddingY / 2f; chevronBaseSize = paddingY * 3f / 2f; - buttonYpos = (int) (container.height - paddingY * 4f); + buttonYpos = height - (int) (paddingY * 4f); slopeImageSize = (int) (paddingY * 3f); slopeImageSlopeWidth = (int) (slopeImageSize * 0.295f); firstButtonWidth = slopeImageSize; @@ -101,10 +102,10 @@ public class BackButton { if (GameImage.MENU_BACK.getImages() != null) { Animation back = GameImage.MENU_BACK.getAnimation(120); - backButton = new MenuButton(back, back.getWidth() / 2f, container.height - (back.getHeight() / 2f)); + backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f)); } else { Image back = GameImage.MENU_BACK.getImage(); - backButton = new MenuButton(back, back.getWidth() / 2f, container.height - (back.getHeight() / 2f)); + backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f)); } backButton.setHoverAnimationDuration(350); backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); @@ -171,11 +172,11 @@ public class BackButton { /** * Processes a hover action depending on whether or not the cursor * is hovering over the button. - * @param delta the delta interval - * @param cx the x coordinate - * @param cy the y coordinate */ - public void hoverUpdate(int delta, int cx, int cy) { + public void hoverUpdate() { + final int delta = renderDelta; + final int cx = mouseX; + final int cy = mouseY; if (backButton != null) { backButton.hoverUpdate(delta, cx, cy); return; diff --git a/src/yugecin/opsudance/ui/ImagePosition.java b/src/yugecin/opsudance/ui/ImagePosition.java new file mode 100644 index 00000000..a50d4e8c --- /dev/null +++ b/src/yugecin/opsudance/ui/ImagePosition.java @@ -0,0 +1,62 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2018 yugecin + * + * opsu!dance 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!dance 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!dance. If not, see . + */ +package yugecin.opsudance.ui; + +import java.awt.geom.Rectangle2D; + +import org.newdawn.slick.Color; +import org.newdawn.slick.Image; + +public class ImagePosition extends Rectangle2D.Float { + + private Image image; + + public ImagePosition(Image image) { + this.image = image; + } + + public boolean contains(int x, int y, float alphaThreshold) { + if (!super.contains(x, y)) { + return false; + } + final int ix = x - (int) this.x; + final int iy = y - (int) this.y; + return this.image.getAlphaAt(ix, iy) > alphaThreshold; + } + + public float middleX() { + return this.x + this.width / 2; + } + + public float middleY() { + return this.y + this.height / 2; + } + + public void scale(float scale) { + final float width = this.width * scale; + final float height = this.height * scale; + this.x -= (width - this.width) / 2f; + this.y -= (height - this.height) / 2f; + this.width = width; + this.height = height; + } + + public void draw(Color filter) { + this.image.draw(this.x, this.y, this.width, this.height, filter); + } +} diff --git a/src/yugecin/opsudance/ui/OptionsOverlay.java b/src/yugecin/opsudance/ui/OptionsOverlay.java index 331191ac..3ce0c5d3 100644 --- a/src/yugecin/opsudance/ui/OptionsOverlay.java +++ b/src/yugecin/opsudance/ui/OptionsOverlay.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,8 +26,10 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.lwjgl.input.Keyboard; import org.newdawn.slick.*; import org.newdawn.slick.gui.TextField; -import yugecin.opsudance.core.DisplayContainer; -import yugecin.opsudance.core.state.OverlayOpsuState; + +import yugecin.opsudance.core.Constants; +import yugecin.opsudance.events.ResolutionChangedListener; +import yugecin.opsudance.events.SkinChangedListener; import yugecin.opsudance.options.*; import yugecin.opsudance.utils.FontUtil; @@ -35,11 +37,11 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Random; +import static itdelatrisu.opsu.GameImage.*; +import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.options.Options.*; -public class OptionsOverlay extends OverlayOpsuState { - - private final DisplayContainer displayContainer; +public class OptionsOverlay implements ResolutionChangedListener, SkinChangedListener { private static final float BG_ALPHA = 0.7f; private static final float LINEALPHA = 0.8f; @@ -60,6 +62,9 @@ public class OptionsOverlay extends OverlayOpsuState { private static final float INDICATOR_ALPHA = 0.8f; private static final Color COL_INDICATOR = new Color(Color.black); + private boolean active; + private boolean acceptInput; + private boolean dirty; /** Duration, in ms, of the show (slide-in) animation. */ private static final int SHOWANIMATIONTIME = 1000; @@ -102,6 +107,7 @@ public class OptionsOverlay extends OverlayOpsuState { private int sliderOptionStartX; private int sliderOptionLength; private boolean isAdjustingSlider; + private int unchangedSliderValue; private final HashMap> dropdownMenus; private final LinkedList> visibleDropdownMenus; @@ -110,8 +116,7 @@ public class OptionsOverlay extends OverlayOpsuState { private int openDropdownVirtualY; private int targetWidth; - private int width; - private int height; + private int currentWidth; private int navButtonSize; private int navStartY; @@ -141,8 +146,12 @@ public class OptionsOverlay extends OverlayOpsuState { private final KineticScrolling scrollHandler; private int maxScrollOffset; + private int lastOptionHeight; private int mousePressY; + + private boolean isDraggingFromOutside; + private boolean wasPressed; private boolean keyEntryLeft; private boolean keyEntryRight; @@ -161,10 +170,9 @@ public class OptionsOverlay extends OverlayOpsuState { private int invalidSearchAnimationProgress; private final int INVALID_SEARCH_ANIMATION_TIME = 500; - public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] sections) { - this.displayContainer = displayContainer; - + public OptionsOverlay(OptionTab[] sections) { this.sections = sections; + this.dirty = true; dropdownMenus = new HashMap<>(); visibleDropdownMenus = new LinkedList<>(); @@ -174,29 +182,53 @@ public class OptionsOverlay extends OverlayOpsuState { scrollHandler = new KineticScrolling(); scrollHandler.setAllowOverScroll(true); + + displayContainer.addResolutionChangedListener(this); + skinservice.addSkinChangedListener(this); + } + + @Override + public void onResolutionChanged(int w, int h) { + this.dirty = true; + if (this.active) { + this.revalidate(); + } + } + + @Override + public void onSkinChanged(String name) { + this.dirty = true; + if (this.active) { + this.revalidate(); + } + } + + public boolean isActive() { + return this.active; + } + + public boolean containsMouse() { + return this.active && mouseX <= this.currentWidth; } public void setListener(Listener listener) { this.listener = listener; } - @Override public void revalidate() { - super.revalidate(); + this.dirty = false; - boolean isWidescreen = displayContainer.isWidescreen(); - targetWidth = (int) (displayContainer.width * (isWidescreen ? 0.4f : 0.5f)); - height = displayContainer.height; + targetWidth = (int) (width * (isWidescreen ? 0.4f : 0.5f)); // calculate positions float navIconWidthRatio = isWidescreen ? 0.046875f : 0.065f; // non-widescreen ratio is not accurate - navButtonSize = (int) (displayContainer.width * navIconWidthRatio); + navButtonSize = (int) (width * navIconWidthRatio); navIndicatorSize = navButtonSize / 10; navTargetWidth = (int) (targetWidth * 0.45f) - navButtonSize; - paddingRight = (int) (displayContainer.width * 0.009375f); // not so accurate - paddingLeft = navButtonSize + (int) (displayContainer.width * 0.0180f); // not so accurate - paddingTextLeft = paddingLeft + LINEWIDTH + (int) (displayContainer.width * 0.00625f); // not so accurate + paddingRight = (int) (width * 0.009375f); // not so accurate + paddingLeft = navButtonSize + (int) (width * 0.0180f); // not so accurate + paddingTextLeft = paddingLeft + LINEWIDTH + (int) (width * 0.00625f); // not so accurate optionStartX = paddingTextLeft; textOptionsY = Fonts.LARGE.getLineHeight() * 2; textChangeY = textOptionsY + Fonts.LARGE.getLineHeight(); @@ -206,8 +238,8 @@ public class OptionsOverlay extends OverlayOpsuState { sectionLineHeight = (int) (Fonts.LARGE.getLineHeight() * 1.5f); if (active) { - width = targetWidth; - optionWidth = width - optionStartX - paddingRight; + currentWidth = targetWidth; + optionWidth = currentWidth - optionStartX - paddingRight; } optionHeight = (int) (Fonts.MEDIUM.getLineHeight() * 1.3f); @@ -215,9 +247,10 @@ public class OptionsOverlay extends OverlayOpsuState { controlImageSize = (int) (Fonts.MEDIUM.getLineHeight() * 0.7f); controlImagePadding = (optionHeight - controlImageSize) / 2; - sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(controlImageSize, controlImageSize); - checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(controlImageSize, controlImageSize); - checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(controlImageSize, controlImageSize); + final int s = controlImageSize; + sliderBallImg = CONTROL_SLIDER_BALL.getScaledImage(s, s); + checkOnImg = CONTROL_CHECK_ON.getScaledImage(s, s); + checkOffImg = CONTROL_CHECK_OFF.getScaledImage(s, s); int navTotalHeight = 0; dropdownMenus.clear(); @@ -232,23 +265,27 @@ public class OptionsOverlay extends OverlayOpsuState { } final ListOption listOption = (ListOption) option; Object[] items = listOption.getListItems(); - DropdownMenu menu = new DropdownMenu(displayContainer, items, 0, 0, 0) { + DropdownMenu menu = new DropdownMenu(items, 0, 0, 0) { @Override public void itemSelected(int index, Object item) { listOption.clickListItem(index); openDropdownMenu = null; } }; - // not the best way to determine the selected option AT ALL, but seems like it's the only one right now... - String selectedValue = option.getValueString(); - int idx = 0; - for (Object item : items) { - if (item.toString().equals(selectedValue)) { - break; + final Runnable observer = () -> { + // not the best way to determine the selected option AT ALL, but seems like it's the only one right now... + String selectedValue = option.getValueString(); + int idx = 0; + for (Object item : items) { + if (item.toString().equals(selectedValue)) { + break; + } + idx++; } - idx++; - } - menu.setSelectedIndex(idx); + menu.setSelectedIndex(idx); + }; + observer.run(); + listOption.observer = observer; menu.setBackgroundColor(COL_BG); menu.setBorderColor(Color.transparent); menu.setChevronDownColor(COL_WHITE); @@ -265,13 +302,16 @@ public class OptionsOverlay extends OverlayOpsuState { searchImg = GameImage.SEARCH.getImage().getScaledCopy(searchImgSize, searchImgSize); } - @Override - public void onRender(Graphics g) { - g.setClip(navButtonSize, 0, width - navButtonSize, height); + public void render(Graphics g) { + if (!this.active && this.currentWidth == this.navButtonSize) { + return; + } + + g.setClip(navButtonSize, 0, currentWidth - navButtonSize, height); // bg g.setColor(COL_BG); - g.fillRect(navButtonSize, 0, width, height); + g.fillRect(navButtonSize, 0, currentWidth, height); // title renderTitle(); @@ -291,16 +331,15 @@ public class OptionsOverlay extends OverlayOpsuState { // scrollbar g.setColor(COL_WHITE); - g.fillRect(width - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45); + g.fillRect(currentWidth - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45); g.clearClip(); renderNavigation(g); - // UI - UI.getBackButton().draw(g); - // tooltip - renderTooltip(g); + if (this.active) { + renderTooltip(g); + } // key input options if (keyEntryLeft || keyEntryRight) { @@ -314,7 +353,7 @@ public class OptionsOverlay extends OverlayOpsuState { navWidth += navTargetWidth; } else if (navHoverTime > 300) { AnimationEquation anim = AnimationEquation.IN_EXPO; - if (displayContainer.mouseX < navWidth) { + if (mouseX < navWidth) { anim = AnimationEquation.OUT_EXPO; } float progress = anim.calc((navHoverTime - 300f) / 300f); @@ -363,7 +402,7 @@ public class OptionsOverlay extends OverlayOpsuState { g.setColor(COL_INDICATOR); int indicatorPos = this.indicatorPos; if (indicatorMoveAnimationTime > 0) { - indicatorMoveAnimationTime += displayContainer.renderDelta; + indicatorMoveAnimationTime += renderDelta; if (indicatorMoveAnimationTime > INDICATORMOVEANIMATIONTIME) { indicatorMoveAnimationTime = 0; indicatorPos += indicatorOffsetToNextPos; @@ -373,16 +412,16 @@ public class OptionsOverlay extends OverlayOpsuState { indicatorPos += AnimationEquation.OUT_BACK.calc((float) indicatorMoveAnimationTime / INDICATORMOVEANIMATIONTIME) * indicatorOffsetToNextPos; } } - g.fillRect(navButtonSize, indicatorPos - scrollHandler.getPosition(), width, optionHeight); + g.fillRect(navButtonSize, indicatorPos - scrollHandler.getPosition(), currentWidth, optionHeight); } private void renderKeyEntry(Graphics g) { g.setColor(COL_BG); - g.fillRect(0, 0, displayContainer.width, height); + g.fillRect(0, 0, width, height); g.setColor(COL_WHITE); String prompt = (keyEntryLeft) ? "Please press the new left-click key." : "Please press the new right-click key."; - int y = (displayContainer.height - Fonts.LARGE.getLineHeight()) / 2; - FontUtil.drawCentered(Fonts.LARGE, displayContainer.width, 0, y, prompt, COL_WHITE); + int y = height2 - Fonts.LARGE.getLineHeight() / 2; + FontUtil.drawCentered(Fonts.LARGE, width, 0, y, prompt, COL_WHITE); } private void renderTooltip(Graphics g) { @@ -391,7 +430,7 @@ public class OptionsOverlay extends OverlayOpsuState { if (hoverOption instanceof NumericOption) { tip = "(" + hoverOption.getValueString() + ") " + tip; } - UI.updateTooltip(displayContainer.renderDelta, tip, true); + UI.updateTooltip(renderDelta, tip, true); UI.drawTooltip(g); } } @@ -413,7 +452,7 @@ public class OptionsOverlay extends OverlayOpsuState { if (section != activeSection) { COL_CYAN.a *= 0.2f; } - FontUtil.drawRightAligned(Fonts.XLARGE, width, -paddingRight, + FontUtil.drawRightAligned(Fonts.XLARGE, currentWidth, -paddingRight, (int) (y + Fonts.XLARGE.getLineHeight() * 0.3f), section.name.toUpperCase(), COL_CYAN); COL_CYAN.a = previousAlpha; @@ -451,6 +490,7 @@ public class OptionsOverlay extends OverlayOpsuState { g.setColor(COL_GREY); g.fillRect(paddingLeft, lineStartY, LINEWIDTH, lineHeight); } + lastOptionHeight = maxScrollOffset; // iterate over skipped options to correctly calculate max scroll offset for (; sectionIndex < sections.length; sectionIndex++) { if (sections[sectionIndex].filtered) { @@ -569,11 +609,22 @@ public class OptionsOverlay extends OverlayOpsuState { } private void renderTitle() { - int textWidth = width - navButtonSize; + int textWidth = currentWidth - navButtonSize; FontUtil.drawCentered(Fonts.LARGE, textWidth, navButtonSize, textOptionsY - scrollHandler.getIntPosition(), "Options", COL_WHITE); FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize, textChangeY - scrollHandler.getIntPosition(), "Change the way opsu! behaves", COL_PINK); + + int y = lastOptionHeight - scrollHandler.getIntPosition(); + y += Fonts.LARGE.getLineHeight() * 2.5f; + FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize, + y, Constants.PROJECT_NAME + " " + updater.getCurrentVersion(), COL_WHITE); + y += Fonts.MEDIUM.getLineHeight() * 1.2f; + FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize, + y, Constants.DANCE_REPOSITORY_URI.toString(), COL_WHITE); + y += Fonts.MEDIUM.getLineHeight() * 1.2f; + FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize, + y, Constants.REPOSITORY_URI.toString(), COL_WHITE); } private void renderSearch(Graphics g) { @@ -581,7 +632,7 @@ public class OptionsOverlay extends OverlayOpsuState { if (scrollHandler.getIntPosition() > posSearchY) { ypos = textSearchYOffset; g.setColor(COL_BG); - g.fillRect(navButtonSize, 0, width, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight()); + g.fillRect(navButtonSize, 0, currentWidth, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight()); } Color searchCol = COL_WHITE; float invalidProgress = 0f; @@ -597,7 +648,7 @@ public class OptionsOverlay extends OverlayOpsuState { if (lastSearchText.length() > 0) { searchText = lastSearchText; } - int textWidth = width - navButtonSize; + int textWidth = currentWidth - navButtonSize; if (invalidSearchAnimationProgress > 0) { g.rotate(navButtonSize + textWidth / 2, ypos, invalidProgress * invalidSearchTextRotation); } @@ -611,33 +662,37 @@ public class OptionsOverlay extends OverlayOpsuState { g.resetTransform(); } - @Override public void hide() { + if (!this.active) { + return; + } + acceptInput = active = false; searchField.setFocused(false); - acceptInput = false; - SoundController.playSound(SoundEffect.MENUBACK); hideAnimationTime = animationtime; hideAnimationStartProgress = (float) animationtime / SHOWANIMATIONTIME; + hoverOption = null; } - @Override public void show() { navHoverTime = 0; indicatorPos = -optionHeight; indicatorOffsetToNextPos = 0; indicatorMoveAnimationTime = 0; indicatorHideAnimationTime = 0; - acceptInput = true; - active = true; + acceptInput = active = true; animationtime = 0; resetSearch(); + if (this.dirty) { + this.revalidate(); + } } - @Override - public void onPreRenderUpdate() { - int mouseX = displayContainer.mouseX; - int mouseY = displayContainer.mouseY; - int delta = displayContainer.renderDelta; + public void preRenderUpdate() { + if (!this.active && this.currentWidth == this.navButtonSize) { + return; + } + + int delta = renderDelta; int prevscrollpos = scrollHandler.getIntPosition(); scrollHandler.update(delta); @@ -684,7 +739,6 @@ public class OptionsOverlay extends OverlayOpsuState { prevMouseY = mouseY; updateHoverOption(mouseX, mouseY); updateIndicatorAlpha(); - UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); if (isAdjustingSlider) { int sliderValue = ((NumericOption) hoverOption).val; updateSliderOption(); @@ -716,7 +770,7 @@ public class OptionsOverlay extends OverlayOpsuState { private void updateIndicatorAlpha() { if (hoverOption == null) { if (indicatorHideAnimationTime < INDICATORHIDEANIMATIONTIME) { - indicatorHideAnimationTime += displayContainer.renderDelta; + indicatorHideAnimationTime += renderDelta; if (indicatorHideAnimationTime > INDICATORHIDEANIMATIONTIME) { indicatorHideAnimationTime = INDICATORHIDEANIMATIONTIME; } @@ -725,7 +779,7 @@ public class OptionsOverlay extends OverlayOpsuState { COL_INDICATOR.a = (1f - progress) * INDICATOR_ALPHA * showHideProgress; } } else if (indicatorHideAnimationTime > 0) { - indicatorHideAnimationTime -= displayContainer.renderDelta * 3; + indicatorHideAnimationTime -= renderDelta * 3; if (indicatorHideAnimationTime < 0) { indicatorHideAnimationTime = 0; } @@ -737,11 +791,11 @@ public class OptionsOverlay extends OverlayOpsuState { private void updateShowHideAnimation(int delta) { if (acceptInput && animationtime >= SHOWANIMATIONTIME) { // animation already finished - width = targetWidth; + currentWidth = targetWidth; showHideProgress = 1f; return; } - optionWidth = width - optionStartX - paddingRight; + optionWidth = currentWidth - optionStartX - paddingRight; // navigation elemenst fade out with a different animation float navProgress; @@ -763,7 +817,7 @@ public class OptionsOverlay extends OverlayOpsuState { navProgress = hideAnimationStartProgress * AnimationEquation.IN_CIRC.calc(showHideProgress); showHideProgress = hideAnimationStartProgress * AnimationEquation.IN_EXPO.calc(showHideProgress); } - width = navButtonSize + (int) (showHideProgress * (targetWidth - navButtonSize)); + currentWidth = navButtonSize + (int) (showHideProgress * (targetWidth - navButtonSize)); COL_NAV_FILTERED.a = COL_NAV_INACTIVE.a = COL_NAV_FILTERED_HOVERED.a = COL_NAV_INDICATOR.a = COL_NAV_WHITE.a = COL_NAV_BG.a = navProgress; COL_BG.a = BG_ALPHA * showHideProgress; @@ -775,14 +829,23 @@ public class OptionsOverlay extends OverlayOpsuState { COL_COMBOBOX_HOVER.a = showHideProgress; } - @Override - public boolean onMousePressed(int button, int x, int y) { + public boolean mousePressed(int button, int x, int y) { + if (!this.active) { + return false; + } + if (x > this.currentWidth) { + this.isDraggingFromOutside = true; + return false; + } + + wasPressed = true; + if (keyEntryLeft || keyEntryRight) { keyEntryLeft = keyEntryRight = false; return true; } - if (x > width) { + if (x > currentWidth) { return false; } @@ -794,6 +857,7 @@ public class OptionsOverlay extends OverlayOpsuState { if (hoverOption != null && hoverOption instanceof NumericOption) { isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength; if (isAdjustingSlider) { + unchangedSliderValue = ((NumericOption) hoverOption).val; updateSliderOption(); } } @@ -801,15 +865,33 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } - @Override - public boolean onMouseReleased(int button, int x, int y) { - selectedOption = null; - if (isAdjustingSlider && listener != null) { - listener.onSaveOption(hoverOption); + public boolean mouseReleased(int button, int x, int y) { + this.isDraggingFromOutside = false; + if (!this.active || (!wasPressed && x > this.currentWidth)) { + return false; + } + + wasPressed = false; + + selectedOption = null; + if (isAdjustingSlider) { + if (listener != null) { + listener.onSaveOption(hoverOption); + } + updateHoverOption(x, y); + isAdjustingSlider = false; } - isAdjustingSlider = false; sliderOptionLength = 0; + if (backButton.contains(x, y)){ + SoundController.playSound(SoundEffect.MENUBACK); + hide(); + if (listener != null) { + listener.onLeaveOptionsMenu(); + } + return true; + } + if (x > navWidth) { if (openDropdownMenu != null) { openDropdownMenu.mouseReleased(button); @@ -873,18 +955,14 @@ public class OptionsOverlay extends OverlayOpsuState { sectionPosition = Utils.clamp(sectionPosition, (int) scrollHandler.min, (int) scrollHandler.max); scrollHandler.scrollToPosition(sectionPosition); } - - if (UI.getBackButton().contains(x, y)){ - hide(); - if (listener != null) { - listener.onLeaveOptionsMenu(); - } - } return true; } - @Override - public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) { + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + if (!this.active || this.isDraggingFromOutside) { + return false; + } + if (!isAdjustingSlider) { int diff = newy - oldy; if (diff != 0) { @@ -894,16 +972,22 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } - @Override - public boolean onMouseWheelMoved(int delta) { + public boolean mouseWheelMoved(int delta) { + if (!this.active || mouseX > this.currentWidth) { + return false; + } + if (!isAdjustingSlider) { scrollHandler.scrollOffset(-delta); } return true; } - @Override - public boolean onKeyPressed(int key, char c) { + public boolean keyPressed(int key, char c) { + if (!this.active) { + return false; + } + if (keyEntryRight) { if (Utils.isValidGameKey(key)) { OPTION_KEY_RIGHT.intval = key; @@ -921,6 +1005,9 @@ public class OptionsOverlay extends OverlayOpsuState { } if (key == Keyboard.KEY_ESCAPE) { + if (isAdjustingSlider) { + cancelAdjustingSlider(); + } if (openDropdownMenu != null) { openDropdownMenu.keyPressed(key, c); return true; @@ -930,6 +1017,7 @@ public class OptionsOverlay extends OverlayOpsuState { updateHoverOption(prevMouseX, prevMouseY); return true; } + SoundController.playSound(SoundEffect.MENUBACK); hide(); if (listener != null) { listener.onLeaveOptionsMenu(); @@ -961,14 +1049,20 @@ public class OptionsOverlay extends OverlayOpsuState { return true; } - @Override - public boolean onKeyReleased(int key, char c) { + public boolean keyReleased(int key, char c) { return false; } + private void cancelAdjustingSlider() { + if (isAdjustingSlider) { + isAdjustingSlider = false; + ((NumericOption) hoverOption).setValue(unchangedSliderValue); + } + } + private void updateSliderOption() { NumericOption o = (NumericOption) hoverOption; - int value = o.min + Math.round((float) (o.max - o.min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength)); + int value = o.min + Math.round((float) (o.max - o.min) * (mouseX - sliderOptionStartX) / (sliderOptionLength)); o.setValue(Utils.clamp(value, o.min, o.max)); } @@ -1000,6 +1094,7 @@ public class OptionsOverlay extends OverlayOpsuState { private void updateHoverOption(int mouseX, int mouseY) { if (mouseX < navWidth) { + cancelAdjustingSlider(); hoverOption = null; return; } @@ -1011,7 +1106,7 @@ public class OptionsOverlay extends OverlayOpsuState { return; } hoverOption = null; - if (mouseX > width) { + if (mouseX > currentWidth) { return; } @@ -1118,5 +1213,4 @@ public class OptionsOverlay extends OverlayOpsuState { void onLeaveOptionsMenu(); void onSaveOption(Option option); } - } diff --git a/src/yugecin/opsudance/ui/StoryboardOverlay.java b/src/yugecin/opsudance/ui/StoryboardOverlay.java index a9f540ce..a62f79ec 100644 --- a/src/yugecin/opsudance/ui/StoryboardOverlay.java +++ b/src/yugecin/opsudance/ui/StoryboardOverlay.java @@ -1,6 +1,6 @@ /* * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin + * Copyright (C) 2016-2018 yugecin * * opsu!dance is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ import itdelatrisu.opsu.ui.Fonts; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; import yugecin.opsudance.ObjectColorOverrides; -import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.state.OverlayOpsuState; import yugecin.opsudance.options.OptionTab; import yugecin.opsudance.sbv2.MoveStoryboard; @@ -35,14 +34,12 @@ import java.util.*; import static org.lwjgl.input.Keyboard.*; import static yugecin.opsudance.options.Options.*; +import static yugecin.opsudance.core.InstanceContainer.*; -@SuppressWarnings("unchecked") public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener { private final static List