Merge branch 'switch-base' into reorganise

This commit is contained in:
yugecin 2017-01-21 13:11:54 +01:00
commit dd731592aa
70 changed files with 2716 additions and 4320 deletions

View File

@ -1,184 +0,0 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package itdelatrisu.opsu;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapGroup;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSortOrder;
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.ui.UI;
import org.lwjgl.opengl.Display;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Game;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.InternalTextureLoader;
/**
* AppGameContainer extension that sends critical errors to ErrorHandler.
*/
public class Container extends AppGameContainer {
/** Exception causing game failure. */
protected Exception e = null;
public static Container instance;
/**
* Create a new container wrapping a game
*
* @param game The game to be wrapped
* @throws SlickException Indicates a failure to initialise the display
*/
public Container(Game game) throws SlickException {
super(game);
instance = this;
}
/**
* Create a new container wrapping a game
*
* @param game The game to be wrapped
* @param width The width of the display required
* @param height The height of the display required
* @param fullscreen True if we want fullscreen mode
* @throws SlickException Indicates a failure to initialise the display
*/
public Container(Game game, int width, int height, boolean fullscreen) throws SlickException {
super(game, width, height, fullscreen);
}
@Override
public void start() throws SlickException {
try {
setup();
ErrorHandler.setGlString();
getDelta();
while (running())
gameLoop();
} catch (Exception e) {
this.e = e;
}
// destroy the game container
try {
close_sub();
} catch (Exception e) {
if (this.e == null) // suppress if caused by a previous exception
this.e = e;
}
destroy();
// report any critical errors
if (e != null) {
ErrorHandler.error(null, e, true);
e = null;
forceExit = true;
}
if (forceExit) {
Opsu.close();
System.exit(0);
}
}
@Override
protected void gameLoop() throws SlickException {
int delta = getDelta();
if (!Display.isVisible() && updateOnlyOnVisible) {
try { Thread.sleep(100); } catch (Exception e) {}
} else {
try {
updateAndRender(delta);
} catch (SlickException e) {
this.e = e; // store exception to display later
running = false;
return;
}
}
updateFPS();
Display.update();
if (Display.isCloseRequested()) {
if (game.closeRequested())
running = false;
}
}
/**
* Actions to perform before destroying the game container.
*/
private void close_sub() {
// save user options
Options.saveOptions();
// reset cursor
UI.getCursor().reset();
// destroy images
InternalTextureLoader.get().clear();
// reset image references
GameImage.clearReferences();
GameData.Grade.clearReferences();
Beatmap.clearBackgroundImageCache();
// prevent loading tracks from re-initializing OpenAL
MusicController.reset();
// stop any playing track
SoundController.stopTrack();
// reset BeatmapSetList data
BeatmapGroup.set(BeatmapGroup.ALL);
BeatmapSortOrder.set(BeatmapSortOrder.TITLE);
if (BeatmapSetList.get() != null)
BeatmapSetList.get().reset();
// delete OpenGL objects involved in the Curve rendering
CurveRenderState.shutdown();
// destroy watch service
if (!Options.isWatchServiceEnabled())
BeatmapWatchService.destroy();
BeatmapWatchService.removeListeners();
// delete temporary directory
Utils.deleteDirectory(Options.TEMP_DIR);
}
@Override
public void exit() {
// show confirmation dialog if any downloads are active
if (forceExit) {
if (DownloadList.get().hasActiveDownloads() &&
UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION))
return;
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING &&
UI.showExitConfirmation(Updater.EXIT_CONFIRMATION))
return;
}
super.exit();
}
}

View File

@ -1,242 +0,0 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package itdelatrisu.opsu;
import java.awt.Cursor;
import java.awt.Desktop;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Properties;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
/**
* Error handler to log and display errors.
*/
public class ErrorHandler {
/** Error popup title. */
private static final String title = "Error";
/** Error popup description text. */
private static final String
desc = "opsu! has encountered an error.",
descReport = "opsu! has encountered an error. Please report this!";
/** Error popup button options. */
private static final String[]
optionsLog = {"View Error Log", "Close"},
optionsReport = {"Send Report", "Close"},
optionsLogReport = {"Send Report", "View Error Log", "Close"};
/** Text area for Exception. */
private static final JTextArea textArea = new JTextArea(15, 100);
static {
textArea.setEditable(false);
textArea.setBackground(UIManager.getColor("Panel.background"));
textArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
textArea.setTabSize(2);
textArea.setLineWrap(false);
textArea.setWrapStyleWord(true);
}
/** Scroll pane holding JTextArea. */
private static final JScrollPane scroll = new JScrollPane(textArea);
/** Error popup objects. */
private static final Object[]
message = { desc, scroll },
messageReport = { descReport, scroll };
/** OpenGL string (if any). */
private static String glString = null;
// This class should not be instantiated.
private ErrorHandler() {}
/**
* Sets the OpenGL version string.
*/
public static void setGlString() {
try {
String glVersion = GL11.glGetString(GL11.GL_VERSION);
String glVendor = GL11.glGetString(GL11.GL_VENDOR);
glString = String.format("%s (%s)", glVersion, glVendor);
} catch (Exception e) {}
}
/**
* Displays an error popup and logs the given error.
* @param error a description of the error
* @param e the exception causing the error
* @param report whether to ask to report the error
*/
public static void error(String error, Throwable e, boolean report) {
if (error == null && e == null)
return;
// log the error
if (error == null)
Log.error(e);
else if (e == null)
Log.error(error);
else
Log.error(error, e);
// set the textArea to the error message
textArea.setText(null);
if (error != null) {
textArea.append(error);
textArea.append("\n");
}
String trace = null;
if (e != null) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
trace = sw.toString();
textArea.append(trace);
}
// display popup
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
Desktop desktop = null;
boolean isBrowseSupported = false, isOpenSupported = false;
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
isBrowseSupported = desktop.isSupported(Desktop.Action.BROWSE);
isOpenSupported = desktop.isSupported(Desktop.Action.OPEN);
}
if (desktop != null && (isOpenSupported || (report && isBrowseSupported))) { // try to open the log file and/or issues webpage
if (report && isBrowseSupported) { // ask to report the error
if (isOpenSupported) { // also ask to open the log
int n = JOptionPane.showOptionDialog(null, messageReport, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
null, optionsLogReport, optionsLogReport[2]);
if (n == 0)
desktop.browse(getIssueURI(error, e, trace));
else if (n == 1)
desktop.open(Options.LOG_FILE);
} else { // only ask to report the error
int n = JOptionPane.showOptionDialog(null, message, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
null, optionsReport, optionsReport[1]);
if (n == 0)
desktop.browse(getIssueURI(error, e, trace));
}
} else { // don't report the error
int n = JOptionPane.showOptionDialog(null, message, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE,
null, optionsLog, optionsLog[1]);
if (n == 0)
desktop.open(Options.LOG_FILE);
}
} else { // display error only
JOptionPane.showMessageDialog(null, report ? messageReport : message,
title, JOptionPane.ERROR_MESSAGE);
}
} catch (Exception e1) {
Log.error("An error occurred in the crash popup.", e1);
}
}
/**
* Returns the issue reporting URI.
* This will auto-fill the report with the relevant information if possible.
* @param error a description of the error
* @param e the exception causing the error
* @param trace the stack trace
* @return the created URI
*/
private static URI getIssueURI(String error, Throwable e, String trace) {
// generate report information
String issueTitle = (error != null) ? error : e.getMessage();
StringBuilder sb = new StringBuilder();
try {
// read version and build date from version file, if possible
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
String version = props.getProperty("version");
if (version != null && !version.equals("${pom.version}")) {
sb.append("**Version:** ");
sb.append(version);
String hash = Utils.getGitHash();
if (hash != null) {
sb.append(" (");
sb.append(hash.substring(0, 12));
sb.append(')');
}
sb.append('\n');
}
String timestamp = props.getProperty("build.date");
if (timestamp != null &&
!timestamp.equals("${maven.build.timestamp}") && !timestamp.equals("${timestamp}")) {
sb.append("**Build date:** ");
sb.append(timestamp);
sb.append('\n');
}
} catch (IOException e1) {
Log.warn("Could not read version file.", e1);
}
sb.append("**OS:** ");
sb.append(System.getProperty("os.name"));
sb.append(" (");
sb.append(System.getProperty("os.arch"));
sb.append(")\n");
sb.append("**JRE:** ");
sb.append(System.getProperty("java.version"));
sb.append('\n');
if (glString != null) {
sb.append("**OpenGL Version:** ");
sb.append(glString);
sb.append('\n');
}
if (error != null) {
sb.append("**Error:** `");
sb.append(error);
sb.append("`\n");
}
if (trace != null) {
sb.append("**Stack trace:**");
sb.append("\n```\n");
sb.append(trace);
sb.append("```");
}
// return auto-filled URI
try {
return URI.create(String.format(Options.ISSUES_URL,
URLEncoder.encode(issueTitle, "UTF-8"),
URLEncoder.encode(sb.toString(), "UTF-8")));
} catch (UnsupportedEncodingException e1) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.");
return URI.create(String.format(Options.ISSUES_URL, "", ""));
}
}
}

View File

@ -42,7 +42,7 @@ import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.Dancer; import yugecin.opsudance.utils.SlickUtil;
/** /**
* Holds game data and renders all related elements. * Holds game data and renders all related elements.
@ -101,9 +101,17 @@ public class GameData {
* This does NOT destroy images, so be careful of memory leaks! * This does NOT destroy images, so be careful of memory leaks!
*/ */
public static void clearReferences() { public static void clearReferences() {
for (Grade grade : Grade.values()) for (Grade grade : Grade.values()) {
grade.menuImage = null; grade.menuImage = null;
} }
}
public static void destroyImages() {
for (Grade grade : Grade.values()) {
SlickUtil.destroyImage(grade.menuImage);
grade.menuImage = null;
}
}
/** /**
* Constructor. * Constructor.

View File

@ -27,7 +27,12 @@ import java.util.List;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.utils.SlickUtil;
/** /**
* Game images. * Game images.
@ -461,6 +466,18 @@ public enum GameImage {
} }
} }
public static void destroyImages() {
for (GameImage img : GameImage.values()) {
SlickUtil.destroyImages(img.defaultImages);
SlickUtil.destroyImage(img.defaultImage);
SlickUtil.destroyImages(img.skinImages);
SlickUtil.destroyImage(img.skinImage);
img.isSkinned = false;
img.defaultImages = img.skinImages = null;
img.defaultImage = img.skinImage = null;
}
}
/** /**
* Returns the bitmask image type from a type string. * Returns the bitmask image type from a type string.
* @param type the type string * @param type the type string
@ -693,7 +710,9 @@ public enum GameImage {
return; return;
} }
ErrorHandler.error(String.format("Could not find default image '%s'.", filename), null, false); String err = String.format("Could not find default image '%s'.", filename);
Log.warn(err);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
/** /**
@ -752,7 +771,7 @@ public enum GameImage {
img = img.getScaledCopy(0.5f); img = img.getScaledCopy(0.5f);
list.add(img); list.add(img);
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED));
break; break;
} }
} }
@ -766,7 +785,7 @@ public enum GameImage {
img = img.getScaledCopy(0.5f); img = img.getScaledCopy(0.5f);
list.add(img); list.add(img);
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", name), BubbleNotificationEvent.COMMONCOLOR_RED));
break; break;
} }
} }
@ -793,7 +812,7 @@ public enum GameImage {
img = img.getScaledCopy(0.5f); img = img.getScaledCopy(0.5f);
return img; return img;
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to set image '%s'.", filename), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to set image '%s'.", filename), BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
} }
@ -838,7 +857,7 @@ public enum GameImage {
skinImages = null; skinImages = null;
} }
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e, true); ErrorHandler.error(String.format("Failed to destroy beatmap skin images for '%s'.", this.name()), e).show();
} }
} }

View File

@ -1,309 +0,0 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package itdelatrisu.opsu;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.db.DBController;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.states.ButtonMenu;
import itdelatrisu.opsu.states.DownloadsMenu;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.states.GamePauseMenu;
import itdelatrisu.opsu.states.GameRanking;
import itdelatrisu.opsu.states.MainMenu;
import itdelatrisu.opsu.states.OptionsMenu;
import itdelatrisu.opsu.states.SongMenu;
import itdelatrisu.opsu.states.Splash;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
import org.newdawn.slick.util.DefaultLogSystem;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
/**
* Main class.
* <p>
* Creates game container, adds all other states, and initializes song data.
*/
public class Opsu extends StateBasedGame {
/** Game states. */
public static final int
STATE_SPLASH = 0,
STATE_MAINMENU = 1,
STATE_BUTTONMENU = 2,
STATE_SONGMENU = 3,
STATE_GAME = 4,
STATE_GAMEPAUSEMENU = 5,
STATE_GAMERANKING = 6,
STATE_OPTIONSMENU = 7,
STATE_DOWNLOADSMENU = 8;
/** Server socket for restricting the program to a single instance. */
private static ServerSocket SERVER_SOCKET;
/**
* Constructor.
* @param name the program name
*/
public Opsu(String name) {
super(name);
}
@Override
public void initStatesList(GameContainer container) throws SlickException {
addState(new Splash(STATE_SPLASH));
addState(new MainMenu(STATE_MAINMENU));
addState(new ButtonMenu(STATE_BUTTONMENU));
addState(new SongMenu(STATE_SONGMENU));
addState(new Game(STATE_GAME));
addState(new GamePauseMenu(STATE_GAMEPAUSEMENU));
addState(new GameRanking(STATE_GAMERANKING));
addState(new OptionsMenu(STATE_OPTIONSMENU));
addState(new DownloadsMenu(STATE_DOWNLOADSMENU));
}
/**
* Launches opsu!.
*/
public static void main(String[] args) {
// log all errors to a file
Log.setVerbose(false);
try {
DefaultLogSystem.out = new PrintStream(new FileOutputStream(Options.LOG_FILE, true));
} catch (FileNotFoundException e) {
Log.error(e);
}
// set default exception handler
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
ErrorHandler.error("** Uncaught Exception! **", e, true);
System.exit(1);
}
});
// parse configuration file
Options.parseOptions();
// only allow a single instance
if (!Options.noSingleInstance()) {
try {
SERVER_SOCKET = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost());
} catch (UnknownHostException e) {
// shouldn't happen
} catch (IOException e) {
errorAndExit(
null,
String.format(
"opsu! could not be launched for one of these reasons:\n" +
"- An instance of opsu! is already running.\n" +
"- Another program is bound to port %d. " +
"You can change the port opsu! uses by editing the \"Port\" field in the configuration file.",
Options.getPort()
),
false
);
}
}
// load natives
File nativeDir;
if (!Utils.isJarRunning() && (
(nativeDir = new File("./target/natives/")).isDirectory() ||
(nativeDir = new File("./build/natives/")).isDirectory()))
;
else {
nativeDir = Options.NATIVE_DIR;
try {
new NativeLoader(nativeDir).loadNatives();
} catch (IOException e) {
Log.error("Error loading natives.", e);
}
}
System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath());
System.setProperty("java.library.path", nativeDir.getAbsolutePath());
try {
// Workaround for "java.library.path" property being read-only.
// http://stackoverflow.com/a/24988095
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
Log.warn("Failed to set 'sys_paths' field.", e);
}
// set the resource paths
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
// initialize databases
try {
DBController.init();
} catch (UnsatisfiedLinkError e) {
errorAndExit(e, "The databases could not be initialized.", true);
}
// check if just updated
if (args.length >= 2)
Updater.get().setUpdateInfo(args[0], args[1]);
// check for updates
if (!Options.isUpdaterDisabled()) {
new Thread() {
@Override
public void run() {
try {
Updater.get().checkForUpdates();
} catch (IOException e) {
Log.warn("Check for updates failed.", e);
}
}
}.start();
}
// disable jinput
Input.disableControllers();
// start the game
try {
// loop until force exit
while (true) {
Opsu opsu = new Opsu("opsu!");
Container app = new Container(opsu);
// basic game settings
Options.setDisplayMode(app);
String[] icons = { "icon16.png", "icon32.png" };
try {
app.setIcons(icons);
} catch (Exception e) {
Log.error("could not set icon");
}
app.setForceExit(true);
app.start();
// run update if available
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) {
close();
Updater.get().runUpdate();
break;
}
}
} catch (SlickException e) {
errorAndExit(e, "An error occurred while creating the game container.", true);
}
}
@Override
public boolean closeRequested() {
int id = this.getCurrentStateID();
// intercept close requests in game-related states and return to song menu
if (id == STATE_GAME || id == STATE_GAMEPAUSEMENU || id == STATE_GAMERANKING) {
// start playing track at preview position
SongMenu songMenu = (SongMenu) this.getState(Opsu.STATE_SONGMENU);
if (id == STATE_GAMERANKING) {
GameData data = ((GameRanking) this.getState(Opsu.STATE_GAMERANKING)).getGameData();
if (data != null && data.isGameplay())
songMenu.resetTrackOnLoad();
} else {
if (id == STATE_GAME) {
MusicController.pause();
MusicController.setPitch(1.0f);
MusicController.resume();
} else
songMenu.resetTrackOnLoad();
}
// reset game data
if (UI.getCursor().isBeatmapSkinned())
UI.getCursor().reset();
songMenu.resetGameDataOnLoad();
this.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
return false;
}
// show confirmation dialog if any downloads are active
if (DownloadList.get().hasActiveDownloads() &&
UI.showExitConfirmation(DownloadList.EXIT_CONFIRMATION))
return false;
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING &&
UI.showExitConfirmation(Updater.EXIT_CONFIRMATION))
return false;
return true;
}
/**
* Closes all resources.
*/
public static void close() {
// close databases
DBController.closeConnections();
// cancel all downloads
DownloadList.get().cancelAllDownloads();
// close server socket
if (SERVER_SOCKET != null) {
try {
SERVER_SOCKET.close();
} catch (IOException e) {
ErrorHandler.error("Failed to close server socket.", e, false);
}
}
}
/**
* Throws an error and exits the application with the given message.
* @param e the exception that caused the crash
* @param message the message to display
* @param report whether to ask to report the error
*/
private static void errorAndExit(Throwable e, String message, boolean report) {
// JARs will not run properly inside directories containing '!'
// http://bugs.java.com/view_bug.do?bug_id=4523159
if (Utils.isJarRunning() && Utils.getRunningDirectory() != null &&
Utils.getRunningDirectory().getAbsolutePath().indexOf('!') != -1)
ErrorHandler.error("JARs cannot be run from some paths containing '!'. Please move or rename the file and try again.", null, false);
else
ErrorHandler.error(message, e, report);
System.exit(1);
}
}

View File

@ -59,6 +59,10 @@ import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.Win32Exception; import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinReg; import com.sun.jna.platform.win32.WinReg;
import yugecin.opsudance.*; import yugecin.opsudance.*;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.movers.factories.ExgonMoverFactory; import yugecin.opsudance.movers.factories.ExgonMoverFactory;
import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory; import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory;
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController; import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
@ -195,7 +199,7 @@ public class Options {
} }
File dir = new File(rootPath, "opsu"); File dir = new File(rootPath, "opsu");
if (!dir.isDirectory() && !dir.mkdir()) if (!dir.isDirectory() && !dir.mkdir())
ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), null, false); ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), new Exception("empty")).preventReport().show();
return dir; return dir;
} else } else
return workingDir; return workingDir;
@ -393,8 +397,7 @@ public class Options {
@Override @Override
public void clickListItem(int index) { public void clickListItem(int index) {
targetFPSindex = index; targetFPSindex = index;
Container.instance.setTargetFrameRate(targetFPS[index]); displayContainer.setFPS(targetFPS[index]);
Container.instance.setVSync(targetFPS[index] == 60);
} }
@Override @Override
@ -414,8 +417,7 @@ public class Options {
SHOW_FPS ("Show FPS Counter", "FpsCounter", "Show an FPS counter in the bottom-right hand corner.", true), SHOW_FPS ("Show FPS Counter", "FpsCounter", "Show an FPS counter in the bottom-right hand corner.", true),
SHOW_UNICODE ("Prefer Non-English Metadata", "ShowUnicode", "Where available, song titles will be shown in their native language.", false) { SHOW_UNICODE ("Prefer Non-English Metadata", "ShowUnicode", "Where available, song titles will be shown in their native language.", false) {
@Override @Override
public void click(GameContainer container) { public void click() {
super.click(container);
if (bool) { if (bool) {
try { try {
Fonts.LARGE.loadGlyphs(); Fonts.LARGE.loadGlyphs();
@ -465,13 +467,7 @@ public class Options {
val = i; val = i;
} }
}, },
NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true) { NEW_CURSOR ("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true),
@Override
public void click(GameContainer container) {
super.click(container);
UI.getCursor().reset();
}
},
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "DynamicBackground", "The song background will be used as the main menu background.", true), DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "DynamicBackground", "The song background will be used as the main menu background.", true),
LOAD_VERBOSE ("Show Detailed Loading Progress", "LoadVerbose", "Display more specific loading information in the splash screen.", false), LOAD_VERBOSE ("Show Detailed Loading Progress", "LoadVerbose", "Display more specific loading information in the splash screen.", false),
COLOR_MAIN_MENU_LOGO ("Use cursor color as main menu logo tint", "ColorMainMenuLogo", "Colorful main menu logo", false), COLOR_MAIN_MENU_LOGO ("Use cursor color as main menu logo tint", "ColorMainMenuLogo", "Colorful main menu logo", false),
@ -983,6 +979,7 @@ public class Options {
PIPPI_SLIDER_FOLLOW_EXPAND ("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false), PIPPI_SLIDER_FOLLOW_EXPAND ("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false),
PIPPI_PREVENT_WOBBLY_STREAMS ("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true); PIPPI_PREVENT_WOBBLY_STREAMS ("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true);
public static DisplayContainer displayContainer;
/** Option name. */ /** Option name. */
private final String name; private final String name;
@ -1129,9 +1126,8 @@ public class Options {
* Processes a mouse click action (via override). * Processes a mouse click action (via override).
* <p> * <p>
* By default, this inverts the current {@code bool} field. * By default, this inverts the current {@code bool} field.
* @param container the game container
*/ */
public void click(GameContainer container) { bool = !bool; } public void click() { bool = !bool; }
/** /**
* Get a list of values to choose from * Get a list of values to choose from
@ -1279,9 +1275,9 @@ public class Options {
/** /**
* Sets the target frame rate to the next available option, and sends a * Sets the target frame rate to the next available option, and sends a
* bar notification about the action. * bar notification about the action.
* @param container the game container
*/ */
public static void setNextFPS(GameContainer container) { public static void setNextFPS(DisplayContainer displayContainer) {
GameOption.displayContainer = displayContainer; // TODO dirty shit
GameOption.TARGET_FPS.clickListItem((targetFPSindex + 1) % targetFPS.length); GameOption.TARGET_FPS.clickListItem((targetFPSindex + 1) % targetFPS.length);
UI.sendBarNotification(String.format("Frame limiter: %s", GameOption.TARGET_FPS.getValueString())); UI.sendBarNotification(String.format("Frame limiter: %s", GameOption.TARGET_FPS.getValueString()));
} }
@ -1294,10 +1290,9 @@ public class Options {
/** /**
* Sets the master volume level (if within valid range). * Sets the master volume level (if within valid range).
* @param container the game container
* @param volume the volume [0, 1] * @param volume the volume [0, 1]
*/ */
public static void setMasterVolume(GameContainer container, float volume) { public static void setMasterVolume(float volume) {
if (volume >= 0f && volume <= 1f) { if (volume >= 0f && volume <= 1f) {
GameOption.MASTER_VOLUME.setValue((int) (volume * 100f)); GameOption.MASTER_VOLUME.setValue((int) (volume * 100f));
MusicController.setVolume(getMasterVolume() * getMusicVolume()); MusicController.setVolume(getMasterVolume() * getMusicVolume());
@ -1346,11 +1341,10 @@ public class Options {
* <p> * <p>
* If the configured resolution is larger than the screen size, the smallest * If the configured resolution is larger than the screen size, the smallest
* available resolution will be used. * available resolution will be used.
* @param app the game container
*/ */
public static void setDisplayMode(Container app) { public static void setDisplayMode(DisplayContainer container) {
int screenWidth = app.getScreenWidth(); int screenWidth = container.nativeDisplayMode.getWidth();
int screenHeight = app.getScreenHeight(); int screenHeight = container.nativeDisplayMode.getHeight();
resolutions[0] = screenWidth + "x" + screenHeight; resolutions[0] = screenWidth + "x" + screenHeight;
if (resolutionIdx < 0 || resolutionIdx > resolutions.length) { if (resolutionIdx < 0 || resolutionIdx > resolutions.length) {
@ -1370,9 +1364,10 @@ public class Options {
} }
try { try {
app.setDisplayMode(width, height, isFullscreen()); container.setDisplayMode(width, height, isFullscreen());
} catch (SlickException e) { } catch (Exception e) {
ErrorHandler.error("Failed to set display mode.", e, true); container.eventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED));
Log.error("Failed to set display mode.", e);
} }
if (!isFullscreen()) { if (!isFullscreen()) {
@ -1696,7 +1691,7 @@ public class Options {
* sends a bar notification about the action. * sends a bar notification about the action.
*/ */
public static void toggleMouseDisabled() { public static void toggleMouseDisabled() {
GameOption.DISABLE_MOUSE_BUTTONS.click(null); GameOption.DISABLE_MOUSE_BUTTONS.click();
UI.sendBarNotification((GameOption.DISABLE_MOUSE_BUTTONS.getBooleanValue()) ? UI.sendBarNotification((GameOption.DISABLE_MOUSE_BUTTONS.getBooleanValue()) ?
"Mouse buttons are disabled." : "Mouse buttons are enabled."); "Mouse buttons are disabled." : "Mouse buttons are enabled.");
} }
@ -1787,7 +1782,7 @@ public class Options {
// use default directory // use default directory
beatmapDir = BEATMAP_DIR; beatmapDir = BEATMAP_DIR;
if (!beatmapDir.isDirectory() && !beatmapDir.mkdir()) if (!beatmapDir.isDirectory() && !beatmapDir.mkdir())
ErrorHandler.error(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create beatmap directory at '%s'.", beatmapDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return beatmapDir; return beatmapDir;
} }
@ -1802,7 +1797,7 @@ public class Options {
oszDir = new File(DATA_DIR, "SongPacks/"); oszDir = new File(DATA_DIR, "SongPacks/");
if (!oszDir.isDirectory() && !oszDir.mkdir()) if (!oszDir.isDirectory() && !oszDir.mkdir())
ErrorHandler.error(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create song packs directory at '%s'.", oszDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return oszDir; return oszDir;
} }
@ -1817,7 +1812,7 @@ public class Options {
replayImportDir = new File(DATA_DIR, "ReplayImport/"); replayImportDir = new File(DATA_DIR, "ReplayImport/");
if (!replayImportDir.isDirectory() && !replayImportDir.mkdir()) if (!replayImportDir.isDirectory() && !replayImportDir.mkdir())
ErrorHandler.error(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create replay import directory at '%s'.", replayImportDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return replayImportDir; return replayImportDir;
} }
@ -1867,7 +1862,7 @@ public class Options {
// use default directory // use default directory
skinRootDir = SKIN_ROOT_DIR; skinRootDir = SKIN_ROOT_DIR;
if (!skinRootDir.isDirectory() && !skinRootDir.mkdir()) if (!skinRootDir.isDirectory() && !skinRootDir.mkdir())
ErrorHandler.error(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create skins directory at '%s'.", skinRootDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return skinRootDir; return skinRootDir;
} }
@ -2000,7 +1995,9 @@ public class Options {
} }
GameOption.DANCE_HIDE_WATERMARK.setValue(false); GameOption.DANCE_HIDE_WATERMARK.setValue(false);
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
@ -2029,7 +2026,9 @@ public class Options {
} }
writer.close(); writer.close();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath()), e, false); String err = String.format("Failed to write to file '%s'.", OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
} }

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu; package itdelatrisu.opsu;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
@ -71,6 +72,10 @@ import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import com.sun.jna.platform.FileUtils; import com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Contains miscellaneous utilities. * Contains miscellaneous utilities.
@ -89,40 +94,18 @@ public class Utils {
Arrays.sort(illegalChars); Arrays.sort(illegalChars);
} }
// game-related variables
private static Input input;
// This class should not be instantiated. // This class should not be instantiated.
private Utils() {} private Utils() {}
/** /**
* Initializes game settings and class data. * Initializes game settings and class data.
* @param container the game container
* @param game the game object
*/ */
public static void init(GameContainer container, StateBasedGame game) { public static void init(DisplayContainer displayContainer) {
input = container.getInput(); // TODO clean this up
int width = container.getWidth();
int height = container.getHeight();
// game settings // game settings
container.setTargetFrameRate(Options.getTargetFPS()); displayContainer.setFPS(Options.getTargetFPS()); // TODO move this elsewhere
container.setVSync(Options.getTargetFPS() == 60); MusicController.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
container.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
container.setShowFPS(false);
container.getInput().enableKeyRepeat();
container.setAlwaysRender(true);
container.setUpdateOnlyWhenVisible(false);
// calculate UI scale
GameImage.init(width, height);
// create fonts
try {
Fonts.init();
} catch (Exception e) {
ErrorHandler.error("Failed to load fonts.", e, true);
}
// load skin // load skin
Options.loadSkin(); Options.loadSkin();
@ -134,19 +117,19 @@ public class Utils {
} }
// initialize game mods // initialize game mods
GameMod.init(width, height); GameMod.init(displayContainer.width, displayContainer.height);
// initialize playback buttons // initialize playback buttons
PlaybackSpeed.init(width, height); PlaybackSpeed.init(displayContainer.width, displayContainer.height);
// initialize hit objects // initialize hit objects
HitObject.init(width, height); HitObject.init(displayContainer.width, displayContainer.height);
// initialize download nodes // initialize download nodes
DownloadNode.init(width, height); DownloadNode.init(displayContainer.width, displayContainer.height);
// initialize UI components // initialize UI components
UI.init(container, game); UI.init(displayContainer);
} }
/** /**
@ -246,12 +229,15 @@ public class Utils {
* @return true if pressed * @return true if pressed
*/ */
public static boolean isGameKeyPressed() { public static boolean isGameKeyPressed() {
/*
boolean mouseDown = !Options.isMouseDisabled() && ( boolean mouseDown = !Options.isMouseDisabled() && (
input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) ||
input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)); input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
return (mouseDown || return (mouseDown ||
input.isKeyDown(Options.getGameKeyLeft()) || input.isKeyDown(Options.getGameKeyLeft()) ||
input.isKeyDown(Options.getGameKeyRight())); input.isKeyDown(Options.getGameKeyRight()));
*/
return true;
} }
/** /**
@ -262,14 +248,14 @@ public class Utils {
// create the screenshot directory // create the screenshot directory
File dir = Options.getScreenshotDir(); File dir = Options.getScreenshotDir();
if (!dir.isDirectory() && !dir.mkdir()) { if (!dir.isDirectory() && !dir.mkdir()) {
ErrorHandler.error(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), null, false); EventBus.instance.post(new BubbleNotificationEvent(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
// create file name // create file name
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss"); SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
final File file = new File(dir, String.format("screenshot_%s.%s", final String fileName = String.format("screenshot_%s.%s", date.format(new Date()), Options.getScreenshotFormat());
date.format(new Date()), Options.getScreenshotFormat())); final File file = new File(dir, fileName);
SoundController.playSound(SoundEffect.SHUTTER); SoundController.playSound(SoundEffect.SHUTTER);
@ -296,8 +282,9 @@ public class Utils {
} }
} }
ImageIO.write(image, Options.getScreenshotFormat(), file); ImageIO.write(image, Options.getScreenshotFormat(), file);
EventBus.instance.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error("Failed to take a screenshot.", e, true); ErrorHandler.error("Failed to take a screenshot.", e).show();
} }
} }
}.start(); }.start();
@ -446,7 +433,7 @@ public class Utils {
try { try {
json = new JSONObject(s); json = new JSONObject(s);
} catch (JSONException e) { } catch (JSONException e) {
ErrorHandler.error("Failed to create JSON object.", e, true); ErrorHandler.error("Failed to create JSON object.", e).show();
} }
} }
return json; return json;
@ -465,7 +452,7 @@ public class Utils {
try { try {
json = new JSONArray(s); json = new JSONArray(s);
} catch (JSONException e) { } catch (JSONException e) {
ErrorHandler.error("Failed to create JSON array.", e, true); ErrorHandler.error("Failed to create JSON array.", e).show();
} }
} }
return json; return json;
@ -507,7 +494,7 @@ public class Utils {
result.append(String.format("%02x", b)); result.append(String.format("%02x", b));
return result.toString(); return result.toString();
} catch (NoSuchAlgorithmException | IOException e) { } catch (NoSuchAlgorithmException | IOException e) {
ErrorHandler.error("Failed to calculate MD5 hash.", e, true); ErrorHandler.error("Failed to calculate MD5 hash.", e).show();
} }
return null; return null;
} }
@ -531,7 +518,7 @@ public class Utils {
* @return true if JAR, false if file * @return true if JAR, false if file
*/ */
public static boolean isJarRunning() { public static boolean isJarRunning() {
return Opsu.class.getResource(String.format("%s.class", Opsu.class.getSimpleName())).toString().startsWith("jar:"); return Utils.class.getResource(String.format("%s.class", Utils.class.getSimpleName())).toString().startsWith("jar:");
} }
/** /**
@ -543,7 +530,7 @@ public class Utils {
return null; return null;
try { try {
return new JarFile(new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false); return new JarFile(new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false);
} catch (URISyntaxException | IOException e) { } catch (URISyntaxException | IOException e) {
Log.error("Could not determine the JAR file.", e); Log.error("Could not determine the JAR file.", e);
return null; return null;
@ -556,7 +543,7 @@ public class Utils {
*/ */
public static File getRunningDirectory() { public static File getRunningDirectory() {
try { try {
return new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); return new File(Utils.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
Log.error("Could not get the running directory.", e); Log.error("Could not get the running directory.", e);
return null; return null;

View File

@ -1,6 +1,6 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
@ -194,7 +194,7 @@ public class MultiClip {
try { try {
audioIn.close(); audioIn.close();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e, true); ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e).show();
} }
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -44,8 +43,12 @@ import org.newdawn.slick.MusicListener;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.openal.Audio; import org.newdawn.slick.openal.Audio;
import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import org.tritonus.share.sampled.file.TAudioFileFormat; import org.tritonus.share.sampled.file.TAudioFileFormat;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Controller for all music. * Controller for all music.
@ -152,7 +155,9 @@ public class MusicController {
}); });
playAt(position, loop); playAt(position, loop);
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error(String.format("Could not play track '%s'.", file.getName()), e, false); String err = String.format("Could not play track '%s'.", file.getName());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
@ -496,9 +501,7 @@ public class MusicController {
trackLoader.interrupt(); trackLoader.interrupt();
try { try {
trackLoader.join(); trackLoader.join();
} catch (InterruptedException e) { } catch (InterruptedException ignored) { }
ErrorHandler.error(null, e, true);
}
} }
trackLoader = null; trackLoader = null;
@ -575,7 +578,16 @@ public class MusicController {
player = null; player = null;
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error("Failed to destroy OpenAL.", e, true); ErrorHandler.error("Failed to destroy OpenAL.", e).show();
} }
} }
/**
* Set the default volume for music
* @param volume the new default value for music volume
*/
public static void setMusicVolume(float volume) {
SoundStore.get().setMusicVolume(volume);
}
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.HitSound.SampleSet; import itdelatrisu.opsu.audio.HitSound.SampleSet;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
@ -41,6 +40,9 @@ import javax.sound.sampled.LineUnavailableException;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Controller for all (non-music) sound components. * Controller for all (non-music) sound components.
@ -99,7 +101,7 @@ public class SoundController {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url); AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
return loadClip(ref, audioIn, isMP3); return loadClip(ref, audioIn, isMP3);
} catch (Exception e) { } catch (Exception e) {
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true); ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e).show();
return null; return null;
} }
} }
@ -214,7 +216,7 @@ public class SoundController {
// menu and game sounds // menu and game sounds
for (SoundEffect s : SoundEffect.values()) { for (SoundEffect s : SoundEffect.values()) {
if ((currentFileName = getSoundFileName(s.getFileName())) == null) { if ((currentFileName = getSoundFileName(s.getFileName())) == null) {
ErrorHandler.error(String.format("Could not find sound file '%s'.", s.getFileName()), null, false); EventBus.instance.post(new BubbleNotificationEvent("Could not find sound file " + s.getFileName(), BubbleNotificationEvent.COLOR_ORANGE));
continue; continue;
} }
MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3")); MultiClip newClip = loadClip(currentFileName, currentFileName.endsWith(".mp3"));
@ -233,7 +235,7 @@ public class SoundController {
for (HitSound s : HitSound.values()) { for (HitSound s : HitSound.values()) {
String filename = String.format("%s-%s", ss.getName(), s.getFileName()); String filename = String.format("%s-%s", ss.getName(), s.getFileName());
if ((currentFileName = getSoundFileName(filename)) == null) { if ((currentFileName = getSoundFileName(filename)) == null) {
ErrorHandler.error(String.format("Could not find hit sound file '%s'.", filename), null, false); EventBus.instance.post(new BubbleNotificationEvent("Could not find hit sound file " + filename, BubbleNotificationEvent.COLOR_ORANGE));
continue; continue;
} }
MultiClip newClip = loadClip(currentFileName, false); MultiClip newClip = loadClip(currentFileName, false);
@ -277,7 +279,7 @@ public class SoundController {
try { try {
clip.start(volume, listener); clip.start(volume, listener);
} catch (LineUnavailableException e) { } catch (LineUnavailableException e) {
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true); ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e).show();
} }
} }
} }

View File

@ -22,6 +22,7 @@ import itdelatrisu.opsu.Options;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
@ -59,6 +60,14 @@ public class Beatmap implements Comparable<Beatmap> {
*/ */
public static void clearBackgroundImageCache() { bgImageCache.clear(); } public static void clearBackgroundImageCache() { bgImageCache.clear(); }
public static void destroyBackgroundImageCache() {
Collection<ImageLoader> values = bgImageCache.values();
for (ImageLoader value : values) {
value.destroy();
}
bgImageCache.clear();
}
/** The OSU File object associated with this beatmap. */ /** The OSU File object associated with this beatmap. */
private File file; private File file;

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
@ -35,6 +34,9 @@ import java.util.Map;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Parser for beatmaps. * Parser for beatmaps.
@ -243,9 +245,11 @@ public class BeatmapParser {
} }
map.timingPoints.trimToSize(); map.timingPoints.trimToSize();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
ErrorHandler.error("Failed to get MD5 hash stream.", e, true); ErrorHandler.error("Failed to get MD5 hash stream.", e).show();
// retry without MD5 // retry without MD5
hasNoMD5Algorithm = true; hasNoMD5Algorithm = true;
@ -646,9 +650,11 @@ public class BeatmapParser {
if (md5stream != null) if (md5stream != null)
beatmap.md5Hash = md5stream.getMD5(); beatmap.md5Hash = md5stream.getMD5();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", file.getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", file.getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
ErrorHandler.error("Failed to get MD5 hash stream.", e, true); ErrorHandler.error("Failed to get MD5 hash stream.", e).show();
// retry without MD5 // retry without MD5
hasNoMD5Algorithm = true; hasNoMD5Algorithm = true;
@ -727,7 +733,9 @@ public class BeatmapParser {
} }
beatmap.timingPoints.trimToSize(); beatmap.timingPoints.trimToSize();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }
@ -803,9 +811,11 @@ public class BeatmapParser {
// check that all objects were parsed // check that all objects were parsed
if (objectIndex != beatmap.objects.length) if (objectIndex != beatmap.objects.length)
ErrorHandler.error(String.format("Parsed %d objects for beatmap '%s', %d objects expected.", ErrorHandler.error(String.format("Parsed %d objects for beatmap '%s', %d objects expected.",
objectIndex, beatmap.toString(), beatmap.objects.length), null, true); objectIndex, beatmap.toString(), beatmap.objects.length), new Exception("no")).show();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", beatmap.getFile().getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }

View File

@ -18,11 +18,12 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -215,7 +216,7 @@ public class BeatmapSetList {
try { try {
Utils.deleteToTrash(dir); Utils.deleteToTrash(dir);
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not delete song group.", e, true); EventBus.instance.post(new BubbleNotificationEvent("Could not delete song group", BubbleNotificationEvent.COLOR_ORANGE));
} }
if (ws != null) if (ws != null)
ws.resume(); ws.resume();
@ -270,7 +271,7 @@ public class BeatmapSetList {
try { try {
Utils.deleteToTrash(file); Utils.deleteToTrash(file);
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not delete song.", e, true); EventBus.instance.post(new BubbleNotificationEvent("Could not delete song", BubbleNotificationEvent.COLOR_ORANGE));
} }
if (ws != null) if (ws != null)
ws.resume(); ws.resume();

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import java.io.IOException; import java.io.IOException;
@ -42,6 +41,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/* /*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
@ -96,7 +97,8 @@ public class BeatmapWatchService {
ws = new BeatmapWatchService(); ws = new BeatmapWatchService();
ws.register(Options.getBeatmapDir().toPath()); ws.register(Options.getBeatmapDir().toPath());
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("An I/O exception occurred while creating the watch service.", e, true); Log.error("Could not create watch service", e);
EventBus.instance.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
@ -117,8 +119,9 @@ public class BeatmapWatchService {
ws.service.shutdownNow(); ws.service.shutdownNow();
ws = null; ws = null;
} catch (IOException e) { } catch (IOException e) {
Log.error("An I/O exception occurred while closing the previous watch service.", e);
EventBus.instance.post(new BubbleNotificationEvent("An I/O exception occurred while closing the previous watch service.", BubbleNotificationEvent.COMMONCOLOR_RED));
ws = null; ws = null;
ErrorHandler.error("An I/O exception occurred while closing the previous watch service.", e, true);
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import java.io.File; import java.io.File;
@ -28,6 +27,9 @@ import java.util.List;
import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.exception.ZipException;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Unpacker for OSZ (ZIP) archives. * Unpacker for OSZ (ZIP) archives.
@ -97,8 +99,9 @@ public class OszUnpacker {
ZipFile zipFile = new ZipFile(file); ZipFile zipFile = new ZipFile(file);
zipFile.extractAll(dest.getAbsolutePath()); zipFile.extractAll(dest.getAbsolutePath());
} catch (ZipException e) { } catch (ZipException e) {
ErrorHandler.error(String.format("Failed to unzip file %s to dest %s.", String err = String.format("Failed to unzip file %s to dest %s.", file.getAbsolutePath(), dest.getAbsolutePath());
file.getAbsolutePath(), dest.getAbsolutePath()), e, false); Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -35,6 +34,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Handles connections and queries with the cached beatmap database. * Handles connections and queries with the cached beatmap database.
@ -110,7 +110,7 @@ public class BeatmapDB {
try { try {
updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)"); updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)");
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e, true); ErrorHandler.error("Failed to prepare beatmap statements.", e).show();
} }
// retrieve the cache size // retrieve the cache size
@ -132,7 +132,7 @@ public class BeatmapDB {
updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?"); updatePlayStatsStmt = connection.prepareStatement("UPDATE beatmaps SET playCount = ?, lastPlayed = ? WHERE dir = ? AND file = ?");
setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?"); setFavoriteStmt = connection.prepareStatement("UPDATE beatmaps SET favorite = ? WHERE dir = ? AND file = ?");
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare beatmap statements.", e, true); ErrorHandler.error("Failed to prepare beatmap statements.", e).show();
} }
} }
@ -170,7 +170,7 @@ public class BeatmapDB {
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION); sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION);
stmt.executeUpdate(sql); stmt.executeUpdate(sql);
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not create beatmap database.", e, true); ErrorHandler.error("Coudl not create beatmap database.", e).show();
} }
} }
@ -222,7 +222,7 @@ public class BeatmapDB {
ps.close(); ps.close();
} }
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to update beatmap database.", e, true); ErrorHandler.error("Failed to update beatmap database.", e).show();
} }
} }
@ -240,7 +240,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not get beatmap cache size.", e, true); ErrorHandler.error("Could not get beatmap cache size.", e).show();
} }
} }
@ -255,7 +255,7 @@ public class BeatmapDB {
updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0))); updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0)));
updateSizeStmt.executeUpdate(); updateSizeStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not update beatmap cache size.", e, true); ErrorHandler.error("Could not update beatmap cache size.", e).show();
} }
} }
@ -273,7 +273,7 @@ public class BeatmapDB {
cacheSize = 0; cacheSize = 0;
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not drop beatmap database.", e, true); ErrorHandler.error("Could not drop beatmap database.", e).show();
} }
createDatabase(); createDatabase();
} }
@ -291,7 +291,7 @@ public class BeatmapDB {
cacheSize += insertStmt.executeUpdate(); cacheSize += insertStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to add beatmap to database.", e, true); ErrorHandler.error("Failed to add beatmap to database.", e).show();
} }
} }
@ -344,7 +344,7 @@ public class BeatmapDB {
// update cache size // update cache size
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to add beatmaps to database.", e, true); ErrorHandler.error("Failed to add beatmaps to database.", e).show();
} }
} }
@ -432,7 +432,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to load Beatmap from database.", e, true); ErrorHandler.error("Failed to load Beatmap from database.", e).show();
} }
} }
@ -495,7 +495,7 @@ public class BeatmapDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to load beatmaps from database.", e, true); ErrorHandler.error("Failed to load beatmaps from database.", e).show();
} }
} }
@ -596,7 +596,7 @@ public class BeatmapDB {
rs.close(); rs.close();
return map; return map;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to get last modified map from database.", e, true); ErrorHandler.error("Failed to get last modified map from database.", e).show();
return null; return null;
} }
} }
@ -616,7 +616,7 @@ public class BeatmapDB {
cacheSize -= deleteMapStmt.executeUpdate(); cacheSize -= deleteMapStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete beatmap entry from database.", e, true); ErrorHandler.error("Failed to delete beatmap entry from database.", e).show();
} }
} }
@ -633,7 +633,7 @@ public class BeatmapDB {
cacheSize -= deleteGroupStmt.executeUpdate(); cacheSize -= deleteGroupStmt.executeUpdate();
updateCacheSize(); updateCacheSize();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete beatmap group entry from database.", e, true); ErrorHandler.error("Failed to delete beatmap group entry from database.", e).show();
} }
} }
@ -652,7 +652,7 @@ public class BeatmapDB {
setStarsStmt.executeUpdate(); setStarsStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.", ErrorHandler.error(String.format("Failed to save star rating '%.4f' for beatmap '%s' in database.",
beatmap.starRating, beatmap.toString()), e, true); beatmap.starRating, beatmap.toString()), e).show();
} }
} }
@ -672,7 +672,7 @@ public class BeatmapDB {
updatePlayStatsStmt.executeUpdate(); updatePlayStatsStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.", ErrorHandler.error(String.format("Failed to update play statistics for beatmap '%s' in database.",
beatmap.toString()), e, true); beatmap.toString()), e).show();
} }
} }
@ -691,7 +691,7 @@ public class BeatmapDB {
setFavoriteStmt.executeUpdate(); setFavoriteStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.", ErrorHandler.error(String.format("Failed to update favorite status for beatmap '%s' in database.",
beatmap.toString()), e, true); beatmap.toString()), e).show();
} }
} }
@ -711,7 +711,7 @@ public class BeatmapDB {
connection.close(); connection.close();
connection = null; connection = null;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to close beatmap database.", e, true); ErrorHandler.error("Failed to close beatmap database.", e).show();
} }
} }
} }

View File

@ -18,7 +18,7 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import itdelatrisu.opsu.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
@ -39,7 +39,7 @@ public class DBController {
try { try {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
ErrorHandler.error("Could not load sqlite-JDBC driver.", e, true); ErrorHandler.error("Could not load sqlite-JDBC driver.", e).show();
} }
// initialize the databases // initialize the databases
@ -65,7 +65,7 @@ public class DBController {
return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path)); return DriverManager.getConnection(String.format("jdbc:sqlite:%s", path));
} catch (SQLException e) { } catch (SQLException e) {
// if the error message is "out of memory", it probably means no database file is found // if the error message is "out of memory", it probably means no database file is found
ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e, true); ErrorHandler.error(String.format("Could not connect to database: '%s'.", path), e).show();
return null; return null;
} }
} }

View File

@ -18,10 +18,10 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -124,7 +124,7 @@ public class ScoreDB {
// TODO: extra playerName checks not needed if name is guaranteed not null // TODO: extra playerName checks not needed if name is guaranteed not null
); );
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to prepare score statements.", e, true); ErrorHandler.error("Failed to prepare score statements.", e).show();
} }
} }
@ -157,7 +157,7 @@ public class ScoreDB {
sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION); sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', %d)", DATABASE_VERSION);
stmt.executeUpdate(sql); stmt.executeUpdate(sql);
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Could not create score database.", e, true); ErrorHandler.error("Could not create score database.", e).show();
} }
} }
@ -209,7 +209,7 @@ public class ScoreDB {
ps.close(); ps.close();
} }
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to update score database.", e, true); ErrorHandler.error("Failed to update score database.", e).show();
} }
} }
@ -227,7 +227,7 @@ public class ScoreDB {
insertStmt.setString(19, data.playerName); insertStmt.setString(19, data.playerName);
insertStmt.executeUpdate(); insertStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to save score to database.", e, true); ErrorHandler.error("Failed to save score to database.", e).show();
} }
} }
@ -247,7 +247,7 @@ public class ScoreDB {
deleteScoreStmt.setString(21, data.playerName); deleteScoreStmt.setString(21, data.playerName);
deleteScoreStmt.executeUpdate(); deleteScoreStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete score from database.", e, true); ErrorHandler.error("Failed to delete score from database.", e).show();
} }
} }
@ -267,7 +267,7 @@ public class ScoreDB {
deleteSongStmt.setString(5, beatmap.version); deleteSongStmt.setString(5, beatmap.version);
deleteSongStmt.executeUpdate(); deleteSongStmt.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to delete scores from database.", e, true); ErrorHandler.error("Failed to delete scores from database.", e).show();
} }
} }
@ -335,7 +335,7 @@ public class ScoreDB {
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to read scores from database.", e, true); ErrorHandler.error("Failed to read scores from database.", e).show();
return null; return null;
} }
return getSortedArray(list); return getSortedArray(list);
@ -377,7 +377,7 @@ public class ScoreDB {
map.put(version, getSortedArray(list)); map.put(version, getSortedArray(list));
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to read scores from database.", e, true); ErrorHandler.error("Failed to read scores from database.", e).show();
return null; return null;
} }
return map; return map;
@ -406,7 +406,7 @@ public class ScoreDB {
connection.close(); connection.close();
connection = null; connection = null;
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to close score database.", e, true); ErrorHandler.error("Failed to close score database.", e).show();
} }
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads; package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import java.io.File; import java.io.File;
@ -35,6 +34,9 @@ import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* File download. * File download.
@ -142,7 +144,7 @@ public class Download {
this.url = new URL(remoteURL); this.url = new URL(remoteURL);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
this.status = Status.ERROR; this.status = Status.ERROR;
ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e, true); ErrorHandler.error(String.format("Bad download URL: '%s'", remoteURL), e).show();
return; return;
} }
this.localPath = localPath; this.localPath = localPath;
@ -215,7 +217,7 @@ public class Download {
else if (redirectCount > MAX_REDIRECTS) else if (redirectCount > MAX_REDIRECTS)
error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS); error = String.format("Download for URL '%s' is attempting too many redirects (over %d).", base.toString(), MAX_REDIRECTS);
if (error != null) { if (error != null) {
ErrorHandler.error(error, null, false); EventBus.instance.post(new BubbleNotificationEvent(error, BubbleNotificationEvent.COLOR_ORANGE));
throw new IOException(); throw new IOException();
} }
@ -417,7 +419,7 @@ public class Download {
} }
} catch (IOException e) { } catch (IOException e) {
this.status = Status.ERROR; this.status = Status.ERROR;
ErrorHandler.error("Failed to cancel download.", e, true); ErrorHandler.error("Failed to cancel download.", e).show();
} }
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads; package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -35,6 +34,8 @@ import java.io.File;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Node containing song data and a Download object. * Node containing song data and a Download object.
@ -402,7 +403,7 @@ public class DownloadNode {
public void drawDownload(Graphics g, float position, int id, boolean hover) { public void drawDownload(Graphics g, float position, int id, boolean hover) {
Download download = this.download; // in case clearDownload() is called asynchronously Download download = this.download; // in case clearDownload() is called asynchronously
if (download == null) { if (download == null) {
ErrorHandler.error("Trying to draw download information for button without Download object.", null, false); EventBus.instance.post(new BubbleNotificationEvent("Trying to draw download information for button without Download object", BubbleNotificationEvent.COLOR_ORANGE));
return; return;
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads; package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.Download.DownloadListener; import itdelatrisu.opsu.downloads.Download.DownloadListener;
@ -38,6 +37,7 @@ import java.util.Properties;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Handles automatic program updates. * Handles automatic program updates.
@ -298,7 +298,7 @@ public class Updater {
pb.start(); pb.start();
} catch (IOException e) { } catch (IOException e) {
status = Status.INTERNAL_ERROR; status = Status.INTERNAL_ERROR;
ErrorHandler.error("Failed to start new process.", e, true); ErrorHandler.error("Failed to start new process.", e).show();
} }
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -34,6 +33,7 @@ import java.util.Date;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Download server: http://bloodcat.com/osu/ * Download server: http://bloodcat.com/osu/
@ -97,7 +97,7 @@ public class BloodcatServer extends DownloadServer {
resultCount++; resultCount++;
this.totalResults = resultCount; this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} }
return nodes; return nodes;
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -30,6 +29,7 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Download server: https://osu.hexide.com/ * Download server: https://osu.hexide.com/
@ -128,7 +128,7 @@ public class HexideServer extends DownloadServer {
// all results at once; this approach just gets pagination correct. // all results at once; this approach just gets pagination correct.
this.totalResults = arr.length() + resultIndex; this.totalResults = arr.length() + resultIndex;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} }
return nodes; return nodes;
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -30,6 +29,7 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Download server: http://osu.mengsky.net/ * Download server: http://osu.mengsky.net/
@ -101,7 +101,7 @@ public class MengSkyServer extends DownloadServer {
resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT; resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
this.totalResults = resultCount; this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} }
return nodes; return nodes;
} }

View File

@ -18,9 +18,9 @@
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -120,7 +120,7 @@ public class MnetworkServer extends DownloadServer {
// store total result count // store total result count
this.totalResults = nodes.length; this.totalResults = nodes.length;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} }
return nodes; return nodes;
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -36,6 +35,7 @@ import java.util.TimeZone;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Download server: http://loli.al/ * Download server: http://loli.al/
@ -123,7 +123,7 @@ public class OsuMirrorServer extends DownloadServer {
else else
this.totalResults = maxServerID; this.totalResults = maxServerID;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} }
return nodes; return nodes;
} }

View File

@ -17,7 +17,6 @@
*/ */
package itdelatrisu.opsu.downloads.servers; package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
@ -34,6 +33,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
/** /**
* Download server: http://osu.yas-online.net/ * Download server: http://osu.yas-online.net/
@ -114,7 +114,7 @@ public class YaSOnlineServer extends DownloadServer {
String downloadLink = item.getString("downloadLink"); String downloadLink = item.getString("downloadLink");
return String.format(DOWNLOAD_FETCH_URL, downloadLink); return String.format(DOWNLOAD_FETCH_URL, downloadLink);
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e, true); ErrorHandler.error(String.format("Problem retrieving download URL for beatmap '%d'.", beatmapSetID), e).show();
return null; return null;
} finally { } finally {
Utils.setSSLCertValidation(true); Utils.setSSLCertValidation(true);
@ -186,7 +186,7 @@ public class YaSOnlineServer extends DownloadServer {
else else
this.totalResults = maxServerID; this.totalResults = maxServerID;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();
} finally { } finally {
Utils.setSSLCertValidation(true); Utils.setSSLCertValidation(true);
} }

View File

@ -64,10 +64,9 @@ public class Circle extends GameObject {
/** /**
* Initializes the Circle data type with map modifiers, images, and dimensions. * Initializes the Circle data type with map modifiers, images, and dimensions.
* @param container the game container
* @param circleDiameter the circle diameter * @param circleDiameter the circle diameter
*/ */
public static void init(GameContainer container, float circleDiameter) { public static void init(float circleDiameter) {
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480) diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
int diameterInt = (int) diameter; int diameterInt = (int) diameter;
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt)); GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));

