From a48f8bd54d2ebfede6f6f0c900e9b4e92a9c9ff7 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Wed, 21 Jan 2015 01:38:02 -0500 Subject: [PATCH] Implemented in-game beatmap reloading. - Press F5 to reload beatmaps in the song menu (resets OsuGroupList, then invokes OSZ unpacker and OsuFile parser). - Many components reused from Splash screen (progress display, 'Esc' interrupt). Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/Opsu.java | 4 +- src/itdelatrisu/opsu/Utils.java | 41 ++++++++++ src/itdelatrisu/opsu/states/OptionsMenu.java | 2 +- src/itdelatrisu/opsu/states/SongMenu.java | 85 +++++++++++++++++++- src/itdelatrisu/opsu/states/Splash.java | 54 ++----------- 5 files changed, 131 insertions(+), 55 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 15542a09..ff9cc2f4 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -63,7 +63,7 @@ public class Opsu extends StateBasedGame { STATE_GAME = 4, STATE_GAMEPAUSEMENU = 5, STATE_GAMERANKING = 6, - STATE_OPTIONS = 7; + STATE_OPTIONSMENU = 7; /** * Used to restrict the program to a single instance. @@ -83,7 +83,7 @@ public class Opsu extends StateBasedGame { addState(new Game(STATE_GAME)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GameRanking(STATE_GAMERANKING)); - addState(new OptionsMenu(STATE_OPTIONS)); + addState(new OptionsMenu(STATE_OPTIONSMENU)); } /** diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 4f72b763..3676e8b1 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -587,6 +587,47 @@ public class Utils { volumeDisplay = VOLUME_DISPLAY_TIME / 10; } + /** + * Draws loading progress (OSZ unpacking, OsuFile parsing, sound loading) + * at the bottom of the screen. + */ + public static void drawLoadingProgress(Graphics g) { + String text, file; + int progress; + + // determine current action + if ((file = OszUnpacker.getCurrentFileName()) != null) { + text = "Unpacking new beatmaps..."; + progress = OszUnpacker.getUnpackerProgress(); + } else if ((file = OsuParser.getCurrentFileName()) != null) { + text = "Loading beatmaps..."; + progress = OsuParser.getParserProgress(); + } else if ((file = SoundController.getCurrentFileName()) != null) { + text = "Loading sounds..."; + progress = SoundController.getLoadingProgress(); + } else + return; + + // draw loading info + float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f; + float lineY = container.getHeight() - marginY; + int lineOffsetY = Utils.FONT_MEDIUM.getLineHeight(); + if (Options.isLoadVerbose()) { + // verbose: display percentages and file names + Utils.FONT_MEDIUM.drawString( + marginX, lineY - (lineOffsetY * 2), + String.format("%s (%d%%)", text, progress), Color.white); + Utils.FONT_MEDIUM.drawString(marginX, lineY - lineOffsetY, file, Color.white); + } else { + // draw loading bar + Utils.FONT_MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white); + g.setColor(Color.white); + g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f), + (container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 + ); + } + } + /** * Takes a screenshot. * @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots diff --git a/src/itdelatrisu/opsu/states/OptionsMenu.java b/src/itdelatrisu/opsu/states/OptionsMenu.java index 2f73b2dd..f63acafd 100644 --- a/src/itdelatrisu/opsu/states/OptionsMenu.java +++ b/src/itdelatrisu/opsu/states/OptionsMenu.java @@ -43,7 +43,7 @@ import org.newdawn.slick.state.transition.EmptyTransition; import org.newdawn.slick.state.transition.FadeInTransition; /** - * "Game OptionsMenu" state. + * "Game Options" state. */ public class OptionsMenu extends BasicGameState { /** diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 672a7bfe..d7e95c38 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -22,10 +22,12 @@ import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.MenuButton; import itdelatrisu.opsu.Opsu; +import itdelatrisu.opsu.Options; import itdelatrisu.opsu.OsuFile; import itdelatrisu.opsu.OsuGroupList; import itdelatrisu.opsu.OsuGroupNode; import itdelatrisu.opsu.OsuParser; +import itdelatrisu.opsu.OszUnpacker; import itdelatrisu.opsu.SongSort; import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.audio.HitSound; @@ -33,6 +35,7 @@ import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; +import java.io.File; import java.util.Stack; import org.lwjgl.opengl.Display; @@ -187,6 +190,11 @@ public class SongMenu extends BasicGameState { */ private boolean resetTrack = false; + /** + * Beatmap reloading thread. + */ + private Thread reloadThread; + // game-related variables private GameContainer container; private StateBasedGame game; @@ -339,8 +347,18 @@ public class SongMenu extends BasicGameState { g.fillRoundRect(width - 10, scrollStartY + (scrollEndY * startNode.index / OsuGroupList.get().size()), 5, 20, 4); } + // reloading beatmaps + if (reloadThread != null) { + // darken the screen + g.setColor(Utils.COLOR_BLACK_ALPHA); + g.fillRect(0, 0, width, height); + + Utils.drawLoadingProgress(g); + } + // back button - Utils.getBackButton().draw(); + else + Utils.getBackButton().draw(); Utils.drawVolume(g); Utils.drawFPS(); @@ -445,6 +463,10 @@ public class SongMenu extends BasicGameState { if (button != Input.MOUSE_LEFT_BUTTON) return; + // block input during beatmap reloading + if (reloadThread != null) + return; + // back if (Utils.getBackButton().contains(x, y)) { SoundController.playSound(SoundEffect.MENUBACK); @@ -456,7 +478,7 @@ public class SongMenu extends BasicGameState { // options if (optionsButton.contains(x, y)) { SoundController.playSound(SoundEffect.MENUHIT); - game.enterState(Opsu.STATE_OPTIONS, new EmptyTransition(), new FadeInTransition(Color.black)); + game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition(Color.black)); return; } @@ -520,19 +542,30 @@ public class SongMenu extends BasicGameState { @Override public void keyPressed(int key, char c) { + // block input during beatmap reloading + if (reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) + return; + switch (key) { case Input.KEY_ESCAPE: - if (!search.getText().isEmpty()) { + if (reloadThread != null) { + // beatmap reloading: stop parsing OsuFiles by sending interrupt to OsuParser + if (reloadThread != null) + reloadThread.interrupt(); + } else if (!search.getText().isEmpty()) { + // clear search text search.setText(""); searchTimer = SEARCH_DELAY; } else { + // return to main menu SoundController.playSound(SoundEffect.MENUBACK); ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); } break; case Input.KEY_F1: - game.enterState(Opsu.STATE_OPTIONS, new EmptyTransition(), new FadeInTransition(Color.black)); + SoundController.playSound(SoundEffect.MENUHIT); + game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition(Color.black)); break; case Input.KEY_F2: if (focusNode == null) @@ -549,6 +582,42 @@ public class SongMenu extends BasicGameState { setFocus(OsuGroupList.get().getRandomNode(), -1, true); } break; + case Input.KEY_F5: + // TODO: osu! has a confirmation menu + SoundController.playSound(SoundEffect.MENUCLICK); + + // reset state and node references + MusicController.reset(); + startNode = focusNode = null; + oldFocusNode = null; + randomStack = new Stack(); + songInfo = null; + hoverOffset = 0f; + hoverIndex = -1; + search.setText(""); + searchTimer = SEARCH_DELAY; + + // reload songs in new thread + reloadThread = new Thread() { + @Override + public void run() { + // invoke unpacker and parser + File beatmapDir = Options.getBeatmapDir(); + OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir); + OsuParser.parseAllFiles(beatmapDir, container.getWidth(), container.getHeight()); + + // initialize song list + if (OsuGroupList.get().size() > 0) { + OsuGroupList.get().init(); + setFocus(OsuGroupList.get().getRandomNode(), -1, true); + } else if (Options.isThemSongEnabled()) + MusicController.playThemeSong(); + + reloadThread = null; + } + }; + reloadThread.start(); + break; case Input.KEY_F12: Utils.takeScreenShot(); break; @@ -616,6 +685,10 @@ public class SongMenu extends BasicGameState { @Override public void mouseDragged(int oldx, int oldy, int newx, int newy) { + // block input during beatmap reloading + if (reloadThread != null) + return; + // check mouse button (right click scrolls faster) int multiplier; if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) @@ -634,6 +707,10 @@ public class SongMenu extends BasicGameState { @Override public void mouseWheelMoved(int newValue) { + // block input during beatmap reloading + if (reloadThread != null) + return; + changeIndex((newValue < 0) ? 1 : -1); } diff --git a/src/itdelatrisu/opsu/states/Splash.java b/src/itdelatrisu/opsu/states/Splash.java index 2f06687e..1acf03d8 100644 --- a/src/itdelatrisu/opsu/states/Splash.java +++ b/src/itdelatrisu/opsu/states/Splash.java @@ -85,27 +85,7 @@ public class Splash extends BasicGameState { throws SlickException { g.setBackground(Color.black); GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2); - - // display progress - String unpackedFile = OszUnpacker.getCurrentFileName(); - String parsedFile = OsuParser.getCurrentFileName(); - String soundFile = SoundController.getCurrentFileName(); - if (unpackedFile != null) { - drawLoadProgress( - g, OszUnpacker.getUnpackerProgress(), - "Unpacking new beatmaps...", unpackedFile - ); - } else if (parsedFile != null) { - drawLoadProgress( - g, OsuParser.getParserProgress(), - "Loading beatmaps...", parsedFile - ); - } else if (soundFile != null) { - drawLoadProgress( - g, SoundController.getLoadingProgress(), - "Loading sounds...", soundFile - ); - } + Utils.drawLoadingProgress(g); } @Override @@ -136,6 +116,7 @@ public class Splash extends BasicGameState { SoundController.init(); finished = true; + thread = null; } }; thread.start(); @@ -151,8 +132,10 @@ public class Splash extends BasicGameState { // change states when loading complete if (finished && alpha >= 1f) { // initialize song list - OsuGroupList.get().init(); - ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(OsuGroupList.get().getRandomNode(), -1, true); + if (OsuGroupList.get().size() > 0) { + OsuGroupList.get().init(); + ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(OsuGroupList.get().getRandomNode(), -1, true); + } // play the theme song if (Options.isThemSongEnabled()) @@ -175,29 +158,4 @@ public class Splash extends BasicGameState { else if (key == Input.KEY_ESCAPE && thread != null) thread.interrupt(); } - - /** - * Draws loading progress. - * @param g the graphics context - * @param progress the completion percentage - * @param text the progress text - * @param file the file being loaded - */ - private void drawLoadProgress(Graphics g, int progress, String text, String file) { - float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f; - float lineY = container.getHeight() - marginY; - int lineOffsetY = Utils.FONT_MEDIUM.getLineHeight(); - if (Options.isLoadVerbose()) { - Utils.FONT_MEDIUM.drawString( - marginX, lineY - (lineOffsetY * 2), - String.format("%s (%d%%)", text, progress), Color.white); - Utils.FONT_MEDIUM.drawString(marginX, lineY - lineOffsetY, file, Color.white); - } else { - Utils.FONT_MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white); - g.setColor(Color.white); - g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f), - (container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 - ); - } - } }