From 3b847d088d2a7d385a2b76efbaa0d81e265571cb Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Thu, 13 Oct 2016 14:41:58 -0400 Subject: [PATCH] Follow-up to #155. Some code cleanup and tweaks to the scoreboard display. --- src/itdelatrisu/opsu/GameData.java | 55 ++++++++++------- src/itdelatrisu/opsu/Opsu.java | 1 - src/itdelatrisu/opsu/ScoreData.java | 59 +++++++++++++----- src/itdelatrisu/opsu/db/ScoreDB.java | 9 +-- src/itdelatrisu/opsu/states/Game.java | 87 +++++++++++++++------------ src/itdelatrisu/opsu/ui/Colors.java | 1 + 6 files changed, 130 insertions(+), 82 deletions(-) diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index a69020d1..a9c5f54e 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -1394,34 +1394,45 @@ public class GameData { * (i.e. this will not overwrite existing data). * @param beatmap the beatmap * @return the ScoreData object + * @see #getCurrentScoreData(Beatmap, boolean) */ public ScoreData getScoreData(Beatmap beatmap) { if (scoreData == null) - scoreData = new ScoreData(); - - scoreData.timestamp = System.currentTimeMillis() / 1000L; - scoreData.MID = beatmap.beatmapID; - scoreData.MSID = beatmap.beatmapSetID; - scoreData.title = beatmap.title; - scoreData.artist = beatmap.artist; - scoreData.creator = beatmap.creator; - scoreData.version = beatmap.version; - scoreData.hit300 = hitResultCount[HIT_300]; - scoreData.hit100 = hitResultCount[HIT_100]; - scoreData.hit50 = hitResultCount[HIT_50]; - scoreData.geki = hitResultCount[HIT_300G]; - scoreData.katu = hitResultCount[HIT_300K] + hitResultCount[HIT_100K]; - scoreData.miss = hitResultCount[HIT_MISS]; - scoreData.score = score; - scoreData.combo = comboMax; - scoreData.perfect = (comboMax == fullObjectCount); - scoreData.mods = GameMod.getModState(); - scoreData.replayString = (replay == null) ? null : replay.getReplayFilename(); - scoreData.playerName = null; // TODO - + scoreData = getCurrentScoreData(beatmap, false); return scoreData; } + /** + * Returns a ScoreData object encapsulating all current game data. + * @param beatmap the beatmap + * @param slidingScore if true, use the display score (might not be actual score) + * @return the ScoreData object + * @see #getScoreData(Beatmap) + */ + public ScoreData getCurrentScoreData(Beatmap beatmap, boolean slidingScore) { + ScoreData sd = new ScoreData(); + sd.timestamp = System.currentTimeMillis() / 1000L; + sd.MID = beatmap.beatmapID; + sd.MSID = beatmap.beatmapSetID; + sd.title = beatmap.title; + sd.artist = beatmap.artist; + sd.creator = beatmap.creator; + sd.version = beatmap.version; + sd.hit300 = hitResultCount[HIT_300]; + sd.hit100 = hitResultCount[HIT_100]; + sd.hit50 = hitResultCount[HIT_50]; + sd.geki = hitResultCount[HIT_300G]; + sd.katu = hitResultCount[HIT_300K] + hitResultCount[HIT_100K]; + sd.miss = hitResultCount[HIT_MISS]; + sd.score = slidingScore ? scoreDisplay : score; + sd.combo = comboMax; + sd.perfect = (comboMax == fullObjectCount); + sd.mods = GameMod.getModState(); + sd.replayString = (replay == null) ? null : replay.getReplayFilename(); + sd.playerName = null; // TODO + return sd; + } + /** * Returns a Replay object encapsulating all game data. * If a replay already exists and frames is null, the existing object will be returned. diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 4a7a3abd..82e9f553 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -230,7 +230,6 @@ public class Opsu extends StateBasedGame { } else { if (id == STATE_GAME) { MusicController.pause(); - MusicController.setPitch(1.0f); MusicController.resume(); } else songMenu.resetTrackOnLoad(); diff --git a/src/itdelatrisu/opsu/ScoreData.java b/src/itdelatrisu/opsu/ScoreData.java index 71971160..b873672f 100644 --- a/src/itdelatrisu/opsu/ScoreData.java +++ b/src/itdelatrisu/opsu/ScoreData.java @@ -330,39 +330,66 @@ public class ScoreData implements Comparable { } /** - * Draws the score ingame (smaller and with less information). + * Draws the score in-game (smaller and with less information). * @param g the current graphics context * @param vPos the base y position of the scoreboard * @param rank the current rank of this score * @param position the animated position offset * @param data an instance of GameData to draw rank number - * @param alpha the transparancy of the score + * @param alpha the transparency of the score * @param isActive if this score is the one currently played */ public void drawSmall(Graphics g, int vPos, int rank, float position, GameData data, float alpha, boolean isActive) { - + int rectWidth = (int) (145 * GameImage.getUIscale()); //135 int rectHeight = data.getScoreSymbolImage('0').getHeight(); int vertDistance = rectHeight + 10; - int yPos = (int)(vPos + position * vertDistance - rectHeight/2); + int yPos = (int) (vPos + position * vertDistance - rectHeight / 2); + int xPaddingLeft = Math.max(4, (int) (rectWidth * 0.04f)); + int xPaddingRight = Math.max(2, (int) (rectWidth * 0.02f)); + int yPadding = Math.max(2, (int) (rectHeight * 0.02f)); String scoreString = String.format(Locale.US, "%,d", score); String comboString = String.format("%dx", combo); String rankString = String.format("%d", rank); - int rectWidth = (int) (170 * GameImage.getUIscale()); - Color rectColor = isActive ? Colors.YELLOW_ALPHA : Colors.BLACK_ALPHA; - rectColor.a = 0.5f * alpha; - + Color white = Colors.WHITE_ALPHA, blue = Colors.BLUE_SCOREBOARD, black = Colors.BLACK_ALPHA; + float oldAlphaWhite = white.a, oldAlphaBlue = blue.a, oldAlphaBlack = black.a; + + // rectangle background + Color rectColor = isActive ? white : blue; + rectColor.a = alpha * 0.2f; g.setColor(rectColor); g.fillRect(0, yPos, rectWidth, rectHeight); - data.drawSymbolString(rankString, rectWidth, yPos, 1.0f, 0.25f*alpha, true); - if (playerName != null) { - Colors.WHITE_ALPHA.a = 0.5f * alpha; - Fonts.MEDIUM.drawString(0, yPos, playerName, Colors.WHITE_ALPHA); - } - Colors.WHITE_ALPHA.a = alpha; - Fonts.MEDIUMBOLD.drawString(0, yPos + rectHeight - Fonts.MEDIUMBOLD.getLineHeight(), scoreString, Colors.WHITE_ALPHA); - Fonts.MEDIUMBOLD.drawString(rectWidth - Fonts.MEDIUMBOLD.getWidth(comboString), yPos + rectHeight - Fonts.MEDIUMBOLD.getLineHeight(), comboString, Colors.WHITE_ALPHA); + black.a = alpha * 0.2f; + g.setColor(black); + float oldLineWidth = g.getLineWidth(); + g.setLineWidth(1f); + g.drawRect(0, yPos, rectWidth, rectHeight); + g.setLineWidth(oldLineWidth); + // rank + data.drawSymbolString(rankString, rectWidth, yPos, 1.0f, alpha * 0.2f, true); + + white.a = blue.a = alpha * 0.75f; + + // player name + if (playerName != null) + Fonts.MEDIUMBOLD.drawString(xPaddingLeft, yPos + yPadding, playerName, white); + + // score + Fonts.DEFAULT.drawString( + xPaddingLeft, yPos + rectHeight - Fonts.DEFAULT.getLineHeight() - yPadding, scoreString, white + ); + + // combo + Fonts.DEFAULT.drawString( + rectWidth - Fonts.DEFAULT.getWidth(comboString) - xPaddingRight, + yPos + rectHeight - Fonts.DEFAULT.getLineHeight() - yPadding, + comboString, blue + ); + + white.a = oldAlphaWhite; + blue.a = oldAlphaBlue; + black.a = oldAlphaBlack; } /** diff --git a/src/itdelatrisu/opsu/db/ScoreDB.java b/src/itdelatrisu/opsu/db/ScoreDB.java index e6f27aef..8ae37f8c 100644 --- a/src/itdelatrisu/opsu/db/ScoreDB.java +++ b/src/itdelatrisu/opsu/db/ScoreDB.java @@ -304,16 +304,16 @@ public class ScoreDB { * @return all scores for the beatmap, or null if any error occurred */ public static ScoreData[] getMapScores(Beatmap beatmap) { - return getMapScores(beatmap, null); + return getMapScoresExcluding(beatmap, null); } /** * Retrieves the game scores for a beatmap while excluding a score. * @param beatmap the beatmap - * @param exclude the filename of the score to exclude + * @param exclude the filename (replay string) of the score to exclude * @return all scores for the beatmap except for exclude, or null if any error occurred */ - public static ScoreData[] getMapScores(Beatmap beatmap, String exclude) { + public static ScoreData[] getMapScoresExcluding(Beatmap beatmap, String exclude) { if (connection == null) return null; @@ -328,7 +328,7 @@ public class ScoreDB { while (rs.next()) { ScoreData s = new ScoreData(rs); if (s.replayString != null && s.replayString.equals(exclude)) { - // dont return this score + // don't return this score } else { list.add(s); } @@ -340,6 +340,7 @@ public class ScoreDB { } return getSortedArray(list); } + /** * Retrieves the game scores for a beatmap set. * @param beatmap the beatmap diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 2ce8a3a8..7a5b1c27 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -101,11 +101,11 @@ public class Game extends BasicGameState { /** Maximum rotation, in degrees, over fade out upon death. */ private static final float MAX_ROTATION = 90f; - /** The duration of the score changing animation */ + /** The duration of the score changing animation. */ private static final float SCOREBOARD_ANIMATION_TIME = 500f; - /** The time the scoreboard takes to fade in */ - private static final float SCOREBOARD_FADE_IN_TIME = 500f; + /** The time the scoreboard takes to fade in. */ + private static final float SCOREBOARD_FADE_IN_TIME = 300f; /** Minimum time before start of song, in milliseconds, to process skip-related actions. */ private static final int SKIP_OFFSET = 2000; @@ -271,10 +271,10 @@ public class Game extends BasicGameState { /** The time the rank was last updated. */ private int lastRankUpdateTime; - /** Is the scoreboard visible? */ + /** Whether the scoreboard is visible. */ private boolean scoreboardVisible; - /** The current aplha of the scoreboard. */ + /** The current alpha of the scoreboard. */ private float currentScoreboardAlpha; /** Music position bar background colors. */ @@ -503,7 +503,6 @@ public class Game extends BasicGameState { arrow.draw(width * 0.75f, height * 0.75f); } } - } // non-break @@ -585,33 +584,42 @@ public class Game extends BasicGameState { drawHitObjects(g, trackPosition); } + // in-game scoreboard if (previousScores != null && trackPosition >= firstObjectTime && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive()) { - ScoreData currentScore = data.getScoreData(beatmap); - while (currentRank > 0 && previousScores[currentRank-1].score < currentScore.score) { + ScoreData currentScore = data.getCurrentScoreData(beatmap, true); + while (currentRank > 0 && previousScores[currentRank - 1].score < currentScore.score) { currentRank--; lastRankUpdateTime = trackPosition; } - float animation = AnimationEquation.IN_OUT_QUAD.calc(Utils.clamp((trackPosition - lastRankUpdateTime) / SCOREBOARD_ANIMATION_TIME, 0f, 1f)); + float animation = AnimationEquation.IN_OUT_QUAD.calc( + Utils.clamp((trackPosition - lastRankUpdateTime) / SCOREBOARD_ANIMATION_TIME, 0f, 1f) + ); int scoreboardPosition = 2 * container.getHeight() / 3; if (currentRank < 4) { - //draw the (new) top 5 ranks + // draw the (new) top 5 ranks for (int i = 0; i < 4; i++) { - int ii = i + (i>=currentRank ? 1 : 0); - if (i < previousScores.length) - previousScores[i].drawSmall(g, scoreboardPosition, ii + 1, ii + (i==currentRank ? animation-3f : -2f), data, currentScoreboardAlpha, false); + int index = i + (i >= currentRank ? 1 : 0); + if (i < previousScores.length) { + float position = index + (i == currentRank ? animation - 3f : -2f); + previousScores[i].drawSmall(g, scoreboardPosition, index + 1, position, data, currentScoreboardAlpha, false); + } } currentScore.drawSmall(g, scoreboardPosition, currentRank + 1, currentRank - 1f - animation, data, currentScoreboardAlpha, true); } else { - //draw the top 2 and next 2 ranks + // draw the top 2 and next 2 ranks previousScores[0].drawSmall(g, scoreboardPosition, 1, -2f, data, currentScoreboardAlpha, false); previousScores[1].drawSmall(g, scoreboardPosition, 2, -1f, data, currentScoreboardAlpha, false); - previousScores[currentRank-2].drawSmall(g, scoreboardPosition, currentRank - 1, animation - 1f, data, currentScoreboardAlpha*animation, false); - previousScores[currentRank-1].drawSmall(g, scoreboardPosition, currentRank, animation, data, currentScoreboardAlpha, false); + previousScores[currentRank - 2].drawSmall( + g, scoreboardPosition, currentRank - 1, animation - 1f, data, currentScoreboardAlpha * animation, false + ); + previousScores[currentRank - 1].drawSmall(g, scoreboardPosition, currentRank, animation, data, currentScoreboardAlpha, false); currentScore.drawSmall(g, scoreboardPosition, currentRank + 1, 2f, data, currentScoreboardAlpha, true); if (animation < 1.0f && currentRank < previousScores.length) { - previousScores[currentRank].drawSmall(g, scoreboardPosition, currentRank + 2, 1f + 5 * animation, data, currentScoreboardAlpha*(1f - animation), false); + previousScores[currentRank].drawSmall( + g, scoreboardPosition, currentRank + 2, 1f + 5 * animation, data, currentScoreboardAlpha * (1f - animation), false + ); } } } @@ -672,20 +680,6 @@ public class Game extends BasicGameState { int trackPosition = MusicController.getPosition(); int firstObjectTime = beatmap.objects[0].getTime(); - - if (previousScores != null && trackPosition > firstObjectTime) { - // show scoreboard when in break - if (scoreboardVisible || breakTime > 0) { - currentScoreboardAlpha += 1f/SCOREBOARD_FADE_IN_TIME * delta; - if (currentScoreboardAlpha > 1f) - currentScoreboardAlpha = 1f; - } else { - currentScoreboardAlpha -= 1f/SCOREBOARD_FADE_IN_TIME * delta; - if (currentScoreboardAlpha < 0f) - currentScoreboardAlpha = 0f; - } - } - // returning from pause screen: must click previous mouse position if (pauseTime > -1) { // paused during lead-in or break, or "relax" or "autopilot": continue immediately @@ -790,6 +784,20 @@ public class Game extends BasicGameState { } } + // update in-game scoreboard + if (previousScores != null && trackPosition > firstObjectTime) { + // show scoreboard if selected, and always in break + if (scoreboardVisible || breakTime > 0) { + currentScoreboardAlpha += 1f / SCOREBOARD_FADE_IN_TIME * delta; + if (currentScoreboardAlpha > 1f) + currentScoreboardAlpha = 1f; + } else { + currentScoreboardAlpha -= 1f / SCOREBOARD_FADE_IN_TIME * delta; + if (currentScoreboardAlpha < 0f) + currentScoreboardAlpha = 0f; + } + } + data.updateDisplays(delta); } @@ -1055,7 +1063,7 @@ public class Game extends BasicGameState { Utils.takeScreenShot(); break; case Input.KEY_TAB: - scoreboardVisible = ! scoreboardVisible; + scoreboardVisible = !scoreboardVisible; break; } } @@ -1209,13 +1217,6 @@ public class Game extends BasicGameState { if (beatmap == null || beatmap.objects == null) throw new RuntimeException("Running game with no beatmap loaded."); - // fetch previous results - previousScores = ScoreDB.getMapScores(beatmap, replay == null ? null : replay.getReplayFilename()); - lastRankUpdateTime = -1000; - if (previousScores != null) - currentRank = previousScores.length; - scoreboardVisible = true; - currentScoreboardAlpha = 0f; // free all previously cached hitobject to framebuffer mappings if some still exist FrameBufferCache.getInstance().freeMap(); @@ -1348,6 +1349,14 @@ public class Game extends BasicGameState { leadInTime = beatmap.audioLeadIn + approachTime; restart = Restart.FALSE; + // fetch previous scores + previousScores = ScoreDB.getMapScoresExcluding(beatmap, replay == null ? null : replay.getReplayFilename()); + lastRankUpdateTime = -1000; + if (previousScores != null) + currentRank = previousScores.length; + scoreboardVisible = true; + currentScoreboardAlpha = 0f; + // needs to play before setting position to resume without lag later MusicController.play(false); MusicController.setPosition(0); diff --git a/src/itdelatrisu/opsu/ui/Colors.java b/src/itdelatrisu/opsu/ui/Colors.java index 2f79efb9..7f00603a 100644 --- a/src/itdelatrisu/opsu/ui/Colors.java +++ b/src/itdelatrisu/opsu/ui/Colors.java @@ -42,6 +42,7 @@ public class Colors { DARK_GRAY = new Color(0.3f, 0.3f, 0.3f, 1f), RED_HIGHLIGHT = new Color(246, 154, 161), BLUE_HIGHLIGHT = new Color(173, 216, 230), + BLUE_SCOREBOARD = new Color(133, 208, 212), BLACK_BG_NORMAL = new Color(0, 0, 0, 0.25f), BLACK_BG_HOVER = new Color(0, 0, 0, 0.5f), BLACK_BG_FOCUS = new Color(0, 0, 0, 0.75f);