View File

@ -37,6 +37,7 @@ import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.core.DisplayContainer;
/** /**
* Data type representing a slider object. * Data type representing a slider object.
@ -127,13 +128,12 @@ public class Slider extends GameObject {
/** /**
* Initializes the Slider data type with images and dimensions. * Initializes the Slider data type with images and dimensions.
* @param container the game container
* @param circleDiameter the circle diameter * @param circleDiameter the circle diameter
* @param beatmap the associated beatmap * @param beatmap the associated beatmap
*/ */
public static void init(GameContainer container, float circleDiameter, Beatmap beatmap) { public static void init(DisplayContainer displayContainer, float circleDiameter, Beatmap beatmap) {
containerWidth = container.getWidth(); containerWidth = displayContainer.width;
containerHeight = container.getHeight(); containerHeight = displayContainer.height;
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480) diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
int diameterInt = (int) diameter; int diameterInt = (int) diameter;

View File

@ -35,6 +35,7 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer; import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
/** /**
* Data type representing a spinner object. * Data type representing a spinner object.
@ -109,12 +110,11 @@ public class Spinner extends GameObject {
/** /**
* Initializes the Spinner data type with images and dimensions. * Initializes the Spinner data type with images and dimensions.
* @param container the game container
* @param difficulty the map's overall difficulty value * @param difficulty the map's overall difficulty value
*/ */
public static void init(GameContainer container, float difficulty) { public static void init(DisplayContainer displayContainer, float difficulty) {
width = container.getWidth(); width = displayContainer.width;
height = container.getHeight(); height = displayContainer.height;
overallDifficulty = difficulty; overallDifficulty = difficulty;
} }

View File

@ -96,7 +96,7 @@ public class CurveRenderState {
*/ */
public static void shutdown() { public static void shutdown() {
staticState.shutdown(); staticState.shutdown();
FrameBufferCache.shutdown(); //FrameBufferCache.shutdown();
} }
/** /**

View File

@ -128,6 +128,7 @@ public class FrameBufferCache {
* <p> * <p>
* This is necessary for cases when the game gets restarted with a * This is necessary for cases when the game gets restarted with a
* different resolution without closing the process. * different resolution without closing the process.
* // TODO d do we still need this
*/ */
public static void shutdown() { public static void shutdown() {
FrameBufferCache fbcInstance = FrameBufferCache.getInstance(); FrameBufferCache fbcInstance = FrameBufferCache.getInstance();

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.replay; package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -45,6 +44,9 @@ import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import lzma.streams.LzmaOutputStream; import lzma.streams.LzmaOutputStream;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Captures osu! replay data. * Captures osu! replay data.
@ -273,7 +275,7 @@ public class Replay {
File dir = Options.getReplayDir(); File dir = Options.getReplayDir();
if (!dir.isDirectory()) { if (!dir.isDirectory()) {
if (!dir.mkdir()) { if (!dir.mkdir()) {
ErrorHandler.error("Failed to create replay directory.", null, false); EventBus.instance.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
} }
@ -343,7 +345,7 @@ public class Replay {
compressedOut.write(bytes); compressedOut.write(bytes);
} catch (IOException e) { } catch (IOException e) {
// possible OOM: https://github.com/jponge/lzma-java/issues/9 // possible OOM: https://github.com/jponge/lzma-java/issues/9
ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e, true); ErrorHandler.error("LZMA compression failed (possible out-of-memory error).", e).show();
} }
compressedOut.close(); compressedOut.close();
bout.close(); bout.close();
@ -357,7 +359,7 @@ public class Replay {
writer.close(); writer.close();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not save replay data.", e, true); ErrorHandler.error("Could not save replay data.", e).show();
} }
} }
}.start(); }.start();

View File

@ -18,7 +18,7 @@
package itdelatrisu.opsu.replay; package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.ErrorHandler; import com.sun.deploy.security.EnhancedJarVerifier;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
@ -31,6 +31,8 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Importer for replay files. * Importer for replay files.
@ -70,7 +72,9 @@ public class ReplayImporter {
File replayDir = Options.getReplayDir(); File replayDir = Options.getReplayDir();
if (!replayDir.isDirectory()) { if (!replayDir.isDirectory()) {
if (!replayDir.mkdir()) { if (!replayDir.mkdir()) {
ErrorHandler.error(String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath()), null, false); String err = String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath());
Log.error(err);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
} }
@ -83,7 +87,9 @@ public class ReplayImporter {
r.loadHeader(); r.loadHeader();
} catch (IOException e) { } catch (IOException e) {
moveToFailedDirectory(file); moveToFailedDirectory(file);
ErrorHandler.error(String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName()), e, false); String err = String.format("Failed to import replay '%s'. The replay file could not be parsed.", file.getName());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
continue; continue;
} }
Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash); Beatmap beatmap = BeatmapSetList.get().getBeatmapFromHash(r.beatmapHash);
@ -100,8 +106,9 @@ public class ReplayImporter {
} }
} else { } else {
moveToFailedDirectory(file); moveToFailedDirectory(file);
ErrorHandler.error(String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName()), null, false); String err = String.format("Failed to import replay '%s'. The associated beatmap could not be found.", file.getName());
continue; Log.error(err);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.skins; package itdelatrisu.opsu.skins;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -32,6 +31,8 @@ import java.util.LinkedList;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Loads skin configuration files. * Loads skin configuration files.
@ -290,7 +291,9 @@ public class SkinLoader {
} }
} }
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error(String.format("Failed to read file '%s'.", skinFile.getAbsolutePath()), e, false); String err = String.format("Failed to read file '%s'.", skinFile.getAbsolutePath());
Log.error(err, e);
EventBus.instance.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
} }
return skin; return skin;

View File

