Merge branch 'master' into replaystuff

# Conflicts:
#	src/itdelatrisu/opsu/Options.java
#	src/itdelatrisu/opsu/ui/Cursor.java
This commit is contained in:
yugecin
2017-03-30 00:05:45 +02:00
95 changed files with 3242 additions and 3216 deletions

View File

@@ -17,10 +17,11 @@
*/
package yugecin.opsudance;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ui.Cursor;
import org.newdawn.slick.Color;
import static yugecin.opsudance.options.Options.*;
public enum CursorColorOverrides {
NONE ("Do not override", 0) {
@@ -111,12 +112,12 @@ public enum CursorColorOverrides {
}
private static Color nextRainbowColor() {
hue += Options.getRGBCursorInc() / 1000f;
hue += OPTION_DANCE_RGB_CURSOR_INC.val / 1000f;
return new Color(java.awt.Color.getHSBColor(hue / 360f, 1.0f, 1.0f).getRGB());
}
private static Color nextMirrorRainbowColor() {
hue += Options.getRGBCursorInc() / 1000f;
hue += OPTION_DANCE_RGB_CURSOR_INC.val / 1000f;
return new Color(java.awt.Color.getHSBColor((hue + 180f) / 360f, 1.0f, 1.0f).getRGB());
}

View File

@@ -22,10 +22,8 @@ import awlex.ospu.movers.factories.SpiralMoverFactory;
import awlex.ospu.polymover.factory.ArcFactory;
import awlex.ospu.polymover.factory.PolyMoverFactory;
import awlex.ospu.spinners.SpiralSpinner;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.DummyObject;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
@@ -37,10 +35,13 @@ 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.*;
import static yugecin.opsudance.options.Options.*;
public class Dancer {
public static MoverFactory[] moverFactories = new MoverFactory[] {
@@ -193,12 +194,12 @@ public class Dancer {
}
isCurrentLazySlider = false;
// detect lazy sliders, should work pretty good
if (c.isSlider() && Options.isLazySliders() && Utils.distance(c.start.x, c.start.y, c.end.x, c.end.y) <= Circle.diameter * 0.8f) {
if (c.isSlider() && OPTION_DANCE_LAZY_SLIDERS.state && Utils.distance(c.start.x, c.start.y, c.end.x, c.end.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) {
Slider s = (Slider) c;
Vec2f mid = s.getCurve().pointAt(1f);
if (s.getRepeats() == 1 || Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= Circle.diameter * 0.8f) {
if (s.getRepeats() == 1 || Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) {
mid = s.getCurve().pointAt(0.5f);
if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= Circle.diameter * 0.8f) {
if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) {
isCurrentLazySlider = true;
}
}
@@ -250,8 +251,8 @@ public class Dancer {
}
}
Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, Options.width - 10);
y = Utils.clamp(y, 10, Options.height - 10);
x = Utils.clamp(x, 10, width - 10);
y = Utils.clamp(y, 10, height - 10);
}
private void createNewMover() {

View File

@@ -17,9 +17,10 @@
*/
package yugecin.opsudance;
import itdelatrisu.opsu.Options;
import org.newdawn.slick.Color;
import static yugecin.opsudance.options.Options.*;
public enum ObjectColorOverrides {
NONE ("Do not override", 0) {
@@ -95,7 +96,7 @@ public enum ObjectColorOverrides {
}
private static Color nextRainbowColor() {
hue += Options.getRGBObjInc() / 10f;
hue += OPTION_DANCE_RGB_OBJECT_INC.val / 10f;
return new Color(java.awt.Color.getHSBColor(hue / 360f, 1.0f, 1.0f).getRGB());
}

View File

@@ -17,7 +17,6 @@
*/
package yugecin.opsudance;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.db.DBController;
@@ -27,6 +26,9 @@ import itdelatrisu.opsu.states.Splash;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import java.io.File;
import java.io.IOException;
@@ -35,18 +37,29 @@ import java.net.ServerSocket;
import java.net.UnknownHostException;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.options.Options.*;
/*
* loosely based on itdelatrisu.opsu.Opsu
*/
public class OpsuDance {
private final DisplayContainer container;
@Inject
private DisplayContainer container;
@Inject
private OptionsService optionsService;
@Inject
private Configuration config;
@Inject
private Updater updater;
private ServerSocket singleInstanceSocket;
public OpsuDance(DisplayContainer container) {
this.container = container;
@Inject
public OpsuDance() {
}
public void start(String[] args) {
@@ -54,7 +67,7 @@ public class OpsuDance {
sout("initialized");
checkRunningDirectory();
Options.parseOptions();
optionsService.loadOptions();
ensureSingleInstance();
sout("prechecks done and options parsed");
@@ -70,12 +83,12 @@ public class OpsuDance {
while (rungame());
container.teardownAL();
Options.saveOptions();
optionsService.saveOptions();
closeSingleInstanceSocket();
DBController.closeConnections();
DownloadList.get().cancelAllDownloads();
Utils.deleteDirectory(Options.TEMP_DIR);
if (!Options.isWatchServiceEnabled()) {
Utils.deleteDirectory(config.TEMP_DIR);
if (!OPTION_ENABLE_WATCH_SERVICE.state) {
BeatmapWatchService.destroy();
}
}
@@ -101,7 +114,7 @@ public class OpsuDance {
private void initDatabase() {
try {
DBController.init();
DBController.init(config);
} catch (UnsatisfiedLinkError e) {
errorAndExit("Could not initialize database.", e);
}
@@ -109,18 +122,19 @@ public class OpsuDance {
private void initUpdater(String[] args) {
// check if just updated
if (args.length >= 2)
Updater.get().setUpdateInfo(args[0], args[1]);
if (args.length >= 2) {
updater.setUpdateInfo(args[0], args[1]);
}
// check for updates
if (Options.isUpdaterDisabled()) {
if (OPTION_DISABLE_UPDATER.state) {
return;
}
new Thread() {
@Override
public void run() {
try {
Updater.get().checkForUpdates();
updater.checkForUpdates();
} catch (IOException e) {
Log.warn("updatecheck failed.", e);
}
@@ -143,18 +157,18 @@ public class OpsuDance {
}
private void ensureSingleInstance() {
if (Options.noSingleInstance()) {
if (OPTION_NOSINGLEINSTANCE.state) {
return;
}
try {
singleInstanceSocket = new ServerSocket(Options.getPort(), 1, InetAddress.getLocalHost());
singleInstanceSocket = new ServerSocket(OPTION_PORT.val, 1, InetAddress.getLocalHost());
} catch (UnknownHostException e) {
// shouldn't happen
} catch (IOException e) {
errorAndExit(String.format(
"Could not launch. Either opsu! is already running or a different program uses port %d.\n" +
"You can change the port opsu! uses by editing the 'Port' field in the .opsu.cfg configuration file.\n" +
"If that still does not resolve the problem, you can set 'NoSingleInstance' to 'true', but this is not recommended.", Options.getPort()), e);
"If that still does not resolve the problem, you can set 'NoSingleInstance' to 'true', but this is not recommended.", OPTION_PORT.val), e);
}
}

View File

@@ -17,10 +17,11 @@
*/
package yugecin.opsudance;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
public class Pippi {
@@ -37,30 +38,30 @@ public class Pippi {
public static void setRadiusPercent(int radiusPercent) {
Pippi.radiusPercent = radiusPercent;
pippiminrad = pippirad = (Circle.diameter / 2d - 10d) * radiusPercent / 100d;
pippiminrad = pippirad = (GameObjectRenderer.instance.getCircleDiameter() / 2d - 10d) * radiusPercent / 100d;
}
public static void reset() {
angle = 0;
currentdelta = 0;
setRadiusPercent(radiusPercent);
pippimaxrad = Circle.diameter - 10d;
pippimaxrad = GameObjectRenderer.instance.getCircleDiameter() - 10d;
}
public static void dance(int time, GameObject c, boolean isCurrentLazySlider) {
boolean slowSlider = Options.isCircleInSlowSliders() && c.isSlider() && (((((Slider) c).pixelLength < 200 || c.getEndTime() - c.getTime() > 400)) || isCurrentLazySlider);
boolean slowSlider = OPTION_DANCE_CIRLCE_IN_SLOW_SLIDERS.state && c.isSlider() && (((((Slider) c).pixelLength < 200 || c.getEndTime() - c.getTime() > 400)) || isCurrentLazySlider);
if (!slowSlider) {
slowSlider = Options.isCircleInLazySliders() && isCurrentLazySlider;
slowSlider = OPTION_DANCE_CIRLCE_IN_LAZY_SLIDERS.state && isCurrentLazySlider;
}
if ((!Options.isPippiEnabled() || c.isSpinner()) && !slowSlider) {
if ((!OPTION_PIPPI_ENABLE.state || c.isSpinner()) && !slowSlider) {
return;
}
if (currentdelta >= targetdelta && c != previous) {
currentdelta = 0;
if (c.isSlider() && c.getTime() < time) {
angle += Options.getPippiAngIncMultiplierSlider() / 1800d * Math.PI;
angle += OPTION_PIPPI_ANGLE_INC_MUL_SLIDER.val / 1800d * Math.PI;
if (!slowSlider) {
if (Options.isPippiFollowcircleExpand()) {
if (OPTION_PIPPI_SLIDER_FOLLOW_EXPAND.state) {
if (c.getEndTime() - time < 40 && pippirad > pippimaxrad) {
pippirad -= 5d;
} else if (time - c.getTime() > 10 && c.getEndTime() - c.getTime() > 600 && pippirad < pippimaxrad) {
@@ -69,10 +70,10 @@ public class Pippi {
}
}
} else if (!c.isSpinner()) {
if (Options.isPippiFollowcircleExpand() && pippirad != pippiminrad) {
if (OPTION_PIPPI_SLIDER_FOLLOW_EXPAND.state && pippirad != pippiminrad) {
pippirad = pippiminrad;
}
angle += Options.getPippiAngIncMultiplier() / 1800d * Math.PI;
angle += OPTION_PIPPI_ANGLE_INC_MUL.val / 1800d * Math.PI;
}
// don't inc on long movements
if (c.getTime() - time > 400) {
@@ -91,7 +92,7 @@ public class Pippi {
}
public static boolean shouldPreventWobblyStream(double distance) {
return Options.isPippiEnabled() && distance < Circle.diameter * 0.93f && Options.isPippiPreventWobblyStreams();
return OPTION_PIPPI_ENABLE.state && distance < GameObjectRenderer.instance.getCircleDiameter() * 0.93f && OPTION_PIPPI_PREVENT_WOBBLY_STREAMS.state;
}
}

View File

@@ -18,10 +18,11 @@
package yugecin.opsudance;
import itdelatrisu.opsu.NativeLoader;
import itdelatrisu.opsu.Options;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.options.Configuration;
import java.io.File;
import java.io.IOException;
@@ -29,7 +30,11 @@ import java.lang.reflect.Field;
public class PreStartupInitializer {
public PreStartupInitializer() {
private final Configuration config;
@Inject
public PreStartupInitializer(Configuration config) {
this.config = config;
loadNatives();
setResourcePath();
}
@@ -52,7 +57,7 @@ public class PreStartupInitializer {
}
private File loadNativesUsingOptionsPath() {
File nativeDir = Options.NATIVE_DIR;
File nativeDir = config.NATIVE_DIR;
try {
new NativeLoader(nativeDir).loadNatives();
} catch (IOException e) {

View File

@@ -17,15 +17,15 @@
*/
package yugecin.opsudance.core;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.*;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI;
@@ -42,6 +42,7 @@ import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
@@ -50,19 +51,25 @@ import yugecin.opsudance.core.state.specialstates.FpsRenderState;
import yugecin.opsudance.core.state.transitions.*;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.options.Options.*;
/**
* based on org.newdawn.slick.AppGameContainer
*/
public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListener {
@Deprecated
public static DisplayContainer instance; // TODO d remove this
@Inject
private SkinService skinService;
@Inject
private Configuration config;
private static SGL GL = Renderer.get();
@@ -116,11 +123,11 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public final Cursor cursor;
public boolean drawCursor;
@Inject
public DisplayContainer(InstanceContainer instanceContainer) {
this.instanceContainer = instanceContainer;
this.cursor = new Cursor();
drawCursor = true;
instance = this;
outTransitionListener = new TransitionFinishedListener() {
@Override
@@ -145,8 +152,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
destroyImages();
Utils.init(DisplayContainer.this); // TODO this shouldn't be here
UI.revalidate(); // TODO this shouldn't be here
reinit();
}
});
@@ -155,8 +161,29 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
lastFrame = getTime();
delta = 1;
renderDelta = 1;
}
Options.GameOption.displayContainer = this;
private void reinit() {
// this used to be in Utils.init
// TODO find a better place for this?
setFPS(targetFPS[targetFPSIndex]);
MusicController.setMusicVolume(OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f);
skinService.loadSkin();
// initialize game images
for (GameImage img : GameImage.values()) {
if (img.isPreload()) {
img.setDefaultImage();
}
}
// 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) {
@@ -170,8 +197,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
}
public void init(Class<? extends OpsuState> startingState) {
setUPS(Options.getTargetUPS());
setFPS(Options.getTargetFPS());
setUPS(OPTION_TARGET_UPS.val);
setFPS(targetFPS[targetFPSIndex]);
state = instanceContainer.provide(startingState);
state.enter();
@@ -245,7 +272,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
width = height = -1;
Input.disableControllers();
Display.setTitle("opsu!dance");
Options.setDisplayMode(this);
updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString());
Display.create();
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
initGL();
@@ -303,6 +330,38 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
return true;
}
public void updateDisplayMode(String resolutionString) {
int screenWidth = nativeDisplayMode.getWidth();
int screenHeight = nativeDisplayMode.getHeight();
int width = screenWidth;
int height = screenHeight;
if (resolutionString.matches("^[0-9]+x[0-9]+$")) {
String[] res = resolutionString.split("x");
width = Integer.parseInt(res[0]);
height = Integer.parseInt(res[1]);
}
// check for larger-than-screen dimensions
if (!OPTION_ALLOW_LARGER_RESOLUTIONS.state && (screenWidth < width || screenHeight < height)) {
width = 800;
height = 600;
}
try {
setDisplayMode(width, height, OPTION_FULLSCREEN.state);
} catch (Exception e) {
EventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED));
Log.error("Failed to set display mode.", e);
}
if (OPTION_FULLSCREEN.state) {
// set borderless window if dimensions match screen size
boolean borderless = (screenWidth == width && screenHeight == height);
System.setProperty("org.lwjgl.opengl.Window.undecorated", Boolean.toString(borderless));
}
}
public void setDisplayMode(int width, int height, boolean fullscreen) throws Exception {
if (this.width == width && this.height == height) {
Display.setFullscreen(fullscreen);
@@ -353,9 +412,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
sout("GL ready");
GameImage.init(width, height);
Fonts.init();
Fonts.init(config);
EventBus.post(new ResolutionOrSkinChangedEvent());
EventBus.post(new ResolutionOrSkinChangedEvent(null, width, height));
}
public void resetCursor() {

View File

@@ -17,10 +17,10 @@
*/
package yugecin.opsudance.core.errorhandling;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.utils.MiscUtils;
import javax.swing.*;
@@ -42,6 +42,7 @@ public class ErrorHandler {
private static ErrorHandler instance;
private final Configuration config;
private final DisplayContainer displayContainer;
private String customMessage;
@@ -54,8 +55,9 @@ public class ErrorHandler {
private boolean ignoreAndContinue;
private boolean allowTerminate;
public ErrorHandler(DisplayContainer displayContainer) {
public ErrorHandler(DisplayContainer displayContainer, Configuration config) {
this.displayContainer = displayContainer;
this.config = config;
instance = this;
}
@@ -164,7 +166,7 @@ public class ErrorHandler {
@Override
public void actionPerformed(ActionEvent event) {
try {
Desktop.getDesktop().open(Options.LOG_FILE);
Desktop.getDesktop().open(config.LOG_FILE);
} catch (IOException e) {
Log.warn("Could not open log file", e);
JOptionPane.showMessageDialog(null, "whoops could not open log file", "errorception", JOptionPane.ERROR_MESSAGE);
@@ -230,7 +232,7 @@ public class ErrorHandler {
} catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
}
return URI.create(String.format(Options.ISSUES_URL, issueTitle, issueBody));
return URI.create(String.format(config.ISSUES_URL, issueTitle, issueBody));
}
private String truncateGithubIssueBody(String body) {

View File

@@ -101,6 +101,11 @@ public abstract class Injector implements InstanceContainer, Binder {
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;

View File

@@ -20,5 +20,6 @@ package yugecin.opsudance.core.inject;
public interface InstanceContainer {
<T> T provide(Class<T> type);
<T> T injectFields(T instance);
}

View File

@@ -17,6 +17,10 @@
*/
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.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer;
@@ -27,10 +31,23 @@ import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
import yugecin.opsudance.core.state.transitions.FadeInTransitionState;
import yugecin.opsudance.core.state.transitions.FadeOutTransitionState;
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();
@@ -44,6 +61,8 @@ public class OpsuDanceInjector extends Injector {
bind(FadeInTransitionState.class).asEagerSingleton();
bind(FadeOutTransitionState.class).asEagerSingleton();
bind(GameObjectRenderer.class).asEagerSingleton();
bind(Splash.class).asEagerSingleton();
bind(MainMenu.class).asEagerSingleton();
bind(ButtonMenu.class).asEagerSingleton();

View File

@@ -17,8 +17,6 @@
*/
package yugecin.opsudance.core.state;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Graphics;
@@ -27,15 +25,26 @@ import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import java.io.StringWriter;
import static yugecin.opsudance.options.Options.*;
public abstract class BaseOpsuState implements OpsuState, EventListener<ResolutionOrSkinChangedEvent> {
@Inject
protected DisplayContainer displayContainer;
@Inject
protected Configuration config;
@Inject
protected SkinService skinService;
/**
* state is dirty when resolution or skin changed but hasn't rendered yet
*/
@@ -97,20 +106,21 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
@Override
public boolean keyReleased(int key, char c) {
if (key == Input.KEY_F7) {
Options.setNextFPS(displayContainer);
OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length);
EventBus.post(new BarNotificationEvent(String.format("Frame limiter: %s", OPTION_TARGET_FPS.getValueString())));
return true;
}
if (key == Input.KEY_F10) {
Options.toggleMouseDisabled();
OPTION_DISABLE_MOUSE_BUTTONS.toggle();
return true;
}
if (key == Input.KEY_F12) {
Utils.takeScreenShot();
config.takeScreenShot();
return true;
}
Input input = displayContainer.input;
if (key == Input.KEY_S && input.isKeyDown(Input.KEY_LMENU) && input.isKeyDown(Input.KEY_LSHIFT) &&input.isKeyDown(Input.KEY_LCONTROL) && !displayContainer.isInState(Game.class)) {
Options.reloadSkin();
skinService.reloadSkin();
}
return false;
}

View File

@@ -17,7 +17,6 @@
*/
package yugecin.opsudance.core.state.specialstates;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
@@ -27,6 +26,8 @@ import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.utils.FPSMeter;
import static yugecin.opsudance.options.Options.*;
public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEvent> {
private final static Color GREEN = new Color(171, 218, 25);
@@ -54,17 +55,17 @@ public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEven
public void render(Graphics g) {
fpsMeter.update(displayContainer.renderDelta);
if (!Options.isFPSCounterEnabled()) {
if (!OPTION_SHOW_FPS.state) {
return;
}
int x = this.x;
int fpsDeviation = displayContainer.delta % displayContainer.targetRenderInterval;
x = drawText(g, getColor((int) (Options.getTargetFPS() * 0.9f) - fpsDeviation, fpsMeter.getValue()), getText(fpsMeter.getValue(), "fps"), x, this.y);
drawText(g, getColor((int) (Options.getTargetUPS() * 0.9f), upsMeter.getValue()), getText(upsMeter.getValue(), "ups"), x, this.y);
x = drawText(g, getColor((int) (targetFPS[targetFPSIndex] * 0.9f) - fpsDeviation, fpsMeter.getValue()), getText(fpsMeter.getValue(), "fps"), x, this.y);
drawText(g, getColor((int) (OPTION_TARGET_UPS.val * 0.9f), upsMeter.getValue()), getText(upsMeter.getValue(), "ups"), x, this.y);
}
private String getText(int value, String unit) {
if (Options.useDeltasForFPSCounter()) {
if (OPTION_USE_FPS_DELTAS.state) {
return String.format("%.2fms", 1000f / value);
}
return value + " " + unit;

View File

@@ -19,4 +19,14 @@ package yugecin.opsudance.events;
public class ResolutionOrSkinChangedEvent {
public final String skin;
public final int width;
public final int height;
public ResolutionOrSkinChangedEvent(String skin, int width, int height) {
this.skin = skin;
this.width = width;
this.height = height;
}
}

View File

@@ -17,9 +17,9 @@
*/
package yugecin.opsudance.movers;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.options.Options;
public class CircleMover extends Mover {

View File

@@ -17,7 +17,6 @@
*/
package yugecin.opsudance.movers;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
@@ -25,6 +24,8 @@ import itdelatrisu.opsu.objects.curves.Vec2f;
import java.awt.*;
import static yugecin.opsudance.options.Options.*;
public class CubicBezierMover extends Mover {
private static Point p2 = new Point(0, 0);
@@ -41,7 +42,7 @@ public class CubicBezierMover extends Mover {
double ang = s.getCurve().getStartAngle() * Math.PI / 180d + Math.PI;
Vec2f nextpos = s.getPointAt(s.getTime() + 10);
double dist = Utils.distance(end.start.x, end.start.y, nextpos.x, nextpos.y);
double speed = dist * Options.getQuadBezAggressiveness() * Options.getQuadBezSliderEntryAggressiveness() / 10;
double speed = dist * OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS.val * OPTION_DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR.val / 10;
p2.x = (int) (end.start.x + Math.cos(ang) * speed);
p2.y = (int) (end.start.y + Math.sin(ang) * speed);
}

View File

@@ -17,11 +17,13 @@
*/
package yugecin.opsudance.movers;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.options.Options;
import java.util.Random;
import static yugecin.opsudance.options.Options.*;
public class ExgonMover extends Mover {
private double[] pos;
@@ -30,15 +32,15 @@ public class ExgonMover extends Mover {
public ExgonMover(GameObject start, GameObject end, int dir) {
super(start, end, dir);
nextTime = start.getEndTime() + Options.getExgonDelay();
nextTime = start.getEndTime() + OPTION_DANCE_EXGON_DELAY.val;
pos = new double[] { start.end.x, start.end.y };
}
@Override
public double[] getPointAt(int time) {
if (time > nextTime) {
nextTime = time + Options.getExgonDelay();
if (time > getEnd().getEndTime() - Options.getExgonDelay()) {
nextTime = time + OPTION_DANCE_EXGON_DELAY.val;
if (time > getEnd().getEndTime() - OPTION_DANCE_EXGON_DELAY.val) {
pos[0] = endX;
pos[1] = endY;
} else {

View File

@@ -17,12 +17,13 @@
*/
package yugecin.opsudance.movers;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject;
import java.awt.*;
import static yugecin.opsudance.options.Options.*;
public class QuadraticBezierMover extends Mover {
public static Point p;
@@ -34,7 +35,7 @@ public class QuadraticBezierMover extends Mover {
}
public static void setPrevspeed(double distance, int timedelta) {
prevspeed = distance * Options.getQuadBezAggressiveness() * Options.getQuadBezSliderAggressiveness() / timedelta;
prevspeed = distance * OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS.val * OPTION_DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR.val / timedelta;
}
public static double getPrevspeed() {
@@ -53,7 +54,7 @@ public class QuadraticBezierMover extends Mover {
double dist = Utils.distance(startX, startY, endX, endY);
p.x = (int) (startX + Math.cos(startAngle) * prevspeed);
p.y = (int) (startY + Math.sin(startAngle) * prevspeed);
prevspeed = (dist / totalTime) * Options.getQuadBezAggressiveness();
prevspeed = (dist / totalTime) * OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS.val;
}
@Override

View File

@@ -17,13 +17,15 @@
*/
package yugecin.opsudance.movers.factories;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.Pippi;
import yugecin.opsudance.movers.*;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
public class AutoMoverFactory implements MoverFactory {
@@ -42,8 +44,8 @@ public class AutoMoverFactory implements MoverFactory {
}
// stacked: circles if not too quick
int circle_stream = Options.isCircleStreams() ? 58: 85;
if (distance < Circle.diameter && ((dt > circle_stream && !Options.isOnlyCircleStacks()) || distance < HitObject.getStackOffset() * 5.2f)) { // TODO get the correct multiplier for stackoffsets
int circle_stream = OPTION_DANCE_CIRCLE_STREAMS.state ? 58: 85;
if (distance < GameObjectRenderer.instance.getCircleDiameter() && ((dt > circle_stream && !OPTION_DANCE_ONLY_CIRCLE_STACKS.state) || distance < HitObject.getStackOffset() * 5.2f)) { // TODO get the correct multiplier for stackoffsets
return new CircleMover(start, end, dir);
}
@@ -93,16 +95,14 @@ public class AutoMoverFactory implements MoverFactory {
}
@SuppressWarnings("SimplifiableIfStatement")
protected boolean inbounds(Mover m )
{
protected boolean inbounds(Mover m ) {
this.m = m;
if (!checkBounds(m.getPointAt((int) (starttime + (endtime - starttime) * 0.3)))) return false;
if (!checkBounds(m.getPointAt((int) (starttime + (endtime - starttime) * 0.7)))) return false;
return checkBounds(m.getPointAt((int) (starttime + (endtime - starttime) * 0.5)));
}
private boolean checkBounds( double[] pos )
{
private boolean checkBounds( double[] pos ) {
return 0 < pos[0] && pos[0] < Options.width - Options.width / 8 && 0 < pos[1] && pos[1] < Options.height - Options.height / 8;
}

View File

@@ -17,17 +17,18 @@
*/
package yugecin.opsudance.movers.factories;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.movers.CubicBezierMover;
import yugecin.opsudance.movers.Mover;
import yugecin.opsudance.movers.QuadraticBezierMover;
import static yugecin.opsudance.options.Options.*;
public class QuadraticBezierMoverFactory implements MoverFactory {
@Override
public Mover create(GameObject start, GameObject end, int dir) {
if (Options.isQuadBezCubicEnabled() && end.isSlider()) {
if (OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS.state && end.isSlider()) {
return new CubicBezierMover(start, end, dir);
}
return new QuadraticBezierMover(start, end, dir);

View File

@@ -20,12 +20,36 @@ package yugecin.opsudance.objects.curves;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.curves.Curve;
import itdelatrisu.opsu.objects.curves.Vec2f;
import itdelatrisu.opsu.render.CurveRenderState;
import org.newdawn.slick.Color;
import java.util.ArrayList;
import java.util.List;
public class FakeCombinedCurve extends Curve {
private List<Integer> pointsToRender;
public FakeCombinedCurve(Vec2f[] points) {
super(new HitObject(0, 0, 0), false);
this.curve = points;
pointsToRender = new ArrayList<>();
}
public void initForFrame() {
pointsToRender.clear();
}
public void addRange(int from, int to) {
pointsToRender.add(from);
pointsToRender.add(to);
}
@Override
public void draw(Color color) {
if (renderState == null)
renderState = new CurveRenderState(hitObject, curve, true);
renderState.draw(color, borderColor, pointsToRender);
}
@Override

View File

@@ -0,0 +1,304 @@
/*
* 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.options;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinReg;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.TimingPoint;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BubbleNotificationEvent;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static yugecin.opsudance.options.Options.*;
public class Configuration {
public final boolean USE_XDG;
public final File CONFIG_DIR;
public final File DATA_DIR;
public final File CACHE_DIR;
public final File BEATMAP_DIR;
public final File SKIN_ROOT_DIR;
public final File BEATMAP_DB;
public final File SCORE_DB;
public final File NATIVE_DIR;
public final File TEMP_DIR;
public final File LOG_FILE;
public final File OPTIONS_FILE;
public final String FONT_NAME;
public final String VERSION_FILE;
public final URI REPOSITORY_URI;
public final URI DANCE_REPOSITORY_URI;
public final String ISSUES_URL;
public final String VERSION_REMOTE;
public final File osuInstallationDirectory;
public final Beatmap themeBeatmap;
public File beatmapDir;
public File oszDir;
public File screenshotDir;
public File replayDir;
public File replayImportDir;
public File skinRootDir;
@Inject
public Configuration() {
USE_XDG = areXDGDirectoriesEnabled();
CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config");
DATA_DIR = getXDGBaseDir("XDG_DATA_HOME", ".local/share");
CACHE_DIR = getXDGBaseDir("XDG_CACHE_HOME", ".cache");
BEATMAP_DIR = new File(DATA_DIR, "Songs/");
SKIN_ROOT_DIR = new File(DATA_DIR, "Skins/");
BEATMAP_DB = new File(DATA_DIR, ".opsu.db");
SCORE_DB = new File(DATA_DIR, ".opsu_scores.db");
NATIVE_DIR = new File(CACHE_DIR, "Natives/");
TEMP_DIR = new File(CACHE_DIR, "Temp/");
LOG_FILE = new File(CONFIG_DIR, ".opsu.log");
OPTIONS_FILE = new File(CONFIG_DIR, ".opsu.cfg");
FONT_NAME = "DroidSansFallback.ttf";
VERSION_FILE = "version";
REPOSITORY_URI = URI.create("https://github.com/itdelatrisu/opsu");
DANCE_REPOSITORY_URI = URI.create("https://github.com/yugecin/opsu-dance");
ISSUES_URL = "https://github.com/yugecin/opsu-dance/issues/new?title=%s&body=%s";
VERSION_REMOTE = "https://raw.githubusercontent.com/yugecin/opsu-dance/master/version";
osuInstallationDirectory = loadOsuInstallationDirectory();
themeBeatmap = createThemeBeatmap();
}
private Beatmap createThemeBeatmap() {
try {
String[] tokens = {"theme.mp3", "Rainbows", "Kevin MacLeod", "219350"};
Beatmap beatmap = new Beatmap(null);
beatmap.audioFilename = new File(tokens[0]);
beatmap.title = tokens[1];
beatmap.artist = tokens[2];
beatmap.endTime = Integer.parseInt(tokens[3]);
beatmap.timingPoints = new ArrayList<>(1);
beatmap.timingPoints.add(new TimingPoint("1080,545.454545454545,4,1,0,100,0,0"));
return beatmap;
} catch (Exception e) {
return null;
}
}
private File loadOsuInstallationDirectory() {
if (!System.getProperty("os.name").startsWith("Win")) {
return null;
}
final WinReg.HKEY rootKey = WinReg.HKEY_CLASSES_ROOT;
final String regKey = "osu\\DefaultIcon";
final String regValue = null; // default value
final String regPathPattern = "\"(.+)\\\\[^\\/]+\\.exe\"";
String value;
try {
value = Advapi32Util.registryGetStringValue(rootKey, regKey, regValue);
} catch (Win32Exception ignored) {
return null;
}
Pattern pattern = Pattern.compile(regPathPattern);
Matcher m = pattern.matcher(value);
if (!m.find()) {
return null;
}
File dir = new File(m.group(1));
if (dir.isDirectory()) {
return dir;
}
return null;
}
public void loadDirectories() {
replayImportDir = loadDirectory(replayImportDir, new File(DATA_DIR, "ReplayImport"), "replay import");
oszDir = loadDirectory(oszDir, new File(DATA_DIR, "SongPacks"), "song packs");
screenshotDir = loadDirectory(screenshotDir, new File(DATA_DIR, "Screenshots"), "screenshots");
replayDir = loadDirectory(replayDir, new File(DATA_DIR, "Replays"), "replays");
beatmapDir = loadOsuDirectory(beatmapDir, BEATMAP_DIR, "beatmap");
skinRootDir = loadOsuDirectory(skinRootDir, SKIN_ROOT_DIR, "skin root");
}
private File loadDirectory(File dir, File defaultDir, String kind) {
if (dir.exists() && dir.isDirectory()) {
return dir;
}
if (!defaultDir.isDirectory() && !defaultDir.mkdir()) {
String msg = String.format("Failed to create %s directory at '%s'.", kind, defaultDir.getAbsolutePath());
EventBus.post(new BubbleNotificationEvent(msg, BubbleNotificationEvent.COMMONCOLOR_RED));
}
return defaultDir;
}
private File loadOsuDirectory(File dir, File defaultDir, String kind) {
if (dir != null && dir.isDirectory()) {
return dir;
}
if (osuInstallationDirectory != null) {
dir = new File(osuInstallationDirectory, defaultDir.getName());
if (dir.isDirectory()) {
return dir;
}
}
return loadDirectory(dir, defaultDir, kind);
}
private boolean areXDGDirectoriesEnabled() {
JarFile jarFile = Utils.getJarFile();
if (jarFile == null) {
return false;
}
try {
Manifest manifest = jarFile.getManifest();
if (manifest == null) {
return false;
}
Attributes attributes = manifest.getMainAttributes();
String value = attributes.getValue("Use-XDG");
return (value != null && value.equalsIgnoreCase("true"));
} catch (IOException e) {
return false;
}
}
/**
* Returns the directory based on the XDG base directory specification for
* Unix-like operating systems, only if the "XDG" flag is enabled.
* @param env 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 env, String fallback) {
File workingdir;
if (Utils.isJarRunning()) {
workingdir = Utils.getRunningDirectory().getParentFile();
} else {
workingdir = Paths.get(".").toAbsolutePath().normalize().toFile();
}
if (!USE_XDG) {
return workingdir;
}
String OS = System.getProperty("os.name").toLowerCase();
if (OS.indexOf("nix") == -1 && OS.indexOf("nux") == -1 && OS.indexOf("aix") == -1){
return workingdir;
}
String rootPath = System.getenv(env);
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()) {
ErrorHandler.error(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath), new Exception("empty")).preventReport().show();
}
return dir;
}
/**
* @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots
*/
public void takeScreenShot() {
// TODO: get a decent place for this
// create the screenshot directory
if (!screenshotDir.isDirectory() && !screenshotDir.mkdir()) {
EventBus.post(new BubbleNotificationEvent(String.format("Failed to create screenshot directory at '%s'.", screenshotDir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return;
}
// create file name
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
final String fileName = String.format("screenshot_%s.%s", date.format(new Date()), OPTION_SCREENSHOT_FORMAT.getValueString().toLowerCase());
final File file = new File(screenshotDir, fileName);
SoundController.playSound(SoundEffect.SHUTTER);
// copy the screen to file
final int width = Display.getWidth();
final int height = Display.getHeight();
final int bpp = 3; // assuming a 32-bit display with a byte each for red, green, blue, and alpha
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
GL11.glReadBuffer(GL11.GL_FRONT);
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
new Thread() {
@Override
public void run() {
try {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int i = (x + (width * y)) * bpp;
int r = buffer.get(i) & 0xFF;
int g = buffer.get(i + 1) & 0xFF;
int b = buffer.get(i + 2) & 0xFF;
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
ImageIO.write(image, OPTION_SCREENSHOT_FORMAT.getValueString().toLowerCase(), file);
EventBus.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
} catch (Exception e) {
ErrorHandler.error("Failed to take a screenshot.", e).show();
}
}
}.start();
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.options;
public abstract class GenericOption extends Option {
public int intval;
public String strval;
public boolean boolval;
public GenericOption(String name, String configurationName, String description, int intval, String strval, boolean boolval) {
super(name, configurationName, description);
this.intval = intval;
this.strval = strval;
this.boolval = boolval;
}
@Override
public abstract String getValueString();
@Override
public abstract void read(String s);
@Override
public abstract String write();
}

View File

@@ -0,0 +1,33 @@
/*
* 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.options;
public abstract class ListOption extends Option {
public ListOption(String name, String configurationName, String description) {
super(name, configurationName, description);
}
@Override
public abstract String write();
@Override
public abstract void read(String s);
public abstract Object[] getListItems();
public abstract void clickListItem(int index);
}

View File

@@ -0,0 +1,60 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
import itdelatrisu.opsu.Utils;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
public class NumericOption extends Option {
public final int min;
public final int max;
public int val;
public NumericOption(String name, String configurationName, String description, int val, int min, int max) {
super(name, configurationName, description);
this.min = min;
this.max = max;
this.val = val;
}
public void setValue(int val) {
this.val = val;
}
@Override
public String getValueString() {
return String.format("%d%%", val);
}
@Override
public String write() {
return Integer.toString(val);
}
@Override
public void read(String s) {
try {
val = Utils.clamp(Integer.parseInt(s), min, max);
} catch (Exception ignored) {
EventBus.post(new BubbleNotificationEvent("Failed to parse " + configurationName + " option", BubbleNotificationEvent.COMMONCOLOR_RED));
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.options;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
public class Option {
// keep a reference to the instancecontainer so that not every option instance needs to be injected
protected static InstanceContainer instanceContainer;
// caching some commonly used classes
protected static Configuration config;
protected static DisplayContainer displayContainer;
public static void setInstanceContainer(InstanceContainer instanceContainer) {
Option.instanceContainer = instanceContainer;
Option.config = instanceContainer.provide(Configuration.class);
Option.displayContainer = instanceContainer.provide(DisplayContainer.class);
}
public final String name;
public final String configurationName;
public final String description;
/**
* If this option should not be shown in the optionsmenu because it does
* not match the search string.
*/
private boolean filtered;
/**
* Constructor for internal options (not displayed in-game).
*/
public Option(String configurationName) {
this(null, configurationName, null);
}
public Option(String name, String configurationName, String description) {
this.name = name;
this.configurationName = configurationName;
this.description = description;
OptionsService.registerOption(this);
}
/**
* should the option be shown
* @return true if the option should be shown
*/
public boolean showCondition() {
return true;
}
public String getValueString() {
return "";
}
public String write() {
return getValueString();
}
public void read(String s) {
}
/**
* Update the filtered flag for this option based on the given searchString.
* @param searchString the searched string or null to reset the filtered flag
* @return true if this option does need to be filtered
*/
public boolean filter(String searchString) {
if (searchString == null || searchString.length() == 0) {
filtered = false;
return false;
}
filtered = !name.toLowerCase().contains(searchString) && !description.toLowerCase().contains(searchString);
return filtered;
}
/**
* Check if this option should be filtered (= not shown) because it does not
* match the search string.
* @return true if the option shouldn't be shown.
*/
public boolean isFiltered() {
return filtered;
}
}

View File

@@ -0,0 +1,239 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
import static yugecin.opsudance.options.Options.*;
public class OptionGroups {
public static final OptionTab[] normalOptions = new OptionTab[] {
new OptionTab("GENERAL", null),
new OptionTab("GENERAL", new Option[]{
OPTION_DISABLE_UPDATER,
OPTION_ENABLE_WATCH_SERVICE
}),
new OptionTab("LANGUAGE", new Option[]{
OPTION_SHOW_UNICODE,
}),
new OptionTab("GRAPHICS", null),
new OptionTab("RENDERER", new Option[] {
OPTION_SCREEN_RESOLUTION,
OPTION_ALLOW_LARGER_RESOLUTIONS,
OPTION_FULLSCREEN,
OPTION_TARGET_UPS,
OPTION_TARGET_FPS,
OPTION_SHOW_FPS,
OPTION_USE_FPS_DELTAS,
OPTION_SCREENSHOT_FORMAT,
}),
new OptionTab("SLIDER OPTIONS", new Option[]{
OPTION_SNAKING_SLIDERS,
OPTION_FALLBACK_SLIDERS,
OPTION_SHRINKING_SLIDERS,
OPTION_MERGING_SLIDERS,
//OPTION_MERGING_SLIDERS_MIRROR_POOL,
OPTION_DRAW_SLIDER_ENDCIRCLES,
}),
new OptionTab("DANCING HITCIRCLES", new Option[] {
OPTION_DANCING_CIRCLES,
OPTION_DANCING_CIRCLES_MULTIPLIER,
}),
new OptionTab("SKIN", null),
new OptionTab("SKIN", new Option[]{
OPTION_SKIN,
OPTION_IGNORE_BEATMAP_SKINS,
OPTION_DYNAMIC_BACKGROUND,
OPTION_LOAD_HD_IMAGES,
OPTION_LOAD_VERBOSE,
OPTION_COLOR_MAIN_MENU_LOGO,
}),
new OptionTab("CURSOR", new Option[]{
OPTION_CURSOR_SIZE,
OPTION_NEW_CURSOR,
OPTION_DISABLE_CURSOR
// TODO use combo colour as tint for slider ball option
}),
new OptionTab("AUDIO", null),
new OptionTab("VOLUME", new Option[]{
OPTION_MASTER_VOLUME,
OPTION_MUSIC_VOLUME,
OPTION_EFFECT_VOLUME,
OPTION_HITSOUND_VOLUME,
OPTION_SAMPLE_VOLUME_OVERRIDE,
}),
new OptionTab("MISC", new Option[] {
OPTION_MUSIC_OFFSET,
OPTION_DISABLE_SOUNDS,
OPTION_ENABLE_THEME_SONG
}),
new OptionTab("GAMEPLAY", null),
new OptionTab("GENERAL", new Option[] {
OPTION_BACKGROUND_DIM,
OPTION_FORCE_DEFAULT_PLAYFIELD,
OPTION_SHOW_HIT_LIGHTING,
OPTION_SHOW_HIT_ANIMATIONS,
OPTION_SHOW_COMBO_BURSTS,
OPTION_SHOW_PERFECT_HIT,
OPTION_SHOW_FOLLOW_POINTS,
OPTION_SHOW_HIT_ERROR_BAR,
OPTION_MAP_START_DELAY,
OPTION_MAP_END_DELAY,
OPTION_EPILEPSY_WARNING,
}),
new OptionTab("INPUT", null),
new OptionTab("KEY MAPPING", new Option[]{
OPTION_KEY_LEFT,
OPTION_KEY_RIGHT,
}),
new OptionTab("MOUSE", new Option[] {
OPTION_DISABLE_MOUSE_WHEEL,
OPTION_DISABLE_MOUSE_BUTTONS,
}),
new OptionTab("CUSTOM", null),
new OptionTab("DIFFICULTY", new Option[]{
OPTION_FIXED_CS,
OPTION_FIXED_HP,
OPTION_FIXED_AR,
OPTION_FIXED_OD,
}),
new OptionTab("MISC", new Option[] {
OPTION_CHECKPOINT,
OPTION_REPLAY_SEEKING,
}),
new OptionTab("DANCE", null),
new OptionTab("MOVER", new Option[]{
OPTION_DANCE_MOVER,
OPTION_DANCE_EXGON_DELAY,
OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS,
OPTION_DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
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_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("ADVANCED DISPLAY", null),
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,
}),
new OptionTab("MISC", new Option[] {
OPTION_DANCE_HIDE_UI,
OPTION_DANCE_REMOVE_BG,
OPTION_DANCE_ENABLE_SB,
}),
new OptionTab ("PIPPI", null),
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,
})
};
public static final OptionTab[] storyboardOptions = new OptionTab[] {
new OptionTab("Gameplay", new Option[] {
OPTION_BACKGROUND_DIM,
OPTION_DANCE_REMOVE_BG,
OPTION_SNAKING_SLIDERS,
OPTION_SHRINKING_SLIDERS,
OPTION_SHOW_HIT_LIGHTING,
OPTION_SHOW_HIT_ANIMATIONS,
OPTION_SHOW_COMBO_BURSTS,
OPTION_SHOW_PERFECT_HIT,
OPTION_SHOW_FOLLOW_POINTS,
}),
new OptionTab("Input", new Option[] {
OPTION_CURSOR_SIZE,
OPTION_NEW_CURSOR,
OPTION_DISABLE_CURSOR
}),
new OptionTab("Dance", new Option[] {
OPTION_DANCE_MOVER,
OPTION_DANCE_EXGON_DELAY,
OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS,
OPTION_DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
OPTION_DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
OPTION_DANCE_MOVER_DIRECTION,
OPTION_DANCE_SLIDER_MOVER_TYPE,
OPTION_DANCE_SPINNER,
OPTION_DANCE_SPINNER_DELAY,
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,
OPTION_DANCE_MIRROR,
}),
new OptionTab("Dance display", new Option[] {
OPTION_DANCE_DRAW_APPROACH,
OPTION_DANCE_OBJECT_COLOR_OVERRIDE,
OPTION_DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
OPTION_DANCE_RGB_OBJECT_INC,
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[] {
OPTION_PIPPI_ENABLE,
OPTION_PIPPI_RADIUS_PERCENT,
OPTION_PIPPI_ANGLE_INC_MUL,
OPTION_PIPPI_ANGLE_INC_MUL_SLIDER,
OPTION_PIPPI_SLIDER_FOLLOW_EXPAND,
OPTION_PIPPI_PREVENT_WOBBLY_STREAMS,
})
};
}

View File

@@ -0,0 +1,31 @@
/*
* 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.options;
public class OptionTab {
public final String name;
public final Option[] options;
public boolean filtered;
public OptionTab(String name, Option[] options) {
this.name = name;
this.options = options;
}
}

View File

@@ -0,0 +1,978 @@
/*
* 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.options;
import awlex.ospu.polymover.factory.PolyMoverFactory;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Fonts;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.*;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.movers.factories.ExgonMoverFactory;
import yugecin.opsudance.movers.factories.QuadraticBezierMoverFactory;
import yugecin.opsudance.movers.slidermovers.DefaultSliderMoverController;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.CachedVariable;
import yugecin.opsudance.utils.CachedVariable.Getter;
import java.io.File;
import java.util.concurrent.TimeUnit;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class Options {
// TODO remove this?
public static int width;
public static int height;
static {
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
if (event.width > 0) {
width = event.width;
height = event.height;
}
}
});
}
// internal options (not displayed in-game)
public static final Option OPTION_BEATMAP_DIRECTORY = new Option("BeatmapDirectory") {
@Override
public String write() {
return config.BEATMAP_DIR.getAbsolutePath();
}
@Override
public void read(String s) {
config.beatmapDir = new File(s);
}
};
public static final Option OPTION_OSZ_DIRECTORY = new Option("OSZDirectory") {
@Override
public String write() {
return config.oszDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.oszDir = new File(s);
}
};
public static final Option OPTION_SCREENSHOT_DIRECTORY = new Option("ScreenshotDirectory") {
@Override
public String write() {
return config.screenshotDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.screenshotDir = new File(s);
}
};
public static final Option OPTION_REPLAY_DIRECTORY = new Option("ReplayDirectory") {
@Override
public String write() {
return config.replayDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.replayDir = new File(s);
}
};
public static final Option OPTION_REPLAY_IMPORT_DIRECTORY = new Option("ReplayImportDirectory") {
@Override
public String write() {
return config.replayImportDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.replayImportDir = new File(s);
}
};
public static final Option OPTION_SKIN_DIRECTORY = new Option("SkinDirectory") {
@Override
public String write() {
return config.skinRootDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.skinRootDir = new File(s);
}
};
public static final NumericOption OPTION_PORT = new NumericOption("-", "Port", "-", 49250, 1024, 65535) {
@Override
public void read (String s){
super.read(s);
}
};
public static final ToggleOption OPTION_NOSINGLEINSTANCE = new ToggleOption("-", "NoSingleInstance", "-", false);
// in-game options
public static final Option OPTION_SCREEN_RESOLUTION = new ListOption("Screen Resolution", "ScreenResolution", "Change the size of the game.") {
private final String[] resolutions = {
null,
"800x600",
"1024x600",
"1024x768",
"1280x720",
"1280x800",
"1280x960",
"1280x1024",
"1366x768",
"1440x900",
"1600x900",
"1600x1200",
"1680x1050",
"1920x1080",
"1920x1200",
"2560x1440",
"2560x1600",
"3840x2160"
};
private int idx;
@Override
public String getValueString () {
return resolutions[idx]; // do not change (see DisplayContainer#setup)
}
@Override
public Object[] getListItems () {
return resolutions;
}
@Override
public void clickListItem(int index){
idx = index;
displayContainer.updateDisplayMode(resolutions[idx]);
}
@Override
public void read (String s){
resolutions[0] = displayContainer.nativeDisplayMode.getWidth() + "x" + displayContainer.nativeDisplayMode.getHeight();
try {
idx = Integer.parseInt(s);
} catch (Exception ignored) {
}
idx = Utils.clamp(idx, 0, resolutions.length);
}
@Override
public String write () {
return String.valueOf(idx);
}
};
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 ListOption OPTION_SKIN = new ListOption("Skin", "Skin", "Change how the game looks.") {
private CachedVariable<SkinService> skinService = new CachedVariable<>(new Getter<SkinService>() {
@Override
public SkinService get() {
return instanceContainer.provide(SkinService.class);
}
});
@Override
public String getValueString () {
return skinService.get().usedSkinName;
}
@Override
public Object[] getListItems () {
return skinService.get().availableSkinDirectories;
}
@Override
public void clickListItem(int index){
skinService.get().usedSkinName = skinService.get().availableSkinDirectories[index];
skinService.get().reloadSkin();
}
@Override
public void read (String s){
skinService.get().usedSkinName = s;
}
@Override
public String write() {
return skinService.get().usedSkinName;
}
};
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) {
@Override
public String getValueString () {
return String.format("%dups", val);
}
@Override
public void setValue ( int value){
super.setValue(value);
displayContainer.setUPS(value);
}
};
public static final int[] targetFPS = {60, 120, 240, 1000};
public static int targetFPSIndex = 0;
public static final ListOption OPTION_TARGET_FPS = new ListOption("FPS limit", "FPSlimit", "Higher values may cause high CPU usage. A value higher than the UPS has no effect.") {
private CachedVariable<String[]> $_getListItems = new CachedVariable<>(new Getter<String[]>() {
@Override
public String[] get() {
String[] list = new String[targetFPS.length];
for (int i = 0; i < targetFPS.length; i++) {
list[i] = String.format("%dfps", targetFPS[i]);
}
return list;
}
});
@Override
public String getValueString () {
return $_getListItems.get()[targetFPSIndex];
}
@Override
public Object[] getListItems () {
return $_getListItems.get();
}
@Override
public void clickListItem(int index){
targetFPSIndex = index;
displayContainer.setFPS(targetFPS[targetFPSIndex]);
}
@Override
public String write () {
return Integer.toString(targetFPS[targetFPSIndex]);
}
@Override
public void read (String s){
int i = Integer.parseInt(s);
for (int j = 0; j < targetFPS.length; j++) {
if (i == targetFPS[j]) {
targetFPSIndex = j;
break;
}
}
}
};
public static final ToggleOption OPTION_SHOW_FPS = new ToggleOption("Show FPS Counters", "FpsCounter", "Show FPS and UPS counters in the bottom-right hand corner.", true);
public static final ToggleOption OPTION_USE_FPS_DELTAS = new ToggleOption("Use deltas for FPS counters", "FpsCounterDeltas", "Show time between updates instead of updates per second.", false) {
@Override
public boolean showCondition () {
return OPTION_SHOW_FPS.state;
}
};
public static final ToggleOption OPTION_SHOW_UNICODE = new ToggleOption("Prefer Non-English Metadata", "ShowUnicode", "Where available, song titles will be shown in their native language.", false) {
@Override
public void toggle () {
super.toggle();
if (!state) {
return;
}
try {
Fonts.LARGE.loadGlyphs();
Fonts.MEDIUM.loadGlyphs();
Fonts.DEFAULT.loadGlyphs();
} catch (SlickException e) {
Log.warn("Failed to load glyphs.", e);
}
}
};
public static final ListOption OPTION_SCREENSHOT_FORMAT = new ListOption("Screenshot Format", "ScreenshotFormat", "Press F12 to take a screenshot.") {
private String[] formats = { "PNG", "JPG", "BMP" };
private int index = 0;
@Override
public String getValueString () {
return formats[index];
}
@Override
public Object[] getListItems () {
return formats;
}
@Override
public void clickListItem(int index){
this.index = index;
}
@Override
public String write () {
return Integer.toString(index);
}
@Override
public void read (String s){
int i = Integer.parseInt(s);
if (0 <= i && i < formats.length) {
index = i;
}
}
};
public static final NumericOption OPTION_CURSOR_SIZE = new NumericOption("Size", "CursorSize", "Change the cursor scale.", 100, 50, 200) {
@Override
public String getValueString () {
return String.format("%.2fx", val / 100f);
}
@Override
public String write () {
return String.format("%.2f", val / 100f);
}
@Override
public void read (String s){
int i = (int) (Float.parseFloat(s.replace(',', '.')) * 100f);
if (i >= 50 && i <= 200)
val = i;
}
};
public static final ToggleOption OPTION_NEW_CURSOR = new ToggleOption("Enable New Cursor", "NewCursor", "Use the new cursor style (may cause higher CPU usage).", true);
public static final ToggleOption OPTION_DYNAMIC_BACKGROUND = new ToggleOption("Enable Dynamic Backgrounds", "DynamicBackground", "The song background will be used as the main menu background.", true);
public static final ToggleOption OPTION_LOAD_VERBOSE = new ToggleOption("Show Detailed Loading Progress", "LoadVerbose", "Display more specific loading information in the splash screen.", false);
public static final ToggleOption OPTION_COLOR_MAIN_MENU_LOGO = new ToggleOption("Use cursor color as main menu logo tint", "ColorMainMenuLogo", "Colorful main menu logo", false);
public static final NumericOption OPTION_MASTER_VOLUME = new NumericOption("Master", "VolumeUniversal", "Global volume level.", 35, 0, 100) {
@Override
public void setValue(int value){
super.setValue(value);
// changing mastervolume, so music volume should change too
OPTION_MUSIC_VOLUME.setValue(OPTION_MUSIC_VOLUME.val);
}
};
public static final NumericOption OPTION_MUSIC_VOLUME = new NumericOption("Music", "VolumeMusic", "Volume of music.", 80, 0, 100) {
@Override
public void setValue(int value){
super.setValue(value);
SoundStore.get().setMusicVolume(OPTION_MASTER_VOLUME.val * OPTION_MUSIC_VOLUME.val / 10000f);
}
};
public static final NumericOption OPTION_SAMPLE_VOLUME_OVERRIDE = new NumericOption("Sample override", "BMSampleOverride", "Override beatmap hitsound volume", 100, 0, 100) {
@Override
public String getValueString () {
if (val == 0) {
return "Disabled";
}
return super.getValueString();
}
};
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) {
@Override
public String getValueString () {
return String.format("%dms", val);
}
};
public static final ToggleOption OPTION_DISABLE_SOUNDS = new ToggleOption("Disable All Sound Effects", "DisableSound", "May resolve Linux sound driver issues. Requires a restart.", (System.getProperty("os.name").toLowerCase().contains("linux")));
public static final GenericOption OPTION_KEY_LEFT = new GenericOption("Left Game Key", "keyOsuLeft", "Select this option to input a key.", Input.KEY_Z, null, false) {
@Override
public String getValueString () {
return Keyboard.getKeyName(intval);
}
@Override
public String write () {
return Keyboard.getKeyName(intval);
}
@Override
public void read(String s){
intval = Keyboard.getKeyIndex(s);
if (intval == Keyboard.KEY_NONE) {
intval = Input.KEY_Y;
}
}
};
public static final GenericOption OPTION_KEY_RIGHT = new GenericOption("Right Game Key", "keyOsuRight", "Select this option to input a key.", Input.KEY_X, null, false) {
@Override
public String getValueString () {
return Keyboard.getKeyName(intval);
}
@Override
public String write () {
return Keyboard.getKeyName(intval);
}
@Override
public void read(String s){
intval = Keyboard.getKeyIndex(s);
if (intval == Keyboard.KEY_NONE) {
intval = Input.KEY_X;
}
}
};
public static final NumericOption OPTION_BACKGROUND_DIM = new NumericOption("Background Dim", "DimLevel", "Percentage to dim the background image during gameplay.", 50, 0, 100);
public static final ToggleOption OPTION_DISABLE_MOUSE_WHEEL = new ToggleOption("Disable mouse wheel in play mode", "MouseDisableWheel", "During play, you can use the mouse wheel to adjust the volume and pause the game. This will disable that functionality.", false);
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() {
EventBus.post(new BarNotificationEvent(state ? "Mouse buttons are disabled." : "Mouse buttons are enabled."));
}
};
public static final ToggleOption OPTION_DISABLE_CURSOR = new ToggleOption("Disable Cursor", "DisableCursor", "Hide the cursor sprite.", false);
public static final ToggleOption OPTION_DANCE_REMOVE_BG = new ToggleOption("Use black background instead of image", "RemoveBG", "Hello darkness my old friend", true);
public static final ToggleOption OPTION_FORCE_DEFAULT_PLAYFIELD = new ToggleOption("Force Default Playfield", "ForceDefaultPlayfield", "Override the song background with the default playfield background.", false);
public static final ToggleOption OPTION_IGNORE_BEATMAP_SKINS = new ToggleOption("Ignore All Beatmap Skins", "IgnoreBeatmapSkins", "Never use skin element overrides provided by beatmaps.", false);
public static final ToggleOption OPTION_SNAKING_SLIDERS = new ToggleOption("Snaking sliders", "SnakingSliders", "Sliders gradually snake out from their starting point.", true);
public static final ToggleOption OPTION_SHRINKING_SLIDERS = new ToggleOption("Shrinking sliders", "ShrinkingSliders", "Sliders shrinks when sliderball passes (aka knorkesliders)", true);
public static final ToggleOption OPTION_FALLBACK_SLIDERS = new ToggleOption("Fallback sliders", "FallbackSliders", "Enable this if sliders won't render", false);
public static final ToggleOption OPTION_MERGING_SLIDERS = new ToggleOption("Merging sliders", "MergingSliders", "Merge sliders (aka knorkesliders)", true) {
@Override
public boolean showCondition () {
return !OPTION_FALLBACK_SLIDERS.state;
}
};
public static final NumericOption OPTION_MERGING_SLIDERS_MIRROR_POOL = new NumericOption("Merging sliders mirror pool", "MergingSliderMirrorPool", "Amount of mirrors to calculate for merging sliders (impacts performance)", 2, 1, 5) {
@Override
public String getValueString () {
return String.valueOf(val);
}
@Override
public boolean showCondition () {
return OPTION_MERGING_SLIDERS.showCondition() && OPTION_MERGING_SLIDERS.state;
}
};
public static final ToggleOption OPTION_DRAW_SLIDER_ENDCIRCLES = new ToggleOption("Draw endcircles", "DrawSliderEndCircles", "Old slider style", false);
public static final ToggleOption OPTION_DANCING_CIRCLES = new ToggleOption("Enable", "DancingHitcircles", "Make hitcircles dance to the beat", false);
public static final NumericOption OPTION_DANCING_CIRCLES_MULTIPLIER = new NumericOption("Multiplier", "DancingHitcirclesMP", "Multiplier to expand the hitcircles when dancing to the beat", 50, 1, 200) {
@Override
public String getValueString () {
return String.format("%.1f%%", val / 10f);
}
};
public static final ToggleOption OPTION_SHOW_HIT_LIGHTING = new ToggleOption("Show Hit Lighting", "HitLighting", "Adds an effect behind hit explosions.", true);
public static final ToggleOption OPTION_SHOW_HIT_ANIMATIONS = new ToggleOption("Show Hit Animations", "HitAnimations", "Fade out circles and curves.", true);
public static final ToggleOption OPTION_SHOW_REVERSEARROW_ANIMATIONS = new ToggleOption("Show reverse arrow animations", "ReverseArrowAnimations", "Fade out reverse arrows after passing.", true);
public static final ToggleOption OPTION_SHOW_COMBO_BURSTS = new ToggleOption("Show Combo Bursts", "ComboBurst", "A character image is displayed at combo milestones.", true);
public static final ToggleOption OPTION_SHOW_PERFECT_HIT = new ToggleOption("Show Perfect Hits", "PerfectHit", "Whether to show perfect hit result bursts (300s, slider ticks).", true);
public static final ToggleOption OPTION_SHOW_FOLLOW_POINTS = new ToggleOption("Show Follow Points", "FollowPoints", "Whether to show follow points between hit objects.", true);
public static final ToggleOption OPTION_SHOW_HIT_ERROR_BAR = new ToggleOption("Show Hit Error Bar", "ScoreMeter", "Shows precisely how accurate you were with each hit.", false);
public static final NumericOption OPTION_MAP_START_DELAY = new NumericOption("Map start delay", "StartDelay", "Have a fix amount of time to prepare your play/record", 20, 1, 50) {
@Override
public String getValueString () {
return (val * 100) + "ms";
}
};
public static final NumericOption OPTION_MAP_END_DELAY = new NumericOption("Map end delay", "EndDelay", "Have a fix amount of time at the and of the map for a smooth finish", 50, 1, 150) {
@Override
public String getValueString () {
return (val * 100) + "ms";
}
};
public static final NumericOption OPTION_EPILEPSY_WARNING = new NumericOption("Epilepsy warning image", "EpiWarn", "Show a little warning for flashing colours in the beginning", 0, 0, 20) {
@Override
public String getValueString () {
if (val == 0) {
return "Disabled";
}
return (val * 100) + "ms";
}
};
public static final ToggleOption OPTION_LOAD_HD_IMAGES = new ToggleOption("Load HD Images", "LoadHDImages", String.format("Loads HD (%s) images when available. Increases memory usage and loading times.", GameImage.HD_SUFFIX), true);
public static final NumericOption OPTION_FIXED_CS = new NumericOption("Fixed CS", "FixedCS", "Determines the size of circles and sliders.", 0, 0, 100) {
@Override
public String getValueString () {
return (val == 0) ? "Disabled" : String.format("%.1f", val / 10f);
}
@Override
public String write () {
return String.format("%.1f", val / 10f);
}
@Override
public void read (String s){
int i = (int) (Float.parseFloat(s.replace(',', '.')) * 10f);
if (i >= 0 && i <= 100)
val = i;
}
};
public static final NumericOption OPTION_FIXED_HP = new NumericOption("Fixed HP", "FixedHP", "Determines the rate at which health decreases.", 0, 0, 100) {
@Override
public String getValueString () {
return (val == 0) ? "Disabled" : String.format("%.1f", val / 10f);
}
@Override
public String write () {
return String.format("%.1f", val / 10f);
}
@Override
public void read (String s){
int i = (int) (Float.parseFloat(s.replace(',', '.')) * 10f);
if (i >= 0 && i <= 100)
val = i;
}
};
public static final NumericOption OPTION_FIXED_AR = new NumericOption("Fixed AR", "FixedAR", "Determines how long hit circles stay on the screen.", 0, 0, 100) {
@Override
public String getValueString () {
return (val == 0) ? "Disabled" : String.format("%.1f", val / 10f);
}
@Override
public String write () {
return String.format("%.1f", val / 10f);
}
@Override
public void read (String s){
int i = (int) (Float.parseFloat(s.replace(',', '.')) * 10f);
if (i >= 0 && i <= 100)
val = i;
}
};
public static final NumericOption OPTION_FIXED_OD = new NumericOption("Fixed OD", "FixedOD", "Determines the time window for hit results.", 0, 0, 100) {
@Override
public String getValueString () {
return (val == 0) ? "Disabled" : String.format("%.1f", val / 10f);
}
@Override
public String write () {
return String.format("%.1f", val / 10f);
}
@Override
public void read (String s){
int i = (int) (Float.parseFloat(s.replace(',', '.')) * 10f);
if (i >= 0 && i <= 100)
val = i;
}
};
public static final NumericOption OPTION_CHECKPOINT = new NumericOption("Track Checkpoint", "Checkpoint", "Press Ctrl+L while playing to load a checkpoint, and Ctrl+S to set one.", 0, 0, 3599) {
@Override
public String getValueString () {
return (val == 0) ? "Disabled" : String.format("%02d:%02d",
TimeUnit.SECONDS.toMinutes(val),
val - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(val)));
}
};
public static final ToggleOption OPTION_ENABLE_THEME_SONG = new ToggleOption("Enable Theme Song", "MenuMusic", "Whether to play the theme song upon starting opsu!", true);
public static final ToggleOption OPTION_REPLAY_SEEKING = new ToggleOption("Replay Seeking", "ReplaySeeking", "Enable a seeking bar on the left side of the screen during replays.", false);
public static final ToggleOption OPTION_DISABLE_UPDATER = new ToggleOption("Disable Automatic Updates", "DisableUpdater", "Disable automatic checking for updates upon starting opsu!.", false);
public static final ToggleOption OPTION_ENABLE_WATCH_SERVICE = new ToggleOption("Enable Watch Service", "WatchService", "Watch the beatmap directory for changes. Requires a restart.", false);
public static final ListOption OPTION_DANCE_MOVER = new ListOption("Algorithm", "Mover", "Algorithm that decides how to move from note to note" ) {
@Override
public Object[] getListItems () {
return Dancer.moverFactories;
}
@Override
public void clickListItem(int index){
if (Game.isInGame && Dancer.moverFactories[index] instanceof PolyMoverFactory) {
// TODO remove this when #79 is fixed
EventBus.post(new BarNotificationEvent("This mover is disabled in the storyboard right now"));
return;
}
Dancer.instance.setMoverFactoryIndex(index);
}
@Override
public String getValueString () {
return Dancer.moverFactories[Dancer.instance.getMoverFactoryIndex()].toString();
}
@Override
public String write () {
return String.valueOf(Dancer.instance.getMoverFactoryIndex());
}
@Override
public void read (String s){
int i = Integer.parseInt(s);
Dancer.instance.setMoverFactoryIndex(i);
}
};
public static final NumericOption OPTION_DANCE_EXGON_DELAY = new NumericOption("ExGon delay", "ExGonDelay", "Delay between moves for the ExGon mover", 25, 2, 750) {
@Override
public String getValueString () {
return String.valueOf(val);
}
@Override
public boolean showCondition () {
return Dancer.moverFactories[Dancer.instance.getMoverFactoryIndex()] instanceof ExgonMoverFactory;
}
};
public static final NumericOption OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS = new NumericOption("Bezier aggressiveness", "QuadBezAgr", "AKA initial D factor", 50, 0, 200) {
@Override
public String getValueString () {
return String.valueOf(val);
}
@Override
public boolean showCondition () {
return Dancer.moverFactories[Dancer.instance.getMoverFactoryIndex()] instanceof QuadraticBezierMoverFactory;
}
};
public static final NumericOption OPTION_DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR = new NumericOption("Exit aggressiveness", "CubBezSliderExitAgr", "AKA initial D factor for sliderexits", 4, 1, 6) {
@Override
public String getValueString () {
return String.valueOf(val);
}
@Override
public boolean showCondition () {
return OPTION_DANCE_QUAD_BEZ_AGGRESSIVENESS.showCondition()
&& Dancer.sliderMoverController instanceof DefaultSliderMoverController;
}
};
public static final ToggleOption OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS = new ToggleOption("Use cubic bezier before sliders", "QuadBezCubicSliders", "Slider entry looks better using this", true) {
@Override
public boolean showCondition () {
return OPTION_DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR.showCondition();
}
};
public static final NumericOption OPTION_DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR = new NumericOption("Entry aggressiveness", "CubBezSliderEntryAgr", "AKA initial D factor for sliderentries", 4, 1, 6) {
@Override
public String getValueString () {
return String.valueOf(val);
}
@Override
public boolean showCondition () {
return OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS.showCondition()
&& OPTION_DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS.state;
}
};
public static final ListOption OPTION_DANCE_MOVER_DIRECTION = new ListOption("Direction", "MoverDirection", "The direction the mover goes" ) {
@Override
public String getValueString () {
return Dancer.moverDirection.toString();
}
@Override
public Object[] getListItems () {
return MoverDirection.values();
}
@Override
public void clickListItem(int index){
Dancer.moverDirection = MoverDirection.values()[index];
}
@Override
public String write () {
return "" + Dancer.moverDirection.nr;
}
@Override
public void read (String s){
Dancer.moverDirection = MoverDirection.values()[Integer.parseInt(s)];
}
};
public static final ListOption OPTION_DANCE_SLIDER_MOVER_TYPE = new ListOption("Slider mover", "SliderMover", "How to move in sliders") {
private int val;
@Override
public String getValueString () {
return Dancer.sliderMoverController.toString();
}
@Override
public Object[] getListItems () {
return Dancer.sliderMovers;
}
@Override
public void clickListItem(int index){
val = index;
Dancer.sliderMoverController = Dancer.sliderMovers[index];
}
@Override
public String write () {
return String.valueOf(val);
}
@Override
public void read (String s){
Dancer.sliderMoverController = Dancer.sliderMovers[val = Integer.parseInt(s)];
}
};
public static final ListOption OPTION_DANCE_SPINNER = new ListOption("Algorithm", "Spinner", "Spinner style") {
@Override
public Object[] getListItems () {
return Dancer.spinners;
}
@Override
public void clickListItem(int index){
Dancer.instance.setSpinnerIndex(index);
}
@Override
public String getValueString () {
return Dancer.spinners[Dancer.instance.getSpinnerIndex()].toString();
}
@Override
public String write () {
return Dancer.instance.getSpinnerIndex() + "";
}
@Override
public void read (String s){
Dancer.instance.setSpinnerIndex(Integer.parseInt(s));
}
};
public static final NumericOption OPTION_DANCE_SPINNER_DELAY = new NumericOption("Delay", "SpinnerDelay", "Fiddle with this if spinner goes too fast.", 3, 0, 20) {
@Override
public String getValueString () {
return String.format("%dms", val);
}
};
public static final ToggleOption OPTION_DANCE_LAZY_SLIDERS = new ToggleOption("Lazy sliders", "LazySliders", "Don't do short sliders", false);
public static final ToggleOption OPTION_DANCE_ONLY_CIRCLE_STACKS = new ToggleOption("Only circle stacks", "CircleStacks", "Only do circle movement on stacks", false);
public static final ToggleOption OPTION_DANCE_CIRCLE_STREAMS = new ToggleOption("Circle streams", "CircleStreams", "Make circles while streaming", false);
public static final ToggleOption OPTION_DANCE_MIRROR = new ToggleOption("Mirror collage", "MirrorCollage", "Hypnotizing stuff. Toggle this ingame by pressing the M key.", false);
public static final ToggleOption OPTION_DANCE_DRAW_APPROACH = new ToggleOption("Draw approach circles", "DrawApproach", "Can get a bit busy when using mirror collage", true);
public static final ListOption OPTION_DANCE_OBJECT_COLOR_OVERRIDE = new ListOption("Color", "ObjColorOverride", "Override object colors") {
@Override
public String getValueString () {
return Dancer.colorOverride.toString();
}
@Override
public Object[] getListItems () {
return ObjectColorOverrides.values();
}
@Override
public void clickListItem(int index){
Dancer.colorOverride = ObjectColorOverrides.values()[index];
}
@Override
public String write () {
return "" + Dancer.colorOverride.nr;
}
@Override
public void read (String s){
Dancer.colorOverride = ObjectColorOverrides.values()[Integer.parseInt(s)];
}
};
public static final ListOption OPTION_DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED = new ListOption("Mirror color", "ObjColorMirroredOverride", "Override collage object colors") {
@Override
public String getValueString () {
return Dancer.colorMirrorOverride.toString();
}
@Override
public Object[] getListItems () {
return ObjectColorOverrides.values();
}
@Override
public void clickListItem(int index){
Dancer.colorMirrorOverride = ObjectColorOverrides.values()[index];
}
@Override
public String write () {
return "" + Dancer.colorMirrorOverride.nr;
}
@Override
public void read (String s){
Dancer.colorMirrorOverride = ObjectColorOverrides.values()[Integer.parseInt(s)];
}
};
public static final NumericOption OPTION_DANCE_RGB_OBJECT_INC = new NumericOption("RGB increment", "RGBInc", "Amount of hue to shift, used for rainbow object override", 70, -1800, 1800) {
@Override
public String getValueString () {
return String.format("%.1f°", val / 10f);
}
};
public static final ListOption OPTION_DANCE_CURSOR_COLOR_OVERRIDE = new ListOption("Color", "CursorColorOverride", "Override cursor color") {
@Override
public String getValueString () {
return Dancer.cursorColorOverride.toString();
}
@Override
public Object[] getListItems () {
return CursorColorOverrides.values();
}
@Override
public void clickListItem(int index){
Dancer.cursorColorOverride = CursorColorOverrides.values()[index];
}
@Override
public String write () {
return "" + Dancer.cursorColorOverride.nr;
}
@Override
public void read (String s){
Dancer.cursorColorOverride = CursorColorOverrides.values()[Integer.parseInt(s)];
}
};
public static final ListOption OPTION_DANCE_CURSOR_MIRROR_COLOR_OVERRIDE = new ListOption("Mirror color", "CursorMirrorColorOverride", "Override mirror cursor color") {
@Override
public String getValueString () {
return Dancer.cursorColorMirrorOverride.toString();
}
@Override
public Object[] getListItems () {
return CursorColorOverrides.values();
}
@Override
public void clickListItem(int index){
Dancer.cursorColorMirrorOverride = CursorColorOverrides.values()[index];
}
@Override
public String write () {
return "" + Dancer.cursorColorMirrorOverride.nr;
}
@Override
public void read (String s){
Dancer.cursorColorMirrorOverride = CursorColorOverrides.values()[Integer.parseInt(s)];
}
};
public static final ToggleOption OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL = new ToggleOption("Only color cursor trail", "OnlyColorTrail", "Don't color the cursor, only the trail", false);
public static final NumericOption OPTION_DANCE_RGB_CURSOR_INC = new NumericOption("RGB cursor increment", "RGBCursorInc", "Amount of hue to shift, used for rainbow cursor override", 100, -2000, 2000) {
@Override
public String getValueString () {
return String.format("%.2f°", val / 1000f);
}
};
public static final NumericOption OPTION_DANCE_CURSOR_TRAIL_OVERRIDE = new NumericOption("Trail length", "CursorTrailOverride", "Override cursor trail length", 20, 20, 600) {
@Override
public String getValueString () {
if (val == 20) {
return "Disabled";
}
return "" + val;
}
};
public static final ToggleOption OPTION_DANCE_HIDE_OBJECTS = new ToggleOption("Don't draw objects", "HideObj", "If you only want to see cursors :)", false);
public static final ToggleOption OPTION_DANCE_CIRLCE_IN_SLOW_SLIDERS = new ToggleOption("Do circles in slow sliders", "CircleInSlider", "Circle around sliderball in lazy & slow sliders", false);
public static final ToggleOption OPTION_DANCE_CIRLCE_IN_LAZY_SLIDERS = new ToggleOption("Do circles in lazy sliders", "CircleInLazySlider", "Circle in hitcircle in lazy sliders", false);
public static final ToggleOption OPTION_DANCE_HIDE_UI = new ToggleOption("Hide all UI", "HideUI", ".", true);
public static final ToggleOption OPTION_DANCE_ENABLE_SB = new ToggleOption("Enable storyboard editor", "EnableStoryBoard", "Dance storyboard", false);
public static final ToggleOption OPTION_PIPPI_ENABLE = new ToggleOption("Enable", "Pippi", "Move in circles like dancing pippi (osu! april fools joke 2016)", false);
public static final NumericOption OPTION_PIPPI_RADIUS_PERCENT = new NumericOption("Radius", "PippiRad", "Radius of pippi, percentage of circle radius", 100, 0, 100) {
@Override
public String getValueString () {
return val + "%";
}
@Override
public void setValue ( int value){
super.setValue(value);
Pippi.setRadiusPercent(value);
}
};
public static final NumericOption OPTION_PIPPI_ANGLE_INC_MUL = new NumericOption("Normal", "PippiAngIncMul", "How fast pippi's angle increments", 10, -200, 200) {
@Override
public String getValueString () {
return String.format("x%.1f", val / 10f);
}
};
public static final NumericOption OPTION_PIPPI_ANGLE_INC_MUL_SLIDER = new NumericOption("In slider", "PippiAngIncMulSlider", "Same as above, but in sliders", 50, -200, 200) {
@Override
public String getValueString () {
return String.format("x%.1f", val / 10f);
}
};
public static final ToggleOption OPTION_PIPPI_SLIDER_FOLLOW_EXPAND = new ToggleOption("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false);
public static final ToggleOption OPTION_PIPPI_PREVENT_WOBBLY_STREAMS = new ToggleOption("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true);
}

View File

@@ -0,0 +1,118 @@
/*
* 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.options;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class OptionsService {
@Inject
private Configuration config;
public static final HashMap<String, Option> optionMap = new HashMap<>();
@Inject
public OptionsService(InstanceContainer instanceContainer) {
Option.setInstanceContainer(instanceContainer);
}
public static void registerOption(Option option) {
optionMap.put(option.configurationName, option);
}
public void loadOptions() {
// if no config file, use default settings
if (!config.OPTIONS_FILE.isFile()) {
saveOptions();
return;
}
// read file
try (BufferedReader in = new BufferedReader(new FileReader(config.OPTIONS_FILE))) {
String line;
while ((line = in.readLine()) != null) {
line = line.trim();
if (line.length() < 2 || line.charAt(0) == '#') {
continue;
}
int index = line.indexOf('=');
if (index == -1) {
continue;
}
// read option
String name = line.substring(0, index).trim();
Option option = optionMap.get(name);
if (option != null) {
try {
String value = line.substring(index + 1).trim();
option.read(value);
} catch (Exception e) {
Log.warn(String.format("Format error in options file for line: '%s'.", line), e);
}
}
}
} catch (IOException e) {
String err = String.format("Failed to read option file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
}
config.loadDirectories();
}
public void saveOptions() {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(config.OPTIONS_FILE), "utf-8"))) {
// header
SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM dd, yyyy");
String date = dateFormat.format(new Date());
writer.write("# opsu! configuration");
writer.newLine();
writer.write("# last updated on ");
writer.write(date);
writer.newLine();
writer.newLine();
// options
for (Option option : optionMap.values()) {
writer.write(option.configurationName);
writer.write(" = ");
writer.write(option.write());
writer.newLine();
}
writer.close();
} catch (IOException e) {
String err = String.format("Failed to write to file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.options;
public class ToggleOption extends Option {
public boolean state;
public ToggleOption(String name, String configurationName, String description, boolean state) {
super(name, configurationName, description);
this.state = state;
}
public void toggle() {
this.state = !this.state;
}
@Override
public String write() {
return Boolean.toString(state);
}
@Override
public void read(String s) {
state = Boolean.parseBoolean(s);
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.render;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
public class GameObjectRenderer {
@Inject
private DisplayContainer displayContainer;
private GameData gameData;
private float circleDiameter;
private int circleDiameterInt;
private Image hitcircle;
private Image hitcircleOverlay;
private Image approachCircle;
@Deprecated
public static GameObjectRenderer instance;
public GameObjectRenderer() {
instance = this; // TODO get rid of this
}
public void initForGame(GameData gameData, float circleDiameter) {
this.gameData = gameData;
this.circleDiameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
this.circleDiameterInt = (int) this.circleDiameter;
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
hitcircle = GameImage.HITCIRCLE.getImage();
hitcircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
approachCircle = GameImage.APPROACHCIRCLE.getImage();
}
public float getCircleDiameter() {
return circleDiameter;
}
public void setGameData(GameData gameData) {
this.gameData = gameData;
}
public void initForFrame() {
if (!OPTION_DANCING_CIRCLES.state) {
return;
}
Float position = MusicController.getBeatProgress();
if (position == null) {
position = 0f;
}
int size = circleDiameterInt + (int) (circleDiameter * OPTION_DANCING_CIRCLES_MULTIPLIER.val / 1000f * AnimationEquation.IN_OUT_QUAD.calc(position));
hitcircle = GameImage.HITCIRCLE.getImage().getScaledCopy(size, size);
hitcircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(size, size);
}
public void renderHitCircle(float x, float y, Color color, int comboNumber, float comboNumberAlpha) {
renderHitCircleOnly(x, y, color);
boolean overlayAboveNumber = SkinService.skin.isHitCircleOverlayAboveNumber();
if (!overlayAboveNumber) {
renderHitCircleOverlayOnly(x, y, Colors.WHITE_FADE);
}
renderComboNumberOnly(x, y, comboNumber, comboNumberAlpha);
if (overlayAboveNumber) {
renderHitCircleOverlayOnly(x, y, Colors.WHITE_FADE);
}
}
public void renderHitCircleOnly(float x, float y, Color color) {
hitcircle.drawCentered(x, y, color);
}
public void renderHitCircleOverlayOnly(float x, float y, Color color) {
hitcircleOverlay.drawCentered(x, y, color);
}
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);
}
}
public void renderApproachCircle(float x, float y, Color color, float approachScale) {
if (!GameMod.HIDDEN.isActive() && OPTION_DANCE_DRAW_APPROACH.state) {
approachCircle.getScaledCopy(approachScale).drawCentered(x, y, color);
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.skinning;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.skins.Skin;
import itdelatrisu.opsu.skins.SkinLoader;
import org.newdawn.slick.util.ClasspathLocation;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.options.Configuration;
import java.io.File;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class SkinService {
@Inject
private Configuration config;
public String[] availableSkinDirectories;
public String usedSkinName = "Default";
public static Skin skin;
@Inject
public SkinService() {
}
public void reloadSkin() {
loadSkin();
SoundController.init();
EventBus.post(new ResolutionOrSkinChangedEvent(usedSkinName, -1, -1));
}
/**
* Loads the skin given by the current skin directory.
* If the directory is invalid, the default skin will be loaded.
*/
public void loadSkin() {
File skinDir = getCurrentSkinDirectory();
if (skinDir == null) {
// invalid skin name
usedSkinName = Skin.DEFAULT_SKIN_NAME;
}
// create available skins list
File[] dirs = SkinLoader.getSkinDirectories(config.skinRootDir);
availableSkinDirectories = new String[dirs.length + 1];
availableSkinDirectories[0] = Skin.DEFAULT_SKIN_NAME;
for (int i = 0; i < dirs.length; i++) {
availableSkinDirectories[i + 1] = dirs[i].getName();
}
// set skin and modify resource locations
ResourceLoader.removeAllResourceLocations();
if (skinDir == null) {
skin = new Skin(null);
} else {
// load the skin
skin = SkinLoader.loadSkin(skinDir);
ResourceLoader.addResourceLocation(new FileSystemLocation(skinDir));
}
ResourceLoader.addResourceLocation(new ClasspathLocation());
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
}
/**
* Returns the current skin directory.
* <p>
* NOTE: This directory will differ from that of the currently loaded skin
* if {@link #loadSkin()} has not been called after a directory change.
* Use {@link Skin#getDirectory()} to get the directory of the currently
* loaded skin.
* @return the skin directory, or null for the default skin
*/
public File getCurrentSkinDirectory() {
File dir = new File(config.skinRootDir, usedSkinName);
return (dir.isDirectory()) ? dir : null;
}
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class ApproachCircleSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class BeamSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class CircleSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class CubeSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class DonutSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class FivePointStarApproachSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class FivePointStarSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class HalfCircleSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class IlluminatiSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class LessThanThreeSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class RektCircleSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import yugecin.opsudance.options.Options;
public class RektSpinner extends Spinner {

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import itdelatrisu.opsu.Options;
import static yugecin.opsudance.options.Options.*;
public abstract class Spinner {
@@ -42,7 +42,7 @@ public abstract class Spinner {
}
public boolean waitForDelay() {
if (delay >= Options.getSpinnerDelay()) {
if (delay >= OPTION_DANCE_SPINNER_DELAY.val) {
delay = 0;
return true;
}

View File

@@ -18,9 +18,6 @@
package yugecin.opsudance.ui;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Options.GameOption;
import itdelatrisu.opsu.Options.GameOption.OptionType;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
@@ -30,11 +27,14 @@ import org.newdawn.slick.*;
import org.newdawn.slick.gui.TextField;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.options.*;
import yugecin.opsudance.utils.FontUtil;
import java.util.HashMap;
import java.util.LinkedList;
import static yugecin.opsudance.options.Options.*;
public class OptionsOverlay extends OverlayOpsuState {
private final DisplayContainer displayContainer;
@@ -84,14 +84,14 @@ public class OptionsOverlay extends OverlayOpsuState {
private OptionTab[] sections;
private GameOption hoverOption;
private GameOption selectedOption;
private Option hoverOption;
private Option selectedOption;
private int sliderOptionStartX;
private int sliderOptionLength;
private boolean isAdjustingSlider;
private final HashMap<GameOption, DropdownMenu<Object>> dropdownMenus;
private final HashMap<ListOption, DropdownMenu<Object>> dropdownMenus;
private final LinkedList<DropdownMenu<Object>> visibleDropdownMenus;
private int dropdownMenuPaddingY;
private DropdownMenu<Object> openDropdownMenu;
@@ -195,15 +195,16 @@ public class OptionsOverlay extends OverlayOpsuState {
if (section.options == null) {
continue;
}
for (final GameOption option : section.options) {
Object[] items = option.getListItems();
if (items == null) {
for (final Option option : section.options) {
if (!(option instanceof ListOption)) {
continue;
}
final ListOption listOption = (ListOption) option;
Object[] items = listOption.getListItems();
DropdownMenu<Object> menu = new DropdownMenu<Object>(displayContainer, items, 0, 0, 0) {
@Override
public void itemSelected(int index, Object item) {
option.clickListItem(index);
listOption.clickListItem(index);
openDropdownMenu = null;
}
};
@@ -224,7 +225,7 @@ public class OptionsOverlay extends OverlayOpsuState {
menu.setHighlightColor(COL_COMBOBOX_HOVER);
menu.setTextColor(COL_WHITE);
dropdownMenuPaddingY = (optionHeight - menu.getHeight()) / 2;
dropdownMenus.put(option, menu);
dropdownMenus.put(listOption, menu);
}
}
@@ -301,8 +302,8 @@ public class OptionsOverlay extends OverlayOpsuState {
private void renderTooltip(Graphics g) {
if (hoverOption != null) {
String tip = hoverOption.getDescription();
if (hoverOption.getType() == OptionType.NUMERIC) {
String tip = hoverOption.description;
if (hoverOption instanceof NumericOption) {
tip = "(" + hoverOption.getValueString() + ") " + tip;
}
UI.updateTooltip(displayContainer.renderDelta, tip, true);
@@ -336,7 +337,7 @@ public class OptionsOverlay extends OverlayOpsuState {
}
int lineHeight = (int) (Fonts.LARGE.getLineHeight() * 0.9f);
for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) {
GameOption option = section.options[optionIndex];
Option option = section.options[optionIndex];
if (!option.showCondition() || option.isFiltered()) {
continue;
}
@@ -368,7 +369,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (sections[sectionIndex].options == null) {
continue;
}
for (GameOption option : sections[sectionIndex].options) {
for (Option option : sections[sectionIndex].options) {
if (option.showCondition() && !option.isFiltered()) {
maxScrollOffset += optionHeight;
}
@@ -384,24 +385,22 @@ public class OptionsOverlay extends OverlayOpsuState {
scrollHandler.setMinMax(0, maxScrollOffset);
}
private void renderOption(Graphics g, GameOption option, int y) {
OptionType type = option.getType();
Object[] listItems = option.getListItems();
if (listItems != null) {
renderListOption(g, option, y);
} else if (type == OptionType.BOOLEAN) {
renderCheckOption(option, y);
} else if (type == OptionType.NUMERIC) {
renderSliderOption(g, option, y);
} else {
renderGenericOption(option, y);
private void renderOption(Graphics g, Option option, int y) {
if (option instanceof ListOption) {
renderListOption(g, (ListOption) option, y);
} else if (option instanceof ToggleOption) {
renderCheckOption((ToggleOption) option, y);
} else if (option instanceof NumericOption) {
renderSliderOption(g, (NumericOption) option, y);
} else if (option instanceof GenericOption) {
renderGenericOption((GenericOption) option, y);
}
}
private void renderListOption(Graphics g, GameOption option, int y) {
private void renderListOption(Graphics g, ListOption option, int y) {
// draw option name
int nameWith = Fonts.MEDIUM.getWidth(option.getName());
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE);
int nameWith = Fonts.MEDIUM.getWidth(option.name);
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.name, COL_WHITE);
nameWith += 15;
int comboboxStartX = optionStartX + nameWith;
int comboboxWidth = optionWidth - nameWith;
@@ -424,19 +423,19 @@ public class OptionsOverlay extends OverlayOpsuState {
dropdown.render(g);
}
private void renderCheckOption(GameOption option, int y) {
if (option.getBooleanValue()) {
private void renderCheckOption(ToggleOption option, int y) {
if (option.state) {
checkOnImg.draw(optionStartX, y + controlImagePadding, COL_PINK);
} else {
checkOffImg.draw(optionStartX, y + controlImagePadding, COL_PINK);
}
Fonts.MEDIUM.drawString(optionStartX + 30, y + optionTextOffsetY, option.getName(), COL_WHITE);
Fonts.MEDIUM.drawString(optionStartX + 30, y + optionTextOffsetY, option.name, COL_WHITE);
}
private void renderSliderOption(Graphics g, GameOption option, int y) {
private void renderSliderOption(Graphics g, NumericOption option, int y) {
final int padding = 10;
int nameLen = Fonts.MEDIUM.getWidth(option.getName());
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE);
int nameLen = Fonts.MEDIUM.getWidth(option.name);
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.name, COL_WHITE);
int sliderLen = optionWidth - nameLen - padding;
if (sliderLen <= 1) {
return;
@@ -453,7 +452,7 @@ public class OptionsOverlay extends OverlayOpsuState {
}
}
float sliderValue = (float) (option.getIntegerValue() - option.getMinValue()) / (option.getMaxValue() - option.getMinValue());
float sliderValue = (float) (option.val - option.min) / (option.max - option.min);
float sliderBallPos = sliderStartX + (int) ((sliderLen - controlImageSize) * sliderValue);
g.setLineWidth(3f);
@@ -471,10 +470,10 @@ public class OptionsOverlay extends OverlayOpsuState {
}
}
private void renderGenericOption(GameOption option, int y) {
private void renderGenericOption(GenericOption option, int y) {
String value = option.getValueString();
int valueLen = Fonts.MEDIUM.getWidth(value);
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.getName(), COL_WHITE);
Fonts.MEDIUM.drawString(optionStartX, y + optionTextOffsetY, option.name, COL_WHITE);
Fonts.MEDIUM.drawString(optionStartX + optionWidth - valueLen, y + optionTextOffsetY, value, COL_BLUE);
}
@@ -556,9 +555,9 @@ public class OptionsOverlay extends OverlayOpsuState {
updateIndicatorAlpha();
UI.getBackButton().hoverUpdate(delta, mouseX, mouseY);
if (isAdjustingSlider) {
int sliderValue = hoverOption.getIntegerValue();
int sliderValue = ((NumericOption) hoverOption).val;
updateSliderOption();
if (hoverOption.getIntegerValue() - sliderValue != 0 && sliderSoundDelay <= 0) {
if (((NumericOption) hoverOption).val - sliderValue != 0 && sliderSoundDelay <= 0) {
sliderSoundDelay = 90;
SoundController.playSound(SoundEffect.MENUHIT);
}
@@ -634,7 +633,7 @@ public class OptionsOverlay extends OverlayOpsuState {
mousePressY = y;
selectedOption = hoverOption;
if (hoverOption != null && hoverOption.getType() == OptionType.NUMERIC) {
if (hoverOption != null && hoverOption instanceof NumericOption) {
isAdjustingSlider = sliderOptionStartX <= x && x < sliderOptionStartX + sliderOptionLength;
if (isAdjustingSlider) {
updateSliderOption();
@@ -678,16 +677,16 @@ public class OptionsOverlay extends OverlayOpsuState {
}
if (hoverOption != null) {
if (hoverOption.getType() == OptionType.BOOLEAN) {
hoverOption.click();
if (hoverOption instanceof ToggleOption) {
((ToggleOption) hoverOption).toggle();
if (listener != null) {
listener.onSaveOption(hoverOption);
}
SoundController.playSound(SoundEffect.MENUHIT);
return true;
} else if (hoverOption == GameOption.KEY_LEFT) {
} else if (hoverOption == OPTION_KEY_LEFT) {
keyEntryLeft = true;
} else if (hoverOption == GameOption.KEY_RIGHT) {
} else if (hoverOption == OPTION_KEY_RIGHT) {
keyEntryLeft = true;
}
}
@@ -724,13 +723,17 @@ public class OptionsOverlay extends OverlayOpsuState {
@Override
public boolean onKeyPressed(int key, char c) {
if (keyEntryRight) {
Options.setGameKeyRight(key);
if (Utils.isValidGameKey(key)) {
OPTION_KEY_RIGHT.intval = key;
}
keyEntryRight = false;
return true;
}
if (keyEntryLeft) {
Options.setGameKeyLeft(key);
if (Utils.isValidGameKey(key)) {
OPTION_KEY_LEFT.intval = key;
}
keyEntryLeft = false;
return true;
}
@@ -767,10 +770,9 @@ public class OptionsOverlay extends OverlayOpsuState {
}
private void updateSliderOption() {
int min = hoverOption.getMinValue();
int max = hoverOption.getMaxValue();
int value = min + Math.round((float) (max - min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
hoverOption.setValue(Utils.clamp(value, min, max));
NumericOption o = (NumericOption) hoverOption;
int value = o.min + Math.round((float) (o.max - o.min) * (displayContainer.mouseX - sliderOptionStartX) / (sliderOptionLength));
o.setValue(Utils.clamp(value, o.min, o.max));
}
private void updateHoverOption(int mouseX, int mouseY) {
@@ -797,7 +799,7 @@ public class OptionsOverlay extends OverlayOpsuState {
continue;
}
for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) {
GameOption option = section.options[optionIndex];
Option option = section.options[optionIndex];
if (option.isFiltered() || !option.showCondition()) {
continue;
}
@@ -824,7 +826,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (section.options == null) {
continue;
}
for (GameOption opt : section.options) {
for (Option opt : section.options) {
opt.filter(null);
}
}
@@ -844,7 +846,7 @@ public class OptionsOverlay extends OverlayOpsuState {
continue;
}
section.filtered = true;
for (GameOption option : section.options) {
for (Option option : section.options) {
if (lastBigSectionMatches || sectionMatches) {
section.filtered = false;
option.filter(null);
@@ -859,24 +861,9 @@ public class OptionsOverlay extends OverlayOpsuState {
updateHoverOption(prevMouseX, prevMouseY);
}
public static class OptionTab {
public final String name;
public final GameOption[] options;
private boolean filtered;
public OptionTab(String name, GameOption[] options) {
this.name = name;
this.options = options;
}
}
public interface Listener {
void onLeaveOptionsMenu();
void onSaveOption(GameOption option);
void onSaveOption(Option option);
}
}

View File

@@ -17,12 +17,11 @@
*/
package yugecin.opsudance.ui;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Options.GameOption;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.states.OptionsMenu;
import yugecin.opsudance.options.Option;
import yugecin.opsudance.options.OptionGroups;
import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
@@ -30,15 +29,17 @@ import org.newdawn.slick.Input;
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;
import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
import java.util.*;
import static yugecin.opsudance.options.Options.*;
@SuppressWarnings("unchecked")
public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverlay.Listener {
private final static List<GameOption> optionList = new ArrayList<>();
private final static List<Option> optionList = new ArrayList<>();
private final DisplayContainer displayContainer;
@@ -47,7 +48,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
private int speed;
private GameObject[] gameObjects;
private HashMap[] optionsMap;
private HashMap<Options.GameOption, String> initialOptions;
private HashMap<Option, String> initialOptions;
private int index;
@@ -56,7 +57,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
private final OptionsOverlay optionsOverlay;
static {
for (OptionTab tab : OptionsMenu.storyboardOptions) {
for (OptionTab tab : OptionGroups.storyboardOptions) {
optionList.addAll(Arrays.asList(tab.options));
}
}
@@ -73,7 +74,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
@Override
public void onRender(Graphics g) {
if (!Options.isEnableSB() || hide) {
if (!OPTION_DANCE_ENABLE_SB.state || hide) {
return;
}
int lh = Fonts.SMALL.getLineHeight();
@@ -86,8 +87,8 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
if (index < optionsMap.length && optionsMap[index] != null) {
int i = 0;
for (Object o : optionsMap[index].entrySet()) {
Map.Entry<Options.GameOption, String> option = (Map.Entry<Options.GameOption, String>) o;
Fonts.SMALL.drawString(10, 50 + i * lh, option.getKey().getName(), Color.cyan);
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);
g.fillRect(0, 50 + i * lh + lh / 4, 10, 10);
i++;
@@ -177,7 +178,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
if (optionsMap.length > 0) {
// copy all current settings in first obj map
optionsMap[0] = new HashMap<>();
for (Options.GameOption o : optionList) {
for (Option o : optionList) {
optionsMap[0].put(o, o.write());
}
}
@@ -210,7 +211,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
continue;
}
for (Object o : options.entrySet()) {
Map.Entry<Options.GameOption, String> next = (Map.Entry<Options.GameOption, String>) o;
Map.Entry<Option, String> next = (Map.Entry<Option, String>) o;
next.getKey().read(next.getValue());
readOption(next.getKey());
}
@@ -227,7 +228,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
int ypos = 50 + lh / 4;
for (Object o : optionsMap[index].entrySet()) {
if (y >= ypos && y <= ypos + 10) {
optionsMap[index].remove(((Map.Entry<Options.GameOption, String>) o).getKey());
optionsMap[index].remove(((Map.Entry<Option, String>) o).getKey());
if (optionsMap[index].size() == 0) {
optionsMap[index] = null;
}
@@ -246,7 +247,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
public void onEnter() {
// enter, save current settings
for (Options.GameOption o : optionList) {
for (Option o : optionList) {
initialOptions.put(o, o.write());
}
speed = 10;
@@ -254,7 +255,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
public void onLeave() {
// leave, revert the settings saved before entering
for (Options.GameOption o : optionList) {
for (Option o : optionList) {
if (initialOptions.containsKey(o)) {
o.read(initialOptions.get(o));
readOption(o);
@@ -263,10 +264,10 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
}
// needed for object color overrides...
private void readOption(Options.GameOption o) {
if (o == Options.GameOption.DANCE_OBJECT_COLOR_OVERRIDE
|| o == Options.GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED
|| o == Options.GameOption.DANCE_RGB_OBJECT_INC) {
private void readOption(Option o) {
if (o == OPTION_DANCE_OBJECT_COLOR_OVERRIDE
|| o == OPTION_DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED
|| o == OPTION_DANCE_RGB_OBJECT_INC) {
if (index < gameObjects.length) {
ObjectColorOverrides.hue = gameObjects[index].getHue();
}
@@ -284,7 +285,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
}
@Override
public void onSaveOption(GameOption option) {
public void onSaveOption(Option option) {
if (optionsMap[index] == null) {
optionsMap[index] = new HashMap<>();
}