From 0c1b86de624d0e26c3ed8c99c88de9a8a71b83f8 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Sat, 24 Jan 2015 22:23:49 -0500 Subject: [PATCH] Cursor updates. - Supported loading beatmap-skinned cursors. - Moved cursor loading into GameImage. - Removed unnecessary scalings/rotations when drawing. --- src/itdelatrisu/opsu/GameImage.java | 52 ++++--- src/itdelatrisu/opsu/Opsu.java | 1 + src/itdelatrisu/opsu/Options.java | 6 +- src/itdelatrisu/opsu/Utils.java | 141 +++++++----------- .../opsu/states/GamePauseMenu.java | 2 + src/itdelatrisu/opsu/states/GameRanking.java | 3 + src/itdelatrisu/opsu/states/SongMenu.java | 6 +- 7 files changed, 102 insertions(+), 109 deletions(-) diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index c05f801b..cbde33e2 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -29,6 +29,38 @@ import org.newdawn.slick.SlickException; * Game images. */ public enum GameImage { + // Cursor + CURSOR ("cursor", "png") { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(1 + ((h - 600) / 1000f)); + } + }, + CURSOR_MIDDLE ("cursormiddle", "png") { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(1 + ((h - 600) / 1000f)); + } + }, + CURSOR_TRAIL ("cursortrail", "png") { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(1 + ((h - 600) / 1000f)); + } + }, + CURSOR_OLD ("cursor2", "png", false, false) { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(1 + ((h - 600) / 1000f)); + } + }, + CURSOR_TRAIL_OLD ("cursortrail2", "png", false, false) { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy(1 + ((h - 600) / 1000f)); + } + }, + // Game SECTION_PASS ("section-pass", "png"), SECTION_FAIL ("section-fail", "png"), @@ -438,9 +470,6 @@ public enum GameImage { /** Container dimensions. */ private static int containerWidth, containerHeight; - /** Whether a skin image has been loaded. */ - private static boolean skinImageLoaded = false; - /** * Initializes the GameImage class with container dimensions. * @param width the container width @@ -462,19 +491,6 @@ public enum GameImage { } } - /** - * Destroys all skin images, if any have been loaded. - */ - public static void destroySkinImages() { - if (skinImageLoaded) { - for (GameImage img : GameImage.values()) { - if (img.isSkinnable()) - img.destroySkinImage(); - } - skinImageLoaded = false; - } - } - /** * Returns the bitmask image type from a type string. * @param type the type string @@ -698,7 +714,6 @@ public enum GameImage { if (!list.isEmpty()) { this.skinImages = list.toArray(new Image[list.size()]); process(); - skinImageLoaded = true; return true; } } @@ -716,7 +731,6 @@ public enum GameImage { // image successfully loaded this.skinImage = img; process(); - skinImageLoaded = true; return true; } catch (SlickException | RuntimeException e) { errorFile = file.getAbsolutePath(); @@ -744,7 +758,7 @@ public enum GameImage { /** * Destroys the associated skin image(s), if any. */ - private void destroySkinImage() { + public void destroySkinImage() { if (skinImage == null && skinImages == null) return; try { diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index da7710dd..cfa0c6d8 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -176,6 +176,7 @@ public class Opsu extends StateBasedGame { MusicController.resume(); } else songMenu.resetTrackOnLoad(); + Utils.resetCursor(); this.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); return false; } diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 18a03452..2793a0d3 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -213,11 +213,7 @@ public class Options { @Override public void click(GameContainer container) { newCursor = !newCursor; - try { - Utils.loadCursor(); - } catch (SlickException e) { - ErrorHandler.error("Failed to load cursor.", e, true); - } + Utils.resetCursor(); } }, DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.") { diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 1b7208e4..d50b51b3 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -87,12 +87,12 @@ public class Utils { /** Back button (shared by other states). */ private static MenuButton backButton; - /** Cursor image and trail. */ - private static Image cursor, cursorTrail, cursorMiddle; - /** Last cursor coordinates. */ private static int lastX = -1, lastY = -1; + /** Cursor rotation angle. */ + private static float cursorAngle = 0f; + /** Stores all previous cursor locations to display a trail. */ private static LinkedList cursorX = new LinkedList(), @@ -148,7 +148,6 @@ public class Utils { } catch (LWJGLException e) { ErrorHandler.error("Failed to set the cursor.", e, true); } - loadCursor(); // create fonts float fontBase; @@ -292,84 +291,46 @@ public class Utils { return val; } - /** - * Loads the cursor images. - * @throws SlickException - */ - public static void loadCursor() throws SlickException { - // destroy old cursors, if they exist - if (cursor != null && !cursor.isDestroyed()) - cursor.destroy(); - if (cursorTrail != null && !cursorTrail.isDestroyed()) - cursorTrail.destroy(); - if (cursorMiddle != null && !cursorMiddle.isDestroyed()) - cursorMiddle.destroy(); - cursor = cursorTrail = cursorMiddle = null; - - // TODO: cleanup - boolean skinCursor = new File(Options.getSkinDir(), "cursor.png").isFile(); - if (Options.isNewCursorEnabled()) { - // load new cursor type - // if skin cursor exists but middle part does not, don't load default middle - if (skinCursor && !new File(Options.getSkinDir(), "cursormiddle.png").isFile()) - ; - else { - cursorMiddle = new Image("cursormiddle.png"); - cursor = new Image("cursor.png"); - cursorTrail = new Image("cursortrail.png"); - } - } - if (cursorMiddle == null) { - // load old cursor type - // default is stored as *2.png, but load skin cursor if it exists - if (skinCursor) - cursor = new Image("cursor.png"); - else - cursor = new Image("cursor2.png"); - if (new File(Options.getSkinDir(), "cursortrail.png").isFile()) - cursorTrail = new Image("cursortrail.png"); - else - cursorTrail = new Image("cursortrail2.png"); - } - - // scale the cursor - float scale = 1 + ((container.getHeight() - 600) / 1000f); - cursor = cursor.getScaledCopy(scale); - cursorTrail = cursorTrail.getScaledCopy(scale); - if (cursorMiddle != null) - cursorMiddle = cursorMiddle.getScaledCopy(scale); - } - /** * Draws the cursor. */ public static void drawCursor() { - // TODO: use an image buffer - - int x = input.getMouseX(); - int y = input.getMouseY(); + // determine correct cursor image + // TODO: most beatmaps don't skin CURSOR_MIDDLE, so how to determine style? + Image cursor = null, cursorMiddle = null, cursorTrail = null; + boolean skinned = GameImage.CURSOR.hasSkinImage(); + boolean newStyle = (skinned) ? true : Options.isNewCursorEnabled(); + if (skinned || newStyle) { + cursor = GameImage.CURSOR.getImage(); + cursorTrail = GameImage.CURSOR_TRAIL.getImage(); + } else { + cursor = GameImage.CURSOR_OLD.getImage(); + cursorTrail = GameImage.CURSOR_TRAIL_OLD.getImage(); + } + if (newStyle) + cursorMiddle = GameImage.CURSOR_MIDDLE.getImage(); + int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int removeCount = 0; int FPSmod = (Options.getTargetFPS() / 60); - // if middle exists, add all points between cursor movements - if (cursorMiddle != null) { + // TODO: use an image buffer + if (newStyle) { + // new style: add all points between cursor movements if (lastX < 0) { - lastX = x; - lastY = y; + lastX = mouseX; + lastY = mouseY; return; } - addCursorPoints(lastX, lastY, x, y); - lastX = x; - lastY = y; + addCursorPoints(lastX, lastY, mouseX, mouseY); + lastX = mouseX; + lastY = mouseY; removeCount = (cursorX.size() / (6 * FPSmod)) + 1; - } - - // else, sample one point at a time - else { - cursorX.add(x); - cursorY.add(y); + } else { + // old style: sample one point at a time + cursorX.add(mouseX); + cursorY.add(mouseY); int max = 10 * FPSmod; if (cursorX.size() > max) @@ -395,24 +356,24 @@ public class Utils { // if (cx != x || cy != y) cursorTrail.drawCentered(cx, cy); } - cursorTrail.drawCentered(x, y); + cursorTrail.drawCentered(mouseX, mouseY); // increase the cursor size if pressed + final float scale = 1.25f; int state = game.getCurrentStateID(); - float scale = 1f; if (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && isGameKeyPressed()) || - (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))) - scale = 1.25f; + (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))) { + cursor = cursor.getScaledCopy(scale); + if (newStyle) + cursorMiddle = cursorMiddle.getScaledCopy(scale); + } // draw the other components - Image cursorScaled = cursor.getScaledCopy(scale); - cursorScaled.setRotation(cursor.getRotation()); - cursorScaled.drawCentered(x, y); - if (cursorMiddle != null) { - Image cursorMiddleScaled = cursorMiddle.getScaledCopy(scale); - cursorMiddleScaled.setRotation(cursorMiddle.getRotation()); - cursorMiddleScaled.drawCentered(x, y); - } + if (newStyle) + cursor.setRotation(cursorAngle); + cursor.drawCentered(mouseX, mouseY); + if (newStyle) + cursorMiddle.drawCentered(mouseX, mouseY); } /** @@ -472,10 +433,22 @@ public class Utils { * @param delta the delta interval since the last call */ public static void updateCursor(int delta) { - if (cursorMiddle == null) - return; + cursorAngle += delta / 40f; + cursorAngle %= 360; + } - cursor.rotate(delta / 40f); + /** + * Resets all cursor data and skins. + */ + public static void resetCursor() { + GameImage.CURSOR.destroySkinImage(); + GameImage.CURSOR_MIDDLE.destroySkinImage(); + GameImage.CURSOR_TRAIL.destroySkinImage(); + cursorAngle = 0f; + lastX = lastY = -1; + cursorX.clear(); + cursorY.clear(); + GameImage.CURSOR.getImage().setRotation(0f); } /** diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index da9e034e..c3c398d7 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -135,6 +135,7 @@ public class GamePauseMenu extends BasicGameState { SoundController.playSound(SoundEffect.MENUBACK); ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); MusicController.playAt(MusicController.getOsuFile().previewTime, true); + Utils.resetCursor(); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); } else { SoundController.playSound(SoundEffect.MENUBACK); @@ -181,6 +182,7 @@ public class GamePauseMenu extends BasicGameState { MusicController.playAt(MusicController.getOsuFile().previewTime, true); else MusicController.resume(); + Utils.resetCursor(); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); } } diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 5c3c0087..d39b42ea 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -150,6 +150,7 @@ public class GameRanking extends BasicGameState { SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); songMenu.resetGameDataOnLoad(); songMenu.resetTrackOnLoad(); + Utils.resetCursor(); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); break; case Input.KEY_F12: @@ -174,12 +175,14 @@ public class GameRanking extends BasicGameState { SoundController.playSound(SoundEffect.MENUBACK); ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); + Utils.resetCursor(); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); } else if (Utils.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); songMenu.resetGameDataOnLoad(); songMenu.resetTrackOnLoad(); + Utils.resetCursor(); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); } } diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 9082e3b2..621c6645 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -703,7 +703,11 @@ public class SongMenu extends BasicGameState { // reset game data if (resetGame) { ((Game) game.getState(Opsu.STATE_GAME)).resetGameData(); - GameImage.destroySkinImages(); // destroy skin images, if any + // destroy skin images, if any + for (GameImage img : GameImage.values()) { + if (img.isSkinnable()) + img.destroySkinImage(); + } resetGame = false; } }