@ -20,7 +20,6 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -39,114 +38,112 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.BasicGameState; import yugecin.opsudance.core.inject.InstanceContainer;
import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.state.BaseOpsuState;
import org.newdawn.slick.state.transition.EmptyTransition;
import org.newdawn.slick.state.transition.FadeInTransition;
/** /**
* Generic button menu state. * Generic button menu state.
* <p> * <p>
* Displays a header and a set of defined options to the player. * Displays a header and a set of defined options to the player.
*/ */
public class ButtonMenu extends BasicGameState { public class ButtonMenu extends BaseOpsuState {
/** Menu states. */ /** Menu states. */
public enum MenuState { public enum MenuState {
/** The exit confirmation screen. */ /** The exit confirmation screen. */
EXIT (new Button[] { Button.YES, Button.NO }) { EXIT (new Button[] { Button.YES, Button.NO }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return new String[] { "Are you sure you want to exit opsu!?" }; return new String[] { "Are you sure you want to exit opsu!?" };
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.NO.click(container, game); Button.NO.click();
} }
}, },
/** The initial beatmap management screen (for a non-"favorite" beatmap). */ /** The initial beatmap management screen (for a non-"favorite" beatmap). */
BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) { BEATMAP (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_ADD, Button.DELETE, Button.CANCEL }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : ""; String beatmapString = (node != null) ? BeatmapSetList.get().getBaseNode(node.index).toString() : "";
return new String[] { beatmapString, "What do you want to do with this beatmap?" }; return new String[] { beatmapString, "What do you want to do with this beatmap?" };
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.CANCEL.click(container, game); Button.CANCEL.click();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
Input input = container.getInput(); if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) super.mouseWheelMoved(newValue);
super.scroll(container, game, newValue); }
} }
}, },
/** The initial beatmap management screen (for a "favorite" beatmap). */ /** The initial beatmap management screen (for a "favorite" beatmap). */
BEATMAP_FAVORITE (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_REMOVE, Button.DELETE, Button.CANCEL }) { BEATMAP_FAVORITE (new Button[] { Button.CLEAR_SCORES, Button.FAVORITE_REMOVE, Button.DELETE, Button.CANCEL }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return BEATMAP.getTitle(container, game); return BEATMAP.getTitle();
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
BEATMAP.leave(container, game); BEATMAP.leave();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
BEATMAP.scroll(container, game, newValue); BEATMAP.mouseWheelMoved(newValue);
} }
}, },
/** The beatmap deletion screen for a beatmap set with multiple beatmaps. */ /** The beatmap deletion screen for a beatmap set with multiple beatmaps. */
BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) { BEATMAP_DELETE_SELECT (new Button[] { Button.DELETE_GROUP, Button.DELETE_SONG, Button.CANCEL_DELETE }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
String beatmapString = (node != null) ? node.toString() : ""; String beatmapString = (node != null) ? node.toString() : "";
return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) }; return new String[] { String.format("Are you sure you wish to delete '%s' from disk?", beatmapString) };
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.CANCEL_DELETE.click(container, game); Button.CANCEL_DELETE.click();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
MenuState.BEATMAP.scroll(container, game, newValue); MenuState.BEATMAP.mouseWheelMoved(newValue);
} }
}, },
/** The beatmap deletion screen for a single beatmap. */ /** The beatmap deletion screen for a single beatmap. */
BEATMAP_DELETE_CONFIRM (new Button[] { Button.DELETE_CONFIRM, Button.CANCEL_DELETE }) { BEATMAP_DELETE_CONFIRM (new Button[] { Button.DELETE_CONFIRM, Button.CANCEL_DELETE }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return BEATMAP_DELETE_SELECT.getTitle(container, game); return BEATMAP_DELETE_SELECT.getTitle();
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.CANCEL_DELETE.click(container, game); Button.CANCEL_DELETE.click();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
MenuState.BEATMAP.scroll(container, game, newValue); MenuState.BEATMAP.mouseWheelMoved(newValue);
} }
}, },
/** The beatmap reloading confirmation screen. */ /** The beatmap reloading confirmation screen. */
RELOAD (new Button[] { Button.RELOAD_CONFIRM, Button.RELOAD_CANCEL }) { RELOAD (new Button[] { Button.RELOAD_CONFIRM, Button.RELOAD_CANCEL }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return new String[] { return new String[] {
"You have requested a full process of your beatmaps.", "You have requested a full process of your beatmaps.",
"This could take a few minutes.", "This could take a few minutes.",
@ -155,70 +152,68 @@ public class ButtonMenu extends BasicGameState {
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.RELOAD_CANCEL.click(container, game); Button.RELOAD_CANCEL.click();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
MenuState.BEATMAP.scroll(container, game, newValue); MenuState.BEATMAP.mouseWheelMoved(newValue);
} }
}, },
/** The score management screen. */ /** The score management screen. */
SCORE (new Button[] { Button.DELETE_SCORE, Button.CLOSE }) { SCORE (new Button[] { Button.DELETE_SCORE, Button.CLOSE }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return new String[] { "Score Management" }; return new String[] { "Score Management" };
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) { public void leave() {
Button.CLOSE.click(container, game); Button.CLOSE.click();
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
MenuState.BEATMAP.scroll(container, game, newValue); MenuState.BEATMAP.mouseWheelMoved(newValue);
} }
}, },
/** The game mod selection screen. */ /** The game mod selection screen. */
MODS (new Button[] { Button.RESET_MODS, Button.CLOSE }) { MODS (new Button[] { Button.RESET_MODS, Button.CLOSE }) {
@Override @Override
public String[] getTitle(GameContainer container, StateBasedGame game) { public String[] getTitle() {
return new String[] { return new String[] {
"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun." "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."
}; };
} }
@Override @Override
protected float getBaseY(GameContainer container, StateBasedGame game) { protected float getBaseY(DisplayContainer displayContainer) {
return container.getHeight() * 2f / 3; return displayContainer.height * 2f / 3;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) { public void enter() {
super.enter(container, game); super.enter();
for (GameMod mod : GameMod.values()) for (GameMod mod : GameMod.values()) {
mod.resetHover(); mod.resetHover();
} }
@Override
public void leave(GameContainer container, StateBasedGame game) {
Button.CLOSE.click(container, game);
} }
@Override @Override
public void draw(GameContainer container, StateBasedGame game, Graphics g) { public void leave() {
int width = container.getWidth(); Button.CLOSE.click();
int height = container.getHeight(); }
@Override
public void render(Graphics g) {
// score multiplier (TODO: fade in color changes) // score multiplier (TODO: fade in color changes)
float mult = GameMod.getScoreMultiplier(); float mult = GameMod.getScoreMultiplier();
String multString = String.format("Score Multiplier: %.2fx", mult); String multString = String.format("Score Multiplier: %.2fx", mult);
Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red; Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red;
float multY = Fonts.LARGE.getLineHeight() * 2 + height * 0.06f; float multY = Fonts.LARGE.getLineHeight() * 2 + displayContainer.height * 0.06f;
Fonts.LARGE.drawString( Fonts.LARGE.drawString(
(width - Fonts.LARGE.getWidth(multString)) / 2f, (displayContainer.width - Fonts.LARGE.getWidth(multString)) / 2f,
multY, multString, multColor); multY, multString, multColor);
// category text // category text
@ -232,27 +227,28 @@ public class ButtonMenu extends BasicGameState {
for (GameMod mod : GameMod.values()) for (GameMod mod : GameMod.values())
mod.draw(); mod.draw();
super.draw(container, game, g); super.render(g);
} }
@Override @Override
public void update(GameContainer container, int delta, int mouseX, int mouseY) { public void preRenderUpdate() {
super.update(container, delta, mouseX, mouseY); super.preRenderUpdate();
GameMod hoverMod = null; GameMod hoverMod = null;
for (GameMod mod : GameMod.values()) { for (GameMod mod : GameMod.values()) {
mod.hoverUpdate(delta, mod.isActive()); mod.hoverUpdate(displayContainer.renderDelta, mod.isActive());
if (hoverMod == null && mod.contains(mouseX, mouseY)) if (hoverMod == null && mod.contains(displayContainer.mouseX, displayContainer.mouseY))
hoverMod = mod; hoverMod = mod;
} }
// tooltips // tooltips
if (hoverMod != null) if (hoverMod != null) {
UI.updateTooltip(delta, hoverMod.getDescription(), true); UI.updateTooltip(displayContainer.renderDelta, hoverMod.getDescription(), true);
}
} }
@Override @Override
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) { public void keyPressed(int key, char c) {
super.keyPress(container, game, key, c); super.keyPressed(key, c);
for (GameMod mod : GameMod.values()) { for (GameMod mod : GameMod.values()) {
if (key == mod.getKey()) { if (key == mod.getKey()) {
mod.toggle(true); mod.toggle(true);
@ -262,8 +258,8 @@ public class ButtonMenu extends BasicGameState {
} }
@Override @Override
public void click(GameContainer container, StateBasedGame game, int cx, int cy) { public void mousePressed(int cx, int cy) {
super.click(container, game, cx, cy); super.mousePressed(cx, cy);
for (GameMod mod : GameMod.values()) { for (GameMod mod : GameMod.values()) {
if (mod.contains(cx, cy)) { if (mod.contains(cx, cy)) {
boolean prevState = mod.isActive(); boolean prevState = mod.isActive();
@ -276,11 +272,14 @@ public class ButtonMenu extends BasicGameState {
} }
@Override @Override
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
MenuState.BEATMAP.scroll(container, game, newValue); MenuState.BEATMAP.mouseWheelMoved(newValue);
} }
}; };
public static DisplayContainer displayContainer;
public static InstanceContainer instanceContainer;
/** The buttons in the state. */ /** The buttons in the state. */
private final Button[] buttons; private final Button[] buttons;
@ -306,15 +305,10 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Initializes the menu state. * Initializes the menu state.
* @param container the game container
* @param game the game
* @param button the center button image
* @param buttonL the left button image
* @param buttonR the right button image
*/ */
public void init(GameContainer container, StateBasedGame game, Image button, Image buttonL, Image buttonR) { public void revalidate(Image button, Image buttonL, Image buttonR) {
float center = container.getWidth() / 2f; float center = displayContainer.width / 2;
float baseY = getBaseY(container, game); float baseY = getBaseY(displayContainer);
float offsetY = button.getHeight() * 1.25f; float offsetY = button.getHeight() * 1.25f;
menuButtons = new MenuButton[buttons.length]; menuButtons = new MenuButton[buttons.length];
@ -328,25 +322,21 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Returns the base Y coordinate for the buttons. * Returns the base Y coordinate for the buttons.
* @param container the game container
* @param game the game
*/ */
protected float getBaseY(GameContainer container, StateBasedGame game) { protected float getBaseY(DisplayContainer displayContainer) {
float baseY = container.getHeight() * 0.2f; float baseY = displayContainer.height * 0.2f;
baseY += ((getTitle(container, game).length - 1) * Fonts.LARGE.getLineHeight()); baseY += ((getTitle().length - 1) * Fonts.LARGE.getLineHeight());
return baseY; return baseY;
} }
/** /**
* Draws the title and buttons to the graphics context. * Draws the title and buttons to the graphics context.
* @param container the game container
* @param game the game
* @param g the graphics context * @param g the graphics context
*/ */
public void draw(GameContainer container, StateBasedGame game, Graphics g) { public void render(Graphics g) {
// draw title // draw title
if (actualTitle != null) { if (actualTitle != null) {
float marginX = container.getWidth() * 0.015f, marginY = container.getHeight() * 0.01f; float marginX = displayContainer.width * 0.015f, marginY = displayContainer.height * 0.01f;
int lineHeight = Fonts.LARGE.getLineHeight(); int lineHeight = Fonts.LARGE.getLineHeight();
for (int i = 0, size = actualTitle.size(); i < size; i++) for (int i = 0, size = actualTitle.size(); i < size; i++)
Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white); Fonts.LARGE.drawString(marginX, marginY + (i * lineHeight), actualTitle.get(i), Color.white);
@ -361,17 +351,13 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Updates the menu state. * Updates the menu state.
* @param container the game container
* @param delta the delta interval
* @param mouseX the mouse x coordinate
* @param mouseY the mouse y coordinate
*/ */
public void update(GameContainer container, int delta, int mouseX, int mouseY) { public void preRenderUpdate() {
float center = container.getWidth() / 2f; float center = displayContainer.width / 2f;
boolean centerOffsetUpdated = centerOffset.update(delta); boolean centerOffsetUpdated = centerOffset.update(displayContainer.renderDelta);
float centerOffsetX = centerOffset.getValue(); float centerOffsetX = centerOffset.getValue();
for (int i = 0; i < buttons.length; i++) { for (int i = 0; i < buttons.length; i++) {
menuButtons[i].hoverUpdate(delta, mouseX, mouseY); menuButtons[i].hoverUpdate(displayContainer.renderDelta, displayContainer.mouseX, displayContainer.mouseY);
// move button to center // move button to center
if (centerOffsetUpdated) if (centerOffsetUpdated)
@ -381,15 +367,11 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Processes a mouse click action. * Processes a mouse click action.
* @param container the game container
* @param game the game
* @param cx the x coordinate
* @param cy the y coordinate
*/ */
public void click(GameContainer container, StateBasedGame game, int cx, int cy) { public void mousePressed(int x, int y) {
for (int i = 0; i < buttons.length; i++) { for (int i = 0; i < buttons.length; i++) {
if (menuButtons[i].contains(cx, cy)) { if (menuButtons[i].contains(x, y)) {
buttons[i].click(container, game); buttons[i].click();
break; break;
} }
} }
@ -397,42 +379,34 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Processes a key press action. * Processes a key press action.
* @param container the game container
* @param game the game
* @param key the key code that was pressed (see {@link org.newdawn.slick.Input}) * @param key the key code that was pressed (see {@link org.newdawn.slick.Input})
* @param c the character of the key that was pressed * @param c the character of the key that was pressed
*/ */
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) { public void keyPressed(int key, char c) {
int index = Character.getNumericValue(c) - 1; int index = Character.getNumericValue(c) - 1;
if (index >= 0 && index < buttons.length) if (index >= 0 && index < buttons.length)
buttons[index].click(container, game); buttons[index].click();
} }
/** /**
* Retrieves the title strings for the menu state (via override). * Retrieves the title strings for the menu state (via override).
* @param container the game container
* @param game the game
*/ */
public String[] getTitle(GameContainer container, StateBasedGame game) { return new String[0]; } public String[] getTitle() { return new String[0]; }
/** /**
* Processes a mouse wheel movement. * Processes a mouse wheel movement.
* @param container the game container
* @param game the game
* @param newValue the amount that the mouse wheel moved * @param newValue the amount that the mouse wheel moved
*/ */
public void scroll(GameContainer container, StateBasedGame game, int newValue) { public void mouseWheelMoved(int newValue) {
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
} }
/** /**
* Processes a state enter request. * Processes a state enter request.
* @param container the game container
* @param game the game
*/ */
public void enter(GameContainer container, StateBasedGame game) { public void enter() {
float center = container.getWidth() / 2f; float center = displayContainer.width / 2f;
float centerOffsetX = container.getWidth() * OFFSET_WIDTH_RATIO; float centerOffsetX = displayContainer.width * OFFSET_WIDTH_RATIO;
centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE); centerOffset = new AnimatedValue(700, centerOffsetX, 0, AnimationEquation.OUT_BOUNCE);
for (int i = 0; i < buttons.length; i++) { for (int i = 0; i < buttons.length; i++) {
menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1)); menuButtons[i].setX(center + ((i % 2 == 0) ? centerOffsetX : centerOffsetX * -1));
@ -440,150 +414,149 @@ public class ButtonMenu extends BasicGameState {
} }
// create title string list // create title string list
actualTitle = new ArrayList<String>(); actualTitle = new ArrayList<>();
String[] title = getTitle(container, game); String[] title = getTitle();
int maxLineWidth = (int) (container.getWidth() * 0.96f); int maxLineWidth = (int) (displayContainer.width * 0.96f);
for (int i = 0; i < title.length; i++) { for (String aTitle : title) {
// wrap text if too long // wrap text if too long
if (Fonts.LARGE.getWidth(title[i]) > maxLineWidth) { if (Fonts.LARGE.getWidth(aTitle) > maxLineWidth) {
List<String> list = Fonts.wrap(Fonts.LARGE, title[i], maxLineWidth, false); List<String> list = Fonts.wrap(Fonts.LARGE, aTitle, maxLineWidth, false);
actualTitle.addAll(list); actualTitle.addAll(list);
} else } else {
actualTitle.add(title[i]); actualTitle.add(aTitle);
}
} }
} }
/** /**
* Processes a state exit request (via override). * Processes a state exit request (via override).
* @param container the game container
* @param game the game
*/ */
public void leave(GameContainer container, StateBasedGame game) {} public void leave() {}
}; }
/** Button types. */ /** Button types. */
private enum Button { private enum Button {
YES ("Yes", Color.green) { YES ("Yes", Color.green) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
container.exit(); displayContainer.exitRequested = true;
} }
}, },
NO ("No", Color.red) { NO ("No", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(MainMenu.class);
} }
}, },
CLEAR_SCORES ("Clear local scores", Color.magenta) { CLEAR_SCORES ("Clear local scores", Color.magenta) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP, node); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
FAVORITE_ADD ("Add to Favorites", Color.blue) { FAVORITE_ADD ("Add to Favorites", Color.blue) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
node.getBeatmapSet().setFavorite(true); node.getBeatmapSet().setFavorite(true);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
FAVORITE_REMOVE ("Remove from Favorites", Color.blue) { FAVORITE_REMOVE ("Remove from Favorites", Color.blue) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
node.getBeatmapSet().setFavorite(false); node.getBeatmapSet().setFavorite(false);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_FAVORITE);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
DELETE ("Delete...", Color.red) { DELETE ("Delete...", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ? MenuState ms = (node.beatmapIndex == -1 || node.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, node); instanceContainer.provide(ButtonMenu.class).setMenuState(ms, node);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
} }
}, },
CANCEL ("Cancel", Color.gray) { CANCEL ("Cancel", Color.gray) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) { DELETE_CONFIRM ("Yes, delete this beatmap!", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_CONFIRM, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
DELETE_GROUP ("Yes, delete all difficulties!", Color.red) { DELETE_GROUP ("Yes, delete all difficulties!", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
DELETE_CONFIRM.click(container, game); DELETE_CONFIRM.click();
} }
}, },
DELETE_SONG ("Yes, but only this difficulty", Color.red) { DELETE_SONG ("Yes, but only this difficulty", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
BeatmapSetNode node = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getNode(); BeatmapSetNode node = instanceContainer.provide(ButtonMenu.class).getNode();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.BEATMAP_DELETE_SELECT, node);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) { CANCEL_DELETE ("Nooooo! I didn't mean to!", Color.gray) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
CANCEL.click(container, game); CANCEL.click();
} }
}, },
RELOAD_CONFIRM ("Let's do it!", Color.green) { RELOAD_CONFIRM ("Let's do it!", Color.green) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.RELOAD); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.RELOAD);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
RELOAD_CANCEL ("Cancel", Color.red) { RELOAD_CANCEL ("Cancel", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
CANCEL.click(container, game); CANCEL.click();
} }
}, },
DELETE_SCORE ("Delete score", Color.green) { DELETE_SCORE ("Delete score", Color.green) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
ScoreData scoreData = ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).getScoreData(); ScoreData scoreData = instanceContainer.provide(ButtonMenu.class).getScoreData();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).doStateActionOnLoad(MenuState.SCORE, scoreData); instanceContainer.provide(SongMenu.class).doStateActionOnLoad(MenuState.SCORE, scoreData);
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition()); displayContainer.switchState(SongMenu.class);
} }
}, },
CLOSE ("Close", Color.gray) { CLOSE ("Close", Color.gray) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
CANCEL.click(container, game); CANCEL.click();
} }
}, },
RESET_MODS ("Reset All Mods", Color.red) { RESET_MODS ("Reset All Mods", Color.red) {
@Override @Override
public void click(GameContainer container, StateBasedGame game) { public void click() {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
for (GameMod mod : GameMod.values()) { for (GameMod mod : GameMod.values()) {
if (mod.isActive()) if (mod.isActive())
@ -592,6 +565,9 @@ public class ButtonMenu extends BasicGameState {
} }
}; };
public static DisplayContainer displayContainer;
public static InstanceContainer instanceContainer;
/** The text to show on the button. */ /** The text to show on the button. */
private final String text; private final String text;
@ -620,10 +596,8 @@ public class ButtonMenu extends BasicGameState {
/** /**
* Processes a mouse click action (via override). * Processes a mouse click action (via override).
* @param container the game container
* @param game the game
*/ */
public void click(GameContainer container, StateBasedGame game) {} public void click() {}
} }
/** The current menu state. */ /** The current menu state. */
@ -635,97 +609,83 @@ public class ButtonMenu extends BasicGameState {
/** The score data to process in the state. */ /** The score data to process in the state. */
private ScoreData scoreData; private ScoreData scoreData;
// game-related variables public ButtonMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
private GameContainer container; super(displayContainer);
private StateBasedGame game; Button.displayContainer = MenuState.displayContainer = displayContainer;
private Input input; Button.instanceContainer = MenuState.instanceContainer = instanceContainer;
private final int state;
public ButtonMenu(int state) {
this.state = state;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
this.game = game;
this.input = container.getInput();
// initialize buttons // initialize buttons
Image button = GameImage.MENU_BUTTON_MID.getImage(); Image button = GameImage.MENU_BUTTON_MID.getImage();
button = button.getScaledCopy(container.getWidth() / 2, button.getHeight()); button = button.getScaledCopy(displayContainer.width / 2, button.getHeight());
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
for (MenuState ms : MenuState.values()) for (MenuState ms : MenuState.values()) {
ms.init(container, game, button, buttonL, buttonR); ms.revalidate(button, buttonL, buttonR);
}
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException { super.render(g);
g.setBackground(Color.black); g.setBackground(Color.black);
if (menuState != null) if (menuState == null) {
menuState.draw(container, game, g);
}
@Override
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
UI.update(delta);
MusicController.loopTrackIfEnded(false);
if (menuState != null)
menuState.update(container, delta, input.getMouseX(), input.getMouseY());
}
@Override
public int getID() { return state; }
@Override
public void mousePressed(int button, int x, int y) {
// check mouse button
if (button == Input.MOUSE_MIDDLE_BUTTON)
return; return;
}
if (menuState != null) menuState.render(g);
menuState.click(container, game, x, y);
} }
@Override @Override
public void mouseWheelMoved(int newValue) { public void preRenderUpdate() {
if (menuState != null) super.preRenderUpdate();
menuState.scroll(container, game, newValue);
UI.update(displayContainer.renderDelta);
MusicController.loopTrackIfEnded(false);
menuState.preRenderUpdate();
} }
@Override @Override
public void keyPressed(int key, char c) { public boolean mousePressed(int button, int x, int y) {
switch (key) { if (button == Input.MOUSE_MIDDLE_BUTTON) {
case Input.KEY_ESCAPE: return false;
if (menuState != null)
menuState.leave(container, game);
break;
case Input.KEY_F7:
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
default:
if (menuState != null)
menuState.keyPress(container, game, key, c);
break;
} }
menuState.mousePressed(x, y);
return true;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public boolean mouseWheelMoved(int newValue) {
throws SlickException { menuState.mouseWheelMoved(newValue);
return true;
}
@Override
public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
if (key == Input.KEY_ESCAPE) {
menuState.leave();
return true;
}
menuState.keyPressed(key, c);
return true;
}
@Override
public void enter() {
super.enter();
UI.enter(); UI.enter();
if (menuState != null) menuState.enter();
menuState.enter(container, game);
} }
/** /**

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
@ -51,17 +50,15 @@ import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener; import javax.sound.sampled.LineListener;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.gui.TextField; import org.newdawn.slick.gui.TextField;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState;
/** /**
* Downloads menu. * Downloads menu.
@ -69,7 +66,10 @@ import org.newdawn.slick.util.Log;
* Players are able to download beatmaps off of various servers and import them * Players are able to download beatmaps off of various servers and import them
* from this state. * from this state.
*/ */
public class DownloadsMenu extends BasicGameState { public class DownloadsMenu extends ComplexOpsuState {
private final InstanceContainer instanceContainer;
/** Delay time, in milliseconds, between each search. */ /** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 700; private static final int SEARCH_DELAY = 700;
@ -288,45 +288,42 @@ public class DownloadsMenu extends BasicGameState {
} }
} }
// game-related variables public DownloadsMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
private GameContainer container; super(displayContainer);
private StateBasedGame game; this.instanceContainer = instanceContainer;
private Input input;
private final int state;
public DownloadsMenu(int state) {
this.state = state;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
this.game = game;
this.input = container.getInput();
int width = container.getWidth(); components.clear();
int height = container.getHeight();
float baseX = width * 0.024f; int width = displayContainer.width;
float searchY = (height * 0.04f) + Fonts.LARGE.getLineHeight(); int height = displayContainer.height;
float searchWidth = width * 0.3f; int baseX = (int) (width * 0.024f);
int searchY = (int) (height * 0.04f + Fonts.LARGE.getLineHeight());
int searchWidth = (int) (width * 0.3f);
// search // search
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchResultString = "Loading data from server..."; searchResultString = "Loading data from server...";
search = new TextField( search = new TextField(displayContainer, Fonts.DEFAULT, baseX, searchY, searchWidth, Fonts.MEDIUM.getLineHeight()) {
container, Fonts.DEFAULT, (int) baseX, (int) searchY, @Override
(int) searchWidth, Fonts.MEDIUM.getLineHeight() public boolean isFocusable() {
); return false;
}
};
search.setFocused(true);
search.setBackgroundColor(Colors.BLACK_BG_NORMAL); search.setBackgroundColor(Colors.BLACK_BG_NORMAL);
search.setBorderColor(Color.white); search.setBorderColor(Color.white);
search.setTextColor(Color.white); search.setTextColor(Color.white);
search.setConsumeEvents(false);
search.setMaxLength(255); search.setMaxLength(255);
components.add(search);
// page buttons // page buttons
float pageButtonY = height * 0.2f; int pageButtonY = (int) (height * 0.2f);
float pageButtonWidth = width * 0.7f; int pageButtonWidth = (int) (width * 0.7f);
Image prevImg = GameImage.MUSIC_PREVIOUS.getImage(); Image prevImg = GameImage.MUSIC_PREVIOUS.getImage();
Image nextImg = GameImage.MUSIC_NEXT.getImage(); Image nextImg = GameImage.MUSIC_NEXT.getImage();
prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f, prevPage = new MenuButton(prevImg, baseX + prevImg.getWidth() / 2f,
@ -337,25 +334,25 @@ public class DownloadsMenu extends BasicGameState {
nextPage.setHoverExpand(1.5f); nextPage.setHoverExpand(1.5f);
// buttons // buttons
float buttonMarginX = width * 0.004f; int buttonMarginX = (int) (width * 0.004f);
float buttonHeight = height * 0.038f; int buttonHeight = (int) (height * 0.038f);
float resetWidth = width * 0.085f; int resetWidth = (int) (width * 0.085f);
float rankedWidth = width * 0.15f; int rankedWidth = (int) (width * 0.15f);
float lowerWidth = width * 0.12f; int lowerWidth = (int) (width * 0.12f);
float topButtonY = searchY + Fonts.MEDIUM.getLineHeight() / 2f; int topButtonY = (int) (searchY + Fonts.MEDIUM.getLineHeight() / 2f);
float lowerButtonY = height * 0.995f - searchY - buttonHeight / 2f; int lowerButtonY = (int) (height * 0.995f - searchY - buttonHeight / 2f);
Image button = GameImage.MENU_BUTTON_MID.getImage(); Image button = GameImage.MENU_BUTTON_MID.getImage();
Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage(); Image buttonL = GameImage.MENU_BUTTON_LEFT.getImage();
Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage(); Image buttonR = GameImage.MENU_BUTTON_RIGHT.getImage();
buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight()); buttonL = buttonL.getScaledCopy(buttonHeight / buttonL.getHeight());
buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight()); buttonR = buttonR.getScaledCopy(buttonHeight / buttonR.getHeight());
int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth(); int lrButtonWidth = buttonL.getWidth() + buttonR.getWidth();
Image resetButtonImage = button.getScaledCopy((int) resetWidth - lrButtonWidth, (int) buttonHeight); Image resetButtonImage = button.getScaledCopy(resetWidth - lrButtonWidth, buttonHeight);
Image rankedButtonImage = button.getScaledCopy((int) rankedWidth - lrButtonWidth, (int) buttonHeight); Image rankedButtonImage = button.getScaledCopy(rankedWidth - lrButtonWidth, buttonHeight);
Image lowerButtonImage = button.getScaledCopy((int) lowerWidth - lrButtonWidth, (int) buttonHeight); Image lowerButtonImage = button.getScaledCopy(lowerWidth - lrButtonWidth, buttonHeight);
float resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth; int resetButtonWidth = resetButtonImage.getWidth() + lrButtonWidth;
float rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth; int rankedButtonWidth = rankedButtonImage.getWidth() + lrButtonWidth;
float lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth; int lowerButtonWidth = lowerButtonImage.getWidth() + lrButtonWidth;
clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR, clearButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY); width * 0.75f + buttonMarginX + lowerButtonWidth / 2f, lowerButtonY);
importButton = new MenuButton(lowerButtonImage, buttonL, buttonR, importButton = new MenuButton(lowerButtonImage, buttonL, buttonR,
@ -374,8 +371,8 @@ public class DownloadsMenu extends BasicGameState {
// dropdown menu // dropdown menu
int serverWidth = (int) (width * 0.12f); int serverWidth = (int) (width * 0.12f);
serverMenu = new DropdownMenu<DownloadServer>(container, SERVERS, int x = baseX + searchWidth + buttonMarginX * 3 + resetButtonWidth + rankedButtonWidth;
baseX + searchWidth + buttonMarginX * 3f + resetButtonWidth + rankedButtonWidth, searchY, serverWidth) { serverMenu = new DropdownMenu<DownloadServer>(displayContainer, SERVERS, x, searchY, serverWidth) {
@Override @Override
public void itemSelected(int index, DownloadServer item) { public void itemSelected(int index, DownloadServer item) {
resultList = null; resultList = null;
@ -388,13 +385,14 @@ public class DownloadsMenu extends BasicGameState {
searchResultString = "Loading data from server..."; searchResultString = "Loading data from server...";
lastQuery = null; lastQuery = null;
pageDir = Page.RESET; pageDir = Page.RESET;
if (searchQuery != null) if (searchQuery != null) {
searchQuery.interrupt(); searchQuery.interrupt();
}
resetSearchTimer(); resetSearchTimer();
} }
@Override @Override
public boolean menuClicked(int index) { public boolean canSelect(int index) {
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null)
return false; return false;
@ -406,28 +404,25 @@ public class DownloadsMenu extends BasicGameState {
serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); serverMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
serverMenu.setBorderColor(Color.black); serverMenu.setBorderColor(Color.black);
serverMenu.setChevronRightColor(Color.white); serverMenu.setChevronRightColor(Color.white);
components.add(serverMenu);
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException { super.render(g);
int width = container.getWidth();
int height = container.getHeight();
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY);
// background // background
GameImage.SEARCH_BG.getImage().draw(); GameImage.SEARCH_BG.getImage().draw();
// title // title
Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white); Fonts.LARGE.drawString(displayContainer.width * 0.024f, displayContainer.height * 0.03f, "Download Beatmaps!", Color.white);
// search // search
g.setColor(Color.white); g.setColor(Color.white);
g.setLineWidth(2f); g.setLineWidth(2f);
search.render(container, g); search.render(g);
Fonts.BOLD.drawString( Fonts.BOLD.drawString(
search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f, search.x + search.width * 0.01f, search.y + search.height * 1.3f,
searchResultString, Color.white searchResultString, Color.white
); );
@ -446,7 +441,7 @@ public class DownloadsMenu extends BasicGameState {
if (index >= nodes.length) if (index >= nodes.length)
break; break;
nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(), nodes[index].drawResult(g, offset + i * DownloadNode.getButtonOffset(),
DownloadNode.resultContains(mouseX, mouseY - offset, i) && !inDropdownMenu, DownloadNode.resultContains(displayContainer.mouseX, displayContainer.mouseY - offset, i) && !serverMenu.isHovered(),
(index == focusResult), (previewID == nodes[index].getID())); (index == focusResult), (previewID == nodes[index].getID()));
} }
g.clearClip(); g.clearClip();
@ -457,9 +452,9 @@ public class DownloadsMenu extends BasicGameState {
// pages // pages
if (nodes.length > 0) { if (nodes.length > 0) {
float baseX = width * 0.024f; float baseX = displayContainer.width * 0.024f;
float buttonY = height * 0.2f; float buttonY = displayContainer.height * 0.2f;
float buttonWidth = width * 0.7f; float buttonWidth = displayContainer.width * 0.7f;
Fonts.BOLD.drawString( Fonts.BOLD.drawString(
baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f, baseX + (buttonWidth - Fonts.BOLD.getWidth("Page 1")) / 2f,
buttonY - Fonts.BOLD.getLineHeight() * 1.3f, buttonY - Fonts.BOLD.getLineHeight() * 1.3f,
@ -473,11 +468,11 @@ public class DownloadsMenu extends BasicGameState {
} }
// downloads // downloads
float downloadsX = width * 0.75f, downloadsY = search.getY(); float downloadsX = displayContainer.width * 0.75f, downloadsY = search.y;
g.setColor(Colors.BLACK_BG_NORMAL); g.setColor(Colors.BLACK_BG_NORMAL);
g.fillRect(downloadsX, downloadsY, g.fillRect(downloadsX, downloadsY,
width * 0.25f, height - downloadsY * 2f); displayContainer.width * 0.25f, displayContainer.height - downloadsY * 2f);
Fonts.LARGE.drawString(downloadsX + width * 0.015f, downloadsY + height * 0.015f, "Downloads", Color.white); Fonts.LARGE.drawString(downloadsX + displayContainer.width * 0.015f, downloadsY + displayContainer.height * 0.015f, "Downloads", Color.white);
int downloadsSize = DownloadList.get().size(); int downloadsSize = DownloadList.get().size();
if (downloadsSize > 0) { if (downloadsSize > 0) {
int maxDownloadsShown = DownloadNode.maxDownloadsShown(); int maxDownloadsShown = DownloadNode.maxDownloadsShown();
@ -493,7 +488,7 @@ public class DownloadsMenu extends BasicGameState {
if (node == null) if (node == null)
break; break;
node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index, node.drawDownload(g, i * DownloadNode.getInfoHeight() + offset, index,
DownloadNode.downloadContains(mouseX, mouseY - offset, i)); DownloadNode.downloadContains(displayContainer.mouseX, displayContainer.mouseY - offset, i));
} }
g.clearClip(); g.clearClip();
@ -510,13 +505,13 @@ public class DownloadsMenu extends BasicGameState {
rankedButton.draw(Color.magenta); rankedButton.draw(Color.magenta);
// dropdown menu // dropdown menu
serverMenu.render(container, g); serverMenu.render(g);
// importing beatmaps // importing beatmaps
if (importThread != null) { if (importThread != null) {
// darken the screen // darken the screen
g.setColor(Colors.BLACK_ALPHA); g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, 0, width, height); g.fillRect(0, 0, displayContainer.width, displayContainer.height);
UI.drawLoadingProgress(g); UI.drawLoadingProgress(g);
} }
@ -529,8 +524,10 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { super.preRenderUpdate();
int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
if (importThread == null) if (importThread == null)
MusicController.loopTrackIfEnded(false); MusicController.loopTrackIfEnded(false);
@ -547,11 +544,12 @@ public class DownloadsMenu extends BasicGameState {
// focus new beatmap // focus new beatmap
// NOTE: This can't be called in another thread because it makes OpenGL calls. // NOTE: This can't be called in another thread because it makes OpenGL calls.
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(importedNode, -1, true, true); instanceContainer.provide(SongMenu.class).setFocus(importedNode, -1, true, true);
} }
importThread = null; importThread = null;
} }
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
prevPage.hoverUpdate(delta, mouseX, mouseY); prevPage.hoverUpdate(delta, mouseX, mouseY);
nextPage.hoverUpdate(delta, mouseX, mouseY); nextPage.hoverUpdate(delta, mouseX, mouseY);
@ -572,7 +570,6 @@ public class DownloadsMenu extends BasicGameState {
focusTimer += delta; focusTimer += delta;
// search // search
search.setFocus(true);
searchTimer += delta; searchTimer += delta;
if (searchTimer >= SEARCH_DELAY && importThread == null) { if (searchTimer >= SEARCH_DELAY && importThread == null) {
searchTimer = 0; searchTimer = 0;
@ -608,24 +605,25 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public int getID() { return state; } public boolean mousePressed(int button, int x, int y) {
if (super.mousePressed(button, x, y)) {
return true;
}
@Override if (button == Input.MOUSE_MIDDLE_BUTTON) {
public void mousePressed(int button, int x, int y) { return false;
// check mouse button }
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
// back // back
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); displayContainer.switchState(MainMenu.class);
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); return true;
return;
} }
// search results // search results
@ -694,11 +692,12 @@ public class DownloadsMenu extends BasicGameState {
} }
}.start(); }.start();
} }
return; return true;
} }
if (isLoaded) if (isLoaded) {
return; return true;
}
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
if (index == focusResult) { if (index == focusResult) {
@ -725,7 +724,7 @@ public class DownloadsMenu extends BasicGameState {
break; break;
} }
} }
return; return true;
} }
// pages // pages
@ -741,7 +740,7 @@ public class DownloadsMenu extends BasicGameState {
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
} }
return; return true;
} }
if (pageResultTotal < totalResults && nextPage.contains(x, y)) { if (pageResultTotal < totalResults && nextPage.contains(x, y)) {
if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete()) if (lastQueryDir == Page.NEXT && searchQuery != null && !searchQuery.isComplete())
@ -753,7 +752,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
} }
} }
@ -763,7 +762,7 @@ public class DownloadsMenu extends BasicGameState {
if (clearButton.contains(x, y)) { if (clearButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
DownloadList.get().clearInactiveDownloads(); DownloadList.get().clearInactiveDownloads();
return; return true;
} }
if (importButton.contains(x, y)) { if (importButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -771,7 +770,7 @@ public class DownloadsMenu extends BasicGameState {
// import songs in new thread // import songs in new thread
importThread = new BeatmapImportThread(); importThread = new BeatmapImportThread();
importThread.start(); importThread.start();
return; return true;
} }
if (resetButton.contains(x, y)) { if (resetButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -781,7 +780,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
if (rankedButton.contains(x, y)) { if (rankedButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
@ -791,7 +790,7 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
return; return true;
} }
// downloads // downloads
@ -807,8 +806,9 @@ public class DownloadsMenu extends BasicGameState {
if (DownloadNode.downloadIconContains(x, y - offset, i)) { if (DownloadNode.downloadIconContains(x, y - offset, i)) {
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
DownloadNode node = DownloadList.get().getNode(index); DownloadNode node = DownloadList.get().getNode(index);
if (node == null) if (node == null) {
return; return true;
}
Download dl = node.getDownload(); Download dl = node.getDownload();
switch (dl.getStatus()) { switch (dl.getStatus()) {
case CANCELLED: case CANCELLED:
@ -822,62 +822,78 @@ public class DownloadsMenu extends BasicGameState {
dl.cancel(); dl.cancel();
break; break;
} }
return; return true;
} }
} }
} }
return false;
} }
@Override @Override
public void mouseReleased(int button, int x, int y) { public boolean mouseReleased(int button, int x, int y) {
// check mouse button if (super.mouseReleased(button, x, y)) {
if (button == Input.MOUSE_MIDDLE_BUTTON) return true;
return; }
if (button == Input.MOUSE_MIDDLE_BUTTON) {
return false;
}
startDownloadIndexPos.released(); startDownloadIndexPos.released();
startResultPos.released(); startResultPos.released();
return true;
} }
@Override @Override
public void mouseWheelMoved(int newValue) { public boolean mouseWheelMoved(int newValue) {
// change volume // change volume
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
return; return true;
} }
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
int shift = (newValue < 0) ? 1 : -1; int shift = (newValue < 0) ? 1 : -1;
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); scrollLists(displayContainer.mouseX, displayContainer.mouseY, shift);
scrollLists(mouseX, mouseY, shift); return true;
} }
@Override @Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) { public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
// block input during beatmap importing // block input during beatmap importing
if (importThread != null) if (importThread != null) {
return; return true;
}
// check mouse button
if (!input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON) &&
!input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
return;
int diff = newy - oldy; int diff = newy - oldy;
if (diff == 0) if (diff == 0) {
return; return false;
}
startDownloadIndexPos.dragged(-diff); startDownloadIndexPos.dragged(-diff);
startResultPos.dragged(-diff); startResultPos.dragged(-diff);
return true;
} }
@Override @Override
public void keyPressed(int key, char c) { public boolean keyReleased(int key, char c) {
return super.keyReleased(key, c);
}
@Override
public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
// block input during beatmap importing // block input during beatmap importing
if (importThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) if (importThread != null && key != Input.KEY_ESCAPE) {
return; return true;
}
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case Input.KEY_ESCAPE:
@ -892,16 +908,15 @@ public class DownloadsMenu extends BasicGameState {
} else { } else {
// return to main menu // return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); displayContainer.switchState(MainMenu.class);
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
} }
break; return true;
case Input.KEY_ENTER: case Input.KEY_ENTER:
if (!search.getText().isEmpty()) { if (!search.getText().isEmpty()) {
pageDir = Page.RESET; pageDir = Page.RESET;
resetSearchTimer(); resetSearchTimer();
} }
break; return true;
case Input.KEY_F5: case Input.KEY_F5:
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
lastQuery = null; lastQuery = null;
@ -909,29 +924,21 @@ public class DownloadsMenu extends BasicGameState {
if (searchQuery != null) if (searchQuery != null)
searchQuery.interrupt(); searchQuery.interrupt();
resetSearchTimer(); resetSearchTimer();
break; return true;
case Input.KEY_F7: }
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
default:
// wait for user to finish typing // wait for user to finish typing
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) { if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK) {
search.keyPressed(key, c);
searchTimer = 0; searchTimer = 0;
pageDir = Page.RESET; pageDir = Page.RESET;
} }
break; return true;
}
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public void enter() {
throws SlickException { super.enter();
UI.enter(); UI.enter();
prevPage.resetHover(); prevPage.resetHover();
nextPage.resetHover(); nextPage.resetHover();
@ -939,7 +946,6 @@ public class DownloadsMenu extends BasicGameState {
importButton.resetHover(); importButton.resetHover();
resetButton.resetHover(); resetButton.resetHover();
rankedButton.resetHover(); rankedButton.resetHover();
serverMenu.activate();
serverMenu.reset(); serverMenu.reset();
focusResult = -1; focusResult = -1;
startResultPos.setPosition(0); startResultPos.setPosition(0);
@ -953,10 +959,10 @@ public class DownloadsMenu extends BasicGameState {
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) public void leave() {
throws SlickException { super.leave();
search.setFocus(false);
serverMenu.deactivate(); focusComponent(search);
SoundController.stopTrack(); SoundController.stopTrack();
MusicController.resume(); MusicController.resume();
} }

File diff suppressed because it is too large Load Diff

View File

@ -19,9 +19,7 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
@ -31,14 +29,11 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.BasicGameState; import yugecin.opsudance.core.inject.InstanceContainer;
import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.state.BaseOpsuState;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
/** /**
* "Game Pause/Fail" state. * "Game Pause/Fail" state.
@ -46,33 +41,22 @@ import org.newdawn.slick.state.transition.EasedFadeOutTransition;
* Players are able to continue the game (if applicable), retry the beatmap, * Players are able to continue the game (if applicable), retry the beatmap,
* or return to the song menu from this state. * or return to the song menu from this state.
*/ */
public class GamePauseMenu extends BasicGameState { public class GamePauseMenu extends BaseOpsuState {
/** "Continue", "Retry", and "Back" buttons. */
private final InstanceContainer instanceContainer;
private MenuButton continueButton, retryButton, backButton; private MenuButton continueButton, retryButton, backButton;
// game-related variables private final Game gameState;
private GameContainer container;
private StateBasedGame game;
private Input input;
private final int state;
private Game gameState;
public GamePauseMenu(int state) { public GamePauseMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer, Game gameState) {
this.state = state; super(displayContainer);
this.instanceContainer = instanceContainer;
this.gameState = gameState;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void render(Graphics g) {
throws SlickException {
this.container = container;
this.game = game;
this.input = container.getInput();
this.gameState = (Game) game.getState(Opsu.STATE_GAME);
}
@Override
public void render(GameContainer container, StateBasedGame game, Graphics g)
throws SlickException {
// get background image // get background image
GameImage bg = (gameState.getRestart() == Game.Restart.LOSE) ? GameImage bg = (gameState.getRestart() == Game.Restart.LOSE) ?
GameImage.FAIL_BACKGROUND : GameImage.PAUSE_OVERLAY; GameImage.FAIL_BACKGROUND : GameImage.PAUSE_OVERLAY;
@ -97,102 +81,107 @@ public class GamePauseMenu extends BasicGameState {
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); continueButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
continueButton.hoverUpdate(delta, mouseX, mouseY); retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
retryButton.hoverUpdate(delta, mouseX, mouseY); backButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
backButton.hoverUpdate(delta, mouseX, mouseY);
} }
@Override @Override
public int getID() { return state; } public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
@Override
public void keyPressed(int key, char c) {
// game keys // game keys
if (!Keyboard.isRepeatEvent()) { if (!Keyboard.isRepeatEvent()) {
if (key == Options.getGameKeyLeft()) if (key == Options.getGameKeyLeft()) {
mousePressed(Input.MOUSE_LEFT_BUTTON, input.getMouseX(), input.getMouseY()); mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
else if (key == Options.getGameKeyRight()) } else if (key == Options.getGameKeyRight()) {
mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY()); mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
}
} }
switch (key) { if (key == Input.KEY_ESCAPE) {
case Input.KEY_ESCAPE:
// 'esc' will normally unpause, but will return to song menu if health is zero // 'esc' will normally unpause, but will return to song menu if health is zero
if (gameState.getRestart() == Game.Restart.LOSE) { if (gameState.getRestart() == Game.Restart.LOSE) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
MusicController.playAt(MusicController.getBeatmap().previewTime, true); MusicController.playAt(MusicController.getBeatmap().previewTime, true);
if (UI.getCursor().isBeatmapSkinned()) displayContainer.switchState(SongMenu.class);
UI.getCursor().reset();
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
} else { } else {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE); gameState.setRestart(Game.Restart.FALSE);
game.enterState(Opsu.STATE_GAME); displayContainer.switchState(Game.class);
} }
break; return true;
case Input.KEY_R: }
// restart
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { if (key == Input.KEY_R && (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL))) {
gameState.setRestart(Game.Restart.MANUAL); gameState.setRestart(Game.Restart.MANUAL);
game.enterState(Opsu.STATE_GAME); displayContainer.switchState(Game.class);
} return true;
break;
case Input.KEY_F7:
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
} }
return false;
} }
@Override @Override
public void mousePressed(int button, int x, int y) { public boolean mousePressed(int button, int x, int y) {
if (button == Input.MOUSE_MIDDLE_BUTTON) if (super.mousePressed(button, x, y)) {
return; return true;
}
if (button == Input.MOUSE_MIDDLE_BUTTON) {
return true;
}
boolean loseState = (gameState.getRestart() == Game.Restart.LOSE); boolean loseState = (gameState.getRestart() == Game.Restart.LOSE);
if (continueButton.contains(x, y) && !loseState) { if (continueButton.contains(x, y) && !loseState) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
gameState.setRestart(Game.Restart.FALSE); gameState.setRestart(Game.Restart.FALSE);
game.enterState(Opsu.STATE_GAME); displayContainer.switchState(Game.class);
} else if (retryButton.contains(x, y)) { } else if (retryButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
gameState.setRestart(Game.Restart.MANUAL); gameState.setRestart(Game.Restart.MANUAL);
game.enterState(Opsu.STATE_GAME); displayContainer.switchState(Game.class);
} else if (backButton.contains(x, y)) { } else if (backButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); instanceContainer.provide(SongMenu.class).resetGameDataOnLoad();
if (loseState) if (loseState)
MusicController.playAt(MusicController.getBeatmap().previewTime, true); MusicController.playAt(MusicController.getBeatmap().previewTime, true);
else else
MusicController.resume(); MusicController.resume();
if (UI.getCursor().isBeatmapSkinned()) if (displayContainer.cursor.isBeatmapSkinned()) {
UI.getCursor().reset(); displayContainer.resetCursor();
MusicController.setPitch(1.0f);
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition());
} }
MusicController.setPitch(1.0f);
displayContainer.switchState(SongMenu.class);
}
return true;
} }
@Override @Override
public void mouseWheelMoved(int newValue) { public boolean mouseWheelMoved(int newValue) {
if (Options.isMouseWheelDisabled()) if (super.mouseWheelMoved(newValue)) {
return; return true;
}
if (Options.isMouseWheelDisabled()) {
return true;
}
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
return true;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public void enter() {
throws SlickException { super.enter();
UI.enter(); UI.enter();
MusicController.pause(); MusicController.pause();
continueButton.resetHover(); continueButton.resetHover();
@ -200,17 +189,23 @@ public class GamePauseMenu extends BasicGameState {
backButton.resetHover(); backButton.resetHover();
} }
@Override
public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
songmenu.resetTrackOnLoad();
songmenu.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class);
return false;
}
/** /**
* Loads all game pause/fail menu images. * Loads all game pause/fail menu images.
*/ */
public void loadImages() { public void loadImages() {
int width = container.getWidth();
int height = container.getHeight();
// initialize buttons // initialize buttons
continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), width / 2f, height * 0.25f); continueButton = new MenuButton(GameImage.PAUSE_CONTINUE.getImage(), displayContainer.width / 2f, displayContainer.height * 0.25f);
retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), width / 2f, height * 0.5f); retryButton = new MenuButton(GameImage.PAUSE_RETRY.getImage(), displayContainer.width / 2f, displayContainer.height * 0.5f);
backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), width / 2f, height * 0.75f); backButton = new MenuButton(GameImage.PAUSE_BACK.getImage(), displayContainer.width / 2f, displayContainer.height * 0.75f);
final int buttonAnimationDuration = 300; final int buttonAnimationDuration = 300;
continueButton.setHoverAnimationDuration(buttonAnimationDuration); continueButton.setHoverAnimationDuration(buttonAnimationDuration);
retryButton.setHoverAnimationDuration(buttonAnimationDuration); retryButton.setHoverAnimationDuration(buttonAnimationDuration);
@ -223,4 +218,5 @@ public class GamePauseMenu extends BasicGameState {
retryButton.setHoverExpand(); retryButton.setHoverExpand();
backButton.setHoverExpand(); backButton.setHoverExpand();
} }
} }

View File

