Merge branch 'master' into replaystuff

# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
This commit is contained in:
yugecin
2017-12-16 16:35:11 +01:00
131 changed files with 2595 additions and 3598 deletions

View File

@@ -40,6 +40,7 @@ import yugecin.opsudance.spinners.*;
import java.awt.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class Dancer {
@@ -194,12 +195,12 @@ public class Dancer {
}
isCurrentLazySlider = false;
// detect lazy sliders, should work pretty good
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) {
if (c.isSlider() && OPTION_DANCE_LAZY_SLIDERS.state && Utils.distance(c.start.x, c.start.y, c.end.x, c.end.y) <= gameObjectRenderer.circleDiameter * 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) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) {
if (s.getRepeats() == 1 || Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= gameObjectRenderer.circleDiameter * 0.8f) {
mid = s.getCurve().pointAt(0.5f);
if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= GameObjectRenderer.instance.getCircleDiameter() * 0.8f) {
if (Utils.distance(c.start.x, c.start.y, mid.x, mid.y) <= gameObjectRenderer.circleDiameter * 0.8f) {
isCurrentLazySlider = true;
}
}
@@ -251,8 +252,8 @@ public class Dancer {
}
}
Pippi.dance(time, c, isCurrentLazySlider);
x = Utils.clamp(x, 10, width - 10);
y = Utils.clamp(y, 10, height - 10);
x = Utils.clamp(x, 10, displayContainer.width - 10);
y = Utils.clamp(y, 10, displayContainer.height - 10);
}
private void createNewMover() {

View File

@@ -21,22 +21,16 @@ import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.db.DBController;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
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;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/*
@@ -44,30 +38,13 @@ import static yugecin.opsudance.options.Options.*;
*/
public class OpsuDance {
@Inject
private DisplayContainer container;
@Inject
private OptionsService optionsService;
@Inject
private Configuration config;
@Inject
private Updater updater;
private ServerSocket singleInstanceSocket;
@Inject
public OpsuDance() {
}
public void start(String[] args) {
try {
sout("initialized");
checkRunningDirectory();
optionsService.loadOptions();
optionservice.loadOptions();
ensureSingleInstance();
sout("prechecks done and options parsed");
@@ -75,15 +52,15 @@ public class OpsuDance {
initUpdater(args);
sout("database & updater initialized");
container.init(Splash.class);
displayContainer.init(splashState);
} catch (Exception e) {
errorAndExit("startup failure", e);
}
while (rungame());
container.teardownAL();
displayContainer.teardownAL();
optionsService.saveOptions();
optionservice.saveOptions();
closeSingleInstanceSocket();
DBController.closeConnections();
DownloadList.get().cancelAllDownloads();
@@ -95,26 +72,26 @@ public class OpsuDance {
private boolean rungame() {
try {
container.setup();
container.resume();
displayContainer.setup();
displayContainer.resume();
} catch (Exception e) {
ErrorHandler.error("could not initialize GL", e).allowTerminate().preventContinue().show();
explode("could not initialize GL", e, ALLOW_TERMINATE | PREVENT_CONTINUE);
return false;
}
Exception caughtException = null;
try {
container.run();
displayContainer.run();
} catch (Exception e) {
caughtException = e;
}
container.teardown();
container.pause();
return caughtException != null && ErrorHandler.error("update/render error", caughtException).allowTerminate().show().shouldIgnoreAndContinue();
displayContainer.teardown();
displayContainer.pause();
return caughtException != null && explode("update/render error", caughtException, ALLOW_TERMINATE);
}
private void initDatabase() {
try {
DBController.init(config);
DBController.init();
} catch (UnsatisfiedLinkError e) {
errorAndExit("Could not initialize database.", e);
}
@@ -142,20 +119,6 @@ public class OpsuDance {
}.start();
}
private void checkRunningDirectory() {
if (!Utils.isJarRunning()) {
return;
}
File runningDir = Utils.getRunningDirectory();
if (runningDir == null) {
return;
}
if (runningDir.getAbsolutePath().indexOf('!') == -1) {
return;
}
errorAndExit("Cannot run from a path that contains a '!'. Please move or rename the jar and try again.");
}
private void ensureSingleInstance() {
if (OPTION_NOSINGLEINSTANCE.state) {
return;
@@ -183,13 +146,8 @@ public class OpsuDance {
}
}
private void errorAndExit(String errstr) {
ErrorHandler.error(errstr, new Throwable()).allowTerminate().preventContinue().show();
System.exit(1);
}
private void errorAndExit(String errstr, Throwable cause) {
ErrorHandler.error(errstr, cause).preventContinue().show();
explode(errstr, cause, PREVENT_CONTINUE);
System.exit(1);
}

View File

@@ -19,9 +19,9 @@ package yugecin.opsudance;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.Slider;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Pippi {
@@ -38,14 +38,14 @@ public class Pippi {
public static void setRadiusPercent(int radiusPercent) {
Pippi.radiusPercent = radiusPercent;
pippiminrad = pippirad = (GameObjectRenderer.instance.getCircleDiameter() / 2d - 10d) * radiusPercent / 100d;
pippiminrad = pippirad = (gameObjectRenderer.circleDiameter / 2d - 10d) * radiusPercent / 100d;
}
public static void reset() {
angle = 0;
currentdelta = 0;
setRadiusPercent(radiusPercent);
pippimaxrad = GameObjectRenderer.instance.getCircleDiameter() - 10d;
pippimaxrad = gameObjectRenderer.circleDiameter - 10d;
}
public static void dance(int time, GameObject c, boolean isCurrentLazySlider) {
@@ -92,7 +92,7 @@ public class Pippi {
}
public static boolean shouldPreventWobblyStream(double distance) {
return OPTION_PIPPI_ENABLE.state && distance < GameObjectRenderer.instance.getCircleDiameter() * 0.93f && OPTION_PIPPI_PREVENT_WOBBLY_STREAMS.state;
return OPTION_PIPPI_ENABLE.state && distance < gameObjectRenderer.circleDiameter * 0.93f && OPTION_PIPPI_PREVENT_WOBBLY_STREAMS.state;
}
}

View File

@@ -1,73 +0,0 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance;
import itdelatrisu.opsu.NativeLoader;
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;
import java.lang.reflect.Field;
public class PreStartupInitializer {
private final Configuration config;
@Inject
public PreStartupInitializer(Configuration config) {
this.config = config;
loadNatives();
setResourcePath();
}
private void loadNatives() {
File nativeDir = loadNativesUsingOptionsPath();
System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath());
System.setProperty("java.library.path", nativeDir.getAbsolutePath());
try {
// Workaround for "java.library.path" property being read-only.
// http://stackoverflow.com/a/24988095
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
Log.warn("Failed to set 'sys_paths' field.", e);
}
}
private File loadNativesUsingOptionsPath() {
File nativeDir = config.NATIVE_DIR;
try {
new NativeLoader(nativeDir).loadNatives();
} catch (IOException e) {
Log.error("Error loading natives.", e);
}
return nativeDir;
}
private void setResourcePath() {
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
}
}

View File

@@ -15,28 +15,18 @@
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
package yugecin.opsudance.core;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import java.net.URI;
public abstract class FadeTransitionState extends TransitionState {
public class Constants {
private final Color black;
public FadeTransitionState() {
super();
black = new Color(Color.black);
}
@Override
public void render(Graphics g) {
applicableState.render(g);
black.a = getMaskAlphaLevel((float) transitionTime / transitionTargetTime);
g.setColor(black);
g.fillRect(0, 0, displayContainer.width, displayContainer.height);
}
protected abstract float getMaskAlphaLevel(float fadeProgress);
public static final String PROJECT_NAME = "opsu!dance";
public static final String FONT_NAME = "DroidSansFallback.ttf";
public static final String VERSION_FILE = "version";
public static final URI REPOSITORY_URI = URI.create("https://github.com/itdelatrisu/opsu");
public static final URI DANCE_REPOSITORY_URI = URI.create("https://github.com/yugecin/opsu-dance");
public static final String ISSUES_URL = "https://github.com/yugecin/opsu-dance/issues/new?title=%s&body=%s";
public static final String VERSION_REMOTE = "https://raw.githubusercontent.com/yugecin/opsu-dance/master/version";
}

View File

@@ -26,10 +26,12 @@ import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI;
import org.lwjgl.Sys;
import org.lwjgl.input.Mouse;
import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
@@ -39,58 +41,38 @@ import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
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;
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
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.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.events.SkinChangedListener;
import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter;
import static yugecin.opsudance.core.Entrypoint.sout;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
/**
* based on org.newdawn.slick.AppGameContainer
*/
public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListener {
@Inject
private SkinService skinService;
@Inject
private Configuration config;
public class DisplayContainer implements ErrorDumpable, ResolutionChangedListener, SkinChangedListener {
private static SGL GL = Renderer.get();
private final InstanceContainer instanceContainer;
private FpsRenderState fpsState;
private BarNotificationState barNotifState;
private BubbleNotificationState bubNotifState;
private TransitionState outTransitionState;
private TransitionState inTransitionState;
private final TransitionFinishedListener outTransitionListener;
private final TransitionFinishedListener inTransitionListener;
private BubNotifState bubNotifState;
private OpsuState state;
public final DisplayMode nativeDisplayMode;
private Graphics graphics;
public Input input;
public int width;
public int height;
@@ -123,38 +105,52 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public final Cursor cursor;
public boolean drawCursor;
@Inject
public DisplayContainer(InstanceContainer instanceContainer) {
this.instanceContainer = instanceContainer;
class Transition {
int in;
int out;
int total;
int progress = -1;
OpsuState nextstate;
Color OVERLAY = new Color(Color.black);
public void update() {
if (progress == -1) {
return;
}
progress += delta;
if (progress > out && nextstate != null) {
switchStateInstantly(nextstate);
nextstate = null;
}
if (progress > total) {
progress = -1;
}
}
public void render(Graphics graphics) {
if (progress == -1) {
return;
}
int relprogress = progress;
int reltotal = out;
if (progress > out) {
reltotal = in;
relprogress = total - progress;
}
OVERLAY.a = (float) relprogress / reltotal;
graphics.setColor(OVERLAY);
graphics.fillRect(0, 0, width, height);
}
}
private final Transition transition = new Transition();
public DisplayContainer() {
this.cursor = new Cursor();
drawCursor = true;
outTransitionListener = new TransitionFinishedListener() {
@Override
public void onFinish() {
state.leave();
outTransitionState.getApplicableState().leave();
state = inTransitionState;
state.enter();
inTransitionState.getApplicableState().enter();
}
};
inTransitionListener = new TransitionFinishedListener() {
@Override
public void onFinish() {
state.leave();
state = inTransitionState.getApplicableState();
}
};
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
destroyImages();
reinit();
}
});
ResolutionChangedListener.EVENT.addListener(this);
SkinChangedListener.EVENT.addListener(this);
this.nativeDisplayMode = Display.getDisplayMode();
targetBackgroundRenderInterval = 41; // ~24 fps
@@ -163,13 +159,25 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
renderDelta = 1;
}
@Override
public void onResolutionChanged(int w, int h) {
destroyImages();
reinit();
}
@Override
public void onSkinChanged(String stringName) {
destroyImages();
reinit();
}
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();
skinservice.loadSkin();
// initialize game images
for (GameImage img : GameImage.values()) {
@@ -196,16 +204,16 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
targetRenderInterval = 1000 / targetRendersPerSecond;
}
public void init(Class<? extends OpsuState> startingState) {
public void init(OpsuState startingState) {
setUPS(OPTION_TARGET_UPS.val);
setFPS(targetFPS[targetFPSIndex]);
state = instanceContainer.provide(startingState);
state.enter();
fpsState = new FpsRenderState();
bubNotifState = new BubNotifState();
barNotifState = new BarNotificationState();
fpsState = instanceContainer.provide(FpsRenderState.class);
bubNotifState = instanceContainer.provide(BubbleNotificationState.class);
barNotifState = instanceContainer.provide(BarNotificationState.class);
state = startingState;
state.enter();
}
@@ -220,6 +228,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
mouseX = input.getMouseX();
mouseY = input.getMouseY();
transition.update();
fpsState.update();
state.update();
@@ -254,13 +263,17 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
cursor.updateAngle(renderDelta);
if (drawCursor) {
cursor.draw(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
cursor.draw(Mouse.isButtonDown(Input.MOUSE_LEFT_BUTTON) ||
Mouse.isButtonDown(Input.MOUSE_RIGHT_BUTTON));
}
UI.drawTooltip(graphics);
transition.render(graphics);
timeSinceLastRender = 0;
Display.update(false);
GL11.glFlush();
}
Display.processMessages();
@@ -270,8 +283,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
public void setup() throws Exception {
width = height = -1;
Input.disableControllers();
Display.setTitle("opsu!dance");
setupResolutionOptionlist(nativeDisplayMode.getWidth(), nativeDisplayMode.getHeight());
updateDisplayMode(OPTION_SCREEN_RESOLUTION.getValueString());
Display.create();
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
@@ -281,6 +294,19 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
GLHelper.hideNativeCursor();
}
// TODO: move this elsewhere
private void setupResolutionOptionlist(int width, int height) {
final Object[] resolutions = OPTION_SCREEN_RESOLUTION.getListItems();
final String nativeRes = width + "x" + height;
resolutions[0] = nativeRes;
for (int i = 0; i < resolutions.length; i++) {
if (nativeRes.equals(resolutions[i].toString())) {
resolutions[i] = resolutions[i] + " (borderless)";
}
}
}
public void teardown() {
destroyImages();
CurveRenderState.shutdown();
@@ -316,13 +342,13 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
return true;
}
if (DownloadList.get().hasActiveDownloads()) {
EventBus.post(new BubbleNotificationEvent(DownloadList.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
BubNotifListener.EVENT.make().onBubNotif(DownloadList.EXIT_CONFIRMATION, Colors.BUB_RED);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
}
if (Updater.get().getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
EventBus.post(new BubbleNotificationEvent(Updater.EXIT_CONFIRMATION, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
if (updater.getStatus() == Updater.Status.UPDATE_DOWNLOADING) {
BubNotifListener.EVENT.make().onBubNotif(Updater.EXIT_CONFIRMATION, Colors.BUB_PURPLE);
exitRequested = false;
exitconfirmation = System.currentTimeMillis();
return false;
@@ -334,6 +360,11 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
int screenWidth = nativeDisplayMode.getWidth();
int screenHeight = nativeDisplayMode.getHeight();
int eos = resolutionString.indexOf(' ');
if (eos > -1) {
resolutionString = resolutionString.substring(0, eos);
}
int width = screenWidth;
int height = screenHeight;
if (resolutionString.matches("^[0-9]+x[0-9]+$")) {
@@ -348,18 +379,17 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
height = 600;
}
if (!OPTION_FULLSCREEN.state) {
boolean borderless = (screenWidth == width && screenHeight == height);
System.setProperty("org.lwjgl.opengl.Window.undecorated", Boolean.toString(borderless));
}
try {
setDisplayMode(width, height, OPTION_FULLSCREEN.state);
} catch (Exception e) {
EventBus.post(new BubbleNotificationEvent("Failed to change resolution", BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif("Failed to change resolution", Colors.BUB_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 {
@@ -377,8 +407,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
displayMode = new DisplayMode(width, height);
if (fullscreen) {
fullscreen = false;
Log.warn("could not find fullscreen displaymode for " + width + "x" + height);
EventBus.post(new BubbleNotificationEvent("Fullscreen mode is not supported for " + width + "x" + height, BubbleNotificationEvent.COLOR_ORANGE));
String msg = String.format("Fullscreen mode is not supported for %sx%s", width, height);
Log.warn(msg);
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_ORANGE);
}
}
@@ -404,17 +435,20 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
graphics = new Graphics(width, height);
graphics.setAntiAlias(false);
input = new Input(height);
input.enableKeyRepeat();
input.addKeyListener(this);
input.addMouseListener(this);
if (input == null) {
input = new Input(height);
input.enableKeyRepeat();
input.addListener(new GlobalInputListener());
input.addMouseListener(bubNotifState);
}
input.addListener(state);
sout("GL ready");
GameImage.init(width, height);
Fonts.init(config);
Fonts.init();
EventBus.post(new ResolutionOrSkinChangedEvent(null, width, height));
ResolutionChangedListener.EVENT.make().onResolutionChanged(width, height);
}
public void resetCursor() {
@@ -432,110 +466,51 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
public boolean isWidescreen() {
return width * 1000 / height > 1500; // 1777 = 16:9, 1333 = 4:3
}
@Override
public void writeErrorDump(StringWriter dump) {
dump.append("> DisplayContainer dump\n");
dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")\n");
if (isTransitioning()) {
dump.append("doing a transition\n");
dump.append("using out transition ").append(outTransitionState.getClass().getSimpleName()).append('\n');
dump.append("using in transition ").append(inTransitionState.getClass().getSimpleName()).append('\n');
if (state == inTransitionState) {
dump.append("currently doing the in transition\n");
} else {
dump.append("currently doing the out transition\n");
}
if (state == null) {
dump.append("state is null!\n");
return;
}
state.writeErrorDump(dump);
}
// TODO change this
public boolean isInState(Class<? extends OpsuState> state) {
return state.isInstance(state);
}
public boolean isTransitioning() {
return state instanceof TransitionState;
public void switchState(OpsuState state) {
switchState(state, 200, 300);
}
public void switchState(Class<? extends OpsuState> newState) {
switchState(newState, FadeOutTransitionState.class, 200, FadeInTransitionState.class, 300);
}
public void switchStateNow(Class<? extends OpsuState> newState) {
switchState(newState, EmptyTransitionState.class, 0, FadeInTransitionState.class, 300);
}
public void switchStateInstantly(Class<? extends OpsuState> newState) {
state.leave();
state = instanceContainer.provide(newState);
state.enter();
}
public void switchState(Class<? extends OpsuState> newState, Class<? extends TransitionState> outTransition, int outTime, Class<? extends TransitionState> inTransition, int inTime) {
if (isTransitioning()) {
public void switchState(OpsuState newstate, int outtime, int intime) {
if (transition.progress != -1) {
return;
}
outTransitionState = instanceContainer.provide(outTransition).set(state, outTime, outTransitionListener);
inTransitionState = instanceContainer.provide(inTransition).set(instanceContainer.provide(newState), inTime, inTransitionListener);
state = outTransitionState;
state.enter();
}
/*
* input events below, see org.newdawn.slick.KeyListener & org.newdawn.slick.MouseListener
*/
@Override
public void keyPressed(int key, char c) {
state.keyPressed(key, c);
}
@Override
public void keyReleased(int key, char c) {
state.keyReleased(key, c);
}
@Override
public void mouseWheelMoved(int change) {
state.mouseWheelMoved(change);
}
@Override
public void mouseClicked(int button, int x, int y, int clickCount) { }
@Override
public void mousePressed(int button, int x, int y) {
state.mousePressed(button, x, y);
}
@Override
public void mouseReleased(int button, int x, int y) {
if (bubNotifState.mouseReleased(x, y)) {
return;
if (outtime == 0) {
switchStateInstantly(newstate);
newstate = null;
}
state.mouseReleased(button, x, y);
transition.nextstate = newstate;
transition.total = transition.in = intime;
transition.out = outtime;
transition.total += outtime;
transition.progress = 0;
}
@Override
public void mouseMoved(int oldx, int oldy, int newx, int newy) { }
@Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
state.mouseDragged(oldx, oldy, newx, newy);
public void switchStateInstantly(OpsuState state) {
this.state.leave();
input.removeListener(this.state);
this.state = state;
this.state.enter();
input.addListener(this.state);
}
@Override
public void setInput(Input input) { }
@Override
public boolean isAcceptingInput() {
return true;
}
@Override
public void inputEnded() { }
@Override
public void inputStarted() { }
}

View File

@@ -19,7 +19,11 @@ package yugecin.opsudance.core;
import itdelatrisu.opsu.downloads.Updater;
import yugecin.opsudance.OpsuDance;
import yugecin.opsudance.core.inject.OpsuDanceInjector;
import javax.swing.*;
import static yugecin.opsudance.core.Constants.PROJECT_NAME;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Entrypoint {
@@ -27,10 +31,18 @@ public class Entrypoint {
public static void main(String[] args) {
sout("launched");
(new OpsuDanceInjector()).provide(OpsuDance.class).start(args);
if (Updater.get().getStatus() == Updater.Status.UPDATE_FINAL) {
Updater.get().runUpdate();
try {
InstanceContainer.kickstart();
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(), "Cannot start " + PROJECT_NAME, JOptionPane.ERROR_MESSAGE);
// TODO replace with errorhandler
}
new OpsuDance().start(args);
if (updater.getStatus() == Updater.Status.UPDATE_FINAL) {
updater.runUpdate();
}
}
@@ -39,7 +51,7 @@ public class Entrypoint {
}
public static void sout(String message) {
System.out.println(String.format("[%7d] %s", runtime(), message));
System.out.println(String.format("[%8d] %s", runtime(), message));
}
}

View File

@@ -0,0 +1,53 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import java.io.File;
import java.nio.file.Paths;
import static yugecin.opsudance.core.Constants.PROJECT_NAME;
public class Environment {
public final boolean isJarRunning;
public final File workingdir;
public final File jarfile;
public Environment() {
Class thiz = Environment.class;
String thisClassLocation = thiz.getResource(thiz.getSimpleName() + ".class").toString();
this.isJarRunning = thisClassLocation.startsWith("jar:");
if (!isJarRunning) {
this.workingdir = Paths.get(".").toAbsolutePath().normalize().toFile();
this.jarfile = null;
} else {
String wdir = thisClassLocation.substring(9); // remove jar:file:
String separator = "!/";
int separatorIdx = wdir.indexOf(separator);
int lastSeparatorIdx = wdir.lastIndexOf(separator);
if (separatorIdx != lastSeparatorIdx) {
String msg = String.format("%s cannot run from paths containing '!/', please move the file. Current directory: %s",
PROJECT_NAME, thisClassLocation.substring(0, lastSeparatorIdx));
throw new RuntimeException(msg);
}
this.jarfile = new File(wdir.substring(0, separatorIdx));
this.workingdir = jarfile.getParentFile();
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Input;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.events.BarNotifListener;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class GlobalInputListener implements InputListener {
@Override
public boolean keyPressed(int key, char c) {
return false;
}
@Override
public boolean keyReleased(int key, char c) {
if (key == KEY_F7) {
OPTION_TARGET_FPS.clickListItem((targetFPSIndex + 1) % targetFPS.length);
BarNotifListener.EVENT.make().onBarNotif(String.format("Frame limiter: %s",
OPTION_TARGET_FPS.getValueString()));
return true;
}
if (key == KEY_F10) {
OPTION_DISABLE_MOUSE_BUTTONS.toggle();
return true;
}
if (key == KEY_F12) {
config.takeScreenShot();
return true;
}
if (key == KEY_S && isKeyDown(KEY_LMENU) && isKeyDown(KEY_LSHIFT) &&
input.isControlDown() && !displayContainer.isInState(Game.class)) {
skinservice.reloadSkin();
}
return false;
}
@Override
public boolean mouseWheelMoved(int delta) {
if (isKeyDown(Input.KEY_LALT) || isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((delta < 0) ? -1 : 1);
return true;
}
return false;
}
@Override
public boolean mousePressed(int button, int x, int y) {
return false;
}
@Override
public boolean mouseReleased(int button, int x, int y) {
return false;
}
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
}

View File

@@ -0,0 +1,137 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core;
import itdelatrisu.opsu.NativeLoader;
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 org.newdawn.slick.Input;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.ManifestWrapper;
import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import static yugecin.opsudance.utils.SyntacticSugar.closeAndSwallow;
public class InstanceContainer {
public static Environment env;
public static Configuration config;
public static OptionsService optionservice;
public static SkinService skinservice;
public static OszUnpacker oszunpacker;
public static ReplayImporter replayImporter;
public static BeatmapParser beatmapParser;
public static Updater updater;
public static DisplayContainer displayContainer;
public static Input input;
public static GameObjectRenderer gameObjectRenderer;
public static Splash splashState;
public static MainMenu mainmenuState;
public static ButtonMenu buttonState;
public static SongMenu songMenuState;
public static DownloadsMenu downloadState;
public static Game gameState;
public static GameRanking gameRankingState;
public static GamePauseMenu pauseState;
public static void kickstart() {
updater = new Updater();
env = new Environment();
JarFile jarfile = getJarfile();
ManifestWrapper manifest = new ManifestWrapper(getJarManifest(jarfile));
config = new Configuration(manifest);
if (jarfile != null) {
try {
NativeLoader.loadNatives(jarfile, manifest);
} catch (IOException e) {
String msg = String.format("Could not unpack native(s): %s", e.getMessage());
throw new RuntimeException(msg, e);
} finally {
closeAndSwallow(jarfile);
}
}
NativeLoader.setNativePath();
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
optionservice = new OptionsService();
skinservice = new SkinService();
oszunpacker = new OszUnpacker();
replayImporter = new ReplayImporter();
beatmapParser = new BeatmapParser();
updater = new Updater();
displayContainer = new DisplayContainer();
gameObjectRenderer = new GameObjectRenderer();
splashState = new Splash();
mainmenuState = new MainMenu();
buttonState = new ButtonMenu();
songMenuState = new SongMenu();
downloadState = new DownloadsMenu();
gameState = new Game();
gameRankingState = new GameRanking();
pauseState = new GamePauseMenu();
}
@Nullable
private static JarFile getJarfile() {
if (env.jarfile == null) {
return null;
}
try {
return new JarFile(env.jarfile);
} catch (IOException e) {
String msg = String.format("Cannot read from jarfile (%s): %s", env.jarfile.getAbsolutePath(),
e.getMessage());
throw new RuntimeException(msg, e);
}
}
@Nullable
private static Manifest getJarManifest(@Nullable JarFile jarfile) {
if (jarfile == null) {
return null;
}
try {
return jarfile.getManifest();
} catch (IOException e) {
String msg = String.format("Cannot read manifest from jarfile: %s", e.getMessage());
throw new RuntimeException(msg, e);
}
}
}

View File

@@ -15,10 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.events;
package yugecin.opsudance.core;
public interface EventListener<T> {
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
void onEvent(T event);
}
@Retention(RetentionPolicy.SOURCE) public @interface NotNull {}

View File

@@ -15,13 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
package yugecin.opsudance.core;
public class EmptyTransitionState extends TransitionState {
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Override
public void enter() {
finish();
}
}
@Retention(RetentionPolicy.SOURCE) public @interface Nullable {}

View File

@@ -19,8 +19,7 @@ package yugecin.opsudance.core.errorhandling;
import itdelatrisu.opsu.Utils;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.core.Constants;
import yugecin.opsudance.utils.MiscUtils;
import javax.swing.*;
@@ -35,80 +34,52 @@ import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* based on itdelatrisu.opsu.ErrorHandler
*/
public class ErrorHandler {
private static ErrorHandler instance;
private final Configuration config;
private final DisplayContainer displayContainer;
private String customMessage;
private Throwable cause;
private String errorDump;
private String messageBody;
private boolean preventContinue;
private boolean preventReport;
private boolean ignoreAndContinue;
private boolean allowTerminate;
public ErrorHandler(DisplayContainer displayContainer, Configuration config) {
this.displayContainer = displayContainer;
this.config = config;
instance = this;
}
private ErrorHandler init(String customMessage, Throwable cause) {
this.customMessage = customMessage;
this.cause = cause;
public final static int DEFAULT_OPTIONS = 0;
public final static int PREVENT_CONTINUE = 1;
public final static int PREVENT_REPORT = 2;
public final static int ALLOW_TERMINATE = 4;
public static boolean explode(String customMessage, Throwable cause, int flags) {
StringWriter dump = new StringWriter();
try {
displayContainer.writeErrorDump(dump);
} catch (Exception e) {
dump
.append("### ")
.append(e.getClass().getSimpleName())
.append(" while creating errordump");
e.printStackTrace(new PrintWriter(dump));
if (displayContainer == null) {
dump.append("displayContainer is null!\n");
} else {
try {
displayContainer.writeErrorDump(dump);
} catch (Exception e) {
dump
.append("### ")
.append(e.getClass().getSimpleName())
.append(" while creating errordump");
e.printStackTrace(new PrintWriter(dump));
}
}
errorDump = dump.toString();
String errorDump = dump.toString();
dump = new StringWriter();
dump.append(customMessage).append("\n");
cause.printStackTrace(new PrintWriter(dump));
dump.append("\n").append(errorDump);
messageBody = dump.toString();
String messageBody = dump.toString();
Log.error("====== start unhandled exception dump");
Log.error(messageBody);
Log.error("====== end unhandled exception dump");
return this;
int result = show(messageBody, customMessage, cause, errorDump, flags);
return (flags & ALLOW_TERMINATE) == 0 || result == 1;
}
public static ErrorHandler error(String message, Throwable cause) {
return instance.init(message, cause);
}
public ErrorHandler preventReport() {
preventReport = true;
return this;
}
public ErrorHandler allowTerminate() {
allowTerminate = true;
return this;
}
public ErrorHandler preventContinue() {
preventContinue = true;
return this;
}
public ErrorHandler show() {
private static int show(final String messageBody, final String customMessage, final Throwable cause,
final String errorDump, final int flags) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
@@ -118,7 +89,7 @@ public class ErrorHandler {
String title = "opsu!dance error - " + customMessage;
String messageText = "opsu!dance has encountered an error.";
if (!preventReport) {
if ((flags & PREVENT_REPORT) == 0) {
messageText += " Please report this!";
}
JLabel message = new JLabel(messageText);
@@ -132,12 +103,27 @@ public class ErrorHandler {
textArea.setWrapStyleWord(true);
textArea.setText(messageBody);
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(), createReportButton() };
ActionListener reportAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
try {
URI url = createGithubIssueUrl(customMessage, cause, errorDump);
Desktop.getDesktop().browse(url);
} catch (IOException e) {
Log.warn("Could not open browser to report issue", e);
JOptionPane.showMessageDialog(null, "whoops could not launch a browser",
"errorception", JOptionPane.ERROR_MESSAGE);
}
}
};
Object[] messageComponents = new Object[] { message, new JScrollPane(textArea), createViewLogButton(),
createReportButton(flags, reportAction) };
String[] buttons;
if (!allowTerminate && !preventContinue) {
if ((flags & (ALLOW_TERMINATE | PREVENT_CONTINUE)) == 0) {
buttons = new String[] { "Ignore & continue" };
} else if (preventContinue) {
} else if ((flags & PREVENT_CONTINUE) == 0) {
buttons = new String[] { "Terminate" };
} else {
buttons = new String[] { "Terminate", "Ignore & continue" };
@@ -147,52 +133,46 @@ public class ErrorHandler {
frame.setUndecorated(true);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
int result = JOptionPane.showOptionDialog(frame,
messageComponents,
title,
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE,
null,
buttons,
buttons[buttons.length - 1]);
ignoreAndContinue = !allowTerminate || result == 1;
int result = JOptionPane.showOptionDialog(frame, messageComponents, title, JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE, null, buttons, buttons[buttons.length - 1]);
frame.dispose();
return this;
return result;
}
private JComponent createViewLogButton() {
private static JComponent createViewLogButton() {
return createButton("View log", Desktop.Action.OPEN, new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
try {
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);
}
openLogfile();
}
});
}
private JComponent createReportButton() {
if (preventReport) {
private static void openLogfile() {
if (config == null) {
JOptionPane.showMessageDialog(null,
"Cannot open logfile, check your opsu! installation folder for .opsu.cfg",
"errorception", JOptionPane.ERROR_MESSAGE);
return;
}
try {
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);
}
}
private static JComponent createReportButton(int flags, ActionListener reportAction) {
if ((flags & PREVENT_REPORT) > 0) {
return new JLabel();
}
return createButton("Report error", Desktop.Action.BROWSE, new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
try {
Desktop.getDesktop().browse(createGithubIssueUrl());
} catch (IOException e) {
Log.warn("Could not open browser to report issue", e);
JOptionPane.showMessageDialog(null, "whoops could not launch a browser", "errorception", JOptionPane.ERROR_MESSAGE);
}
}
});
return createButton("Report error", Desktop.Action.BROWSE, reportAction);
}
private JButton createButton(String buttonText, Desktop.Action action, ActionListener listener) {
private static JButton createButton(String buttonText, Desktop.Action action, ActionListener listener) {
JButton button = new JButton(buttonText);
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(action)) {
button.addActionListener(listener);
@@ -202,7 +182,7 @@ public class ErrorHandler {
return button;
}
private URI createGithubIssueUrl() {
private static URI createGithubIssueUrl(String customMessage, Throwable cause, String errorDump) {
StringWriter dump = new StringWriter();
dump.append(customMessage).append("\n");
@@ -227,15 +207,16 @@ public class ErrorHandler {
String issueTitle = "";
String issueBody = "";
try {
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " + customMessage, "UTF-8");
issueTitle = URLEncoder.encode("*** Unhandled " + cause.getClass().getSimpleName() + " " +
customMessage, "UTF-8");
issueBody = URLEncoder.encode(truncateGithubIssueBody(dump.toString()), "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.warn("URLEncoder failed to encode the auto-filled issue report URL.", e);
}
return URI.create(String.format(config.ISSUES_URL, issueTitle, issueBody));
return URI.create(String.format(Constants.ISSUES_URL, issueTitle, issueBody));
}
private String truncateGithubIssueBody(String body) {
private static String truncateGithubIssueBody(String body) {
if (body.replaceAll("[^a-zA-Z+-]", "").length() < 1750) {
return body;
}
@@ -243,8 +224,4 @@ public class ErrorHandler {
return body.substring(0, 1640) + "** TRUNCATED **\n```";
}
public boolean shouldIgnoreAndContinue() {
return ignoreAndContinue;
}
}

View File

@@ -17,38 +17,37 @@
*/
package yugecin.opsudance.core.events;
import java.util.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
@SuppressWarnings("unchecked")
public class EventBus {
public class Event<T> {
private EventBus() {
private final Class<T> type;
private final LinkedList<T> listeners;
public Event(Class<T> type) {
this.type = type;
this.listeners = new LinkedList<>();
}
private static final List<Subscriber> subscribers = new LinkedList<>();
public static <T> void subscribe(Class<T> eventType, EventListener<T> eventListener) {
subscribers.add(new Subscriber<>(eventType, eventListener));
public void addListener(T listener) {
this.listeners.add(listener);
}
public static void post(Object event) {
for (Subscriber s : subscribers) {
if (s.eventType.isInstance(event)) {
s.listener.onEvent(event);
}
}
}
private static class Subscriber<T> {
private final Class<T> eventType;
private final EventListener<T> listener;
private Subscriber(Class<T> eventType, EventListener<T> listener) {
this.eventType = eventType;
this.listener = listener;
}
public T make() {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (T listener : listeners) {
method.invoke(listener, args);
}
return null;
}
});
}
}

View File

@@ -13,6 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
*
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.inject;
@@ -22,14 +23,10 @@ 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;
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
import yugecin.opsudance.core.state.specialstates.BubNotifState;
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
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;
@@ -48,18 +45,14 @@ public class OpsuDanceInjector extends Injector {
bind(Updater.class).asLazySingleton();
bind(SkinService.class).asEagerSingleton();
bind(PreStartupInitializer.class).asEagerSingleton();
//bind(PreStartupInitializer.class).asEagerSingleton();
bind(DisplayContainer.class).asEagerSingleton();
bind(ErrorHandler.class).asEagerSingleton();
bind(FpsRenderState.class).asEagerSingleton();
bind(BarNotificationState.class).asEagerSingleton();
bind(BubbleNotificationState.class).asEagerSingleton();
bind(EmptyTransitionState.class).asEagerSingleton();
bind(FadeInTransitionState.class).asEagerSingleton();
bind(FadeOutTransitionState.class).asEagerSingleton();
bind(BubNotifState.class).asEagerSingleton();
bind(GameObjectRenderer.class).asEagerSingleton();

View File

@@ -17,33 +17,12 @@
*/
package yugecin.opsudance.core.state;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.UI;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
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 yugecin.opsudance.events.ResolutionChangedListener;
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;
public abstract class BaseOpsuState implements OpsuState, ResolutionChangedListener {
/**
* state is dirty when resolution or skin changed but hasn't rendered yet
@@ -52,7 +31,7 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
private boolean isCurrentState;
public BaseOpsuState() {
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, this);
ResolutionChangedListener.EVENT.addListener(this);
}
protected void revalidate() {
@@ -71,7 +50,7 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
}
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
public void onResolutionChanged(int w, int h) {
if (isCurrentState) {
revalidate();
return;
@@ -105,32 +84,11 @@ public abstract class BaseOpsuState implements OpsuState, EventListener<Resoluti
@Override
public boolean keyReleased(int key, char c) {
if (key == Input.KEY_F7) {
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) {
OPTION_DISABLE_MOUSE_BUTTONS.toggle();
return true;
}
if (key == Input.KEY_F12) {
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)) {
skinService.reloadSkin();
}
return false;
}
@Override
public boolean mouseWheelMoved(int delta) {
if (displayContainer.input.isKeyDown(Input.KEY_LALT) || displayContainer.input.isKeyDown(Input.KEY_RALT)) {
UI.changeVolume((delta < 0) ? -1 : 1);
return true;
}
return false;
}

View File

@@ -17,12 +17,14 @@
*/
package yugecin.opsudance.core.state;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.core.components.Component;
import java.util.LinkedList;
import static yugecin.opsudance.core.InstanceContainer.*;
public abstract class ComplexOpsuState extends BaseOpsuState {
protected final LinkedList<Component> components;
@@ -163,7 +165,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState {
}
}
if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) {
if (key == Keyboard.KEY_ESCAPE) {
focusedComponent.setFocused(false);
focusedComponent = null;
return true;
@@ -182,7 +184,7 @@ public abstract class ComplexOpsuState extends BaseOpsuState {
}
}
if (focusedComponent != null) {
if (key == Input.KEY_ESCAPE) {
if (key == Keyboard.KEY_ESCAPE) {
focusedComponent.setFocused(false);
focusedComponent = null;
return true;

View File

@@ -18,9 +18,10 @@
package yugecin.opsudance.core.state;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.InputListener;
import yugecin.opsudance.core.errorhandling.ErrorDumpable;
public interface OpsuState extends ErrorDumpable {
public interface OpsuState extends ErrorDumpable, InputListener {
void update();
void preRenderUpdate();
@@ -33,34 +34,4 @@ public interface OpsuState extends ErrorDumpable {
*/
boolean onCloseRequest();
/**
* @return false to stop event bubbling
*/
boolean keyPressed(int key, char c);
/**
* @return false to stop event bubbling
*/
boolean keyReleased(int key, char c);
/**
* @return false to stop event bubbling
*/
boolean mouseWheelMoved(int delta);
/**
* @return false to stop event bubbling
*/
boolean mousePressed(int button, int x, int y);
/**
* @return false to stop event bubbling
*/
boolean mouseReleased(int button, int x, int y);
/**
* @return false to stop event bubbling
*/
boolean mouseDragged(int oldx, int oldy, int newx, int newy);
}

View File

@@ -21,22 +21,20 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import java.util.List;
public class BarNotificationState implements EventListener<BarNotificationEvent> {
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class BarNotificationState implements BarNotifListener, ResolutionChangedListener {
private final int IN_TIME = 200;
private final int DISPLAY_TIME = 5700 + IN_TIME;
private final int OUT_TIME = 200;
private final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final Color bgcol;
private final Color textCol;
@@ -49,21 +47,12 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
private int barHalfTargetHeight;
private int barHalfHeight;
public BarNotificationState(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public BarNotificationState() {
this.bgcol = new Color(Color.black);
this.textCol = new Color(Color.white);
this.timeShown = TOTAL_TIME;
EventBus.subscribe(BarNotificationEvent.class, this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
if (timeShown >= TOTAL_TIME) {
return;
}
calculatePosition();
}
});
BarNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
@@ -108,10 +97,18 @@ public class BarNotificationState implements EventListener<BarNotificationEvent>
}
@Override
public void onEvent(BarNotificationEvent event) {
this.message = event.message;
public void onBarNotif(String message) {
this.message = message;
calculatePosition();
timeShown = 0;
}
@Override
public void onResolutionChanged(int w, int h) {
if (timeShown >= TOTAL_TIME) {
return;
}
calculatePosition();
}
}

View File

@@ -21,40 +21,33 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import org.newdawn.slick.MouseListener;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.events.ResolutionChangedListener;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class BubbleNotificationState implements EventListener<BubbleNotificationEvent> {
import static yugecin.opsudance.core.InstanceContainer.*;
public class BubNotifState implements MouseListener, BubNotifListener, ResolutionChangedListener {
public static final int IN_TIME = 633;
public static final int DISPLAY_TIME = 7000 + IN_TIME;
public static final int OUT_TIME = 433;
public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
private final DisplayContainer displayContainer;
private final LinkedList<Notification> bubbles;
private int addAnimationTime;
private int addAnimationHeight;
public BubbleNotificationState(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public BubNotifState() {
this.bubbles = new LinkedList<>();
this.addAnimationTime = IN_TIME;
EventBus.subscribe(BubbleNotificationEvent.class, this);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, new EventListener<ResolutionOrSkinChangedEvent>() {
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
calculatePositions();
}
});
BubNotifListener.EVENT.addListener(this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void render(Graphics g) {
@@ -79,20 +72,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
} while (iter.hasNext());
}
public boolean mouseReleased(int x, int y) {
if (x < Notification.finalX) {
return false;
}
for (Notification bubble : bubbles) {
if (bubble.mouseReleased(x, y)) {
return true;
}
}
return false;
}
private void calculatePositions() {
Notification.width = (int) (displayContainer.width * 0.1703125f);
// if width is 0, attempting to wrap it will result in infinite loop
Notification.width = Math.max(50, (int) (displayContainer.width * 0.1703125f));
Notification.baseLine = (int) (displayContainer.height * 0.9645f);
Notification.paddingY = (int) (displayContainer.height * 0.0144f);
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
@@ -130,9 +112,9 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
}
@Override
public void onEvent(BubbleNotificationEvent event) {
public void onBubNotif(String message, Color borderColor) {
finishAddAnimation();
Notification newBubble = new Notification(event.message, event.borderColor);
Notification newBubble = new Notification(message, borderColor);
bubbles.add(0, newBubble);
addAnimationTime = 0;
addAnimationHeight = newBubble.height + Notification.paddingY;
@@ -144,6 +126,39 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
}
}
@Override
public void onResolutionChanged(int w, int h) {
calculatePositions();
}
@Override
public boolean mouseWheelMoved(int delta) {
return false;
}
@Override
public boolean mousePressed(int button, int x, int y) {
return false;
}
@Override
public boolean mouseReleased(int button, int x, int y) {
if (x < Notification.finalX) {
return false;
}
for (Notification bubble : bubbles) {
if (bubble.mouseReleased(x, y)) {
return true;
}
}
return false;
}
@Override
public boolean mouseDragged(int oldx, int oldy, int newx, int newy) {
return false;
}
private static class Notification {
private final static int HOVER_ANIM_TIME = 150;
@@ -205,7 +220,7 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor);
y += lineHeight;
}
return timeShown > BubbleNotificationState.TOTAL_TIME;
return timeShown > BubNotifState.TOTAL_TIME;
}
private void processAnimations(boolean mouseHovered, int delta) {
@@ -218,17 +233,17 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress;
borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress;
borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress;
if (timeShown < BubbleNotificationState.IN_TIME) {
float progress = (float) timeShown / BubbleNotificationState.IN_TIME;
if (timeShown < BubNotifState.IN_TIME) {
float progress = (float) timeShown / BubNotifState.IN_TIME;
this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2);
textColor.a = borderColor.a = bgcol.a = progress;
bgcol.a = borderColor.a * 0.8f;
return;
}
x = Notification.finalX;
if (timeShown > BubbleNotificationState.DISPLAY_TIME) {
if (timeShown > BubNotifState.DISPLAY_TIME) {
isFading = true;
float progress = (float) (timeShown - BubbleNotificationState.DISPLAY_TIME) / BubbleNotificationState.OUT_TIME;
float progress = (float) (timeShown - BubNotifState.DISPLAY_TIME) / BubNotifState.OUT_TIME;
textColor.a = borderColor.a = 1f - progress;
bgcol.a = borderColor.a * 0.8f;
}
@@ -236,7 +251,7 @@ public class BubbleNotificationState implements EventListener<BubbleNotification
private boolean mouseReleased(int x, int y) {
if (!isFading && isMouseHovered(x, y)) {
timeShown = BubbleNotificationState.DISPLAY_TIME;
timeShown = BubNotifState.DISPLAY_TIME;
return true;
}
return false;

View File

@@ -20,21 +20,18 @@ package yugecin.opsudance.core.state.specialstates;
import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.events.EventListener;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.events.ResolutionChangedListener;
import yugecin.opsudance.utils.FPSMeter;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEvent> {
public class FpsRenderState implements ResolutionChangedListener {
private final static Color GREEN = new Color(171, 218, 25);
private final static Color ORANGE = new Color(255, 204, 34);
private final static Color DARKORANGE = new Color(255, 149, 24);
private final DisplayContainer displayContainer;
private final FPSMeter fpsMeter;
private final FPSMeter upsMeter;
@@ -42,11 +39,10 @@ public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEven
private int y;
private int singleHeight;
public FpsRenderState(DisplayContainer displayContainer) {
this.displayContainer = displayContainer;
public FpsRenderState() {
fpsMeter = new FPSMeter(10);
upsMeter = new FPSMeter(10);
EventBus.subscribe(ResolutionOrSkinChangedEvent.class, this);
ResolutionChangedListener.EVENT.addListener(this);
}
public void update() {
@@ -93,7 +89,7 @@ public class FpsRenderState implements EventListener<ResolutionOrSkinChangedEven
}
@Override
public void onEvent(ResolutionOrSkinChangedEvent event) {
public void onResolutionChanged(int w, int h) {
singleHeight = Fonts.SMALL.getLineHeight();
x = displayContainer.width - 3;
y = displayContainer.height - 3 - singleHeight - 10;

View File

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

View File

@@ -1,93 +0,0 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.state.BaseOpsuState;
import yugecin.opsudance.core.state.OpsuState;
import java.io.StringWriter;
public abstract class TransitionState extends BaseOpsuState {
protected OpsuState applicableState;
protected int transitionTargetTime;
protected int transitionTime;
private TransitionFinishedListener listener;
public final TransitionState set(OpsuState applicableState, int targetTime, TransitionFinishedListener listener) {
this.applicableState = applicableState;
this.transitionTargetTime = targetTime;
this.listener = listener;
return this;
}
public final OpsuState getApplicableState() {
return applicableState;
}
@Override
public void update() {
applicableState.update();
transitionTime += displayContainer.delta;
if (transitionTime >= transitionTargetTime) {
finish();
}
}
@Override
public void preRenderUpdate() {
applicableState.preRenderUpdate();
}
@Override
public void render(Graphics g) {
applicableState.render(g);
}
@Override
public void enter() {
super.enter();
transitionTime = 0;
}
protected final void finish() {
listener.onFinish();
}
@Override
public boolean onCloseRequest() {
return false;
}
@Override
public void writeErrorDump(StringWriter dump) {
dump.append("> TransitionState dump\n");
dump.append("progress: ").append(String.valueOf(transitionTime)).append("/").append(String.valueOf(transitionTargetTime)).append('\n');
dump.append("applicable state: ");
if (applicableState == null) {
dump.append("IS NULL");
return;
}
dump.append(applicableState.getClass().getSimpleName()).append('\n');
applicableState.writeErrorDump(dump);
}
}

View File

@@ -17,12 +17,12 @@
*/
package yugecin.opsudance.events;
public class BarNotificationEvent {
import yugecin.opsudance.core.events.Event;
public final String message;
public interface BarNotifListener {
public BarNotificationEvent(String message) {
this.message = message;
}
Event<BarNotifListener> EVENT = new Event<>(BarNotifListener.class);
void onBarNotif(String message);
}

View File

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

View File

@@ -1,38 +0,0 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.events;
import org.newdawn.slick.Color;
public class BubbleNotificationEvent {
public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59);
public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220);
public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149);
public static final Color COMMONCOLOR_RED = new Color(141, 49, 16);
public static final Color COLOR_ORANGE = new Color(138, 72, 51);
public final String message;
public final Color borderColor;
public BubbleNotificationEvent(String message, Color borderColor) {
this.message = message;
this.borderColor = borderColor;
}
}

View File

@@ -17,16 +17,12 @@
*/
package yugecin.opsudance.events;
public class ResolutionOrSkinChangedEvent {
import yugecin.opsudance.core.events.Event;
public final String skin;
public final int width;
public final int height;
public interface ResolutionChangedListener {
public ResolutionOrSkinChangedEvent(String skin, int width, int height) {
this.skin = skin;
this.width = width;
this.height = height;
}
Event<ResolutionChangedListener> EVENT = new Event<>(ResolutionChangedListener.class);
void onResolutionChanged(int w, int h);
}

View File

@@ -15,13 +15,14 @@
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
package yugecin.opsudance.events;
public class FadeInTransitionState extends FadeTransitionState {
import yugecin.opsudance.core.events.Event;
@Override
protected float getMaskAlphaLevel(float fadeProgress) {
return 1f - fadeProgress;
}
public interface SkinChangedListener {
Event<SkinChangedListener> EVENT = new Event<>(SkinChangedListener.class);
void onSkinChanged(String stringName);
}

View File

@@ -19,7 +19,8 @@ package yugecin.opsudance.movers;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class CircleMover extends Mover {
@@ -32,12 +33,12 @@ public class CircleMover extends Mover {
public CircleMover(GameObject start, GameObject end, int dir) {
super(start, end, dir);
if (startX - endX == 0 && startY - endY == 0) {
int quadr = Utils.getQuadrant(startX, startY);
int quadr = Utils.getRegion(startX, startY);
switch (quadr) {
case 1: ang = 135d / 180d * Math.PI; break;
case 3: ang = 135d / 180d * Math.PI; break;
case 2: ang = 45d / 180d * Math.PI; break;
case 3: ang = -45d / 180d * Math.PI; break;
case 4: ang = -135d / 180d * Math.PI; break;
case 0: ang = -45d / 180d * Math.PI; break;
case 1: ang = -135d / 180d * Math.PI; break;
}
} else {
ang = Math.atan2(startY - endY, startX - endX);
@@ -63,7 +64,7 @@ public class CircleMover extends Mover {
double a = ang + SOME_CONSTANT * t;
pos[0] = (startX + (endX - startX) * t) - middlexoffset - Math.cos(a) * radius;
pos[1] = (startY + (endY - startY) * t) - middleyoffset - Math.sin(a) * radius;
if (pos[0] < 0 || Options.width < pos[0] || pos[1] < 0 || Options.height < pos[1]) {
if (pos[0] < 0 || displayContainer.width < pos[0] || pos[1] < 0 || displayContainer.height < pos[1]) {
pass = false;
break;
}

View File

@@ -18,11 +18,11 @@
package yugecin.opsudance.movers;
import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.options.Options;
import java.util.Random;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class ExgonMover extends Mover {
@@ -44,8 +44,8 @@ public class ExgonMover extends Mover {
pos[0] = endX;
pos[1] = endY;
} else {
pos[0] = randgen.nextInt(Options.width);
pos[1] = randgen.nextInt(Options.height);
pos[0] = randgen.nextInt(displayContainer.width);
pos[1] = randgen.nextInt(displayContainer.height);
}
}
return pos;

View File

@@ -22,9 +22,8 @@ import itdelatrisu.opsu.beatmap.HitObject;
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.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class AutoMoverFactory implements MoverFactory {
@@ -45,7 +44,7 @@ public class AutoMoverFactory implements MoverFactory {
// stacked: circles if not too quick
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
if (distance < gameObjectRenderer.circleDiameter && ((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);
}
@@ -103,7 +102,8 @@ public class AutoMoverFactory implements MoverFactory {
}
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;
return 0 < pos[0] && pos[0] < displayContainer.width - displayContainer.width / 8 &&
0 < pos[1] && pos[1] < displayContainer.height - displayContainer.height / 8;
}
@Override

View File

@@ -20,36 +20,31 @@ 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 itdelatrisu.opsu.ui.Colors;
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 org.newdawn.slick.util.Log;
import yugecin.opsudance.events.BubNotifListener;
import yugecin.opsudance.utils.ManifestWrapper;
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.core.errorhandling.ErrorHandler.*;
import static yugecin.opsudance.options.Options.*;
import static yugecin.opsudance.core.InstanceContainer.*;
public class Configuration {
@@ -67,13 +62,6 @@ public class Configuration {
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;
@@ -85,9 +73,8 @@ public class Configuration {
public File replayImportDir;
public File skinRootDir;
@Inject
public Configuration() {
USE_XDG = areXDGDirectoriesEnabled();
public Configuration(ManifestWrapper jarmanifest) {
USE_XDG = jarmanifest.valueOrDefault(null, "Use-XDG", "").equalsIgnoreCase("true");
CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config");
DATA_DIR = getXDGBaseDir("XDG_DATA_HOME", ".local/share");
@@ -103,13 +90,6 @@ public class Configuration {
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();
@@ -117,14 +97,14 @@ public class Configuration {
private Beatmap createThemeBeatmap() {
try {
String[] tokens = {"theme.mp3", "Rainbows", "Kevin MacLeod", "219350"};
String[] tokens = {"theme.ogg", "On the Bach", "Jingle Punks", "66000"};
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"));
beatmap.timingPoints.add(new TimingPoint("-44,631.578947368421,4,1,0,100,1,0"));
return beatmap;
} catch (Exception e) {
return null;
@@ -170,12 +150,12 @@ public class Configuration {
}
private File loadDirectory(File dir, File defaultDir, String kind) {
if (dir.exists() && dir.isDirectory()) {
if (dir != null && 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));
BubNotifListener.EVENT.make().onBubNotif(msg, Colors.BUB_RED);
}
return defaultDir;
}
@@ -195,49 +175,24 @@ public class Configuration {
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 envvar the environment variable to check (XDG_*_*)
* @param fallback the fallback directory relative to ~home
* @return the XDG base directory, or the working directory if unavailable
*/
private File getXDGBaseDir(String env, String fallback) {
File workingdir;
if (Utils.isJarRunning()) {
workingdir = Utils.getRunningDirectory().getParentFile();
} else {
workingdir = Paths.get(".").toAbsolutePath().normalize().toFile();
}
private File getXDGBaseDir(String envvar, String fallback) {
if (!USE_XDG) {
return workingdir;
return env.workingdir;
}
String OS = System.getProperty("os.name").toLowerCase();
if (OS.indexOf("nix") == -1 && OS.indexOf("nux") == -1 && OS.indexOf("aix") == -1){
return workingdir;
return env.workingdir;
}
String rootPath = System.getenv(env);
String rootPath = System.getenv(envvar);
if (rootPath == null) {
String home = System.getProperty("user.home");
if (home == null) {
@@ -247,7 +202,8 @@ public class Configuration {
}
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();
explode(String.format("Failed to create configuration folder at '%s/opsu'.", rootPath),
new Exception("empty"), PREVENT_REPORT);
}
return dir;
}
@@ -259,7 +215,9 @@ public class Configuration {
// 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));
BubNotifListener.EVENT.make().onBubNotif(
String.format( "Failed to create screenshot directory at '%s'.",
screenshotDir.getAbsolutePath()), Colors.BUB_RED);
return;
}
@@ -293,9 +251,13 @@ public class Configuration {
}
}
ImageIO.write(image, OPTION_SCREENSHOT_FORMAT.getValueString().toLowerCase(), file);
EventBus.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
BubNotifListener.EVENT.make().onBubNotif("Created " + fileName,
Colors.BUB_PURPLE);
} catch (Exception e) {
ErrorHandler.error("Failed to take a screenshot.", e).show();
Log.error("Could not take screenshot", e);
BubNotifListener.EVENT.make().onBubNotif(
"Failed to take a screenshot. See log file for details",
Colors.BUB_PURPLE);
}
}
}.start();

View File

@@ -18,8 +18,8 @@
package yugecin.opsudance.options;
import itdelatrisu.opsu.Utils;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import itdelatrisu.opsu.ui.Colors;
import yugecin.opsudance.events.BubNotifListener;
public class NumericOption extends Option {
@@ -53,7 +53,8 @@ public class NumericOption extends Option {
try {
val = Utils.clamp(Integer.parseInt(s), min, max);
} catch (Exception ignored) {
EventBus.post(new BubbleNotificationEvent("Failed to parse " + configurationName + " option", BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif("Failed to parse " + configurationName + " option",
Colors.BUB_RED);
}
}

View File

@@ -17,22 +17,10 @@
*/
package yugecin.opsudance.options;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
import static yugecin.opsudance.core.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;
@@ -54,7 +42,7 @@ public class Option {
this.name = name;
this.configurationName = configurationName;
this.description = description;
OptionsService.registerOption(this);
optionservice.registerOption(this);
}
/**
@@ -90,6 +78,10 @@ public class Option {
return filtered;
}
public void setFiltered(boolean flag) {
this.filtered = flag;
}
/**
* Check if this option should be filtered (= not shown) because it does not
* match the search string.

View File

@@ -18,12 +18,14 @@
package yugecin.opsudance.options;
import itdelatrisu.opsu.GameImage;
import static yugecin.opsudance.options.Options.*;
public class OptionGroups {
public static final OptionTab[] normalOptions = new OptionTab[] {
new OptionTab("GENERAL", null),
new OptionTab("General", GameImage.MENU_NAV_GENERAL),
new OptionTab("GENERAL", new Option[]{
OPTION_DISABLE_UPDATER,
OPTION_ENABLE_WATCH_SERVICE
@@ -31,7 +33,7 @@ public class OptionGroups {
new OptionTab("LANGUAGE", new Option[]{
OPTION_SHOW_UNICODE,
}),
new OptionTab("GRAPHICS", null),
new OptionTab("Graphics", GameImage.MENU_NAV_GRAPHICS),
new OptionTab("RENDERER", new Option[] {
OPTION_SCREEN_RESOLUTION,
OPTION_ALLOW_LARGER_RESOLUTIONS,
@@ -54,7 +56,7 @@ public class OptionGroups {
OPTION_DANCING_CIRCLES,
OPTION_DANCING_CIRCLES_MULTIPLIER,
}),
new OptionTab("SKIN", null),
new OptionTab("Skin", GameImage.MENU_NAV_SKIN),
new OptionTab("SKIN", new Option[]{
OPTION_SKIN,
OPTION_IGNORE_BEATMAP_SKINS,
@@ -69,7 +71,7 @@ public class OptionGroups {
OPTION_DISABLE_CURSOR
// TODO use combo colour as tint for slider ball option
}),
new OptionTab("AUDIO", null),
new OptionTab("Audio", GameImage.MENU_NAV_AUDIO),
new OptionTab("VOLUME", new Option[]{
OPTION_MASTER_VOLUME,
OPTION_MUSIC_VOLUME,
@@ -82,7 +84,7 @@ public class OptionGroups {
OPTION_DISABLE_SOUNDS,
OPTION_ENABLE_THEME_SONG
}),
new OptionTab("GAMEPLAY", null),
new OptionTab("Gameplay", GameImage.MENU_NAV_GAMEPLAY),
new OptionTab("GENERAL", new Option[] {
OPTION_BACKGROUND_DIM,
OPTION_FORCE_DEFAULT_PLAYFIELD,
@@ -96,7 +98,7 @@ public class OptionGroups {
OPTION_MAP_END_DELAY,
OPTION_EPILEPSY_WARNING,
}),
new OptionTab("INPUT", null),
new OptionTab("Input", GameImage.MENU_NAV_INPUT),
new OptionTab("KEY MAPPING", new Option[]{
OPTION_KEY_LEFT,
OPTION_KEY_RIGHT,
@@ -105,7 +107,7 @@ public class OptionGroups {
OPTION_DISABLE_MOUSE_WHEEL,
OPTION_DISABLE_MOUSE_BUTTONS,
}),
new OptionTab("CUSTOM", null),
new OptionTab("Custom", GameImage.MENU_NAV_CUSTOM),
new OptionTab("DIFFICULTY", new Option[]{
OPTION_FIXED_CS,
OPTION_FIXED_HP,
@@ -116,7 +118,7 @@ public class OptionGroups {
OPTION_CHECKPOINT,
OPTION_REPLAY_SEEKING,
}),
new OptionTab("DANCE", null),
new OptionTab("Dance", GameImage.MENU_NAV_DANCE),
new OptionTab("MOVER", new Option[]{
OPTION_DANCE_MOVER,
OPTION_DANCE_EXGON_DELAY,
@@ -143,7 +145,7 @@ public class OptionGroups {
new OptionTab("MIRROR", new Option[] {
OPTION_DANCE_MIRROR,
}),
new OptionTab("ADVANCED DISPLAY", null),
new OptionTab("Advanced Display", GameImage.MENU_NAV_ADVANCED),
new OptionTab("OBJECTS", new Option[]{
OPTION_DANCE_DRAW_APPROACH,
OPTION_DANCE_OBJECT_COLOR_OVERRIDE,
@@ -163,7 +165,7 @@ public class OptionGroups {
OPTION_DANCE_REMOVE_BG,
OPTION_DANCE_ENABLE_SB,
}),
new OptionTab ("PIPPI", null),
new OptionTab ("Pippi", GameImage.MENU_NAV_PIPPI),
new OptionTab ("GENERAL", new Option[]{
OPTION_PIPPI_ENABLE,
OPTION_PIPPI_RADIUS_PERCENT,

View File

@@ -17,15 +17,25 @@
*/
package yugecin.opsudance.options;
import itdelatrisu.opsu.GameImage;
public class OptionTab {
public final String name;
public final Option[] options;
public final GameImage icon;
public boolean filtered;
public OptionTab(String name, GameImage icon) {
this.name = name;
this.icon = icon;
this.options = null;
}
public OptionTab(String name, Option[] options) {
this.name = name;
this.options = options;
this.icon = null;
}
}

View File

@@ -23,118 +23,101 @@ 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.events.BarNotifListener;
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;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* @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();
}
static {
new Option("BeatmapDirectory") {
@Override
public String write() {
return config.BEATMAP_DIR.getAbsolutePath();
}
@Override
public void read(String s) {
config.beatmapDir = new File(s);
}
};
@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();
}
new Option("OSZDirectory") {
@Override
public String write() {
return config.oszDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.oszDir = new File(s);
}
};
@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();
}
new Option("ScreenshotDirectory") {
@Override
public String write() {
return config.screenshotDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.screenshotDir = new File(s);
}
};
@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();
}
new Option("ReplayDirectory") {
@Override
public String write() {
return config.replayDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.replayDir = new File(s);
}
};
@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();
}
new Option("ReplayImportDirectory") {
@Override
public String write() {
return config.replayImportDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.replayImportDir = new File(s);
}
};
@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();
}
new Option("SkinDirectory") {
@Override
public String write() {
return config.skinRootDir.getAbsolutePath();
}
@Override
public void read(String s) {
config.skinRootDir = new File(s);
}
};
@Override
public void read(String s) {
config.skinRootDir = new File(s);
}
};
}
public static final NumericOption OPTION_PORT = new NumericOption("-", "Port", "-", 49250, 1024, 65535) {
@Override
@@ -146,7 +129,7 @@ public class Options {
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.") {
public static final ListOption OPTION_SCREEN_RESOLUTION = new ListOption("Screen Resolution", "ScreenResolution", "Change the size of the game.") {
private final String[] resolutions = {
null,
"800x600",
@@ -188,7 +171,6 @@ public class Options {
@Override
public void read (String s){
resolutions[0] = displayContainer.nativeDisplayMode.getWidth() + "x" + displayContainer.nativeDisplayMode.getHeight();
try {
idx = Integer.parseInt(s);
} catch (Exception ignored) {
@@ -205,37 +187,31 @@ public class Options {
public static final ToggleOption OPTION_ALLOW_LARGER_RESOLUTIONS = new ToggleOption("Allow large resolutions", "AllowLargeRes", "Allow resolutions larger than the native resolution", false);
public static final ToggleOption OPTION_FULLSCREEN = new ToggleOption("Fullscreen Mode", "Fullscreen", "Restart to apply changes.", false);
public static final 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;
return skinservice.usedSkinName;
}
@Override
public Object[] getListItems () {
return skinService.get().availableSkinDirectories;
return skinservice.availableSkinDirectories;
}
@Override
public void clickListItem(int index){
skinService.get().usedSkinName = skinService.get().availableSkinDirectories[index];
skinService.get().reloadSkin();
skinservice.usedSkinName = skinservice.availableSkinDirectories[index];
skinservice.reloadSkin();
}
@Override
public void read (String s){
skinService.get().usedSkinName = s;
skinservice.usedSkinName = s;
}
@Override
public String write() {
return skinService.get().usedSkinName;
return skinservice.usedSkinName;
}
};
@@ -418,7 +394,7 @@ public class Options {
};
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) {
public static final GenericOption OPTION_KEY_LEFT = new GenericOption("Left Game Key", "keyOsuLeft", "Select this option to input a key.", Keyboard.KEY_Z, null, false) {
@Override
public String getValueString () {
return Keyboard.getKeyName(intval);
@@ -433,12 +409,12 @@ public class Options {
public void read(String s){
intval = Keyboard.getKeyIndex(s);
if (intval == Keyboard.KEY_NONE) {
intval = Input.KEY_Y;
intval = Keyboard.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) {
public static final GenericOption OPTION_KEY_RIGHT = new GenericOption("Right Game Key", "keyOsuRight", "Select this option to input a key.", Keyboard.KEY_X, null, false) {
@Override
public String getValueString () {
return Keyboard.getKeyName(intval);
@@ -453,7 +429,7 @@ public class Options {
public void read(String s){
intval = Keyboard.getKeyIndex(s);
if (intval == Keyboard.KEY_NONE) {
intval = Input.KEY_X;
intval = Keyboard.KEY_X;
}
}
};
@@ -463,7 +439,8 @@ public class Options {
public static final ToggleOption OPTION_DISABLE_MOUSE_BUTTONS = new ToggleOption("Disable mouse buttons in play mode", "MouseDisableButtons", "This option will disable all mouse buttons. Specifically for people who use their keyboard to click.", false) {
@Override
public void toggle() {
EventBus.post(new BarNotificationEvent(state ? "Mouse buttons are disabled." : "Mouse buttons are enabled."));
BarNotifListener.EVENT.make().onBarNotif(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);
@@ -503,7 +480,6 @@ public class Options {
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);
@@ -632,7 +608,7 @@ public class Options {
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"));
BarNotifListener.EVENT.make().onBarNotif("This mover is disabled in the storyboard right now");
return;
}
Dancer.instance.setMoverFactoryIndex(index);

View File

@@ -17,39 +17,37 @@
*/
package yugecin.opsudance.options;
import itdelatrisu.opsu.ui.Colors;
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 yugecin.opsudance.events.BubNotifListener;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
* @author itdelatrisu (https://github.com/itdelatrisu)
* most functions are copied from itdelatrisu.opsu.Options.java
*/
public class OptionsService {
@Inject
private Configuration config;
public final HashMap<String, Option> optionMap;
public static final HashMap<String, Option> optionMap = new HashMap<>();
@Inject
public OptionsService(InstanceContainer instanceContainer) {
Option.setInstanceContainer(instanceContainer);
public OptionsService() {
optionMap = new HashMap<>();
}
public static void registerOption(Option option) {
public void registerOption(Option option) {
optionMap.put(option.configurationName, option);
}
public void loadOptions() {
// if no config file, use default settings
if (!config.OPTIONS_FILE.isFile()) {
config.loadDirectories();
saveOptions();
return;
}
@@ -82,7 +80,7 @@ public class OptionsService {
} catch (IOException e) {
String err = String.format("Failed to read option file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
}
config.loadDirectories();
}
@@ -111,7 +109,7 @@ public class OptionsService {
} catch (IOException e) {
String err = String.format("Failed to write to file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
BubNotifListener.EVENT.make().onBubNotif(err, Colors.BUB_RED);
}
}

View File

@@ -26,33 +26,21 @@ 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;
public GameData gameData;
private GameData gameData;
private float circleDiameter;
private int circleDiameterInt;
public float circleDiameter;
public 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)
@@ -65,14 +53,6 @@ public class GameObjectRenderer {
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;

View File

@@ -24,9 +24,8 @@ import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.state.OverlayOpsuState;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BarNotifListener;
import yugecin.opsudance.sbv2.movers.CubicStoryboardMover;
import yugecin.opsudance.sbv2.movers.LinearStoryboardMover;
import yugecin.opsudance.sbv2.movers.QuadraticStoryboardMover;
@@ -186,7 +185,7 @@ public class MoveStoryboard extends OverlayOpsuState{
private StoryboardMove getCurrentMoveOrCreateNew() {
if (gameObjects[objectIndex].isSlider() && trackPosition > gameObjects[objectIndex].getTime() && trackPosition < gameObjects[objectIndex].getEndTime()) {
EventBus.post(new BarNotificationEvent("Wait until the slider ended"));
BarNotifListener.EVENT.make().onBarNotif("Wait until the slider ended");
return dummyMove;
}
if (moves[objectIndex] == null) {

View File

@@ -23,33 +23,24 @@ 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 yugecin.opsudance.events.SkinChangedListener;
import java.io.File;
import static yugecin.opsudance.core.InstanceContainer.*;
/**
* @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));
SkinChangedListener.EVENT.make().onSkinChanged(usedSkinName);
}
/**

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class ApproachCircleSpinner extends Spinner {
@@ -38,10 +38,10 @@ public class ApproachCircleSpinner extends Spinner {
ang += 15;
}
double rad = Options.width / 4.0f * (1d - Spinner.PROGRESS);
double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS);
point[0] = Options.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = Options.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
return point;
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class BeamSpinner extends Spinner {
@@ -26,16 +26,14 @@ public class BeamSpinner extends Spinner {
private int index;
@Override
public void init()
{
public void init() {
ang = 0;
index = 0;
point = new double[2];
}
@Override
public double[] getPoint()
{
public double[] getPoint() {
if (!waitForDelay()) {
return point;
}
@@ -43,26 +41,19 @@ public class BeamSpinner extends Spinner {
index = ++index % 4;
final int MOD = 60;
point[0] = Options.width / 2d;
point[1] = Options.height / 2d;
point[0] = displayContainer.width / 2d;
point[1] = displayContainer.height / 2d;
if( index == 0 )
{
if( index == 0 ) {
add( MOD, 90 );
add( MOD, 180 );
}
else if( index == 1 )
{
} else if( index == 1 ) {
add( MOD, 90 );
add( Options.height / 2 * 0.8d, 0 );
}
else if( index == 2 )
{
add( displayContainer.height / 2 * 0.8d, 0 );
} else if( index == 2 ) {
add( MOD, -90 );
add( Options.height / 2 * 0.8d, 0 );
}
else if( index == 3 )
{
add( displayContainer.height / 2 * 0.8d, 0 );
} else if( index == 3 ) {
add( MOD, -90 );
add( MOD, 180 );
ang += 0.3;
@@ -71,8 +62,7 @@ public class BeamSpinner extends Spinner {
return point;
}
private void add( double rad, double ang )
{
private void add( double rad, double ang ) {
point[0] += rad * Math.cos( (this.ang + ang) / 180d * Math.PI);
point[1] -= rad * Math.sin( (this.ang + ang) / 180d * Math.PI);
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class CircleSpinner extends Spinner {
@@ -26,22 +26,20 @@ public class CircleSpinner extends Spinner {
private double[] point = new double[2];
@Override
public void init()
{
public void init() {
ang = 0;
}
@Override
public double[] getPoint()
{
public double[] getPoint() {
if (waitForDelay()) {
ang += 15;
}
double rad = Options.width / 4.0f;
double rad = displayContainer.width / 4.0f;
point[0] = Options.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = Options.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang / 180d * Math.PI);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang / 180d * Math.PI);
return point;
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class CubeSpinner extends Spinner {
@@ -90,10 +90,10 @@ public class CubeSpinner extends Spinner {
x *= 3.0d / ( z + 3.0d + 5.0d + 0.5 );
y *= 3.0d / ( z + 3.0d + 5.0d + 0.5 );
double scale = Options.width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI));
double scale = displayContainer.width / (3.0f + 0.5f * Math.cos(size / 180f * Math.PI));
//double scale = Options.width / (3.0f + -1f * ((float)(Options.s.ElapsedMilliseconds % Options.beatTimeMs)/(float)Options.beatTimeMs));
point[0] = (int) ( Options.width / 2.0f + scale * x );
point[1] = (int) ( Options.height / 2.0f - scale * y );
point[0] = (int) ( displayContainer.width / 2.0f + scale * x );
point[1] = (int) ( displayContainer.height / 2.0f - scale * y );
return point;
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class DonutSpinner extends Spinner {
@@ -38,10 +38,10 @@ public class DonutSpinner extends Spinner {
ang += 15;
}
double rad = Options.width / 4.0f;
double rad = displayContainer.width / 4.0f;
point[0] = Options.width / 2.0f + rad * Math.sin(ang);
point[1] = Options.height / 2.0f - rad * Math.cos(ang);
point[0] = displayContainer.width / 2.0f + rad * Math.sin(ang);
point[1] = displayContainer.height / 2.0f - rad * Math.cos(ang);
return point;
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class FivePointStarApproachSpinner extends Spinner {
@@ -39,12 +39,12 @@ public class FivePointStarApproachSpinner extends Spinner {
odd = !odd;
}
double rad = Options.width / 4.0f * (1d - Spinner.PROGRESS);
double rad = displayContainer.width / 4.0f * (1d - Spinner.PROGRESS);
if (!odd) {
rad /= 3d;
}
point[0] = Options.width / 2d + Math.cos(ang) * rad;
point[1] = Options.height / 2d + Math.sin(ang) * rad;
point[0] = displayContainer.width / 2d + Math.cos(ang) * rad;
point[1] = displayContainer.height / 2d + Math.sin(ang) * rad;
return point;
}

View File

@@ -17,18 +17,18 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class FivePointStarSpinner extends Spinner {
@Override
public void init() {
double[][] points = new double[10][];
double midx = Options.width / 2d;
double midy = Options.height / 2d;
double midx = displayContainer.width / 2d;
double midy = displayContainer.height / 2d;
double angleIncRads = Math.PI * 36d / 180d;
double ang = -Math.PI / 2d;
double maxrad = Options.width / 4d;
double maxrad = displayContainer.width / 4d;
double minrad = maxrad / 3d;
for (int i = 0; i < 10; i++) {
double rad = maxrad;

View File

@@ -19,6 +19,8 @@ package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.displayContainer;
public class HalfCircleSpinner extends Spinner {
private int ang = 0;
@@ -45,8 +47,8 @@ public class HalfCircleSpinner extends Spinner {
skipang += 359;
}
point[0] = Options.width / 2.0d + Options.height / 2 * 0.8d * Math.cos(ang/180d*Math.PI);
point[1] = Options.height / 2.0d + Options.height / 2 * 0.8d * Math.sin(ang/180d*Math.PI);
point[0] = displayContainer.width / 2.0d + displayContainer.height / 2 * 0.8d * Math.cos(ang/180d*Math.PI);
point[1] = displayContainer.height / 2.0d + displayContainer.height / 2 * 0.8d * Math.sin(ang/180d*Math.PI);
return point;
}

View File

@@ -17,17 +17,16 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class IlluminatiSpinner extends Spinner {
@Override
public void init()
{
public void init() {
init( new double[][] {
new double[] { Options.width / 2d - 120, Options.height / 2d + 80 },
new double[] { Options.width / 2d, Options.height / 2d - 160 },
new double[] { Options.width / 2d + 120, Options.height / 2d + 80 }
new double[] { displayContainer.width / 2d - 120, displayContainer.height / 2d + 80 },
new double[] { displayContainer.width / 2d, displayContainer.height / 2d - 160 },
new double[] { displayContainer.width / 2d + 120, displayContainer.height / 2d + 80 }
} );
}

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class LessThanThreeSpinner extends Spinner {
@@ -38,8 +38,8 @@ public class LessThanThreeSpinner extends Spinner {
if( angle > 360 ) angle = 0;
double theta = angle / 180d * Math.PI;
double[] pos = new double[] {
Options.width / 2d,
Options.height / 2d
displayContainer.width / 2d,
displayContainer.height / 2d
};
double r = 2 - 2 * Math.sin( theta ) + Math.sin( theta ) * Math.sqrt( Math.abs( Math.cos( theta ) ) ) / ( Math.sin( theta ) + 1.4 );

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class RektCircleSpinner extends Spinner {
@@ -25,51 +25,39 @@ public class RektCircleSpinner extends Spinner {
private int index;
private int pos;
private double size;
private int delay = 0;
@Override
public void init()
{
public void init() {
index = 0;
size = Options.height * 0.8d;
size = displayContainer.height * 0.8d;
point = new double[2];
}
@Override
public double[] getPoint()
{
public double[] getPoint() {
if (!waitForDelay()) {
return point;
}
delay = 0;
final int INC = 50;
if( index == 0 )
{
point[0] = Options.width / 2d + size / 2d - pos;
point[1] = Options.height / 2d - size / 2d;
if( index == 0 ) {
point[0] = displayContainer.width / 2d + size / 2d - pos;
point[1] = displayContainer.height / 2d - size / 2d;
index++;
}
else if( index == 1 )
{
point[0] = Options.width / 2 - size / 2;
point[1] = Options.height / 2 - size / 2 + pos;
} else if( index == 1 ) {
point[0] = displayContainer.width / 2 - size / 2;
point[1] = displayContainer.height / 2 - size / 2 + pos;
index++;
}
else if( index == 2 )
{
point[0] = Options.width / 2 - size / 2 + pos;
point[1] = Options.height / 2 + size / 2;
} else if( index == 2 ) {
point[0] = displayContainer.width / 2 - size / 2 + pos;
point[1] = displayContainer.height / 2 + size / 2;
index++;
}
else if( index == 3 )
{
point[0] = Options.width / 2 + size / 2;
point[1] = Options.height / 2 + size / 2 - pos;
} else if( index == 3 ) {
point[0] = displayContainer.width / 2 + size / 2;
point[1] = displayContainer.height / 2 + size / 2 - pos;
pos += INC;
if( pos > size )
{
if( pos > size ) {
pos = INC;
}
index = 0;

View File

@@ -17,7 +17,7 @@
*/
package yugecin.opsudance.spinners;
import yugecin.opsudance.options.Options;
import static yugecin.opsudance.core.InstanceContainer.*;
public class RektSpinner extends Spinner {
@@ -25,11 +25,11 @@ public class RektSpinner extends Spinner {
public void init() {
init(new double[][] {
{ 10, 10 },
{ Options.width / 2d, 10 },
{ Options.width - 10, 10 },
{ Options.width - 10, Options.height - 10 },
{ Options.width / 2d, Options.height - 10 },
{ 10, Options.height - 10 }
{ displayContainer.width / 2d, 10 },
{ displayContainer.width - 10, 10 },
{ displayContainer.width - 10, displayContainer.height - 10 },
{ displayContainer.width / 2d, displayContainer.height - 10 },
{ 10, displayContainer.height - 10 }
});
}

View File

@@ -23,6 +23,7 @@ import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.ui.*;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.*;
import org.newdawn.slick.gui.TextField;
import yugecin.opsudance.core.DisplayContainer;
@@ -32,6 +33,7 @@ import yugecin.opsudance.utils.FontUtil;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;
import static yugecin.opsudance.options.Options.*;
@@ -48,6 +50,12 @@ public class OptionsOverlay extends OverlayOpsuState {
private static final Color COL_GREY = new Color(55, 55, 57);
private static final Color COL_BLUE = new Color(Colors.BLUE_BACKGROUND);
private static final Color COL_COMBOBOX_HOVER = new Color(185, 19, 121);
private static final Color COL_NAV_BG = new Color(COL_BG);
private static final Color COL_NAV_INDICATOR = new Color(COL_PINK);
private static final Color COL_NAV_WHITE = new Color(COL_WHITE);
private static final Color COL_NAV_FILTERED = new Color(37, 37, 37);
private static final Color COL_NAV_INACTIVE = new Color(153, 153, 153);
private static final Color COL_NAV_FILTERED_HOVERED = new Color(58, 58, 58);
private static final float INDICATOR_ALPHA = 0.8f;
private static final Color COL_INDICATOR = new Color(Color.black);
@@ -75,6 +83,8 @@ public class OptionsOverlay extends OverlayOpsuState {
/** Selected option indicator hide animation time past. */
private int indicatorHideAnimationTime;
private float showHideProgress;
private Listener listener;
private Image sliderBallImg;
@@ -83,6 +93,8 @@ public class OptionsOverlay extends OverlayOpsuState {
private Image searchImg;
private OptionTab[] sections;
private OptionTab activeSection;
private OptionTab hoveredNavigationEntry;
private Option hoverOption;
private Option selectedOption;
@@ -97,10 +109,17 @@ public class OptionsOverlay extends OverlayOpsuState {
private DropdownMenu<Object> openDropdownMenu;
private int openDropdownVirtualY;
private int finalWidth;
private int targetWidth;
private int width;
private int height;
private int navButtonSize;
private int navStartY;
private int navTargetWidth;
private int navWidth;
private int navHoverTime;
private int navIndicatorSize;
private int optionWidth;
private int optionStartX;
private int optionStartY;
@@ -137,6 +156,10 @@ public class OptionsOverlay extends OverlayOpsuState {
private final TextField searchField;
private String lastSearchText;
private int invalidSearchImgRotation;
private int invalidSearchTextRotation;
private int invalidSearchAnimationProgress;
private final int INVALID_SEARCH_ANIMATION_TIME = 500;
public OptionsOverlay(DisplayContainer displayContainer, OptionTab[] sections) {
this.displayContainer = displayContainer;
@@ -146,7 +169,7 @@ public class OptionsOverlay extends OverlayOpsuState {
dropdownMenus = new HashMap<>();
visibleDropdownMenus = new LinkedList<>();
searchField = new TextField(displayContainer, null, 0, 0, 0, 0);
searchField = new TextField(null, 0, 0, 0, 0);
searchField.setMaxLength(20);
scrollHandler = new KineticScrolling();
@@ -161,12 +184,18 @@ public class OptionsOverlay extends OverlayOpsuState {
public void revalidate() {
super.revalidate();
finalWidth = Math.max((int) (displayContainer.width * 0.36f), 340); // 0.321f
boolean isWidescreen = displayContainer.isWidescreen();
targetWidth = (int) (displayContainer.width * (isWidescreen ? 0.4f : 0.5f));
height = displayContainer.height;
// calculate positions
float navIconWidthRatio = isWidescreen ? 0.046875f : 0.065f;
// non-widescreen ratio is not accurate
navButtonSize = (int) (displayContainer.width * navIconWidthRatio);
navIndicatorSize = navButtonSize / 10;
navTargetWidth = (int) (targetWidth * 0.45f) - navButtonSize;
paddingRight = (int) (displayContainer.width * 0.009375f); // not so accurate
paddingLeft = (int) (displayContainer.width * 0.0180f); // not so accurate
paddingLeft = navButtonSize + (int) (displayContainer.width * 0.0180f); // not so accurate
paddingTextLeft = paddingLeft + LINEWIDTH + (int) (displayContainer.width * 0.00625f); // not so accurate
optionStartX = paddingTextLeft;
textOptionsY = Fonts.LARGE.getLineHeight() * 2;
@@ -177,7 +206,7 @@ public class OptionsOverlay extends OverlayOpsuState {
sectionLineHeight = (int) (Fonts.LARGE.getLineHeight() * 1.5f);
if (active) {
width = finalWidth;
width = targetWidth;
optionWidth = width - optionStartX - paddingRight;
}
@@ -190,9 +219,11 @@ public class OptionsOverlay extends OverlayOpsuState {
checkOnImg = GameImage.CONTROL_CHECK_ON.getImage().getScaledCopy(controlImageSize, controlImageSize);
checkOffImg = GameImage.CONTROL_CHECK_OFF.getImage().getScaledCopy(controlImageSize, controlImageSize);
int navTotalHeight = 0;
dropdownMenus.clear();
for (OptionTab section : sections) {
if (section.options == null) {
navTotalHeight += navButtonSize;
continue;
}
for (final Option option : section.options) {
@@ -228,6 +259,7 @@ public class OptionsOverlay extends OverlayOpsuState {
dropdownMenus.put(listOption, menu);
}
}
navStartY = (height - navTotalHeight) / 2;
int searchImgSize = (int) (Fonts.LARGE.getLineHeight() * 0.75f);
searchImg = GameImage.SEARCH.getImage().getScaledCopy(searchImgSize, searchImgSize);
@@ -235,11 +267,11 @@ public class OptionsOverlay extends OverlayOpsuState {
@Override
public void onRender(Graphics g) {
g.setClip(0, 0, width, height);
g.setClip(navButtonSize, 0, width - navButtonSize, height);
// bg
g.setColor(COL_BG);
g.fillRect(0, 0, width, height);
g.fillRect(navButtonSize, 0, width, height);
// title
renderTitle();
@@ -262,6 +294,8 @@ public class OptionsOverlay extends OverlayOpsuState {
g.fillRect(width - 5, scrollHandler.getPosition() / maxScrollOffset * (height - 45), 5, 45);
g.clearClip();
renderNavigation(g);
// UI
UI.getBackButton().draw(g);
@@ -274,6 +308,57 @@ public class OptionsOverlay extends OverlayOpsuState {
}
}
private void renderNavigation(Graphics g) {
navWidth = navButtonSize;
if (navHoverTime >= 600) {
navWidth += navTargetWidth;
} else if (navHoverTime > 300) {
AnimationEquation anim = AnimationEquation.IN_EXPO;
if (displayContainer.mouseX < navWidth) {
anim = AnimationEquation.OUT_EXPO;
}
float progress = anim.calc((navHoverTime - 300f) / 300f);
navWidth += (int) (progress * navTargetWidth);
}
g.setClip(0, 0, navWidth, height);
g.setColor(COL_NAV_BG);
g.fillRect(0, 0, navWidth, height);
int y = navStartY;
float iconSize = navButtonSize / 2.5f;
float iconPadding = iconSize * 0.75f;
int fontOffsetX = navButtonSize + navIndicatorSize;
int fontOffsetY = (navButtonSize - Fonts.MEDIUM.getLineHeight()) / 2;
for (OptionTab section : sections) {
if (section.icon == null) {
continue;
}
Color iconCol = COL_NAV_INACTIVE;
Color fontCol = COL_NAV_WHITE;
if (section == activeSection) {
iconCol = COL_NAV_WHITE;
g.fillRect(0, y, navWidth, navButtonSize);
g.setColor(COL_NAV_INDICATOR);
g.fillRect(navWidth - navIndicatorSize, y, navIndicatorSize, navButtonSize);
} else if (section == hoveredNavigationEntry) {
iconCol = COL_NAV_WHITE;
}
if (section.filtered) {
iconCol = fontCol = COL_NAV_FILTERED;
if (section == hoveredNavigationEntry) {
iconCol = COL_NAV_FILTERED_HOVERED;
}
}
section.icon.getImage().draw(iconPadding, y + iconPadding, iconSize, iconSize, iconCol);
if (navHoverTime > 0) {
Fonts.MEDIUM.drawString(fontOffsetX, y + fontOffsetY, section.name, fontCol);
}
y += navButtonSize;
}
g.clearClip();
}
private void renderIndicator(Graphics g) {
g.setColor(COL_INDICATOR);
int indicatorPos = this.indicatorPos;
@@ -288,7 +373,7 @@ public class OptionsOverlay extends OverlayOpsuState {
indicatorPos += AnimationEquation.OUT_BACK.calc((float) indicatorMoveAnimationTime / INDICATORMOVEANIMATIONTIME) * indicatorOffsetToNextPos;
}
}
g.fillRect(0, indicatorPos - scrollHandler.getPosition(), width, optionHeight);
g.fillRect(navButtonSize, indicatorPos - scrollHandler.getPosition(), width, optionHeight);
}
private void renderKeyEntry(Graphics g) {
@@ -323,12 +408,17 @@ public class OptionsOverlay extends OverlayOpsuState {
continue;
}
int lineStartY = (int) (y + Fonts.LARGE.getLineHeight() * 0.6f);
if (render) {
if (section.options == null) {
FontUtil.drawRightAligned(Fonts.XLARGE, width, -paddingRight, (int) (y + Fonts.XLARGE.getLineHeight() * 0.3f), section.name, COL_CYAN);
} else {
Fonts.MEDIUMBOLD.drawString(paddingTextLeft, lineStartY, section.name, COL_WHITE);
if (section.options == null) {
float previousAlpha = COL_CYAN.a;
if (section != activeSection) {
COL_CYAN.a *= 0.2f;
}
FontUtil.drawRightAligned(Fonts.XLARGE, width, -paddingRight,
(int) (y + Fonts.XLARGE.getLineHeight() * 0.3f), section.name.toUpperCase(),
COL_CYAN);
COL_CYAN.a = previousAlpha;
} else {
Fonts.MEDIUMBOLD.drawString(paddingTextLeft, lineStartY, section.name, COL_WHITE);
}
y += sectionLineHeight;
maxScrollOffset += sectionLineHeight;
@@ -341,7 +431,8 @@ public class OptionsOverlay extends OverlayOpsuState {
if (!option.showCondition() || option.isFiltered()) {
continue;
}
if (y > -optionHeight || (openDropdownMenu != null && openDropdownMenu.equals(dropdownMenus.get(option)))) {
if (y > -optionHeight || (option instanceof ListOption && openDropdownMenu != null
&& openDropdownMenu.equals(dropdownMenus.get(option)))) {
renderOption(g, option, y);
}
y += optionHeight;
@@ -478,8 +569,11 @@ public class OptionsOverlay extends OverlayOpsuState {
}
private void renderTitle() {
FontUtil.drawCentered(Fonts.LARGE, width, 0, textOptionsY - scrollHandler.getIntPosition(), "Options", COL_WHITE);
FontUtil.drawCentered(Fonts.MEDIUM, width, 0, textChangeY - scrollHandler.getIntPosition(), "Change the way opsu! behaves", COL_PINK);
int textWidth = width - navButtonSize;
FontUtil.drawCentered(Fonts.LARGE, textWidth, navButtonSize,
textOptionsY - scrollHandler.getIntPosition(), "Options", COL_WHITE);
FontUtil.drawCentered(Fonts.MEDIUM, textWidth, navButtonSize,
textChangeY - scrollHandler.getIntPosition(), "Change the way opsu! behaves", COL_PINK);
}
private void renderSearch(Graphics g) {
@@ -487,15 +581,34 @@ public class OptionsOverlay extends OverlayOpsuState {
if (scrollHandler.getIntPosition() > posSearchY) {
ypos = textSearchYOffset;
g.setColor(COL_BG);
g.fillRect(0, 0, width, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight());
g.fillRect(navButtonSize, 0, width, textSearchYOffset * 2 + Fonts.LARGE.getLineHeight());
}
Color searchCol = COL_WHITE;
float invalidProgress = 0f;
if (invalidSearchAnimationProgress > 0) {
invalidProgress = 1f - (float) invalidSearchAnimationProgress / INVALID_SEARCH_ANIMATION_TIME;
searchCol = new Color(0f, 0f, 0f, searchCol.a);
searchCol.r = COL_PINK.r + (1f - COL_PINK.r) * invalidProgress;
searchCol.g = COL_PINK.g + (1f - COL_PINK.g) * invalidProgress;
searchCol.b = COL_PINK.b + (1f - COL_PINK.b) * invalidProgress;
invalidProgress = 1f - invalidProgress;
}
String searchText = "Type to search!";
if (lastSearchText.length() > 0) {
searchText = lastSearchText;
}
FontUtil.drawCentered(Fonts.LARGE, width, 0, ypos, searchText, COL_WHITE);
int imgPosX = (width - Fonts.LARGE.getWidth(searchText)) / 2 - searchImg.getWidth() - 10;
searchImg.draw(imgPosX, ypos + Fonts.LARGE.getLineHeight() * 0.25f, COL_WHITE);
int textWidth = width - navButtonSize;
if (invalidSearchAnimationProgress > 0) {
g.rotate(navButtonSize + textWidth / 2, ypos, invalidProgress * invalidSearchTextRotation);
}
FontUtil.drawCentered(Fonts.LARGE, textWidth, navButtonSize, ypos, searchText, searchCol);
g.resetTransform();
int imgPosX = navButtonSize + (textWidth - Fonts.LARGE.getWidth(searchText)) / 2 - searchImg.getWidth() - 10;
if (invalidSearchAnimationProgress > 0) {
g.rotate(imgPosX + searchImg.getWidth() / 2, ypos, invalidProgress * invalidSearchImgRotation);
}
searchImg.draw(imgPosX, ypos + Fonts.LARGE.getLineHeight() * 0.25f, searchCol);
g.resetTransform();
}
@Override
@@ -509,6 +622,7 @@ public class OptionsOverlay extends OverlayOpsuState {
@Override
public void show() {
navHoverTime = 0;
indicatorPos = -optionHeight;
indicatorOffsetToNextPos = 0;
indicatorMoveAnimationTime = 0;
@@ -525,7 +639,9 @@ public class OptionsOverlay extends OverlayOpsuState {
int mouseY = displayContainer.mouseY;
int delta = displayContainer.renderDelta;
int prevscrollpos = scrollHandler.getIntPosition();
scrollHandler.update(delta);
boolean scrollPositionChanged = prevscrollpos != scrollHandler.getIntPosition();
if (openDropdownMenu == null) {
for (DropdownMenu<Object> menu : visibleDropdownMenus) {
@@ -535,6 +651,10 @@ public class OptionsOverlay extends OverlayOpsuState {
openDropdownMenu.updateHover(mouseX, mouseY);
}
if (invalidSearchAnimationProgress > 0) {
invalidSearchAnimationProgress -= delta;
}
updateShowHideAnimation(delta);
if (animationtime <= 0) {
active = false;
@@ -545,10 +665,21 @@ public class OptionsOverlay extends OverlayOpsuState {
sliderSoundDelay -= delta;
}
if (mouseX - prevMouseX == 0 && mouseY - prevMouseY == 0) {
if (mouseX < navWidth) {
if (navHoverTime < 600) {
navHoverTime += delta;
}
} else if (navHoverTime > 0) {
navHoverTime -= delta;
}
navHoverTime = Utils.clamp(navHoverTime, 0, 600);
if (!scrollPositionChanged && (mouseX - prevMouseX == 0 && mouseY - prevMouseY == 0)) {
updateIndicatorAlpha();
return;
}
updateActiveSection();
updateHoverNavigation(mouseX, mouseY);
prevMouseX = mouseX;
prevMouseY = mouseY;
updateHoverOption(mouseX, mouseY);
@@ -564,6 +695,24 @@ public class OptionsOverlay extends OverlayOpsuState {
}
}
private void updateHoverNavigation(int mouseX, int mouseY) {
hoveredNavigationEntry = null;
if (mouseX >= navWidth) {
return;
}
int y = navStartY;
for (OptionTab section : sections) {
if (section.options != null) {
continue;
}
int nextY = y + navButtonSize;
if (y <= mouseY && mouseY < nextY) {
hoveredNavigationEntry = section;
}
y = nextY;
}
}
private void updateIndicatorAlpha() {
if (hoverOption == null) {
if (indicatorHideAnimationTime < INDICATORHIDEANIMATIONTIME) {
@@ -571,50 +720,59 @@ public class OptionsOverlay extends OverlayOpsuState {
if (indicatorHideAnimationTime > INDICATORHIDEANIMATIONTIME) {
indicatorHideAnimationTime = INDICATORHIDEANIMATIONTIME;
}
float progress = AnimationEquation.IN_CUBIC.calc((float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME);
COL_INDICATOR.a = (1f - progress) * INDICATOR_ALPHA;
float progress = AnimationEquation.IN_CUBIC.calc((float) indicatorHideAnimationTime /
INDICATORHIDEANIMATIONTIME);
COL_INDICATOR.a = (1f - progress) * INDICATOR_ALPHA * showHideProgress;
}
} else if (indicatorHideAnimationTime > 0) {
indicatorHideAnimationTime -= displayContainer.renderDelta * 3;
if (indicatorHideAnimationTime < 0) {
indicatorHideAnimationTime = 0;
}
COL_INDICATOR.a = (1f - (float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME) * INDICATOR_ALPHA;
COL_INDICATOR.a = (1f - (float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME) *
INDICATOR_ALPHA * showHideProgress;
}
}
private void updateShowHideAnimation(int delta) {
if (acceptInput && animationtime >= SHOWANIMATIONTIME) {
// animation already finished
width = finalWidth;
width = targetWidth;
showHideProgress = 1f;
return;
}
optionWidth = width - optionStartX - paddingRight;
// navigation elemenst fade out with a different animation
float navProgress;
// if acceptInput is false, it means that we're currently hiding ourselves
float progress;
if (acceptInput) {
animationtime += delta;
if (animationtime >= SHOWANIMATIONTIME) {
animationtime = SHOWANIMATIONTIME;
}
progress = AnimationEquation.OUT_EXPO.calc((float) animationtime / SHOWANIMATIONTIME);
showHideProgress = (float) animationtime / SHOWANIMATIONTIME;
navProgress = Utils.clamp(showHideProgress * 10f, 0f, 1f);
showHideProgress = AnimationEquation.OUT_EXPO.calc(showHideProgress);
} else {
animationtime -= delta;
if (animationtime < 0) {
animationtime = 0;
}
progress = hideAnimationStartProgress * AnimationEquation.IN_EXPO.calc((float) animationtime / hideAnimationTime);
showHideProgress = (float) animationtime / hideAnimationTime;
navProgress = hideAnimationStartProgress * AnimationEquation.IN_CIRC.calc(showHideProgress);
showHideProgress = hideAnimationStartProgress * AnimationEquation.IN_EXPO.calc(showHideProgress);
}
width = (int) (progress * finalWidth);
COL_BG.a = BG_ALPHA * progress;
COL_WHITE.a = progress;
COL_PINK.a = progress;
COL_CYAN.a = progress;
COL_GREY.a = progress * LINEALPHA;
COL_BLUE.a = progress;
COL_COMBOBOX_HOVER.a = progress;
COL_INDICATOR.a = progress * (1f - (float) indicatorHideAnimationTime / INDICATORHIDEANIMATIONTIME) * INDICATOR_ALPHA;
width = navButtonSize + (int) (showHideProgress * (targetWidth - navButtonSize));
COL_NAV_FILTERED.a = COL_NAV_INACTIVE.a = COL_NAV_FILTERED_HOVERED.a = COL_NAV_INDICATOR.a =
COL_NAV_WHITE.a = COL_NAV_BG.a = navProgress;
COL_BG.a = BG_ALPHA * showHideProgress;
COL_WHITE.a = showHideProgress;
COL_PINK.a = showHideProgress;
COL_CYAN.a = showHideProgress;
COL_GREY.a = showHideProgress * LINEALPHA;
COL_BLUE.a = showHideProgress;
COL_COMBOBOX_HOVER.a = showHideProgress;
}
@Override
@@ -652,15 +810,17 @@ public class OptionsOverlay extends OverlayOpsuState {
isAdjustingSlider = false;
sliderOptionLength = 0;
if (openDropdownMenu != null) {
openDropdownMenu.mouseReleased(button);
updateHoverOption(x, y);
return true;
} else {
for (DropdownMenu<Object> menu : visibleDropdownMenus) {
menu.mouseReleased(button);
if (menu.isOpen()) {
return true;
if (x > navWidth) {
if (openDropdownMenu != null) {
openDropdownMenu.mouseReleased(button);
updateHoverOption(x, y);
return true;
} else {
for (DropdownMenu<Object> menu : visibleDropdownMenus) {
menu.mouseReleased(button);
if (menu.isOpen()) {
return true;
}
}
}
}
@@ -672,7 +832,7 @@ public class OptionsOverlay extends OverlayOpsuState {
return true;
}
if (x > finalWidth) {
if (x > targetWidth) {
return false;
}
@@ -687,10 +847,33 @@ public class OptionsOverlay extends OverlayOpsuState {
} else if (hoverOption == OPTION_KEY_LEFT) {
keyEntryLeft = true;
} else if (hoverOption == OPTION_KEY_RIGHT) {
keyEntryLeft = true;
keyEntryRight = true;
}
}
if (hoveredNavigationEntry != null && !hoveredNavigationEntry.filtered) {
int sectionPosition = 0;
for (OptionTab section : sections) {
if (section == hoveredNavigationEntry) {
break;
}
if (section.filtered) {
continue;
}
sectionPosition += sectionLineHeight;
if (section.options == null) {
continue;
}
for (Option option : section.options) {
if (!option.isFiltered() && option.showCondition()) {
sectionPosition += optionHeight;
}
}
}
sectionPosition = Utils.clamp(sectionPosition, (int) scrollHandler.min, (int) scrollHandler.max);
scrollHandler.scrollToPosition(sectionPosition);
}
if (UI.getBackButton().contains(x, y)){
hide();
if (listener != null) {
@@ -716,7 +899,6 @@ public class OptionsOverlay extends OverlayOpsuState {
if (!isAdjustingSlider) {
scrollHandler.scrollOffset(-delta);
}
updateHoverOption(prevMouseX, prevMouseY);
return true;
}
@@ -738,7 +920,7 @@ public class OptionsOverlay extends OverlayOpsuState {
return true;
}
if (key == Input.KEY_ESCAPE) {
if (key == Keyboard.KEY_ESCAPE) {
if (openDropdownMenu != null) {
openDropdownMenu.keyPressed(key, c);
return true;
@@ -757,8 +939,23 @@ public class OptionsOverlay extends OverlayOpsuState {
searchField.keyPressed(key, c);
if (!searchField.getText().equals(lastSearchText)) {
lastSearchText = searchField.getText().toLowerCase();
updateSearch();
String newSearchText = searchField.getText().toLowerCase();
if (!hasSearchResults(newSearchText)) {
searchField.setText(lastSearchText);
invalidSearchAnimationProgress = INVALID_SEARCH_ANIMATION_TIME;
Random rand = new Random();
invalidSearchImgRotation = 10 + rand.nextInt(10);
invalidSearchTextRotation = 10 + rand.nextInt(10);
if (rand.nextBoolean()) {
invalidSearchImgRotation = -invalidSearchImgRotation;
}
if (rand.nextBoolean()) {
invalidSearchTextRotation = -invalidSearchTextRotation;
}
} else {
lastSearchText = newSearchText;
updateSearch();
}
}
return true;
@@ -775,7 +972,37 @@ public class OptionsOverlay extends OverlayOpsuState {
o.setValue(Utils.clamp(value, o.min, o.max));
}
private void updateActiveSection() {
// active section is the one that is visible in the top half of the screen
activeSection = sections[0];
int virtualY = optionStartY;
for (OptionTab section : sections) {
if (section.filtered) {
continue;
}
virtualY += sectionLineHeight;
if (virtualY > scrollHandler.getPosition() + height / 2) {
return;
}
if (section.options == null) {
activeSection = section;
continue;
}
for (int optionIndex = 0; optionIndex < section.options.length; optionIndex++) {
Option option = section.options[optionIndex];
if (option.isFiltered() || !option.showCondition()) {
continue;
}
virtualY += optionHeight;
}
}
}
private void updateHoverOption(int mouseX, int mouseY) {
if (mouseX < navWidth) {
hoverOption = null;
return;
}
if (openDropdownMenu != null || keyEntryLeft || keyEntryRight) {
return;
}
@@ -842,7 +1069,7 @@ public class OptionsOverlay extends OverlayOpsuState {
if (section.options == null) {
lastBigSectionMatches = sectionMatches;
lastBigSection = section;
section.filtered = true;
section.filtered = !lastBigSectionMatches;
continue;
}
section.filtered = true;
@@ -854,11 +1081,37 @@ public class OptionsOverlay extends OverlayOpsuState {
}
if (!option.filter(lastSearchText)) {
section.filtered = false;
//noinspection ConstantConditions
lastBigSection.filtered = false;
}
}
}
updateHoverOption(prevMouseX, prevMouseY);
updateActiveSection();
if (openDropdownMenu != null) {
openDropdownMenu.reset();
openDropdownMenu = null;
}
}
private boolean hasSearchResults(String searchText) {
for (OptionTab section : sections) {
if (section.name.toLowerCase().contains(searchText)) {
return true;
}
if (section.options == null) {
continue;
}
for (Option option : section.options) {
boolean wasFiltered = option.isFiltered();
boolean isFiltered = option.filter(searchText);
option.setFiltered(wasFiltered);
if (!isFiltered) {
return true;
}
}
}
return false;
}
public interface Listener {

View File

@@ -25,7 +25,6 @@ import yugecin.opsudance.options.OptionGroups;
import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.ObjectColorOverrides;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.state.OverlayOpsuState;
@@ -34,6 +33,7 @@ import yugecin.opsudance.sbv2.MoveStoryboard;
import java.util.*;
import static org.lwjgl.input.Keyboard.*;
import static yugecin.opsudance.options.Options.*;
@SuppressWarnings("unchecked")
@@ -108,7 +108,7 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
@Override
public boolean onKeyPressed(int key, char c) {
if (key == Input.KEY_C) {
if (key == KEY_C) {
if (speed > 0) {
speed -= 1;
}
@@ -117,24 +117,24 @@ public class StoryboardOverlay extends OverlayOpsuState implements OptionsOverla
} else {
MusicController.setPitch(speed / 10f);
}
} else if (key == Input.KEY_V && speed < 21) {
} else if (key == KEY_V && speed < 21) {
if (speed == 0) {
MusicController.resume();
}
speed += 1;
MusicController.setPitch(speed / 10f);
} else if (key == Input.KEY_H) {
} else if (key == KEY_H) {
hide = !hide;
} else if (key == Input.KEY_N) {
} else if (key == KEY_N) {
optionsOverlay.show();
if (speed != 0) {
MusicController.pause();
}
} else if (key == Input.KEY_J && index > 0) {
} else if (key == KEY_J && index > 0) {
index--;
goBackOneSBIndex();
setMusicPosition();
} else if (key == Input.KEY_K && index < gameObjects.length - 1) {
} else if (key == KEY_K && index < gameObjects.length - 1) {
index++;
updateIndex(index);
setMusicPosition();

View File

@@ -28,11 +28,12 @@ import org.newdawn.slick.opengl.LoadableImageData;
import org.newdawn.slick.opengl.TGAImageData;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static yugecin.opsudance.core.errorhandling.ErrorHandler.*;
public class GLHelper {
/**
@@ -96,7 +97,7 @@ public class GLHelper {
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
Mouse.setNativeCursor(new Cursor(min, min, min / 2, min / 2, 1, tmp, null));
} catch (LWJGLException e) {
ErrorHandler.error("Cannot hide native cursor", e).show();
explode("Cannot hide native cursor", e, DEFAULT_OPTIONS);
}
}
@@ -104,7 +105,7 @@ public class GLHelper {
try {
Mouse.setNativeCursor(null);
} catch (LWJGLException e) {
ErrorHandler.error("Cannot show native cursor", e).show();
explode("Cannot show native cursor", e, DEFAULT_OPTIONS);
}
}

View File

@@ -0,0 +1,54 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.utils;
import yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.Nullable;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
public class ManifestWrapper {
@Nullable
public final Manifest manifest;
public ManifestWrapper(@Nullable Manifest manifest) {
this.manifest = manifest;
}
/**
* @param attribute attribute in jarfile or null for default attributes
*/
public String valueOrDefault(@Nullable String attribute, @NotNull String key, @Nullable String dfault) {
if (manifest == null) {
return dfault;
}
Attributes attributes =
attribute == null ? manifest.getMainAttributes() : manifest.getAttributes(attribute);
if (attributes == null) {
return dfault;
}
String val = attributes.getValue(key);
if (val == null) {
return dfault;
}
return val;
}
}

View File

@@ -17,9 +17,9 @@
*/
package yugecin.opsudance.utils;
import itdelatrisu.opsu.Options;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.Constants;
import java.io.IOException;
import java.util.Properties;
@@ -31,7 +31,7 @@ public class MiscUtils {
public Properties get() {
Properties props = new Properties();
try {
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
props.load(ResourceLoader.getResourceAsStream(Constants.VERSION_FILE));
} catch (IOException e) {
Log.error("Could not read version file", e);
}

View File

@@ -15,13 +15,20 @@
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.core.state.transitions;
package yugecin.opsudance.utils;
public class FadeOutTransitionState extends FadeTransitionState {
import java.io.Closeable;
import java.io.IOException;
@Override
protected float getMaskAlphaLevel(float fadeProgress) {
return fadeProgress;
public class SyntacticSugar {
/**
* close the closeable and swallow exceptions, if any
*/
public static void closeAndSwallow(Closeable closable) {
try {
closable.close();
} catch (IOException ignored) {}
}
}