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 <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-01-21 01:38:02 -05:00
parent 19476993f9
commit a48f8bd54d
5 changed files with 131 additions and 55 deletions

View File

@ -63,7 +63,7 @@ public class Opsu extends StateBasedGame {
STATE_GAME = 4, STATE_GAME = 4,
STATE_GAMEPAUSEMENU = 5, STATE_GAMEPAUSEMENU = 5,
STATE_GAMERANKING = 6, STATE_GAMERANKING = 6,
STATE_OPTIONS = 7; STATE_OPTIONSMENU = 7;
/** /**
* Used to restrict the program to a single instance. * 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 Game(STATE_GAME));
addState(new GamePauseMenu(STATE_GAMEPAUSEMENU)); addState(new GamePauseMenu(STATE_GAMEPAUSEMENU));
addState(new GameRanking(STATE_GAMERANKING)); addState(new GameRanking(STATE_GAMERANKING));
addState(new OptionsMenu(STATE_OPTIONS)); addState(new OptionsMenu(STATE_OPTIONSMENU));
} }
/** /**

View File

@ -587,6 +587,47 @@ public class Utils {
volumeDisplay = VOLUME_DISPLAY_TIME / 10; 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. * Takes a screenshot.
* @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots * @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots

View File

@ -43,7 +43,7 @@ import org.newdawn.slick.state.transition.EmptyTransition;
import org.newdawn.slick.state.transition.FadeInTransition; import org.newdawn.slick.state.transition.FadeInTransition;
/** /**
* "Game OptionsMenu" state. * "Game Options" state.
*/ */
public class OptionsMenu extends BasicGameState { public class OptionsMenu extends BasicGameState {
/** /**

View File

@ -22,10 +22,12 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.MenuButton; import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuFile; import itdelatrisu.opsu.OsuFile;
import itdelatrisu.opsu.OsuGroupList; import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.OsuGroupNode; import itdelatrisu.opsu.OsuGroupNode;
import itdelatrisu.opsu.OsuParser; import itdelatrisu.opsu.OsuParser;
import itdelatrisu.opsu.OszUnpacker;
import itdelatrisu.opsu.SongSort; import itdelatrisu.opsu.SongSort;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.HitSound; import itdelatrisu.opsu.audio.HitSound;
@ -33,6 +35,7 @@ import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import java.io.File;
import java.util.Stack; import java.util.Stack;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
@ -187,6 +190,11 @@ public class SongMenu extends BasicGameState {
*/ */
private boolean resetTrack = false; private boolean resetTrack = false;
/**
* Beatmap reloading thread.
*/
private Thread reloadThread;
// game-related variables // game-related variables
private GameContainer container; private GameContainer container;
private StateBasedGame game; 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); 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 // back button
Utils.getBackButton().draw(); else
Utils.getBackButton().draw();
Utils.drawVolume(g); Utils.drawVolume(g);
Utils.drawFPS(); Utils.drawFPS();
@ -445,6 +463,10 @@ public class SongMenu extends BasicGameState {
if (button != Input.MOUSE_LEFT_BUTTON) if (button != Input.MOUSE_LEFT_BUTTON)
return; return;
// block input during beatmap reloading
if (reloadThread != null)
return;
// back // back
if (Utils.getBackButton().contains(x, y)) { if (Utils.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
@ -456,7 +478,7 @@ public class SongMenu extends BasicGameState {
// options // options
if (optionsButton.contains(x, y)) { if (optionsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); 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; return;
} }
@ -520,19 +542,30 @@ public class SongMenu extends BasicGameState {
@Override @Override
public void keyPressed(int key, char c) { 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) { switch (key) {
case Input.KEY_ESCAPE: 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(""); search.setText("");
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
} else { } else {
// return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} }
break; break;
case Input.KEY_F1: 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; break;
case Input.KEY_F2: case Input.KEY_F2:
if (focusNode == null) if (focusNode == null)
@ -549,6 +582,42 @@ public class SongMenu extends BasicGameState {
setFocus(OsuGroupList.get().getRandomNode(), -1, true); setFocus(OsuGroupList.get().getRandomNode(), -1, true);
} }
break; 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<SongNode>();
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: case Input.KEY_F12:
Utils.takeScreenShot(); Utils.takeScreenShot();
break; break;
@ -616,6 +685,10 @@ public class SongMenu extends BasicGameState {
@Override @Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) { 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) // check mouse button (right click scrolls faster)
int multiplier; int multiplier;
if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))
@ -634,6 +707,10 @@ public class SongMenu extends BasicGameState {
@Override @Override
public void mouseWheelMoved(int newValue) { public void mouseWheelMoved(int newValue) {
// block input during beatmap reloading
if (reloadThread != null)
return;
changeIndex((newValue < 0) ? 1 : -1); changeIndex((newValue < 0) ? 1 : -1);
} }

View File

@ -85,27 +85,7 @@ public class Splash extends BasicGameState {
throws SlickException { throws SlickException {
g.setBackground(Color.black); g.setBackground(Color.black);
GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2); GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2);
Utils.drawLoadingProgress(g);
// 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
);
}
} }
@Override @Override
@ -136,6 +116,7 @@ public class Splash extends BasicGameState {
SoundController.init(); SoundController.init();
finished = true; finished = true;
thread = null;
} }
}; };
thread.start(); thread.start();
@ -151,8 +132,10 @@ public class Splash extends BasicGameState {
// change states when loading complete // change states when loading complete
if (finished && alpha >= 1f) { if (finished && alpha >= 1f) {
// initialize song list // initialize song list
OsuGroupList.get().init(); if (OsuGroupList.get().size() > 0) {
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(OsuGroupList.get().getRandomNode(), -1, true); OsuGroupList.get().init();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(OsuGroupList.get().getRandomNode(), -1, true);
}
// play the theme song // play the theme song
if (Options.isThemSongEnabled()) if (Options.isThemSongEnabled())
@ -175,29 +158,4 @@ public class Splash extends BasicGameState {
else if (key == Input.KEY_ESCAPE && thread != null) else if (key == Input.KEY_ESCAPE && thread != null)
thread.interrupt(); 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
);
}
}
} }