@ -21,9 +21,6 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
@ -35,17 +32,13 @@ import itdelatrisu.opsu.ui.UI;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import org.lwjgl.opengl.Display;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.EasedFadeOutTransition;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState;
/** /**
* "Game Ranking" (score card) state. * "Game Ranking" (score card) state.
@ -54,7 +47,10 @@ import org.newdawn.slick.util.Log;
* or watch a replay of the game from this state. * or watch a replay of the game from this state.
* </ul> * </ul>
*/ */
public class GameRanking extends BasicGameState { public class GameRanking extends BaseOpsuState {
private final InstanceContainer instanceContainer;
/** Associated GameData object. */ /** Associated GameData object. */
private GameData data; private GameData data;
@ -64,48 +60,35 @@ public class GameRanking extends BasicGameState {
/** Button coordinates. */ /** Button coordinates. */
private float retryY, replayY; private float retryY, replayY;
// game-related variables public GameRanking(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
private GameContainer container; super(displayContainer);
private StateBasedGame game; this.instanceContainer = instanceContainer;
private final int state;
private Input input;
public GameRanking(int state) {
this.state = state;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
this.game = game;
this.input = container.getInput();
int width = container.getWidth();
int height = container.getHeight();
// buttons // buttons
Image retry = GameImage.PAUSE_RETRY.getImage(); Image retry = GameImage.PAUSE_RETRY.getImage();
Image replay = GameImage.PAUSE_REPLAY.getImage(); Image replay = GameImage.PAUSE_REPLAY.getImage();
replayY = (height * 0.985f) - replay.getHeight() / 2f; replayY = (displayContainer.height * 0.985f) - replay.getHeight() / 2f;
retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f); retryY = replayY - (replay.getHeight() / 2f) - (retry.getHeight() / 1.975f);
retryButton = new MenuButton(retry, width - (retry.getWidth() / 2f), retryY); retryButton = new MenuButton(retry, displayContainer.width - (retry.getWidth() / 2f), retryY);
replayButton = new MenuButton(replay, width - (replay.getWidth() / 2f), replayY); replayButton = new MenuButton(replay, displayContainer.width - (replay.getWidth() / 2f), replayY);
retryButton.setHoverFade(); retryButton.setHoverFade();
replayButton.setHoverFade(); replayButton.setHoverFade();
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException {
int width = container.getWidth();
int height = container.getHeight();
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
// background // background
if (!beatmap.drawBackground(width, height, 0.7f, true)) if (!beatmap.drawBackground(displayContainer.width, displayContainer.height, 0.7f, true)) {
GameImage.PLAYFIELD.getImage().draw(0,0); GameImage.PLAYFIELD.getImage().draw(0, 0);
}
// ranking screen elements // ranking screen elements
data.drawRankingElements(g, beatmap); data.drawRankingElements(g, beatmap);
@ -117,62 +100,62 @@ public class GameRanking extends BasicGameState {
UI.getBackButton().draw(); UI.getBackButton().draw();
UI.draw(g); UI.draw(g);
super.render(g);
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); replayButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
replayButton.hoverUpdate(delta, mouseX, mouseY); if (data.isGameplay()) {
if (data.isGameplay()) retryButton.hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
retryButton.hoverUpdate(delta, mouseX, mouseY); } else {
else
MusicController.loopTrackIfEnded(true); MusicController.loopTrackIfEnded(true);
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); }
UI.getBackButton().hoverUpdate(delta, displayContainer.mouseX, displayContainer.mouseY);
} }
@Override @Override
public int getID() { return state; } public boolean mouseWheelMoved(int newValue) {
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
@Override
public void mouseWheelMoved(int newValue) {
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT))
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
} }
return true;
}
@Override @Override
public void keyPressed(int key, char c) { public boolean keyPressed(int key, char c) {
switch (key) { if (super.keyPressed(key, c)) {
case Input.KEY_ESCAPE: return true;
}
if (key == Input.KEY_ESCAPE) {
returnToSongMenu(); returnToSongMenu();
break;
case Input.KEY_F7:
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
} }
return true;
} }
@Override @Override
public void mousePressed(int button, int x, int y) { public boolean mousePressed(int button, int x, int y) {
if (super.mousePressed(button, x, y)) {
return true;
}
// check mouse button // check mouse button
if (button == Input.MOUSE_MIDDLE_BUTTON) if (button == Input.MOUSE_MIDDLE_BUTTON) {
return; return false;
}
// back to menu // back to menu
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
returnToSongMenu(); returnToSongMenu();
return; return true;
} }
// replay // replay
Game gameState = (Game) game.getState(Opsu.STATE_GAME); Game gameState = instanceContainer.provide(Game.class);
boolean returnToGame = false; boolean returnToGame = false;
boolean replayButtonPressed = replayButton.contains(x, y); boolean replayButtonPressed = replayButton.contains(x, y);
if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) { if (replayButtonPressed && !(data.isGameplay() && GameMod.AUTO.isActive())) {
@ -206,16 +189,16 @@ public class GameRanking extends BasicGameState {
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
gameState.loadBeatmap(beatmap); gameState.loadBeatmap(beatmap);
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition()); // TODO d displayContainer.switchState(Game.class);
return;
} }
return true;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public void enter() {
throws SlickException { super.enter();
UI.enter(); UI.enter();
Display.setTitle(game.getTitle());
if (!data.isGameplay()) { if (!data.isGameplay()) {
if (!MusicController.isTrackDimmed()) if (!MusicController.isTrackDimmed())
MusicController.toggleTrackDimmed(0.5f); MusicController.toggleTrackDimmed(0.5f);
@ -229,12 +212,25 @@ public class GameRanking extends BasicGameState {
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) public void leave() {
throws SlickException { super.leave();
this.data = null; this.data = null;
if (MusicController.isTrackDimmed()) if (MusicController.isTrackDimmed()) {
MusicController.toggleTrackDimmed(1f); MusicController.toggleTrackDimmed(1f);
} }
}
@Override
public boolean onCloseRequest() {
SongMenu songmenu = instanceContainer.provide(SongMenu.class);
if (data != null && data.isGameplay()) {
songmenu.resetTrackOnLoad();
}
songmenu.resetGameDataOnLoad();
displayContainer.switchState(SongMenu.class);
return false;
}
/** /**
* Returns to the song menu. * Returns to the song menu.
@ -242,13 +238,15 @@ public class GameRanking extends BasicGameState {
private void returnToSongMenu() { private void returnToSongMenu() {
SoundController.muteSoundComponent(); SoundController.muteSoundComponent();
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); SongMenu songMenu = instanceContainer.provide(SongMenu.class);
if (data.isGameplay()) if (data.isGameplay()) {
songMenu.resetTrackOnLoad(); songMenu.resetTrackOnLoad();
}
songMenu.resetGameDataOnLoad(); songMenu.resetGameDataOnLoad();
if (UI.getCursor().isBeatmapSkinned()) if (displayContainer.cursor.isBeatmapSkinned()) {
UI.getCursor().reset(); displayContainer.resetCursor();
game.enterState(Opsu.STATE_SONGMENU, new EasedFadeOutTransition(), new FadeInTransition()); }
displayContainer.switchState(SongMenu.class);
} }
/** /**

View File

@ -18,9 +18,7 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
@ -29,7 +27,6 @@ import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode; import itdelatrisu.opsu.beatmap.BeatmapSetNode;
import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.states.ButtonMenu.MenuState; import itdelatrisu.opsu.states.ButtonMenu.MenuState;
import itdelatrisu.opsu.ui.*; import itdelatrisu.opsu.ui.*;
@ -43,23 +40,28 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Stack; import java.util.Stack;
import org.lwjgl.opengl.Display;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.util.Log;
import org.newdawn.slick.state.BasicGameState; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.events.EventBus;
import org.newdawn.slick.state.transition.EasedFadeOutTransition; import yugecin.opsudance.core.inject.InstanceContainer;
import org.newdawn.slick.state.transition.FadeInTransition; import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* "Main Menu" state. * "Main Menu" state.
* <p> * <p>
* Players are able to enter the song menu or downloads menu from this state. * Players are able to enter the song menu or downloads menu from this state.
*/ */
public class MainMenu extends BasicGameState { public class MainMenu extends BaseOpsuState {
private final InstanceContainer instanceContainer;
/** Idle time, in milliseconds, before returning the logo to its original position. */ /** Idle time, in milliseconds, before returning the logo to its original position. */
private static final short LOGO_IDLE_DELAY = 10000; private static final short LOGO_IDLE_DELAY = 10000;
@ -123,40 +125,27 @@ public class MainMenu extends BasicGameState {
/** The star fountain. */ /** The star fountain. */
private StarFountain starFountain; private StarFountain starFountain;
// game-related variables public MainMenu(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
private GameContainer container; super(displayContainer);
private StateBasedGame game; this.instanceContainer = instanceContainer;
private Input input;
private final int state;
public MainMenu(int state) {
this.state = state;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) protected void revalidate() {
throws SlickException {
this.container = container;
this.game = game;
this.input = container.getInput();
programStartTime = System.currentTimeMillis(); programStartTime = System.currentTimeMillis();
previous = new Stack<Integer>(); previous = new Stack<>();
int width = container.getWidth();
int height = container.getHeight();
// initialize menu buttons // initialize menu buttons
Image logoImg = GameImage.MENU_LOGO.getImage(); Image logoImg = GameImage.MENU_LOGO.getImage();
Image playImg = GameImage.MENU_PLAY.getImage(); Image playImg = GameImage.MENU_PLAY.getImage();
Image exitImg = GameImage.MENU_EXIT.getImage(); Image exitImg = GameImage.MENU_EXIT.getImage();
float exitOffset = (playImg.getWidth() - exitImg.getWidth()) / 3f; float exitOffset = (playImg.getWidth() - exitImg.getWidth()) / 3f;
logo = new MenuButton(logoImg, width / 2f, height / 2f); logo = new MenuButton(logoImg, displayContainer.width / 2f, displayContainer.height / 2f);
playButton = new MenuButton(playImg, playButton = new MenuButton(playImg,
width * 0.75f, (height / 2) - (logoImg.getHeight() / 5f) displayContainer.width * 0.75f, (displayContainer.height / 2) - (logoImg.getHeight() / 5f)
); );
exitButton = new MenuButton(exitImg, exitButton = new MenuButton(exitImg,
width * 0.75f - exitOffset, (height / 2) + (exitImg.getHeight() / 2f) displayContainer.width * 0.75f - exitOffset, (displayContainer.height / 2) + (exitImg.getHeight() / 2f)
); );
final int logoAnimationDuration = 350; final int logoAnimationDuration = 350;
logo.setHoverAnimationDuration(logoAnimationDuration); logo.setHoverAnimationDuration(logoAnimationDuration);
@ -174,30 +163,30 @@ public class MainMenu extends BasicGameState {
// initialize music buttons // initialize music buttons
int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth(); int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth();
int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight(); int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight();
musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), width - (2 * musicWidth), musicHeight / 1.5f); musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f);
musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), width - (2 * musicWidth), musicHeight / 1.5f); musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), displayContainer.width - (2 * musicWidth), musicHeight / 1.5f);
musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), width - musicWidth, musicHeight / 1.5f); musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), displayContainer.width - musicWidth, musicHeight / 1.5f);
musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), width - (3 * musicWidth), musicHeight / 1.5f); musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), displayContainer.width - (3 * musicWidth), musicHeight / 1.5f);
musicPlay.setHoverExpand(1.5f); musicPlay.setHoverExpand(1.5f);
musicPause.setHoverExpand(1.5f); musicPause.setHoverExpand(1.5f);
musicNext.setHoverExpand(1.5f); musicNext.setHoverExpand(1.5f);
musicPrevious.setHoverExpand(1.5f); musicPrevious.setHoverExpand(1.5f);
// initialize music position bar location // initialize music position bar location
musicBarX = width - musicWidth * 3.5f; musicBarX = displayContainer.width - musicWidth * 3.5f;
musicBarY = musicHeight * 1.25f; musicBarY = musicHeight * 1.25f;
musicBarWidth = musicWidth * 3f; musicBarWidth = musicWidth * 3f;
musicBarHeight = musicHeight * 0.11f; musicBarHeight = musicHeight * 0.11f;
// initialize downloads button // initialize downloads button
Image dlImg = GameImage.DOWNLOADS.getImage(); Image dlImg = GameImage.DOWNLOADS.getImage();
downloadsButton = new MenuButton(dlImg, width - dlImg.getWidth() / 2f, height / 2f); downloadsButton = new MenuButton(dlImg, displayContainer.width - dlImg.getWidth() / 2f, displayContainer.height / 2f);
downloadsButton.setHoverAnimationDuration(350); downloadsButton.setHoverAnimationDuration(350);
downloadsButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); downloadsButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
downloadsButton.setHoverExpand(1.03f, Expand.LEFT); downloadsButton.setHoverExpand(1.03f, Expand.LEFT);
// initialize repository button // initialize repository button
float startX = width * 0.997f, startY = height * 0.997f; float startX = displayContainer.width * 0.997f, startY = displayContainer.height * 0.997f;
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { // only if a webpage can be opened if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { // only if a webpage can be opened
Image repoImg; Image repoImg;
repoImg = GameImage.REPOSITORY.getImage(); repoImg = GameImage.REPOSITORY.getImage();
@ -217,7 +206,7 @@ public class MainMenu extends BasicGameState {
} }
// initialize update buttons // initialize update buttons
float updateX = width / 2f, updateY = height * 17 / 18f; float updateX = displayContainer.width / 2f, updateY = displayContainer.height * 17 / 18f;
Image downloadImg = GameImage.DOWNLOAD.getImage(); Image downloadImg = GameImage.DOWNLOAD.getImage();
updateButton = new MenuButton(downloadImg, updateX, updateY); updateButton = new MenuButton(downloadImg, updateX, updateY);
updateButton.setHoverAnimationDuration(400); updateButton.setHoverAnimationDuration(400);
@ -230,22 +219,19 @@ public class MainMenu extends BasicGameState {
restartButton.setHoverRotate(360); restartButton.setHoverRotate(360);
// initialize star fountain // initialize star fountain
starFountain = new StarFountain(width, height); starFountain = new StarFountain(displayContainer.width, displayContainer.height);
// logo animations // logo animations
float centerOffsetX = width / 6.5f; float centerOffsetX = displayContainer.width / 6.5f;
logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD); logoOpen = new AnimatedValue(100, 0, centerOffsetX, AnimationEquation.OUT_QUAD);
logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD); logoClose = new AnimatedValue(2200, centerOffsetX, 0, AnimationEquation.OUT_QUAD);
logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); logoButtonAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR);
reset();
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException { int width = displayContainer.width;
int width = container.getWidth(); int height = displayContainer.height;
int height = container.getHeight();
// draw background // draw background
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
@ -305,7 +291,8 @@ public class MainMenu extends BasicGameState {
musicPrevious.draw(); musicPrevious.draw();
// draw music position bar // draw music position bar
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
g.setColor((musicPositionBarContains(mouseX, mouseY)) ? Colors.BLACK_BG_HOVER : Colors.BLACK_BG_NORMAL); g.setColor((musicPositionBarContains(mouseX, mouseY)) ? Colors.BLACK_BG_HOVER : Colors.BLACK_BG_NORMAL);
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4);
g.setColor(Color.white); g.setColor(Color.white);
@ -366,12 +353,14 @@ public class MainMenu extends BasicGameState {
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
if (MusicController.trackEnded()) if (MusicController.trackEnded())
nextTrack(false); // end of track: go to next track nextTrack(false); // end of track: go to next track
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
logo.hoverUpdate(delta, mouseX, mouseY, 0.25f); logo.hoverUpdate(delta, mouseX, mouseY, 0.25f);
playButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); playButton.hoverUpdate(delta, mouseX, mouseY, 0.25f);
exitButton.hoverUpdate(delta, mouseX, mouseY, 0.25f); exitButton.hoverUpdate(delta, mouseX, mouseY, 0.25f);
@ -396,7 +385,7 @@ public class MainMenu extends BasicGameState {
// window focus change: increase/decrease theme song volume // window focus change: increase/decrease theme song volume
if (MusicController.isThemePlaying() && if (MusicController.isThemePlaying() &&
MusicController.isTrackDimmed() == container.hasFocus()) MusicController.isTrackDimmed() == Display.isActive())
MusicController.toggleTrackDimmed(0.33f); MusicController.toggleTrackDimmed(0.33f);
// fade in background // fade in background
@ -413,7 +402,7 @@ public class MainMenu extends BasicGameState {
} }
// buttons // buttons
int centerX = container.getWidth() / 2; int centerX = displayContainer.width / 2;
float currentLogoButtonAlpha; float currentLogoButtonAlpha;
switch (logoState) { switch (logoState) {
case DEFAULT: case DEFAULT:
@ -468,11 +457,16 @@ public class MainMenu extends BasicGameState {
} }
@Override @Override
public int getID() { return state; } public void enter() {
super.enter();
logo.setX(displayContainer.width / 2);
logoOpen.setTime(0);
logoClose.setTime(0);
logoButtonAlpha.setTime(0);
logoTimer = 0;
logoState = LogoState.DEFAULT;
@Override
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
UI.enter(); UI.enter();
if (!enterNotification) { if (!enterNotification) {
if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) { if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) {
@ -489,7 +483,8 @@ public class MainMenu extends BasicGameState {
starFountain.clear(); starFountain.clear();
// reset button hover states if mouse is not currently hovering over the button // reset button hover states if mouse is not currently hovering over the button
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
if (!logo.contains(mouseX, mouseY, 0.25f)) if (!logo.contains(mouseX, mouseY, 0.25f))
logo.resetHover(); logo.resetHover();
if (!playButton.contains(mouseX, mouseY, 0.25f)) if (!playButton.contains(mouseX, mouseY, 0.25f))
@ -515,17 +510,17 @@ public class MainMenu extends BasicGameState {
} }
@Override @Override
public void leave(GameContainer container, StateBasedGame game) public void leave() {
throws SlickException { super.leave();
if (MusicController.isTrackDimmed()) if (MusicController.isTrackDimmed())
MusicController.toggleTrackDimmed(1f); MusicController.toggleTrackDimmed(1f);
} }
@Override @Override
public void mousePressed(int button, int x, int y) { public boolean mousePressed(int button, int x, int y) {
// check mouse button // check mouse button
if (button == Input.MOUSE_MIDDLE_BUTTON) if (button == Input.MOUSE_MIDDLE_BUTTON)
return; return false;
// music position bar // music position bar
if (MusicController.isPlaying()) { if (MusicController.isPlaying()) {
@ -533,7 +528,7 @@ public class MainMenu extends BasicGameState {
lastMeasureProgress = 0f; lastMeasureProgress = 0f;
float pos = (x - musicBarX) / musicBarWidth; float pos = (x - musicBarX) / musicBarWidth;
MusicController.setPosition((int) (pos * MusicController.getDuration())); MusicController.setPosition((int) (pos * MusicController.getDuration()));
return; return true;
} }
} }
@ -546,29 +541,28 @@ public class MainMenu extends BasicGameState {
MusicController.resume(); MusicController.resume();
UI.sendBarNotification("Play"); UI.sendBarNotification("Play");
} }
return; return true;
} else if (musicNext.contains(x, y)) { } else if (musicNext.contains(x, y)) {
nextTrack(true); nextTrack(true);
UI.sendBarNotification(">> Next"); UI.sendBarNotification(">> Next");
return; return true;
} else if (musicPrevious.contains(x, y)) { } else if (musicPrevious.contains(x, y)) {
lastMeasureProgress = 0f; lastMeasureProgress = 0f;
if (!previous.isEmpty()) { if (!previous.isEmpty()) {
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
menu.setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
if (Options.isDynamicBackgroundEnabled()) if (Options.isDynamicBackgroundEnabled())
bgAlpha.setTime(0); bgAlpha.setTime(0);
} else } else
MusicController.setPosition(0); MusicController.setPosition(0);
UI.sendBarNotification("<< Previous"); UI.sendBarNotification("<< Previous");
return; return true;
} }
// downloads button actions // downloads button actions
if (downloadsButton.contains(x, y)) { if (downloadsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(DownloadsMenu.class);
return; return true;
} }
// repository button actions // repository button actions
@ -578,9 +572,10 @@ public class MainMenu extends BasicGameState {
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
UI.sendBarNotification("The repository web page could not be opened."); UI.sendBarNotification("The repository web page could not be opened.");
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not browse to repository URI.", e, false); Log.error("could not browse to repo", e);
displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE));
} }
return; return true;
} }
if (danceRepoButton != null && danceRepoButton.contains(x, y)) { if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
@ -589,9 +584,10 @@ public class MainMenu extends BasicGameState {
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
UI.sendBarNotification("The repository web page could not be opened."); UI.sendBarNotification("The repository web page could not be opened.");
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.error("Could not browse to repository URI.", e, false); Log.error("could not browse to repo", e);
displayContainer.eventBus.post(new BubbleNotificationEvent("Could not browse to repo", BubbleNotificationEvent.COLOR_ORANGE));
} }
return; return true;
} }
// update button actions // update button actions
@ -604,13 +600,12 @@ public class MainMenu extends BasicGameState {
updateButton.setHoverAnimationDuration(800); updateButton.setHoverAnimationDuration(800);
updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD); updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD);
updateButton.setHoverFade(0.6f); updateButton.setHoverFade(0.6f);
return; return true;
} else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) { } else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
Updater.get().prepareUpdate(); Updater.get().prepareUpdate();
container.setForceExit(false); displayContainer.exitRequested = true;
container.exit(); return true;
return;
} }
} }
@ -623,7 +618,7 @@ public class MainMenu extends BasicGameState {
playButton.getImage().setAlpha(0f); playButton.getImage().setAlpha(0f);
exitButton.getImage().setAlpha(0f); exitButton.getImage().setAlpha(0f);
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
return; return true;
} }
} }
@ -632,21 +627,27 @@ public class MainMenu extends BasicGameState {
if (logo.contains(x, y, 0.25f) || playButton.contains(x, y, 0.25f)) { if (logo.contains(x, y, 0.25f) || playButton.contains(x, y, 0.25f)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
enterSongMenu(); enterSongMenu();
return; return true;
} else if (exitButton.contains(x, y, 0.25f)) { } else if (exitButton.contains(x, y, 0.25f)) {
container.exit(); displayContainer.exitRequested = true;
return; return true;
} }
} }
return false;
} }
@Override @Override
public void mouseWheelMoved(int newValue) { public boolean mouseWheelMoved(int newValue) {
UI.changeVolume((newValue < 0) ? -1 : 1); UI.changeVolume((newValue < 0) ? -1 : 1);
return true;
} }
@Override @Override
public void keyPressed(int key, char c) { public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case Input.KEY_ESCAPE:
case Input.KEY_Q: case Input.KEY_Q:
@ -656,9 +657,9 @@ public class MainMenu extends BasicGameState {
logoTimer = 0; logoTimer = 0;
break; break;
} }
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.EXIT); instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.EXIT);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
break; return true;
case Input.KEY_P: case Input.KEY_P:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) { if (logoState == LogoState.DEFAULT || logoState == LogoState.CLOSING) {
@ -669,30 +670,22 @@ public class MainMenu extends BasicGameState {
exitButton.getImage().setAlpha(0f); exitButton.getImage().setAlpha(0f);
} else } else
enterSongMenu(); enterSongMenu();
break; return true;
case Input.KEY_D: case Input.KEY_D:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
game.enterState(Opsu.STATE_DOWNLOADSMENU, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(DownloadsMenu.class);
break; return true;
case Input.KEY_R: case Input.KEY_R:
nextTrack(true); nextTrack(true);
break; return true;
case Input.KEY_UP: case Input.KEY_UP:
UI.changeVolume(1); UI.changeVolume(1);
break; return true;
case Input.KEY_DOWN: case Input.KEY_DOWN:
UI.changeVolume(-1); UI.changeVolume(-1);
break; return true;
case Input.KEY_F7:
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
} }
return false;
} }
/** /**
@ -705,34 +698,6 @@ public class MainMenu extends BasicGameState {
(cy > musicBarY && cy < musicBarY + musicBarHeight)); (cy > musicBarY && cy < musicBarY + musicBarHeight));
} }
/**
* Resets the button states.
*/
public void reset() {
// reset logo
logo.setX(container.getWidth() / 2);
logoOpen.setTime(0);
logoClose.setTime(0);
logoButtonAlpha.setTime(0);
logoTimer = 0;
logoState = LogoState.DEFAULT;
logo.resetHover();
playButton.resetHover();
exitButton.resetHover();
musicPlay.resetHover();
musicPause.resetHover();
musicNext.resetHover();
musicPrevious.resetHover();
if (repoButton != null)
repoButton.resetHover();
if (danceRepoButton != null)
danceRepoButton.resetHover();
updateButton.resetHover();
restartButton.resetHover();
downloadsButton.resetHover();
}
/** /**
* Plays the next track, and adds the previous one to the stack. * Plays the next track, and adds the previous one to the stack.
* @param user {@code true} if this was user-initiated, false otherwise (track end) * @param user {@code true} if this was user-initiated, false otherwise (track end)
@ -746,8 +711,7 @@ public class MainMenu extends BasicGameState {
MusicController.playAt(0, false); MusicController.playAt(0, false);
return; return;
} }
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); BeatmapSetNode node = instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
BeatmapSetNode node = menu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, false);
boolean sameAudio = false; boolean sameAudio = false;
if (node != null) { if (node != null) {
sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename); sameAudio = MusicController.getBeatmap().audioFilename.equals(node.getBeatmapSet().get(0).audioFilename);
@ -762,11 +726,11 @@ public class MainMenu extends BasicGameState {
* Enters the song menu, or the downloads menu if no beatmaps are loaded. * Enters the song menu, or the downloads menu if no beatmaps are loaded.
*/ */
private void enterSongMenu() { private void enterSongMenu() {
int state = Opsu.STATE_SONGMENU; Class<? extends OpsuState> state = SongMenu.class;
if (BeatmapSetList.get().getMapSetCount() == 0) { if (BeatmapSetList.get().getMapSetCount() == 0) {
((DownloadsMenu) game.getState(Opsu.STATE_DOWNLOADSMENU)).notifyOnLoad("Download some beatmaps to get started!"); instanceContainer.provide(DownloadsMenu.class).notifyOnLoad("Download some beatmaps to get started!");
state = Opsu.STATE_DOWNLOADSMENU; state = DownloadsMenu.class;
} }
game.enterState(state, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(state);
} }
} }

View File

@ -18,35 +18,14 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Options.GameOption; import itdelatrisu.opsu.Options.GameOption;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.EmptyTransition;
import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay;
import yugecin.opsudance.ui.OptionsOverlay.OptionTab; import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
/** public class OptionsMenu {
* "Game Options" state.
* <p>
* Players are able to view and change various game settings in this state.
*/
public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent {
/** Option tabs. */ public static final OptionTab[] normalOptions = new OptionsOverlay.OptionTab[]{
private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{
new OptionTab("Display", new GameOption[]{ new OptionTab("Display", new GameOption[]{
GameOption.SCREEN_RESOLUTION, GameOption.SCREEN_RESOLUTION,
GameOption.FULLSCREEN, GameOption.FULLSCREEN,
@ -154,104 +133,63 @@ public class OptionsMenu extends BasicGameState implements OptionsOverlay.Parent
}) })
}; };
private StateBasedGame game; public static final OptionTab[] storyboardOptions = new OptionsOverlay.OptionTab[]{
private Input input; new OptionTab("Gameplay", new GameOption[] {
private final int state; GameOption.BACKGROUND_DIM,
GameOption.DANCE_REMOVE_BG,
private OptionsOverlay optionsOverlay; GameOption.SNAKING_SLIDERS,
GameOption.SHRINKING_SLIDERS,
public OptionsMenu(int state) { GameOption.SHOW_HIT_LIGHTING,
this.state = state; GameOption.SHOW_HIT_ANIMATIONS,
} GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT,
@Override GameOption.SHOW_FOLLOW_POINTS,
public void init(GameContainer container, StateBasedGame game) }),
throws SlickException { new OptionTab("Input", new GameOption[] {
this.game = game; GameOption.CURSOR_SIZE,
this.input = container.getInput(); GameOption.NEW_CURSOR,
GameOption.DISABLE_CURSOR
optionsOverlay = new OptionsOverlay(this, options, 5, container); }),
} new OptionTab("Dance", new GameOption[] {
GameOption.DANCE_MOVER,
@Override GameOption.DANCE_EXGON_DELAY,
public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException { GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
// background GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
GameImage.OPTIONS_BG.getImage().draw(); GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); GameOption.DANCE_MOVER_DIRECTION,
optionsOverlay.render(g, mouseX, mouseY); GameOption.DANCE_SLIDER_MOVER_TYPE,
UI.draw(g); GameOption.DANCE_SPINNER,
} GameOption.DANCE_SPINNER_DELAY,
GameOption.DANCE_LAZY_SLIDERS,
@Override GameOption.DANCE_CIRCLE_STREAMS,
public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { GameOption.DANCE_ONLY_CIRCLE_STACKS,
UI.update(delta); GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
MusicController.loopTrackIfEnded(false); GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
optionsOverlay.update(delta, input.getMouseX(), input.getMouseY()); GameOption.DANCE_MIRROR,
} }),
new OptionTab("Dance display", new GameOption[] {
@Override GameOption.DANCE_DRAW_APPROACH,
public int getID() { GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
return state; GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
} GameOption.DANCE_RGB_OBJECT_INC,
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
@Override GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
public void mouseReleased(int button, int x, int y) { GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
optionsOverlay.mouseReleased(button, x, y); GameOption.DANCE_RGB_CURSOR_INC,
} GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
GameOption.DANCE_HIDE_OBJECTS,
@Override GameOption.DANCE_HIDE_UI,
public void mousePressed(int button, int x, int y) { GameOption.DANCE_HIDE_WATERMARK,
optionsOverlay.mousePressed(button, x, y); }),
} new OptionTab ("Pippi", new GameOption[] {
GameOption.PIPPI_ENABLE,
@Override GameOption.PIPPI_RADIUS_PERCENT,
public void mouseDragged(int oldx, int oldy, int newx, int newy) { GameOption.PIPPI_ANGLE_INC_MUL,
optionsOverlay.mouseDragged(oldx, oldy, newx, newy); GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
} GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
@Override })
public void mouseWheelMoved(int newValue) { };
optionsOverlay.mouseWheelMoved(newValue);
}
@Override
public void keyPressed(int key, char c) {
optionsOverlay.keyPressed(key, c);
}
/**
* This string is built with option values when entering the options menu.
* When leaving the options menu, this string is checked against the new optionstring with the same options.
* If those do not match, it means some option has change which requires a restart
*/
private String restartOptions;
@Override
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
UI.enter();
restartOptions = "" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName();
}
@Override
public void leave(GameContainer container, StateBasedGame game) throws SlickException {
if (!("" + Options.getResolutionIdx() + Options.isFullscreen() + Options.allowLargeResolutions() + Options.getSkinName()).equals(restartOptions)) {
container.setForceExit(false);
container.exit();
return;
}
SoundController.playSound(SoundEffect.MENUBACK);
}
@Override
public void onLeave() {
game.enterState(Opsu.STATE_SONGMENU, new EmptyTransition(), new FadeInTransition());
}
@Override
public void onSaveOption(GameOption option) {
}
} }

View File

