Merge remote-tracking branch 'org/master' into ReplayTest

Conflicts:
	src/itdelatrisu/opsu/GameData.java
	src/itdelatrisu/opsu/Options.java
	src/itdelatrisu/opsu/OsuFile.java
	src/itdelatrisu/opsu/OsuGroupList.java
	src/itdelatrisu/opsu/OsuHitObject.java
	src/itdelatrisu/opsu/OsuParser.java
	src/itdelatrisu/opsu/UI.java
	src/itdelatrisu/opsu/db/OsuDB.java
	src/itdelatrisu/opsu/objects/Circle.java
	src/itdelatrisu/opsu/objects/HitObject.java
	src/itdelatrisu/opsu/objects/Slider.java
	src/itdelatrisu/opsu/objects/Spinner.java
	src/itdelatrisu/opsu/states/Game.java
	src/itdelatrisu/opsu/states/Splash.java
This commit is contained in:
fd
2015-06-13 20:28:30 -04:00
88 changed files with 9798 additions and 1575 deletions

View File

@@ -20,17 +20,17 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.OsuGroupNode;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.util.ArrayList;
import java.util.List;
@@ -66,9 +66,9 @@ public class ButtonMenu extends BasicGameState {
BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.DELETE, Button.CANCEL }) {
@Override
public String[] getTitle(GameContainer container, StateBasedGame game) {
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
String osuString = (node != null) ? OsuGroupList.get().getBaseNode(node.index).toString() : "";
return new String[] { osuString, "What do you want to do with this beatmap?" };
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : "";
return new String[] { beatmapString, "What do you want to do with this beatmap?" };
}
@Override
@@ -86,9 +86,9 @@ public class ButtonMenu extends BasicGameState {
BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) {
@Override
public String[] getTitle(GameContainer container, StateBasedGame game) {
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
String osuString = (node != null) ? node.toString() : "";
return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", osuString) };
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
String beatmapString = (node != null) ? node.toString() : "";
return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) };
}
@Override
@@ -314,10 +314,10 @@ public class ButtonMenu extends BasicGameState {
public void draw(GameContainer container, StateBasedGame game, Graphics g) {
// draw title
if (actualTitle != null) {
float c = container.getWidth() * 0.02f;
float marginX = container.getWidth() * 0.015f, marginY = container.getHeight() * 0.01f;
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);
Utils.FONT_LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white);
}
// draw buttons
@@ -451,7 +451,7 @@ public class ButtonMenu extends BasicGameState {
@Override
public void click(GameContainer container, StateBasedGame game) {
SoundController.playSound(SoundEffect.MENUHIT);
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black));
}
@@ -460,8 +460,8 @@ public class ButtonMenu extends BasicGameState {
@Override
public void click(GameContainer container, StateBasedGame game) {
SoundController.playSound(SoundEffect.MENUHIT);
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
MenuState ms = (node.osuFileIndex == -1 || node.osuFiles.size() == 1) ?
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, node);
game.enterState(Opsu.STATE_BUTTONMENU);
@@ -478,7 +478,7 @@ public class ButtonMenu extends BasicGameState {
@Override
public void click(GameContainer container, StateBasedGame game) {
SoundController.playSound(SoundEffect.MENUHIT);
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black));
}
@@ -493,7 +493,7 @@ public class ButtonMenu extends BasicGameState {
@Override
public void click(GameContainer container, StateBasedGame game) {
SoundController.playSound(SoundEffect.MENUHIT);
OsuGroupNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition(Color.black));
}
@@ -582,7 +582,7 @@ public class ButtonMenu extends BasicGameState {
private MenuState menuState;
/** The song node to process in the state. */
private OsuGroupNode node;
private BeatmapSetNode node;
/** The score data to process in the state. */
private ScoreData scoreData;
@@ -691,7 +691,7 @@ public class ButtonMenu extends BasicGameState {
* @param menuState the new menu state
* @param node the song node to process in the state
*/
public void setMenuState(MenuState menuState, OsuGroupNode node) { setMenuState(menuState, node, null); }
public void setMenuState(MenuState menuState, BeatmapSetNode node) { setMenuState(menuState, node, null); }
/**
* Changes the menu state.
@@ -706,7 +706,7 @@ public class ButtonMenu extends BasicGameState {
* @param node the song node to process in the state
* @param scoreData the score scoreData
*/
private void setMenuState(MenuState menuState, OsuGroupNode node, ScoreData scoreData) {
private void setMenuState(MenuState menuState, BeatmapSetNode node, ScoreData scoreData) {
this.menuState = menuState;
this.node = node;
this.scoreData = scoreData;
@@ -715,7 +715,7 @@ public class ButtonMenu extends BasicGameState {
/**
* Returns the song node being processed, or null if none.
*/
private OsuGroupNode getNode() { return node; }
private BeatmapSetNode getNode() { return node; }
/**
* Returns the score data being processed, or null if none.

View File

@@ -19,23 +19,25 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.OsuGroupNode;
import itdelatrisu.opsu.OsuParser;
import itdelatrisu.opsu.OszUnpacker;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.downloads.BloodcatServer;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.DownloadServer;
import itdelatrisu.opsu.downloads.servers.BloodcatServer;
import itdelatrisu.opsu.downloads.servers.DownloadServer;
import itdelatrisu.opsu.downloads.servers.HexideServer;
import itdelatrisu.opsu.downloads.servers.OsuMirrorServer;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.io.IOException;
@@ -71,8 +73,11 @@ public class DownloadsMenu extends BasicGameState {
/** Minimum time, in milliseconds, that must elapse between queries. */
private static final int MIN_REQUEST_INTERVAL = 300;
/** The beatmap download server. */
private DownloadServer server = new BloodcatServer();
/** Available beatmap download servers. */
private static final DownloadServer[] SERVERS = { new BloodcatServer(), new OsuMirrorServer(), new HexideServer() };
/** The beatmap download server index. */
private int serverIndex = 0;
/** The current list of search results. */
private DownloadNode[] resultList;
@@ -138,7 +143,7 @@ public class DownloadsMenu extends BasicGameState {
private MenuButton prevPage, nextPage;
/** Buttons. */
private MenuButton clearButton, importButton, resetButton, rankedButton;
private MenuButton clearButton, importButton, resetButton, rankedButton, serverButton;
/** Beatmap importing thread. */
private Thread importThread;
@@ -169,12 +174,12 @@ public class DownloadsMenu extends BasicGameState {
int width = container.getWidth();
int height = container.getHeight();
float baseX = width * 0.024f;
float searchY = (height * 0.05f) + Utils.FONT_LARGE.getLineHeight();
float searchWidth = width * 0.35f;
float searchY = (height * 0.04f) + Utils.FONT_LARGE.getLineHeight();
float searchWidth = width * 0.3f;
// search
searchTimer = SEARCH_DELAY;
searchResultString = "Type to search!";
searchResultString = "Loading data from server...";
search = new TextField(
container, Utils.FONT_DEFAULT, (int) baseX, (int) searchY,
(int) searchWidth, Utils.FONT_MEDIUM.getLineHeight()
@@ -200,8 +205,10 @@ public class DownloadsMenu extends BasicGameState {
// buttons
float buttonMarginX = width * 0.004f;
float buttonHeight = height * 0.038f;
float topButtonWidth = width * 0.14f;
float lowerButtonWidth = width * 0.12f;
float resetWidth = width * 0.085f;
float rankedWidth = width * 0.15f;
float serverWidth = width * 0.12f;
float lowerWidth = width * 0.12f;
float topButtonY = searchY + Utils.FONT_MEDIUM.getLineHeight() / 2f;
float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f;
Image button = GameImage.MENU_BUTTON_MID.getImage();
@@ -209,25 +216,33 @@ public class DownloadsMenu extends BasicGameState {
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight());
buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight());
Image topButton = button.getScaledCopy((int) topButtonWidth - buttonL.getWidth() - buttonR.getWidth(), (int) buttonHeight);
Image lowerButton = button.getScaledCopy((int) lowerButtonWidth - buttonL.getWidth() - buttonR.getWidth(), (int) buttonHeight);
float fullTopButtonWidth = topButton.getWidth() + buttonL.getWidth() + buttonR.getWidth();
float fullLowerButtonWidth = lowerButton.getWidth() + buttonL.getWidth() + buttonR.getWidth();
clearButton = new MenuButton(lowerButton, buttonL, buttonR,
width * 0.75f + buttonMarginX + fullLowerButtonWidth / 2f, lowerButtonY);
importButton = new MenuButton(lowerButton, buttonL, buttonR,
width - buttonMarginX - fullLowerButtonWidth / 2f, lowerButtonY);
resetButton = new MenuButton(topButton, buttonL, buttonR,
baseX + searchWidth + buttonMarginX + fullTopButtonWidth / 2f, topButtonY);
rankedButton = new MenuButton(topButton, buttonL, buttonR,
baseX + searchWidth + buttonMarginX * 2f + fullTopButtonWidth * 3 / 2f, topButtonY);
int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth();
Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight);
Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight);
Image serverButtonImage = button.getScaledCopy((int) serverWidth - lrButtonWidth, (int) buttonHeight);
Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight);
float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth;
float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth;
float serverButtonWidth = serverButtonImage.getWidth() + lrButtonWidth;
float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth;
clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY);
importButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
width - buttonMarginX - lowerButtonWidth / 2f, lowerButtonY);
resetButton = new MenuButton(resetButtonImage, buttonL, buttonR,
baseX + searchWidth + buttonMarginX + resetButtonWidth / 2f, topButtonY);
rankedButton = new MenuButton(rankedButtonImage, buttonL, buttonR,
baseX + searchWidth + buttonMarginX * 2f + resetButtonWidth + rankedButtonWidth / 2f, topButtonY);
serverButton = new MenuButton(serverButtonImage, buttonL, buttonR,
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth + serverButtonWidth / 2f, topButtonY);
clearButton.setText("Clear", Utils.FONT_MEDIUM, Color.white);
importButton.setText("Import All", Utils.FONT_MEDIUM, Color.white);
resetButton.setText("Reset Search", Utils.FONT_MEDIUM, Color.white);
resetButton.setText("Reset", Utils.FONT_MEDIUM, Color.white);
clearButton.setHoverFade();
importButton.setHoverFade();
resetButton.setHoverFade();
rankedButton.setHoverFade();
serverButton.setHoverFade();
}
@Override
@@ -241,7 +256,7 @@ public class DownloadsMenu extends BasicGameState {
GameImage.SEARCH_BG.getImage().draw();
// title
Utils.FONT_LARGE.drawString(width * 0.024f, height * 0.04f, "Download Beatmaps!", Color.white);
Utils.FONT_LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white);
// search
g.setColor(Color.white);
@@ -317,6 +332,8 @@ public class DownloadsMenu extends BasicGameState {
resetButton.draw(Color.red);
rankedButton.setText((rankedOnly) ? "Show Unranked" : "Hide Unranked", Utils.FONT_MEDIUM, Color.white);
rankedButton.draw(Color.magenta);
serverButton.setText(SERVERS[serverIndex].getName(), Utils.FONT_MEDIUM, Color.white);
serverButton.draw(Color.blue);
// importing beatmaps
if (importThread != null) {
@@ -348,6 +365,7 @@ public class DownloadsMenu extends BasicGameState {
importButton.hoverUpdate(delta, mouseX, mouseY);
resetButton.hoverUpdate(delta, mouseX, mouseY);
rankedButton.hoverUpdate(delta, mouseX, mouseY);
serverButton.hoverUpdate(delta, mouseX, mouseY);
// focus timer
if (focusResult != -1 && focusTimer < FOCUS_DELAY)
@@ -361,7 +379,9 @@ public class DownloadsMenu extends BasicGameState {
searchTimerReset = false;
final String query = search.getText().trim().toLowerCase();
if (lastQuery == null || !query.equals(lastQuery)) {
final DownloadServer server = SERVERS[serverIndex];
if ((lastQuery == null || !query.equals(lastQuery)) &&
(query.length() == 0 || query.length() >= server.minQueryLength())) {
lastQuery = query;
lastQueryDir = pageDir;
@@ -409,7 +429,7 @@ public class DownloadsMenu extends BasicGameState {
else {
if (query.isEmpty())
searchResultString = "Type to search!";
else if (totalResults == 0)
else if (totalResults == 0 || resultList.length == 0)
searchResultString = "No results found.";
else
searchResultString = String.format("%d result%s found!",
@@ -427,6 +447,14 @@ public class DownloadsMenu extends BasicGameState {
queryThread.start();
}
}
// tooltips
if (resetButton.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Reset the current search.", false);
else if (rankedButton.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Toggle the display of unranked maps.\nSome download servers may not support this option.", true);
else if (serverButton.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Select a download server.", false);
}
@Override
@@ -463,7 +491,7 @@ public class DownloadsMenu extends BasicGameState {
final DownloadNode node = nodes[index];
// check if map is already loaded
boolean isLoaded = OsuGroupList.get().containsBeatmapSetID(node.getID());
boolean isLoaded = BeatmapSetList.get().containsBeatmapSetID(node.getID());
// track preview
if (DownloadNode.resultIconContains(x, y, i)) {
@@ -481,7 +509,7 @@ public class DownloadsMenu extends BasicGameState {
} else {
// play preview
try {
final URL url = new URL(server.getPreviewURL(node.getID()));
final URL url = new URL(SERVERS[serverIndex].getPreviewURL(node.getID()));
MusicController.pause();
new Thread() {
@Override
@@ -525,9 +553,13 @@ public class DownloadsMenu extends BasicGameState {
} else {
// start download
if (!DownloadList.get().contains(node.getID())) {
DownloadList.get().addNode(node);
node.createDownload(server);
node.getDownload().start();
node.createDownload(SERVERS[serverIndex]);
if (node.getDownload() == null)
UI.sendBarNotification("The download could not be started.");
else {
DownloadList.get().addNode(node);
node.getDownload().start();
}
}
}
} else {
@@ -584,15 +616,15 @@ public class DownloadsMenu extends BasicGameState {
// invoke unpacker and parser
File[] dirs = OszUnpacker.unpackAllFiles(Options.getOSZDir(), Options.getBeatmapDir());
if (dirs != null && dirs.length > 0) {
OsuGroupNode node = OsuParser.parseDirectories(dirs);
BeatmapSetNode node = BeatmapParser.parseDirectories(dirs);
if (node != null) {
// stop preview
previewID = -1;
SoundController.stopTrack();
// initialize song list
OsuGroupList.get().reset();
OsuGroupList.get().init();
BeatmapSetList.get().reset();
BeatmapSetList.get().init();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(node, -1, true, true);
// send notification
@@ -624,6 +656,22 @@ public class DownloadsMenu extends BasicGameState {
resetSearchTimer();
return;
}
if (serverButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK);
resultList = null;
startResult = 0;
focusResult = -1;
totalResults = 0;
page = 0;
pageResultTotal = 1;
pageDir = Page.RESET;
searchResultString = "Loading data from server...";
serverIndex = (serverIndex + 1) % SERVERS.length;
lastQuery = null;
pageDir = Page.RESET;
resetSearchTimer();
return;
}
// downloads
if (!DownloadList.get().isEmpty() && DownloadNode.downloadAreaContains(x, y)) {
@@ -700,7 +748,7 @@ public class DownloadsMenu extends BasicGameState {
switch (key) {
case Input.KEY_ESCAPE:
if (importThread != null) {
// beatmap importing: stop parsing OsuFiles by sending interrupt to OsuParser
// beatmap importing: stop parsing beatmaps by sending interrupt to BeatmapParser
importThread.interrupt();
} else if (!search.getText().isEmpty()) {
// clear search text
@@ -755,6 +803,7 @@ public class DownloadsMenu extends BasicGameState {
importButton.resetHover();
resetButton.resetHover();
rankedButton.resetHover();
serverButton.resetHover();
focusResult = -1;
startResult = 0;
startDownloadIndex = 0;

View File

@@ -22,29 +22,32 @@ import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameData;
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.OsuHitObject;
import itdelatrisu.opsu.OsuParser;
import itdelatrisu.opsu.OsuTimingPoint;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.HitSound;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.db.OsuDB;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.db.ScoreDB;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.DummyObject;
import itdelatrisu.opsu.objects.HitObject;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
import itdelatrisu.opsu.objects.Spinner;
import itdelatrisu.opsu.objects.curves.Curve;
import itdelatrisu.opsu.render.FrameBufferCache;
import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.replay.ReplayFrame;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.util.LinkedList;
@@ -95,17 +98,17 @@ public class Game extends BasicGameState {
/** Stack position offset modifier. */
private static final float STACK_OFFSET_MODIFIER = 0.05f;
/** The associated OsuFile object. */
private OsuFile osu;
/** The associated beatmap. */
private Beatmap beatmap;
/** The associated GameData object. */
private GameData data;
/** Current hit object index in OsuHitObject[] array. */
/** Current hit object index (in both hit object arrays). */
private int objectIndex = 0;
/** The map's HitObjects, indexed by objectIndex. */
private HitObject[] hitObjects;
/** The map's game objects, indexed by objectIndex. */
private GameObject[] gameObjects;
/** Delay time, in milliseconds, before song starts. */
private int leadInTime;
@@ -211,6 +214,9 @@ public class Game extends BasicGameState {
/** Whether or not the cursor should be pressed using the "auto" mod. */
private boolean autoMousePressed;
/** Playback speed (used in replays and "auto" mod). */
private PlaybackSpeed playbackSpeed;
// game-related variables
private GameContainer container;
private StateBasedGame game;
@@ -254,7 +260,7 @@ public class Game extends BasicGameState {
trackPosition = pauseTime;
else if (deathTime > -1) // "Easy" mod: health bar increasing
trackPosition = deathTime;
int firstObjectTime = osu.objects[0].getTime();
int firstObjectTime = beatmap.objects[0].getTime();
int timeDiff = firstObjectTime - trackPosition;
g.setBackground(Color.black);
@@ -269,11 +275,11 @@ public class Game extends BasicGameState {
float dimLevel = Options.getBackgroundDim();
if (trackPosition < firstObjectTime) {
if (timeDiff < approachTime)
dimLevel += (1f - dimLevel) * ((float) timeDiff / Math.min(approachTime, firstObjectTime));
dimLevel += (1f - dimLevel) * ((float) timeDiff / approachTime);
else
dimLevel = 1f;
}
if (Options.isDefaultPlayfieldForced() || !osu.drawBG(width, height, dimLevel, false)) {
if (Options.isDefaultPlayfieldForced() || !beatmap.drawBG(width, height, dimLevel, false)) {
Image playfield = GameImage.PLAYFIELD.getImage();
playfield.setAlpha(dimLevel);
playfield.draw();
@@ -292,51 +298,51 @@ public class Game extends BasicGameState {
float[] autoXY = null;
if (isLeadIn()) {
// lead-in
float progress = Math.max((float) (leadInTime - osu.audioLeadIn) / approachTime, 0f);
float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f);
autoMouseY = (int) (height / (2f - progress));
} else if (objectIndex == 0 && trackPosition < firstObjectTime) {
// before first object
timeDiff = firstObjectTime - trackPosition;
if (timeDiff < approachTime) {
float[] xy = hitObjects[0].getPointAt(trackPosition);
autoXY = getPointAt(autoMouseX, autoMouseY, xy[0], xy[1], 1f - ((float) timeDiff / Math.min(approachTime, firstObjectTime)));
float[] xy = gameObjects[0].getPointAt(trackPosition);
autoXY = getPointAt(autoMouseX, autoMouseY, xy[0], xy[1], 1f - ((float) timeDiff / approachTime));
}
} else if (objectIndex < osu.objects.length) {
} else if (objectIndex < beatmap.objects.length) {
// normal object
int objectTime = osu.objects[objectIndex].getTime();
int objectTime = beatmap.objects[objectIndex].getTime();
if (trackPosition < objectTime) {
float[] xyStart = hitObjects[objectIndex - 1].getPointAt(trackPosition);
int startTime = hitObjects[objectIndex - 1].getEndTime();
if (osu.breaks != null && breakIndex < osu.breaks.size()) {
float[] xyStart = gameObjects[objectIndex - 1].getPointAt(trackPosition);
int startTime = gameObjects[objectIndex - 1].getEndTime();
if (beatmap.breaks != null && breakIndex < beatmap.breaks.size()) {
// starting a break: keep cursor at previous hit object position
if (breakTime > 0 || objectTime > osu.breaks.get(breakIndex))
if (breakTime > 0 || objectTime > beatmap.breaks.get(breakIndex))
autoXY = xyStart;
// after a break ends: move startTime to break end time
else if (breakIndex > 1) {
int lastBreakEndTime = osu.breaks.get(breakIndex - 1);
int lastBreakEndTime = beatmap.breaks.get(breakIndex - 1);
if (objectTime > lastBreakEndTime && startTime < lastBreakEndTime)
startTime = lastBreakEndTime;
}
}
if (autoXY == null) {
float[] xyEnd = hitObjects[objectIndex].getPointAt(trackPosition);
float[] xyEnd = gameObjects[objectIndex].getPointAt(trackPosition);
int totalTime = objectTime - startTime;
autoXY = getPointAt(xyStart[0], xyStart[1], xyEnd[0], xyEnd[1], (float) (trackPosition - startTime) / totalTime);
// hit circles: show a mouse press
int offset300 = hitResultOffset[GameData.HIT_300];
if ((osu.objects[objectIndex].isCircle() && objectTime - trackPosition < offset300) ||
(osu.objects[objectIndex - 1].isCircle() && trackPosition - osu.objects[objectIndex - 1].getTime() < offset300))
if ((beatmap.objects[objectIndex].isCircle() && objectTime - trackPosition < offset300) ||
(beatmap.objects[objectIndex - 1].isCircle() && trackPosition - beatmap.objects[objectIndex - 1].getTime() < offset300))
autoMousePressed = true;
}
} else {
autoXY = hitObjects[objectIndex].getPointAt(trackPosition);
autoXY = gameObjects[objectIndex].getPointAt(trackPosition);
autoMousePressed = true;
}
} else {
// last object
autoXY = hitObjects[objectIndex - 1].getPointAt(trackPosition);
autoXY = gameObjects[objectIndex - 1].getPointAt(trackPosition);
}
// set mouse coordinates
@@ -388,12 +394,12 @@ public class Game extends BasicGameState {
}
// break periods
if (osu.breaks != null && breakIndex < osu.breaks.size() && breakTime > 0) {
int endTime = osu.breaks.get(breakIndex);
if (beatmap.breaks != null && breakIndex < beatmap.breaks.size() && breakTime > 0) {
int endTime = beatmap.breaks.get(breakIndex);
int breakLength = endTime - breakTime;
// letterbox effect (black bars on top/bottom)
if (osu.letterboxInBreaks && breakLength >= 4000) {
if (beatmap.letterboxInBreaks && breakLength >= 4000) {
g.setColor(Color.black);
g.fillRect(0, 0, width, height * 0.125f);
g.fillRect(0, height * 0.875f, width, height * 0.125f);
@@ -441,7 +447,7 @@ public class Game extends BasicGameState {
// skip beginning
if (objectIndex == 0 &&
trackPosition < osu.objects[0].getTime() - SKIP_OFFSET)
trackPosition < beatmap.objects[0].getTime() - SKIP_OFFSET)
skipButton.draw();
// show retries
@@ -465,40 +471,41 @@ public class Game extends BasicGameState {
trackPosition = (leadInTime - Options.getMusicOffset()) * -1; // render approach circles during song lead-in
// countdown
if (osu.countdown > 0) { // TODO: implement half/double rate settings
if (beatmap.countdown > 0) {
float speedModifier = GameMod.getSpeedMultiplier() * playbackSpeed.getModifier();
timeDiff = firstObjectTime - trackPosition;
if (timeDiff >= 500 && timeDiff < 3000) {
if (timeDiff >= 1500) {
if (timeDiff >= 500 * speedModifier && timeDiff < 3000 * speedModifier) {
if (timeDiff >= 1500 * speedModifier) {
GameImage.COUNTDOWN_READY.getImage().drawCentered(width / 2, height / 2);
if (!countdownReadySound) {
SoundController.playSound(SoundEffect.READY);
countdownReadySound = true;
}
}
if (timeDiff < 2000) {
if (timeDiff < 2000 * speedModifier) {
GameImage.COUNTDOWN_3.getImage().draw(0, 0);
if (!countdown3Sound) {
SoundController.playSound(SoundEffect.COUNT3);
countdown3Sound = true;
}
}
if (timeDiff < 1500) {
if (timeDiff < 1500 * speedModifier) {
GameImage.COUNTDOWN_2.getImage().draw(width - GameImage.COUNTDOWN_2.getImage().getWidth(), 0);
if (!countdown2Sound) {
SoundController.playSound(SoundEffect.COUNT2);
countdown2Sound = true;
}
}
if (timeDiff < 1000) {
if (timeDiff < 1000 * speedModifier) {
GameImage.COUNTDOWN_1.getImage().drawCentered(width / 2, height / 2);
if (!countdown1Sound) {
SoundController.playSound(SoundEffect.COUNT1);
countdown1Sound = true;
}
}
} else if (timeDiff >= -500 && timeDiff < 500) {
} else if (timeDiff >= -500 * speedModifier && timeDiff < 500 * speedModifier) {
Image go = GameImage.COUNTDOWN_GO.getImage();
go.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
go.setAlpha((timeDiff < 0) ? 1 - (timeDiff / speedModifier / -500f) : 1);
go.drawCentered(width / 2, height / 2);
if (!countdownGoSound) {
SoundController.playSound(SoundEffect.GO);
@@ -515,6 +522,10 @@ public class Game extends BasicGameState {
if (GameMod.AUTO.isActive())
GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
// draw replay speed button
if (isReplay || GameMod.AUTO.isActive())
playbackSpeed.getButton().draw();
// returning from pause screen
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
// darken the screen
@@ -539,7 +550,6 @@ public class Game extends BasicGameState {
UI.draw(g, autoMouseX, autoMouseY, Utils.isGameKeyPressed());
else
UI.draw(g);
}
@Override
@@ -548,6 +558,8 @@ public class Game extends BasicGameState {
UI.update(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
skipButton.hoverUpdate(delta, mouseX, mouseY);
if (isReplay || GameMod.AUTO.isActive())
playbackSpeed.getButton().hoverUpdate(delta, mouseX, mouseY);
int trackPosition = MusicController.getPosition();
// returning from pause screen: must click previous mouse position
@@ -660,10 +672,10 @@ public class Game extends BasicGameState {
}
// map complete!
if (objectIndex >= hitObjects.length || (MusicController.trackEnded() && objectIndex > 0)) {
if (objectIndex >= gameObjects.length || (MusicController.trackEnded() && objectIndex > 0)) {
// track ended before last object was processed: force a hit result
if (MusicController.trackEnded() && objectIndex < hitObjects.length)
hitObjects[objectIndex].update(true, delta, mouseX, mouseY, false, trackPosition);
if (MusicController.trackEnded() && objectIndex < gameObjects.length)
gameObjects[objectIndex].update(true, delta, mouseX, mouseY, false, trackPosition);
// if checkpoint used, skip ranking screen
if (checkpointLoaded)
@@ -681,11 +693,11 @@ public class Game extends BasicGameState {
replayFrames.getFirst().setTimeDiff(replaySkipTime * -1);
replayFrames.addFirst(ReplayFrame.getStartFrame(replaySkipTime));
replayFrames.addFirst(ReplayFrame.getStartFrame(0));
Replay r = data.getReplay(replayFrames.toArray(new ReplayFrame[replayFrames.size()]), osu);
Replay r = data.getReplay(replayFrames.toArray(new ReplayFrame[replayFrames.size()]), beatmap);
if (r != null && !unranked)
r.save();
}
ScoreData score = data.getScoreData(osu);
ScoreData score = data.getScoreData(beatmap);
// add score to database
if (!unranked && !isReplay)
@@ -697,23 +709,21 @@ public class Game extends BasicGameState {
}
// timing points
if (timingPointIndex < osu.timingPoints.size()) {
OsuTimingPoint timingPoint = osu.timingPoints.get(timingPointIndex);
if (timingPointIndex < beatmap.timingPoints.size()) {
TimingPoint timingPoint = beatmap.timingPoints.get(timingPointIndex);
if (trackPosition >= timingPoint.getTime()) {
setBeatLength(timingPoint);
HitSound.setDefaultSampleSet(timingPoint.getSampleType());
SoundController.setSampleVolume(timingPoint.getSampleVolume());
setBeatLength(timingPoint, true);
timingPointIndex++;
}
}
// song beginning
if (objectIndex == 0 && trackPosition < osu.objects[0].getTime())
if (objectIndex == 0 && trackPosition < beatmap.objects[0].getTime())
return; // nothing to do here
// break periods
if (osu.breaks != null && breakIndex < osu.breaks.size()) {
int breakValue = osu.breaks.get(breakIndex);
if (beatmap.breaks != null && breakIndex < beatmap.breaks.size()) {
int breakValue = beatmap.breaks.get(breakIndex);
if (breakTime > 0) { // in a break period
if (trackPosition < breakValue)
return;
@@ -765,13 +775,13 @@ public class Game extends BasicGameState {
// update objects (loop in unlikely event of any skipped indexes)
boolean keyPressed = keys != ReplayFrame.KEY_NONE;
while (objectIndex < hitObjects.length && trackPosition > osu.objects[objectIndex].getTime()) {
while (objectIndex < gameObjects.length && trackPosition > beatmap.objects[objectIndex].getTime()) {
// check if we've already passed the next object's start time
boolean overlap = (objectIndex + 1 < hitObjects.length &&
trackPosition > osu.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]);
// update hit object and check completion status
if (hitObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition))
if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition))
objectIndex++; // done, so increment object index
else
break;
@@ -807,7 +817,7 @@ public class Game extends BasicGameState {
}
// pause game
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= osu.objects[0].getTime()) {
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= beatmap.objects[0].getTime()) {
pausedMouseX = mouseX;
pausedMouseY = mouseY;
pausePulse = 0f;
@@ -824,7 +834,7 @@ public class Game extends BasicGameState {
// restart
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
try {
if (trackPosition < osu.objects[0].getTime())
if (trackPosition < beatmap.objects[0].getTime())
retries--; // don't count this retry (cancel out later increment)
restart = Restart.MANUAL;
enter(container, game);
@@ -851,7 +861,7 @@ public class Game extends BasicGameState {
// load checkpoint
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) {
int checkpoint = Options.getCheckpoint();
if (checkpoint == 0 || checkpoint > osu.endTime)
if (checkpoint == 0 || checkpoint > beatmap.endTime)
break; // invalid checkpoint
try {
restart = Restart.MANUAL;
@@ -866,11 +876,12 @@ public class Game extends BasicGameState {
// skip to checkpoint
MusicController.setPosition(checkpoint);
while (objectIndex < hitObjects.length &&
osu.objects[objectIndex++].getTime() <= checkpoint)
MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
while (objectIndex < gameObjects.length &&
beatmap.objects[objectIndex++].getTime() <= checkpoint)
;
objectIndex--;
lastReplayTime = osu.objects[objectIndex].getTime();
lastReplayTime = beatmap.objects[objectIndex].getTime();
} catch (SlickException e) {
ErrorHandler.error("Failed to load checkpoint.", e, false);
}
@@ -896,13 +907,13 @@ public class Game extends BasicGameState {
@Override
public void mousePressed(int button, int x, int y) {
if (Options.isMouseDisabled())
return;
// watching replay
if (isReplay) {
// only allow skip button
if (button != Input.MOUSE_MIDDLE_BUTTON && skipButton.contains(x, y))
if (isReplay || GameMod.AUTO.isActive()) {
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
// skip button
if (skipButton.contains(x, y))
skipIntro();
if(y < 50){
float pos = (float)x / width * osu.endTime;
@@ -912,10 +923,13 @@ public class Game extends BasicGameState {
return;
}
if (Options.isMouseDisabled())
return;
// mouse wheel: pause the game
if (button == Input.MOUSE_MIDDLE_BUTTON && !Options.isMouseWheelDisabled()) {
int trackPosition = MusicController.getPosition();
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= osu.objects[0].getTime()) {
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= beatmap.objects[0].getTime()) {
pausedMouseX = x;
pausedMouseY = y;
pausePulse = 0f;
@@ -1031,8 +1045,11 @@ public class Game extends BasicGameState {
throws SlickException {
UI.enter();
if (osu == null || osu.objects == null)
throw new RuntimeException("Running game with no OsuFile loaded.");
if (beatmap == null || beatmap.objects == null)
throw new RuntimeException("Running game with no beatmap loaded.");
// free all previously cached hitobject to framebuffer mappings if some still exist
FrameBufferCache.getInstance().freeMap();
// grab the mouse (not working for touchscreen)
// container.setMouseGrabbed(true);
@@ -1053,10 +1070,14 @@ public class Game extends BasicGameState {
// reset game data
resetGameData();
// needs to play before setting position to resume without lag later
MusicController.play(false);
MusicController.setPosition(0);
MusicController.pause();
// load the first timingPoint for stacking
if (!beatmap.timingPoints.isEmpty()) {
TimingPoint timingPoint = beatmap.timingPoints.get(0);
if (!timingPoint.isInherited()) {
setBeatLength(timingPoint, true);
timingPointIndex++;
}
}
if (!osu.timingPoints.isEmpty()) {
OsuTimingPoint timingPoint = osu.timingPoints.get(0);
@@ -1067,39 +1088,39 @@ public class Game extends BasicGameState {
hitObjects = new HitObject[osu.objects.length];
// initialize object maps
for (int i = 0; i < osu.objects.length; i++) {
OsuHitObject hitObject = osu.objects[i];
Color[] combo = beatmap.getComboColors();
for (int i = 0; i < beatmap.objects.length; i++) {
HitObject hitObject = beatmap.objects[i];
// is this the last note in the combo?
boolean comboEnd = false;
if (i + 1 >= osu.objects.length || osu.objects[i + 1].isNewCombo())
comboEnd = true;
Color color = osu.combo[hitObject.getComboIndex()];
Color color = combo[hitObject.getComboIndex()];
// pass beatLength to hit objects
int hitObjectTime = hitObject.getTime();
int timingPointIndex = 0;
while (timingPointIndex < osu.timingPoints.size()) {
OsuTimingPoint timingPoint = osu.timingPoints.get(timingPointIndex);
while (timingPointIndex < beatmap.timingPoints.size()) {
TimingPoint timingPoint = beatmap.timingPoints.get(timingPointIndex);
if (timingPoint.getTime() > hitObjectTime)
break;
setBeatLength(timingPoint);
setBeatLength(timingPoint, false);
timingPointIndex++;
}
try {
if (hitObject.isCircle())
hitObjects[i] = new Circle(hitObject, this, data, color, comboEnd);
gameObjects[i] = new Circle(hitObject, this, data, color, comboEnd);
else if (hitObject.isSlider())
hitObjects[i] = new Slider(hitObject, this, data, color, comboEnd);
gameObjects[i] = new Slider(hitObject, this, data, color, comboEnd);
else if (hitObject.isSpinner())
hitObjects[i] = new Spinner(hitObject, this, data);
gameObjects[i] = new Spinner(hitObject, this, data);
} catch (Exception e) {
// try to handle the error gracefully: substitute in a dummy HitObject
// try to handle the error gracefully: substitute in a dummy GameObject
ErrorHandler.error(String.format("Failed to create %s at index %d:\n%s",
hitObject.getTypeName(), i, hitObject.toString()), e, true);
hitObjects[i] = new DummyObject(hitObject);
gameObjects[i] = new DummyObject(hitObject);
continue;
}
}
@@ -1108,19 +1129,19 @@ public class Game extends BasicGameState {
calculateStacks();
// load the first timingPoint
if (!osu.timingPoints.isEmpty()) {
OsuTimingPoint timingPoint = osu.timingPoints.get(0);
timingPointIndex = 0;
beatLengthBase = beatLength = 1;
if (!beatmap.timingPoints.isEmpty()) {
TimingPoint timingPoint = beatmap.timingPoints.get(0);
if (!timingPoint.isInherited()) {
beatLengthBase = beatLength = timingPoint.getBeatLength();
HitSound.setDefaultSampleSet(timingPoint.getSampleType());
SoundController.setSampleVolume(timingPoint.getSampleVolume());
setBeatLength(timingPoint, true);
timingPointIndex++;
}
}
// unhide cursor for "auto" mod and replays
if (GameMod.AUTO.isActive() || isReplay)
UI.showCursor();
UI.getCursor().show();
// load replay frames
if (isReplay) {
@@ -1156,11 +1177,20 @@ public class Game extends BasicGameState {
replayFrames.add(new ReplayFrame(0, 0, input.getMouseX(), input.getMouseY(), 0));
}
leadInTime = osu.audioLeadIn + approachTime;
leadInTime = beatmap.audioLeadIn + approachTime;
restart = Restart.FALSE;
// needs to play before setting position to resume without lag later
MusicController.play(false);
MusicController.setPosition(0);
MusicController.setPitch(GameMod.getSpeedMultiplier());
MusicController.pause();
}
skipButton.resetHover();
if (isReplay || GameMod.AUTO.isActive())
playbackSpeed.getButton().resetHover();
MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
}
@Override
@@ -1170,11 +1200,14 @@ public class Game extends BasicGameState {
// re-hide cursor
if (GameMod.AUTO.isActive() || isReplay)
UI.hideCursor();
UI.getCursor().hide();
// replays
if (isReplay)
GameMod.loadModState(previousMods);
// reset playback speed
MusicController.setPitch(1f);
}
/**
@@ -1185,29 +1218,29 @@ public class Game extends BasicGameState {
private void drawHitObjects(Graphics g, int trackPosition) {
// include previous object in follow points
int lastObjectIndex = -1;
if (objectIndex > 0 && objectIndex < osu.objects.length &&
trackPosition < osu.objects[objectIndex].getTime() && !osu.objects[objectIndex - 1].isSpinner())
if (objectIndex > 0 && objectIndex < beatmap.objects.length &&
trackPosition < beatmap.objects[objectIndex].getTime() && !beatmap.objects[objectIndex - 1].isSpinner())
lastObjectIndex = objectIndex - 1;
// draw hit objects in reverse order, or else overlapping objects are unreadable
Stack<Integer> stack = new Stack<Integer>();
for (int index = objectIndex; index < hitObjects.length && osu.objects[index].getTime() < trackPosition + approachTime; index++) {
for (int index = objectIndex; index < gameObjects.length && beatmap.objects[index].getTime() < trackPosition + approachTime; index++) {
stack.add(index);
// draw follow points
if (!Options.isFollowPointEnabled())
continue;
if (osu.objects[index].isSpinner()) {
if (beatmap.objects[index].isSpinner()) {
lastObjectIndex = -1;
continue;
}
if (lastObjectIndex != -1 && !osu.objects[index].isNewCombo()) {
if (lastObjectIndex != -1 && !beatmap.objects[index].isNewCombo()) {
// calculate points
final int followPointInterval = container.getHeight() / 14;
int lastObjectEndTime = hitObjects[lastObjectIndex].getEndTime() + 1;
int objectStartTime = osu.objects[index].getTime();
float[] startXY = hitObjects[lastObjectIndex].getPointAt(lastObjectEndTime);
float[] endXY = hitObjects[index].getPointAt(objectStartTime);
int lastObjectEndTime = gameObjects[lastObjectIndex].getEndTime() + 1;
int objectStartTime = beatmap.objects[index].getTime();
float[] startXY = gameObjects[lastObjectIndex].getPointAt(lastObjectEndTime);
float[] endXY = gameObjects[index].getPointAt(objectStartTime);
float xDiff = endXY[0] - startXY[0];
float yDiff = endXY[1] - startXY[1];
float dist = (float) Math.hypot(xDiff, yDiff);
@@ -1254,29 +1287,32 @@ public class Game extends BasicGameState {
}
while (!stack.isEmpty())
hitObjects[stack.pop()].draw(g, trackPosition);
gameObjects[stack.pop()].draw(g, trackPosition);
// draw OsuHitObjectResult objects
data.drawHitResults(trackPosition);
}
/**
* Loads all required data from an OsuFile.
* @param osu the OsuFile to load
* Loads all required data from a beatmap.
* @param beatmap the beatmap to load
*/
public void loadOsuFile(OsuFile osu) {
this.osu = osu;
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString()));
if (osu.timingPoints == null || osu.combo == null)
OsuDB.load(osu, OsuDB.LOAD_ARRAY);
OsuParser.parseHitObjects(osu);
HitSound.setDefaultSampleSet(osu.sampleSet);
public void loadBeatmap(Beatmap beatmap) {
this.beatmap = beatmap;
Display.setTitle(String.format("%s - %s", game.getTitle(), beatmap.toString()));
if (beatmap.timingPoints == null)
BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY);
BeatmapParser.parseHitObjects(beatmap);
HitSound.setDefaultSampleSet(beatmap.sampleSet);
}
/**
* Resets all game data and structures.
*/
public void resetGameData() {
//conflict
gameObjects = new GameObject[beatmap.objects.length];
//
data.clear();
objectIndex = 0;
breakIndex = 0;
@@ -1301,16 +1337,17 @@ public class Game extends BasicGameState {
autoMouseY = 0;
autoMousePressed = false;
flashlightRadius = container.getHeight() * 2 / 3;
playbackSpeed = PlaybackSpeed.NORMAL;
System.gc();
}
/**
* Skips the beginning of a track.
* @return true if skipped, false otherwise
* @return {@code true} if skipped, {@code false} otherwise
*/
private synchronized boolean skipIntro() {
int firstObjectTime = osu.objects[0].getTime();
int firstObjectTime = beatmap.objects[0].getTime();
int trackPosition = MusicController.getPosition();
if (objectIndex == 0 && trackPosition < firstObjectTime - SKIP_OFFSET) {
if (isLeadIn()) {
@@ -1318,6 +1355,7 @@ public class Game extends BasicGameState {
MusicController.resume();
}
MusicController.setPosition(firstObjectTime - SKIP_OFFSET);
MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
replaySkipTime = (isReplay) ? -1 : trackPosition;
if (isReplay) {
replayX = (int) skipButton.getX();
@@ -1337,7 +1375,7 @@ public class Game extends BasicGameState {
int height = container.getHeight();
// set images
File parent = osu.getFile().getParentFile();
File parent = beatmap.getFile().getParentFile();
for (GameImage img : GameImage.values()) {
if (img.isSkinnable()) {
img.setDefaultImage();
@@ -1365,26 +1403,11 @@ public class Game extends BasicGameState {
*/
private void setMapModifiers() {
// map-based properties, re-initialized each game
float circleSize = osu.circleSize;
float approachRate = osu.approachRate;
float overallDifficulty = osu.overallDifficulty;
float HPDrainRate = osu.HPDrainRate;
// "Hard Rock" modifiers
if (GameMod.HARD_ROCK.isActive()) {
circleSize = Math.min(circleSize * 1.4f, 10);
approachRate = Math.min(approachRate * 1.4f, 10);
overallDifficulty = Math.min(overallDifficulty * 1.4f, 10);
HPDrainRate = Math.min(HPDrainRate * 1.4f, 10);
}
// "Easy" modifiers
else if (GameMod.EASY.isActive()) {
circleSize /= 2f;
approachRate /= 2f;
overallDifficulty /= 2f;
HPDrainRate /= 2f;
}
float multiplier = GameMod.getDifficultyMultiplier();
float circleSize = Math.min(beatmap.circleSize * multiplier, 10f);
float approachRate = Math.min(beatmap.approachRate * multiplier, 10f);
float overallDifficulty = Math.min(beatmap.overallDifficulty * multiplier, 10f);
float HPDrainRate = Math.min(beatmap.HPDrainRate * multiplier, 10f);
// fixed difficulty overrides
if (Options.getFixedCS() > 0f)
@@ -1399,12 +1422,14 @@ public class Game extends BasicGameState {
// Stack modifier scales with hit object size
// StackOffset = HitObjectRadius / 10
int diameter = (int) (104 - (circleSize * 8));
OsuHitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER);
HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER);
// initialize objects
Circle.init(container, circleSize);
Slider.init(container, circleSize, osu);
Slider.init(container, circleSize, beatmap);
Spinner.init(container);
Curve.init(container.getWidth(), container.getHeight(), circleSize, (Options.isBeatmapSkinIgnored()) ?
Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor());
// approachRate (hit object approach time)
if (approachRate < 5)
@@ -1436,9 +1461,14 @@ public class Game extends BasicGameState {
}
/**
* Sets/returns whether entering the state will restart it.
* Sets the restart state.
* @param restart the new restart state
*/
public void setRestart(Restart restart) { this.restart = restart; }
/**
* Returns the current restart state.
*/
public Restart getRestart() { return restart; }
/**
@@ -1463,12 +1493,18 @@ public class Game extends BasicGameState {
/**
* Sets the beat length fields based on a given timing point.
* @param timingPoint the timing point
* @param setSampleSet whether to set the hit sample set based on the timing point
*/
private void setBeatLength(OsuTimingPoint timingPoint) {
private void setBeatLength(TimingPoint timingPoint, boolean setSampleSet) {
if (!timingPoint.isInherited())
beatLengthBase = beatLength = timingPoint.getBeatLength();
else
beatLength = beatLengthBase * timingPoint.getSliderMultiplier();
if (setSampleSet) {
HitSound.setDefaultSampleSet(timingPoint.getSampleType());
SoundController.setSampleVolume(timingPoint.getSampleVolume());
}
}
/**
@@ -1540,18 +1576,18 @@ public class Game extends BasicGameState {
*/
private void sendGameKeyPress(int keys, int x, int y, int trackPosition) {
System.out.println("Game Key Pressed"+keys+" "+x+" "+y+" "+objectIndex);
if (objectIndex >= hitObjects.length) // nothing to do here
if (objectIndex >= gameObjects.length) // nothing to do here
return;
OsuHitObject hitObject = osu.objects[objectIndex];
HitObject hitObject = beatmap.objects[objectIndex];
// circles
if (hitObject.isCircle() && hitObjects[objectIndex].mousePressed(x, y, trackPosition))
if (hitObject.isCircle() && gameObjects[objectIndex].mousePressed(x, y, trackPosition))
objectIndex++; // circle hit
// sliders
else if (hitObject.isSlider())
hitObjects[objectIndex].mousePressed(x, y, trackPosition);
gameObjects[objectIndex].mousePressed(x, y, trackPosition);
}
/**
@@ -1565,8 +1601,8 @@ public class Game extends BasicGameState {
private ReplayFrame addReplayFrame(int x, int y, int keys, int time) {
int timeDiff = time - lastReplayTime;
lastReplayTime = time;
int cx = (int) ((x - OsuHitObject.getXOffset()) / OsuHitObject.getXMultiplier());
int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier());
int cx = (int) ((x - HitObject.getXOffset()) / HitObject.getXMultiplier());
int cy = (int) ((y - HitObject.getYOffset()) / HitObject.getYMultiplier());
ReplayFrame frame = new ReplayFrame(timeDiff, time, cx, cy, keys);
if (replayFrames != null)
replayFrames.add(frame);
@@ -1603,14 +1639,14 @@ public class Game extends BasicGameState {
return;
int width = container.getWidth(), height = container.getHeight();
boolean firstObject = (objectIndex == 0 && trackPosition < osu.objects[0].getTime());
boolean firstObject = (objectIndex == 0 && trackPosition < beatmap.objects[0].getTime());
if (isLeadIn()) {
// lead-in: expand area
float progress = Math.max((float) (leadInTime - osu.audioLeadIn) / approachTime, 0f);
float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f);
flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress);
} else if (firstObject) {
// before first object: shrink area
int timeDiff = osu.objects[0].getTime() - trackPosition;
int timeDiff = beatmap.objects[0].getTime() - trackPosition;
flashlightRadius = width;
if (timeDiff < approachTime) {
float progress = (float) timeDiff / approachTime;
@@ -1626,10 +1662,10 @@ public class Game extends BasicGameState {
targetRadius = height / 2;
else
targetRadius = height / 3;
if (osu.breaks != null && breakIndex < osu.breaks.size() && breakTime > 0) {
if (beatmap.breaks != null && breakIndex < beatmap.breaks.size() && breakTime > 0) {
// breaks: expand at beginning, shrink at end
flashlightRadius = targetRadius;
int endTime = osu.breaks.get(breakIndex);
int endTime = beatmap.breaks.get(breakIndex);
int breakLength = endTime - breakTime;
if (breakLength > approachTime * 3) {
float progress = 1f;
@@ -1662,8 +1698,8 @@ public class Game extends BasicGameState {
*/
private void calculateStacks() {
// reverse pass for stack calculation
for (int i = hitObjects.length - 1; i > 0; i--) {
OsuHitObject hitObjectI = osu.objects[i];
for (int i = gameObjects.length - 1; i > 0; i--) {
HitObject hitObjectI = beatmap.objects[i];
// already calculated
if (hitObjectI.getStack() != 0 || hitObjectI.isSpinner())
@@ -1671,33 +1707,33 @@ public class Game extends BasicGameState {
// search for hit objects in stack
for (int n = i - 1; n >= 0; n--) {
OsuHitObject hitObjectN = osu.objects[n];
HitObject hitObjectN = beatmap.objects[n];
if (hitObjectN.isSpinner())
continue;
// check if in range stack calculation
float timeI = hitObjectI.getTime() - (STACK_TIMEOUT * osu.stackLeniency);
float timeN = hitObjectN.isSlider() ? hitObjects[n].getEndTime() : hitObjectN.getTime();
float timeI = hitObjectI.getTime() - (STACK_TIMEOUT * beatmap.stackLeniency);
float timeN = hitObjectN.isSlider() ? gameObjects[n].getEndTime() : hitObjectN.getTime();
if (timeI > timeN)
break;
// possible special case: if slider end in the stack,
// all next hit objects in stack move right down
if (hitObjectN.isSlider()) {
float[] p1 = hitObjects[i].getPointAt(hitObjectI.getTime());
float[] p2 = hitObjects[n].getPointAt(hitObjects[n].getEndTime());
float[] p1 = gameObjects[i].getPointAt(hitObjectI.getTime());
float[] p2 = gameObjects[n].getPointAt(gameObjects[n].getEndTime());
float distance = Utils.distance(p1[0], p1[1], p2[0], p2[1]);
// check if hit object part of this stack
if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier()) {
if (distance < STACK_LENIENCE * HitObject.getXMultiplier()) {
int offset = hitObjectI.getStack() - hitObjectN.getStack() + 1;
for (int j = n + 1; j <= i; j++) {
OsuHitObject hitObjectJ = osu.objects[j];
p1 = hitObjects[j].getPointAt(hitObjectJ.getTime());
HitObject hitObjectJ = beatmap.objects[j];
p1 = gameObjects[j].getPointAt(hitObjectJ.getTime());
distance = Utils.distance(p1[0], p1[1], p2[0], p2[1]);
// hit object below slider end
if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier())
if (distance < STACK_LENIENCE * HitObject.getXMultiplier())
hitObjectJ.setStack(hitObjectJ.getStack() - offset);
}
break; // slider end always start of the stack: reset calculation
@@ -1717,9 +1753,9 @@ public class Game extends BasicGameState {
}
// update hit object positions
for (int i = 0; i < hitObjects.length; i++) {
if (osu.objects[i].getStack() != 0)
hitObjects[i].updatePosition();
for (int i = 0; i < gameObjects.length; i++) {
if (beatmap.objects[i].getStack() != 0)
gameObjects[i].updatePosition();
}
}
}

View File

@@ -19,14 +19,14 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color;
@@ -132,8 +132,9 @@ public class GamePauseMenu extends BasicGameState {
if (gameState.getRestart() == Game.Restart.LOSE) {
SoundController.playSound(SoundEffect.MENUBACK);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
MusicController.playAt(MusicController.getOsuFile().previewTime, true);
UI.resetCursor();
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
if (UI.getCursor().isSkinned())
UI.getCursor().reset();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} else {
SoundController.playSound(SoundEffect.MENUBACK);
@@ -183,10 +184,11 @@ public class GamePauseMenu extends BasicGameState {
SoundController.playSound(SoundEffect.MENUBACK);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
if (loseState)
MusicController.playAt(MusicController.getOsuFile().previewTime, true);
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
else
MusicController.resume();
UI.resetCursor();
if (UI.getCursor().isSkinned())
UI.getCursor().reset();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
}
}

View File

@@ -20,16 +20,16 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuFile;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -102,14 +102,14 @@ public class GameRanking extends BasicGameState {
int width = container.getWidth();
int height = container.getHeight();
OsuFile osu = MusicController.getOsuFile();
Beatmap beatmap = MusicController.getBeatmap();
// background
if (!osu.drawBG(width, height, 0.7f, true))
if (!beatmap.drawBG(width, height, 0.7f, true))
GameImage.PLAYFIELD.getImage().draw(0,0);
// ranking screen elements
data.drawRankingElements(g, osu);
data.drawRankingElements(g, beatmap);
// buttons
replayButton.draw();
@@ -201,8 +201,8 @@ public class GameRanking extends BasicGameState {
}
if (returnToGame) {
OsuFile osu = MusicController.getOsuFile();
gameState.loadOsuFile(osu);
Beatmap beatmap = MusicController.getBeatmap();
gameState.loadBeatmap(beatmap);
SoundController.playSound(SoundEffect.MENUHIT);
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
return;
@@ -244,7 +244,8 @@ public class GameRanking extends BasicGameState {
songMenu.resetGameDataOnLoad();
songMenu.resetTrackOnLoad();
}
UI.resetCursor();
if (UI.getCursor().isSkinned())
UI.getCursor().reset();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
}

View File

@@ -20,20 +20,20 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.MenuButton.Expand;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuFile;
import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.OsuGroupNode;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.ui.MenuButton.Expand;
import java.awt.Desktop;
import java.io.IOException;
@@ -91,7 +91,7 @@ public class MainMenu extends BasicGameState {
private MenuButton updateButton;
/** Application start time, for drawing the total running time. */
private long osuStartTime;
private long programStartTime;
/** Indexes of previous songs. */
private Stack<Integer> previous;
@@ -127,7 +127,7 @@ public class MainMenu extends BasicGameState {
this.game = game;
this.input = container.getInput();
osuStartTime = System.currentTimeMillis();
programStartTime = System.currentTimeMillis();
previous = new Stack<Integer>();
int width = container.getWidth();
@@ -199,9 +199,9 @@ public class MainMenu extends BasicGameState {
int height = container.getHeight();
// draw background
OsuFile osu = MusicController.getOsuFile();
Beatmap beatmap = MusicController.getBeatmap();
if (Options.isDynamicBackgroundEnabled() &&
osu != null && osu.drawBG(width, height, bgAlpha, true))
beatmap != null && beatmap.drawBG(width, height, bgAlpha, true))
;
else {
Image bg = GameImage.MENU_BG.getImage();
@@ -240,7 +240,7 @@ public class MainMenu extends BasicGameState {
g.setColor((musicPositionBarContains(mouseX, mouseY)) ? BG_HOVER : BG_NORMAL);
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4);
g.setColor(Color.white);
if (!MusicController.isTrackLoading() && osu != null) {
if (!MusicController.isTrackLoading() && beatmap != null) {
float musicBarPosition = Math.min((float) MusicController.getPosition() / MusicController.getDuration(), 1f);
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth * musicBarPosition, musicBarHeight, 4);
}
@@ -270,25 +270,25 @@ public class MainMenu extends BasicGameState {
}
// draw text
float marginX = width * 0.015f, marginY = height * 0.015f;
float marginX = width * 0.015f, topMarginY = height * 0.01f, bottomMarginY = height * 0.015f;
g.setFont(Utils.FONT_MEDIUM);
int lineHeight = Utils.FONT_MEDIUM.getLineHeight() * 9 / 10;
float lineHeight = Utils.FONT_MEDIUM.getLineHeight() * 0.925f;
g.drawString(String.format("Loaded %d songs and %d beatmaps.",
OsuGroupList.get().getMapSetCount(), OsuGroupList.get().getMapCount()), marginX, marginY);
BeatmapSetList.get().getMapSetCount(), BeatmapSetList.get().getMapCount()), marginX, topMarginY);
if (MusicController.isTrackLoading())
g.drawString("Track loading...", marginX, marginY + lineHeight);
g.drawString("Track loading...", marginX, topMarginY + lineHeight);
else if (MusicController.trackExists()) {
if (Options.useUnicodeMetadata()) // load glyphs
Utils.loadGlyphs(Utils.FONT_MEDIUM, osu.titleUnicode, osu.artistUnicode);
g.drawString((MusicController.isPlaying()) ? "Now Playing:" : "Paused:", marginX, marginY + lineHeight);
g.drawString(String.format("%s: %s", osu.getArtist(), osu.getTitle()), marginX + 25, marginY + (lineHeight * 2));
Utils.loadGlyphs(Utils.FONT_MEDIUM, beatmap.titleUnicode, beatmap.artistUnicode);
g.drawString((MusicController.isPlaying()) ? "Now Playing:" : "Paused:", marginX, topMarginY + lineHeight);
g.drawString(String.format("%s: %s", beatmap.getArtist(), beatmap.getTitle()), marginX + 25, topMarginY + (lineHeight * 2));
}
g.drawString(String.format("opsu! has been running for %s.",
Utils.getTimeString((int) (System.currentTimeMillis() - osuStartTime) / 1000)),
marginX, height - marginY - (lineHeight * 2));
Utils.getTimeString((int) (System.currentTimeMillis() - programStartTime) / 1000)),
marginX, height - bottomMarginY - (lineHeight * 2));
g.drawString(String.format("It is currently %s.",
new SimpleDateFormat("h:mm a").format(new Date())),
marginX, height - marginY - lineHeight);
marginX, height - bottomMarginY - lineHeight);
UI.draw(g);
}
@@ -455,7 +455,7 @@ public class MainMenu extends BasicGameState {
} else if (musicPrevious.contains(x, y)) {
if (!previous.isEmpty()) {
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
menu.setFocus(OsuGroupList.get().getBaseNode(previous.pop()), -1, true, false);
menu.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
if (Options.isDynamicBackgroundEnabled())
bgAlpha = 0f;
} else
@@ -603,10 +603,10 @@ public class MainMenu extends BasicGameState {
private void nextTrack() {
boolean isTheme = MusicController.isThemePlaying();
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
OsuGroupNode node = menu.setFocus(OsuGroupList.get().getRandomNode(), -1, true, false);
BeatmapSetNode node = menu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
boolean sameAudio = false;
if (node != null) {
sameAudio = MusicController.getOsuFile().audioFilename.equals(node.osuFiles.get(0).audioFilename);
sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename);
if (!isTheme && !sameAudio)
previous.add(node.index);
}
@@ -619,7 +619,7 @@ public class MainMenu extends BasicGameState {
*/
private void enterSongMenu() {
int state = Opsu.STATE_SONGMENU;
if (OsuGroupList.get().getMapSetCount() == 0) {
if (BeatmapSetList.get().getMapSetCount() == 0) {
((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!");
state = Opsu.STATE_DOWNLOADSMENU;
}

View File

@@ -19,15 +19,15 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MenuButton;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Options.GameOption;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.util.Arrays;
import java.util.Collections;
@@ -52,12 +52,14 @@ public class OptionsMenu extends BasicGameState {
DISPLAY ("Display", new GameOption[] {
GameOption.SCREEN_RESOLUTION,
// GameOption.FULLSCREEN,
GameOption.SKIN,
GameOption.TARGET_FPS,
GameOption.SHOW_FPS,
GameOption.SHOW_UNICODE,
GameOption.SCREENSHOT_FORMAT,
GameOption.NEW_CURSOR,
GameOption.DYNAMIC_BACKGROUND,
GameOption.LOAD_HD_IMAGES,
GameOption.LOAD_VERBOSE
}),
MUSIC ("Music", new GameOption[] {
@@ -90,8 +92,7 @@ public class OptionsMenu extends BasicGameState {
GameOption.FIXED_HP,
GameOption.FIXED_AR,
GameOption.FIXED_OD,
GameOption.CHECKPOINT,
GameOption.LOAD_HD_IMAGES
GameOption.CHECKPOINT
});
/** Total number of tabs. */
@@ -180,9 +181,9 @@ public class OptionsMenu extends BasicGameState {
// option tabs
Image tabImage = GameImage.MENU_TAB.getImage();
float tabX = (width / 50) + (tabImage.getWidth() / 2f);
float tabY = Utils.FONT_LARGE.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() +
height * 0.03f + (tabImage.getHeight() / 2f);
float tabX = width * 0.032f + Utils.FONT_DEFAULT.getWidth("Change the way opsu! behaves") + (tabImage.getWidth() / 2);
float tabY = Utils.FONT_XLARGE.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() +
height * 0.015f - (tabImage.getHeight() / 2f);
int tabOffset = Math.min(tabImage.getWidth(), width / OptionTab.SIZE);
for (OptionTab tab : OptionTab.values())
tab.button = new MenuButton(tabImage, tabX + (tab.ordinal() * tabOffset), tabY);
@@ -201,12 +202,16 @@ public class OptionsMenu extends BasicGameState {
int width = container.getWidth();
int height = container.getHeight();
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
float lineY = OptionTab.DISPLAY.button.getY() + (GameImage.MENU_TAB.getImage().getHeight() / 2f);
// title
float c = container.getWidth() * 0.02f;
Utils.FONT_LARGE.drawString(c, c, "Game Options", Color.white);
Utils.FONT_DEFAULT.drawString(c, c + Utils.FONT_LARGE.getLineHeight() * 0.9f,
"Click or drag an option to change it.", Color.white);
float marginX = width * 0.015f, marginY = height * 0.01f;
Utils.FONT_XLARGE.drawString(marginX, marginY, "Options", Color.white);
Utils.FONT_DEFAULT.drawString(marginX, marginY + Utils.FONT_XLARGE.getLineHeight() * 0.92f,
"Change the way opsu! behaves", Color.white);
// background
GameImage.OPTIONS_BG.getImage().draw(0, lineY);
// game options
g.setLineWidth(1f);
@@ -235,7 +240,6 @@ public class OptionsMenu extends BasicGameState {
currentTab.getName(), true, false);
g.setColor(Color.white);
g.setLineWidth(2f);
float lineY = OptionTab.DISPLAY.button.getY() + (GameImage.MENU_TAB.getImage().getHeight() / 2f);
g.drawLine(0, lineY, width, lineY);
g.resetLineWidth();
@@ -302,7 +306,7 @@ public class OptionsMenu extends BasicGameState {
// options (click only)
GameOption option = getOptionAt(y);
if (option != GameOption.NULL)
if (option != null)
option.click(container);
// special key entry states
@@ -338,7 +342,7 @@ public class OptionsMenu extends BasicGameState {
// options (drag only)
GameOption option = getOptionAt(oldy);
if (option != GameOption.NULL)
if (option != null)
option.drag(container, diff);
}
@@ -425,14 +429,13 @@ public class OptionsMenu extends BasicGameState {
* @return the option, or GameOption.NULL if no such option exists
*/
private GameOption getOptionAt(int y) {
GameOption option = GameOption.NULL;
if (y < textY || y > textY + (offsetY * maxOptionsScreen))
return option;
return null;
int index = (y - textY + Utils.FONT_LARGE.getLineHeight()) / offsetY;
if (index < currentTab.options.length)
option = currentTab.options[index];
return option;
if (index >= currentTab.options.length)
return null;
return currentTab.options[index];
}
}

View File

@@ -22,25 +22,25 @@ import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.Grade;
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.ScoreData;
import itdelatrisu.opsu.SongSort;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MultiClip;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.db.OsuDB;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.beatmap.BeatmapSortOrder;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.db.ScoreDB;
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.util.Map;
@@ -91,28 +91,28 @@ public class SongMenu extends BasicGameState {
/** Line width of the header/footer divider. */
private static final int DIVIDER_LINE_WIDTH = 4;
/** Song node class representing an OsuGroupNode and file index. */
/** Song node class representing an BeatmapSetNode and file index. */
private static class SongNode {
/** Song node. */
private OsuGroupNode node;
private BeatmapSetNode node;
/** File index. */
private int index;
/**
* Constructor.
* @param node the OsuGroupNode
* @param node the BeatmapSetNode
* @param index the file index
*/
public SongNode(OsuGroupNode node, int index) {
public SongNode(BeatmapSetNode node, int index) {
this.node = node;
this.index = index;
}
/**
* Returns the associated OsuGroupNode.
* Returns the associated BeatmapSetNode.
*/
public OsuGroupNode getNode() { return node; }
public BeatmapSetNode getNode() { return node; }
/**
* Returns the associated file index.
@@ -121,10 +121,10 @@ public class SongMenu extends BasicGameState {
}
/** Current start node (topmost menu entry). */
private OsuGroupNode startNode;
private BeatmapSetNode startNode;
/** Current focused (selected) node. */
private OsuGroupNode focusNode;
private BeatmapSetNode focusNode;
/** The base node of the previous focus node. */
private SongNode oldFocusNode = null;
@@ -172,7 +172,7 @@ public class SongMenu extends BasicGameState {
private MenuState stateAction;
/** If non-null, the node that stateAction acts upon. */
private OsuGroupNode stateActionNode;
private BeatmapSetNode stateActionNode;
/** If non-null, the score data that stateAction acts upon. */
private ScoreData stateActionScore;
@@ -228,7 +228,7 @@ public class SongMenu extends BasicGameState {
footerY = height - GameImage.SELECTION_MODS.getImage().getHeight();
// initialize sorts
for (SongSort sort : SongSort.values())
for (BeatmapSortOrder sort : BeatmapSortOrder.values())
sort.init(width, headerY - SongMenu.DIVIDER_LINE_WIDTH / 2);
// initialize score data buttons
@@ -292,13 +292,13 @@ public class SongMenu extends BasicGameState {
// background
if (focusNode != null) {
OsuFile focusNodeOsu = focusNode.osuFiles.get(focusNode.osuFileIndex);
if (!focusNodeOsu.drawBG(width, height, 1.0f, true))
Beatmap focusNodeBeatmap = focusNode.getBeatmapSet().get(focusNode.beatmapIndex);
if (!focusNodeBeatmap.drawBG(width, height, 1.0f, true))
GameImage.PLAYFIELD.getImage().draw();
}
// song buttons
OsuGroupNode node = startNode;
BeatmapSetNode node = startNode;
int songButtonIndex = 0;
if (node != null && node.prev != null) {
node = node.prev;
@@ -339,21 +339,27 @@ public class SongMenu extends BasicGameState {
if (songInfo == null) {
songInfo = focusNode.getInfo();
if (Options.useUnicodeMetadata()) { // load glyphs
OsuFile osu = focusNode.osuFiles.get(0);
Utils.loadGlyphs(Utils.FONT_LARGE, osu.titleUnicode, osu.artistUnicode);
Beatmap beatmap = focusNode.getBeatmapSet().get(0);
Utils.loadGlyphs(Utils.FONT_LARGE, beatmap.titleUnicode, beatmap.artistUnicode);
}
}
marginX += 5;
float headerTextY = marginY;
float headerTextY = marginY * 0.2f;
Utils.FONT_LARGE.drawString(marginX + iconWidth * 1.05f, headerTextY, songInfo[0], Color.white);
headerTextY += Utils.FONT_LARGE.getLineHeight() - 8;
headerTextY += Utils.FONT_LARGE.getLineHeight() - 6;
Utils.FONT_DEFAULT.drawString(marginX + iconWidth * 1.05f, headerTextY, songInfo[1], Color.white);
headerTextY += Utils.FONT_DEFAULT.getLineHeight() - 2;
Utils.FONT_BOLD.drawString(marginX, headerTextY, songInfo[2], Color.white);
float speedModifier = GameMod.getSpeedMultiplier();
Color color2 = (speedModifier == 1f) ? Color.white :
(speedModifier > 1f) ? Utils.COLOR_RED_HIGHLIGHT : Utils.COLOR_BLUE_HIGHLIGHT;
Utils.FONT_BOLD.drawString(marginX, headerTextY, songInfo[2], color2);
headerTextY += Utils.FONT_BOLD.getLineHeight() - 4;
Utils.FONT_DEFAULT.drawString(marginX, headerTextY, songInfo[3], Color.white);
headerTextY += Utils.FONT_DEFAULT.getLineHeight() - 4;
Utils.FONT_SMALL.drawString(marginX, headerTextY, songInfo[4], Color.white);
float multiplier = GameMod.getDifficultyMultiplier();
Color color4 = (multiplier == 1f) ? Color.white :
(multiplier > 1f) ? Utils.COLOR_RED_HIGHLIGHT : Utils.COLOR_BLUE_HIGHLIGHT;
Utils.FONT_SMALL.drawString(marginX, headerTextY, songInfo[4], color4);
}
// score buttons
@@ -382,15 +388,15 @@ public class SongMenu extends BasicGameState {
selectOptionsButton.draw();
// sorting tabs
SongSort currentSort = SongSort.getSort();
SongSort hoverSort = null;
for (SongSort sort : SongSort.values()) {
BeatmapSortOrder currentSort = BeatmapSortOrder.getSort();
BeatmapSortOrder hoverSort = null;
for (BeatmapSortOrder sort : BeatmapSortOrder.values()) {
if (sort.contains(mouseX, mouseY)) {
hoverSort = sort;
break;
}
}
for (SongSort sort : SongSort.VALUES_REVERSED) {
for (BeatmapSortOrder sort : BeatmapSortOrder.VALUES_REVERSED) {
if (sort != currentSort)
sort.draw(false, sort == hoverSort);
}
@@ -431,14 +437,14 @@ public class SongMenu extends BasicGameState {
// scroll bar
if (focusNode != null) {
int focusNodes = focusNode.osuFiles.size();
int totalNodes = OsuGroupList.get().size() + focusNodes - 1;
int focusNodes = focusNode.getBeatmapSet().size();
int totalNodes = BeatmapSetList.get().size() + focusNodes - 1;
if (totalNodes > MAX_SONG_BUTTONS) {
int startIndex = startNode.index;
if (startNode.index > focusNode.index)
startIndex += focusNodes;
else if (startNode.index == focusNode.index)
startIndex += startNode.osuFileIndex;
startIndex += startNode.beatmapIndex;
UI.drawScrollbar(g, startIndex, totalNodes, MAX_SONG_BUTTONS,
width, headerY + DIVIDER_LINE_WIDTH / 2, 0, buttonOffset - DIVIDER_LINE_WIDTH * 1.5f, buttonOffset,
Utils.COLOR_BLACK_ALPHA, Color.white, true);
@@ -495,9 +501,9 @@ public class SongMenu extends BasicGameState {
// store the start/focus nodes
if (focusNode != null)
oldFocusNode = new SongNode(OsuGroupList.get().getBaseNode(focusNode.index), focusNode.osuFileIndex);
oldFocusNode = new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex);
if (OsuGroupList.get().search(search.getText())) {
if (BeatmapSetList.get().search(search.getText())) {
// reset song stack
randomStack = new Stack<SongNode>();
@@ -509,19 +515,19 @@ public class SongMenu extends BasicGameState {
startNode = focusNode = null;
scoreMap = null;
focusScores = null;
if (OsuGroupList.get().size() > 0) {
OsuGroupList.get().init();
if (BeatmapSetList.get().size() > 0) {
BeatmapSetList.get().init();
if (search.getText().isEmpty()) { // cleared search
// use previous start/focus if possible
if (oldFocusNode != null)
setFocus(oldFocusNode.getNode(), oldFocusNode.getIndex(), true, true);
else
setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} else {
int size = OsuGroupList.get().size();
int size = BeatmapSetList.get().size();
searchResultString = String.format("%d match%s found!",
size, (size == 1) ? "" : "es");
setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
}
oldFocusNode = null;
} else if (!search.getText().isEmpty())
@@ -549,9 +555,9 @@ public class SongMenu extends BasicGameState {
// mouse hover
boolean isHover = false;
if (mouseY > headerY && mouseY < footerY) {
OsuGroupNode node = startNode;
BeatmapSetNode node = startNode;
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
float cx = (node.index == OsuGroupList.get().getExpandedIndex()) ? buttonX * 0.9f : buttonX;
float cx = (node.index == BeatmapSetList.get().getExpandedIndex()) ? buttonX * 0.9f : buttonX;
if ((mouseX > cx && mouseX < cx + buttonWidth) &&
(mouseY > buttonY + (i * buttonOffset) && mouseY < buttonY + (i * buttonOffset) + buttonHeight)) {
if (i == hoverIndex) {
@@ -630,15 +636,15 @@ public class SongMenu extends BasicGameState {
return;
// sorting buttons
for (SongSort sort : SongSort.values()) {
for (BeatmapSortOrder sort : BeatmapSortOrder.values()) {
if (sort.contains(x, y)) {
if (sort != SongSort.getSort()) {
SongSort.setSort(sort);
if (sort != BeatmapSortOrder.getSort()) {
BeatmapSortOrder.setSort(sort);
SoundController.playSound(SoundEffect.MENUCLICK);
OsuGroupNode oldFocusBase = OsuGroupList.get().getBaseNode(focusNode.index);
int oldFocusFileIndex = focusNode.osuFileIndex;
BeatmapSetNode oldFocusBase = BeatmapSetList.get().getBaseNode(focusNode.index);
int oldFocusFileIndex = focusNode.beatmapIndex;
focusNode = null;
OsuGroupList.get().init();
BeatmapSetList.get().init();
setFocus(oldFocusBase, oldFocusFileIndex, true, true);
}
return;
@@ -647,8 +653,8 @@ public class SongMenu extends BasicGameState {
// song buttons
if (y > headerY && y < footerY) {
int expandedIndex = OsuGroupList.get().getExpandedIndex();
OsuGroupNode node = startNode;
int expandedIndex = BeatmapSetList.get().getExpandedIndex();
BeatmapSetNode node = startNode;
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
// is button at this index clicked?
float cx = (node.index == expandedIndex) ? buttonX * 0.9f : buttonX;
@@ -659,7 +665,7 @@ public class SongMenu extends BasicGameState {
// clicked node is already expanded
if (node.index == expandedIndex) {
if (node.osuFileIndex == focusNode.osuFileIndex) {
if (node.beatmapIndex == focusNode.beatmapIndex) {
// if already focused, load the beatmap
if (button != Input.MOUSE_RIGHT_BUTTON)
startGame();
@@ -724,7 +730,7 @@ public class SongMenu extends BasicGameState {
switch (key) {
case Input.KEY_ESCAPE:
if (reloadThread != null) {
// beatmap reloading: stop parsing OsuFiles by sending interrupt to OsuParser
// beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser
reloadThread.interrupt();
} else if (!search.getText().isEmpty()) {
// clear search text
@@ -755,8 +761,8 @@ public class SongMenu extends BasicGameState {
setFocus(prev.getNode(), prev.getIndex(), true, true);
} else {
// random track, add previous to stack
randomStack.push(new SongNode(OsuGroupList.get().getBaseNode(focusNode.index), focusNode.osuFileIndex));
setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
randomStack.push(new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex));
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
}
break;
case Input.KEY_F3:
@@ -776,7 +782,7 @@ public class SongMenu extends BasicGameState {
break;
if (input.isKeyDown(Input.KEY_RSHIFT) || input.isKeyDown(Input.KEY_LSHIFT)) {
SoundController.playSound(SoundEffect.MENUHIT);
MenuState ms = (focusNode.osuFileIndex == -1 || focusNode.osuFiles.size() == 1) ?
MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU);
@@ -810,10 +816,10 @@ public class SongMenu extends BasicGameState {
case Input.KEY_RIGHT:
if (focusNode == null)
break;
OsuGroupNode next = focusNode.next;
BeatmapSetNode next = focusNode.next;
if (next != null) {
SoundController.playSound(SoundEffect.MENUCLICK);
OsuGroupNode oldStartNode = startNode;
BeatmapSetNode oldStartNode = startNode;
float oldHoverOffset = hoverOffset;
int oldHoverIndex = hoverIndex;
setFocus(next, 0, false, true);
@@ -826,13 +832,13 @@ public class SongMenu extends BasicGameState {
case Input.KEY_LEFT:
if (focusNode == null)
break;
OsuGroupNode prev = focusNode.prev;
BeatmapSetNode prev = focusNode.prev;
if (prev != null) {
SoundController.playSound(SoundEffect.MENUCLICK);
OsuGroupNode oldStartNode = startNode;
BeatmapSetNode oldStartNode = startNode;
float oldHoverOffset = hoverOffset;
int oldHoverIndex = hoverIndex;
setFocus(prev, (prev.index == focusNode.index) ? 0 : prev.osuFiles.size() - 1, false, true);
setFocus(prev, (prev.index == focusNode.index) ? 0 : prev.getBeatmapSet().size() - 1, false, true);
if (startNode == oldStartNode) {
hoverOffset = oldHoverOffset;
hoverIndex = oldHoverIndex;
@@ -937,18 +943,19 @@ public class SongMenu extends BasicGameState {
startScore = 0;
beatmapMenuTimer = -1;
searchTransitionTimer = SEARCH_TRANSITION_TIME;
songInfo = null;
// 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)
setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
if (focusNode == null && BeatmapSetList.get().size() > 0)
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
// reset music track
else if (resetTrack) {
MusicController.pause();
MusicController.playAt(MusicController.getOsuFile().previewTime, true);
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
resetTrack = false;
}
@@ -975,7 +982,7 @@ public class SongMenu extends BasicGameState {
// reload scores
if (focusNode != null) {
scoreMap = ScoreDB.getMapSetScores(focusNode.osuFiles.get(focusNode.osuFileIndex));
scoreMap = ScoreDB.getMapSetScores(focusNode.getBeatmapSet().get(focusNode.beatmapIndex));
focusScores = getScoreDataForNode(focusNode, true);
}
@@ -986,31 +993,31 @@ public class SongMenu extends BasicGameState {
if (stateAction != null) {
switch (stateAction) {
case BEATMAP: // clear all scores
if (stateActionNode == null || stateActionNode.osuFileIndex == -1)
if (stateActionNode == null || stateActionNode.beatmapIndex == -1)
break;
OsuFile osu = stateActionNode.osuFiles.get(stateActionNode.osuFileIndex);
ScoreDB.deleteScore(osu);
Beatmap beatmap = stateActionNode.getBeatmapSet().get(stateActionNode.beatmapIndex);
ScoreDB.deleteScore(beatmap);
if (stateActionNode == focusNode) {
focusScores = null;
scoreMap.remove(osu.version);
scoreMap.remove(beatmap.version);
}
break;
case SCORE: // clear single score
if (stateActionScore == null)
break;
ScoreDB.deleteScore(stateActionScore);
scoreMap = ScoreDB.getMapSetScores(focusNode.osuFiles.get(focusNode.osuFileIndex));
scoreMap = ScoreDB.getMapSetScores(focusNode.getBeatmapSet().get(focusNode.beatmapIndex));
focusScores = getScoreDataForNode(focusNode, true);
startScore = 0;
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);
BeatmapSetNode
prev = BeatmapSetList.get().getBaseNode(stateActionNode.index - 1),
next = BeatmapSetList.get().getBaseNode(stateActionNode.index + 1);
int oldIndex = stateActionNode.index, focusNodeIndex = focusNode.index, startNodeIndex = startNode.index;
OsuGroupList.get().deleteSongGroup(stateActionNode);
BeatmapSetList.get().deleteSongGroup(stateActionNode);
if (oldIndex == focusNodeIndex) {
if (prev != null)
setFocus(prev, -1, true, true);
@@ -1039,7 +1046,7 @@ public class SongMenu extends BasicGameState {
if (stateActionNode == null)
break;
int index = stateActionNode.index;
OsuGroupList.get().deleteSong(stateActionNode);
BeatmapSetList.get().deleteSong(stateActionNode);
if (stateActionNode == focusNode) {
if (stateActionNode.prev != null &&
!(stateActionNode.next != null && stateActionNode.next.index == index)) {
@@ -1081,17 +1088,17 @@ public class SongMenu extends BasicGameState {
@Override
public void run() {
// clear the beatmap cache
OsuDB.clearDatabase();
BeatmapDB.clearDatabase();
// invoke unpacker and parser
File beatmapDir = Options.getBeatmapDir();
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
OsuParser.parseAllFiles(beatmapDir);
BeatmapParser.parseAllFiles(beatmapDir);
// initialize song list
if (OsuGroupList.get().size() > 0) {
OsuGroupList.get().init();
setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
if (BeatmapSetList.get().size() > 0) {
BeatmapSetList.get().init();
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} else
MusicController.playThemeSong();
@@ -1139,7 +1146,7 @@ public class SongMenu extends BasicGameState {
n++;
shifted = true;
} else if (n > 0 && startNode.next != null &&
OsuGroupList.get().getNode(startNode, MAX_SONG_BUTTONS) != null) {
BeatmapSetList.get().getNode(startNode, MAX_SONG_BUTTONS) != null) {
startNode = startNode.next;
buttonY -= buttonOffset / 4;
if (buttonY < headerY - height * 0.02f)
@@ -1159,69 +1166,69 @@ public class SongMenu extends BasicGameState {
/**
* Sets a new focus node.
* @param node the base node; it will be expanded if it isn't already
* @param osuFileIndex the OsuFile element to focus; if out of bounds, it will be randomly chosen
* @param beatmapIndex the beatmap element to focus; if out of bounds, it will be randomly chosen
* @param changeStartNode if true, startNode will be set to the first node in the group
* @param preview whether to start at the preview time (true) or beginning (false)
* @return the old focus node
*/
public OsuGroupNode setFocus(OsuGroupNode node, int osuFileIndex, boolean changeStartNode, boolean preview) {
public BeatmapSetNode setFocus(BeatmapSetNode node, int beatmapIndex, boolean changeStartNode, boolean preview) {
if (node == null)
return null;
hoverOffset = 0f;
hoverIndex = -1;
songInfo = null;
OsuGroupNode oldFocus = focusNode;
BeatmapSetNode oldFocus = focusNode;
// expand node before focusing it
int expandedIndex = OsuGroupList.get().getExpandedIndex();
int expandedIndex = BeatmapSetList.get().getExpandedIndex();
if (node.index != expandedIndex) {
node = OsuGroupList.get().expand(node.index);
node = BeatmapSetList.get().expand(node.index);
// if start node was previously expanded, move it
if (startNode != null && startNode.index == expandedIndex)
startNode = OsuGroupList.get().getBaseNode(startNode.index);
startNode = BeatmapSetList.get().getBaseNode(startNode.index);
}
// check osuFileIndex bounds
int length = node.osuFiles.size();
if (osuFileIndex < 0 || osuFileIndex > length - 1) // set a random index
osuFileIndex = (int) (Math.random() * length);
// check beatmapIndex bounds
int length = node.getBeatmapSet().size();
if (beatmapIndex < 0 || beatmapIndex > length - 1) // set a random index
beatmapIndex = (int) (Math.random() * length);
// change the focus node
if (changeStartNode || (startNode.index == 0 && startNode.osuFileIndex == -1 && startNode.prev == null))
if (changeStartNode || (startNode.index == 0 && startNode.beatmapIndex == -1 && startNode.prev == null))
startNode = node;
focusNode = OsuGroupList.get().getNode(node, osuFileIndex);
OsuFile osu = focusNode.osuFiles.get(focusNode.osuFileIndex);
MusicController.play(osu, false, preview);
focusNode = BeatmapSetList.get().getNode(node, beatmapIndex);
Beatmap beatmap = focusNode.getBeatmapSet().get(focusNode.beatmapIndex);
MusicController.play(beatmap, false, preview);
// load scores
scoreMap = ScoreDB.getMapSetScores(osu);
scoreMap = ScoreDB.getMapSetScores(beatmap);
focusScores = getScoreDataForNode(focusNode, true);
startScore = 0;
// check startNode bounds
while (startNode.index >= OsuGroupList.get().size() + length - MAX_SONG_BUTTONS && startNode.prev != null)
while (startNode.index >= BeatmapSetList.get().size() + length - MAX_SONG_BUTTONS && startNode.prev != null)
startNode = startNode.prev;
// make sure focusNode is on the screen (TODO: cleanup...)
int val = focusNode.index + focusNode.osuFileIndex - (startNode.index + MAX_SONG_BUTTONS) + 1;
int val = focusNode.index + focusNode.beatmapIndex - (startNode.index + MAX_SONG_BUTTONS) + 1;
if (val > 0) // below screen
changeIndex(val);
else { // above screen
if (focusNode.index == startNode.index) {
val = focusNode.index + focusNode.osuFileIndex - (startNode.index + startNode.osuFileIndex);
val = focusNode.index + focusNode.beatmapIndex - (startNode.index + startNode.beatmapIndex);
if (val < 0)
changeIndex(val);
} else if (startNode.index > focusNode.index) {
val = focusNode.index - focusNode.osuFiles.size() + focusNode.osuFileIndex - startNode.index + 1;
val = focusNode.index - focusNode.getBeatmapSet().size() + focusNode.beatmapIndex - startNode.index + 1;
if (val < 0)
changeIndex(val);
}
}
// if start node is expanded and on group node, move it
if (startNode.index == focusNode.index && startNode.osuFileIndex == -1)
if (startNode.index == focusNode.index && startNode.beatmapIndex == -1)
changeIndex(1);
return oldFocus;
@@ -1248,7 +1255,7 @@ public class SongMenu extends BasicGameState {
* @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) {
public void doStateActionOnLoad(MenuState menuState, BeatmapSetNode node) {
doStateActionOnLoad(menuState, node, null);
}
@@ -1267,32 +1274,32 @@ public class SongMenu extends BasicGameState {
* @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) {
private void doStateActionOnLoad(MenuState menuState, BeatmapSetNode node, ScoreData scoreData) {
stateAction = menuState;
stateActionNode = node;
stateActionScore = scoreData;
}
/**
* Returns all the score data for an OsuGroupNode from scoreMap.
* Returns all the score data for an BeatmapSetNode from scoreMap.
* If no score data is available for the node, return null.
* @param node the OsuGroupNode
* @param node the BeatmapSetNode
* @param setTimeSince whether or not to set the "time since" field for the scores
* @return the ScoreData array
*/
private ScoreData[] getScoreDataForNode(OsuGroupNode node, boolean setTimeSince) {
if (scoreMap == null || scoreMap.isEmpty() || node.osuFileIndex == -1) // node not expanded
private ScoreData[] getScoreDataForNode(BeatmapSetNode node, boolean setTimeSince) {
if (scoreMap == null || scoreMap.isEmpty() || node.beatmapIndex == -1) // node not expanded
return null;
OsuFile osu = node.osuFiles.get(node.osuFileIndex);
ScoreData[] scores = scoreMap.get(osu.version);
Beatmap beatmap = node.getBeatmapSet().get(node.beatmapIndex);
ScoreData[] scores = scoreMap.get(beatmap.version);
if (scores == null || scores.length < 1) // no scores
return null;
ScoreData s = scores[0];
if (osu.beatmapID == s.MID && osu.beatmapSetID == s.MSID &&
osu.title.equals(s.title) && osu.artist.equals(s.artist) &&
osu.creator.equals(s.creator)) {
if (beatmap.beatmapID == s.MID && beatmap.beatmapSetID == s.MSID &&
beatmap.title.equals(s.title) && beatmap.artist.equals(s.artist) &&
beatmap.creator.equals(s.creator)) {
if (setTimeSince) {
for (int i = 0; i < scores.length; i++)
scores[i].getTimeSince();
@@ -1311,9 +1318,9 @@ public class SongMenu extends BasicGameState {
SoundController.playSound(SoundEffect.MENUHIT);
MultiClip.destroyExtraClips();
OsuFile osu = MusicController.getOsuFile();
Beatmap beatmap = MusicController.getBeatmap();
Game gameState = (Game) game.getState(Opsu.STATE_GAME);
gameState.loadOsuFile(osu);
gameState.loadBeatmap(beatmap);
gameState.setRestart(Game.Restart.NEW);
gameState.setReplay(null);
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));

View File

@@ -21,14 +21,15 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.OsuParser;
import itdelatrisu.opsu.OszUnpacker;
import itdelatrisu.opsu.UI;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.replay.ReplayImporter;
//conflict
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
@@ -56,6 +57,9 @@ public class Splash extends BasicGameState {
/** Number of times the 'Esc' key has been pressed. */
private int escapeCount = 0;
/** Whether the skin being loaded is a new skin (for program restarts). */
private boolean newSkin = false;
// game-related variables
private int state;
private GameContainer container;
@@ -70,6 +74,10 @@ public class Splash extends BasicGameState {
throws SlickException {
this.container = container;
// check if skin changed
if (Options.getSkin() != null)
this.newSkin = (Options.getSkin().getDirectory() != Options.getSkinDir());
// load Utils class first (needed in other 'init' methods)
Utils.init(container, game);
@@ -90,11 +98,27 @@ public class Splash extends BasicGameState {
if (!init) {
init = true;
if (OsuGroupList.get() != null) {
// resources already loaded (from application restart)
finished = true;
} else {
// load resources in a new thread
// resources already loaded (from application restart)
if (BeatmapSetList.get() != null) {
// reload sounds if skin changed
if (newSkin) {
thread = new Thread() {
@Override
public void run() {
// TODO: only reload each sound if actually needed?
SoundController.init();
finished = true;
thread = null;
}
};
thread.start();
} else // don't reload anything
finished = true;
}
// load all resources in a new thread
else {
thread = new Thread() {
@Override
public void run() {
@@ -104,7 +128,7 @@ public class Splash extends BasicGameState {
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
// parse song directory
OsuParser.parseAllFiles(beatmapDir);
BeatmapParser.parseAllFiles(beatmapDir);
// import replays
ReplayImporter.importAllReplaysFromDir(Options.getReplayImportDir());
@@ -129,12 +153,12 @@ public class Splash extends BasicGameState {
// change states when loading complete
if (finished && alpha >= 1f) {
// initialize song list
if (OsuGroupList.get().size() > 0) {
OsuGroupList.get().init();
if (BeatmapSetList.get().size() > 0) {
BeatmapSetList.get().init();
if (Options.isThemeSongEnabled())
MusicController.playThemeSong();
else
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(OsuGroupList.get().getRandomNode(), -1, true, true);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
}
// play the theme song
@@ -155,7 +179,7 @@ public class Splash extends BasicGameState {
if (++escapeCount >= 3)
container.exit();
// stop parsing OsuFiles by sending interrupt to OsuParser
// stop parsing beatmaps by sending interrupt to BeatmapParser
else if (thread != null)
thread.interrupt();
}