Added new menus, implemented using a generic ButtonMenu state.

New menus: beatmap options (erase scores/delete), delete beatmaps (single/all), reload songs.
- Pressing F3 or right-clicking in the song menu state will open the beatmap options menu.
- Pressing F5 in the song menu now opens a confirmation menu before reloading beatmaps.
- Deleted MainMenuExit state, which is replaced by MenuState.EXIT in ButtonMenu.
- Decreased Utils.FONT_XLARGE size (to fit in buttons).

Note: as of 800014e, song directory deletion is broken.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han
2015-02-13 02:43:34 -05:00
parent 4920508060
commit c6791c4714
7 changed files with 731 additions and 223 deletions

View File

@@ -38,6 +38,7 @@ import itdelatrisu.opsu.audio.HitSound;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
import java.io.File;
import java.util.Map;
@@ -76,6 +77,9 @@ public class SongMenu extends BasicGameState {
/** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 500;
/** Delay time, in milliseconds, before moving to the beatmap menu after a right click. */
private static final int BEATMAP_MENU_DELAY = 600;
/** Maximum x offset of song buttons for mouse hover, in pixels. */
private static final float MAX_HOVER_OFFSET = 30f;
@@ -158,6 +162,15 @@ public class SongMenu extends BasicGameState {
/** Whether or not to reset music track upon entering the state. */
private boolean resetTrack = false;
/** If non-null, determines the action to perform upon entering the state. */
private MenuState stateAction;
/** If non-null, the node that stateAction acts upon. */
private OsuGroupNode stateActionNode;
/** Timer before moving to the beatmap menu with the current focus node. */
private int beatmapMenuTimer = -1;
/** Beatmap reloading thread. */
private Thread reloadThread;
@@ -376,10 +389,23 @@ public class SongMenu extends BasicGameState {
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
optionsButton.hoverUpdate(delta, mouseX, mouseY);
// beatmap menu timer
if (beatmapMenuTimer > -1) {
beatmapMenuTimer += delta;
if (beatmapMenuTimer >= BEATMAP_MENU_DELAY) {
beatmapMenuTimer = -1;
if (focusNode != null) {
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.BEATMAP, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU);
}
return;
}
}
// search
search.setFocus(true);
searchTimer += delta;
if (searchTimer >= SEARCH_DELAY && reloadThread == null) {
if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) {
searchTimer = 0;
// store the start/focus nodes
@@ -464,11 +490,11 @@ public class SongMenu extends BasicGameState {
@Override
public void mousePressed(int button, int x, int y) {
// check mouse button
if (button != Input.MOUSE_LEFT_BUTTON)
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
// block input during beatmap reloading
if (reloadThread != null)
// block input
if (reloadThread != null || beatmapMenuTimer > -1)
return;
// back
@@ -520,8 +546,10 @@ public class SongMenu extends BasicGameState {
if (node.index == expandedIndex) {
if (node.osuFileIndex == focusNode.osuFileIndex) {
// if already focused, load the beatmap
startGame();
if (button != Input.MOUSE_RIGHT_BUTTON)
startGame();
else
SoundController.playSound(SoundEffect.MENUCLICK);
} else {
// focus the node
SoundController.playSound(SoundEffect.MENUCLICK);
@@ -539,6 +567,10 @@ public class SongMenu extends BasicGameState {
hoverOffset = oldHoverOffset;
hoverIndex = oldHoverIndex;
// open beatmap menu
if (button == Input.MOUSE_RIGHT_BUTTON)
beatmapMenuTimer = (node.index == expandedIndex) ? BEATMAP_MENU_DELAY * 4 / 5 : 0;
return;
}
}
@@ -562,8 +594,8 @@ 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))
// block input
if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1)
return;
switch (key) {
@@ -601,44 +633,17 @@ public class SongMenu extends BasicGameState {
setFocus(OsuGroupList.get().getRandomNode(), -1, true);
}
break;
case Input.KEY_F3:
if (focusNode == null)
break;
SoundController.playSound(SoundEffect.MENUHIT);
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.BEATMAP, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU);
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;
scoreMap = null;
focusScores = null;
oldFocusNode = null;
randomStack = new Stack<SongNode>();
songInfo = null;
hoverOffset = 0f;
hoverIndex = -1;
search.setText("");
searchTimer = SEARCH_DELAY;
searchResultString = "Type to search!";
// 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);
// initialize song list
if (OsuGroupList.get().size() > 0) {
OsuGroupList.get().init();
setFocus(OsuGroupList.get().getRandomNode(), -1, true);
} else
MusicController.playThemeSong();
reloadThread = null;
}
};
reloadThread.start();
SoundController.playSound(SoundEffect.MENUHIT);
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.RELOAD);
game.enterState(Opsu.STATE_BUTTONMENU);
break;
case Input.KEY_F12:
Utils.takeScreenShot();
@@ -707,8 +712,8 @@ 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)
// block input
if (reloadThread != null || beatmapMenuTimer > -1)
return;
int diff = newy - oldy;
@@ -739,8 +744,8 @@ public class SongMenu extends BasicGameState {
@Override
public void mouseWheelMoved(int newValue) {
// block input during beatmap reloading
if (reloadThread != null)
// block input
if (reloadThread != null || beatmapMenuTimer > -1)
return;
int shift = (newValue < 0) ? 1 : -1;
@@ -767,6 +772,10 @@ public class SongMenu extends BasicGameState {
hoverOffset = 0f;
hoverIndex = -1;
startScore = 0;
beatmapMenuTimer = -1;
// reset song stack
randomStack = new Stack<SongNode>();
// set focus node if not set (e.g. theme song playing)
if (focusNode == null && OsuGroupList.get().size() > 0)
@@ -787,9 +796,6 @@ public class SongMenu extends BasicGameState {
if (MusicController.isTrackDimmed())
MusicController.toggleTrackDimmed(1f);
// reset song stack
randomStack = new Stack<SongNode>();
// reset game data
if (resetGame) {
((Game) game.getState(Opsu.STATE_GAME)).resetGameData();
@@ -808,6 +814,119 @@ public class SongMenu extends BasicGameState {
resetGame = false;
}
// state-based action
if (stateAction != null) {
switch (stateAction) {
case BEATMAP: // clear scores
if (stateActionNode == null || stateActionNode.osuFileIndex == -1)
break;
OsuFile osu = stateActionNode.osuFiles.get(stateActionNode.osuFileIndex);
ScoreDB.deleteScore(osu);
if (stateActionNode == focusNode) {
focusScores = null;
scoreMap.remove(osu.version);
}
break;
case BEATMAP_DELETE_CONFIRM: // delete song group
if (stateActionNode == null)
break;
OsuGroupNode
prev = OsuGroupList.get().getBaseNode(stateActionNode.index - 1),
next = OsuGroupList.get().getBaseNode(stateActionNode.index + 1);
int oldIndex = stateActionNode.index, focusNodeIndex = focusNode.index, startNodeIndex = startNode.index;
OsuGroupList.get().deleteSongGroup(stateActionNode);
if (oldIndex == focusNodeIndex) {
if (prev != null)
setFocus(prev, -1, true);
else if (next != null)
setFocus(next, -1, true);
else {
startNode = focusNode = null;
oldFocusNode = null;
randomStack = new Stack<SongNode>();
songInfo = null;
scoreMap = null;
focusScores = null;
}
} else if (oldIndex == startNodeIndex) {
if (startNode.prev != null)
startNode = startNode.prev;
else if (startNode.next != null)
startNode = startNode.next;
else {
startNode = null;
songInfo = null;
}
}
break;
case BEATMAP_DELETE_SELECT: // delete single song
if (stateActionNode == null)
break;
int index = stateActionNode.index;
OsuGroupList.get().deleteSong(stateActionNode);
if (stateActionNode == focusNode) {
if (stateActionNode.prev != null &&
!(stateActionNode.next != null && stateActionNode.next.index == index)) {
if (stateActionNode.prev.index == index)
setFocus(stateActionNode.prev, 0, true);
else
setFocus(stateActionNode.prev, -1, true);
} else if (stateActionNode.next != null) {
if (stateActionNode.next.index == index)
setFocus(stateActionNode.next, 0, true);
else
setFocus(stateActionNode.next, -1, true);
}
} else if (stateActionNode == startNode) {
if (startNode.prev != null)
startNode = startNode.prev;
else if (startNode.next != null)
startNode = startNode.next;
}
break;
case RELOAD: // reload beatmaps
// reset state and node references
MusicController.reset();
startNode = focusNode = null;
scoreMap = null;
focusScores = null;
oldFocusNode = null;
randomStack = new Stack<SongNode>();
songInfo = null;
hoverOffset = 0f;
hoverIndex = -1;
search.setText("");
searchTimer = SEARCH_DELAY;
searchResultString = "Type to search!";
// 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);
// initialize song list
if (OsuGroupList.get().size() > 0) {
OsuGroupList.get().init();
setFocus(OsuGroupList.get().getRandomNode(), -1, true);
} else
MusicController.playThemeSong();
reloadThread = null;
}
};
reloadThread.start();
break;
default:
break;
}
stateAction = null;
stateActionNode = null;
}
}
@Override
@@ -938,6 +1057,22 @@ public class SongMenu extends BasicGameState {
*/
public void resetTrackOnLoad() { resetTrack = true; }
/**
* Performs an action based on a menu state upon entering this state.
* @param menuState the menu state determining the action
*/
public void doStateActionOnLoad(MenuState menuState) { doStateActionOnLoad(menuState, null); }
/**
* Performs an action based on a menu state upon entering this state.
* @param menuState the menu state determining the action
* @param node the song node to perform the action on
*/
public void doStateActionOnLoad(MenuState menuState, OsuGroupNode node) {
stateAction = menuState;
stateActionNode = node;
}
/**
* Returns all the score data for an OsuGroupNode from scoreMap.
* If no score data is available for the node, return null.