Follow-up to c6791c4
.
- Added score management menu for deleting individual scores. - Store generated title string lists in the beatmap menus to prevent performance issues. Allow using the right mouse button (as well as left mouse button) in all states. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
c6791c4714
commit
7cc4ff51d0
|
@ -45,7 +45,7 @@ public class ScoreDB {
|
|||
private static PreparedStatement selectMapStmt, selectMapSetStmt;
|
||||
|
||||
/** Score deletion statement. */
|
||||
private static PreparedStatement deleteStmt;
|
||||
private static PreparedStatement deleteSongStmt, deleteScoreStmt;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private ScoreDB() {}
|
||||
|
@ -85,10 +85,16 @@ public class ScoreDB {
|
|||
"SELECT * FROM scores WHERE " +
|
||||
"MSID = ? AND title = ? AND artist = ? AND creator = ? ORDER BY version DESC"
|
||||
);
|
||||
deleteStmt = connection.prepareStatement(
|
||||
deleteSongStmt = connection.prepareStatement(
|
||||
"DELETE FROM scores WHERE " +
|
||||
"MID = ? AND title = ? AND artist = ? AND creator = ? AND version = ?"
|
||||
);
|
||||
deleteScoreStmt = connection.prepareStatement(
|
||||
"DELETE FROM scores WHERE " +
|
||||
"timestamp = ? AND MID = ? AND MSID = ? AND title = ? AND artist = ? AND " +
|
||||
"creator = ? AND version = ? AND hit300 = ? AND hit100 = ? AND hit50 = ? AND " +
|
||||
"geki = ? AND katu = ? AND miss = ? AND score = ? AND combo = ? AND perfect = ? AND mods = ?"
|
||||
);
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to prepare score insertion statement.", e, true);
|
||||
}
|
||||
|
@ -123,46 +129,70 @@ public class ScoreDB {
|
|||
*/
|
||||
public static void addScore(ScoreData data) {
|
||||
try {
|
||||
insertStmt.setLong(1, data.timestamp);
|
||||
insertStmt.setInt(2, data.MID);
|
||||
insertStmt.setInt(3, data.MSID);
|
||||
insertStmt.setString(4, data.title);
|
||||
insertStmt.setString(5, data.artist);
|
||||
insertStmt.setString(6, data.creator);
|
||||
insertStmt.setString(7, data.version);
|
||||
insertStmt.setInt(8, data.hit300);
|
||||
insertStmt.setInt(9, data.hit100);
|
||||
insertStmt.setInt(10, data.hit50);
|
||||
insertStmt.setInt(11, data.geki);
|
||||
insertStmt.setInt(12, data.katu);
|
||||
insertStmt.setInt(13, data.miss);
|
||||
insertStmt.setLong(14, data.score);
|
||||
insertStmt.setInt(15, data.combo);
|
||||
insertStmt.setBoolean(16, data.perfect);
|
||||
insertStmt.setInt(17, data.mods);
|
||||
setStatementFields(insertStmt, data);
|
||||
insertStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to save score to database.", e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given score from the database.
|
||||
* @param data the score to delete
|
||||
*/
|
||||
public static void deleteScore(ScoreData data) {
|
||||
try {
|
||||
setStatementFields(deleteScoreStmt, data);
|
||||
deleteScoreStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete score from database.", e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the scores for the given beatmap from the database.
|
||||
* @param osu the OsuFile object
|
||||
*/
|
||||
public static void deleteScore(OsuFile osu) {
|
||||
try {
|
||||
deleteStmt.setInt(1, osu.beatmapID);
|
||||
deleteStmt.setString(2, osu.title);
|
||||
deleteStmt.setString(3, osu.artist);
|
||||
deleteStmt.setString(4, osu.creator);
|
||||
deleteStmt.setString(5, osu.version);
|
||||
deleteStmt.executeUpdate();
|
||||
deleteSongStmt.setInt(1, osu.beatmapID);
|
||||
deleteSongStmt.setString(2, osu.title);
|
||||
deleteSongStmt.setString(3, osu.artist);
|
||||
deleteSongStmt.setString(4, osu.creator);
|
||||
deleteSongStmt.setString(5, osu.version);
|
||||
deleteSongStmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
ErrorHandler.error("Failed to delete score from database.", e, true);
|
||||
ErrorHandler.error("Failed to delete scores from database.", e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all statement fields using a given ScoreData object.
|
||||
* @param stmt the statement to set fields for
|
||||
* @param data the score data
|
||||
* @throws SQLException
|
||||
*/
|
||||
private static void setStatementFields(PreparedStatement stmt, ScoreData data)
|
||||
throws SQLException {
|
||||
stmt.setLong(1, data.timestamp);
|
||||
stmt.setInt(2, data.MID);
|
||||
stmt.setInt(3, data.MSID);
|
||||
stmt.setString(4, data.title);
|
||||
stmt.setString(5, data.artist);
|
||||
stmt.setString(6, data.creator);
|
||||
stmt.setString(7, data.version);
|
||||
stmt.setInt(8, data.hit300);
|
||||
stmt.setInt(9, data.hit100);
|
||||
stmt.setInt(10, data.hit50);
|
||||
stmt.setInt(11, data.geki);
|
||||
stmt.setInt(12, data.katu);
|
||||
stmt.setInt(13, data.miss);
|
||||
stmt.setLong(14, data.score);
|
||||
stmt.setInt(15, data.combo);
|
||||
stmt.setBoolean(16, data.perfect);
|
||||
stmt.setInt(17, data.mods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the game scores for an OsuFile map.
|
||||
* @param osu the OsuFile
|
||||
|
|
|
@ -23,10 +23,12 @@ import itdelatrisu.opsu.MenuButton;
|
|||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.OsuGroupList;
|
||||
import itdelatrisu.opsu.OsuGroupNode;
|
||||
import itdelatrisu.opsu.ScoreData;
|
||||
import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.audio.SoundController;
|
||||
import itdelatrisu.opsu.audio.SoundEffect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
|
@ -108,6 +110,17 @@ public class ButtonMenu extends BasicGameState {
|
|||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.RELOAD_CANCEL.click(container, game);
|
||||
}
|
||||
},
|
||||
SCORE (new Button[] { Button.DELETE_SCORE, Button.CLOSE }) {
|
||||
@Override
|
||||
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||
return new String[] { "Score Management" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leave(GameContainer container, StateBasedGame game) {
|
||||
Button.CLOSE.click(container, game);
|
||||
}
|
||||
};
|
||||
|
||||
/** The buttons in the state. */
|
||||
|
@ -116,6 +129,9 @@ public class ButtonMenu extends BasicGameState {
|
|||
/** The associated MenuButton objects. */
|
||||
private MenuButton[] menuButtons;
|
||||
|
||||
/** The actual title string list, generated upon entering the state. */
|
||||
private List<String> actualTitle;
|
||||
|
||||
/** Initial x coordinate offsets left/right of center (for shifting animation), times width. (TODO) */
|
||||
private static final float OFFSET_WIDTH_RATIO = 1 / 18f;
|
||||
|
||||
|
@ -162,18 +178,11 @@ public class ButtonMenu extends BasicGameState {
|
|||
*/
|
||||
public void draw(GameContainer container, StateBasedGame game, Graphics g) {
|
||||
// draw title
|
||||
String[] title = getTitle(container, game);
|
||||
float c = container.getWidth() * 0.02f;
|
||||
int maxLineWidth = container.getWidth() - (int) (c * 2);
|
||||
int lineHeight = Utils.FONT_LARGE.getLineHeight();
|
||||
for (int i = 0, j = 0; i < title.length; i++, j++) {
|
||||
// wrap text if too long
|
||||
if (Utils.FONT_LARGE.getWidth(title[i]) > maxLineWidth) {
|
||||
List<String> list = Utils.wrap(title[i], Utils.FONT_LARGE, maxLineWidth);
|
||||
for (String str : list)
|
||||
Utils.FONT_LARGE.drawString(c, c + (j++ * lineHeight), str, Color.white);
|
||||
} else
|
||||
Utils.FONT_LARGE.drawString(c, c + (j * lineHeight), title[i], Color.white);
|
||||
if (actualTitle != null) {
|
||||
float c = container.getWidth() * 0.02f;
|
||||
int lineHeight = Utils.FONT_LARGE.getLineHeight();
|
||||
for (int i = 0, size = actualTitle.size(); i < size; i++)
|
||||
Utils.FONT_LARGE.drawString(c, c + (i * lineHeight), actualTitle.get(i), Color.white);
|
||||
}
|
||||
|
||||
// draw buttons
|
||||
|
@ -252,6 +261,19 @@ public class ButtonMenu extends BasicGameState {
|
|||
menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffset * -1 : centerOffset));
|
||||
menuButtons[i].resetHover();
|
||||
}
|
||||
|
||||
// create title string list
|
||||
actualTitle = new ArrayList<String>();
|
||||
String[] title = getTitle(container, game);
|
||||
int maxLineWidth = (int) (container.getWidth() * 0.96f);
|
||||
for (int i = 0; i < title.length; i++) {
|
||||
// wrap text if too long
|
||||
if (Utils.FONT_LARGE.getWidth(title[i]) > maxLineWidth) {
|
||||
List<String> list = Utils.wrap(title[i], Utils.FONT_LARGE, maxLineWidth);
|
||||
actualTitle.addAll(list);
|
||||
} else
|
||||
actualTitle.add(title[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,6 +369,21 @@ public class ButtonMenu extends BasicGameState {
|
|||
public void click(GameContainer container, StateBasedGame game) {
|
||||
CANCEL.click(container, game);
|
||||
}
|
||||
},
|
||||
DELETE_SCORE ("Delete score", Color.green) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
ScoreData scoreData = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getScoreData();
|
||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.SCORE, scoreData);
|
||||
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
||||
}
|
||||
},
|
||||
CLOSE ("Close", Color.gray) {
|
||||
@Override
|
||||
public void click(GameContainer container, StateBasedGame game) {
|
||||
CANCEL.click(container, game);
|
||||
}
|
||||
};
|
||||
|
||||
/** The text to show on the button. */
|
||||
|
@ -389,6 +426,9 @@ public class ButtonMenu extends BasicGameState {
|
|||
/** The song node to process in the state. */
|
||||
private OsuGroupNode node;
|
||||
|
||||
/** The score data to process in the state. */
|
||||
private ScoreData scoreData;
|
||||
|
||||
// game-related variables
|
||||
private GameContainer container;
|
||||
private StateBasedGame game;
|
||||
|
@ -441,7 +481,7 @@ public class ButtonMenu 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;
|
||||
|
||||
if (menuState != null)
|
||||
|
@ -476,20 +516,41 @@ public class ButtonMenu extends BasicGameState {
|
|||
* Changes the menu state.
|
||||
* @param menuState the new menu state
|
||||
*/
|
||||
public void setMenuState(MenuState menuState) { setMenuState(menuState, null); }
|
||||
public void setMenuState(MenuState menuState) { setMenuState(menuState, null, null); }
|
||||
|
||||
/**
|
||||
* Changes the menu state.
|
||||
* @param menuState the new menu state
|
||||
* @param node the song node to process in the state
|
||||
*/
|
||||
public void setMenuState(MenuState menuState, OsuGroupNode node) {
|
||||
public void setMenuState(MenuState menuState, OsuGroupNode node) { setMenuState(menuState, node, null); }
|
||||
|
||||
/**
|
||||
* Changes the menu state.
|
||||
* @param menuState the new menu state
|
||||
* @param scoreData the score scoreData
|
||||
*/
|
||||
public void setMenuState(MenuState menuState, ScoreData scoreData) { setMenuState(menuState, null, scoreData); }
|
||||
|
||||
/**
|
||||
* Changes the menu state.
|
||||
* @param menuState the new menu state
|
||||
* @param node the song node to process in the state
|
||||
* @param scoreData the score scoreData
|
||||
*/
|
||||
private void setMenuState(MenuState menuState, OsuGroupNode node, ScoreData scoreData) {
|
||||
this.menuState = menuState;
|
||||
this.node = node;
|
||||
this.scoreData = scoreData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the song node being processed, or null if none.
|
||||
*/
|
||||
public OsuGroupNode getNode() { return node; }
|
||||
private OsuGroupNode getNode() { return node; }
|
||||
|
||||
/**
|
||||
* Returns the score data being processed, or null if none.
|
||||
*/
|
||||
private ScoreData getScoreData() { return scoreData; }
|
||||
}
|
||||
|
|
|
@ -413,7 +413,7 @@ public class DownloadsMenu 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 importing
|
||||
|
|
|
@ -63,10 +63,14 @@ import org.newdawn.slick.state.transition.FadeOutTransition;
|
|||
public class Game extends BasicGameState {
|
||||
/** Game restart states. */
|
||||
public enum Restart {
|
||||
FALSE, // no restart
|
||||
NEW, // first time loading song
|
||||
MANUAL, // retry
|
||||
LOSE; // health is zero: no-continue/force restart
|
||||
/** No restart. */
|
||||
FALSE,
|
||||
/** First time loading the song. */
|
||||
NEW,
|
||||
/** Manual retry. */
|
||||
MANUAL,
|
||||
/** Health is zero: no-continue/force restart. */
|
||||
LOSE;
|
||||
}
|
||||
|
||||
/** Minimum time before start of song, in milliseconds, to process skip-related actions. */
|
||||
|
|
|
@ -140,7 +140,7 @@ public class GameRanking 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;
|
||||
|
||||
if (data.isGameplay()) {
|
||||
|
|
|
@ -341,7 +341,7 @@ public class MainMenu 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;
|
||||
|
||||
// music button actions
|
||||
|
|
|
@ -168,6 +168,9 @@ public class SongMenu extends BasicGameState {
|
|||
/** If non-null, the node that stateAction acts upon. */
|
||||
private OsuGroupNode stateActionNode;
|
||||
|
||||
/** If non-null, the score data that stateAction acts upon. */
|
||||
private ScoreData stateActionScore;
|
||||
|
||||
/** Timer before moving to the beatmap menu with the current focus node. */
|
||||
private int beatmapMenuTimer = -1;
|
||||
|
||||
|
@ -582,10 +585,17 @@ public class SongMenu extends BasicGameState {
|
|||
if (rank >= focusScores.length)
|
||||
break;
|
||||
if (ScoreData.buttonContains(x, y, i)) {
|
||||
// view score
|
||||
GameData data = new GameData(focusScores[rank], container.getWidth(), container.getHeight());
|
||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
if (button != Input.MOUSE_RIGHT_BUTTON) {
|
||||
// view score
|
||||
GameData data = new GameData(focusScores[rank], container.getWidth(), container.getHeight());
|
||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||
} else {
|
||||
// score management
|
||||
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.SCORE, focusScores[rank]);
|
||||
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -818,7 +828,7 @@ public class SongMenu extends BasicGameState {
|
|||
// state-based action
|
||||
if (stateAction != null) {
|
||||
switch (stateAction) {
|
||||
case BEATMAP: // clear scores
|
||||
case BEATMAP: // clear all scores
|
||||
if (stateActionNode == null || stateActionNode.osuFileIndex == -1)
|
||||
break;
|
||||
OsuFile osu = stateActionNode.osuFiles.get(stateActionNode.osuFileIndex);
|
||||
|
@ -828,6 +838,14 @@ public class SongMenu extends BasicGameState {
|
|||
scoreMap.remove(osu.version);
|
||||
}
|
||||
break;
|
||||
case SCORE: // clear single score
|
||||
if (stateActionScore == null)
|
||||
break;
|
||||
ScoreDB.deleteScore(stateActionScore);
|
||||
scoreMap = ScoreDB.getMapSetScores(focusNode.osuFiles.get(focusNode.osuFileIndex));
|
||||
focusScores = getScoreDataForNode(focusNode, true);
|
||||
startScore = 0;
|
||||
break;
|
||||
case BEATMAP_DELETE_CONFIRM: // delete song group
|
||||
if (stateActionNode == null)
|
||||
break;
|
||||
|
@ -926,6 +944,7 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
stateAction = null;
|
||||
stateActionNode = null;
|
||||
stateActionScore = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1080,7 @@ public class SongMenu extends BasicGameState {
|
|||
* 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); }
|
||||
public void doStateActionOnLoad(MenuState menuState) { doStateActionOnLoad(menuState, null, null); }
|
||||
|
||||
/**
|
||||
* Performs an action based on a menu state upon entering this state.
|
||||
|
@ -1069,8 +1088,28 @@ public class SongMenu extends BasicGameState {
|
|||
* @param node the song node to perform the action on
|
||||
*/
|
||||
public void doStateActionOnLoad(MenuState menuState, OsuGroupNode node) {
|
||||
doStateActionOnLoad(menuState, node, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an action based on a menu state upon entering this state.
|
||||
* @param menuState the menu state determining the action
|
||||
* @param scoreData the score data to perform the action on
|
||||
*/
|
||||
public void doStateActionOnLoad(MenuState menuState, ScoreData scoreData) {
|
||||
doStateActionOnLoad(menuState, null, scoreData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param scoreData the score data to perform the action on
|
||||
*/
|
||||
private void doStateActionOnLoad(MenuState menuState, OsuGroupNode node, ScoreData scoreData) {
|
||||
stateAction = menuState;
|
||||
stateActionNode = node;
|
||||
stateActionScore = scoreData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user