Merge branch 'master' into kockout-wdata

# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
#	src/itdelatrisu/opsu/states/Splash.java
This commit is contained in:
yugecin
2018-10-22 21:59:11 +02:00
145 changed files with 2112 additions and 1884 deletions

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -35,7 +35,6 @@ import yugecin.opsudance.movers.factories.*;
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
import yugecin.opsudance.movers.slidermovers.InheritedSliderMoverController;
import yugecin.opsudance.movers.slidermovers.SliderMoverController;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.spinners.*;
import java.awt.*;
@@ -252,8 +251,8 @@ public class Dancer {
}
}
Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, displayContainer.width - 10);
y = Utils.clamp(y, 10, displayContainer.height - 10);
x = Utils.clamp(x, 10, width - 10);
y = Utils.clamp(y, 10, height - 10);
}
private void createNewMover() {

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -25,8 +25,8 @@ import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.render.FrameBufferCache;
import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI;
@@ -43,16 +43,16 @@ import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.SkinChangedListener;
import yugecin.opsudance.ui.BackButton;
import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
@@ -60,33 +60,23 @@ import static yugecin.opsudance.options.Options.*;
/**
* based on org.newdawn.slick.AppGameContainer
*/
public class DisplayContainer implements ErrorDumpable, ResolutionChangedListener, SkinChangedListener {
public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
private static SGL GL = Renderer.get();
private FpsRenderState fpsState;
private BarNotificationState barNotifState;
private BubNotifState bubNotifState;
private OpsuState state;
public final DisplayMode nativeDisplayMode;
private Graphics graphics;
public int width;
public int height;
public int mouseX;
public int mouseY;
private int targetUpdatesPerSecond;
public int targetUpdateInterval;
private int targetRendersPerSecond;
public int targetRenderInterval;
public int targetBackgroundRenderInterval;
public int renderDelta;
private boolean rendering;
public int delta;
public boolean exitRequested;
@@ -104,53 +94,22 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
public final Cursor cursor;
public boolean drawCursor;
private final List<ResolutionChangedListener> resolutionChangedListeners;
class Transition {
int in;
int out;
int total;
int progress = -1;
OpsuState nextstate;
Color OVERLAY = new Color(Color.black);
private int tIn;
private int tOut;
private int tProgress = -1;
private OpsuState tNextState;
private final Color tOVERLAY = new Color(Color.black);
public void update() {
if (progress == -1) {
return;
}
progress += delta;
if (progress > out && nextstate != null) {
switchStateInstantly(nextstate);
nextstate = null;
}
if (progress > total) {
progress = -1;
}
}
public void render(Graphics graphics) {
if (progress == -1) {
return;
}
int relprogress = progress;
int reltotal = out;
if (progress > out) {
reltotal = in;
relprogress = total - progress;
}
OVERLAY.a = (float) relprogress / reltotal;
graphics.setColor(OVERLAY);
graphics.fillRect(0, 0, width, height);
}
}
private final Transition transition = new Transition();
public DisplayContainer() {
public DisplayContainer()
{
this.resolutionChangedListeners = new ArrayList<>();
this.cursor = new Cursor();
drawCursor = true;
ResolutionChangedListener.EVENT.addListener(this);
SkinChangedListener.EVENT.addListener(this);
skinservice.addSkinChangedListener(this);
this.nativeDisplayMode = Display.getDisplayMode();
targetBackgroundRenderInterval = 41; // ~24 fps
@@ -159,10 +118,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
renderDelta = 1;
}
@Override
public void onResolutionChanged(int w, int h) {
destroyImages();
reinit();
public void addResolutionChangedListener(ResolutionChangedListener l)
{
this.resolutionChangedListeners.add(l);
}
@Override
@@ -186,12 +144,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
}
backButton = new BackButton();
// TODO clean this up
GameMod.init(width, height);
PlaybackSpeed.init(width, height);
HitObject.init(width, height);
DownloadNode.init(width, height);
UI.init(this);
}
public void setUPS(int ups) {
@@ -205,13 +164,9 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
public void init(OpsuState startingState) {
setUPS(OPTION_TARGET_UPS.val);
setUPS(targetUPS[OPTION_TARGET_UPS.val]);
setFPS(targetFPS[targetFPSIndex]);
fpsState = new FpsRenderState();
bubNotifState = new BubNotifState();
barNotifState = new BarNotificationState();
state = startingState;
state.enter();
}
@@ -228,8 +183,18 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
mouseX = input.getMouseX();
mouseY = input.getMouseY();
transition.update();
fpsState.update();
// state transition
if (tProgress != -1) {
tProgress += delta;
if (tProgress > tOut && tNextState != null) {
switchStateInstantly(tNextState);
tNextState = null;
}
if (tProgress > tIn + tOut) {
tProgress = -1;
}
}
fpsDisplay.update();
state.update();
if (drawCursor) {
@@ -244,6 +209,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
if (timeSinceLastRender >= maxRenderInterval) {
rendering = true;
GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
/*
@@ -257,32 +223,44 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
state.preRenderUpdate();
state.render(graphics);
fpsState.render(graphics);
bubNotifState.render(graphics);
barNotifState.render(graphics);
fpsDisplay.render(graphics);
bubNotifs.render(graphics);
barNotifs.render(graphics);
cursor.updateAngle(renderDelta);
cursor.updateAngle();
if (drawCursor) {
cursor.draw(Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON) ||
Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON));
}
UI.drawTooltip(graphics);
transition.render(graphics);
// transition
if (tProgress != -1) {
if (tProgress > tOut) {
tOVERLAY.a = 1f - (tProgress - tOut) / (float) tIn;
} else {
tOVERLAY.a = tProgress / (float) tOut;
}
graphics.setColor(tOVERLAY);
graphics.fillRect(0, 0, width, height);
}
timeSinceLastRender = 0;
Display.update(false);
GL11.glFlush();
rendering = false;
}
Display.processMessages();
Display.sync(targetUpdatesPerSecond);
if (targetUpdatesPerSecond >= 60) {
Display.sync(targetUpdatesPerSecond);
}
}
}
public void setup() throws Exception {
width = height = -1;
width = height = width2 = height2 = -1;
Display.setTitle("opsu!dance");
setupResolutionOptionlist(nativeDisplayMode.getWidth(), nativeDisplayMode.getHeight());
updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString());
@@ -318,6 +296,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
GameImage.destroyImages();
GameData.Grade.destroyImages();
Beatmap.destroyBackgroundImageCache();
FrameBufferCache.shutdown();
}
public void teardownAL() {
@@ -342,13 +321,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
return true;
}
if (DownloadList.get().hasActiveDownloads()) {
BubNotifListener.EVENT.make().onBubNotif(DownloadList.EXIT_CONFIRMATION, Colors.BUB_RED);
bubNotifs.send(BUB_RED, DownloadList.EXIT_CONFIRMATION);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
}
if (updater.getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
BubNotifListener.EVENT.make().onBubNotif(Updater.EXIT_CONFIRMATION, Colors.BUB_PURPLE);
bubNotifs.send(BUB_PURPLE, Updater.EXIT_CONFIRMATION);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
@@ -372,6 +351,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
width = Integer.parseInt(res[0]);
height = Integer.parseInt(res[1]);
}
updateDisplayMode(width, height);
}
public void updateDisplayMode(int width, int height) {
int screenWidth = nativeDisplayMode.getWidth();
int screenHeight = nativeDisplayMode.getHeight();
// check for larger-than-screen dimensions
if (!OPTION_ALLOW_LARGER_RESOLUTIONS.state && (screenWidth < width || screenHeight < height)) {
@@ -387,34 +373,35 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
try {
setDisplayMode(width, height, OPTION_FULLSCREEN.state);
} catch (Exception e) {
BubNotifListener.EVENT.make().onBubNotif("Failed to change resolution", Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Failed to change display mode");
Log.error("Failed to set display mode.", e);
}
}
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
if (this.width == width && this.height == height) {
Display.setFullscreen(fullscreen);
return;
}
public void setDisplayMode(int w, int h, boolean fullscreen) throws Exception {
DisplayMode displayMode = null;
if (fullscreen) {
displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height);
final int bpp = this.nativeDisplayMode.getBitsPerPixel();
final int freq = this.nativeDisplayMode.getFrequency();
displayMode = GLHelper.findFullscreenDisplayMode(bpp, freq, w, h);
}
if (displayMode == null) {
displayMode = new DisplayMode(width, height);
displayMode = new DisplayMode(w, h);
if (fullscreen) {
fullscreen = false;
String msg = String.format("Fullscreen mode is not supported for %sx%s", width, height);
String msg = "Fullscreen mode is not supported for %sx%s";
msg = String.format(msg, w, h);
Log.warn(msg);
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_ORANGE);
bubNotifs.send(BUB_ORANGE, msg);
}
}
this.width = displayMode.getWidth();
this.height = displayMode.getHeight();
width = displayMode.getWidth();
height = displayMode.getHeight();
width2 = width / 2;
height2 = height / 2;
isWidescreen = width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3
Display.setDisplayMode(displayMode);
Display.setFullscreen(fullscreen);
@@ -439,7 +426,7 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
input = new Input(height);
input.enableKeyRepeat();
input.addListener(new GlobalInputListener());
input.addMouseListener(bubNotifState);
input.addMouseListener(bubNotifs);
}
input.addListener(state);
@@ -448,7 +435,15 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
GameImage.init(width, height);
Fonts.init();
ResolutionChangedListener.EVENT.make().onResolutionChanged(width, height);
destroyImages();
reinit();
barNotifs.onResolutionChanged(width, height);
bubNotifs.onResolutionChanged(width, height);
fpsDisplay.onResolutionChanged(width, height);
for (ResolutionChangedListener l : this.resolutionChangedListeners) {
l.onResolutionChanged(width, height);
}
}
public void resetCursor() {
@@ -466,10 +461,6 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
public boolean isWidescreen() {
return width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3
}
@Override
public void writeErrorDump(StringWriter dump) {
dump.append("> DisplayContainer dump\n");
@@ -487,22 +478,32 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
}
public void switchState(OpsuState state) {
switchState(state, 200, 300);
switchState(state, 150, 250);
}
public void switchState(OpsuState newstate, int outtime, int intime) {
if (transition.progress != -1) {
if (tProgress != -1 && tProgress <= tOut) {
return;
}
if (outtime == 0) {
switchStateInstantly(newstate);
newstate = null;
} else {
input.removeListener(this.state);
}
transition.nextstate = newstate;
transition.total = transition.in = intime;
transition.out = outtime;
transition.total += outtime;
transition.progress = 0;
if (tProgress == -1) {
tProgress = 0;
} else {
// we were in a transition (out state), so start from the time
// that was already spent transitioning in
tProgress = (int) (((1f - (tProgress - tOut) / (float) tIn)) * outtime);
}
tNextState = newstate;
tIn = intime;
tOut = outtime;
}
public void switchStateInstantly(OpsuState state) {
@@ -511,6 +512,13 @@ public class DisplayContainer implements ErrorDumpable, ResolutionChangedListene
this.state = state;
this.state.enter();
input.addListener(this.state);
backButton.resetHover();
if (this.rendering) {
// state might be changed in preRenderUpdate,
// in that case the new state will be rendered without having
// preRenderUpdate being called first, so do that now
this.state.preRenderUpdate();
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -21,7 +21,6 @@ import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Input;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.events.BarNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@@ -38,8 +37,8 @@ public class GlobalInputListener implements InputListener {
public boolean keyReleased(int key, char c) {
if (key == KEY_F7) {
OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length);
BarNotifListener.EVENT.make().onBarNotif(String.format("Frame limiter: %s",
OPTION_TARGET_FPS.getValueString()));
final String value = OPTION_TARGET_FPS.getValueString();
barNotifs.sendf("Frame limiter: %s", value);
return true;
}
if (key == KEY_F10) {

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -26,11 +26,17 @@ import itdelatrisu.opsu.states.*;
import org.newdawn.slick.Input;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.ManifestWrapper;
import yugecin.opsudance.ui.BackButton;
import yugecin.opsudance.ui.OptionsOverlay;
import java.io.File;
import java.io.IOException;
@@ -51,10 +57,17 @@ public class InstanceContainer {
public static BeatmapParser beatmapParser;
public static Updater updater;
public static BackButton backButton;
public static DisplayContainer displayContainer;
public static Input input;
public static GameObjectRenderer gameObjectRenderer;
public static BarNotificationState barNotifs;
public static BubNotifState bubNotifs;
public static FpsRenderState fpsDisplay;
public static OptionsOverlay optionsOverlay;
public static Splash splashState;
public static MainMenu mainmenuState;
@@ -64,17 +77,21 @@ public class InstanceContainer {
public static Game gameState;
public static GameRanking gameRankingState;
public static GamePauseMenu pauseState;
public static int width, width2, height, height2;
public static boolean isWidescreen;
public static int mouseX, mouseY;
public static int renderDelta;
public static void kickstart() {
updater = new Updater();
env = new Environment();
JarFile jarfile = getJarfile();
ManifestWrapper manifest = new ManifestWrapper(getJarManifest(jarfile));
config = new Configuration(manifest);
config = new Configuration();
if (jarfile != null) {
try {
NativeLoader.loadNatives(jarfile, manifest);
NativeLoader.loadNatives(jarfile);
} catch (IOException e) {
String msg = String.format("Could not unpack native(s): %s", e.getMessage());
throw new RuntimeException(msg, e);
@@ -94,9 +111,15 @@ public class InstanceContainer {
updater = new Updater();
displayContainer = new DisplayContainer();
barNotifs = new BarNotificationState();
bubNotifs = new BubNotifState();
fpsDisplay = new FpsRenderState();
gameObjectRenderer = new GameObjectRenderer();
optionsOverlay = new OptionsOverlay(OptionGroups.normalOptions);
splashState = new Splash();
mainmenuState = new MainMenu();
buttonState = new ButtonMenu();

View File

@@ -24,6 +24,11 @@ import yugecin.opsudance.utils.MiscUtils;
import javax.swing.*;
import java.awt.Desktop;
import java.awt.Dialog.ModalityType;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -94,30 +99,15 @@ public class ErrorHandler {
}
JLabel message = new JLabel(messageText);
JTextArea textArea = new JTextArea(15, 100);
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);
textArea.setText(messageBody);
ActionListener reportAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
try {
URI url = createGithubIssueUrl(customMessage, cause, errorDump);
Desktop.getDesktop().browse(url);
} catch (IOException e) {
Log.warn("Could not open browser to report issue", e);
JOptionPane.showMessageDialog(null, "whoops could not launch a browser",
"errorception", JOptionPane.ERROR_MESSAGE);
}
Window parent = SwingUtilities.getWindowAncestor(message);
showCreateIssueDialog(parent, errorDump, customMessage, cause);
}
};
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(),
Object[] messageComponents = new Object[] { message, readonlyTextarea(messageBody), createViewLogButton(),
createReportButton(flags, reportAction) };
String[] buttons;
@@ -181,8 +171,71 @@ public class ErrorHandler {
button.setEnabled(false);
return button;
}
private static void showCreateIssueDialog(
Window parent,
String errorDump,
String customMessage,
Throwable cause)
{
final String dump = createIssueDump(customMessage, cause, errorDump);
private static URI createGithubIssueUrl(String customMessage, Throwable cause, String errorDump) {
final String title = "report error";
JDialog d = new JDialog(parent, title, ModalityType.APPLICATION_MODAL);
d.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 1;
c.weightx = 1d;
c.weighty = 0d;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(4, 8, 4, 8);
d.add(new JLabel(
"<html>Copy the text in the box below.<br/>"
+ "Then click the button below.<br/>"
+ "Your browser should open a page where you can report the issue.<br/>"
+ "Please paste the dump below in the issue box."
), c);
c.gridy++;
c.weighty = 1d;
d.add(readonlyTextarea(dump), c);
c.gridy++;
c.weighty = c.weightx = 0d;
c.fill = GridBagConstraints.NONE;
JButton btn = new JButton("Report");
btn.addActionListener(e -> {
try {
URI url = createGithubIssueUrl(customMessage, cause);
Desktop.getDesktop().browse(url);
d.dispose();
} catch (IOException t) {
Log.warn("Could not open browser to report issue", t);
JOptionPane.showMessageDialog(null, "whoops could not launch a browser",
"errorception", JOptionPane.ERROR_MESSAGE);
}
});
d.add(btn, c);
d.pack();
d.setLocationRelativeTo(parent);
d.setVisible(true);
}
private static URI createGithubIssueUrl(String customMessage, Throwable cause) {
String issueTitle = "";
String issueBody = "";
try {
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + ": " +
customMessage, "UTF-8");
issueBody = URLEncoder.encode("PASTE THE DUMP HERE", "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
}
return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody));
}
private static String createIssueDump(String customMessage, Throwable cause, String errorDump) {
StringWriter dump = new StringWriter();
dump.append(customMessage).append("\n");
@@ -203,25 +256,19 @@ public class ErrorHandler {
dump.append("**info dump**").append('\n');
dump.append("```\n").append(errorDump).append("\n```\n\n");
String issueTitle = "";
String issueBody = "";
try {
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " +
customMessage, "UTF-8");
issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
}
return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody));
return dump.toString();
}
private static String truncateGithubIssueBody(String body) {
if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) {
return body;
}
Log.warn("error dump too long to fit into github issue url, truncating");
return body.substring(0, 1640) + "** TRUNCATED **\n```";
private static JComponent readonlyTextarea(String contents) {
JTextArea textArea = new JTextArea(15, 100);
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);
textArea.setFont(new JLabel().getFont());
textArea.setText(contents);
return new JScrollPane(textArea);
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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.events;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
@SuppressWarnings("unchecked")
public class Event<T> {
private final Class<T> type;
private final LinkedList<T> listeners;
public Event(Class<T> type) {
this.type = type;
this.listeners = new LinkedList<>();
}
public void addListener(T listener) {
this.listeners.add(listener);
}
public T make() {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (T listener : listeners) {
method.invoke(listener, args);
}
return null;
}
});
}
}

View File

@@ -1,25 +0,0 @@
/*
* 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.inject;
public interface Binder<T> {
void asEagerSingleton();
void asLazySingleton();
void to(Class<? extends T> type);
}

View File

@@ -1,24 +0,0 @@
/*
* 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.inject;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) public @interface Inject {
}

View File

@@ -1,129 +0,0 @@
/*
* 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.inject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
@SuppressWarnings("unchecked")
public abstract class Injector implements InstanceContainer, Binder {
private final HashMap<Class<?>, Object> instances;
private final LinkedList<Class<?>> lazyInstances;
private Class<?> lastType;
public Injector() {
instances = new HashMap<>();
lazyInstances = new LinkedList<>();
instances.put(InstanceContainer.class, this);
configure();
}
protected abstract void configure();
public final <T> T provide(Class<T> type) {
Object instance = instances.get(type);
if (instance != null) {
return (T) instance;
}
ListIterator<Class<?>> iter = lazyInstances.listIterator();
while (iter.hasNext()) {
Class<?> l = iter.next();
if (l == type) {
iter.remove();
instance = createInstance(type);
instances.put(type, instance);
return (T) instance;
}
}
return createInstance(type);
}
private <T> T createInstance(Class<T> type) {
Constructor<?>[] constructors = type.getDeclaredConstructors();
if (constructors.length == 0) {
throw new RuntimeException("Cannot provide " + type.getSimpleName());
}
Constructor constructor = constructors[0];
Class<?>[] parameterTypes = constructor.getParameterTypes();
Object[] params = new Object[parameterTypes.length];
for (int i = parameterTypes.length - 1; i >= 0; i--) {
params[i] = provide(parameterTypes[i]);
}
try {
return injectFields((T) constructor.newInstance(params), type);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private <T> T injectFields(T object, Class<?> type) {
do {
for (Field f : type.getDeclaredFields()) {
if (f.getDeclaredAnnotation(Inject.class) == null) {
continue;
}
boolean accessible = f.isAccessible();
if (!accessible) {
f.setAccessible(true);
}
try {
f.set(object, provide(f.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
if (!accessible) {
f.setAccessible(false);
}
}
type = type.getSuperclass();
} while (type != null);
return object;
}
@Override
public <T> T injectFields(T obj) {
return injectFields(obj, obj.getClass());
}
public final <T> Binder<T> bind(Class<T> type) {
lastType = type;
return this;
}
@Override
public final void asEagerSingleton() {
instances.put(lastType, createInstance(lastType));
}
@Override
public final void asLazySingleton() {
lazyInstances.add(lastType);
}
@Override
public final void to(Class type) {
instances.put(lastType, createInstance(type));
}
}

View File

@@ -1,25 +0,0 @@
/*
* 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.inject;
public interface InstanceContainer {
<T> T provide(Class<T> type);
<T> T injectFields(T instance);
}

View File

@@ -1,69 +0,0 @@
/*
* 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.inject;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.states.*;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
public class OpsuDanceInjector extends Injector {
protected void configure() {
bind(Configuration.class).asEagerSingleton();
bind(OptionsService.class).asLazySingleton();
bind(ReplayImporter.class).asLazySingleton();
bind(OszUnpacker.class).asLazySingleton();
bind(BeatmapParser.class).asLazySingleton();
bind(Updater.class).asLazySingleton();
bind(SkinService.class).asEagerSingleton();
//bind(PreStartupInitializer.class).asEagerSingleton();
bind(DisplayContainer.class).asEagerSingleton();
bind(ErrorHandler.class).asEagerSingleton();
bind(FpsRenderState.class).asEagerSingleton();
bind(BarNotificationState.class).asEagerSingleton();
bind(BubNotifState.class).asEagerSingleton();
bind(GameObjectRenderer.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

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -19,11 +19,15 @@ package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.SkinChangedListener;
import java.io.StringWriter;
public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener {
import static yugecin.opsudance.core.InstanceContainer.*;
public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener,
SkinChangedListener
{
/**
* state is dirty when resolution or skin changed but hasn't rendered yet
*/
@@ -31,7 +35,8 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListe
private boolean isCurrentState;
public BaseOpsuState() {
ResolutionChangedListener.EVENT.addListener(this);
displayContainer.addResolutionChangedListener(this);
skinservice.addSkinChangedListener(this);
}
protected void revalidate() {
@@ -48,9 +53,18 @@ public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListe
@Override
public void render(Graphics g) {
}
@Override
public void onSkinChanged(String name) {
makeDirty();
}
@Override
public void onResolutionChanged(int w, int h) {
makeDirty();
}
private void makeDirty() {
if (isCurrentState) {
revalidate();
return;

View File

@@ -130,7 +130,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState {
public void preRenderUpdate() {
super.preRenderUpdate();
for (Component component : components) {
component.updateHover(displayContainer.mouseX, displayContainer.mouseY);
component.updateHover(mouseX, mouseY);
component.preRenderUpdate();
}
for (OverlayOpsuState overlay : overlays) {

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -33,6 +33,10 @@ public abstract class OverlayOpsuState implements OpsuState {
public void show() {
acceptInput = active = true;
}
public boolean isActive() {
return this.active;
}
@Override
public final void update() {

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -21,17 +21,17 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import java.util.Formatter;
import java.util.List;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
import static yugecin.opsudance.core.InstanceContainer.*;
public class BarNotificationState implements BarNotifListener, ResolutionChangedListener {
public class BarNotificationState implements ResolutionChangedListener {
private final int IN_TIME = 200;
private final int DISPLAY_TIME = 5700 + IN_TIME;
private final int DISPLAY_TIME = 2700 + IN_TIME;
private final int OUT_TIME = 200;
private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
@@ -51,21 +51,19 @@ public class BarNotificationState implements BarNotifListener, ResolutionChanged
this.bgcol = new Color(Color.black);
this.textCol = new Color(Color.white);
this.timeShown = TOTAL_TIME;
BarNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
if (timeShown >= TOTAL_TIME) {
return;
}
timeShown += displayContainer.renderDelta;
timeShown += renderDelta;
processAnimations();
g.setColor(bgcol);
g.fillRect(0, displayContainer.height / 2 - barHalfHeight, displayContainer.width, barHalfHeight * 2);
g.fillRect(0, height2 - barHalfHeight, width, barHalfHeight * 2);
int y = textY;
for (String line : lines) {
Fonts.LARGE.drawString((displayContainer.width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol);
Fonts.LARGE.drawString((width - Fonts.LARGE.getWidth(line)) / 2, y, line, textCol);
y += Fonts.LARGE.getLineHeight();
}
}
@@ -90,14 +88,18 @@ public class BarNotificationState implements BarNotifListener, ResolutionChanged
}
private void calculatePosition() {
this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (displayContainer.width * 0.96f), true);
this.lines = Fonts.wrap(Fonts.LARGE, message, (int) (width * 0.96f), true);
int textHeight = (int) (Fonts.LARGE.getLineHeight() * (lines.size() + 0.5f));
textY = (displayContainer.height - textHeight) / 2 + (int) (Fonts.LARGE.getLineHeight() / 5f);
textY = (height - textHeight) / 2 + (int) (Fonts.LARGE.getLineHeight() / 5f);
barHalfTargetHeight = textHeight / 2;
}
@SuppressWarnings("resource")
public void sendf(String format, Object... args) {
this.send(new Formatter().format(format, args).toString());
}
@Override
public void onBarNotif(String message) {
public void send(String message) {
this.message = message;
calculatePosition();
timeShown = 0;

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -22,16 +22,16 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.MouseListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import java.util.Formatter;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import static yugecin.opsudance.core.InstanceContainer.*;
public class BubNotifState implements MouseListener, BubNotifListener, ResolutionChangedListener {
public class BubNotifState implements MouseListener, ResolutionChangedListener {
public static final int IN_TIME = 633;
public static final int DISPLAY_TIME = 7000 + IN_TIME;
@@ -46,8 +46,6 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio
public BubNotifState() {
this.bubbles = new LinkedList<>();
this.addAnimationTime = IN_TIME;
BubNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
@@ -55,7 +53,7 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio
if (!iter.hasNext()) {
return;
}
addAnimationTime += displayContainer.renderDelta;
addAnimationTime += renderDelta;
if (addAnimationTime > IN_TIME) {
finishAddAnimation();
}
@@ -65,7 +63,7 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio
if (animateUp && addAnimationTime < IN_TIME) {
next.y = next.baseY - (int) (addAnimationHeight * AnimationEquation.OUT_QUINT.calc((float) addAnimationTime / IN_TIME));
}
if (next.render(g, displayContainer.mouseX, displayContainer.mouseY, displayContainer.renderDelta)) {
if (next.render(g, mouseX, mouseY, renderDelta)) {
iter.remove();
}
animateUp = true;
@@ -74,10 +72,10 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio
private void calculatePositions() {
// if width is 0, attempting to wrap it will result in infinite loop
Notification.width = Math.max(50, (int) (displayContainer.width * 0.1703125f));
Notification.baseLine = (int) (displayContainer.height * 0.9645f);
Notification.paddingY = (int) (displayContainer.height * 0.0144f);
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
Notification.width = Math.max(50, (int) (width * 0.1703125f));
Notification.baseLine = (int) (height * 0.9645f);
Notification.paddingY = (int) (height * 0.0144f);
Notification.finalX = width - Notification.width - (int) (width * 0.01);
Notification.fontPaddingX = (int) (Notification.width * 0.02f);
Notification.fontPaddingY = (int) (Fonts.SMALLBOLD.getLineHeight() / 4f);
Notification.lineHeight = Fonts.SMALLBOLD.getLineHeight();
@@ -111,8 +109,12 @@ public class BubNotifState implements MouseListener, BubNotifListener, Resolutio
addAnimationTime = IN_TIME;
}
@Override
public void onBubNotif(String message, Color borderColor) {
@SuppressWarnings("resource")
public void sendf(Color borderColor, String format, Object... args) {
this.send(borderColor, new Formatter().format(format, args).toString());
}
public void send(Color borderColor, String message) {
finishAddAnimation();
Notification newBubble = new Notification(message, borderColor);
bubbles.add(0, newBubble);

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -24,7 +24,7 @@ import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.utils.FPSMeter;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
import static yugecin.opsudance.core.InstanceContainer.*;
public class FpsRenderState implements ResolutionChangedListener {
@@ -42,7 +42,6 @@ public class FpsRenderState implements ResolutionChangedListener {
public FpsRenderState() {
fpsMeter = new FPSMeter(10);
upsMeter = new FPSMeter(10);
ResolutionChangedListener.EVENT.addListener(this);
}
public void update() {
@@ -50,7 +49,7 @@ public class FpsRenderState implements ResolutionChangedListener {
}
public void render(Graphics g) {
fpsMeter.update(displayContainer.renderDelta);
fpsMeter.update(renderDelta);
if (!OPTION_SHOW_FPS.state) {
return;
}
@@ -61,7 +60,7 @@ public class FpsRenderState implements ResolutionChangedListener {
}
private String getText(int value, String unit) {
if (OPTION_USE_FPS_DELTAS.state) {
if (OPTION_USE_FPS_DELTAS.state || value > 1000) {
return String.format("%.2fms", 1000f / value);
}
return value + " " + unit;
@@ -91,8 +90,8 @@ public class FpsRenderState implements ResolutionChangedListener {
@Override
public void onResolutionChanged(int w, int h) {
singleHeight = Fonts.SMALL.getLineHeight();
x = displayContainer.width - 3;
y = displayContainer.height - 3 - singleHeight - 10;
x = width - 3;
y = height - 3 - singleHeight - 10;
}
}

View File

@@ -1,28 +0,0 @@
/*
* 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.events;
import yugecin.opsudance.core.events.Event;
public interface BarNotifListener {
Event<BarNotifListener> EVENT = new Event<>(BarNotifListener.class);
void onBarNotif(String message);
}

View File

@@ -1,30 +0,0 @@
/*
* 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.events;
import org.newdawn.slick.Color;
import yugecin.opsudance.core.events.Event;
@SuppressWarnings({"UnnecessaryInterfaceModifier", "unused"})
public interface BubNotifListener {
Event<BubNotifListener> EVENT = new Event<>(BubNotifListener.class);
void onBubNotif(String message, Color borderColor);
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -17,12 +17,7 @@
*/
package yugecin.opsudance.events;
import yugecin.opsudance.core.events.Event;
public interface ResolutionChangedListener {
Event<ResolutionChangedListener> EVENT = new Event<>(ResolutionChangedListener.class);
public interface ResolutionChangedListener
{
void onResolutionChanged(int w, int h);
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -17,12 +17,7 @@
*/
package yugecin.opsudance.events;
import yugecin.opsudance.core.events.Event;
public interface SkinChangedListener {
Event<SkinChangedListener> EVENT = new Event<>(SkinChangedListener.class);
void onSkinChanged(String stringName);
public interface SkinChangedListener
{
void onSkinChanged(String name);
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -64,7 +64,7 @@ public class CircleMover extends Mover {
double a = ang + SOME_CONSTANT * t;
pos[0] = (startX + (endX - startX) * t) - middlexoffset - Math.cos(a) * radius;
pos[1] = (startY + (endY - startY) * t) - middleyoffset - Math.sin(a) * radius;
if (pos[0] < 0 || displayContainer.width < pos[0] || pos[1] < 0 || displayContainer.height < pos[1]) {
if (pos[0] < 0 || width < pos[0] || pos[1] < 0 || height < pos[1]) {
pass = false;
break;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -44,8 +44,8 @@ public class ExgonMover extends Mover {
pos[0] = endX;
pos[1] = endY;
} else {
pos[0] = randgen.nextInt(displayContainer.width);
pos[1] = randgen.nextInt(displayContainer.height);
pos[0] = randgen.nextInt(width);
pos[1] = randgen.nextInt(height);
}
}
return pos;

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -102,8 +102,8 @@ public class AutoMoverFactory implements MoverFactory {
}
private boolean checkBounds( double[] pos ) {
return 0 < pos[0] && pos[0] < displayContainer.width - displayContainer.width / 8 &&
0 < pos[1] && pos[1] < displayContainer.height - displayContainer.height / 8;
return 0 < pos[0] && pos[0] < width - width / 8 &&
0 < pos[1] && pos[1] < height - height / 8;
}
@Override

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -24,13 +24,10 @@ import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.ui.Colors;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.utils.ManifestWrapper;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -42,13 +39,12 @@ import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Configuration {
public final boolean USE_XDG;
public final File CONFIG_DIR;
public final File DATA_DIR;
public final File CACHE_DIR;
@@ -73,12 +69,10 @@ public class Configuration {
public File replayImportDir;
public File skinRootDir;
public Configuration(ManifestWrapper jarmanifest) {
USE_XDG = jarmanifest.valueOrDefault(null, "Use-XDG", "").equalsIgnoreCase("true");
CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config");
DATA_DIR = getXDGBaseDir("XDG_DATA_HOME", ".local/share");
CACHE_DIR = getXDGBaseDir("XDG_CACHE_HOME", ".cache");
public Configuration() {
CONFIG_DIR = env.workingdir;
DATA_DIR = env.workingdir;
CACHE_DIR = env.workingdir;
BEATMAP_DIR = new File(DATA_DIR, "Songs/");
SKIN_ROOT_DIR = new File(DATA_DIR, "Skins/");
@@ -155,7 +149,7 @@ public class Configuration {
}
if (!defaultDir.isDirectory() && !defaultDir.mkdir()) {
String msg = String.format("Failed to create %s directory at '%s'.", kind, defaultDir.getAbsolutePath());
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_RED);
bubNotifs.send(BUB_RED, msg);
}
return defaultDir;
}
@@ -175,39 +169,6 @@ public class Configuration {
return loadDirectory(dir, defaultDir, kind);
}
/**
* Returns the directory based on the XDG base directory specification for
* Unix-like operating systems, only if the "XDG" flag is enabled.
* @param envvar the environment variable to check (XDG_*_*)
* @param fallback the fallback directory relative to ~home
* @return the XDG base directory, or the working directory if unavailable
*/
private File getXDGBaseDir(String envvar, String fallback) {
if (!USE_XDG) {
return env.workingdir;
}
String OS = System.getProperty("os.name").toLowerCase();
if (OS.indexOf("nix") == -1 && OS.indexOf("nux") == -1 && OS.indexOf("aix") == -1){
return env.workingdir;
}
String rootPath = System.getenv(envvar);
if (rootPath == null) {
String home = System.getProperty("user.home");
if (home == null) {
return new File("./");
}
rootPath = String.format("%s/%s", home, fallback);
}
File dir = new File(rootPath, "opsu");
if (!dir.isDirectory() && !dir.mkdir()) {
explode(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath),
new Exception("empty"), PREVENT_REPORT);
}
return dir;
}
/**
* @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots
*/
@@ -215,9 +176,11 @@ public class Configuration {
// TODO: get a decent place for this
// create the screenshot directory
if (!screenshotDir.isDirectory() && !screenshotDir.mkdir()) {
BubNotifListener.EVENT.make().onBubNotif(
String.format( "Failed to create screenshot directory at '%s'.",
screenshotDir.getAbsolutePath()), Colors.BUB_RED);
bubNotifs.sendf(
BUB_RED,
"Failed to create screenshot directory at '%s'.",
screenshotDir.getAbsolutePath()
);
return;
}
@@ -251,13 +214,13 @@ public class Configuration {
}
}
ImageIO.write(image, OPTION_SCREENSHOT_FORMAT.getValueString().toLowerCase(), file);
BubNotifListener.EVENT.make().onBubNotif("Created " + fileName,
Colors.BUB_PURPLE);
bubNotifs.send(BUB_PURPLE, "Created " + fileName);
} catch (Exception e) {
Log.error("Could not take screenshot", e);
BubNotifListener.EVENT.make().onBubNotif(
"Failed to take a screenshot. See log file for details",
Colors.BUB_PURPLE);
bubNotifs.send(
BUB_PURPLE,
"Failed to take a screenshot. See log file for details"
);
}
}
}.start();

View File

@@ -19,6 +19,8 @@ package yugecin.opsudance.options;
public abstract class ListOption extends Option {
public Runnable observer;
public ListOption(String name, String configurationName, String description) {
super(name, configurationName, description);
}
@@ -30,4 +32,8 @@ public abstract class ListOption extends Option {
public abstract Object[] getListItems();
public abstract void clickListItem(int index);
protected final void onChange() {
observer.run();
}
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -18,8 +18,9 @@
package yugecin.opsudance.options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.ui.Colors;
import yugecin.opsudance.events.BubNotifListener;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class NumericOption extends Option {
@@ -53,8 +54,7 @@ public class NumericOption extends Option {
try {
val = Utils.clamp(Integer.parseInt(s), min, max);
} catch (Exception ignored) {
BubNotifListener.EVENT.make().onBubNotif("Failed to parse " + configurationName + " option",
Colors.BUB_RED);
bubNotifs.send(BUB_RED, "Failed to parse '" + configurationName + "' option");
}
}

View File

@@ -75,6 +75,14 @@ public class Option {
return false;
}
filtered = !name.toLowerCase().contains(searchString) && !description.toLowerCase().contains(searchString);
if (this instanceof ListOption) {
for (Object itm : ((ListOption) this).getListItems()) {
if (itm != null && itm.toString().toLowerCase().contains(searchString)) {
filtered = false;
return false;
}
}
}
return filtered;
}

View File

@@ -42,6 +42,7 @@ public class OptionGroups {
OPTION_TARGET_FPS,
OPTION_SHOW_FPS,
OPTION_USE_FPS_DELTAS,
OPTION_STARFOUNTAINS,
OPTION_SCREENSHOT_FORMAT,
}),
new OptionTab("SLIDER OPTIONS", new Option[]{
@@ -181,7 +182,8 @@ public class OptionGroups {
};
public static final OptionTab[] storyboardOptions = new OptionTab[] {
new OptionTab("Gameplay", new Option[] {
new OptionTab("Gameplay", GameImage.MENU_NAV_GAMEPLAY),
new OptionTab("GENERAL", new Option[] {
OPTION_BACKGROUND_DIM,
OPTION_DANCE_REMOVE_BG,
OPTION_SNAKING_SLIDERS,
@@ -192,12 +194,14 @@ public class OptionGroups {
OPTION_SHOW_PERFECT_HIT,
OPTION_SHOW_FOLLOW_POINTS,
}),
new OptionTab("Input", new Option[] {
new OptionTab("Input", GameImage.MENU_NAV_INPUT),
new OptionTab("INPUT", new Option[] {
OPTION_CURSOR_SIZE,
OPTION_NEW_CURSOR,
OPTION_DISABLE_CURSOR
}),
new OptionTab("Dance", new Option[] {
new OptionTab("Dance", GameImage.MENU_NAV_DANCE),
new OptionTab("MOVER", new Option[]{
OPTION_DANCE_MOVER,
OPTION_DANCE_EXGON_DELAY,
OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS,
@@ -206,36 +210,56 @@ public class OptionGroups {
OPTION_DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
OPTION_DANCE_MOVER_DIRECTION,
OPTION_DANCE_SLIDER_MOVER_TYPE,
}),
new OptionTab("SPINNER", new Option[]{
OPTION_DANCE_SPINNER,
OPTION_DANCE_SPINNER_DELAY,
}),
new OptionTab("SLIDER OPTIONS", new Option[]{
OPTION_DANCE_LAZY_SLIDERS,
OPTION_DANCE_CIRCLE_STREAMS,
OPTION_DANCE_ONLY_CIRCLE_STACKS,
OPTION_DANCE_CIRLCE_IN_SLOW_SLIDERS,
OPTION_DANCE_CIRLCE_IN_LAZY_SLIDERS,
}),
new OptionTab("CIRCLE MOVEMENTS", new Option[]{
OPTION_DANCE_CIRCLE_STREAMS,
OPTION_DANCE_ONLY_CIRCLE_STACKS,
}),
new OptionTab("MIRROR", new Option[] {
OPTION_DANCE_MIRROR,
}),
new OptionTab("Dance display", new Option[] {
new OptionTab("Advanced Display", GameImage.MENU_NAV_ADVANCED),
new OptionTab("OBJECTS", new Option[]{
OPTION_DANCE_DRAW_APPROACH,
OPTION_DANCE_OBJECT_COLOR_OVERRIDE,
OPTION_DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
OPTION_DANCE_RGB_OBJECT_INC,
OPTION_DANCE_HIDE_OBJECTS,
}),
new OptionTab("CURSOR", new Option[]{
OPTION_DANCE_CURSOR_COLOR_OVERRIDE,
OPTION_DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL,
OPTION_DANCE_RGB_CURSOR_INC,
OPTION_DANCE_CURSOR_TRAIL_OVERRIDE,
OPTION_DANCE_HIDE_OBJECTS,
OPTION_DANCE_HIDE_UI,
}),
new OptionTab ("Pippi", new Option[] {
new OptionTab("MISC", new Option[] {
OPTION_DANCE_HIDE_UI,
OPTION_DANCE_REMOVE_BG,
OPTION_DANCE_ENABLE_SB,
}),
new OptionTab ("Pippi", GameImage.MENU_NAV_PIPPI),
new OptionTab ("GENERAL", new Option[]{
OPTION_PIPPI_ENABLE,
OPTION_PIPPI_RADIUS_PERCENT,
}),
new OptionTab ("ANGLE MULTIPLIERS", new Option[]{
OPTION_PIPPI_ANGLE_INC_MUL,
OPTION_PIPPI_ANGLE_INC_MUL_SLIDER,
}),
new OptionTab ("MISC", new Option[] {
OPTION_PIPPI_SLIDER_FOLLOW_EXPAND,
OPTION_PIPPI_PREVENT_WOBBLY_STREAMS,
})
}),
};
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -27,7 +27,6 @@ import org.newdawn.slick.SlickException;
import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.*;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.movers.factories.ExgonMoverFactory;
import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory;
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
@@ -128,6 +127,8 @@ public class Options {
public static final ToggleOption OPTION_NOSINGLEINSTANCE = new ToggleOption("-", "NoSingleInstance", "-", false);
public static final ToggleOption OPTION_STARFOUNTAINS = new ToggleOption("Star fountains in main menu", "StarFountains", "Show star bursts in main menu", true);
// in-game options
public static final ListOption OPTION_SCREEN_RESOLUTION = new ListOption("Screen Resolution", "ScreenResolution", "Change the size of the game.") {
private final String[] resolutions = {
@@ -167,6 +168,7 @@ public class Options {
public void clickListItem(int index){
idx = index;
displayContainer.updateDisplayMode(resolutions[idx]);
this.onChange();
}
@Override
@@ -185,7 +187,14 @@ public class Options {
};
public static final ToggleOption OPTION_ALLOW_LARGER_RESOLUTIONS = new ToggleOption("Allow large resolutions", "AllowLargeRes", "Allow resolutions larger than the native resolution", false);
public static final ToggleOption OPTION_FULLSCREEN = new ToggleOption("Fullscreen Mode", "Fullscreen", "Restart to apply changes.", false);
public static final ToggleOption OPTION_FULLSCREEN = new ToggleOption("Fullscreen Mode", "Fullscreen", "Fullscreen mode", false) {
@Override
public void toggle()
{
super.toggle();
displayContainer.updateDisplayMode(width, height);
}
};
public static final ListOption OPTION_SKIN = new ListOption("Skin", "Skin", "Change how the game looks.") {
@Override
@@ -202,6 +211,7 @@ public class Options {
public void clickListItem(int index){
skinservice.usedSkinName = skinservice.availableSkinDirectories[index];
skinservice.reloadSkin();
this.onChange();
}
@Override
@@ -215,16 +225,34 @@ public class Options {
}
};
public static final NumericOption OPTION_TARGET_UPS = new NumericOption("target UPS", "targetUPS", "Higher values result in less input lag and smoother cursor trail, but may cause high CPU usage.", 480, 20, 1000) {
public static final int[] targetUPS = { 60, 120, 240, 480, 960, 1000, -1 };
public static final NumericOption OPTION_TARGET_UPS = new NumericOption("target UPS", "targetUPS", "Higher values result in less input lag and smoother cursor trail, but may cause high CPU usage.", 2, 0, targetUPS.length - 1) {
@Override
public String getValueString () {
return String.format("%dups", val);
if (targetUPS[val] == -1) {
return "unlimited";
}
return String.valueOf(targetUPS[val]);
}
@Override
public void setValue ( int value){
public void setValue(int value) {
if (value < 0 || targetUPS.length <= value) {
return;
}
final int ups = targetUPS[value];
final int fps = targetFPS[targetFPSIndex];
super.setValue(value);
displayContainer.setUPS(value);
displayContainer.setUPS(ups);
if (ups != -1 && fps > ups) {
for (int i = targetFPSIndex - 1; i >= 0; i--) {
if (targetFPS[i] >= ups) {
OPTION_TARGET_FPS.clickListItem(i);
break;
}
}
}
}
};
@@ -256,7 +284,17 @@ public class Options {
@Override
public void clickListItem(int index){
targetFPSIndex = index;
displayContainer.setFPS(targetFPS[targetFPSIndex]);
int fps = targetFPS[targetFPSIndex];
displayContainer.setFPS(fps);
if (targetUPS[OPTION_TARGET_UPS.val] < fps) {
for (int i = 0; i < targetUPS.length; i++) {
if (targetUPS[i] >= fps) {
OPTION_TARGET_UPS.setValue(i);
break;
}
}
}
this.onChange();
}
@Override
@@ -318,6 +356,7 @@ public class Options {
@Override
public void clickListItem(int index){
this.index = index;
this.onChange();
}
@Override
@@ -386,7 +425,7 @@ public class Options {
public static final NumericOption OPTION_EFFECT_VOLUME = new NumericOption("Effects", "VolumeEffect", "Volume of menu and game sounds.", 70, 0, 100);
public static final NumericOption OPTION_HITSOUND_VOLUME = new NumericOption("Hit Sounds", "VolumeHitSound", "Volume of hit sounds.", 30, 0, 100);
public static final NumericOption OPTION_MUSIC_OFFSET = new NumericOption("Music Offset", "Offset", "Adjust this value if hit objects are out of sync.", -75, -500, 500) {
public static final NumericOption OPTION_MUSIC_OFFSET = new NumericOption("Global Music Offset", "Offset", "Adjust this value if hit objects are out of sync.", -75, -500, 500) {
@Override
public String getValueString () {
return String.format("%dms", val);
@@ -439,7 +478,7 @@ public class Options {
public static final ToggleOption OPTION_DISABLE_MOUSE_BUTTONS = new ToggleOption("Disable mouse buttons in play mode", "MouseDisableButtons", "This option will disable all mouse buttons. Specifically for people who use their keyboard to click.", false) {
@Override
public void toggle() {
BarNotifListener.EVENT.make().onBarNotif(state ?
barNotifs.send(state ?
"Mouse buttons are disabled." : "Mouse buttons are enabled.");
}
};
@@ -608,10 +647,11 @@ public class Options {
public void clickListItem(int index){
if (Game.isInGame && Dancer.moverFactories[index] instanceof PolyMoverFactory) {
// TODO remove this when #79 is fixed
BarNotifListener.EVENT.make().onBarNotif("This mover is disabled in the storyboard right now");
barNotifs.send("This mover is disabled in the storyboard right now");
return;
}
Dancer.instance.setMoverFactoryIndex(index);
this.onChange();
}
@Override
@@ -701,6 +741,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.moverDirection = MoverDirection.values()[index];
this.onChange();
}
@Override
@@ -731,6 +772,7 @@ public class Options {
public void clickListItem(int index){
val = index;
Dancer.sliderMoverController = Dancer.sliderMovers[index];
this.onChange();
}
@Override
@@ -753,6 +795,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.instance.setSpinnerIndex(index);
this.onChange();
}
@Override
@@ -797,6 +840,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.colorOverride = ObjectColorOverrides.values()[index];
this.onChange();
}
@Override
@@ -824,6 +868,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.colorMirrorOverride = ObjectColorOverrides.values()[index];
this.onChange();
}
@Override
@@ -858,6 +903,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.cursorColorOverride = CursorColorOverrides.values()[index];
this.onChange();
}
@Override
@@ -885,6 +931,7 @@ public class Options {
@Override
public void clickListItem(int index){
Dancer.cursorColorMirrorOverride = CursorColorOverrides.values()[index];
this.onChange();
}
@Override

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -17,15 +17,14 @@
*/
package yugecin.opsudance.options;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.events.BubNotifListener;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import static itdelatrisu.opsu.ui.Colors.*;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
@@ -80,7 +79,7 @@ public class OptionsService {
} catch (IOException e) {
String err = String.format("Failed to read option file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
config.loadDirectories();
}
@@ -109,7 +108,7 @@ public class OptionsService {
} catch (IOException e) {
String err = String.format("Failed to write to file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
bubNotifs.send(BUB_RED, err);
}
}

View File

@@ -28,6 +28,7 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import yugecin.opsudance.skinning.SkinService;
import static itdelatrisu.opsu.GameImage.*;
import static yugecin.opsudance.options.Options.*;
public class GameObjectRenderer {
@@ -88,7 +89,8 @@ public class GameObjectRenderer {
public void renderComboNumberOnly(float x, float y, int number, float alpha) {
if (number > 0) {
gameData.drawSymbolNumber(number, x, y, GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / gameData.getDefaultSymbolImage(0).getHeight(), alpha);
float scale = HITCIRCLE.getWidth() * 0.40f / gameData.getDefaultSymbolImage(0).getHeight();
gameData.drawSymbolNumber(number, x, y, scale, alpha);
}
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -23,9 +23,7 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.sbv2.movers.CubicStoryboardMover;
import yugecin.opsudance.sbv2.movers.LinearStoryboardMover;
import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover;
@@ -35,9 +33,9 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MoveStoryboard extends OverlayOpsuState{
import static yugecin.opsudance.core.InstanceContainer.*;
private final DisplayContainer displayContainer;
public class MoveStoryboard extends OverlayOpsuState{
private SimpleButton btnAddLinear;
private SimpleButton btnAddQuadratic;
@@ -56,8 +54,7 @@ public class MoveStoryboard extends OverlayOpsuState{
private int trackPosition;
public MoveStoryboard(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public MoveStoryboard() {
dummyMove = (StoryboardMove) Proxy.newProxyInstance(StoryboardMove.class.getClassLoader(), new Class<?>[]{StoryboardMove.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
@@ -70,12 +67,12 @@ public class MoveStoryboard extends OverlayOpsuState{
public void revalidate() {
super.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);
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);
}
/**
@@ -97,8 +94,8 @@ public class MoveStoryboard extends OverlayOpsuState{
@Override
protected void onPreRenderUpdate() {
int x = displayContainer.mouseX;
int y = displayContainer.mouseY;
int x = mouseX;
int y = mouseY;
btnAddLinear.update(x, y);
btnAddQuadratic.update(x, y);
btnAddCubic.update(x, y);
@@ -106,7 +103,7 @@ public class MoveStoryboard extends OverlayOpsuState{
btnAnimMid.update(x, y);
btnAnimCub.update(x, y);
if (moves[objectIndex] != null) {
moves[objectIndex].update(displayContainer.renderDelta, x, y);
moves[objectIndex].update(renderDelta, x, y);
}
}
@@ -185,11 +182,11 @@ public class MoveStoryboard extends OverlayOpsuState{
private StoryboardMove getCurrentMoveOrCreateNew() {
if (gameObjects[objectIndex].isSlider() && trackPosition > gameObjects[objectIndex].getTime() && trackPosition < gameObjects[objectIndex].getEndTime()) {
BarNotifListener.EVENT.make().onBarNotif("Wait until the slider ended");
barNotifs.send("Wait until the slider ended");
return dummyMove;
}
if (moves[objectIndex] == null) {
return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, displayContainer.width);
return moves[objectIndex] = new StoryboardMoveImpl(gameObjects[objectIndex - 1].end, gameObjects[objectIndex].start, width);
}
return moves[objectIndex];
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -26,21 +26,39 @@ import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.events.SkinChangedListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class SkinService {
public class SkinService
{
private final List<SkinChangedListener> skinChangedListeners;
public String[] availableSkinDirectories;
public String usedSkinName = "Default";
public static Skin skin;
public SkinService()
{
this.skinChangedListeners = new ArrayList<>();
}
public void addSkinChangedListener(SkinChangedListener l)
{
this.skinChangedListeners.add(l);
}
public void reloadSkin() {
public void reloadSkin()
{
loadSkin();
SoundController.init();
SkinChangedListener.EVENT.make().onSkinChanged(usedSkinName);
for (SkinChangedListener l : this.skinChangedListeners) {
l.onSkinChanged(this.usedSkinName);
}
}
/**

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -38,10 +38,10 @@ public class ApproachCircleSpinner extends Spinner {
ang += 15;
}
double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS);
double rad = width / 4.0f * (1d - Spinner.PROGRESS);
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
point[0] = width2 + rad * Math.sin(ang / 180d * Math.PI);
point[1] = height2 - rad * Math.cos(ang / 180d * Math.PI);
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -41,30 +41,30 @@ public class BeamSpinner extends Spinner {
index = ++index % 4;
final int MOD = 60;
point[0] = displayContainer.width / 2d;
point[1] = displayContainer.height / 2d;
point[0] = width2;
point[1] = height2;
if( index == 0 ) {
add( MOD, 90 );
add( MOD, 180 );
} else if( index == 1 ) {
add( MOD, 90 );
add( displayContainer.height / 2 * 0.8d, 0 );
} else if( index == 2 ) {
add( MOD, -90 );
add( displayContainer.height / 2 * 0.8d, 0 );
} else if( index == 3 ) {
add( MOD, -90 );
add( MOD, 180 );
if (index == 0) {
add(MOD, 90);
add(MOD, 180);
} else if (index == 1) {
add(MOD, 90);
add(height2 * 0.8d, 0);
} else if (index == 2) {
add(MOD, -90);
add(height2 * 0.8d, 0);
} else if (index == 3) {
add(MOD, -90);
add(MOD, 180);
ang += 0.3;
}
return point;
}
private void add( double rad, double ang ) {
point[0] += rad * Math.cos( (this.ang + ang) / 180d * Math.PI);
point[1] -= rad * Math.sin( (this.ang + ang) / 180d * Math.PI);
private void add (double rad, double ang ) {
point[0] += rad * Math.cos((this.ang + ang) / 180d * Math.PI);
point[1] -= rad * Math.sin((this.ang + ang) / 180d * Math.PI);
}
@Override

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -36,10 +36,10 @@ public class CircleSpinner extends Spinner {
ang += 15;
}
double rad = displayContainer.width / 4.0f;
double rad = width / 4.0f;
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
point[0] = width2 + rad * Math.sin(ang / 180d * Math.PI);
point[1] = height2 - rad * Math.cos(ang / 180d * Math.PI);
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
import static yugecin.opsudance.core.InstanceContainer.*;
public class CubeSpinner extends Spinner {
@@ -90,10 +90,10 @@ public class CubeSpinner extends Spinner {
x *= 3.0d / ( z + 3.0d + 5.0d + 0.5 );
y *= 3.0d / ( z + 3.0d + 5.0d + 0.5 );
double scale = displayContainer.width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI));
double scale = width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI));
//double scale = Options.width / (3.0f + -1f * ((float)(Options.s.ElapsedMilliseconds % Options.beatTimeMs)/(float)Options.beatTimeMs));
point[0] = (int) ( displayContainer.width / 2.0f + scale * x );
point[1] = (int) ( displayContainer.height / 2.0f - scale * y );
point[0] = (int) (width2 + scale * x );
point[1] = (int) (height2 - scale * y );
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -38,10 +38,10 @@ public class DonutSpinner extends Spinner {
ang += 15;
}
double rad = displayContainer.width / 4.0f;
double rad = width / 4.0f;
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang);
point[0] = width2 + rad * Math.sin(ang);
point[1] = height2 - rad * Math.cos(ang);
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -39,12 +39,12 @@ public class FivePointStarApproachSpinner extends Spinner {
odd = !odd;
}
double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS);
double rad = width / 4.0f * (1d - Spinner.PROGRESS);
if (!odd) {
rad /= 3d;
}
point[0] = displayContainer.width / 2d + Math.cos(ang) * rad;
point[1] = displayContainer.height / 2d + Math.sin(ang) * rad;
point[0] = width2 + Math.cos(ang) * rad;
point[1] = height2 + Math.sin(ang) * rad;
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -24,11 +24,9 @@ public class FivePointStarSpinner extends Spinner {
@Override
public void init() {
double[][] points = new double[10][];
double midx = displayContainer.width / 2d;
double midy = displayContainer.height / 2d;
double angleIncRads = Math.PI * 36d / 180d;
double ang = -Math.PI / 2d;
double maxrad = displayContainer.width / 4d;
double maxrad = width / 4d;
double minrad = maxrad / 3d;
for (int i = 0; i < 10; i++) {
double rad = maxrad;
@@ -36,8 +34,8 @@ public class FivePointStarSpinner extends Spinner {
rad = minrad;
}
points[i] = new double[] {
midx + Math.cos(ang) * rad,
midy + Math.sin(ang) * rad
width2 + Math.cos(ang) * rad,
height2 + Math.sin(ang) * rad
};
ang += angleIncRads;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -17,9 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
import static yugecin.opsudance.core.InstanceContainer.*;
public class HalfCircleSpinner extends Spinner {
@@ -47,8 +45,8 @@ public class HalfCircleSpinner extends Spinner {
skipang += 359;
}
point[0] = displayContainer.width / 2.0d + displayContainer.height / 2 * 0.8d * Math.cos(ang/180d*Math.PI);
point[1] = displayContainer.height / 2.0d + displayContainer.height / 2 * 0.8d * Math.sin(ang/180d*Math.PI);
point[0] = width2 + height2 * 0.8d * Math.cos(ang/180d*Math.PI);
point[1] = height2 + height2 * 0.8d * Math.sin(ang/180d*Math.PI);
return point;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -24,9 +24,9 @@ public class IlluminatiSpinner extends Spinner {
@Override
public void init() {
init( new double[][] {
new double[] { displayContainer.width / 2d - 120, displayContainer.height / 2d + 80 },
new double[] { displayContainer.width / 2d, displayContainer.height / 2d - 160 },
new double[] { displayContainer.width / 2d + 120, displayContainer.height / 2d + 80 }
new double[] { width2 - 120, height2 + 80 },
new double[] { width2, height2 - 160 },
new double[] { width2 + 120, height2 + 80 }
} );
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -17,6 +17,7 @@
*/
package yugecin.opsudance.spinners;
import static java.lang.Math.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class LessThanThreeSpinner extends Spinner {
@@ -24,9 +25,7 @@ public class LessThanThreeSpinner extends Spinner {
private int angle = 0;
@Override
public void init()
{
public void init() {
}
@Override
@@ -37,15 +36,12 @@ public class LessThanThreeSpinner extends Spinner {
}
if( angle > 360 ) angle = 0;
double theta = angle / 180d * Math.PI;
double[] pos = new double[] {
displayContainer.width / 2d,
displayContainer.height / 2d
};
double[] pos = { width2, height2 };
double r = 2 - 2 * Math.sin( theta ) + Math.sin( theta ) * Math.sqrt( Math.abs( Math.cos( theta ) ) ) / ( Math.sin( theta ) + 1.4 );
double r = 2 - 2 * sin(theta) + sin(theta) * sqrt(abs(cos(theta))) / (sin(theta) + 1.4);
pos[0] += Math.cos( theta ) * r * 100;
pos[1] -= Math.sin( theta ) * r * 100 + 100;
pos[0] += Math.cos(theta) * r * 100;
pos[1] -= Math.sin(theta) * r * 100 + 100;
return pos;
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -29,7 +29,7 @@ public class RektCircleSpinner extends Spinner {
@Override
public void init() {
index = 0;
size = displayContainer.height * 0.8d;
size = height * 0.8d;
point = new double[2];
}
@@ -42,20 +42,20 @@ public class RektCircleSpinner extends Spinner {
final int INC = 50;
if( index == 0 ) {
point[0] = displayContainer.width / 2d + size / 2d - pos;
point[1] = displayContainer.height / 2d - size / 2d;
point[0] = width2 + size / 2d - pos;
point[1] = height2 - size / 2d;
index++;
} else if( index == 1 ) {
point[0] = displayContainer.width / 2 - size / 2;
point[1] = displayContainer.height / 2 - size / 2 + pos;
point[0] = width2 - size / 2;
point[1] = height2 - size / 2 + pos;
index++;
} else if( index == 2 ) {
point[0] = displayContainer.width / 2 - size / 2 + pos;
point[1] = displayContainer.height / 2 + size / 2;
point[0] = width2 - size / 2 + pos;
point[1] = height2 + size / 2;
index++;
} else if( index == 3 ) {
point[0] = displayContainer.width / 2 + size / 2;
point[1] = displayContainer.height / 2 + size / 2 - pos;
point[0] = width2 + size / 2;
point[1] = height2 + size / 2 - pos;
pos += INC;
if( pos > size ) {
pos = INC;

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -25,11 +25,11 @@ public class RektSpinner extends Spinner {
public void init() {
init(new double[][] {
{ 10, 10 },
{ displayContainer.width / 2d, 10 },
{ displayContainer.width - 10, 10 },
{ displayContainer.width - 10, displayContainer.height - 10 },
{ displayContainer.width / 2d, displayContainer.height - 10 },
{ 10, displayContainer.height - 10 }
{ width2, 10 },
{ width - 10, 10 },
{ width - 10, height - 10 },
{ width2, height - 10 },
{ 10, height - 10 }
});
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
* Copyright (C) 2017-2018 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
@@ -23,7 +23,8 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.*;
import yugecin.opsudance.core.DisplayContainer;
import static yugecin.opsudance.core.InstanceContainer.*;
public class BackButton {
@@ -80,7 +81,7 @@ public class BackButton {
/** The real button with, determined by the size and animations. */
private int realButtonWidth;
public BackButton(DisplayContainer container) {
public BackButton() {
if (!GameImage.MENU_BACK.hasGameSkinImage()) {
backButton = null;
textWidth = Fonts.MEDIUM.getWidth("back");
@@ -90,7 +91,7 @@ public class BackButton {
paddingY *= 0.736f;
paddingX = paddingY / 2f;
chevronBaseSize = paddingY * 3f / 2f;
buttonYpos = (int) (container.height - paddingY * 4f);
buttonYpos = height - (int) (paddingY * 4f);
slopeImageSize = (int) (paddingY * 3f);
slopeImageSlopeWidth = (int) (slopeImageSize * 0.295f);
firstButtonWidth = slopeImageSize;
@@ -101,10 +102,10 @@ public class BackButton {
if (GameImage.MENU_BACK.getImages() != null) {
Animation back = GameImage.MENU_BACK.getAnimation(120);
backButton = new MenuButton(back, back.getWidth() / 2f, container.height - (back.getHeight() / 2f));
backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f));
} else {
Image back = GameImage.MENU_BACK.getImage();
backButton = new MenuButton(back, back.getWidth() / 2f, container.height - (back.getHeight() / 2f));
backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f));
}
backButton.setHoverAnimationDuration(350);
backButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_BACK);
@@ -171,11 +172,11 @@ public class BackButton {
/**
* Processes a hover action depending on whether or not the cursor
* is hovering over the button.
* @param delta the delta interval
* @param cx the x coordinate
* @param cy the y coordinate
*/
public void hoverUpdate(int delta, int cx, int cy) {
public void hoverUpdate() {
final int delta = renderDelta;
final int cx = mouseX;
final int cy = mouseY;
if (backButton != null) {
backButton.hoverUpdate(delta, cx, cy);
return;

View File

@@ -0,0 +1,62 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2018 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.ui;
import java.awt.geom.Rectangle2D;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
public class ImagePosition extends Rectangle2D.Float {
private Image image;
public ImagePosition(Image image) {
this.image = image;
}
public boolean contains(int x, int y, float alphaThreshold) {
if (!super.contains(x, y)) {
return false;
}
final int ix = x - (int) this.x;
final int iy = y - (int) this.y;
return this.image.getAlphaAt(ix, iy) > alphaThreshold;
}
public float middleX() {
return this.x + this.width / 2;
}
public float middleY() {
return this.y + this.height / 2;
}
public void scale(float scale) {
final float width = this.width * scale;
final float height = this.height * scale;
this.x -= (width - this.width) / 2f;
this.y -= (height - this.height) / 2f;
this.width = width;
this.height = height;
}
public void draw(Color filter) {
this.image.draw(this.x, this.y, this.width, this.height, filter);
}
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -26,8 +26,10 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.*;
import org.newdawn.slick.gui.TextField;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.core.Constants;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.SkinChangedListener;
import yugecin.opsudance.options.*;
import yugecin.opsudance.utils.FontUtil;
@@ -35,11 +37,11 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;
import static itdelatrisu.opsu.GameImage.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class OptionsOverlay extends OverlayOpsuState {
private final DisplayContainer displayContainer;
public class OptionsOverlay implements ResolutionChangedListener, SkinChangedListener {
private static final float BG_ALPHA = 0.7f;
private static final float LINEALPHA = 0.8f;
@@ -60,6 +62,9 @@ public class OptionsOverlay extends OverlayOpsuState {
private static final float INDICATOR_ALPHA = 0.8f;
private static final Color COL_INDICATOR = new Color(Color.black);
private boolean active;
private boolean acceptInput;
private boolean dirty;
/** Duration, in ms, of the show (slide-in) animation. */
private static final int SHOWANIMATIONTIME = 1000;
@@ -102,6 +107,7 @@ public class OptionsOverlay extends OverlayOpsuState {
private int sliderOptionStartX;
private int sliderOptionLength;
private boolean isAdjustingSlider;
private int unchangedSliderValue;
private final HashMap<ListOption, DropdownMenu<Object>> dropdownMenus;
private final LinkedList<DropdownMenu<Object>> visibleDropdownMenus;
@@ -110,8 +116,7 @@ public class OptionsOverlay extends OverlayOpsuState {
private int openDropdownVirtualY;
private int targetWidth;
private int width;
private int height;
private int currentWidth;
private int navButtonSize;
private int navStartY;
@@ -141,8 +146,12 @@ public class OptionsOverlay extends OverlayOpsuState {
private final KineticScrolling scrollHandler;
private int maxScrollOffset;
private int lastOptionHeight;
private int mousePressY;
private boolean isDraggingFromOutside;
private boolean wasPressed;
private boolean keyEntryLeft;
private boolean keyEntryRight;
@@ -161,10 +170,9 @@ public class OptionsOverlay extends OverlayOpsuState {
private int invalidSearchAnimationProgress;
private final int INVALID_SEARCH_ANIMATION_TIME = 500;
public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] sections) {
this.displayContainer = displayContainer;
public OptionsOverlay(OptionTab[] sections) {
this.sections = sections;
this.dirty = true;
dropdownMenus = new HashMap<>();
visibleDropdownMenus = new LinkedList<>();
@@ -174,29 +182,53 @@ public class OptionsOverlay extends OverlayOpsuState {
scrollHandler = new KineticScrolling();
scrollHandler.setAllowOverScroll(true);
displayContainer.addResolutionChangedListener(this);
skinservice.addSkinChangedListener(this);
}
@Override
public void onResolutionChanged(int w, int h) {
this.dirty = true;
if (this.active) {
this.revalidate();
}
}
@Override
public void onSkinChanged(String name) {
this.dirty = true;
if (this.active) {
this.revalidate();
}
}
public boolean isActive() {
return this.active;
}
public boolean containsMouse() {
return this.active && mouseX <= this.currentWidth;
}
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
public void revalidate() {
super.revalidate();
this.dirty = false;
boolean isWidescreen = displayContainer.isWidescreen();
targetWidth = (int) (displayContainer.width * (isWidescreen ? 0.4f : 0.5f));
height = displayContainer.height;
targetWidth = (int) (width * (isWidescreen ? 0.4f : 0.5f));
// calculate positions
float navIconWidthRatio = isWidescreen ? 0.046875f : 0.065f;
// non-widescreen ratio is not accurate
navButtonSize = (int) (displayContainer.width * navIconWidthRatio);
navButtonSize = (int) (width * navIconWidthRatio);
navIndicatorSize = navButtonSize / 10;
navTargetWidth = (int) (targetWidth * 0.45f) - navButtonSize;
paddingRight = (int) (displayContainer.width * 0.009375f); // not so accurate
paddingLeft = navButtonSize + (int) (displayContainer.width * 0.0180f); // not so accurate
paddingTextLeft = paddingLeft + LINEWIDTH + (int) (displayContainer.width * 0.00625f); // not so accurate
paddingRight = (int) (width * 0.009375f); // not so accurate
paddingLeft = navButtonSize + (int) (width * 0.0180f); // not so accurate
paddingTextLeft = paddingLeft + LINEWIDTH + (int) (width * 0.00625f); // not so accurate
optionStartX = paddingTextLeft;
textOptionsY = Fonts.LARGE.getLineHeight() * 2;
textChangeY = textOptionsY + Fonts.LARGE.getLineHeight();
@@ -206,8 +238,8 @@ public class OptionsOverlay extends OverlayOpsuState {
sectionLineHeight = (int) (Fonts.LARGE.getLineHeight() * 1.5f);
if (active) {
width = targetWidth;
optionWidth = width - optionStartX - paddingRight;
currentWidth = targetWidth;
optionWidth = currentWidth - optionStartX - paddingRight;
}
optionHeight = (int) (Fonts.MEDIUM.getLineHeight() * 1.3f);
@@ -215,9 +247,10 @@ public class OptionsOverlay extends OverlayOpsuState {
controlImageSize = (int) (Fonts.MEDIUM.getLineHeight() * 0.7f);
controlImagePadding = (optionHeight - controlImageSize) / 2;
sliderBallImg = GameImage.CONTROL_SLIDER_BALL.getImage().getScaledCopy(controlImageSize, controlImageSize);
checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(controlImageSize, controlImageSize);
checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(controlImageSize, controlImageSize);
final int s = controlImageSize;
sliderBallImg = CONTROL_SLIDER_BALL.getScaledImage(s, s);
checkOnImg = CONTROL_CHECK_ON.getScaledImage(s, s);
checkOffImg = CONTROL_CHECK_OFF.getScaledImage(s, s);
int navTotalHeight = 0;
dropdownMenus.clear();
@@ -232,23 +265,27 @@ public class OptionsOverlay extends OverlayOpsuState {
}
final ListOption listOption = (ListOption) option;
Object[] items = listOption.getListItems();
DropdownMenu<Object> menu = new DropdownMenu<Object>(displayContainer, items, 0, 0, 0) {
DropdownMenu<Object> menu = new DropdownMenu<Object>(items, 0, 0, 0) {
@Override
public void itemSelected(int index, Object item) {
listOption.clickListItem(index);
openDropdownMenu = null;
}
};
// not the best way to determine the selected option AT ALL, but seems like it's the only one right now...
String selectedValue = option.getValueString();
int idx = 0;
for (Object item : items) {
if (item.toString().equals(selectedValue)) {
break;
final Runnable observer = () -> {
// not the best way to determine the selected option AT ALL, but seems like it's the only one right now...
String selectedValue = option.getValueString();
int idx = 0;
for (Object item : items) {
if (item.toString().equals(selectedValue)) {
break;
}
idx++;
}
idx++;
}
menu.setSelectedIndex(idx);
menu.setSelectedIndex(idx);
};
observer.run();
listOption.observer = observer;
menu.setBackgroundColor(COL_BG);
menu.setBorderColor(Color.transparent);
menu.setChevronDownColor(COL_WHITE);
@@ -265,13 +302,16 @@ public class OptionsOverlay extends OverlayOpsuState {
searchImg = GameImage.SEARCH.getImage().getScaledCopy(searchImgSize, searchImgSize);
}
@Override
public void onRender(Graphics g) {
g.setClip(navButtonSize, 0, width - navButtonSize, height);
public void render(Graphics g) {
if (!this.active && this.currentWidth == this.navButtonSize) {
return;
}
g.setClip(navButtonSize, 0, currentWidth - navButtonSize, height);
// bg
g.setColor(COL_BG);
g.fillRect(navButtonSize, 0, width, height);
g.fillRect(navButtonSize, 0, currentWidth, height);
// title
renderTitle();
@@ -291,16 +331,15 @@ public class OptionsOverlay extends OverlayOpsuState {
// scrollbar
g.setColor(COL_WHITE);
g.fillRect(width - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45);
g.fillRect(currentWidth - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45);
g.clearClip();
renderNavigation(g);
// UI
UI.getBackButton().draw(g);
// tooltip
renderTooltip(g);
if (this.active) {
renderTooltip(g);
}
// key input options
if (keyEntryLeft || keyEntryRight) {
@@ -314,7 +353,7 @@ public class OptionsOverlay extends OverlayOpsuState {
navWidth += navTargetWidth;
} else if (navHoverTime > 300) {
AnimationEquation anim = AnimationEquation.IN_EXPO;
if (displayContainer.mouseX < navWidth) {
if (mouseX < navWidth) {
anim = AnimationEquation.OUT_EXPO;
}
float progress = anim.calc((navHoverTime - 300f) / 300f);
@@ -363,7 +402,7 @@ public class OptionsOverlay extends OverlayOpsuState {
g.setColor(COL_INDICATOR);
int indicatorPos = this.indicatorPos;
if (indicatorMoveAnimationTime > 0) {
indicatorMoveAnimationTime += displayContainer.renderDelta;
indicatorMoveAnimationTime += renderDelta;
if (indicatorMoveAnimationTime > INDICATORMOVEANIMATIONTIME) {
indicatorMoveAnimationTime = 0;
indicatorPos += indicatorOffsetToNextPos;
@@ -373,16 +412,16 @@ public class OptionsOverlay extends OverlayOpsuState {
indicatorPos += AnimationEquation.OUT_BACK.calc((float) indicatorMoveAnimationTime / INDICATORMOVEANIMATIONTIME) * indicatorOffsetToNextPos;
}
}
g.fillRect(navButtonSize, indicatorPos - scrollHandler.getPosition(), width, optionHeight);
g.fillRect(navButtonSize, indicatorPos - scrollHandler.getPosition(), currentWidth, optionHeight);
}
private void renderKeyEntry(Graphics g) {
g.setColor(COL_BG);
g.fillRect(0, 0, displayContainer.width, height);
g.fillRect(0, 0, width, height);
g.setColor(COL_WHITE);
String prompt = (keyEntryLeft) ? "Please press the new left-click key." : "Please press the new right-click key.";
int y = (displayContainer.height - Fonts.LARGE.getLineHeight()) / 2;
FontUtil.drawCentered(Fonts.LARGE, displayContainer.width, 0, y, prompt, COL_WHITE);
int y = height2 - Fonts.LARGE.getLineHeight() / 2;
FontUtil.drawCentered(Fonts.LARGE, width, 0, y, prompt, COL_WHITE);
}
private void renderTooltip(Graphics g) {
@@ -391,7 +430,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (hoverOption instanceof NumericOption) {
tip = "(" + hoverOption.getValueString() + ") " + tip;
}
UI.updateTooltip(displayContainer.renderDelta, tip, true);
UI.updateTooltip(renderDelta, tip, true);
UI.drawTooltip(g);
}
}
@@ -413,7 +452,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (section != activeSection) {
COL_CYAN.a *= 0.2f;
}
FontUtil.drawRightAligned(Fonts.XLARGE, width, -paddingRight,
FontUtil.drawRightAligned(Fonts.XLARGE, currentWidth, -paddingRight,
(int) (y + Fonts.XLARGE.getLineHeight() * 0.3f), section.name.toUpperCase(),
COL_CYAN);
COL_CYAN.a = previousAlpha;
@@ -451,6 +490,7 @@ public class OptionsOverlay extends OverlayOpsuState {
g.setColor(COL_GREY);
g.fillRect(paddingLeft, lineStartY, LINEWIDTH, lineHeight);
}
lastOptionHeight = maxScrollOffset;
// iterate over skipped options to correctly calculate max scroll offset
for (; sectionIndex < sections.length; sectionIndex++) {
if (sections[sectionIndex].filtered) {
@@ -569,11 +609,22 @@ public class OptionsOverlay extends OverlayOpsuState {
}
private void renderTitle() {
int textWidth = width - navButtonSize;
int textWidth = currentWidth - navButtonSize;
FontUtil.drawCentered(Fonts.LARGE, textWidth, navButtonSize,
textOptionsY - scrollHandler.getIntPosition(), "Options", COL_WHITE);
FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize,
textChangeY - scrollHandler.getIntPosition(), "Change the way opsu! behaves", COL_PINK);
int y = lastOptionHeight - scrollHandler.getIntPosition();
y += Fonts.LARGE.getLineHeight() * 2.5f;
FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize,
y, Constants.PROJECT_NAME + " " + updater.getCurrentVersion(), COL_WHITE);
y += Fonts.MEDIUM.getLineHeight() * 1.2f;
FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize,
y, Constants.DANCE_REPOSITORY_URI.toString(), COL_WHITE);
y += Fonts.MEDIUM.getLineHeight() * 1.2f;
FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize,
y, Constants.REPOSITORY_URI.toString(), COL_WHITE);
}
private void renderSearch(Graphics g) {
@@ -581,7 +632,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (scrollHandler.getIntPosition() > posSearchY) {
ypos = textSearchYOffset;
g.setColor(COL_BG);
g.fillRect(navButtonSize, 0, width, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight());
g.fillRect(navButtonSize, 0, currentWidth, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight());
}
Color searchCol = COL_WHITE;
float invalidProgress = 0f;
@@ -597,7 +648,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (lastSearchText.length() > 0) {
searchText = lastSearchText;
}
int textWidth = width - navButtonSize;
int textWidth = currentWidth - navButtonSize;
if (invalidSearchAnimationProgress > 0) {
g.rotate(navButtonSize + textWidth / 2, ypos, invalidProgress * invalidSearchTextRotation);
}
@@ -611,33 +662,37 @@ public class OptionsOverlay extends OverlayOpsuState {
g.resetTransform();
}
@Override
public void hide() {
if (!this.active) {
return;
}
acceptInput = active = false;
searchField.setFocused(false);
acceptInput = false;
SoundController.playSound(SoundEffect.MENUBACK);
hideAnimationTime = animationtime;
hideAnimationStartProgress = (float) animationtime / SHOWANIMATIONTIME;
hoverOption = null;
}
@Override
public void show() {
navHoverTime = 0;
indicatorPos = -optionHeight;
indicatorOffsetToNextPos = 0;
indicatorMoveAnimationTime = 0;
indicatorHideAnimationTime = 0;
acceptInput = true;
active = true;
acceptInput = active = true;
animationtime = 0;
resetSearch();
if (this.dirty) {
this.revalidate();
}
}
@Override
public void onPreRenderUpdate() {
int mouseX = displayContainer.mouseX;
int mouseY = displayContainer.mouseY;
int delta = displayContainer.renderDelta;
public void preRenderUpdate() {
if (!this.active && this.currentWidth == this.navButtonSize) {
return;
}
int delta = renderDelta;
int prevscrollpos = scrollHandler.getIntPosition();
scrollHandler.update(delta);
@@ -684,7 +739,6 @@ public class OptionsOverlay extends OverlayOpsuState {
prevMouseY = mouseY;
updateHoverOption(mouseX, mouseY);
updateIndicatorAlpha();
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
if (isAdjustingSlider) {
int sliderValue = ((NumericOption) hoverOption).val;
updateSliderOption();
@@ -716,7 +770,7 @@ public class OptionsOverlay extends OverlayOpsuState {
private void updateIndicatorAlpha() {
if (hoverOption == null) {
if (indicatorHideAnimationTime < INDICATORHIDEANIMATIONTIME) {
indicatorHideAnimationTime += displayContainer.renderDelta;
indicatorHideAnimationTime += renderDelta;
if (indicatorHideAnimationTime > INDICATORHIDEANIMATIONTIME) {
indicatorHideAnimationTime = INDICATORHIDEANIMATIONTIME;
}
@@ -725,7 +779,7 @@ public class OptionsOverlay extends OverlayOpsuState {
COL_INDICATOR.a = (1f - progress) * INDICATOR_ALPHA * showHideProgress;
}
} else if (indicatorHideAnimationTime > 0) {
indicatorHideAnimationTime -= displayContainer.renderDelta * 3;
indicatorHideAnimationTime -= renderDelta * 3;
if (indicatorHideAnimationTime < 0) {
indicatorHideAnimationTime = 0;
}
@@ -737,11 +791,11 @@ public class OptionsOverlay extends OverlayOpsuState {
private void updateShowHideAnimation(int delta) {
if (acceptInput && animationtime >= SHOWANIMATIONTIME) {
// animation already finished
width = targetWidth;
currentWidth = targetWidth;
showHideProgress = 1f;
return;
}
optionWidth = width - optionStartX - paddingRight;
optionWidth = currentWidth - optionStartX - paddingRight;
// navigation elemenst fade out with a different animation
float navProgress;
@@ -763,7 +817,7 @@ public class OptionsOverlay extends OverlayOpsuState {
navProgress = hideAnimationStartProgress * AnimationEquation.IN_CIRC.calc(showHideProgress);
showHideProgress = hideAnimationStartProgress * AnimationEquation.IN_EXPO.calc(showHideProgress);
}
width = navButtonSize + (int) (showHideProgress * (targetWidth - navButtonSize));
currentWidth = navButtonSize + (int) (showHideProgress * (targetWidth - navButtonSize));
COL_NAV_FILTERED.a = COL_NAV_INACTIVE.a = COL_NAV_FILTERED_HOVERED.a = COL_NAV_INDICATOR.a =
COL_NAV_WHITE.a = COL_NAV_BG.a = navProgress;
COL_BG.a = BG_ALPHA * showHideProgress;
@@ -775,14 +829,23 @@ public class OptionsOverlay extends OverlayOpsuState {
COL_COMBOBOX_HOVER.a = showHideProgress;
}
@Override
public boolean onMousePressed(int button, int x, int y) {
public boolean mousePressed(int button, int x, int y) {
if (!this.active) {
return false;
}
if (x > this.currentWidth) {
this.isDraggingFromOutside = true;
return false;
}
wasPressed = true;
if (keyEntryLeft || keyEntryRight) {
keyEntryLeft = keyEntryRight = false;
return true;
}
if (x > width) {
if (x > currentWidth) {
return false;
}
@@ -794,6 +857,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (hoverOption != null && hoverOption instanceof NumericOption) {
isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength;
if (isAdjustingSlider) {
unchangedSliderValue = ((NumericOption) hoverOption).val;
updateSliderOption();
}
}
@@ -801,15 +865,33 @@ public class OptionsOverlay extends OverlayOpsuState {
return true;
}
@Override
public boolean onMouseReleased(int button, int x, int y) {
selectedOption = null;
if (isAdjustingSlider && listener != null) {
listener.onSaveOption(hoverOption);
public boolean mouseReleased(int button, int x, int y) {
this.isDraggingFromOutside = false;
if (!this.active || (!wasPressed && x > this.currentWidth)) {
return false;
}
wasPressed = false;
selectedOption = null;
if (isAdjustingSlider) {
if (listener != null) {
listener.onSaveOption(hoverOption);
}
updateHoverOption(x, y);
isAdjustingSlider = false;
}
isAdjustingSlider = false;
sliderOptionLength = 0;
if (backButton.contains(x, y)){
SoundController.playSound(SoundEffect.MENUBACK);
hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
}
return true;
}
if (x > navWidth) {
if (openDropdownMenu != null) {
openDropdownMenu.mouseReleased(button);
@@ -873,18 +955,14 @@ public class OptionsOverlay extends OverlayOpsuState {
sectionPosition = Utils.clamp(sectionPosition, (int) scrollHandler.min, (int) scrollHandler.max);
scrollHandler.scrollToPosition(sectionPosition);
}
if (UI.getBackButton().contains(x, y)){
hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
}
}
return true;
}
@Override
public boolean onMouseDragged(int oldx, int oldy, int newx, int newy) {
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
if (!this.active || this.isDraggingFromOutside) {
return false;
}
if (!isAdjustingSlider) {
int diff = newy - oldy;
if (diff != 0) {
@@ -894,16 +972,22 @@ public class OptionsOverlay extends OverlayOpsuState {
return true;
}
@Override
public boolean onMouseWheelMoved(int delta) {
public boolean mouseWheelMoved(int delta) {
if (!this.active || mouseX > this.currentWidth) {
return false;
}
if (!isAdjustingSlider) {
scrollHandler.scrollOffset(-delta);
}
return true;
}
@Override
public boolean onKeyPressed(int key, char c) {
public boolean keyPressed(int key, char c) {
if (!this.active) {
return false;
}
if (keyEntryRight) {
if (Utils.isValidGameKey(key)) {
OPTION_KEY_RIGHT.intval = key;
@@ -921,6 +1005,9 @@ public class OptionsOverlay extends OverlayOpsuState {
}
if (key == Keyboard.KEY_ESCAPE) {
if (isAdjustingSlider) {
cancelAdjustingSlider();
}
if (openDropdownMenu != null) {
openDropdownMenu.keyPressed(key, c);
return true;
@@ -930,6 +1017,7 @@ public class OptionsOverlay extends OverlayOpsuState {
updateHoverOption(prevMouseX, prevMouseY);
return true;
}
SoundController.playSound(SoundEffect.MENUBACK);
hide();
if (listener != null) {
listener.onLeaveOptionsMenu();
@@ -961,14 +1049,20 @@ public class OptionsOverlay extends OverlayOpsuState {
return true;
}
@Override
public boolean onKeyReleased(int key, char c) {
public boolean keyReleased(int key, char c) {
return false;
}
private void cancelAdjustingSlider() {
if (isAdjustingSlider) {
isAdjustingSlider = false;
((NumericOption) hoverOption).setValue(unchangedSliderValue);
}
}
private void updateSliderOption() {
NumericOption o = (NumericOption) hoverOption;
int value = o.min + Math.round((float) (o.max - o.min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
int value = o.min + Math.round((float) (o.max - o.min) * (mouseX - sliderOptionStartX) / (sliderOptionLength));
o.setValue(Utils.clamp(value, o.min, o.max));
}
@@ -1000,6 +1094,7 @@ public class OptionsOverlay extends OverlayOpsuState {
private void updateHoverOption(int mouseX, int mouseY) {
if (mouseX < navWidth) {
cancelAdjustingSlider();
hoverOption = null;
return;
}
@@ -1011,7 +1106,7 @@ public class OptionsOverlay extends OverlayOpsuState {
return;
}
hoverOption = null;
if (mouseX > width) {
if (mouseX > currentWidth) {
return;
}
@@ -1118,5 +1213,4 @@ public class OptionsOverlay extends OverlayOpsuState {
void onLeaveOptionsMenu();
void onSaveOption(Option option);
}
}

View File

@@ -1,6 +1,6 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2016 yugecin
* Copyright (C) 2016-2018 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
@@ -26,7 +26,6 @@ import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.ObjectColorOverrides;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.options.OptionTab;
import yugecin.opsudance.sbv2.MoveStoryboard;
@@ -35,14 +34,12 @@ import java.util.*;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
@SuppressWarnings("unchecked")
public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener {
private final static List<Option> optionList = new ArrayList<>();
private final DisplayContainer displayContainer;
private boolean hide;
private int speed;
@@ -58,12 +55,13 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
static {
for (OptionTab tab : OptionGroups.storyboardOptions) {
optionList.addAll(Arrays.asList(tab.options));
if (tab.options != null) {
optionList.addAll(Arrays.asList(tab.options));
}
}
}
public StoryboardOverlay(DisplayContainer displayContainer, MoveStoryboard msb, OptionsOverlay optionsOverlay, Game game) {
this.displayContainer = displayContainer;
public StoryboardOverlay(MoveStoryboard msb, OptionsOverlay optionsOverlay, Game game) {
this.msb = msb;
this.optionsOverlay = optionsOverlay;
this.game = game;
@@ -78,18 +76,18 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
return;
}
int lh = Fonts.SMALL.getLineHeight();
Fonts.SMALL.drawString(10, displayContainer.height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan);
Fonts.SMALL.drawString(10, displayContainer.height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan);
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh, "Menu: N", Color.cyan);
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 2, "HIDE: H", Color.cyan);
Fonts.SMALL.drawString(10, displayContainer.height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan);
Fonts.SMALL.drawString(10, 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, height - 50 - lh, "Menu: N", Color.cyan);
Fonts.SMALL.drawString(10, height - 50 - lh * 2, "HIDE: H", Color.cyan);
Fonts.SMALL.drawString(10, height - 50 - lh * 3, "obj: J " + index + " K", Color.cyan);
g.setColor(Color.red);
if (index < optionsMap.length && optionsMap[index] != null) {
int i = 0;
for (Object o : optionsMap[index].entrySet()) {
Map.Entry<Option, String> option = (Map.Entry<Option, String>) o;
Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().name, Color.cyan);
Fonts.SMALL.drawString(displayContainer.width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan);
Fonts.SMALL.drawString(width / 5, 50 + i * lh, option.getKey().getValueString(), Color.cyan);
g.fillRect(0, 50 + i * lh + lh / 4, 10, 10);
i++;
}
@@ -98,7 +96,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
int start = gameObjects[0].getTime();
int end = gameObjects[gameObjects.length - 1].getEndTime();
float curtime = (float) (MusicController.getPosition() - start) / (end - start);
g.fillRect(curtime * displayContainer.width, displayContainer.height - 10f, 10f, 10f);
g.fillRect(curtime * width, height - 10f, 10f, 10f);
}
}

View File

@@ -1,54 +0,0 @@
/*
* 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 yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.Nullable;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
public class ManifestWrapper {
@Nullable
public final Manifest manifest;
public ManifestWrapper(@Nullable Manifest manifest) {
this.manifest = manifest;
}
/**
* @param attribute attribute in jarfile or null for default attributes
*/
public String valueOrDefault(@Nullable String attribute, @NotNull String key, @Nullable String dfault) {
if (manifest == null) {
return dfault;
}
Attributes attributes =
attribute == null ? manifest.getMainAttributes() : manifest.getAttributes(attribute);
if (attributes == null) {
return dfault;
}
String val = attributes.getValue(key);
if (val == null) {
return dfault;
}
return val;
}
}

View File

@@ -0,0 +1,67 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2018 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 itdelatrisu.opsu.Utils;
import yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.Nullable;
public class SimpleVersion implements Comparable<SimpleVersion> {
@Nullable
public static SimpleVersion parse(@NotNull String version) {
int dashpos = version.indexOf('-');
if (dashpos != -1) {
version = version.substring(0, dashpos);
}
final String[] parts = version.split("\\.");
if (parts.length < 3) {
return null;
}
try {
return new SimpleVersion(
Integer.parseInt(parts[0]),
Integer.parseInt(parts[1]),
Integer.parseInt(parts[2])
);
} catch (Exception e) {
return null;
}
}
final int major, minor, incremental;
public SimpleVersion(int major, int minor, int incremental) {
this.major = major;
this.minor = minor;
this.incremental = incremental;
}
@Override
public int compareTo(@NotNull SimpleVersion o) {
return
Utils.clamp(Integer.compare(major, o.major), -1, 1) * 100 +
Utils.clamp(Integer.compare(minor, o.minor), -1, 1) * 10 +
Utils.clamp(Integer.compare(incremental, o.incremental), -1, 1);
}
@Override
public String toString() {
return "" + major + '.' + minor + '.' + incremental;
}
}