@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.Grade; import itdelatrisu.opsu.GameData.Grade;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -62,21 +61,17 @@ import java.nio.file.WatchEvent.Kind;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import org.lwjgl.opengl.Display;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet; import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.gui.TextField; import org.newdawn.slick.gui.TextField;
import org.newdawn.slick.state.BasicGameState; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.inject.InstanceContainer;
import org.newdawn.slick.state.transition.EasedFadeOutTransition; import yugecin.opsudance.core.state.ComplexOpsuState;
import org.newdawn.slick.state.transition.EmptyTransition; import yugecin.opsudance.ui.OptionsOverlay;
import org.newdawn.slick.state.transition.FadeInTransition;
/** /**
* "Song Selection" state. * "Song Selection" state.
@ -84,7 +79,10 @@ import org.newdawn.slick.state.transition.FadeInTransition;
* Players are able to select a beatmap to play, view previous scores, choose game mods, * Players are able to select a beatmap to play, view previous scores, choose game mods,
* manage beatmaps, or change game options from this state. * manage beatmaps, or change game options from this state.
*/ */
public class SongMenu extends BasicGameState { public class SongMenu extends ComplexOpsuState {
private final InstanceContainer instanceContainer;
/** The max number of song buttons to be shown on each screen. */ /** The max number of song buttons to be shown on each screen. */
public static final int MAX_SONG_BUTTONS = 6; public static final int MAX_SONG_BUTTONS = 6;
@ -169,7 +167,7 @@ public class SongMenu extends BasicGameState {
private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton; private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton;
/** The search textfield. */ /** The search textfield. */
private TextField search; private TextField searchTextField;
/** /**
* Delay timer, in milliseconds, before running another search. * Delay timer, in milliseconds, before running another search.
@ -230,7 +228,7 @@ public class SongMenu extends BasicGameState {
} finally { } finally {
finished = true; finished = true;
} }
}; }
/** Reloads all beatmaps. */ /** Reloads all beatmaps. */
private void reloadBeatmaps() { private void reloadBeatmaps() {
@ -323,45 +321,41 @@ public class SongMenu extends BasicGameState {
/** Sort order dropdown menu. */ /** Sort order dropdown menu. */
private DropdownMenu<BeatmapSortOrder> sortMenu; private DropdownMenu<BeatmapSortOrder> sortMenu;
// game-related variables private final OptionsOverlay optionsOverlay;
private GameContainer container;
private StateBasedGame game;
private Input input;
private final int state;
public SongMenu(int state) { public SongMenu(final DisplayContainer displayContainer, InstanceContainer instanceContainer) {
this.state = state; super(displayContainer);
this.instanceContainer = instanceContainer;
optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.normalOptions, 0);
overlays.add(optionsOverlay);
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) public void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
this.game = game;
this.input = container.getInput();
int width = container.getWidth(); components.clear();
int height = container.getHeight();
// header/footer coordinates // header/footer coordinates
headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() + headerY = displayContainer.height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() +
Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() + Fonts.BOLD.getLineHeight() + Fonts.DEFAULT.getLineHeight() +
Fonts.SMALL.getLineHeight(); Fonts.SMALL.getLineHeight();
footerY = height - GameImage.SELECTION_MODS.getImage().getHeight(); footerY = displayContainer.height - GameImage.SELECTION_MODS.getImage().getHeight();
// footer logo coordinates // footer logo coordinates
float footerHeight = height - footerY; float footerHeight = displayContainer.height - footerY;
footerLogoSize = footerHeight * 3.25f; footerLogoSize = footerHeight * 3.25f;
Image logo = GameImage.MENU_LOGO.getImage(); Image logo = GameImage.MENU_LOGO.getImage();
logo = logo.getScaledCopy(footerLogoSize / logo.getWidth()); logo = logo.getScaledCopy(footerLogoSize / logo.getWidth());
footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f); footerLogoButton = new MenuButton(logo, displayContainer.width - footerHeight * 0.8f, displayContainer.height - footerHeight * 0.65f);
footerLogoButton.setHoverAnimationDuration(1); footerLogoButton.setHoverAnimationDuration(1);
footerLogoButton.setHoverExpand(1.2f); footerLogoButton.setHoverExpand(1.2f);
// initialize sorts // initialize sorts
int sortWidth = (int) (width * 0.12f); int sortWidth = (int) (displayContainer.width * 0.12f);
sortMenu = new DropdownMenu<BeatmapSortOrder>(container, BeatmapSortOrder.values(), int posX = (int) (displayContainer.width * 0.87f);
width * 0.87f, headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f, sortWidth) { int posY = (int) (headerY - GameImage.MENU_TAB.getImage().getHeight() * 2.25f);
sortMenu = new DropdownMenu<BeatmapSortOrder>(displayContainer, BeatmapSortOrder.values(), posX, posY, sortWidth) {
@Override @Override
public void itemSelected(int index, BeatmapSortOrder item) { public void itemSelected(int index, BeatmapSortOrder item) {
BeatmapSortOrder.set(item); BeatmapSortOrder.set(item);
@ -375,7 +369,7 @@ public class SongMenu extends BasicGameState {
} }
@Override @Override
public boolean menuClicked(int index) { public boolean canSelect(int index) {
if (isInputBlocked()) if (isInputBlocked())
return false; return false;
@ -386,36 +380,40 @@ public class SongMenu extends BasicGameState {
sortMenu.setBackgroundColor(Colors.BLACK_BG_HOVER); sortMenu.setBackgroundColor(Colors.BLACK_BG_HOVER);
sortMenu.setBorderColor(Colors.BLUE_DIVIDER); sortMenu.setBorderColor(Colors.BLUE_DIVIDER);
sortMenu.setChevronRightColor(Color.white); sortMenu.setChevronRightColor(Color.white);
components.add(sortMenu);
// initialize group tabs // initialize group tabs
for (BeatmapGroup group : BeatmapGroup.values()) for (BeatmapGroup group : BeatmapGroup.values())
group.init(width, headerY - DIVIDER_LINE_WIDTH / 2); group.init(displayContainer.width, headerY - DIVIDER_LINE_WIDTH / 2);
// initialize score data buttons // initialize score data buttons
ScoreData.init(width, headerY + height * 0.01f); ScoreData.init(displayContainer.width, headerY + displayContainer.height * 0.01f);
// song button background & graphics context // song button background & graphics context
Image menuBackground = GameImage.MENU_BUTTON_BG.getImage(); Image menuBackground = GameImage.MENU_BUTTON_BG.getImage();
// song button coordinates // song button coordinates
buttonX = width * 0.6f; buttonX = displayContainer.width * 0.6f;
//buttonY = headerY; //buttonY = headerY;
buttonWidth = menuBackground.getWidth(); buttonWidth = menuBackground.getWidth();
buttonHeight = menuBackground.getHeight(); buttonHeight = menuBackground.getHeight();
buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS; buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS;
// search // search
int textFieldX = (int) (width * 0.7125f + Fonts.BOLD.getWidth("Search: ")); int textFieldX = (int) (displayContainer.width * 0.7125f + Fonts.BOLD.getWidth("Search: "));
int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2); int textFieldY = (int) (headerY + Fonts.BOLD.getLineHeight() / 2);
search = new TextField( searchTextField = new TextField(displayContainer, Fonts.BOLD, textFieldX, textFieldY, (int) (displayContainer.width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight()) {
container, Fonts.BOLD, textFieldX, textFieldY, @Override
(int) (width * 0.99f) - textFieldX, Fonts.BOLD.getLineHeight() public boolean isFocusable() {
); return false;
search.setBackgroundColor(Color.transparent); }
search.setBorderColor(Color.transparent); };
search.setTextColor(Color.white); searchTextField.setBackgroundColor(Color.transparent);
search.setConsumeEvents(false); searchTextField.setBorderColor(Color.transparent);
search.setMaxLength(60); searchTextField.setTextColor(Color.white);
searchTextField.setMaxLength(60);
searchTextField.setFocused(true);
components.add(searchTextField);
// selection buttons // selection buttons
Image selectionMods = GameImage.SELECTION_MODS.getImage(); Image selectionMods = GameImage.SELECTION_MODS.getImage();
@ -427,8 +425,8 @@ public class SongMenu extends BasicGameState {
if (selectButtonsWidth < 20) { if (selectButtonsWidth < 20) {
selectButtonsWidth = 100; selectButtonsWidth = 100;
} }
float selectX = width * 0.183f + selectButtonsWidth / 2f; float selectX = displayContainer.width * 0.183f + selectButtonsWidth / 2f;
float selectY = height - selectButtonsHeight / 2f; float selectY = displayContainer.height - selectButtonsHeight / 2f;
float selectOffset = selectButtonsWidth * 1.05f; float selectOffset = selectButtonsWidth * 1.05f;
selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(), selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(),
selectX, selectY); selectX, selectY);
@ -449,33 +447,32 @@ public class SongMenu extends BasicGameState {
loader = new Animation(spr, 50); loader = new Animation(spr, 50);
// beatmap watch service listener // beatmap watch service listener
final StateBasedGame game_ = game;
BeatmapWatchService.addListener(new BeatmapWatchServiceListener() { BeatmapWatchService.addListener(new BeatmapWatchServiceListener() {
@Override @Override
public void eventReceived(Kind<?> kind, Path child) { public void eventReceived(Kind<?> kind, Path child) {
if (!songFolderChanged && kind != StandardWatchEventKinds.ENTRY_MODIFY) { if (!songFolderChanged && kind != StandardWatchEventKinds.ENTRY_MODIFY) {
songFolderChanged = true; songFolderChanged = true;
if (game_.getCurrentStateID() == Opsu.STATE_SONGMENU) if (displayContainer.isInState(SongMenu.class)) {
UI.sendBarNotification("Changes in Songs folder detected. Hit F5 to refresh."); UI.sendBarNotification("Changes in Songs folder detected. Hit F5 to refresh.");
} }
} }
}
}); });
// star stream // star stream
starStream = new StarStream(width, (height - GameImage.STAR.getImage().getHeight()) / 2, -width, 0, MAX_STREAM_STARS); starStream = new StarStream(displayContainer.width, (displayContainer.height - GameImage.STAR.getImage().getHeight()) / 2, -displayContainer.width, 0, MAX_STREAM_STARS);
starStream.setPositionSpread(height / 20f); starStream.setPositionSpread(displayContainer.height / 20f);
starStream.setDirectionSpread(10f); starStream.setDirectionSpread(10f);
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException {
g.setBackground(Color.black); g.setBackground(Color.black);
int width = container.getWidth(); int width = displayContainer.width;
int height = container.getHeight(); int height = displayContainer.height;
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); int mouseY = displayContainer.mouseY;
// background // background
if (focusNode != null) { if (focusNode != null) {
@ -547,9 +544,10 @@ public class SongMenu extends BasicGameState {
g.clearClip(); g.clearClip();
// scroll bar // scroll bar
if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !inDropdownMenu) if (focusScores.length > MAX_SCORE_BUTTONS && ScoreData.areaContains(mouseX, mouseY) && !isAnyComponentFocused()) {
ScoreData.drawScrollbar(g, startScorePos.getPosition(), focusScores.length * ScoreData.getButtonOffset()); ScoreData.drawScrollbar(g, startScorePos.getPosition(), focusScores.length * ScoreData.getButtonOffset());
} }
}
// top/bottom bars // top/bottom bars
g.setColor(Colors.BLACK_ALPHA); g.setColor(Colors.BLACK_ALPHA);
@ -565,7 +563,7 @@ public class SongMenu extends BasicGameState {
Float position = MusicController.getBeatProgress(); Float position = MusicController.getBeatProgress();
if (position == null) // default to 60bpm if (position == null) // default to 60bpm
position = System.currentTimeMillis() % 1000 / 1000f; position = System.currentTimeMillis() % 1000 / 1000f;
if (footerLogoButton.contains(mouseX, mouseY, 0.25f) && !inDropdownMenu) { if (footerLogoButton.contains(mouseX, mouseY, 0.25f)) {
// hovering over logo: stop pulsing // hovering over logo: stop pulsing
footerLogoButton.draw(); footerLogoButton.draw();
} else { } else {
@ -658,7 +656,7 @@ public class SongMenu extends BasicGameState {
// group tabs // group tabs
BeatmapGroup currentGroup = BeatmapGroup.current(); BeatmapGroup currentGroup = BeatmapGroup.current();
BeatmapGroup hoverGroup = null; BeatmapGroup hoverGroup = null;
if (!inDropdownMenu) { if (!isAnyComponentFocused()) {
for (BeatmapGroup group : BeatmapGroup.values()) { for (BeatmapGroup group : BeatmapGroup.values()) {
if (group.contains(mouseX, mouseY)) { if (group.contains(mouseX, mouseY)) {
hoverGroup = group; hoverGroup = group;
@ -673,8 +671,9 @@ public class SongMenu extends BasicGameState {
currentGroup.draw(true, false); currentGroup.draw(true, false);
// search // search
boolean searchEmpty = search.getText().isEmpty(); boolean searchEmpty = searchTextField.getText().isEmpty();
int searchX = search.getX(), searchY = search.getY(); int searchX = searchTextField.x;
int searchY = searchTextField.y;
float searchBaseX = width * 0.7f; float searchBaseX = width * 0.7f;
float searchTextX = width * 0.7125f; float searchTextX = width * 0.7125f;
float searchRectHeight = Fonts.BOLD.getLineHeight() * 2; float searchRectHeight = Fonts.BOLD.getLineHeight() * 2;
@ -693,20 +692,15 @@ public class SongMenu extends BasicGameState {
g.fillRect(searchBaseX, headerY + DIVIDER_LINE_WIDTH / 2, width - searchBaseX, searchRectHeight); g.fillRect(searchBaseX, headerY + DIVIDER_LINE_WIDTH / 2, width - searchBaseX, searchRectHeight);
Colors.BLACK_ALPHA.a = oldAlpha; Colors.BLACK_ALPHA.a = oldAlpha;
Fonts.BOLD.drawString(searchTextX, searchY, "Search:", Colors.GREEN_SEARCH); Fonts.BOLD.drawString(searchTextX, searchY, "Search:", Colors.GREEN_SEARCH);
if (searchEmpty) if (searchEmpty) {
Fonts.BOLD.drawString(searchX, searchY, "Type to search!", Color.white); Fonts.BOLD.drawString(searchX, searchY, "Type to search!", Color.white);
else { } else {
g.setColor(Color.white); g.setColor(Color.white);
// TODO: why is this needed to correctly position the TextField? searchTextField.render(g);
search.setLocation(searchX - 3, searchY - 1); Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(), (searchResultString == null) ? "Searching..." : searchResultString, Color.white);
search.render(container, g);
search.setLocation(searchX, searchY);
Fonts.DEFAULT.drawString(searchTextX, searchY + Fonts.BOLD.getLineHeight(),
(searchResultString == null) ? "Searching..." : searchResultString, Color.white);
} }
// sorting options sortMenu.render(g);
sortMenu.render(container, g);
// reloading beatmaps // reloading beatmaps
if (reloadThread != null) { if (reloadThread != null) {
@ -722,11 +716,15 @@ public class SongMenu extends BasicGameState {
UI.getBackButton().draw(); UI.getBackButton().draw();
UI.draw(g); UI.draw(g);
super.render(g);
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException { super.preRenderUpdate();
int delta = displayContainer.renderDelta;
UI.update(delta); UI.update(delta);
if (reloadThread == null) if (reloadThread == null)
MusicController.loopTrackIfEnded(true); MusicController.loopTrackIfEnded(true);
@ -742,8 +740,8 @@ public class SongMenu extends BasicGameState {
MusicController.playThemeSong(); MusicController.playThemeSong();
reloadThread = null; reloadThread = null;
} }
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = displayContainer.mouseX;
boolean inDropdownMenu = sortMenu.contains(mouseX, mouseY); int mouseY = displayContainer.mouseY;
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
selectModsButton.hoverUpdate(delta, mouseX, mouseY); selectModsButton.hoverUpdate(delta, mouseX, mouseY);
selectRandomButton.hoverUpdate(delta, mouseX, mouseY); selectRandomButton.hoverUpdate(delta, mouseX, mouseY);
@ -759,8 +757,8 @@ public class SongMenu extends BasicGameState {
if (focusNode != null) { if (focusNode != null) {
MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState state = focusNode.getBeatmapSet().isFavorite() ?
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode); instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
} }
return; return;
} }
@ -782,7 +780,6 @@ public class SongMenu extends BasicGameState {
starStream.update(delta); starStream.update(delta);
// search // search
search.setFocus(true);
searchTimer += delta; searchTimer += delta;
if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) { if (searchTimer >= SEARCH_DELAY && reloadThread == null && beatmapMenuTimer == -1) {
searchTimer = 0; searchTimer = 0;
@ -791,12 +788,12 @@ public class SongMenu extends BasicGameState {
if (focusNode != null) if (focusNode != null)
oldFocusNode = new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex); oldFocusNode = new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex);
if (BeatmapSetList.get().search(search.getText())) { if (BeatmapSetList.get().search(searchTextField.getText())) {
// reset song stack // reset song stack
randomStack = new Stack<SongNode>(); randomStack = new Stack<>();
// empty search // empty search
if (search.getText().isEmpty()) if (searchTextField.getText().isEmpty())
searchResultString = null; searchResultString = null;
// search produced new list: re-initialize it // search produced new list: re-initialize it
@ -805,7 +802,7 @@ public class SongMenu extends BasicGameState {
focusScores = null; focusScores = null;
if (BeatmapSetList.get().size() > 0) { if (BeatmapSetList.get().size() > 0) {
BeatmapSetList.get().init(); BeatmapSetList.get().init();
if (search.getText().isEmpty()) { // cleared search if (searchTextField.getText().isEmpty()) { // cleared search
// use previous start/focus if possible // use previous start/focus if possible
if (oldFocusNode != null) if (oldFocusNode != null)
setFocus(oldFocusNode.getNode(), oldFocusNode.getIndex(), true, true); setFocus(oldFocusNode.getNode(), oldFocusNode.getIndex(), true, true);
@ -818,7 +815,7 @@ public class SongMenu extends BasicGameState {
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }
oldFocusNode = null; oldFocusNode = null;
} else if (!search.getText().isEmpty()) } else if (!searchTextField.getText().isEmpty())
searchResultString = "No matches found. Hit ESC to reset."; searchResultString = "No matches found. Hit ESC to reset.";
} }
} }
@ -848,7 +845,7 @@ public class SongMenu extends BasicGameState {
// mouse hover // mouse hover
BeatmapSetNode node = getNodeAtPosition(mouseX, mouseY); BeatmapSetNode node = getNodeAtPosition(mouseX, mouseY);
if (node != null && !inDropdownMenu) { if (node != null && !isAnyComponentFocused()) {
if (node == hoverIndex) if (node == hoverIndex)
hoverOffset.update(delta); hoverOffset.update(delta);
else { else {
@ -880,66 +877,65 @@ public class SongMenu extends BasicGameState {
} }
@Override @Override
public int getID() { return state; } public boolean mousePressed(int button, int x, int y) {
if (super.mousePressed(button, x, y)) {
return true;
}
@Override if (button == Input.MOUSE_MIDDLE_BUTTON) {
public void mousePressed(int button, int x, int y) { return false;
// check mouse button }
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
if (isScrollingToFocusNode) if (isScrollingToFocusNode) {
return; return true;
}
songScrolling.pressed(); songScrolling.pressed();
startScorePos.pressed(); startScorePos.pressed();
return true;
} }
@Override @Override
public void mouseReleased(int button, int x, int y) { public boolean mouseReleased(int button, int x, int y) {
// check mouse button if (super.mouseReleased(button, x, y)) {
if (button == Input.MOUSE_MIDDLE_BUTTON) return true;
return; }
if (isScrollingToFocusNode) if (button == Input.MOUSE_MIDDLE_BUTTON) {
return; return false;
}
if (isScrollingToFocusNode) {
return true;
}
songScrolling.released(); songScrolling.released();
startScorePos.released(); startScorePos.released();
if (isInputBlocked()) {
return true;
} }
@Override
public void mouseClicked(int button, int x, int y, int clickCount) {
// check mouse button
if (button == Input.MOUSE_MIDDLE_BUTTON)
return;
// block input
if (isInputBlocked())
return;
// back
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); displayContainer.switchState(MainMenu.class);
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition()); return true;
return;
} }
// selection buttons // selection buttons
if (selectModsButton.contains(x, y)) { if (selectModsButton.contains(x, y)) {
this.keyPressed(Input.KEY_F1, '\0'); this.keyPressed(Input.KEY_F1, '\0');
return; return true;
} else if (selectRandomButton.contains(x, y)) { } else if (selectRandomButton.contains(x, y)) {
this.keyPressed(Input.KEY_F2, '\0'); this.keyPressed(Input.KEY_F2, '\0');
return; return true;
} else if (selectMapOptionsButton.contains(x, y)) { } else if (selectMapOptionsButton.contains(x, y)) {
this.keyPressed(Input.KEY_F3, '\0'); this.keyPressed(Input.KEY_F3, '\0');
return; return true;
} else if (selectOptionsButton.contains(x, y)) { } else if (selectOptionsButton.contains(x, y)) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition()); optionsOverlay.show();
return; return true;
} }
// group tabs // group tabs
@ -954,7 +950,7 @@ public class SongMenu extends BasicGameState {
songInfo = null; songInfo = null;
scoreMap = null; scoreMap = null;
focusScores = null; focusScores = null;
search.setText(""); searchTextField.setText("");
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchTransitionTimer = SEARCH_TRANSITION_TIME; searchTransitionTimer = SEARCH_TRANSITION_TIME;
searchResultString = null; searchResultString = null;
@ -965,17 +961,18 @@ public class SongMenu extends BasicGameState {
if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null) if (BeatmapSetList.get().size() < 1 && group.getEmptyMessage() != null)
UI.sendBarNotification(group.getEmptyMessage()); UI.sendBarNotification(group.getEmptyMessage());
} }
return; return true;
} }
} }
if (focusNode == null) if (focusNode == null) {
return; return false;
}
// logo: start game // logo: start game
if (footerLogoButton.contains(x, y, 0.25f)) { if (footerLogoButton.contains(x, y, 0.25f)) {
startGame(); startGame();
return; return true;
} }
// song buttons // song buttons
@ -1014,7 +1011,7 @@ public class SongMenu extends BasicGameState {
if (button == Input.MOUSE_RIGHT_BUTTON) if (button == Input.MOUSE_RIGHT_BUTTON)
beatmapMenuTimer = (node.index == expandedIndex) ? BEATMAP_MENU_DELAY * 4 / 5 : 0; beatmapMenuTimer = (node.index == expandedIndex) ? BEATMAP_MENU_DELAY * 4 / 5 : 0;
return; return true;
} }
// score buttons // score buttons
@ -1027,49 +1024,55 @@ public class SongMenu extends BasicGameState {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (button != Input.MOUSE_RIGHT_BUTTON) { if (button != Input.MOUSE_RIGHT_BUTTON) {
// view score // view score
GameData data = new GameData(focusScores[rank], container.getWidth(), container.getHeight()); instanceContainer.provide(GameRanking.class).setGameData(new GameData(focusScores[rank], displayContainer.width, displayContainer.height));
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data); displayContainer.switchState(GameRanking.class);
game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition());
} else { } else {
// score management // score management
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.SCORE, focusScores[rank]); instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.SCORE, focusScores[rank]);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
} }
return; return true;
} }
} }
} }
return true;
} }
@Override @Override
public void keyPressed(int key, char c) { public boolean keyPressed(int key, char c) {
if (super.keyPressed(key, c)) {
return true;
}
// block input // block input
if ((reloadThread != null && !(key == Input.KEY_ESCAPE || key == Input.KEY_F12)) || beatmapMenuTimer > -1 || isScrollingToFocusNode) if ((reloadThread != null && key != Input.KEY_ESCAPE) || beatmapMenuTimer > -1 || isScrollingToFocusNode) {
return; return true;
}
Input input = displayContainer.input;
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case Input.KEY_ESCAPE:
if (reloadThread != null) { if (reloadThread != null) {
// beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser // beatmap reloading: stop parsing beatmaps by sending interrupt to BeatmapParser
reloadThread.interrupt(); reloadThread.interrupt();
} else if (!search.getText().isEmpty()) { } else if (!searchTextField.getText().isEmpty()) {
// clear search text // clear search text
search.setText(""); searchTextField.setText("");
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchTransitionTimer = 0; searchTransitionTimer = 0;
searchResultString = null; searchResultString = null;
} else { } else {
// return to main menu // return to main menu
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); displayContainer.switchState(MainMenu.class);
game.enterState(Opsu.STATE_MAINMENU, new EasedFadeOutTransition(), new FadeInTransition());
} }
break; return true;
case Input.KEY_F1: case Input.KEY_F1:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.MODS); instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.MODS);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
break; return true;
case Input.KEY_F2: case Input.KEY_F2:
if (focusNode == null) if (focusNode == null)
break; break;
@ -1089,25 +1092,25 @@ public class SongMenu extends BasicGameState {
randomStack.push(new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex)); randomStack.push(new SongNode(BeatmapSetList.get().getBaseNode(focusNode.index), focusNode.beatmapIndex));
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }
break; return true;
case Input.KEY_F3: case Input.KEY_F3:
if (focusNode == null) if (focusNode == null)
break; break;
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MenuState state = focusNode.getBeatmapSet().isFavorite() ? MenuState state = focusNode.getBeatmapSet().isFavorite() ?
MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP; MenuState.BEATMAP_FAVORITE : MenuState.BEATMAP;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(state, focusNode); instanceContainer.provide(ButtonMenu.class).setMenuState(state, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
break; return true;
case Input.KEY_F5: case Input.KEY_F5:
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (songFolderChanged) if (songFolderChanged)
reloadBeatmaps(false); reloadBeatmaps(false);
else { else {
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.RELOAD); instanceContainer.provide(ButtonMenu.class).setMenuState(MenuState.RELOAD);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
} }
break; return true;
case Input.KEY_DELETE: case Input.KEY_DELETE:
if (focusNode == null) if (focusNode == null)
break; break;
@ -1115,30 +1118,21 @@ public class SongMenu extends BasicGameState {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ? MenuState ms = (focusNode.beatmapIndex == -1 || focusNode.getBeatmapSet().size() == 1) ?
MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT; MenuState.BEATMAP_DELETE_CONFIRM : MenuState.BEATMAP_DELETE_SELECT;
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(ms, focusNode); instanceContainer.provide(ButtonMenu.class).setMenuState(ms, focusNode);
game.enterState(Opsu.STATE_BUTTONMENU); displayContainer.switchState(ButtonMenu.class);
} }
break; return true;
case Input.KEY_F7:
Options.setNextFPS(container);
break;
case Input.KEY_F10:
Options.toggleMouseDisabled();
break;
case Input.KEY_F12:
Utils.takeScreenShot();
break;
case Input.KEY_ENTER: case Input.KEY_ENTER:
if (focusNode == null) if (focusNode == null)
break; break;
startGame(); startGame();
break; return true;
case Input.KEY_DOWN: case Input.KEY_DOWN:
changeIndex(1); changeIndex(1);
break; return true;
case Input.KEY_UP: case Input.KEY_UP:
changeIndex(-1); changeIndex(-1);
break; return true;
case Input.KEY_RIGHT: case Input.KEY_RIGHT:
if (focusNode == null) if (focusNode == null)
break; break;
@ -1154,7 +1148,7 @@ public class SongMenu extends BasicGameState {
hoverIndex = oldHoverIndex; hoverIndex = oldHoverIndex;
} }
} }
break; return true;
case Input.KEY_LEFT: case Input.KEY_LEFT:
if (focusNode == null) if (focusNode == null)
break; break;
@ -1170,24 +1164,25 @@ public class SongMenu extends BasicGameState {
hoverIndex = oldHoverIndex; hoverIndex = oldHoverIndex;
} }
} }
break; return true;
case Input.KEY_NEXT: case Input.KEY_NEXT:
changeIndex(MAX_SONG_BUTTONS); changeIndex(MAX_SONG_BUTTONS);
break; return true;
case Input.KEY_PRIOR: case Input.KEY_PRIOR:
changeIndex(-MAX_SONG_BUTTONS); changeIndex(-MAX_SONG_BUTTONS);
break; return true;
case Input.KEY_O: case Input.KEY_O:
if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) {
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition()); optionsOverlay.show();
} }
break; return true;
default: default:
// wait for user to finish typing // wait for user to finish typing
// TODO: accept all characters (current conditions are from TextField class) // TODO: accept all characters (current conditions are from TextField class)
if ((c > 31 && c < 127) || key == Input.KEY_BACK) { if ((c > 31 && c < 127) || key == Input.KEY_BACK) {
searchTimer = 0; searchTimer = 0;
int textLength = search.getText().length(); searchTextField.keyPressed(key, c);
int textLength = searchTextField.getText().length();
if (lastSearchTextLength != textLength) { if (lastSearchTextLength != textLength) {
if (key == Input.KEY_BACK) { if (key == Input.KEY_BACK) {
if (textLength == 0) if (textLength == 0)
@ -1197,49 +1192,60 @@ public class SongMenu extends BasicGameState {
lastSearchTextLength = textLength; lastSearchTextLength = textLength;
} }
} }
break; return true;
} }
return true;
} }
@Override @Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) { public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
// block input if (super.mouseDragged(oldx, oldy, newx, newy)) {
if (isInputBlocked()) return true;
return; }
if (isInputBlocked()) {
return true;
}
int diff = newy - oldy; int diff = newy - oldy;
if (diff == 0) if (diff == 0) {
return; return false;
}
// check mouse button (right click scrolls faster on songs) // check mouse button (right click scrolls faster on songs)
int multiplier; int multiplier;
if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) if (displayContainer.input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) {
multiplier = 10; multiplier = 10;
else if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) } else if (displayContainer.input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
multiplier = 1; multiplier = 1;
else } else {
return; return false;
}
// score buttons if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy)) {
if (focusScores != null && focusScores.length >= MAX_SCORE_BUTTONS && ScoreData.areaContains(oldx, oldy))
startScorePos.dragged(-diff * multiplier); startScorePos.dragged(-diff * multiplier);
} else {
// song buttons
else
songScrolling.dragged(-diff * multiplier); songScrolling.dragged(-diff * multiplier);
} }
return true;
}
@Override @Override
public void mouseWheelMoved(int newValue) { public boolean mouseWheelMoved(int newValue) {
// change volume if (super.mouseWheelMoved(newValue)) {
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) { return true;
UI.changeVolume((newValue < 0) ? -1 : 1);
return;
} }
// block input Input input = displayContainer.input;
if (isInputBlocked())
return; if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((newValue < 0) ? -1 : 1);
return true;
}
if (isInputBlocked()) {
return true;
}
int shift = (newValue < 0) ? 1 : -1; int shift = (newValue < 0) ? 1 : -1;
int mouseX = input.getMouseX(), mouseY = input.getMouseY(); int mouseX = input.getMouseX(), mouseY = input.getMouseY();
@ -1251,13 +1257,14 @@ public class SongMenu extends BasicGameState {
// song buttons // song buttons
else else
changeIndex(shift); changeIndex(shift);
return false;
} }
@Override @Override
public void enter(GameContainer container, StateBasedGame game) public void enter() {
throws SlickException { super.enter();
UI.enter(); UI.enter();
Display.setTitle(game.getTitle());
selectModsButton.resetHover(); selectModsButton.resetHover();
selectRandomButton.resetHover(); selectRandomButton.resetHover();
selectMapOptionsButton.resetHover(); selectMapOptionsButton.resetHover();
@ -1275,11 +1282,10 @@ public class SongMenu extends BasicGameState {
songChangeTimer.setTime(songChangeTimer.getDuration()); songChangeTimer.setTime(songChangeTimer.getDuration());
musicIconBounceTimer.setTime(musicIconBounceTimer.getDuration()); musicIconBounceTimer.setTime(musicIconBounceTimer.getDuration());
starStream.clear(); starStream.clear();
sortMenu.activate();
sortMenu.reset(); sortMenu.reset();
// reset song stack // reset song stack
randomStack = new Stack<SongNode>(); randomStack = new Stack<>();
// reload beatmaps if song folder changed // reload beatmaps if song folder changed
if (songFolderChanged && stateAction != MenuState.RELOAD) if (songFolderChanged && stateAction != MenuState.RELOAD)
@ -1307,7 +1313,7 @@ public class SongMenu extends BasicGameState {
// reset game data // reset game data
if (resetGame) { if (resetGame) {
((Game) game.getState(Opsu.STATE_GAME)).resetGameData(); instanceContainer.provide(Game.class).resetGameData();
// destroy extra Clips // destroy extra Clips
MultiClip.destroyExtraClips(); MultiClip.destroyExtraClips();
@ -1439,13 +1445,6 @@ public class SongMenu extends BasicGameState {
} }
} }
@Override
public void leave(GameContainer container, StateBasedGame game)
throws SlickException {
search.setFocus(false);
sortMenu.deactivate();
}
/** /**
* Shifts the startNode forward (+) or backwards (-) by a given number of nodes. * Shifts the startNode forward (+) or backwards (-) by a given number of nodes.
* Initiates sliding "animation" by shifting the button Y position. * Initiates sliding "animation" by shifting the button Y position.
@ -1568,9 +1567,9 @@ public class SongMenu extends BasicGameState {
// change the focus node // change the focus node
if (changeStartNode || (startNode.index == 0 && startNode.beatmapIndex == -1 && startNode.prev == null)) { if (changeStartNode || (startNode.index == 0 && startNode.beatmapIndex == -1 && startNode.prev == null)) {
if (startNode == null || game.getCurrentStateID() != Opsu.STATE_SONGMENU) if (startNode == null || displayContainer.isInState(SongMenu.class)) {
songScrolling.setPosition((node.index - 1) * buttonOffset); songScrolling.setPosition((node.index - 1) * buttonOffset);
else { } else {
isScrollingToFocusNode = true; isScrollingToFocusNode = true;
songScrolling.setSpeedMultiplier(2f); songScrolling.setSpeedMultiplier(2f);
songScrolling.released(); songScrolling.released();
@ -1703,7 +1702,7 @@ public class SongMenu extends BasicGameState {
songInfo = null; songInfo = null;
hoverOffset.setTime(0); hoverOffset.setTime(0);
hoverIndex = null; hoverIndex = null;
search.setText(""); searchTextField.setText("");
searchTimer = SEARCH_DELAY; searchTimer = SEARCH_DELAY;
searchTransitionTimer = SEARCH_TRANSITION_TIME; searchTransitionTimer = SEARCH_TRANSITION_TIME;
searchResultString = null; searchResultString = null;
@ -1784,17 +1783,17 @@ public class SongMenu extends BasicGameState {
} }
// turn on "auto" mod if holding "ctrl" key // turn on "auto" mod if holding "ctrl" key
if (input.isKeyDown(Input.KEY_RCONTROL) || input.isKeyDown(Input.KEY_LCONTROL)) { if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) {
if (!GameMod.AUTO.isActive()) if (!GameMod.AUTO.isActive())
GameMod.AUTO.toggle(true); GameMod.AUTO.toggle(true);
} }
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
MultiClip.destroyExtraClips(); MultiClip.destroyExtraClips();
Game gameState = (Game) game.getState(Opsu.STATE_GAME); Game gameState = instanceContainer.provide(Game.class);
gameState.loadBeatmap(beatmap); gameState.loadBeatmap(beatmap);
gameState.setRestart(Game.Restart.NEW); gameState.setRestart(Game.Restart.NEW);
gameState.setReplay(null); gameState.setReplay(null);
game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition()); displayContainer.switchState(Game.class);
} }
} }

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
@ -36,19 +35,22 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.io.File; import java.io.File;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException; import org.newdawn.slick.util.Log;
import org.newdawn.slick.state.BasicGameState; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState;
/** /**
* "Splash Screen" state. * "Splash Screen" state.
* <p> * <p>
* Loads game resources and enters "Main Menu" state. * Loads game resources and enters "Main Menu" state.
*/ */
public class Splash extends BasicGameState { public class Splash extends BaseOpsuState {
private final InstanceContainer instanceContainer;
/** Minimum time, in milliseconds, to display the splash screen (and fade in the logo). */ /** Minimum time, in milliseconds, to display the splash screen (and fade in the logo). */
private static final int MIN_SPLASH_TIME = 400; private static final int MIN_SPLASH_TIME = 400;
@ -71,18 +73,16 @@ public class Splash extends BasicGameState {
private AnimatedValue logoAlpha; private AnimatedValue logoAlpha;
// game-related variables // game-related variables
private final int state;
private GameContainer container;
private boolean init = false; private boolean init = false;
public Splash(int state) { public Splash(DisplayContainer displayContainer, InstanceContainer instanceContainer) {
this.state = state; super(displayContainer);
this.instanceContainer = instanceContainer;
} }
@Override @Override
public void init(GameContainer container, StateBasedGame game) protected void revalidate() {
throws SlickException { super.revalidate();
this.container = container;
// check if skin changed // check if skin changed
if (Options.getSkin() != null) if (Options.getSkin() != null)
@ -92,7 +92,7 @@ public class Splash extends BasicGameState {
this.watchServiceChange = Options.isWatchServiceEnabled() && BeatmapWatchService.get() == null; this.watchServiceChange = Options.isWatchServiceEnabled() && BeatmapWatchService.get() == null;
// load Utils class first (needed in other 'init' methods) // load Utils class first (needed in other 'init' methods)
Utils.init(container, game); Utils.init(displayContainer);
// fade in logo // fade in logo
this.logoAlpha = new AnimatedValue(MIN_SPLASH_TIME, 0f, 1f, AnimationEquation.LINEAR); this.logoAlpha = new AnimatedValue(MIN_SPLASH_TIME, 0f, 1f, AnimationEquation.LINEAR);
@ -100,16 +100,14 @@ public class Splash extends BasicGameState {
} }
@Override @Override
public void render(GameContainer container, StateBasedGame game, Graphics g) public void render(Graphics g) {
throws SlickException {
g.setBackground(Color.black); g.setBackground(Color.black);
GameImage.MENU_LOGO.getImage().drawCentered(container.getWidth() / 2, container.getHeight() / 2); GameImage.MENU_LOGO.getImage().drawCentered(displayContainer.width / 2, displayContainer.height / 2);
UI.drawLoadingProgress(g); UI.drawLoadingProgress(g);
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void preRenderUpdate() {
throws SlickException {
if (!init) { if (!init) {
init = true; init = true;
@ -165,7 +163,7 @@ public class Splash extends BasicGameState {
} }
// fade in logo // fade in logo
if (logoAlpha.update(delta)) if (logoAlpha.update(displayContainer.renderDelta))
GameImage.MENU_LOGO.getImage().setAlpha(logoAlpha.getValue()); GameImage.MENU_LOGO.getImage().setAlpha(logoAlpha.getValue());
// change states when loading complete // change states when loading complete
@ -173,33 +171,41 @@ public class Splash extends BasicGameState {
// initialize song list // initialize song list
if (BeatmapSetList.get().size() > 0) { if (BeatmapSetList.get().size() > 0) {
BeatmapSetList.get().init(); BeatmapSetList.get().init();
if (Options.isThemeSongEnabled()) if (Options.isThemeSongEnabled()) {
MusicController.playThemeSong(); MusicController.playThemeSong();
else } else {
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }
} else {
// play the theme song
else
MusicController.playThemeSong(); MusicController.playThemeSong();
}
game.enterState(Opsu.STATE_MAINMENU); displayContainer.switchState(MainMenu.class);
} }
} }
@Override @Override
public int getID() { return state; } public boolean onCloseRequest() {
if (thread != null && thread.isAlive()) {
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
Log.warn("InterruptedException while waiting for splash thread to die", e);
}
}
return true;
}
@Override @Override
public void keyPressed(int key, char c) { public boolean keyPressed(int key, char c) {
if (key == Input.KEY_ESCAPE) { if (key != Input.KEY_ESCAPE) {
// close program return false;
if (++escapeCount >= 3) }
container.exit(); if (++escapeCount >= 3) {
displayContainer.exitRequested = true;
// stop parsing beatmaps by sending interrupt to BeatmapParser } else if (thread != null) {
else if (thread != null)
thread.interrupt(); thread.interrupt();
} }
return true;
} }
} }

View File

@ -18,31 +18,22 @@
package itdelatrisu.opsu.ui; package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.skins.Skin; import itdelatrisu.opsu.skins.Skin;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Point; import java.awt.Point;
import java.nio.IntBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.newdawn.slick.*; import org.newdawn.slick.*;
import org.newdawn.slick.state.StateBasedGame;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
/** /**
* Updates and draws the cursor. * Updates and draws the cursor.
*/ */
public class Cursor { public class Cursor {
/** Empty cursor. */
private static org.lwjgl.input.Cursor emptyCursor;
/** Last cursor coordinates. */ /** Last cursor coordinates. */
private Point lastPosition; private Point lastPosition;
@ -63,15 +54,10 @@ public class Cursor {
private static final float CURSOR_SCALE_TIME = 125; private static final float CURSOR_SCALE_TIME = 125;
/** Stores all previous cursor locations to display a trail. */ /** Stores all previous cursor locations to display a trail. */
private LinkedList<Point> trail = new LinkedList<Point>(); private LinkedList<Point> trail = new LinkedList<>();
private boolean newStyle; private boolean newStyle;
// game-related variables
private static GameContainer container;
private static StateBasedGame game;
private static Input input;
public static Color lastObjColor = Color.white; public static Color lastObjColor = Color.white;
public static Color lastMirroredObjColor = Color.white; public static Color lastMirroredObjColor = Color.white;
public static Color nextObjColor = Color.white; public static Color nextObjColor = Color.white;
@ -80,26 +66,6 @@ public class Cursor {
private boolean isMirrored; private boolean isMirrored;
/**
* Initializes the class.
* @param container the game container
* @param game the game object
*/
public static void init(GameContainer container, StateBasedGame game) {
Cursor.container = container;
Cursor.game = game;
Cursor.input = container.getInput();
// create empty cursor to simulate hiding the cursor
try {
int min = org.lwjgl.input.Cursor.getMinCursorSize();
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
emptyCursor = new org.lwjgl.input.Cursor(min, min, min/2, min/2, 1, tmp, null);
} catch (LWJGLException e) {
ErrorHandler.error("Failed to create hidden cursor.", e, true);
}
}
/** /**
* Constructor. * Constructor.
*/ */
@ -108,29 +74,15 @@ public class Cursor {
} }
public Cursor(boolean isMirrored) { public Cursor(boolean isMirrored) {
resetLocations(); resetLocations(0, 0);
this.isMirrored = isMirrored; this.isMirrored = isMirrored;
} }
/** /**
* Draws the cursor. * Draws the cursor.
*/
public void draw() {
int state = game.getCurrentStateID();
boolean mousePressed =
(((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) ||
((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) &&
!(state == Opsu.STATE_GAME && Options.isMouseDisabled())));
draw(input.getMouseX(), input.getMouseY(), mousePressed);
}
/**
* Draws the cursor.
* @param mouseX the mouse x coordinate
* @param mouseY the mouse y coordinate
* @param mousePressed whether or not the mouse button is pressed * @param mousePressed whether or not the mouse button is pressed
*/ */
public void draw(int mouseX, int mouseY, boolean mousePressed) { public void draw(boolean mousePressed) {
if (Options.isCursorDisabled()) if (Options.isCursorDisabled())
return; return;
@ -172,8 +124,6 @@ public class Cursor {
cursorTrail = cursorTrail.getScaledCopy(cursorScale); cursorTrail = cursorTrail.getScaledCopy(cursorScale);
} }
setCursorPosition(mouseX, mouseY);
Color filter; Color filter;
if (isMirrored) { if (isMirrored) {
filter = Dancer.cursorColorMirrorOverride.getMirrorColor(); filter = Dancer.cursorColorMirrorOverride.getMirrorColor();
@ -195,16 +145,16 @@ public class Cursor {
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation); cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
} }
cursorTrail.drawEmbedded( cursorTrail.drawEmbedded(
mouseX - (cursorTrailWidth / 2f), mouseY - (cursorTrailHeight / 2f), lastPosition.x - (cursorTrailWidth / 2f), lastPosition.y - (cursorTrailHeight / 2f),
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation); cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
cursorTrail.endUse(); cursorTrail.endUse();
// draw the other components // draw the other components
if (newStyle && skin.isCursorRotated()) if (newStyle && skin.isCursorRotated())
cursor.setRotation(cursorAngle); cursor.setRotation(cursorAngle);
cursor.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter); cursor.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter);
if (hasMiddle) if (hasMiddle)
cursorMiddle.drawCentered(mouseX, mouseY, Options.isCursorOnlyColorTrail() ? Color.white : filter); cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter);
} }
/** /**
@ -212,10 +162,10 @@ public class Cursor {
* @param mouseX x coordinate to set position to * @param mouseX x coordinate to set position to
* @param mouseY y coordinate to set position to * @param mouseY y coordinate to set position to
*/ */
public void setCursorPosition(int mouseX, int mouseY) { public void setCursorPosition(int delta, int mouseX, int mouseY) {
// TODO: use an image buffer // TODO: use an image buffer
int removeCount = 0; int removeCount = 0;
float FPSmod = Math.max(container.getFPS(), 1) / 30f; float FPSmod = Math.max(1000 / Math.max(delta, 1), 1) / 30f; // TODO
if (newStyle) { if (newStyle) {
// new style: add all points between cursor movements // new style: add all points between cursor movements
if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) { if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) {
@ -301,7 +251,7 @@ public class Cursor {
* If the old style cursor is being used, this will do nothing. * If the old style cursor is being used, this will do nothing.
* @param delta the delta interval since the last call * @param delta the delta interval since the last call
*/ */
public void update(int delta) { public void updateAngle(int delta) {
cursorAngle += delta / 40f; cursorAngle += delta / 40f;
cursorAngle %= 360; cursorAngle %= 360;
} }
@ -309,14 +259,14 @@ public class Cursor {
/** /**
* Resets all cursor data and beatmap skins. * Resets all cursor data and beatmap skins.
*/ */
public void reset() { public void reset(int mouseX, int mouseY) {
// destroy skin images // destroy skin images
GameImage.CURSOR.destroyBeatmapSkinImage(); GameImage.CURSOR.destroyBeatmapSkinImage();
GameImage.CURSOR_MIDDLE.destroyBeatmapSkinImage(); GameImage.CURSOR_MIDDLE.destroyBeatmapSkinImage();
GameImage.CURSOR_TRAIL.destroyBeatmapSkinImage(); GameImage.CURSOR_TRAIL.destroyBeatmapSkinImage();
// reset locations // reset locations
resetLocations(); resetLocations(mouseX, mouseY);
// reset angles // reset angles
cursorAngle = 0f; cursorAngle = 0f;
@ -325,15 +275,13 @@ public class Cursor {
/** /**
* Resets all cursor location data. * Resets all cursor location data.
*/ */
public void resetLocations() { public void resetLocations(int mouseX, int mouseY) {
trail.clear(); trail.clear();
if (lastPosition != null) { lastPosition = new Point(mouseX, mouseY);
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
trail.add(new Point(lastPosition)); trail.add(new Point(lastPosition));
} }
} }
lastPosition = new Point(0, 0);
}
/** /**
* Returns whether or not the cursor is skinned. * Returns whether or not the cursor is skinned.
@ -344,23 +292,4 @@ public class Cursor {
GameImage.CURSOR_TRAIL.hasBeatmapSkinImage()); GameImage.CURSOR_TRAIL.hasBeatmapSkinImage());
} }
/**
* Hides the cursor, if possible.
*/
public void hide() {
if (emptyCursor != null) {
try {
container.setMouseCursor(emptyCursor, 0, 0);
} catch (SlickException e) {
ErrorHandler.error("Failed to hide the cursor.", e, true);
}
}
}
/**
* Unhides the cursor.
*/
public void show() {
container.setDefaultMouseCursor();
}
} }

View File

@ -27,152 +27,62 @@ import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont; import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.gui.AbstractComponent; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.gui.GUIContext; import yugecin.opsudance.core.components.Component;
public class DropdownMenu<E> extends Component {
/**
* Simple dropdown menu.
* <p>
* Basic usage:
* <ul>
* <li>Override {@link #menuClicked(int)} to perform actions when the menu is clicked
* (e.g. play a sound effect, block input under certain conditions).
* <li>Override {@link #itemSelected(int, Object)} to perform actions when a new item is selected.
* <li>Call {@link #activate()}/{@link #deactivate()} whenever the component is needed
* (e.g. in a state's {@code enter} and {@code leave} events.
* </ul>
*
* @param <E> the type of the elements in the menu
*/
public class DropdownMenu<E> extends AbstractComponent {
/** Padding ratios for drawing. */
private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f; private static final float PADDING_Y = 0.1f, CHEVRON_X = 0.03f;
/** Whether this component is active. */ private final DisplayContainer displayContainer;
private boolean active;
/** The menu items. */
private E[] items; private E[] items;
/** The menu item names. */
private String[] itemNames; private String[] itemNames;
private int selectedItemIndex = 0;
private boolean expanded;
/** The index of the selected item. */
private int itemIndex = 0;
/** Whether the menu is expanded. */
private boolean expanded = false;
/** The expanding animation progress. */
private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR); private AnimatedValue expandProgress = new AnimatedValue(300, 0f, 1f, AnimationEquation.LINEAR);
/** The last update time, in milliseconds. */
private long lastUpdateTime;
/** The top-left coordinates. */
private float x, y;
/** The width and height of the dropdown menu. */
private int width, height;
/** The height of the base item. */
private int baseHeight; private int baseHeight;
/** The vertical offset between items. */
private float offsetY; private float offsetY;
/** The colors to use. */ private Color textColor = Color.white;
private Color private Color backgroundColor = Color.black;
textColor = Color.white, backgroundColor = Color.black, private Color highlightColor = Colors.BLUE_DIVIDER;
highlightColor = Colors.BLUE_DIVIDER, borderColor = Colors.BLUE_DIVIDER, private Color borderColor = Colors.BLUE_DIVIDER;
chevronDownColor = textColor, chevronRightColor = backgroundColor; private Color chevronDownColor = textColor;
private Color chevronRightColor = backgroundColor;
/** The fonts to use. */ private UnicodeFont fontNormal = Fonts.MEDIUM;
private UnicodeFont fontNormal = Fonts.MEDIUM, fontSelected = Fonts.MEDIUMBOLD; private UnicodeFont fontSelected = Fonts.MEDIUMBOLD;
/** The chevron images. */ private Image chevronDown;
private Image chevronDown, chevronRight; private Image chevronRight;
/** Should the next click be blocked? */ public DropdownMenu(DisplayContainer displayContainer, E[] items, int x, int y, int width) {
private boolean blockClick = false; this.displayContainer = displayContainer;
/**
* Creates a new dropdown menu.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
*/
public DropdownMenu(GUIContext container, E[] items, float x, float y) {
this(container, items, x, y, 0);
}
/**
* Creates a new dropdown menu with the given fonts.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
* @param normal the normal font
* @param selected the font for the selected item
*/
public DropdownMenu(GUIContext container, E[] items, float x, float y, UnicodeFont normal, UnicodeFont selected) {
this(container, items, x, y, 0, normal, selected);
}
/**
* Creates a new dropdown menu with the given width.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
* @param width the menu width
*/
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width) {
super(container);
init(items, x, y, width); init(items, x, y, width);
} }
/**
* Creates a new dropdown menu with the given width and fonts.
* @param container the container rendering this menu
* @param items the list of items (with names given as their {@code toString()} methods)
* @param x the top-left x coordinate
* @param y the top-left y coordinate
* @param width the menu width
* @param normal the normal font
* @param selected the font for the selected item
*/
public DropdownMenu(GUIContext container, E[] items, float x, float y, int width, UnicodeFont normal, UnicodeFont selected) {
super(container);
this.fontNormal = normal;
this.fontSelected = selected;
init(items, x, y, width);
}
/**
* Returns the maximum item width from the list.
*/
private int getMaxItemWidth() { private int getMaxItemWidth() {
int maxWidth = 0; int maxWidth = 0;
for (int i = 0; i < itemNames.length; i++) { for (String itemName : itemNames) {
int w = fontSelected.getWidth(itemNames[i]); int w = fontSelected.getWidth(itemName);
if (w > maxWidth) if (w > maxWidth) {
maxWidth = w; maxWidth = w;
} }
}
return maxWidth; return maxWidth;
} }
/** @SuppressWarnings("SuspiciousNameCombination")
* Initializes the component. private void init(E[] items, int x, int y, int width) {
*/
private void init(E[] items, float x, float y, int width) {
this.items = items; this.items = items;
this.itemNames = new String[items.length]; this.itemNames = new String[items.length];
for (int i = 0; i < itemNames.length; i++) for (int i = 0; i < itemNames.length; i++) {
itemNames[i] = items[i].toString(); itemNames[i] = items[i].toString();
}
this.x = x; this.x = x;
this.y = y; this.y = y;
this.baseHeight = fontNormal.getLineHeight(); this.baseHeight = fontNormal.getLineHeight();
@ -188,77 +98,27 @@ public class DropdownMenu<E> extends AbstractComponent {
} }
@Override @Override
public void setLocation(int x, int y) { public void updateHover(int x, int y) {
this.x = x; this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + (expanded ? height : baseHeight);
this.y = y; }
public boolean baseContains(int x, int y) {
return (x > this.x && x < this.x + width && y > this.y && y < this.y + baseHeight);
} }
@Override @Override
public int getX() { return (int) x; } public void render(Graphics g) {
int delta = displayContainer.renderDelta;
@Override
public int getY() { return (int) y; }
@Override
public int getWidth() { return width; }
@Override
public int getHeight() { return (expanded) ? height : baseHeight; }
/** Activates the component. */
public void activate() { this.active = true; }
/** Deactivates the component. */
public void deactivate() { this.active = false; }
/**
* Returns whether the dropdown menu is currently open.
* @return true if open, false otherwise
*/
public boolean isOpen() { return expanded; }
/**
* Opens or closes the dropdown menu.
* @param flag true to open, false to close
*/
public void open(boolean flag) { this.expanded = flag; }
/**
* Returns true if the coordinates are within the menu bounds.
* @param cx the x coordinate
* @param cy the y coordinate
*/
public boolean contains(float cx, float cy) {
return (cx > x && cx < x + width && (
(cy > y && cy < y + baseHeight) ||
(expanded && cy > y + offsetY && cy < y + height)));
}
/**
* Returns true if the coordinates are within the base item bounds.
* @param cx the x coordinate
* @param cy the y coordinate
*/
public boolean baseContains(float cx, float cy) {
return (cx > x && cx < x + width && cy > y && cy < y + baseHeight);
}
@Override
public void render(GUIContext container, Graphics g) throws SlickException {
// update animation // update animation
long time = container.getTime();
if (lastUpdateTime > 0) {
int delta = (int) (time - lastUpdateTime);
expandProgress.update((expanded) ? delta : -delta * 2); expandProgress.update((expanded) ? delta : -delta * 2);
}
this.lastUpdateTime = time;
// get parameters // get parameters
Input input = container.getInput(); int idx = getIndexAt(displayContainer.mouseY);
int idx = getIndexAt(input.getMouseX(), input.getMouseY());
float t = expandProgress.getValue(); float t = expandProgress.getValue();
if (expanded) if (expanded) {
t = AnimationEquation.OUT_CUBIC.calc(t); t = AnimationEquation.OUT_CUBIC.calc(t);
}
// background and border // background and border
Color oldGColor = g.getColor(); Color oldGColor = g.getColor();
@ -293,12 +153,12 @@ public class DropdownMenu<E> extends AbstractComponent {
// text // text
chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor); chevronDown.draw(x + width - chevronDown.getWidth() - width * CHEVRON_X, y + (baseHeight - chevronDown.getHeight()) / 2f, chevronDownColor);
fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[itemIndex], textColor); fontNormal.drawString(x + (width * 0.03f), y + (fontNormal.getPaddingTop() + fontNormal.getPaddingBottom()) / 2f, itemNames[selectedItemIndex], textColor);
float oldTextAlpha = textColor.a; float oldTextAlpha = textColor.a;
textColor.a *= t; textColor.a *= t;
if (expanded || t >= 0.0001) { if (expanded || t >= 0.0001) {
for (int i = 0; i < itemNames.length; i++) { for (int i = 0; i < itemNames.length; i++) {
Font f = (i == itemIndex) ? fontSelected : fontNormal; Font f = (i == selectedItemIndex) ? fontSelected : fontNormal;
if (i == idx && t >= 0.999) if (i == idx && t >= 0.999)
chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor); chevronRight.draw(x, y + offsetY + (offsetY * i) + (offsetY - chevronRight.getHeight()) / 2f, chevronRightColor);
f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor); f.drawString(x + chevronRight.getWidth(), y + offsetY + (offsetY * i * t), itemNames[i], textColor);
@ -310,131 +170,89 @@ public class DropdownMenu<E> extends AbstractComponent {
/** /**
* Returns the index of the item at the given location, -1 for the base item, * Returns the index of the item at the given location, -1 for the base item,
* and -2 if there is no item at the location. * and -2 if there is no item at the location.
* @param cx the x coordinate * @param y the y coordinate
* @param cy the y coordinate
*/ */
private int getIndexAt(float cx, float cy) { private int getIndexAt(int y) {
if (!contains(cx, cy)) if (!hovered) {
return -2; return -2;
if (cy <= y + baseHeight) }
if (y <= this.y + baseHeight) {
return -1; return -1;
if (!expanded) }
if (!expanded) {
return -2; return -2;
return (int) ((cy - (y + offsetY)) / offsetY); }
return (int) ((y - (this.y + offsetY)) / offsetY);
} }
/**
* Resets the menu state.
*/
public void reset() { public void reset() {
this.expanded = false; this.expanded = false;
this.lastUpdateTime = 0;
expandProgress.setTime(0); expandProgress.setTime(0);
blockClick = false; }
@Override
public void setFocused(boolean focused) {
super.setFocused(focused);
expanded = focused;
} }
@Override @Override
public void mousePressed(int button, int x, int y) { public boolean isFocusable() {
if (!active) return true;
return; }
if (button == Input.MOUSE_MIDDLE_BUTTON) @Override
return; public void mouseReleased(int button) {
super.mouseReleased(button);
int idx = getIndexAt(x, y); if (button == Input.MOUSE_MIDDLE_BUTTON) {
return;
}
int idx = getIndexAt(displayContainer.mouseY);
if (idx == -2) { if (idx == -2) {
this.expanded = false; this.expanded = false;
return; return;
} }
if (!menuClicked(idx)) if (!canSelect(selectedItemIndex)) {
return; return;
this.expanded = (idx == -1) ? !expanded : false;
if (idx >= 0 && itemIndex != idx) {
this.itemIndex = idx;
itemSelected(idx, items[idx]);
} }
blockClick = true; this.expanded = (idx == -1) && !expanded;
consumeEvent(); if (idx >= 0 && selectedItemIndex != idx) {
} this.selectedItemIndex = idx;
itemSelected(idx, items[selectedItemIndex]);
@Override
public void mouseClicked(int button, int x, int y, int clickCount) {
if (!active)
return;
if (blockClick) {
blockClick = false;
consumeEvent();
} }
} }
/** protected boolean canSelect(int index) {
* Notification that a new item was selected (via override). return true;
* @param index the index of the item selected
* @param item the item selected
*/
public void itemSelected(int index, E item) {}
/**
* Notification that the menu was clicked (via override).
* @param index the index of the item clicked, or -1 for the base item
* @return true to process the click, or false to block/intercept it
*/
public boolean menuClicked(int index) { return true; }
@Override
public void setFocus(boolean focus) { /* does not currently use the "focus" concept */ }
@Override
public void mouseReleased(int button, int x, int y) { /* does not currently use the "focus" concept */ }
/**
* Selects the item at the given index.
* @param index the list item index
* @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size
*/
public void setSelectedIndex(int index) {
if (index < 0 || index >= items.length)
throw new IllegalArgumentException();
this.itemIndex = index;
} }
/** protected void itemSelected(int index, E item) {
* Returns the index of the selected item. }
*/
public int getSelectedIndex() { return itemIndex; }
/** public E getSelectedItem() {
* Returns the selected item. return items[selectedItemIndex];
*/ }
public E getSelectedItem() { return items[itemIndex]; }
/** public void setBackgroundColor(Color c) {
* Returns the item at the given index. this.backgroundColor = c;
* @param index the list item index }
*/
public E getItemAt(int index) { return items[index]; }
/** public void setHighlightColor(Color c) {
* Returns the number of items in the list. this.highlightColor = c;
*/ }
public int getItemCount() { return items.length; }
/** Sets the text color. */ public void setBorderColor(Color c) {
public void setTextColor(Color c) { this.textColor = c; } this.borderColor = c;
}
/** Sets the background color. */ public void setChevronDownColor(Color c) {
public void setBackgroundColor(Color c) { this.backgroundColor = c; } this.chevronDownColor = c;
}
/** Sets the highlight color. */ public void setChevronRightColor(Color c) {
public void setHighlightColor(Color c) { this.highlightColor = c; } this.chevronRightColor = c;
}
/** Sets the border color. */
public void setBorderColor(Color c) { this.borderColor = c; }
/** Sets the down chevron color. */
public void setChevronDownColor(Color c) { this.chevronDownColor = c; }
/** Sets the right chevron color. */
public void setChevronRightColor(Color c) { this.chevronRightColor = c; }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.ui; package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
@ -29,23 +28,16 @@ import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.Input; import yugecin.opsudance.core.DisplayContainer;
import org.newdawn.slick.state.StateBasedGame;
/** /**
* Draws common UI components. * Draws common UI components.
*/ */
public class UI { public class UI {
/** Cursor. */
private static Cursor cursor = new Cursor();
/** Back button. */ /** Back button. */
private static MenuButton backButton; private static MenuButton backButton;
@ -75,32 +67,24 @@ public class UI {
private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR); private static AnimatedValue tooltipAlpha = new AnimatedValue(200, 0f, 1f, AnimationEquation.LINEAR);
// game-related variables // game-related variables
private static GameContainer container; private static DisplayContainer displayContainer;
private static Input input;
// This class should not be instantiated. // This class should not be instantiated.
private UI() {} private UI() {}
/** /**
* Initializes UI data. * Initializes UI data.
* @param container the game container
* @param game the game object
*/ */
public static void init(GameContainer container, StateBasedGame game) { public static void init(DisplayContainer displayContainer) {
UI.container = container; UI.displayContainer = displayContainer;
UI.input = container.getInput();
// initialize cursor
Cursor.init(container, game);
cursor.hide();
// back button // back button
if (GameImage.MENU_BACK.getImages() != null) { if (GameImage.MENU_BACK.getImages() != null) {
Animation back = GameImage.MENU_BACK.getAnimation(120); Animation back = GameImage.MENU_BACK.getAnimation(120);
backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f)); backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f));
} else { } else {
Image back = GameImage.MENU_BACK.getImage(); Image back = GameImage.MENU_BACK.getImage();
backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f)); backButton = new MenuButton(back, back.getWidth() / 2f, displayContainer.height - (back.getHeight() / 2f));
} }
backButton.setHoverAnimationDuration(350); backButton.setHoverAnimationDuration(350);
backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK); backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
@ -112,7 +96,6 @@ public class UI {
* @param delta the delta interval since the last call. * @param delta the delta interval since the last call.
*/ */
public static void update(int delta) { public static void update(int delta) {
cursor.update(delta);
updateVolumeDisplay(delta); updateVolumeDisplay(delta);
updateBarNotification(delta); updateBarNotification(delta);
tooltipAlpha.update(-delta); tooltipAlpha.update(-delta);
@ -125,24 +108,6 @@ public class UI {
public static void draw(Graphics g) { public static void draw(Graphics g) {
drawBarNotification(g); drawBarNotification(g);
drawVolume(g); drawVolume(g);
drawFPS();
cursor.draw();
drawTooltip(g);
}
/**
* Draws the global UI components: cursor, FPS, volume bar, tooltips, bar notifications.
* @param g the graphics context
* @param mouseX the mouse x coordinate
* @param mouseY the mouse y coordinate
* @param mousePressed whether or not the mouse button is pressed
*/
public static void draw(Graphics g, int mouseX, int mouseY, boolean mousePressed) {
drawBarNotification(g);
drawVolume(g);
drawFPS();
cursor.draw(mouseX, mouseY, mousePressed);
drawTooltip(g);
} }
/** /**
@ -150,16 +115,10 @@ public class UI {
*/ */
public static void enter() { public static void enter() {
backButton.resetHover(); backButton.resetHover();
cursor.resetLocations();
resetBarNotification(); resetBarNotification();
resetTooltip(); resetTooltip();
} }
/**
* Returns the game cursor.
*/
public static Cursor getCursor() { return cursor; }
/** /**
* Returns the 'menu-back' MenuButton. * Returns the 'menu-back' MenuButton.
*/ */
@ -189,27 +148,6 @@ public class UI {
Fonts.MEDIUM.drawString(tabTextX, tabTextY, text, textColor); Fonts.MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
} }
/**
* Draws the FPS at the bottom-right corner of the game container.
* If the option is not activated, this will do nothing.
*/
public static void drawFPS() {
if (!Options.isFPSCounterEnabled())
return;
String fps = String.format("%dFPS", container.getFPS());
Fonts.BOLD.drawString(
container.getWidth() * 0.997f - Fonts.BOLD.getWidth(fps),
container.getHeight() * 0.997f - Fonts.BOLD.getHeight(fps),
Integer.toString(container.getFPS()), Color.white
);
Fonts.DEFAULT.drawString(
container.getWidth() * 0.997f - Fonts.BOLD.getWidth("FPS"),
container.getHeight() * 0.997f - Fonts.BOLD.getHeight("FPS"),
"FPS", Color.white
);
}
/** /**
* Draws the volume bar on the middle right-hand side of the game container. * Draws the volume bar on the middle right-hand side of the game container.
* Only draws if the volume has recently been changed using with {@link #changeVolume(int)}. * Only draws if the volume has recently been changed using with {@link #changeVolume(int)}.
@ -219,7 +157,6 @@ public class UI {
if (volumeDisplay == -1) if (volumeDisplay == -1)
return; return;
int width = container.getWidth(), height = container.getHeight();
Image img = GameImage.VOLUME.getImage(); Image img = GameImage.VOLUME.getImage();
// move image in/out // move image in/out
@ -230,13 +167,13 @@ public class UI {
else if (ratio >= 0.9f) else if (ratio >= 0.9f)
xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f)); xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f));
img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f); img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f);
float barHeight = img.getHeight() * 0.9f; float barHeight = img.getHeight() * 0.9f;
float volume = Options.getMasterVolume(); float volume = Options.getMasterVolume();
g.setColor(Color.white); g.setColor(Color.white);
g.fillRoundRect( g.fillRoundRect(
width - (img.getWidth() * 0.368f) + xOffset, displayContainer.width - (img.getWidth() * 0.368f) + xOffset,
(height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), (displayContainer.height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
img.getWidth() * 0.15f, barHeight * volume, 3 img.getWidth() * 0.15f, barHeight * volume, 3
); );
} }
@ -260,7 +197,7 @@ public class UI {
*/ */
public static void changeVolume(int units) { public static void changeVolume(int units) {
final float UNIT_OFFSET = 0.05f; final float UNIT_OFFSET = 0.05f;
Options.setMasterVolume(container, Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f)); Options.setMasterVolume(Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f));
if (volumeDisplay == -1) if (volumeDisplay == -1)
volumeDisplay = 0; volumeDisplay = 0;
else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10) else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10)
@ -294,8 +231,8 @@ public class UI {
return; return;
// draw loading info // draw loading info
float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f; float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f;
float lineY = container.getHeight() - marginY; float lineY = displayContainer.height - marginY;
int lineOffsetY = Fonts.MEDIUM.getLineHeight(); int lineOffsetY = Fonts.MEDIUM.getLineHeight();
if (Options.isLoadVerbose()) { if (Options.isLoadVerbose()) {
// verbose: display percentages and file names // verbose: display percentages and file names
@ -308,7 +245,7 @@ public class UI {
Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white); Fonts.MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white);
g.setColor(Color.white); g.setColor(Color.white);
g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f), g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f),
(container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4 (displayContainer.width - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
); );
} }
} }
@ -332,7 +269,7 @@ public class UI {
float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight, float unitBaseX, float unitBaseY, float unitWidth, float scrollAreaHeight,
Color bgColor, Color scrollbarColor, boolean right Color bgColor, Color scrollbarColor, boolean right
) { ) {
float scrollbarWidth = container.getWidth() * 0.00347f; float scrollbarWidth = displayContainer.width * 0.00347f;
float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength; float scrollbarHeight = scrollAreaHeight * lengthShown / totalLength;
float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown)); float offsetY = (scrollAreaHeight - scrollbarHeight) * (position / (totalLength - lengthShown));
float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0); float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0);
@ -368,8 +305,7 @@ public class UI {
if (tooltipAlpha.getTime() == 0 || tooltip == null) if (tooltipAlpha.getTime() == 0 || tooltip == null)
return; return;
int containerWidth = container.getWidth(), containerHeight = container.getHeight(); int margin = displayContainer.width / 100, textMarginX = 2;
int margin = containerWidth / 100, textMarginX = 2;
int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2; int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2;
int lineHeight = Fonts.SMALL.getLineHeight(); int lineHeight = Fonts.SMALL.getLineHeight();
int textWidth = textMarginX * 2, textHeight = lineHeight; int textWidth = textMarginX * 2, textHeight = lineHeight;
@ -387,13 +323,14 @@ public class UI {
textWidth += Fonts.SMALL.getWidth(tooltip); textWidth += Fonts.SMALL.getWidth(tooltip);
// get drawing coordinates // get drawing coordinates
int x = input.getMouseX() + offset, y = input.getMouseY() + offset; int x = displayContainer.mouseX + offset;
if (x + textWidth > containerWidth - margin) int y = displayContainer.mouseY + offset;
x = containerWidth - margin - textWidth; if (x + textWidth > displayContainer.width - margin)
x = displayContainer.width - margin - textWidth;
else if (x < margin) else if (x < margin)
x = margin; x = margin;
if (y + textHeight > containerHeight - margin) if (y + textHeight > displayContainer.height - margin)
y = containerHeight - margin - textHeight; y = displayContainer.height - margin - textHeight;
else if (y < margin) else if (y < margin)
y = margin; y = margin;
@ -467,13 +404,13 @@ public class UI {
float alpha = 1f; float alpha = 1f;
if (barNotifTimer >= BAR_NOTIFICATION_TIME * 0.9f) if (barNotifTimer >= BAR_NOTIFICATION_TIME * 0.9f)
alpha -= 1 - ((BAR_NOTIFICATION_TIME - barNotifTimer) / (BAR_NOTIFICATION_TIME * 0.1f)); alpha -= 1 - ((BAR_NOTIFICATION_TIME - barNotifTimer) / (BAR_NOTIFICATION_TIME * 0.1f));
int midX = container.getWidth() / 2, midY = container.getHeight() / 2; int midX = displayContainer.width / 2, midY = displayContainer.height / 2;
float barHeight = Fonts.LARGE.getLineHeight() * (1f + 0.6f * Math.min(barNotifTimer * 15f / BAR_NOTIFICATION_TIME, 1f)); float barHeight = Fonts.LARGE.getLineHeight() * (1f + 0.6f * Math.min(barNotifTimer * 15f / BAR_NOTIFICATION_TIME, 1f));
float oldAlphaB = Colors.BLACK_ALPHA.a, oldAlphaW = Colors.WHITE_ALPHA.a; float oldAlphaB = Colors.BLACK_ALPHA.a, oldAlphaW = Colors.WHITE_ALPHA.a;
Colors.BLACK_ALPHA.a *= alpha; Colors.BLACK_ALPHA.a *= alpha;
Colors.WHITE_ALPHA.a = alpha; Colors.WHITE_ALPHA.a = alpha;
g.setColor(Colors.BLACK_ALPHA); g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, midY - barHeight / 2f, container.getWidth(), barHeight); g.fillRect(0, midY - barHeight / 2f, displayContainer.width, barHeight);
Fonts.LARGE.drawString( Fonts.LARGE.drawString(
midX - Fonts.LARGE.getWidth(barNotif) / 2f, midX - Fonts.LARGE.getWidth(barNotif) / 2f,
midY - Fonts.LARGE.getLineHeight() / 2.2f, midY - Fonts.LARGE.getLineHeight() / 2.2f,
@ -482,19 +419,4 @@ public class UI {
Colors.WHITE_ALPHA.a = oldAlphaW; Colors.WHITE_ALPHA.a = oldAlphaW;
} }
/**
* Shows a confirmation dialog (used before exiting the game).
* @param message the message to display
* @return true if user selects "yes", false otherwise
*/
public static boolean showExitConfirmation(String message) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
ErrorHandler.error("Could not set system look and feel for exit confirmation.", e, true);
}
int n = JOptionPane.showConfirmDialog(null, message, "Warning",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
return (n != JOptionPane.YES_OPTION);
}
} }

View File

@ -1,907 +0,0 @@
/*
* Copyright (c) 2013, Slick2D
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the Slick2D nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.newdawn.slick;
import java.io.IOException;
import java.util.Properties;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Cursor;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.Drawable;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.newdawn.slick.gui.GUIContext;
import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.opengl.CursorLoader;
import org.newdawn.slick.opengl.ImageData;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
/**
* A generic game container that handles the game loop, fps recording and
* managing the input system
*
* @author kevin
*/
public abstract class GameContainer implements GUIContext {
/** The renderer to use for all GL operations */
protected static SGL GL = Renderer.get();
/** The shared drawable if any */
protected static Drawable SHARED_DRAWABLE;
/** The time the last frame was rendered */
protected long lastFrame;
/** The last time the FPS recorded */
protected long lastFPS;
/** The last recorded FPS */
protected int recordedFPS;
/** The current count of FPS */
protected int fps;
/** True if we're currently running the game loop */
protected boolean running = true;
/** The width of the display */
protected int width;
/** The height of the display */
protected int height;
/** The game being managed */
protected Game game;
/** The default font to use in the graphics context */
private Font defaultFont;
/** The graphics context to be passed to the game */
private Graphics graphics;
/** The input system to pass to the game */
protected Input input;
/** The FPS we want to lock to */
protected int targetFPS = -1;
/** True if we should show the fps */
private boolean showFPS = true;
/** The minimum logic update interval */
protected long minimumLogicInterval = 1;
/** The stored delta */
protected long storedDelta;
/** The maximum logic update interval */
protected long maximumLogicInterval = 0;
/** The last game started */
protected Game lastGame;
/** True if we should clear the screen each frame */
protected boolean clearEachFrame = true;
/** True if the game is paused */
protected boolean paused;
/** True if we should force exit */
protected boolean forceExit = true;
/** True if vsync has been requested */
protected boolean vsync;
/** Smoothed deltas requested */
protected boolean smoothDeltas;
/** The number of samples we'll attempt through hardware */
protected int samples;
/** True if this context supports multisample */
protected boolean supportsMultiSample;
/** True if we should render when not focused */
protected boolean alwaysRender;
/** True if we require stencil bits */
protected static boolean stencil;
/**
* Create a new game container wrapping a given game
*
* @param game The game to be wrapped
*/
protected GameContainer(Game game) {
this.game = game;
lastFrame = getTime();
getBuildVersion();
Log.checkVerboseLogSetting();
}
public static void enableStencil() {
stencil = true;
}
/**
* Set the default font that will be intialised in the graphics held in this container
*
* @param font The font to use as default
*/
public void setDefaultFont(Font font) {
if (font != null) {
this.defaultFont = font;
} else {
Log.warn("Please provide a non null font");
}
}
/**
* Indicate whether we want to try to use fullscreen multisampling. This will
* give antialiasing across the whole scene using a hardware feature.
*
* @param samples The number of samples to attempt (2 is safe)
*/
public void setMultiSample(int samples) {
this.samples = samples;
}
/**
* Check if this hardware can support multi-sampling
*
* @return True if the hardware supports multi-sampling
*/
public boolean supportsMultiSample() {
return supportsMultiSample;
}
/**
* The number of samples we're attempting to performing using
* hardware multisampling
*
* @return The number of samples requested
*/
public int getSamples() {
return samples;
}
/**
* Indicate if we should force exitting the VM at the end
* of the game (default = true)
*
* @param forceExit True if we should force the VM exit
*/
public void setForceExit(boolean forceExit) {
this.forceExit = forceExit;
}
/**
* Indicate if we want to smooth deltas. This feature will report
* a delta based on the FPS not the time passed. This works well with
* vsync.
*
* @param smoothDeltas True if we should report smooth deltas
*/
public void setSmoothDeltas(boolean smoothDeltas) {
this.smoothDeltas = smoothDeltas;
}
/**
* Check if the display is in fullscreen mode
*
* @return True if the display is in fullscreen mode
*/
public boolean isFullscreen() {
return false;
}
/**
* Get the aspect ratio of the screen
*
* @return The aspect ratio of the display
*/
public float getAspectRatio() {
return getWidth() / getHeight();
}
/**
* Indicate whether we want to be in fullscreen mode. Note that the current
* display mode must be valid as a fullscreen mode for this to work
*
* @param fullscreen True if we want to be in fullscreen mode
* @throws SlickException Indicates we failed to change the display mode
*/
public void setFullscreen(boolean fullscreen) throws SlickException {
}
/**
* Enable shared OpenGL context. After calling this all containers created
* will shared a single parent context
*
* @throws SlickException Indicates a failure to create the shared drawable
*/
public static void enableSharedContext() throws SlickException {
try {
SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null);
} catch (LWJGLException e) {
throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e);
}
}
/**
* Get the context shared by all containers
*
* @return The context shared by all the containers or null if shared context isn't enabled
*/
public static Drawable getSharedContext() {
return SHARED_DRAWABLE;
}
/**
* Indicate if we should clear the screen at the beginning of each frame. If you're
* rendering to the whole screen each frame then setting this to false can give
* some performance improvements
*
* @param clear True if the the screen should be cleared each frame
*/
public void setClearEachFrame(boolean clear) {
this.clearEachFrame = clear;
}
/**
* Renitialise the game and the context in which it's being rendered
*
* @throws SlickException Indicates a failure rerun initialisation routines
*/
public void reinit() throws SlickException {
}
/**
* Pause the game - i.e. suspend updates
*/
public void pause()
{
setPaused(true);
}
/**
* Resumt the game - i.e. continue updates
*/
public void resume()
{
setPaused(false);
}
/**
* Check if the container is currently paused.
*
* @return True if the container is paused
*/
public boolean isPaused() {
return paused;
}
/**
* Indicates if the game should be paused, i.e. if updates
* should be propogated through to the game.
*
* @param paused True if the game should be paused
*/
public void setPaused(boolean paused)
{
this.paused = paused;
}
/**
* True if this container should render when it has focus
*
* @return True if this container should render when it has focus
*/
public boolean getAlwaysRender () {
return alwaysRender;
}
/**
* Indicate whether we want this container to render when it has focus
*
* @param alwaysRender True if this container should render when it has focus
*/
public void setAlwaysRender (boolean alwaysRender) {
this.alwaysRender = alwaysRender;
}
/**
* Get the build number of slick
*
* @return The build number of slick
*/
public static int getBuildVersion() {
try {
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream("version"));
int build = Integer.parseInt(props.getProperty("build"));
Log.info("Slick Build #"+build);
return build;
} catch (Exception e) {
Log.info("Unable to determine Slick build number");
return -1;
}
}
/**
* Get the default system font
*
* @return The default system font
*/
@Override
public Font getDefaultFont() {
return defaultFont;
}
/**
* Check if sound effects are enabled
*
* @return True if sound effects are enabled
*/
public boolean isSoundOn() {
return SoundStore.get().soundsOn();
}
/**
* Check if music is enabled
*
* @return True if music is enabled
*/
public boolean isMusicOn() {
return SoundStore.get().musicOn();
}
/**
* Indicate whether music should be enabled
*
* @param on True if music should be enabled
*/
public void setMusicOn(boolean on) {
SoundStore.get().setMusicOn(on);
}
/**
* Indicate whether sound effects should be enabled
*
* @param on True if sound effects should be enabled
*/
public void setSoundOn(boolean on) {
SoundStore.get().setSoundsOn(on);
}
/**
* Retrieve the current default volume for music
* @return the current default volume for music
*/
public float getMusicVolume() {
return SoundStore.get().getMusicVolume();
}
/**
* Retrieve the current default volume for sound fx
* @return the current default volume for sound fx
*/
public float getSoundVolume() {
return SoundStore.get().getSoundVolume();
}
/**
* Set the default volume for sound fx
* @param volume the new default value for sound fx volume
*/
public void setSoundVolume(float volume) {
SoundStore.get().setSoundVolume(volume);
}
/**
* Set the default volume for music
* @param volume the new default value for music volume
*/
public void setMusicVolume(float volume) {
SoundStore.get().setMusicVolume(volume);
}
/**
* Get the width of the standard screen resolution
*
* @return The screen width
*/
@Override
public abstract int getScreenWidth();
/**
* Get the height of the standard screen resolution
*
* @return The screen height
*/
@Override
public abstract int getScreenHeight();
/**
* Get the width of the game canvas
*
* @return The width of the game canvas
*/
@Override
public int getWidth() {
return width;
}
/**
* Get the height of the game canvas
*
* @return The height of the game canvas
*/
@Override
public int getHeight() {
return height;
}
/**
* Set the icon to be displayed if possible in this type of
* container
*
* @param ref The reference to the icon to be displayed
* @throws SlickException Indicates a failure to load the icon
*/
public abstract void setIcon(String ref) throws SlickException;
/**
* Set the icons to be used for this application. Note that the size of the icon
* defines how it will be used. Important ones to note
*
* Windows window icon must be 16x16
* Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32)
*
* @param refs The reference to the icon to be displayed
* @throws SlickException Indicates a failure to load the icon
*/
public abstract void setIcons(String[] refs) throws SlickException;
/**
* Get the accurate system time
*
* @return The system time in milliseconds
*/
@Override
public long getTime() {
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
/**
* Sleep for a given period
*
* @param milliseconds The period to sleep for in milliseconds
*/
public void sleep(int milliseconds) {
long target = getTime()+milliseconds;
while (getTime() < target) {
try { Thread.sleep(1); } catch (Exception e) {}
}
}
/**
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
* shouldn't have any impact on FPS.
*
* @param ref The location of the image to be loaded for the cursor
* @param hotSpotX The x coordinate of the hotspot within the cursor image
* @param hotSpotY The y coordinate of the hotspot within the cursor image
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
*/
@Override
public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException;
/**
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
* shouldn't have any impact on FPS.
*
* @param data The image data from which the cursor can be construted
* @param hotSpotX The x coordinate of the hotspot within the cursor image
* @param hotSpotY The y coordinate of the hotspot within the cursor image
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
*/
@Override
public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException;
/**
* Set the mouse cursor based on the contents of the image. Note that this will not take
* account of render state type changes to images (rotation and such). If these effects
* are required it is recommended that an offscreen buffer be used to produce an appropriate
* image. An offscreen buffer will always be used to produce the new cursor and as such
* this operation an be very expensive
*
* @param image The image to use as the cursor
* @param hotSpotX The x coordinate of the hotspot within the cursor image
* @param hotSpotY The y coordinate of the hotspot within the cursor image
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
*/
public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException;
/**
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
* shouldn't have any impact on FPS.
*
* @param cursor The cursor to use
* @param hotSpotX The x coordinate of the hotspot within the cursor image
* @param hotSpotY The y coordinate of the hotspot within the cursor image
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
*/
@Override
public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException;
/**
* Get a cursor based on a image reference on the classpath. The image
* is assumed to be a set/strip of cursor animation frames running from top to
* bottom.
*
* @param ref The reference to the image to be loaded
* @param x The x-coordinate of the cursor hotspot (left {@literal ->} right)
* @param y The y-coordinate of the cursor hotspot (bottom {@literal ->} top)
* @param width The x width of the cursor
* @param height The y height of the cursor
* @param cursorDelays image delays between changing frames in animation
*
* @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor
*/
public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException
{
try {
Cursor cursor;
cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays);
setMouseCursor(cursor, x, y);
} catch (IOException e) {
throw new SlickException("Failed to set mouse cursor", e);
} catch (LWJGLException e) {
throw new SlickException("Failed to set mouse cursor", e);
}
}
/**
* Set the default mouse cursor - i.e. the original cursor before any native
* cursor was set
*/
@Override
public abstract void setDefaultMouseCursor();
/**
* Get the input system
*
* @return The input system available to this game container
*/
@Override
public Input getInput() {
return input;
}
/**
* Get the current recorded FPS (frames per second)
*
* @return The current FPS
*/
public int getFPS() {
return recordedFPS;
}
/**
* Indicate whether mouse cursor should be grabbed or not
*
* @param grabbed True if mouse cursor should be grabbed
*/
public abstract void setMouseGrabbed(boolean grabbed);
/**
* Check if the mouse cursor is current grabbed. This will cause it not
* to be seen.
*
* @return True if the mouse is currently grabbed
*/
public abstract boolean isMouseGrabbed();
/**
* Retrieve the time taken to render the last frame, i.e. the change in time - delta.
*
* @return The time taken to render the last frame
*/
protected int getDelta() {
long time = getTime();
int delta = (int) (time - lastFrame);
lastFrame = time;
return delta;
}
/**
* Updated the FPS counter
*/
protected void updateFPS() {
if (getTime() - lastFPS > 1000) {
lastFPS = getTime();
recordedFPS = fps;
fps = 0;
}
fps++;
}
/**
* Set the minimum amount of time in milliseonds that has to
* pass before update() is called on the container game. This gives
* a way to limit logic updates compared to renders.
*
* @param interval The minimum interval between logic updates
*/
public void setMinimumLogicUpdateInterval(int interval) {
minimumLogicInterval = interval;
}
/**
* Set the maximum amount of time in milliseconds that can passed
* into the update method. Useful for collision detection without
* sweeping.
*
* @param interval The maximum interval between logic updates
*/
public void setMaximumLogicUpdateInterval(int interval) {
maximumLogicInterval = interval;
}
/**
* Update and render the game
*
* @param delta The change in time since last update and render
* @throws SlickException Indicates an internal fault to the game.
*/
protected void updateAndRender(int delta) throws SlickException {
if (smoothDeltas) {
if (getFPS() != 0) {
delta = 1000 / getFPS();
}
}
input.poll(width, height);
Music.poll(delta);
if (!paused) {
storedDelta += delta;
if (storedDelta >= minimumLogicInterval) {
try {
if (maximumLogicInterval != 0) {
long cycles = storedDelta / maximumLogicInterval;
for (int i=0;i<cycles;i++) {
game.update(this, (int) maximumLogicInterval);
}
int remainder = (int) (storedDelta % maximumLogicInterval);
if (remainder > minimumLogicInterval) {
game.update(this, (int) (remainder % maximumLogicInterval));
storedDelta = 0;
} else {
storedDelta = remainder;
}
} else {
game.update(this, (int) storedDelta);
storedDelta = 0;
}
} catch (Throwable e) {
// Log.error(e);
throw new SlickException("Game.update() failure.", e);
}
}
} else {
game.update(this, 0);
}
if (hasFocus() || getAlwaysRender()) {
if (clearEachFrame) {
GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT);
}
GL.glLoadIdentity();
graphics.resetTransform();
graphics.resetFont();
graphics.resetLineWidth();
graphics.setAntiAlias(false);
try {
game.render(this, graphics);
} catch (Throwable e) {
// Log.error(e);
throw new SlickException("Game.render() failure.", e);
}
graphics.resetTransform();
if (showFPS) {
defaultFont.drawString(10, 10, "FPS: "+recordedFPS);
}
GL.flush();
}
if (targetFPS != -1) {
Display.sync(targetFPS);
}
}
/**
* Indicate if the display should update only when the game is visible
* (the default is true)
*
* @param updateOnlyWhenVisible True if we should updated only when the display is visible
*/
public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) {
}
/**
* Check if this game is only updating when visible to the user (default = true)
*
* @return True if the game is only updated when the display is visible
*/
public boolean isUpdatingOnlyWhenVisible() {
return true;
}
/**
* Initialise the GL context
*/
protected void initGL() {
Log.info("Starting display "+width+"x"+height);
GL.initDisplay(width, height);
if (input == null) {
input = new Input(height);
}
input.init(height);
// no need to remove listeners?
//input.removeAllListeners();
if (game instanceof InputListener) {
input.removeListener((InputListener) game);
input.addListener((InputListener) game);
}
if (graphics != null) {
graphics.setDimensions(getWidth(), getHeight());
}
lastGame = game;
}
/**
* Initialise the system components, OpenGL and OpenAL.
*
* @throws SlickException Indicates a failure to create a native handler
*/
protected void initSystem() throws SlickException {
initGL();
setMusicVolume(1.0f);
setSoundVolume(1.0f);
graphics = new Graphics(width, height);
defaultFont = graphics.getFont();
}
/**
* Enter the orthographic mode
*/
protected void enterOrtho() {
enterOrtho(width, height);
}
/**
* Indicate whether the container should show the FPS
*
* @param show True if the container should show the FPS
*/
public void setShowFPS(boolean show) {
showFPS = show;
}
/**
* Check if the FPS is currently showing
*
* @return True if the FPS is showing
*/
public boolean isShowingFPS() {
return showFPS;
}
/**
* Set the target fps we're hoping to get
*
* @param fps The target fps we're hoping to get
*/
public void setTargetFrameRate(int fps) {
targetFPS = fps;
}
/**
* Indicate whether the display should be synced to the
* vertical refresh (stops tearing)
*
* @param vsync True if we want to sync to vertical refresh
*/
public void setVSync(boolean vsync) {
this.vsync = vsync;
Display.setVSyncEnabled(vsync);
}
/**
* True if vsync is requested
*
* @return True if vsync is requested
*/
public boolean isVSyncRequested() {
return vsync;
}
/**
* True if the game is running
*
* @return True if the game is running
*/
protected boolean running() {
return running;
}
/**
* Inidcate we want verbose logging
*
* @param verbose True if we want verbose logging (INFO and DEBUG)
*/
public void setVerbose(boolean verbose) {
Log.setVerbose(verbose);
}
/**
* Cause the game to exit and shutdown cleanly
*/
public void exit() {
running = false;
}
/**
* Check if the game currently has focus
*
* @return True if the game currently has focus
*/
public abstract boolean hasFocus();
/**
* Get the graphics context used by this container. Note that this
* value may vary over the life time of the game.
*
* @return The graphics context used by this container
*/
public Graphics getGraphics() {
return graphics;
}
/**
* Enter the orthographic mode
*
* @param xsize The size of the panel being used
* @param ysize The size of the panel being used
*/
protected void enterOrtho(int xsize, int ysize) {
GL.enterOrtho(xsize, ysize);
}
}

View File

@ -34,232 +34,70 @@ import org.newdawn.slick.Font;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import org.newdawn.slick.geom.Rectangle; import org.newdawn.slick.geom.Rectangle;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.components.ActionListener;
import yugecin.opsudance.core.components.Component;
/** /**
* A single text field supporting text entry * A single text field supporting text entry
* *
* @author kevin * @author kevin
*/ */
@SuppressWarnings("unused") public class TextField extends Component {
public class TextField extends AbstractComponent {
/** The key repeat interval */
private static final int INITIAL_KEY_REPEAT_INTERVAL = 400; private static final int INITIAL_KEY_REPEAT_INTERVAL = 400;
/** The key repeat interval */
private static final int KEY_REPEAT_INTERVAL = 50; private static final int KEY_REPEAT_INTERVAL = 50;
/** The width of the field */ private final DisplayContainer displayContainer;
private int width;
/** The height of the field */ private String value = "";
private int height; private Font font;
/** The location in the X coordinate */
protected int x;
/** The location in the Y coordinate */
protected int y;
/** The maximum number of characters allowed to be input */
private int maxCharacter = 10000; private int maxCharacter = 10000;
/** The value stored in the text field */ private Color borderCol = Color.white;
private String value = ""; private Color textCol = Color.white;
private Color backgroundCol = new Color(0, 0, 0, 0.5f);
/** The font used to render text in the field */
private Font font;
/** The border color - null if no border */
private Color border = Color.white;
/** The text color */
private Color text = Color.white;
/** The background color - null if no background */
private Color background = new Color(0, 0, 0, 0.5f);
/** The current cursor position */
private int cursorPos; private int cursorPos;
/** True if the cursor should be visible */
private boolean visibleCursor = true;
/** The last key pressed */
private int lastKey = -1; private int lastKey = -1;
/** The last character pressed */
private char lastChar = 0; private char lastChar = 0;
/** The time since last key repeat */
private long repeatTimer; private long repeatTimer;
/** The text before the paste in */ private ActionListener listener;
private String oldText;
/** The cursor position before the paste */
private int oldCursorPos;
/** True if events should be consumed by the field */
private boolean consume = true;
/**
* Create a new text field
*
* @param container
* The container rendering this field
* @param font
* The font to use in the text field
* @param x
* The x coordinate of the top left corner of the text field
* @param y
* The y coordinate of the top left corner of the text field
* @param width
* The width of the text field
* @param height
* The height of the text field
* @param listener
* The listener to add to the text field
*/
public TextField(GUIContext container, Font font, int x, int y, int width,
int height, ComponentListener listener) {
this(container,font,x,y,width,height);
addListener(listener);
}
/**
* Create a new text field
*
* @param container
* The container rendering this field
* @param font
* The font to use in the text field
* @param x
* The x coordinate of the top left corner of the text field
* @param y
* The y coordinate of the top left corner of the text field
* @param width
* The width of the text field
* @param height
* The height of the text field
*/
public TextField(GUIContext container, Font font, int x, int y, int width,
int height) {
super(container);
public TextField(DisplayContainer displayContainer, Font font, int x, int y, int width, int height) {
this.displayContainer = displayContainer;
this.font = font; this.font = font;
this.x = x;
setLocation(x, y); this.y = y;
this.width = width; this.width = width;
this.height = height; this.height = height;
} }
/** public void setListener(ActionListener listener) {
* Indicate if the input events should be consumed by this field this.listener = listener;
*
* @param consume True if events should be consumed by this field
*/
public void setConsumeEvents(boolean consume) {
this.consume = consume;
} }
/** public void setBorderColor(Color border) {
* Deactivate the key input handling for this field this.borderCol = border;
*/ }
public void deactivate() {
setFocus(false); public void setTextColor(Color text) {
this.textCol = text;
}
public void setBackgroundColor(Color background) {
this.backgroundCol = background;
} }
/**
* Moves the component.
*
* @param x
* X coordinate
* @param y
* Y coordinate
*/
@Override @Override
public void setLocation(int x, int y) { public boolean isFocusable() {
this.x = x; return true;
this.y = y;
} }
/** public void render(Graphics g) {
* Returns the position in the X coordinate
*
* @return x
*/
@Override
public int getX() {
return x;
}
/**
* Returns the position in the Y coordinate
*
* @return y
*/
@Override
public int getY() {
return y;
}
/**
* Get the width of the component
*
* @return The width of the component
*/
@Override
public int getWidth() {
return width;
}
/**
* Get the height of the component
*
* @return The height of the component
*/
@Override
public int getHeight() {
return height;
}
/**
* Set the background color. Set to null to disable the background
*
* @param color
* The color to use for the background
*/
public void setBackgroundColor(Color color) {
background = color;
}
/**
* Set the border color. Set to null to disable the border
*
* @param color
* The color to use for the border
*/
public void setBorderColor(Color color) {
border = color;
}
/**
* Set the text color.
*
* @param color
* The color to use for the text
*/
public void setTextColor(Color color) {
text = color;
}
/**
* @see org.newdawn.slick.gui.AbstractComponent#render(org.newdawn.slick.gui.GUIContext,
* org.newdawn.slick.Graphics)
*/
@Override
public void render(GUIContext container, Graphics g) {
if (lastKey != -1) { if (lastKey != -1) {
if (input.isKeyDown(lastKey)) { if (displayContainer.input.isKeyDown(lastKey)) {
if (repeatTimer < System.currentTimeMillis()) { if (repeatTimer < System.currentTimeMillis()) {
repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL; repeatTimer = System.currentTimeMillis() + KEY_REPEAT_INTERVAL;
keyPressed(lastKey, lastChar); keyPressed(lastKey, lastChar);
@ -274,11 +112,11 @@ public class TextField extends AbstractComponent {
// Someone could have set a color for me to blend... // Someone could have set a color for me to blend...
Color clr = g.getColor(); Color clr = g.getColor();
if (background != null) { if (backgroundCol != null) {
g.setColor(background.multiply(clr)); g.setColor(backgroundCol.multiply(clr));
g.fillRect(x, y, width, height); g.fillRect(x, y, width, height);
} }
g.setColor(text.multiply(clr)); g.setColor(textCol.multiply(clr));
Font temp = g.getFont(); Font temp = g.getFont();
int cpos = font.getWidth(value.substring(0, cursorPos)); int cpos = font.getWidth(value.substring(0, cursorPos));
@ -291,14 +129,14 @@ public class TextField extends AbstractComponent {
g.setFont(font); g.setFont(font);
g.drawString(value, x + 1, y + 1); g.drawString(value, x + 1, y + 1);
if (hasFocus() && visibleCursor) { if (focused) {
g.drawString("_", x + 1 + cpos + 2, y + 1); g.drawString("|", x + 1 + cpos + 2, y + 1);
} }
g.translate(-tx - 2, 0); g.translate(-tx - 2, 0);
if (border != null) { if (borderCol != null) {
g.setColor(border.multiply(clr)); g.setColor(borderCol.multiply(clr));
g.drawRect(x, y, width, height); g.drawRect(x, y, width, height);
} }
g.setColor(clr); g.setColor(clr);
@ -307,21 +145,10 @@ public class TextField extends AbstractComponent {
g.setClip(oldClip); g.setClip(oldClip);
} }
/**
* Get the value in the text field
*
* @return The value in the text field
*/
public String getText() { public String getText() {
return value; return value;
} }
/**
* Set the value to be displayed in the text field
*
* @param value
* The value to be displayed in the text field
*/
public void setText(String value) { public void setText(String value) {
this.value = value; this.value = value;
if (cursorPos > value.length()) { if (cursorPos > value.length()) {
@ -329,35 +156,6 @@ public class TextField extends AbstractComponent {
} }
} }
/**
* Set the position of the cursor
*
* @param pos
* The new position of the cursor
*/
public void setCursorPos(int pos) {
cursorPos = pos;
if (cursorPos > value.length()) {
cursorPos = value.length();
}
}
/**
* Indicate whether the mouse cursor should be visible or not
*
* @param visibleCursor
* True if the mouse cursor should be visible
*/
public void setCursorVisible(boolean visibleCursor) {
this.visibleCursor = visibleCursor;
}
/**
* Set the length of the allowed input
*
* @param length
* The length of the allowed input
*/
public void setMaxLength(int length) { public void setMaxLength(int length) {
maxCharacter = length; maxCharacter = length;
if (value.length() > maxCharacter) { if (value.length() > maxCharacter) {
@ -365,71 +163,23 @@ public class TextField extends AbstractComponent {
} }
} }
/**
* Do the paste into the field, overrideable for custom behaviour
*
* @param text The text to be pasted in
*/
protected void doPaste(String text) { protected void doPaste(String text) {
recordOldPosition();
for (int i=0;i<text.length();i++) { for (int i=0;i<text.length();i++) {
keyPressed(-1, text.charAt(i)); keyPressed(-1, text.charAt(i));
} }
} }
/**
* Record the old position and content
*/
protected void recordOldPosition() {
oldText = getText();
oldCursorPos = cursorPos;
}
/**
* Do the undo of the paste, overrideable for custom behaviour
*
* @param oldCursorPos before the paste
* @param oldText The text before the last paste
*/
protected void doUndo(int oldCursorPos, String oldText) {
if (oldText != null) {
setText(oldText);
setCursorPos(oldCursorPos);
}
}
/**
* @see org.newdawn.slick.gui.AbstractComponent#keyPressed(int, char)
*/
@Override
public void keyPressed(int key, char c) { public void keyPressed(int key, char c) {
if (hasFocus()) {
if (key != -1) if (key != -1)
{ {
if ((key == Input.KEY_V) && if ((key == Input.KEY_V) &&
((input.isKeyDown(Input.KEY_LCONTROL)) || (input.isKeyDown(Input.KEY_RCONTROL)))) { ((displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) || (displayContainer.input.isKeyDown(Input.KEY_RCONTROL)))) {
String text = Sys.getClipboard(); String text = Sys.getClipboard();
if (text != null) { if (text != null) {
doPaste(text); doPaste(text);
} }
return; return;
} }
/* if ((key == Input.KEY_Z) &&
((input.isKeyDown(Input.KEY_LCONTROL)) || (input.isKeyDown(Input.KEY_RCONTROL)))) {
if (oldText != null) {
doUndo(oldCursorPos, oldText);
}
return;
} */
// alt and control keys don't come through here
/* if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) {
return;
} */
if (input.isKeyDown(Input.KEY_LALT) || input.isKeyDown(Input.KEY_RALT)) {
return;
}
} }
if (lastKey != key) { if (lastKey != key) {
@ -458,7 +208,7 @@ public class TextField extends AbstractComponent {
} }
*/ } else if (key == Input.KEY_BACK) { */ } else if (key == Input.KEY_BACK) {
if ((cursorPos > 0) && (value.length() > 0)) { if ((cursorPos > 0) && (value.length() > 0)) {
if (input.isKeyDown(Input.KEY_LCONTROL) || input.isKeyDown(Input.KEY_RCONTROL)) { if (displayContainer.input.isKeyDown(Input.KEY_LCONTROL) || displayContainer.input.isKeyDown(Input.KEY_RCONTROL)) {
int sp = 0; int sp = 0;
boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1)); boolean startSpace = Character.isWhitespace(value.charAt(cursorPos - 1));
boolean charSeen = false; boolean charSeen = false;
@ -490,18 +240,10 @@ public class TextField extends AbstractComponent {
cursorPos--; cursorPos--;
} }
} }
// Nobody more will be notified
if (consume) {
container.getInput().consumeEvent();
}
} else if (key == Input.KEY_DELETE) { } else if (key == Input.KEY_DELETE) {
if (value.length() > cursorPos) { if (value.length() > cursorPos) {
value = value.substring(0,cursorPos) + value.substring(cursorPos+1); value = value.substring(0,cursorPos) + value.substring(cursorPos+1);
} }
// Nobody more will be notified
if (consume) {
container.getInput().consumeEvent();
}
} else if ((c < 127) && (c > 31) && (value.length() < maxCharacter)) { } else if ((c < 127) && (c > 31) && (value.length() < maxCharacter)) {
if (cursorPos < value.length()) { if (cursorPos < value.length()) {
value = value.substring(0, cursorPos) + c value = value.substring(0, cursorPos) + c
@ -510,28 +252,12 @@ public class TextField extends AbstractComponent {
value = value.substring(0, cursorPos) + c; value = value.substring(0, cursorPos) + c;
} }
cursorPos++; cursorPos++;
// Nobody more will be notified
if (consume) {
container.getInput().consumeEvent();
}
} else if (key == Input.KEY_RETURN) { } else if (key == Input.KEY_RETURN) {
notifyListeners(); if (listener != null) {
// Nobody more will be notified listener.onAction();
if (consume) {
container.getInput().consumeEvent();
} }
} }
} }
}
/**
* @see org.newdawn.slick.gui.AbstractComponent#setFocus(boolean)
*/
@Override
public void setFocus(boolean focus) {
lastKey = -1;
super.setFocus(focus);
}
} }

View File

@ -19,9 +19,11 @@ package yugecin.opsudance;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.db.DBController; import itdelatrisu.opsu.db.DBController;
import itdelatrisu.opsu.downloads.DownloadList; import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater; import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.states.Splash;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
@ -61,23 +63,32 @@ public class OpsuDance {
initUpdater(args); initUpdater(args);
sout("database & updater initialized"); sout("database & updater initialized");
container.init(EmptyState.class); //container.init(EmptyState.class);
container.init(Splash.class);
} catch (Exception e) { } catch (Exception e) {
errorAndExit("startup failure", e); errorAndExit("startup failure", e);
} }
while (rungame()); while (rungame());
container.teardownAL();
Options.saveOptions();
closeSingleInstanceSocket(); closeSingleInstanceSocket();
DBController.closeConnections(); DBController.closeConnections();
DownloadList.get().cancelAllDownloads(); DownloadList.get().cancelAllDownloads();
Utils.deleteDirectory(Options.TEMP_DIR);
if (!Options.isWatchServiceEnabled()) {
BeatmapWatchService.destroy();
}
} }
private boolean rungame() { private boolean rungame() {
try { try {
container.setup(); container.setup();
container.resume();
} catch (Exception e) { } catch (Exception e) {
errorAndExit("could not initialize GL", e); ErrorHandler.error("could not initialize GL", e).allowTerminate().preventContinue().show();
return false;
} }
Exception caughtException = null; Exception caughtException = null;
try { try {
@ -86,7 +97,8 @@ public class OpsuDance {
caughtException = e; caughtException = e;
} }
container.teardown(); container.teardown();
return caughtException != null && ErrorHandler.error("update/render error", caughtException).show().shouldIgnoreAndContinue(); container.pause();
return caughtException != null && ErrorHandler.error("update/render error", caughtException).allowTerminate().show().shouldIgnoreAndContinue();
} }
private void initDatabase() { private void initDatabase() {
@ -160,7 +172,7 @@ public class OpsuDance {
} }
private void errorAndExit(String errstr) { private void errorAndExit(String errstr) {
ErrorHandler.error(errstr, new Throwable()).preventContinue().show(); ErrorHandler.error(errstr, new Throwable()).allowTerminate().preventContinue().show();
System.exit(1); System.exit(1);
} }

View File

@ -17,18 +17,23 @@
*/ */
package yugecin.opsudance.core; package yugecin.opsudance.core;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import org.lwjgl.LWJGLException; import itdelatrisu.opsu.ui.UI;
import org.lwjgl.Sys; import org.lwjgl.Sys;
import org.lwjgl.openal.AL; import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Graphics; import org.newdawn.slick.*;
import org.newdawn.slick.Input;
import org.newdawn.slick.KeyListener;
import org.newdawn.slick.MouseListener;
import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL; import org.newdawn.slick.opengl.renderer.SGL;
@ -41,6 +46,7 @@ import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState; import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState; import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.core.state.transitions.*; import yugecin.opsudance.core.state.transitions.*;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.events.ResolutionChangedEvent;
import yugecin.opsudance.utils.GLHelper; import yugecin.opsudance.utils.GLHelper;
@ -70,10 +76,10 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
private OpsuState state; private OpsuState state;
private final DisplayMode nativeDisplayMode; public final DisplayMode nativeDisplayMode;
private Graphics graphics; private Graphics graphics;
private Input input; public Input input;
public int width; public int width;
public int height; public int height;
@ -90,16 +96,27 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public int renderDelta; public int renderDelta;
public int delta; public int delta;
public boolean exitRequested;
public int timeSinceLastRender; public int timeSinceLastRender;
private long lastFrame; private long lastFrame;
private boolean wasMusicPlaying;
private String glVersion; private String glVersion;
private String glVendor; private String glVendor;
private long exitconfirmation;
public final Cursor cursor;
public boolean drawCursor;
public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) { public DisplayContainer(InstanceContainer instanceContainer, EventBus eventBus) {
this.instanceContainer = instanceContainer; this.instanceContainer = instanceContainer;
this.eventBus = eventBus; this.eventBus = eventBus;
this.cursor = new Cursor();
drawCursor = true;
outTransitionListener = new TransitionFinishedListener() { outTransitionListener = new TransitionFinishedListener() {
@Override @Override
@ -149,16 +166,21 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
} }
public void run() throws LWJGLException { public void run() throws Exception {
while(!(Display.isCloseRequested() && state.onCloseRequest())) { while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) {
delta = getDelta(); delta = getDelta();
timeSinceLastRender += delta; timeSinceLastRender += delta;
input.poll(width, height); input.poll(width, height);
Music.poll(delta);
mouseX = input.getMouseX(); mouseX = input.getMouseX();
mouseY = input.getMouseY(); mouseY = input.getMouseY();
state.update(); state.update();
if (drawCursor) {
cursor.setCursorPosition(delta, mouseX, mouseY);
}
int maxRenderInterval; int maxRenderInterval;
if (Display.isVisible() && Display.isActive()) { if (Display.isVisible() && Display.isActive()) {
@ -185,6 +207,12 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
bubNotifState.render(graphics); bubNotifState.render(graphics);
barNotifState.render(graphics); barNotifState.render(graphics);
cursor.updateAngle(renderDelta);
if (drawCursor) {
cursor.draw(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
}
UI.drawTooltip(graphics);
timeSinceLastRender = 0; timeSinceLastRender = 0;
Display.update(false); Display.update(false);
@ -193,28 +221,66 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
Display.processMessages(); Display.processMessages();
Display.sync(targetUpdatesPerSecond); Display.sync(targetUpdatesPerSecond);
} }
teardown();
} }
public void setup() throws Exception { public void setup() throws Exception {
width = height = -1; width = height = -1;
Input.disableControllers(); Input.disableControllers();
Display.setTitle("opsu!dance"); Display.setTitle("opsu!dance");
// temp displaymode to not flash the screen with a 1ms black window Options.setDisplayMode(this);
Display.setDisplayMode(new DisplayMode(100, 100));
Display.create(); Display.create();
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" }); GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
setDisplayMode(800, 600, false); initGL();
sout("GL ready");
glVersion = GL11.glGetString(GL11.GL_VERSION); glVersion = GL11.glGetString(GL11.GL_VERSION);
glVendor = GL11.glGetString(GL11.GL_VENDOR); glVendor = GL11.glGetString(GL11.GL_VENDOR);
GLHelper.hideNativeCursor();
} }
public void teardown() { public void teardown() {
InternalTextureLoader.get().clear();
GameImage.destroyImages();
GameData.Grade.destroyImages();
Beatmap.destroyBackgroundImageCache();
CurveRenderState.shutdown();
Display.destroy(); Display.destroy();
}
public void teardownAL() {
AL.destroy(); AL.destroy();
} }
public void pause() {
wasMusicPlaying = MusicController.isPlaying();
if (wasMusicPlaying) {
MusicController.pause();
}
}
public void resume() {
if (wasMusicPlaying) {
MusicController.resume();
}
}
private boolean confirmExit() {
if (System.currentTimeMillis() - exitconfirmation < 10000) {
return true;
}
if (DownloadList.get().hasActiveDownloads()) {
eventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
}
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
eventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
}
return true;
}
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception { public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
if (this.width == width && this.height == height) { if (this.width == width && this.height == height) {
Display.setFullscreen(fullscreen); Display.setFullscreen(fullscreen);
@ -231,6 +297,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
if (fullscreen) { if (fullscreen) {
fullscreen = false; fullscreen = false;
Log.warn("could not find fullscreen displaymode for " + width + "x" + height); Log.warn("could not find fullscreen displaymode for " + width + "x" + height);
eventBus.post(new BubbleNotificationEvent("Fullscreen mode is not supported for " + width + "x" + height, BubbleNotificationEvent.COLOR_ORANGE));
} }
} }
@ -240,9 +307,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
Display.setDisplayMode(displayMode); Display.setDisplayMode(displayMode);
Display.setFullscreen(fullscreen); Display.setFullscreen(fullscreen);
if (Display.isCreated()) {
initGL(); initGL();
}
eventBus.post(new ResolutionChangedEvent(this.width, this.height));
if (displayMode.getBitsPerPixel() == 16) { if (displayMode.getBitsPerPixel() == 16) {
InternalTextureLoader.get().set16BitMode(); InternalTextureLoader.get().set16BitMode();
@ -257,11 +324,20 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
graphics.setAntiAlias(false); graphics.setAntiAlias(false);
input = new Input(height); input = new Input(height);
input.enableKeyRepeat();
input.addKeyListener(this); input.addKeyListener(this);
input.addMouseListener(this); input.addMouseListener(this);
sout("GL ready");
GameImage.init(width, height); GameImage.init(width, height);
Fonts.init(); Fonts.init();
eventBus.post(new ResolutionChangedEvent(this.width, this.height));
}
public void resetCursor() {
cursor.reset(mouseX, mouseY);
} }
private int getDelta() { private int getDelta() {
@ -292,6 +368,10 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
state.writeErrorDump(dump); state.writeErrorDump(dump);
} }
public boolean isInState(Class<? extends OpsuState> state) {
return state.isInstance(state);
}
public boolean isTransitioning() { public boolean isTransitioning() {
return state instanceof TransitionState; return state instanceof TransitionState;
} }
@ -301,7 +381,13 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
} }
public void switchStateNow(Class<? extends OpsuState> newState) { public void switchStateNow(Class<? extends OpsuState> newState) {
switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0); switchState(newState, EmptyTransitionState.class, 0, FadeInTransitionState.class, 300);
}
public void switchStateInstantly(Class<? extends OpsuState> newState) {
state.leave();
state = instanceContainer.provide(newState);
state.enter();
} }
public void switchState(Class<? extends OpsuState> newState, Class<? extends TransitionState> outTransition, int outTime, Class<? extends TransitionState> inTransition, int inTime) { public void switchState(Class<? extends OpsuState> newState, Class<? extends TransitionState> outTransition, int outTime, Class<? extends TransitionState> inTransition, int inTime) {
@ -353,7 +439,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public void mouseMoved(int oldx, int oldy, int newx, int newy) { } public void mouseMoved(int oldx, int oldy, int newx, int newy) { }
@Override @Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) { } public void mouseDragged(int oldx, int oldy, int newx, int newy) {
state.mouseDragged(oldx, oldy, newx, newy);
}
@Override @Override
public void setInput(Input input) { } public void setInput(Input input) { }

View File

@ -17,6 +17,7 @@
*/ */
package yugecin.opsudance.core; package yugecin.opsudance.core;
import itdelatrisu.opsu.downloads.Updater;
import yugecin.opsudance.OpsuDance; import yugecin.opsudance.OpsuDance;
import yugecin.opsudance.core.inject.OpsuDanceInjector; import yugecin.opsudance.core.inject.OpsuDanceInjector;
@ -27,6 +28,10 @@ public class Entrypoint {
public static void main(String[] args) { public static void main(String[] args) {
sout("launched"); sout("launched");
(new OpsuDanceInjector()).provide(OpsuDance.class).start(args); (new OpsuDanceInjector()).provide(OpsuDance.class).start(args);
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) {
Updater.get().runUpdate();
}
} }
public static long runtime() { public static long runtime() {

View File

@ -0,0 +1,24 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.components;
public interface ActionListener {
void onAction();
}

View File

@ -0,0 +1,60 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.components;
import org.newdawn.slick.Graphics;
public abstract class Component {
public int width;
public int height;
public int x;
public int y;
protected boolean focused;
protected boolean hovered;
public abstract boolean isFocusable();
public boolean isHovered() {
return hovered;
}
public void updateHover(int x, int y) {
this.hovered = this.x <= x && x <= this.x + width && this.y <= y && y <= this.y + height;
}
public void mouseReleased(int button) {
}
public void preRenderUpdate() {
}
public abstract void render(Graphics g);
public void keyPressed(int key, char c) {
}
public void keyReleased(int key, char c) {
}
public void setFocused(boolean focused) {
this.focused = focused;
}
}

View File

@ -52,6 +52,7 @@ public class ErrorHandler {
private boolean preventContinue; private boolean preventContinue;
private boolean preventReport; private boolean preventReport;
private boolean ignoreAndContinue; private boolean ignoreAndContinue;
private boolean allowTerminate;
public ErrorHandler(DisplayContainer displayContainer) { public ErrorHandler(DisplayContainer displayContainer) {
this.displayContainer = displayContainer; this.displayContainer = displayContainer;
@ -95,6 +96,11 @@ public class ErrorHandler {
return this; return this;
} }
public ErrorHandler allowTerminate() {
allowTerminate = true;
return this;
}
public ErrorHandler preventContinue() { public ErrorHandler preventContinue() {
preventContinue = true; preventContinue = true;
return this; return this;
@ -127,7 +133,9 @@ public class ErrorHandler {
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() }; Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() };
String[] buttons; String[] buttons;
if (preventContinue) { if (!allowTerminate && !preventContinue) {
buttons = new String[] { "Ignore & continue" };
} else if (preventContinue) {
buttons = new String[] { "Terminate" }; buttons = new String[] { "Terminate" };
} else { } else {
buttons = new String[] { "Terminate", "Ignore & continue" }; buttons = new String[] { "Terminate", "Ignore & continue" };
@ -145,7 +153,7 @@ public class ErrorHandler {
null, null,
buttons, buttons,
buttons[buttons.length - 1]); buttons[buttons.length - 1]);
ignoreAndContinue = result == 1; ignoreAndContinue = !allowTerminate || result == 1;
frame.dispose(); frame.dispose();
return this; return this;

View File

@ -22,10 +22,14 @@ import java.util.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class EventBus { public class EventBus {
@Deprecated
public static EventBus instance; // TODO get rid of this
private final List<Subscriber> subscribers; private final List<Subscriber> subscribers;
public EventBus() { public EventBus() {
subscribers = new LinkedList<>(); subscribers = new LinkedList<>();
instance = this;
} }
public <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) { public <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) {

View File

@ -17,6 +17,7 @@
*/ */
package yugecin.opsudance.core.inject; package yugecin.opsudance.core.inject;
import itdelatrisu.opsu.states.*;
import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
@ -50,6 +51,15 @@ public class OpsuDanceInjector extends Injector {
bind(EmptyRedState.class).asEagerSingleton(); bind(EmptyRedState.class).asEagerSingleton();
bind(EmptyState.class).asEagerSingleton(); bind(EmptyState.class).asEagerSingleton();
bind(Splash.class).asEagerSingleton();
bind(MainMenu.class).asEagerSingleton();
bind(ButtonMenu.class).asEagerSingleton();
bind(SongMenu.class).asEagerSingleton();
bind(DownloadsMenu.class).asEagerSingleton();
bind(Game.class).asEagerSingleton();
bind(GameRanking.class).asEagerSingleton();
bind(GamePauseMenu.class).asEagerSingleton();
} }
} }

View File

@ -17,6 +17,10 @@
*/ */
package yugecin.opsudance.core.state; package yugecin.opsudance.core.state;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventListener; import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.ResolutionChangedEvent; import yugecin.opsudance.events.ResolutionChangedEvent;
@ -41,6 +45,18 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
protected void revalidate() { protected void revalidate() {
} }
@Override
public void update() {
}
@Override
public void preRenderUpdate() {
}
@Override
public void render(Graphics g) {
}
@Override @Override
public void onEvent(ResolutionChangedEvent event) { public void onEvent(ResolutionChangedEvent event) {
if (isCurrentState) { if (isCurrentState) {
@ -76,6 +92,18 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
@Override @Override
public boolean keyReleased(int key, char c) { public boolean keyReleased(int key, char c) {
if (key == Input.KEY_F7) {
Options.setNextFPS(displayContainer);
return true;
}
if (key == Input.KEY_F10) {
Options.toggleMouseDisabled();
return true;
}
if (key == Input.KEY_F12) {
Utils.takeScreenShot();
return true;
}
return false; return false;
} }
@ -94,6 +122,11 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
return false; return false;
} }
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
@Override @Override
public void writeErrorDump(StringWriter dump) { public void writeErrorDump(StringWriter dump) {
dump.append("> BaseOpsuState dump\n"); dump.append("> BaseOpsuState dump\n");

View File

@ -0,0 +1,194 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.components.Component;
import java.util.LinkedList;
public abstract class ComplexOpsuState extends BaseOpsuState {
protected final LinkedList<Component> components;
protected final LinkedList<OverlayOpsuState> overlays;
private Component focusedComponent;
public ComplexOpsuState(DisplayContainer displayContainer) {
super(displayContainer);
this.components = new LinkedList<>();
this.overlays = new LinkedList<>();
}
public final void focusComponent(Component component) {
if (!component.isFocusable()) {
return;
}
if (focusedComponent != null) {
focusedComponent.setFocused(false);
}
focusedComponent = component;
component.setFocused(true);
}
public boolean isAnyComponentFocused() {
return focusedComponent != null || isAnyOverlayActive();
}
public boolean isAnyOverlayActive() {
for (OverlayOpsuState overlay : overlays) {
if (overlay.active) {
return true;
}
}
return false;
}
@Override
public boolean mouseWheelMoved(int delta) {
for (OverlayOpsuState overlay : overlays) {
if (overlay.mouseWheelMoved(delta)) {
return true;
}
}
return false;
}
@Override
public boolean mousePressed(int button, int x, int y) {
for (OverlayOpsuState overlay : overlays) {
if (overlay.mousePressed(button, x, y)) {
return true;
}
}
return false;
}
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
for (OverlayOpsuState overlay : overlays) {
if (overlay.mouseDragged(oldx, oldy, newx, newy)) {
return true;
}
}
return false;
}
@Override
public boolean mouseReleased(int button, int x, int y) {
for (OverlayOpsuState overlay : overlays) {
if (overlay.mouseReleased(button, x, y)) {
return true;
}
}
if (focusedComponent == null) {
for (Component component : components) {
if (!component.isFocusable()) {
continue;
}
component.updateHover(x, y);
if (component.isHovered()) {
focusedComponent = component;
focusedComponent.setFocused(true);
return true;
}
}
return false;
}
focusedComponent.updateHover(x, y);
if (focusedComponent.isHovered()) {
focusedComponent.mouseReleased(button);
return true;
}
focusedComponent.setFocused(false);
focusedComponent = null;
return true;
}
@Override
public void preRenderUpdate() {
super.preRenderUpdate();
for (Component component : components) {
component.updateHover(displayContainer.mouseX, displayContainer.mouseY);
component.preRenderUpdate();
}
for (OverlayOpsuState overlay : overlays) {
overlay.preRenderUpdate();
}
}
@Override
protected void revalidate() {
super.revalidate();
for (OverlayOpsuState overlay : overlays) {
overlay.revalidate();
}
}
@Override
public void render(Graphics g) {
for (OverlayOpsuState overlay : overlays) {
overlay.render(g);
}
super.render(g);
}
@Override
public boolean keyReleased(int key, char c) {
if (super.keyReleased(key, c)) {
return true;
}
for (OverlayOpsuState overlay : overlays) {
if (overlay.keyReleased(key, c)) {
return true;
}
}
if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) {
focusedComponent.setFocused(false);
focusedComponent = null;
return true;
}
focusedComponent.keyReleased(key, c);
return true;
}
return false;
}
@Override
public boolean keyPressed(int key, char c) {
for (OverlayOpsuState overlay : overlays) {
if (overlay.keyPressed(key, c)) {
return true;
}
}
if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) {
focusedComponent.setFocused(false);
focusedComponent = null;
return true;
}
focusedComponent.keyPressed(key, c);
return true;
}
return false;
}
}

View File

@ -58,4 +58,9 @@ public interface OpsuState extends ErrorDumpable {
*/ */
boolean mouseReleased(int button, int x, int y); boolean mouseReleased(int button, int x, int y);
/**
* @return false to stop event bubbling
*/
boolean mouseDragged(int oldx, int oldy, int newx, int newy);
} }

View File

@ -0,0 +1,122 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics;
import java.io.StringWriter;
public abstract class OverlayOpsuState implements OpsuState {
protected boolean active;
protected boolean acceptInput;
public void hide() {
acceptInput = active = false;
}
public void show() {
acceptInput = active = true;
}
@Override
public final void update() {
}
public void revalidate() {
}
protected abstract void onPreRenderUpdate();
@Override
public final void preRenderUpdate() {
if (active) {
onPreRenderUpdate();
}
}
protected abstract void onRender(Graphics g);
@Override
public final void render(Graphics g) {
if (active) {
onRender(g);
}
}
@Override
public final void enter() {
}
@Override
public final void leave() {
}
@Override
public final boolean onCloseRequest() {
return true;
}
protected abstract boolean onKeyPressed(int key, char c);
@Override
public final boolean keyPressed(int key, char c) {
return acceptInput && onKeyPressed(key, c);
}
protected abstract boolean onKeyReleased(int key, char c);
@Override
public final boolean keyReleased(int key, char c) {
return acceptInput && onKeyReleased(key, c);
}
protected abstract boolean onMouseWheelMoved(int delta);
@Override
public final boolean mouseWheelMoved(int delta) {
return acceptInput && onMouseWheelMoved(delta);
}
protected abstract boolean onMousePressed(int button, int x, int y);
@Override
public final boolean mousePressed(int button, int x, int y) {
return acceptInput && onMousePressed(button, x, y);
}
protected abstract boolean onMouseReleased(int button, int x, int y);
@Override
public final boolean mouseReleased(int button, int x, int y) {
return acceptInput && onMouseReleased(button, x, y);
}
protected abstract boolean onMouseDragged(int oldx, int oldy, int newx, int newy);
@Override
public final boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return acceptInput && onMouseDragged(oldx, oldy, newx, newy);
}
@Override
public void writeErrorDump(StringWriter dump) {
dump.append("> OverlayOpsuState dump\n");
dump.append("accepts input: ").append(String.valueOf(acceptInput)).append(" is active: ").append(String.valueOf(active));
}
}

View File

@ -43,16 +43,16 @@ public class FpsRenderState implements EventListener<ResolutionChangedEvent> {
public void render(Graphics g) { public void render(Graphics g) {
int x = this.x; int x = this.x;
int target = displayContainer.targetRenderInterval - (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval); int target = displayContainer.targetRenderInterval + (displayContainer.targetUpdateInterval % displayContainer.targetRenderInterval);
x = drawText(g, getColor(target, displayContainer.renderDelta), (1000 / displayContainer.renderDelta) + " fps", x, this.y); x = drawText(g, getColor(target, displayContainer.renderDelta), (1000 / displayContainer.renderDelta) + " fps", x, this.y);
drawText(g, getColor(displayContainer.targetUpdateInterval, displayContainer.delta), (1000 / displayContainer.delta) + " ups", x, this.y); drawText(g, getColor(displayContainer.targetUpdateInterval, displayContainer.delta), (1000 / displayContainer.delta) + " ups", x, this.y);
} }
private Color getColor(int targetValue, int realValue) { private Color getColor(int targetValue, int realValue) {
if (realValue >= targetValue) { if (realValue <= targetValue) {
return GREEN; return GREEN;
} }
if (realValue >= targetValue * 0.9f) { if (realValue <= targetValue * 1.15f) {
return ORANGE; return ORANGE;
} }
return DARKORANGE; return DARKORANGE;

View File

@ -21,10 +21,11 @@ import org.newdawn.slick.Color;
public class BubbleNotificationEvent { public class BubbleNotificationEvent {
public static final Color COMMONCOLOR_RED = new Color(138, 72, 51);
public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59); public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59);
public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220); public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220);
public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149); public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149);
public static final Color COMMONCOLOR_RED = new Color(141, 49, 16);
public static final Color COLOR_ORANGE = new Color(138, 72, 51);
public final String message; public final String message;
public final Color borderColor; public final Color borderColor;

View File

@ -25,6 +25,8 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer; import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.sbv2.movers.CubicStoryboardMover; import yugecin.opsudance.sbv2.movers.CubicStoryboardMover;
import yugecin.opsudance.sbv2.movers.LinearStoryboardMover; import yugecin.opsudance.sbv2.movers.LinearStoryboardMover;
import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover; import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover;
@ -34,20 +36,20 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
public class MoveStoryboard { public class MoveStoryboard extends OverlayOpsuState{
private final SimpleButton btnAddLinear; private final DisplayContainer displayContainer;
private final SimpleButton btnAddQuadratic;
private final SimpleButton btnAddCubic;
private final SimpleButton btnAnimLin; private SimpleButton btnAddLinear;
private final SimpleButton btnAnimMid; private SimpleButton btnAddQuadratic;
private final SimpleButton btnAnimCub; private SimpleButton btnAddCubic;
private SimpleButton btnAnimLin;
private SimpleButton btnAnimMid;
private SimpleButton btnAnimCub;
private final StoryboardMove dummyMove; private final StoryboardMove dummyMove;
private int width;
private StoryboardMove[] moves; private StoryboardMove[] moves;
private GameObject[] gameObjects; private GameObject[] gameObjects;
@ -55,14 +57,8 @@ public class MoveStoryboard {
private int trackPosition; private int trackPosition;
public MoveStoryboard(GameContainer container) { public MoveStoryboard(DisplayContainer displayContainer) {
this.width = container.getWidth(); this.displayContainer = displayContainer;
btnAddLinear = new SimpleButton(width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAddQuadratic = new SimpleButton(width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAddCubic = new SimpleButton(width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAnimLin = new SimpleButton(width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange);
btnAnimMid = new SimpleButton(width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange);
btnAnimCub = new SimpleButton(width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange);
dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class<?>[]{StoryboardMove.class}, new InvocationHandler() { dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class<?>[]{StoryboardMove.class}, new InvocationHandler() {
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
@ -71,6 +67,16 @@ public class MoveStoryboard {
}); });
} }
@Override
public void revalidate() {
btnAddLinear = new SimpleButton(displayContainer.width - 205, 50, 200, 25, Fonts.SMALL, "add linear", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAddQuadratic = new SimpleButton(displayContainer.width - 205, 80, 200, 25, Fonts.SMALL, "add quadratic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAddCubic = new SimpleButton(displayContainer.width - 205, 110, 200, 25, Fonts.SMALL, "add cubic", Colors.BLUE_BUTTON, Colors.WHITE_FADE, Colors.WHITE_FADE, Colors.ORANGE_BUTTON);
btnAnimLin = new SimpleButton(displayContainer.width - 250, 50, 40, 25, Fonts.SMALL, "lin", Color.blue, Color.white, Color.white, Color.orange);
btnAnimMid = new SimpleButton(displayContainer.width - 250, 80, 40, 25, Fonts.SMALL, "mid", Color.blue, Color.white, Color.white, Color.orange);
btnAnimCub = new SimpleButton(displayContainer.width - 250, 110, 40, 25, Fonts.SMALL, "cub", Color.blue, Color.white, Color.white, Color.orange);
}
/** /**
* Get the point at the current time * Get the point at the current time
* @param trackPosition current time in ms * @param trackPosition current time in ms
@ -88,7 +94,33 @@ public class MoveStoryboard {
return moves[objectIndex].getPointAt(t); return moves[objectIndex].getPointAt(t);
} }
public void render(Graphics g) { @Override
public void hide() {
}
@Override
public void show() {
}
@Override
protected void onPreRenderUpdate() {
int x = displayContainer.mouseX;
int y = displayContainer.mouseY;
btnAddLinear.update(x, y);
btnAddQuadratic.update(x, y);
btnAddCubic.update(x, y);
btnAnimLin.update(x, y);
btnAnimMid.update(x, y);
btnAnimCub.update(x, y);
if (moves[objectIndex] != null) {
moves[objectIndex].update(displayContainer.renderDelta, x, y);
}
}
@Override
protected void onRender(Graphics g) {
btnAddLinear.render(g); btnAddLinear.render(g);
btnAddQuadratic.render(g); btnAddQuadratic.render(g);
btnAddCubic.render(g); btnAddCubic.render(g);
@ -100,13 +132,31 @@ public class MoveStoryboard {
} }
} }
public void mousePressed(int x, int y) { @Override
protected boolean onKeyPressed(int key, char c) {
return false;
}
@Override
protected boolean onKeyReleased(int key, char c) {
return false;
}
@Override
protected boolean onMouseWheelMoved(int delta) {
return false;
}
@Override
protected boolean onMousePressed(int button, int x, int y) {
if (moves[objectIndex] != null) { if (moves[objectIndex] != null) {
moves[objectIndex].mousePressed(x, y); moves[objectIndex].mousePressed(x, y);
} }
return true;
} }
public void mouseReleased(int x, int y) { @Override
protected boolean onMouseReleased(int button, int x, int y) {
if (moves[objectIndex] != null) { if (moves[objectIndex] != null) {
moves[objectIndex].mouseReleased(x, y); moves[objectIndex].mouseReleased(x, y);
if (moves[objectIndex].getAmountOfMovers() == 0) { if (moves[objectIndex].getAmountOfMovers() == 0) {
@ -114,7 +164,7 @@ public class MoveStoryboard {
} }
} }
if (objectIndex == 0) { if (objectIndex == 0) {
return; return true;
} }
if (btnAddLinear.isHovered()) { if (btnAddLinear.isHovered()) {
getCurrentMoveOrCreateNew().add(new LinearStoryboardMover()); getCurrentMoveOrCreateNew().add(new LinearStoryboardMover());
@ -134,6 +184,12 @@ public class MoveStoryboard {
if (btnAnimCub.isHovered()) { if (btnAnimCub.isHovered()) {
getCurrentMoveOrDummy().setAnimationEquation(AnimationEquation.IN_OUT_EASE_MIDDLE); getCurrentMoveOrDummy().setAnimationEquation(AnimationEquation.IN_OUT_EASE_MIDDLE);
} }
return true;
}
@Override
protected boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
} }
private StoryboardMove getCurrentMoveOrCreateNew() { private StoryboardMove getCurrentMoveOrCreateNew() {
@ -142,7 +198,7 @@ public class MoveStoryboard {
return dummyMove; return dummyMove;
} }
if (moves[objectIndex] == null) { if (moves[objectIndex] == null) {
return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, width); return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, displayContainer.width);
} }
return moves[objectIndex]; return moves[objectIndex];
} }
@ -154,18 +210,6 @@ public class MoveStoryboard {
return moves[objectIndex]; return moves[objectIndex];
} }
public void update(int delta, int x, int y) {
btnAddLinear.update(x, y);
btnAddQuadratic.update(x, y);
btnAddCubic.update(x, y);
btnAnimLin.update(x, y);
btnAnimMid.update(x, y);
btnAnimCub.update(x, y);
if (moves[objectIndex] != null) {
moves[objectIndex].update(delta, x, y);
}
}
public void setGameObjects(GameObject[] gameObjects) { public void setGameObjects(GameObject[] gameObjects) {
this.gameObjects = gameObjects; this.gameObjects = gameObjects;
this.moves = new StoryboardMove[gameObjects.length]; this.moves = new StoryboardMove[gameObjects.length];

View File

@ -100,6 +100,11 @@ public class EmptyRedState implements OpsuState {
return false; return false;
} }
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
@Override @Override
public void writeErrorDump(StringWriter dump) { public void writeErrorDump(StringWriter dump) {
dump.append("> EmptyRedState dump\n"); dump.append("> EmptyRedState dump\n");

View File

@ -92,6 +92,11 @@ public class EmptyState implements OpsuState {
return false; return false;
} }
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
@Override @Override
public void writeErrorDump(StringWriter dump) { public void writeErrorDump(StringWriter dump) {
dump.append("> EmptyState dump\n"); dump.append("> EmptyState dump\n");

View File

@ -29,16 +29,18 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.*; import org.newdawn.slick.*;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
@SuppressWarnings("UnusedParameters") public class OptionsOverlay extends OverlayOpsuState {
public class OptionsOverlay {
private Parent parent; private final DisplayContainer displayContainer;
private GameContainer container;
private final Image sliderBallImg; private Listener listener;
private final Image checkOnImg;
private final Image checkOffImg; private Image sliderBallImg;
private Image checkOnImg;
private Image checkOffImg;
private OptionTab[] tabs; private OptionTab[] tabs;
private int selectedTab; private int selectedTab;
@ -79,21 +81,29 @@ public class OptionsOverlay {
private int sliderSoundDelay; private int sliderSoundDelay;
public OptionsOverlay(Parent parent, OptionTab[] tabs, int defaultSelectedTabIndex, GameContainer container) { public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] tabs, int defaultSelectedTabIndex) {
this.parent = parent; this.displayContainer = displayContainer;
this.container = container;
this.tabs = tabs; this.tabs = tabs;
selectedTab = defaultSelectedTabIndex; selectedTab = defaultSelectedTabIndex;
listHoverIndex = -1; listHoverIndex = -1;
}
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
public void revalidate() {
super.revalidate();
sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(20, 20); sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(20, 20);
checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(20, 20); checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(20, 20);
checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(20, 20); checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(20, 20);
width = container.getWidth(); width = displayContainer.width;
height = container.getHeight(); height = displayContainer.height;
// calculate positions // calculate positions
optionWidth = width / 2; optionWidth = width / 2;
@ -109,10 +119,12 @@ public class OptionsOverlay {
maxScrollOffset = Fonts.MEDIUM.getLineHeight() * 2 * tabs.length; maxScrollOffset = Fonts.MEDIUM.getLineHeight() * 2 * tabs.length;
scrollOffset = 0; scrollOffset = 0;
for (OptionTab tab : tabs) { for (OptionTab tab : tabs) {
/*
if (defaultSelectedTabIndex-- > 0) { if (defaultSelectedTabIndex-- > 0) {
scrollOffset += Fonts.MEDIUM.getLineHeight() * 2; scrollOffset += Fonts.MEDIUM.getLineHeight() * 2;
scrollOffset += tab.options.length * optionHeight; scrollOffset += tab.options.length * optionHeight;
} }
*/
maxScrollOffset += tab.options.length * optionHeight; maxScrollOffset += tab.options.length * optionHeight;
tab.button = new MenuButton(tabImage, tabX, tabY); tab.button = new MenuButton(tabImage, tabX, tabY);
tabX += tabOffset; tabX += tabOffset;
@ -127,7 +139,8 @@ public class OptionsOverlay {
optionStartY = (int) (tabY + tabImage.getHeight() / 2 + 2); // +2 for the separator line optionStartY = (int) (tabY + tabImage.getHeight() / 2 + 2); // +2 for the separator line
} }
public void render(Graphics g, int mouseX, int mouseY) { @Override
public void onRender(Graphics g) {
// bg // bg
g.setColor(Colors.BLACK_ALPHA_75); g.setColor(Colors.BLACK_ALPHA_75);
g.fillRect(0, 0, width, height); g.fillRect(0, 0, width, height);
@ -136,7 +149,7 @@ public class OptionsOverlay {
renderTitle(); renderTitle();
// option tabs // option tabs
renderTabs(mouseX, mouseY); renderTabs();
// line separator // line separator
g.setColor(Color.white); g.setColor(Color.white);
@ -159,7 +172,7 @@ public class OptionsOverlay {
UI.getBackButton().draw(); UI.getBackButton().draw();
// tooltip // tooltip
renderTooltip(g, mouseX, mouseY); renderTooltip(g);
// key input options // key input options
if (keyEntryLeft || keyEntryRight) { if (keyEntryLeft || keyEntryRight) {
@ -175,15 +188,10 @@ public class OptionsOverlay {
Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(prompt)) / 2, (height - Fonts.LARGE.getLineHeight()) / 2, prompt); Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(prompt)) / 2, (height - Fonts.LARGE.getLineHeight()) / 2, prompt);
} }
private void renderTooltip(Graphics g, int mouseX, int mouseY) { private void renderTooltip(Graphics g) {
if (hoverOption != null) { if (hoverOption != null) {
String optionDescription = hoverOption.getDescription(); UI.updateTooltip(displayContainer.renderDelta, hoverOption.getDescription(), false);
float textWidth = Fonts.SMALL.getWidth(optionDescription); UI.drawTooltip(g);
Color.black.a = 0.7f;
g.setColor(Color.black);
g.fillRoundRect(mouseX + 10, mouseY + 10, 10 + textWidth, 10 + Fonts.SMALL.getLineHeight(), 4);
Fonts.SMALL.drawString(mouseX + 15, mouseY + 15, optionDescription, Color.white);
Color.black.a = 1f;
} }
} }
@ -322,10 +330,10 @@ public class OptionsOverlay {
Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND); Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y, value, Colors.BLUE_BACKGROUND);
} }
public void renderTabs(int mouseX, int mouseY) { public void renderTabs() {
for (int i = 0; i < tabs.length; i++) { for (int i = 0; i < tabs.length; i++) {
OptionTab tab = tabs[i]; OptionTab tab = tabs[i];
boolean hovering = tab.button.contains(mouseX, mouseY); boolean hovering = tab.button.contains(displayContainer.mouseX, displayContainer.mouseY);
UI.drawTab(tab.button.getX(), tab.button.getY(), tab.name, i == selectedTab, hovering); UI.drawTab(tab.button.getX(), tab.button.getY(), tab.name, i == selectedTab, hovering);
} }
} }
@ -339,7 +347,25 @@ public class OptionsOverlay {
Fonts.DEFAULT.drawString(marginX, marginY, "Change the way opsu! behaves", Color.white); Fonts.DEFAULT.drawString(marginX, marginY, "Change the way opsu! behaves", Color.white);
} }
public void update(int delta, int mouseX, int mouseY) { @Override
public void hide() {
acceptInput = false;
SoundController.playSound(SoundEffect.MENUBACK);
active = false;
}
@Override
public void show() {
acceptInput = true;
active = true;
}
@Override
public void onPreRenderUpdate() {
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
int delta = displayContainer.renderDelta;
if (sliderSoundDelay > 0) { if (sliderSoundDelay > 0) {
sliderSoundDelay -= delta; sliderSoundDelay -= delta;
} }
@ -352,7 +378,7 @@ public class OptionsOverlay {
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
if (isAdjustingSlider) { if (isAdjustingSlider) {
int sliderValue = hoverOption.getIntegerValue(); int sliderValue = hoverOption.getIntegerValue();
updateSliderOption(mouseX, mouseY); updateSliderOption();
if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) { if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) {
sliderSoundDelay = 90; sliderSoundDelay = 90;
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
@ -366,22 +392,27 @@ public class OptionsOverlay {
} }
} }
public void mousePressed(int button, int x, int y) { @Override
public boolean onMousePressed(int button, int x, int y) {
if (keyEntryLeft || keyEntryRight) { if (keyEntryLeft || keyEntryRight) {
keyEntryLeft = keyEntryRight = false; keyEntryLeft = keyEntryRight = false;
return; return true;
} }
if (isListOptionOpen) { if (isListOptionOpen) {
if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) { if (y > optionStartY && listStartX <= x && x < listStartX + listWidth && listStartY <= y && y < listStartY + listHeight) {
if (0 <= listHoverIndex && listHoverIndex < hoverOption.getListItems().length) {
hoverOption.clickListItem(listHoverIndex); hoverOption.clickListItem(listHoverIndex);
parent.onSaveOption(hoverOption); if (listener != null) {
listener.onSaveOption(hoverOption);
}
}
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
} }
isListOptionOpen = false; isListOptionOpen = false;
listHoverIndex = -1; listHoverIndex = -1;
updateHoverOption(x, y); updateHoverOption(x, y);
return; return true;
} }
mousePressY = y; mousePressY = y;
@ -393,35 +424,39 @@ public class OptionsOverlay {
} else if (hoverOption.getType() == OptionType.NUMERIC) { } else if (hoverOption.getType() == OptionType.NUMERIC) {
isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength; isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength;
if (isAdjustingSlider) { if (isAdjustingSlider) {
updateSliderOption(x, y); updateSliderOption();
} }
} }
} }
if (UI.getBackButton().contains(x, y)) { if (UI.getBackButton().contains(x, y)) {
parent.onLeave(); hide();
} }
return true;
} }
public void mouseReleased(int button, int x, int y) { @Override
public boolean onMouseReleased(int button, int x, int y) {
selectedOption = null; selectedOption = null;
if (isAdjustingSlider) { if (isAdjustingSlider && listener != null) {
parent.onSaveOption(hoverOption); listener.onSaveOption(hoverOption);
} }
isAdjustingSlider = false; isAdjustingSlider = false;
sliderOptionLength = 0; sliderOptionLength = 0;
// check if clicked, not dragged // check if clicked, not dragged
if (Math.abs(y - mousePressY) >= 5) { if (Math.abs(y - mousePressY) >= 5) {
return; return true;
} }
if (hoverOption != null) { if (hoverOption != null) {
if (hoverOption.getType() == OptionType.BOOLEAN) { if (hoverOption.getType() == OptionType.BOOLEAN) {
hoverOption.click(container); hoverOption.click();
parent.onSaveOption(hoverOption); if (listener != null) {
listener.onSaveOption(hoverOption);
}
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
return; return true;
} else if (hoverOption == GameOption.KEY_LEFT) { } else if (hoverOption == GameOption.KEY_LEFT) {
keyEntryLeft = true; keyEntryLeft = true;
} else if (hoverOption == GameOption.KEY_RIGHT) { } else if (hoverOption == GameOption.KEY_RIGHT) {
@ -435,27 +470,38 @@ public class OptionsOverlay {
if (tab.button.contains(x, y)) { if (tab.button.contains(x, y)) {
scrollOffset = tScrollOffset; scrollOffset = tScrollOffset;
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
return; return true;
} }
tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2; tScrollOffset += Fonts.MEDIUM.getLineHeight() * 2;
tScrollOffset += tab.options.length * optionHeight; tScrollOffset += tab.options.length * optionHeight;
} }
if (UI.getBackButton().contains(x, y) && listener != null) {
listener.onLeaveOptionsMenu();
} }
public void mouseDragged(int oldx, int oldy, int newx, int newy) { return true;
}
@Override
public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
if (!isAdjustingSlider) { if (!isAdjustingSlider) {
scrollOffset = Utils.clamp(scrollOffset + oldy - newy, 0, maxScrollOffset); scrollOffset = Utils.clamp(scrollOffset + oldy - newy, 0, maxScrollOffset);
} }
return true;
} }
public void mouseWheelMoved(int delta) { @Override
public boolean onMouseWheelMoved(int delta) {
if (!isAdjustingSlider) { if (!isAdjustingSlider) {
scrollOffset = Utils.clamp(scrollOffset - delta, 0, maxScrollOffset); scrollOffset = Utils.clamp(scrollOffset - delta, 0, maxScrollOffset);
} }
updateHoverOption(prevMouseX, prevMouseY); updateHoverOption(prevMouseX, prevMouseY);
return true;
} }
public boolean keyPressed(int key, char c) { @Override
public boolean onKeyPressed(int key, char c) {
if (keyEntryRight) { if (keyEntryRight) {
Options.setGameKeyRight(key); Options.setGameKeyRight(key);
keyEntryRight = false; keyEntryRight = false;
@ -468,23 +514,31 @@ public class OptionsOverlay {
return true; return true;
} }
switch (key) { if (key == Input.KEY_ESCAPE) {
case Input.KEY_ESCAPE:
if (isListOptionOpen) { if (isListOptionOpen) {
isListOptionOpen = false; isListOptionOpen = false;
listHoverIndex = -1; listHoverIndex = -1;
return true; return true;
} }
parent.onLeave(); hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
}
return true; return true;
} }
return false; return false;
} }
private void updateSliderOption(int mouseX, int mouseY) { @Override
public boolean onKeyReleased(int key, char c) {
return false;
}
private void updateSliderOption() {
int min = hoverOption.getMinValue(); int min = hoverOption.getMinValue();
int max = hoverOption.getMaxValue(); int max = hoverOption.getMaxValue();
int value = min + Math.round((float) (max - min) * (mouseX - sliderOptionStartX) / (sliderOptionLength)); int value = min + Math.round((float) (max - min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
hoverOption.setValue(Utils.clamp(value, min, max)); hoverOption.setValue(Utils.clamp(value, min, max));
} }
@ -533,10 +587,9 @@ public class OptionsOverlay {
} }
public interface Parent { public interface Listener {
void onLeave();
void onLeaveOptionsMenu();
void onSaveOption(GameOption option); void onSaveOption(GameOption option);
} }

View File

@ -22,86 +22,27 @@ import itdelatrisu.opsu.Options.GameOption;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.states.OptionsMenu;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
import yugecin.opsudance.ObjectColorOverrides; import yugecin.opsudance.ObjectColorOverrides;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.sbv2.MoveStoryboard; import yugecin.opsudance.sbv2.MoveStoryboard;
import yugecin.opsudance.ui.OptionsOverlay.OptionTab; import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
import java.util.*; import java.util.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class SBOverlay implements OptionsOverlay.Parent { public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener {
private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{
new OptionTab("Gameplay", new GameOption[] {
GameOption.BACKGROUND_DIM,
GameOption.DANCE_REMOVE_BG,
GameOption.SNAKING_SLIDERS,
GameOption.SHRINKING_SLIDERS,
GameOption.SHOW_HIT_LIGHTING,
GameOption.SHOW_HIT_ANIMATIONS,
GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT,
GameOption.SHOW_FOLLOW_POINTS,
}),
new OptionTab("Input", new GameOption[] {
GameOption.CURSOR_SIZE,
GameOption.NEW_CURSOR,
GameOption.DISABLE_CURSOR
}),
new OptionTab("Dance", new GameOption[] {
GameOption.DANCE_MOVER,
GameOption.DANCE_EXGON_DELAY,
GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_MOVER_DIRECTION,
GameOption.DANCE_SLIDER_MOVER_TYPE,
GameOption.DANCE_SPINNER,
GameOption.DANCE_SPINNER_DELAY,
GameOption.DANCE_LAZY_SLIDERS,
GameOption.DANCE_CIRCLE_STREAMS,
GameOption.DANCE_ONLY_CIRCLE_STACKS,
GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
GameOption.DANCE_MIRROR,
}),
new OptionTab("Dance display", new GameOption[] {
GameOption.DANCE_DRAW_APPROACH,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
GameOption.DANCE_RGB_OBJECT_INC,
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
GameOption.DANCE_RGB_CURSOR_INC,
GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
GameOption.DANCE_HIDE_OBJECTS,
GameOption.DANCE_HIDE_UI,
GameOption.DANCE_HIDE_WATERMARK,
}),
new OptionTab ("Pippi", new GameOption[] {
GameOption.PIPPI_ENABLE,
GameOption.PIPPI_RADIUS_PERCENT,
GameOption.PIPPI_ANGLE_INC_MUL,
GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
})
};
private final static List<GameOption> optionList = new ArrayList<>(); private final static List<GameOption> optionList = new ArrayList<>();
private boolean hide; private final DisplayContainer displayContainer;
private boolean menu;
private int width; private boolean hide;
private int height;
private int speed; private int speed;
private GameObject[] gameObjects; private GameObject[] gameObjects;
@ -112,43 +53,42 @@ public class SBOverlay implements OptionsOverlay.Parent {
private final Game game; private final Game game;
private final MoveStoryboard msb; private final MoveStoryboard msb;
private final OptionsOverlay overlay; private final OptionsOverlay optionsOverlay;
static { static {
for (OptionTab tab : options) { for (OptionTab tab : OptionsMenu.storyboardOptions) {
optionList.addAll(Arrays.asList(tab.options)); optionList.addAll(Arrays.asList(tab.options));
} }
} }
public SBOverlay(Game game, MoveStoryboard msb, GameContainer container) { public StoryboardOverlay(DisplayContainer displayContainer, MoveStoryboard msb, OptionsOverlay optionsOverlay, Game game) {
this.game = game; this.displayContainer = displayContainer;
this.msb = msb; this.msb = msb;
this.optionsOverlay = optionsOverlay;
this.game = game;
initialOptions = new HashMap<>(); initialOptions = new HashMap<>();
overlay = new OptionsOverlay(this, options, 2, container);
this.width = container.getWidth();
this.height = container.getHeight();
speed = 10; speed = 10;
gameObjects = new GameObject[0]; gameObjects = new GameObject[0];
} }
public void render(GameContainer container, Graphics g) { @Override
public void onRender(Graphics g) {
if (!Options.isEnableSB() || hide) { if (!Options.isEnableSB() || hide) {
return; return;
} }
msb.render(g);
int lh = Fonts.SMALL.getLineHeight(); int lh = Fonts.SMALL.getLineHeight();
Fonts.SMALL.drawString(10, height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan); Fonts.SMALL.drawString(10, displayContainer.height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan);
Fonts.SMALL.drawString(10, height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan); Fonts.SMALL.drawString(10, displayContainer.height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan);
Fonts.SMALL.drawString(10, height - 50 - lh, "Menu: N", Color.cyan); Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh, "Menu: N", Color.cyan);
Fonts.SMALL.drawString(10, height - 50 - lh * 2, "HIDE: H", Color.cyan); Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 2, "HIDE: H", Color.cyan);
Fonts.SMALL.drawString(10, height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan); Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan);
g.setColor(Color.red); g.setColor(Color.red);
if (index < optionsMap.length && optionsMap[index] != null) { if (index < optionsMap.length && optionsMap[index] != null) {
int i = 0; int i = 0;
for (Object o : optionsMap[index].entrySet()) { for (Object o : optionsMap[index].entrySet()) {
Map.Entry<Options.GameOption, String> option = (Map.Entry<Options.GameOption, String>) o; Map.Entry<Options.GameOption, String> option = (Map.Entry<Options.GameOption, String>) o;
Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().getName(), Color.cyan); Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().getName(), Color.cyan);
Fonts.SMALL.drawString(width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan); Fonts.SMALL.drawString(displayContainer.width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan);
g.fillRect(0, 50 + i * lh + lh / 4, 10, 10); g.fillRect(0, 50 + i * lh + lh / 4, 10, 10);
i++; i++;
} }
@ -157,27 +97,16 @@ public class SBOverlay implements OptionsOverlay.Parent {
int start = gameObjects[0].getTime(); int start = gameObjects[0].getTime();
int end = gameObjects[gameObjects.length - 1].getEndTime(); int end = gameObjects[gameObjects.length - 1].getEndTime();
float curtime = (float) (MusicController.getPosition() - start) / (end - start); float curtime = (float) (MusicController.getPosition() - start) / (end - start);
g.fillRect(curtime * width, height - 10f, 10f, 10f); g.fillRect(curtime * displayContainer.width, displayContainer.height - 10f, 10f, 10f);
}
if (menu) {
overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY());
} }
} }
public void update(int delta, int mouseX, int mouseY) { @Override
if (Options.isEnableSB() && menu) { public void onPreRenderUpdate() {
overlay.update(delta, mouseX, mouseY);
}
msb.update(delta, mouseX, mouseY);
} }
public boolean keyPressed(int key, char c) { @Override
if (!Options.isEnableSB()) { public boolean onKeyPressed(int key, char c) {
return false;
}
if (menu && overlay.keyPressed(key, c)) {
return true;
}
if (key == Input.KEY_C) { if (key == Input.KEY_C) {
if (speed > 0) { if (speed > 0) {
speed -= 1; speed -= 1;
@ -196,11 +125,9 @@ public class SBOverlay implements OptionsOverlay.Parent {
} else if (key == Input.KEY_H) { } else if (key == Input.KEY_H) {
hide = !hide; hide = !hide;
} else if (key == Input.KEY_N) { } else if (key == Input.KEY_N) {
menu = !menu; optionsOverlay.show();
if (menu && speed != 0) { if (speed != 0) {
MusicController.pause(); MusicController.pause();
} else if (!menu && speed != 0) {
MusicController.resume();
} }
} else if (key == Input.KEY_J && index > 0) { } else if (key == Input.KEY_J && index > 0) {
index--; index--;
@ -214,6 +141,11 @@ public class SBOverlay implements OptionsOverlay.Parent {
return false; return false;
} }
@Override
protected boolean onKeyReleased(int key, char c) {
return false;
}
private void goBackOneSBIndex() { private void goBackOneSBIndex() {
if (index + 1 < optionsMap.length) { if (index + 1 < optionsMap.length) {
// new options on previous index, so to revert then we have to reload them all to this point.. // new options on previous index, so to revert then we have to reload them all to this point..
@ -252,20 +184,13 @@ public class SBOverlay implements OptionsOverlay.Parent {
this.gameObjects = gameObjects; this.gameObjects = gameObjects;
} }
public boolean mousePressed(int button, int x, int y) { @Override
msb.mousePressed(x, y); public boolean onMousePressed(int button, int x, int y) {
if (!menu) {
return false;
}
overlay.mousePressed(button, x, y);
return true; return true;
} }
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { @Override
if (!menu) { public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
overlay.mouseDragged(oldx, oldy, newx, newy);
return true; return true;
} }
@ -293,12 +218,8 @@ public class SBOverlay implements OptionsOverlay.Parent {
this.index--; this.index--;
} }
public boolean mouseReleased(int button, int x, int y) { @Override
if (menu) { public boolean onMouseReleased(int button, int x, int y) {
overlay.mouseReleased(button, x, y);
return true;
}
msb.mouseReleased(x, y);
if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) { if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) {
return false; return false;
} }
@ -318,15 +239,12 @@ public class SBOverlay implements OptionsOverlay.Parent {
return true; return true;
} }
public boolean mouseWheelMoved(int delta) { @Override
if (!menu) { public boolean onMouseWheelMoved(int delta) {
return false;
}
overlay.mouseWheelMoved(delta);
return true; return true;
} }
public void enter() { public void onEnter() {
// enter, save current settings // enter, save current settings
for (Options.GameOption o : optionList) { for (Options.GameOption o : optionList) {
initialOptions.put(o, o.write()); initialOptions.put(o, o.write());
@ -334,7 +252,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
speed = 10; speed = 10;
} }
public void leave() { public void onLeave() {
// leave, revert the settings saved before entering // leave, revert the settings saved before entering
for (Options.GameOption o : optionList) { for (Options.GameOption o : optionList) {
if (initialOptions.containsKey(o)) { if (initialOptions.containsKey(o)) {
@ -358,13 +276,8 @@ public class SBOverlay implements OptionsOverlay.Parent {
} }
} }
public float[] getPoint(int trackPosition) {
return msb.getPoint(trackPosition);
}
@Override @Override
public void onLeave() { public void onLeaveOptionsMenu() {
menu = false;
if (speed != 0) { if (speed != 0) {
MusicController.resume(); MusicController.resume();
} }

View File

@ -17,7 +17,10 @@
*/ */
package yugecin.opsudance.utils; package yugecin.opsudance.utils;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLException;
import org.lwjgl.input.Cursor;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.DisplayMode;
import org.newdawn.slick.opengl.ImageIOImageData; import org.newdawn.slick.opengl.ImageIOImageData;
@ -25,8 +28,10 @@ import org.newdawn.slick.opengl.LoadableImageData;
import org.newdawn.slick.opengl.TGAImageData; import org.newdawn.slick.opengl.TGAImageData;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class GLHelper { public class GLHelper {
@ -85,4 +90,22 @@ public class GLHelper {
Display.setIcon(bufs); Display.setIcon(bufs);
} }
public static void hideNativeCursor() {
try {
int min = Cursor.getMinCursorSize();
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
Mouse.setNativeCursor(new Cursor(min, min, min / 2, min / 2, 1, tmp, null));
} catch (LWJGLException e) {
ErrorHandler.error("Cannot hide native cursor", e).show();
}
}
public static void showNativeCursor() {
try {
Mouse.setNativeCursor(null);
} catch (LWJGLException e) {
ErrorHandler.error("Cannot show native cursor", e).show();
}
}
} }

View File

@ -0,0 +1,44 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.utils;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
public class SlickUtil {
public static void destroyImages(Image[] imgs) {
if (imgs == null) {
return;
}
for (Image i : imgs) {
destroyImage(i);
}
}
public static void destroyImage(Image image) {
if (image == null) {
return;
}
try {
image.destroy();
} catch (SlickException ignored) {
}
}
}