Merge branch 'master' into replaystuff

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

View File

@ -1,4 +1,4 @@
#opsu!dance # opsu!dance
[example video](https://www.youtube.com/watch?v=tqZqn7nx8N0) [example video](https://www.youtube.com/watch?v=tqZqn7nx8N0)
Originally started as a fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordance stuff. I made a cursordancing bot in C# for osu!, and by adding it into this clone, it allows me to do even more stuff with it. This way I can also provide this client to other players so they can play with it too, as I will not give my bot to people because I don't want to endorse cheating in any way. Originally started as a fork of [opsu!](https://github.com/itdelatrisu/opsu) with cursordance stuff. I made a cursordancing bot in C# for osu!, and by adding it into this clone, it allows me to do even more stuff with it. This way I can also provide this client to other players so they can play with it too, as I will not give my bot to people because I don't want to endorse cheating in any way.
@ -7,17 +7,17 @@ As of 2017 some major changes were made in this fork which changed the inner wor
My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way. My goal is to to add cool cursordancing things to this fork, but also make it possible to play the normal way.
###Downloads ### Downloads
Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars. Click on the releases link (scroll up) to go to the downloadpage with prebuilt jars.
###Building ### Building
You can find general (run/build) instructions in the original [opsu! README](README-OPSU.md). You can find general (run/build) instructions in the original [opsu! README](README-OPSU.md).
Please note that I am only using maven, gradle scripts are not being updated. Please note that I am only using maven, gradle scripts are not being updated.
###Credits ### Credits
opsu! was made by Jeffrey Han ([@itdelatrisu](https://github.com/itdelatrisu)). All game concepts and designs are based on work by osu! developer Dean Herbert. Other opsu! credits can be found [here](CREDITS.md). opsu! was made by Jeffrey Han ([@itdelatrisu](https://github.com/itdelatrisu)). All game concepts and designs are based on work by osu! developer Dean Herbert. Other opsu! credits can be found [here](CREDITS.md).
opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Edits were made in the opsu! sources, too. opsu!dance (everything in the src package yugecin.opsudance) was made by me ([@yugecin](https://github.com/yugecin)). Edits were made in the opsu! sources, too.
###License ### License
**This software is licensed under GNU GPL version 3.** **This software is licensed under GNU GPL version 3.**
You can find the full text of the license [here](LICENSE). You can find the full text of the license [here](LICENSE).

View File

@ -4,11 +4,11 @@ package awlex.ospu;
* Created by Awlex on 10.10.2016. * Created by Awlex on 10.10.2016.
*/ */
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.options.Options;
/** /**
* This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects. * This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects.

View File

@ -1,11 +1,11 @@
package awlex.ospu.movers; package awlex.ospu.movers;
import awlex.ospu.FakeGameObject; import awlex.ospu.FakeGameObject;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import yugecin.opsudance.movers.Mover; import yugecin.opsudance.movers.Mover;
import yugecin.opsudance.movers.factories.AutoMoverFactory; import yugecin.opsudance.movers.factories.AutoMoverFactory;
import yugecin.opsudance.options.Options;
/** /**
* Created by Alex Wieser on 09.10.2016. * Created by Alex Wieser on 09.10.2016.

View File

@ -1,6 +1,5 @@
package awlex.ospu.movers.factories; package awlex.ospu.movers.factories;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
import awlex.ospu.movers.CentralSpiralMover; import awlex.ospu.movers.CentralSpiralMover;
@ -8,6 +7,7 @@ import awlex.ospu.movers.CombinedSpiralMover;
import yugecin.opsudance.movers.Mover; import yugecin.opsudance.movers.Mover;
import awlex.ospu.movers.SpiralToMover; import awlex.ospu.movers.SpiralToMover;
import yugecin.opsudance.movers.factories.MoverFactory; import yugecin.opsudance.movers.factories.MoverFactory;
import yugecin.opsudance.options.Options;
/** /**
* Created by Alex Wieser on 09.10.2016. * Created by Alex Wieser on 09.10.2016.

View File

@ -1,7 +1,7 @@
package awlex.ospu.spinners; package awlex.ospu.spinners;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.spinners.Spinner; import yugecin.opsudance.spinners.Spinner;
/** /**

View File

@ -42,12 +42,25 @@ import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil; import yugecin.opsudance.utils.SlickUtil;
import static yugecin.opsudance.options.Options.*;
/** /**
* Holds game data and renders all related elements. * Holds game data and renders all related elements.
*/ */
public class GameData { public class GameData {
@Inject
private Configuration config;
@Inject
private InstanceContainer instanceContainer;
/** Delta multiplier for steady HP drain. */ /** Delta multiplier for steady HP drain. */
public static final float HP_DRAIN_MULTIPLIER = 1 / 200f; public static final float HP_DRAIN_MULTIPLIER = 1 / 200f;
@ -383,7 +396,7 @@ public class GameData {
hitResultCount[HIT_100K] = s.katu; hitResultCount[HIT_100K] = s.katu;
hitResultCount[HIT_MISS] = s.miss; hitResultCount[HIT_MISS] = s.miss;
this.replay = (s.replayString == null) ? null : this.replay = (s.replayString == null) ? null :
new Replay(new File(Options.getReplayDir(), String.format("%s.osr", s.replayString))); instanceContainer.injectFields(new Replay(new File(config.replayDir, String.format("%s.osr", s.replayString))));
loadImages(); loadImages();
} }
@ -515,7 +528,7 @@ public class GameData {
if (digitWidth <= 1f) { if (digitWidth <= 1f) {
return; return;
} }
digitWidth = (digitWidth - Options.getSkin().getHitCircleFontOverlap()) * scale; digitWidth = (digitWidth - SkinService.skin.getHitCircleFontOverlap()) * scale;
float cx = x + ((length - 1) * (digitWidth / 2)); float cx = x + ((length - 1) * (digitWidth / 2));
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
@ -544,7 +557,7 @@ public class GameData {
Image digit = getScoreSymbolImage(c[i]); Image digit = getScoreSymbolImage(c[i]);
if (scale != 1.0f) if (scale != 1.0f)
digit = digit.getScaledCopy(scale); digit = digit.getScaledCopy(scale);
cx -= digit.getWidth() + Options.getSkin().getScoreFontOverlap(); cx -= digit.getWidth() + SkinService.skin.getScoreFontOverlap();
digit.setAlpha(alpha); digit.setAlpha(alpha);
digit.draw(cx, y); digit.draw(cx, y);
digit.setAlpha(1f); digit.setAlpha(1f);
@ -557,7 +570,7 @@ public class GameData {
digit.setAlpha(alpha); digit.setAlpha(alpha);
digit.draw(cx, y); digit.draw(cx, y);
digit.setAlpha(1f); digit.setAlpha(1f);
cx += digit.getWidth() - Options.getSkin().getScoreFontOverlap(); cx += digit.getWidth() - SkinService.skin.getScoreFontOverlap();
} }
} }
} }
@ -679,7 +692,7 @@ public class GameData {
} }
// hit error bar // hit error bar
if (Options.isHitErrorBarEnabled() && !hitErrorList.isEmpty()) { if (OPTION_SHOW_HIT_ERROR_BAR.state && !hitErrorList.isEmpty()) {
// fade out with last tick // fade out with last tick
float hitErrorAlpha = 1f; float hitErrorAlpha = 1f;
Color white = new Color(Color.white); Color white = new Color(Color.white);
@ -914,10 +927,8 @@ public class GameData {
spinnerOsu.setAlpha(hitResult.alpha); spinnerOsu.setAlpha(hitResult.alpha);
spinnerOsu.drawCentered(width / 2, height / 4); spinnerOsu.drawCentered(width / 2, height / 4);
spinnerOsu.setAlpha(1f); spinnerOsu.setAlpha(1f);
} } else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS &&
// hit lighting // hit lighting
else if (Options.isHitLightingEnabled() && !hitResult.hideResult && hitResult.result != HIT_MISS &&
hitResult.result != HIT_SLIDER30 && hitResult.result != HIT_SLIDER10) { hitResult.result != HIT_SLIDER30 && hitResult.result != HIT_SLIDER10) {
// TODO: add particle system // TODO: add particle system
Image lighting = GameImage.LIGHTING.getImage(); Image lighting = GameImage.LIGHTING.getImage();
@ -965,14 +976,14 @@ public class GameData {
private void drawHitAnimations(HitObjectResult hitResult, int trackPosition) { private void drawHitAnimations(HitObjectResult hitResult, int trackPosition) {
// fade out slider curve // fade out slider curve
if (hitResult.result != HIT_SLIDER_REPEAT && hitResult.result != HIT_SLIDER_REPEAT_M && hitResult.curve != null) { if (hitResult.result != HIT_SLIDER_REPEAT && hitResult.result != HIT_SLIDER_REPEAT_M && hitResult.curve != null) {
if (!Options.isShrinkingSliders()) { if (!OPTION_SHRINKING_SLIDERS.state) {
float progress = AnimationEquation.OUT_CUBIC.calc( float progress = AnimationEquation.OUT_CUBIC.calc(
(float) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME) / HITCIRCLE_FADE_TIME); (float) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME) / HITCIRCLE_FADE_TIME);
float alpha = 1f - progress; float alpha = 1f - progress;
float oldWhiteAlpha = Colors.WHITE_FADE.a; float oldWhiteAlpha = Colors.WHITE_FADE.a;
float oldColorAlpha = hitResult.color.a; float oldColorAlpha = hitResult.color.a;
Colors.WHITE_FADE.a = hitResult.color.a = alpha; Colors.WHITE_FADE.a = hitResult.color.a = alpha;
hitResult.curve.draw(hitResult.color); hitResult.curve.draw(hitResult.color, (!OPTION_FALLBACK_SLIDERS.state && OPTION_MERGING_SLIDERS.state) ? 1 : 0, hitResult.curve.getCurvePoints().length);
Colors.WHITE_FADE.a = oldWhiteAlpha; Colors.WHITE_FADE.a = oldWhiteAlpha;
hitResult.color.a = oldColorAlpha; hitResult.color.a = oldColorAlpha;
} }
@ -987,7 +998,7 @@ public class GameData {
fc.drawCentered(hitResult.x, hitResult.y); fc.drawCentered(hitResult.x, hitResult.y);
} }
if (!Options.isDrawSliderEndCircles()) { if (!OPTION_DRAW_SLIDER_ENDCIRCLES.state) {
return; return;
} }
} }
@ -1024,7 +1035,7 @@ public class GameData {
} }
scaledRepeat.rotate(ang); scaledRepeat.rotate(ang);
scaledRepeat.drawCentered(hitResult.x, hitResult.y, hitResult.color); scaledRepeat.drawCentered(hitResult.x, hitResult.y, hitResult.color);
if (!Options.isDrawSliderEndCircles()) { if (!OPTION_DRAW_SLIDER_ENDCIRCLES.state) {
GameImage.HITCIRCLE.getImage().draw(-1000, -1000); // TODO this 'fixes' #114. Why? Get a better solution! GameImage.HITCIRCLE.getImage().draw(-1000, -1000); // TODO this 'fixes' #114. Why? Get a better solution!
GameImage.HITCIRCLE_OVERLAY.getImage().draw(-1000, -1000); GameImage.HITCIRCLE_OVERLAY.getImage().draw(-1000, -1000);
return; return;
@ -1186,7 +1197,7 @@ public class GameData {
} }
// combo burst // combo burst
if (comboBurstIndex > -1 && Options.isComboBurstEnabled()) { if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) {
int leftX = 0; int leftX = 0;
int rightX = width - comboBurstImages[comboBurstIndex].getWidth(); int rightX = width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) { if (comboBurstX < leftX) {
@ -1210,7 +1221,7 @@ public class GameData {
comboPopTime = COMBO_POP_TIME; comboPopTime = COMBO_POP_TIME;
// hit error bar // hit error bar
if (Options.isHitErrorBarEnabled()) { if (OPTION_SHOW_HIT_ERROR_BAR.state) {
int trackPosition = MusicController.getPosition(); int trackPosition = MusicController.getPosition();
Iterator<HitErrorInfo> iter = hitErrorList.iterator(); Iterator<HitErrorInfo> iter = hitErrorList.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
@ -1237,23 +1248,24 @@ public class GameData {
comboMax = combo; comboMax = combo;
// combo bursts (at 30, 60, 100+50x) // combo bursts (at 30, 60, 100+50x)
if (Options.isComboBurstEnabled() && if (OPTION_SHOW_COMBO_BURSTS.state && (combo == 30 || combo == 60 || (combo >= 100 && combo % 50 == 0))) {
(combo == 30 || combo == 60 || (combo >= 100 && combo % 50 == 0))) { if (SkinService.skin.isComboBurstRandom()) {
if (Options.getSkin().isComboBurstRandom())
comboBurstIndex = (int) (Math.random() * comboBurstImages.length); comboBurstIndex = (int) (Math.random() * comboBurstImages.length);
else { } else {
if (combo == 30) if (combo == 30) {
comboBurstIndex = 0; comboBurstIndex = 0;
else } else {
comboBurstIndex = (comboBurstIndex + 1) % comboBurstImages.length; comboBurstIndex = (comboBurstIndex + 1) % comboBurstImages.length;
} }
}
comboBurstAlpha = 0.8f; comboBurstAlpha = 0.8f;
if ((comboBurstIndex % 2) == 0) if ((comboBurstIndex % 2) == 0) {
comboBurstX = width; comboBurstX = width;
else } else {
comboBurstX = comboBurstImages[0].getWidth() * -1; comboBurstX = comboBurstImages[0].getWidth() * -1;
} }
} }
}
/** /**
* Resets the combo streak to zero. * Resets the combo streak to zero.
@ -1277,7 +1289,7 @@ public class GameData {
*/ */
public void sendSliderRepeatResult(int time, float x, float y, Color color, Curve curve, HitObjectType type) { public void sendSliderRepeatResult(int time, float x, float y, Color color, Curve curve, HitObjectType type) {
hitResultList.add(new HitObjectResult(time, HIT_SLIDER_REPEAT, x, y, color, type, curve, true, true)); hitResultList.add(new HitObjectResult(time, HIT_SLIDER_REPEAT, x, y, color, type, curve, true, true));
if (!Options.isMirror()) { if (!OPTION_DANCE_MIRROR.state) {
return; return;
} }
float[] m = Utils.mirrorPoint(x, y); float[] m = Utils.mirrorPoint(x, y);
@ -1294,7 +1306,7 @@ public class GameData {
*/ */
public void sendSliderStartResult(int time, float x, float y, Color color, Color mirrorColor, boolean expand) { public void sendSliderStartResult(int time, float x, float y, Color color, Color mirrorColor, boolean expand) {
hitResultList.add(new HitObjectResult(time, HIT_ANIMATION_RESULT, x, y, color, HitObjectType.CIRCLE, null, expand, true)); hitResultList.add(new HitObjectResult(time, HIT_ANIMATION_RESULT, x, y, color, HitObjectType.CIRCLE, null, expand, true));
if (!Options.isMirror()) { if (!OPTION_DANCE_MIRROR.state) {
return; return;
} }
float[] m = Utils.mirrorPoint(x, y); float[] m = Utils.mirrorPoint(x, y);
@ -1338,11 +1350,10 @@ public class GameData {
score += hitValue; score += hitValue;
incrementComboStreak(); incrementComboStreak();
if (!Options.isPerfectHitBurstEnabled()) if (OPTION_SHOW_PERFECT_HIT.state) {
; // hide perfect hit results
else
hitResultList.add(new HitObjectResult(time, result, x, y, null, HitObjectType.SLIDERTICK, null, false, false)); hitResultList.add(new HitObjectResult(time, result, x, y, null, HitObjectType.SLIDERTICK, null, false, false));
} }
}
fullObjectCount++; fullObjectCount++;
} }
@ -1527,7 +1538,7 @@ public class GameData {
if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive())) if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()))
return; // "relax" and "autopilot" mods: hide misses return; // "relax" and "autopilot" mods: hide misses
boolean hideResult = (hitResult == HIT_300 || hitResult == HIT_300G || hitResult == HIT_300K) && !Options.isPerfectHitBurstEnabled(); boolean hideResult = (hitResult == HIT_300 || hitResult == HIT_300G || hitResult == HIT_300K) && !OPTION_SHOW_PERFECT_HIT.state;
hitResultList.add(new HitObjectResult(time, hitResult, x, y, color, hitResultType, curve, expand, hideResult)); hitResultList.add(new HitObjectResult(time, hitResult, x, y, color, hitResultType, curve, expand, hideResult));
} }

View File

@ -32,8 +32,11 @@ import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil; import yugecin.opsudance.utils.SlickUtil;
import static yugecin.opsudance.options.Options.*;
/** /**
* Game images. * Game images.
*/ */
@ -546,7 +549,7 @@ public enum GameImage {
* and UI scale. * and UI scale.
*/ */
private static String[] getSuffixes() { private static String[] getSuffixes() {
return (Options.loadHDImages() && uiscale >= 1) ? SUFFIXES_HD : SUFFIXES_SD; return (OPTION_LOAD_HD_IMAGES.state && uiscale >= 1) ? SUFFIXES_HD : SUFFIXES_SD;
} }
@ -687,11 +690,12 @@ public enum GameImage {
* If the default image has already been loaded, this will do nothing. * If the default image has already been loaded, this will do nothing.
*/ */
public void setDefaultImage() { public void setDefaultImage() {
if (defaultImage != null || defaultImages != null || Options.getSkin() == null) if (defaultImage != null || defaultImages != null || SkinService.skin == null) {
return; return;
}
// try to load skin images // try to load skin images
File skinDir = Options.getSkin().getDirectory(); File skinDir = SkinService.skin.getDirectory();
if (filenameFormat != null) { if (filenameFormat != null) {
if (skinDir != null && ((defaultImages = loadImageArray(skinDir)) != null)) { if (skinDir != null && ((defaultImages = loadImageArray(skinDir)) != null)) {
isSkinned = true; isSkinned = true;
@ -739,15 +743,17 @@ public enum GameImage {
* @return true if a new skin image is loaded, false otherwise * @return true if a new skin image is loaded, false otherwise
*/ */
public boolean setBeatmapSkinImage(File dir) { public boolean setBeatmapSkinImage(File dir) {
if (dir == null) if (dir == null) {
return false; return false;
}
// destroy the existing images, if any // destroy the existing images, if any
destroyBeatmapSkinImage(); destroyBeatmapSkinImage();
// beatmap skins disabled // beatmap skins disabled
if (Options.isBeatmapSkinIgnored()) if (OPTION_IGNORE_BEATMAP_SKINS.state) {
return false; return false;
}
// try to load multiple images // try to load multiple images
if ((skinImages = loadImageArray(dir)) != null) { if ((skinImages = loadImageArray(dir)) != null) {

File diff suppressed because it is too large Load Diff

View File

@ -18,17 +18,8 @@
package itdelatrisu.opsu; package itdelatrisu.opsu;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Download; import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.replay.PlaybackSpeed;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -41,18 +32,13 @@ import java.net.HttpURLConnection;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.Scanner; import java.util.Scanner;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import javax.imageio.ImageIO;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
@ -61,21 +47,15 @@ import javax.net.ssl.X509TrustManager;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.lwjgl.BufferUtils; import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Animation; import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import com.sun.jna.platform.FileUtils; import com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.options.Options;
import yugecin.opsudance.events.BubbleNotificationEvent;
/** /**
* Contains miscellaneous utilities. * Contains miscellaneous utilities.
@ -104,32 +84,6 @@ public class Utils {
// TODO clean this up // TODO clean this up
// game settings // game settings
displayContainer.setFPS(Options.getTargetFPS()); // TODO move this elsewhere
MusicController.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
// load skin
Options.loadSkin();
// initialize game images
for (GameImage img : GameImage.values()) {
if (img.isPreload())
img.setDefaultImage();
}
// initialize game mods
GameMod.init(displayContainer.width, displayContainer.height);
// initialize playback buttons
PlaybackSpeed.init(displayContainer.width, displayContainer.height);
// initialize hit objects
HitObject.init(displayContainer.width, displayContainer.height);
// initialize download nodes
DownloadNode.init(displayContainer.width, displayContainer.height);
// initialize UI components
UI.init(displayContainer);
} }
/** /**
@ -240,55 +194,6 @@ public class Utils {
return true; return true;
} }
/**
* Takes a screenshot.
* @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots
*/
public static void takeScreenShot() {
// create the screenshot directory
File dir = Options.getScreenshotDir();
if (!dir.isDirectory() && !dir.mkdir()) {
EventBus.post(new BubbleNotificationEvent(String.format("Failed to create screenshot directory at '%s'.", dir.getAbsolutePath()), BubbleNotificationEvent.COMMONCOLOR_RED));
return;
}
// create file name
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
final String fileName = String.format("screenshot_%s.%s", date.format(new Date()), Options.getScreenshotFormat());
final File file = new File(dir, fileName);
SoundController.playSound(SoundEffect.SHUTTER);
// copy the screen to file
final int width = Display.getWidth();
final int height = Display.getHeight();
final int bpp = 3; // assuming a 32-bit display with a byte each for red, green, blue, and alpha
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
GL11.glReadBuffer(GL11.GL_FRONT);
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
new Thread() {
@Override
public void run() {
try {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int i = (x + (width * y)) * bpp;
int r = buffer.get(i) & 0xFF;
int g = buffer.get(i + 1) & 0xFF;
int b = buffer.get(i + 2) & 0xFF;
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
ImageIO.write(image, Options.getScreenshotFormat(), file);
EventBus.post(new BubbleNotificationEvent("Created " + fileName, BubbleNotificationEvent.COMMONCOLOR_PURPLE));
} catch (Exception e) {
ErrorHandler.error("Failed to take a screenshot.", e).show();
}
}
}.start();
}
/** /**
* Returns a human-readable representation of a given number of bytes. * Returns a human-readable representation of a given number of bytes.
@ -550,14 +455,6 @@ public class Utils {
} }
} }
/**
* Returns the current working directory.
* @return the directory
*/
public static File getWorkingDirectory() {
return Paths.get(".").toAbsolutePath().normalize().toFile();
}
/** /**
* Parses the integer string argument as a boolean: * Parses the integer string argument as a boolean:
* {@code 1} is {@code true}, and all other values are {@code false}. * {@code 1} is {@code true}, and all other values are {@code false}.
@ -661,4 +558,19 @@ public class Utils {
}; };
} }
/**
* Returns the file extension of a file.
* @param file the file name
*/
public static String getFileExtension(String file) {
int i = file.lastIndexOf('.');
return (i != -1) ? file.substring(i + 1).toLowerCase() : "";
}
public static boolean isValidGameKey(int key) {
return (key != Keyboard.KEY_ESCAPE && key != Keyboard.KEY_SPACE &&
key != Keyboard.KEY_UP && key != Keyboard.KEY_DOWN &&
key != Keyboard.KEY_F7 && key != Keyboard.KEY_F10 && key != Keyboard.KEY_F12);
}
} }

View File

@ -18,9 +18,8 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Game;
@ -50,6 +49,8 @@ import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import static yugecin.opsudance.options.Options.*;
/** /**
* Controller for all music. * Controller for all music.
*/ */
@ -109,7 +110,7 @@ public class MusicController {
reset(); reset();
System.gc(); System.gc();
switch (BeatmapParser.getExtension(beatmap.audioFilename.getName())) { switch (Utils.getFileExtension(beatmap.audioFilename.getName())) {
case "ogg": case "ogg":
case "mp3": case "mp3":
trackLoader = new Thread() { trackLoader = new Thread() {
@ -168,7 +169,7 @@ public class MusicController {
*/ */
public static void playAt(final int position, final boolean loop) { public static void playAt(final int position, final boolean loop) {
if (trackExists()) { if (trackExists()) {
setVolume(Options.getMusicVolume() * Options.getMasterVolume()); setVolume(OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f);
trackEnded = false; trackEnded = false;
pauseTime = 0f; pauseTime = 0f;
resetTimingPoint(); resetTimingPoint();
@ -348,9 +349,9 @@ public class MusicController {
*/ */
public static int getPosition() { public static int getPosition() {
if (isPlaying()) if (isPlaying())
return (int) (player.getPosition() * 1000 + Options.getMusicOffset() + Game.currentMapMusicOffset); return (int) (player.getPosition() * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset);
else if (isPaused()) else if (isPaused())
return Math.max((int) (pauseTime * 1000 + Options.getMusicOffset() + Game.currentMapMusicOffset), 0); return Math.max((int) (pauseTime * 1000 + OPTION_MUSIC_OFFSET.val + Game.currentMapMusicOffset), 0);
else else
return 0; return 0;
} }
@ -443,13 +444,9 @@ public class MusicController {
playAt((preview) ? lastBeatmap.previewTime : 0, false); playAt((preview) ? lastBeatmap.previewTime : 0, false);
} }
/** public static void playThemeSong(Beatmap themeBeatmap) {
* Plays the theme song. if (themeBeatmap != null) {
*/ play(themeBeatmap, false, false);
public static void playThemeSong() {
Beatmap beatmap = Options.getThemeBeatmap();
if (beatmap != null) {
play(beatmap, false, false);
themePlaying = true; themePlaying = true;
} }
} }
@ -470,7 +467,7 @@ public class MusicController {
* @param multiplier the volume multiplier when the track is dimmed * @param multiplier the volume multiplier when the track is dimmed
*/ */
public static void toggleTrackDimmed(float multiplier) { public static void toggleTrackDimmed(float multiplier) {
float volume = Options.getMusicVolume() * Options.getMasterVolume(); float volume = OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f;
dimLevel = (isTrackDimmed()) ? 1f : multiplier; dimLevel = (isTrackDimmed()) ? 1f : multiplier;
trackDimmed = !trackDimmed; trackDimmed = !trackDimmed;
setVolume(volume); setVolume(volume);

View File

@ -18,12 +18,10 @@
package itdelatrisu.opsu.audio; package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.HitSound.SampleSet; import itdelatrisu.opsu.audio.HitSound.SampleSet;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Download; import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.Download.DownloadListener; import itdelatrisu.opsu.downloads.Download.DownloadListener;
import itdelatrisu.opsu.ui.UI;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -44,6 +42,10 @@ import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Controller for all (non-music) sound components. * Controller for all (non-music) sound components.
@ -190,7 +192,7 @@ public class SoundController {
*/ */
private static String getSoundFileName(String filename) { private static String getSoundFileName(String filename) {
String wav = String.format("%s.wav", filename), mp3 = String.format("%s.mp3", filename); String wav = String.format("%s.wav", filename), mp3 = String.format("%s.mp3", filename);
File skinDir = Options.getSkin().getDirectory(); File skinDir = SkinService.skin.getDirectory();
if (skinDir != null) { if (skinDir != null) {
File skinWAV = new File(skinDir, wav), skinMP3 = new File(skinDir, mp3); File skinWAV = new File(skinDir, wav), skinMP3 = new File(skinDir, mp3);
if (skinWAV.isFile()) if (skinWAV.isFile())
@ -209,8 +211,9 @@ public class SoundController {
* Loads all sound files. * Loads all sound files.
*/ */
public static void init() { public static void init() {
if (Options.isSoundDisabled()) if (OPTION_DISABLE_SOUNDS.state) {
return; return;
}
currentFileIndex = 0; currentFileIndex = 0;
@ -290,7 +293,7 @@ public class SoundController {
* @param s the sound effect * @param s the sound effect
*/ */
public static void playSound(SoundComponent s) { public static void playSound(SoundComponent s) {
playClip(s.getClip(), Options.getEffectVolume() * Options.getMasterVolume(), null); playClip(s.getClip(), OPTION_EFFECT_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f, null);
} }
/** /**
@ -303,16 +306,16 @@ public class SoundController {
if (hitSound < 0) if (hitSound < 0)
return; return;
if (Options.getSampleVolumeOverride() > 0) { if (OPTION_SAMPLE_VOLUME_OVERRIDE.val > 0) {
sampleVolumeMultiplier = Options.getSampleVolumeOverride(); sampleVolumeMultiplier = OPTION_SAMPLE_VOLUME_OVERRIDE.val / 100f;
} }
float volume = Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume(); float volume = OPTION_HITSOUND_VOLUME.val / 100f * sampleVolumeMultiplier * OPTION_MASTER_VOLUME.val / 100f;
if (volume == 0f) if (volume == 0f)
return; return;
// play all sounds // play all sounds
if (hitSound == HitObject.SOUND_NORMAL || Options.getSkin().isLayeredHitSounds()) { if (hitSound == HitObject.SOUND_NORMAL || SkinService.skin.isLayeredHitSounds()) {
HitSound.setSampleSet(sampleSet); HitSound.setSampleSet(sampleSet);
playClip(HitSound.NORMAL.getClip(), volume, null); playClip(HitSound.NORMAL.getClip(), volume, null);
} }
@ -333,7 +336,7 @@ public class SoundController {
* @param s the hit sound * @param s the hit sound
*/ */
public static void playHitSound(SoundComponent s) { public static void playHitSound(SoundComponent s) {
playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume(), null); playClip(s.getClip(), OPTION_HITSOUND_VOLUME.val / 100f * sampleVolumeMultiplier * OPTION_MASTER_VOLUME.val / 100f, null);
} }
/** /**
@ -370,15 +373,16 @@ public class SoundController {
* @return true if playing, false otherwise * @return true if playing, false otherwise
* @throws SlickException if any error occurred * @throws SlickException if any error occurred
*/ */
public static synchronized boolean playTrack(String url, String name, boolean isMP3, LineListener listener) public static synchronized boolean playTrack(Configuration config, String url, String name, boolean isMP3, LineListener listener)
throws SlickException { throws SlickException {
// stop previous track // stop previous track
stopTrack(); stopTrack();
// download new track // download new track
File dir = Options.TEMP_DIR; File dir = config.TEMP_DIR;
if (!dir.isDirectory()) if (!dir.isDirectory()) {
dir.mkdir(); dir.mkdir();
}
String filename = String.format("%s.%s", name, isMP3 ? "mp3" : "wav"); String filename = String.format("%s.%s", name, isMP3 ? "mp3" : "wav");
final File downloadFile = new File(dir, filename); final File downloadFile = new File(dir, filename);
boolean complete; boolean complete;
@ -406,7 +410,7 @@ public class SoundController {
try { try {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(downloadFile); AudioInputStream audioIn = AudioSystem.getAudioInputStream(downloadFile);
currentTrack = loadClip(filename, audioIn, isMP3); currentTrack = loadClip(filename, audioIn, isMP3);
playClip(currentTrack, Options.getMusicVolume() * Options.getMasterVolume(), listener); playClip(currentTrack, OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f, listener);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
throw new SlickException(String.format("Failed to load clip '%s'.", url)); throw new SlickException(String.format("Failed to load clip '%s'.", url));

View File

@ -18,8 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -30,6 +28,8 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import static yugecin.opsudance.options.Options.*;
/** /**
* Beatmap structure storing data parsed from OSU files. * Beatmap structure storing data parsed from OSU files.
*/ */
@ -264,7 +264,7 @@ public class Beatmap implements Comparable<Beatmap> {
* @return the song title * @return the song title
*/ */
public String getTitle() { public String getTitle() {
return (Options.useUnicodeMetadata() && !titleUnicode.isEmpty()) ? titleUnicode : title; return (OPTION_SHOW_UNICODE.state && !titleUnicode.isEmpty()) ? titleUnicode : title;
} }
/** /**
@ -273,25 +273,23 @@ public class Beatmap implements Comparable<Beatmap> {
* @return the song artist * @return the song artist
*/ */
public String getArtist() { public String getArtist() {
return (Options.useUnicodeMetadata() && !artistUnicode.isEmpty()) ? artistUnicode : artist; return (OPTION_SHOW_UNICODE.state && !artistUnicode.isEmpty()) ? artistUnicode : artist;
} }
/** /**
* Returns the list of combo colors (max 8). * Returns the list of combo colors (max 8).
* If the beatmap does not provide colors, the skin colors will be returned instead. * @return the combo colors, or null if this beatmap does not have combo colors specified.
* @return the combo colors
*/ */
public Color[] getComboColors() { public Color[] getComboColors() {
return (combo != null) ? combo : Options.getSkin().getComboColors(); return combo;
} }
/** /**
* Returns the slider border color. * Returns the slider border color.
* If the beatmap does not provide a color, the skin color will be returned instead. * @return the slider border color, or null if this beatmap does not have a slider border color specified.
* @return the slider border color
*/ */
public Color getSliderBorderColor() { public Color getSliderBorderColor() {
return (sliderBorder != null) ? sliderBorder : Options.getSkin().getSliderBorderColor(); return sliderBorder;
} }
/** /**

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.io.MD5InputStreamWrapper; import itdelatrisu.opsu.io.MD5InputStreamWrapper;
@ -36,55 +35,69 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Parser for beatmaps. * Parser for beatmaps.
*/ */
public class BeatmapParser { public class BeatmapParser {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
/** The string lookup database. */ /** The string lookup database. */
private static HashMap<String, String> stringdb = new HashMap<String, String>(); private static HashMap<String, String> stringdb = new HashMap<String, String>();
/** The expected pattern for beatmap directories, used to find beatmap set IDs. */ /** The expected pattern for beatmap directories, used to find beatmap set IDs. */
private static final String DIR_MSID_PATTERN = "^\\d+ .*"; private final String DIR_MSID_PATTERN = "^\\d+ .*";
/** The current file being parsed. */ /** The current file being parsed. */
private static File currentFile; private File currentFile;
/** The current directory number while parsing. */ /** The current directory number while parsing. */
private static int currentDirectoryIndex = -1; private int currentDirectoryIndex = -1;
/** The total number of directories to parse. */ /** The total number of directories to parse. */
private static int totalDirectories = -1; private int totalDirectories = -1;
/** Parser statuses. */ /** Parser statuses. */
public enum Status { NONE, PARSING, CACHE, INSERTING }; public enum Status { NONE, PARSING, CACHE, INSERTING };
/** The current status. */ /** The current status. */
private static Status status = Status.NONE; private Status status = Status.NONE;
/** If no Provider supports a MessageDigestSpi implementation for the MD5 algorithm. */ /** If no Provider supports a MessageDigestSpi implementation for the MD5 algorithm. */
private static boolean hasNoMD5Algorithm = false; private boolean hasNoMD5Algorithm = false;
// This class should not be instantiated. @Inject
private BeatmapParser() {} public BeatmapParser() {
}
/** /**
* Invokes parser for each OSU file in a root directory and * Invokes parser for each OSU file in a root directory and
* adds the beatmaps to a new BeatmapSetList. * adds the beatmaps to a new BeatmapSetList.
* @param root the root directory (search has depth 1) * @param root the root directory (search has depth 1)
*/ */
public static void parseAllFiles(File root) { public void parseAll() {
// create a new BeatmapSetList // create a new BeatmapSetList
BeatmapSetList.create(); BeatmapSetList.create();
// create a new watch service // create a new watch service
if (Options.isWatchServiceEnabled()) if (OPTION_ENABLE_WATCH_SERVICE.state) {
BeatmapWatchService.create(); BeatmapWatchService.create(instanceContainer);
}
// parse all directories // parse all directories
parseDirectories(root.listFiles()); parseDirectories(config.beatmapDir.listFiles());
} }
/** /**
@ -93,7 +106,7 @@ public class BeatmapParser {
* @param dirs the array of directories to parse * @param dirs the array of directories to parse
* @return the last BeatmapSetNode parsed, or null if none * @return the last BeatmapSetNode parsed, or null if none
*/ */
public static BeatmapSetNode parseDirectories(File[] dirs) { public BeatmapSetNode parseDirectories(File[] dirs) {
if (dirs == null) if (dirs == null)
return null; return null;
@ -111,7 +124,7 @@ public class BeatmapParser {
List<Beatmap> parsedBeatmaps = new LinkedList<Beatmap>(); // loaded from parser List<Beatmap> parsedBeatmaps = new LinkedList<Beatmap>(); // loaded from parser
// watch service // watch service
BeatmapWatchService ws = (Options.isWatchServiceEnabled()) ? BeatmapWatchService.get() : null; BeatmapWatchService ws = BeatmapWatchService.get();
// parse directories // parse directories
BeatmapSetNode lastNode = null; BeatmapSetNode lastNode = null;
@ -215,7 +228,7 @@ public class BeatmapParser {
return lastNode; return lastNode;
} }
public static void parseOnlyTimingPoints(Beatmap map) { public void parseOnlyTimingPoints(Beatmap map) {
if (map == null || map.getFile() == null || !map.getFile().exists()) { if (map == null || map.getFile() == null || !map.getFile().exists()) {
return; return;
} }
@ -259,7 +272,7 @@ public class BeatmapParser {
} }
} }
private static void parseSectionTimingPoints(Beatmap beatmap, String line) { private void parseSectionTimingPoints(Beatmap beatmap, String line) {
TimingPoint timingPoint = new TimingPoint(line); TimingPoint timingPoint = new TimingPoint(line);
if(!timingPoint.isInherited()) { if(!timingPoint.isInherited()) {
int bpm = Math.round(60000 / timingPoint.getBeatLength()); int bpm = Math.round(60000 / timingPoint.getBeatLength());
@ -282,7 +295,7 @@ public class BeatmapParser {
* @param parseObjects if true, hit objects will be fully parsed now * @param parseObjects if true, hit objects will be fully parsed now
* @return the new beatmap * @return the new beatmap
*/ */
private static Beatmap parseFile(File file, File dir, ArrayList<Beatmap> beatmaps, boolean parseObjects) { private Beatmap parseFile(File file, File dir, ArrayList<Beatmap> beatmaps, boolean parseObjects) {
Beatmap beatmap = new Beatmap(file); Beatmap beatmap = new Beatmap(file);
beatmap.timingPoints = new ArrayList<TimingPoint>(); beatmap.timingPoints = new ArrayList<TimingPoint>();
@ -523,7 +536,7 @@ public class BeatmapParser {
switch (tokens[0]) { switch (tokens[0]) {
case "0": // background case "0": // background
tokens[2] = tokens[2].replaceAll("^\"|\"$", ""); tokens[2] = tokens[2].replaceAll("^\"|\"$", "");
String ext = BeatmapParser.getExtension(tokens[2]); String ext = Utils.getFileExtension(tokens[2]);
if (ext.equals("jpg") || ext.equals("png")) if (ext.equals("jpg") || ext.equals("png"))
beatmap.bg = new File(dir, getDBString(tokens[2])); beatmap.bg = new File(dir, getDBString(tokens[2]));
break; break;
@ -767,6 +780,9 @@ public class BeatmapParser {
// combo info // combo info
Color[] combo = beatmap.getComboColors(); Color[] combo = beatmap.getComboColors();
if (combo == null) {
combo = SkinService.skin.getComboColors();
}
int comboIndex = 0; // color index int comboIndex = 0; // color index
int comboNumber = 1; // combo number int comboNumber = 1; // combo number
@ -832,7 +848,7 @@ public class BeatmapParser {
* Splits line into two strings: tag, value. * Splits line into two strings: tag, value.
* If no ':' character is present, null will be returned. * If no ':' character is present, null will be returned.
*/ */
private static String[] tokenize(String line) { private String[] tokenize(String line) {
int index = line.indexOf(':'); int index = line.indexOf(':');
if (index == -1) { if (index == -1) {
Log.debug(String.format("Failed to tokenize line: '%s'.", line)); Log.debug(String.format("Failed to tokenize line: '%s'.", line));
@ -845,19 +861,10 @@ public class BeatmapParser {
return tokens; return tokens;
} }
/**
* Returns the file extension of a file.
* @param file the file name
*/
public static String getExtension(String file) {
int i = file.lastIndexOf('.');
return (i != -1) ? file.substring(i + 1).toLowerCase() : "";
}
/** /**
* Returns the name of the current file being parsed, or null if none. * Returns the name of the current file being parsed, or null if none.
*/ */
public static String getCurrentFileName() { public String getCurrentFileName() {
if (status == Status.PARSING) if (status == Status.PARSING)
return (currentFile != null) ? currentFile.getName() : null; return (currentFile != null) ? currentFile.getName() : null;
else else
@ -868,7 +875,7 @@ public class BeatmapParser {
* Returns the progress of file parsing, or -1 if not parsing. * Returns the progress of file parsing, or -1 if not parsing.
* @return the completion percent [0, 100] or -1 * @return the completion percent [0, 100] or -1
*/ */
public static int getParserProgress() { public int getParserProgress() {
if (currentDirectoryIndex == -1 || totalDirectories == -1) if (currentDirectoryIndex == -1 || totalDirectories == -1)
return -1; return -1;
@ -878,7 +885,9 @@ public class BeatmapParser {
/** /**
* Returns the current parser status. * Returns the current parser status.
*/ */
public static Status getStatus() { return status; } public Status getStatus() {
return status;
}
/** /**
* Returns the String object in the database for the given String. * Returns the String object in the database for the given String.
@ -894,4 +903,5 @@ public class BeatmapParser {
} else } else
return DBString; return DBString;
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
@ -210,7 +209,7 @@ public class BeatmapSetList {
BeatmapDB.delete(dir.getName()); BeatmapDB.delete(dir.getName());
// delete the associated directory // delete the associated directory
BeatmapWatchService ws = (Options.isWatchServiceEnabled()) ? BeatmapWatchService.get() : null; BeatmapWatchService ws = BeatmapWatchService.get();
if (ws != null) if (ws != null)
ws.pause(); ws.pause();
try { try {
@ -265,9 +264,10 @@ public class BeatmapSetList {
BeatmapDB.delete(file.getParentFile().getName(), file.getName()); BeatmapDB.delete(file.getParentFile().getName(), file.getName());
// delete the associated file // delete the associated file
BeatmapWatchService ws = (Options.isWatchServiceEnabled()) ? BeatmapWatchService.get() : null; BeatmapWatchService ws = BeatmapWatchService.get();
if (ws != null) if (ws != null) {
ws.pause(); ws.pause();
}
try { try {
Utils.deleteToTrash(file); Utils.deleteToTrash(file);
} catch (IOException e) { } catch (IOException e) {

View File

@ -20,12 +20,14 @@ package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.GameData.Grade; import itdelatrisu.opsu.GameData.Grade;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.Fonts;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Node in an BeatmapSetList representing a beatmap set. * Node in an BeatmapSetList representing a beatmap set.
@ -78,14 +80,14 @@ public class BeatmapSetNode {
Beatmap beatmap = beatmapSet.get(expanded ? beatmapIndex : 0); Beatmap beatmap = beatmapSet.get(expanded ? beatmapIndex : 0);
bg.setAlpha(0.9f); bg.setAlpha(0.9f);
Color bgColor; Color bgColor;
Color textColor = Options.getSkin().getSongSelectInactiveTextColor(); Color textColor = SkinService.skin.getSongSelectInactiveTextColor();
// get drawing parameters // get drawing parameters
if (expanded) { if (expanded) {
x -= bg.getWidth() / 10f; x -= bg.getWidth() / 10f;
if (focus) { if (focus) {
bgColor = Color.white; bgColor = Color.white;
textColor = Options.getSkin().getSongSelectActiveTextColor(); textColor = SkinService.skin.getSongSelectActiveTextColor();
} else } else
bgColor = Colors.BLUE_BUTTON; bgColor = Colors.BLUE_BUTTON;
} else if (beatmapSet.isPlayed()) } else if (beatmapSet.isPlayed())
@ -105,7 +107,7 @@ public class BeatmapSetNode {
} }
// draw text // draw text
if (Options.useUnicodeMetadata()) { // load glyphs if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode); Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode);
Fonts.loadGlyphs(Fonts.DEFAULT, beatmap.artistUnicode); Fonts.loadGlyphs(Fonts.DEFAULT, beatmap.artistUnicode);
} }

View File

@ -18,8 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import java.io.IOException; import java.io.IOException;
import java.nio.file.ClosedWatchServiceException; import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
@ -42,7 +40,11 @@ import java.util.concurrent.Executors;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import static yugecin.opsudance.options.Options.*;
/* /*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
@ -88,14 +90,14 @@ public class BeatmapWatchService {
* Creates a new watch service instance (overwriting any previous instance), * Creates a new watch service instance (overwriting any previous instance),
* registers the beatmap directory, and starts processing events. * registers the beatmap directory, and starts processing events.
*/ */
public static void create() { public static void create(InstanceContainer instanceContainer) {
// close the existing watch service // close the existing watch service
destroy(); destroy();
// create a new watch service // create a new watch service
try { try {
ws = new BeatmapWatchService(); ws = instanceContainer.provide(BeatmapWatchService.class);
ws.register(Options.getBeatmapDir().toPath()); ws.register(instanceContainer.provide(Configuration.class).beatmapDir.toPath());
} catch (IOException e) { } catch (IOException e) {
Log.error("Could not create watch service", e); Log.error("Could not create watch service", e);
EventBus.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED)); EventBus.post(new BubbleNotificationEvent("Could not create watch service", BubbleNotificationEvent.COMMONCOLOR_RED));
@ -126,9 +128,14 @@ public class BeatmapWatchService {
} }
/** /**
* Returns the single instance of this class. * Returns the single instance of this class, or null if not enabled.
*/ */
public static BeatmapWatchService get() { return ws; } public static BeatmapWatchService get() {
if (!OPTION_ENABLE_WATCH_SERVICE.state) {
return null;
}
return ws;
}
/** Watch service listener interface. */ /** Watch service listener interface. */
public interface BeatmapWatchServiceListener { public interface BeatmapWatchServiceListener {

View File

@ -18,8 +18,6 @@
package itdelatrisu.opsu.beatmap; package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.util.ArrayList; import java.util.ArrayList;
@ -29,20 +27,27 @@ import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.exception.ZipException;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Unpacker for OSZ (ZIP) archives. * Unpacker for OSZ (ZIP) archives.
*/ */
public class OszUnpacker { public class OszUnpacker {
@Inject
private Configuration config;
/** The index of the current file being unpacked. */ /** The index of the current file being unpacked. */
private static int fileIndex = -1; private int fileIndex = -1;
/** The total number of files to unpack. */ /** The total number of files to unpack. */
private static File[] files; private File[] files;
// This class should not be instantiated. @Inject
private OszUnpacker() {} public OszUnpacker() {
}
/** /**
* Invokes the unpacker for each OSZ archive in a root directory. * Invokes the unpacker for each OSZ archive in a root directory.
@ -51,11 +56,11 @@ public class OszUnpacker {
* @return an array containing the new (unpacked) directories, or null * @return an array containing the new (unpacked) directories, or null
* if no OSZs found * if no OSZs found
*/ */
public static File[] unpackAllFiles(File root, File dest) { public File[] unpackAll() {
List<File> dirs = new ArrayList<File>(); List<File> dirs = new ArrayList<File>();
// find all OSZ files // find all OSZ files
files = root.listFiles(new FilenameFilter() { files = config.oszDir.listFiles(new FilenameFilter() {
@Override @Override
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".osz"); return name.toLowerCase().endsWith(".osz");
@ -67,13 +72,13 @@ public class OszUnpacker {
} }
// unpack OSZs // unpack OSZs
BeatmapWatchService ws = (Options.isWatchServiceEnabled()) ? BeatmapWatchService.get() : null; BeatmapWatchService ws = BeatmapWatchService.get();
if (ws != null) if (ws != null)
ws.pause(); ws.pause();
for (File file : files) { for (File file : files) {
fileIndex++; fileIndex++;
String dirName = file.getName().substring(0, file.getName().lastIndexOf('.')); String dirName = file.getName().substring(0, file.getName().lastIndexOf('.'));
File songDir = new File(dest, dirName); File songDir = new File(config.beatmapDir, dirName);
if (!songDir.isDirectory()) { if (!songDir.isDirectory()) {
songDir.mkdir(); songDir.mkdir();
unzip(file, songDir); unzip(file, songDir);
@ -94,7 +99,7 @@ public class OszUnpacker {
* @param file the ZIP archive * @param file the ZIP archive
* @param dest the destination directory * @param dest the destination directory
*/ */
private static void unzip(File file, File dest) { private void unzip(File file, File dest) {
try { try {
ZipFile zipFile = new ZipFile(file); ZipFile zipFile = new ZipFile(file);
zipFile.extractAll(dest.getAbsolutePath()); zipFile.extractAll(dest.getAbsolutePath());
@ -108,7 +113,7 @@ public class OszUnpacker {
/** /**
* Returns the name of the current file being unpacked, or null if none. * Returns the name of the current file being unpacked, or null if none.
*/ */
public static String getCurrentFileName() { public String getCurrentFileName() {
if (files == null || fileIndex == -1) if (files == null || fileIndex == -1)
return null; return null;
@ -119,10 +124,11 @@ public class OszUnpacker {
* Returns the progress of file unpacking, or -1 if not unpacking. * Returns the progress of file unpacking, or -1 if not unpacking.
* @return the completion percent [0, 100] or -1 * @return the completion percent [0, 100] or -1
*/ */
public static int getUnpackerProgress() { public int getUnpackerProgress() {
if (files == null || fileIndex == -1) if (files == null || fileIndex == -1)
return -1; return -1;
return (fileIndex + 1) * 100 / files.length; return (fileIndex + 1) * 100 / files.length;
} }
} }

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -35,11 +34,13 @@ import java.util.Map;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
/** /**
* Handles connections and queries with the cached beatmap database. * Handles connections and queries with the cached beatmap database.
*/ */
public class BeatmapDB { public class BeatmapDB {
/** /**
* Current database version. * Current database version.
* This value should be changed whenever the database format changes. * This value should be changed whenever the database format changes.
@ -91,12 +92,16 @@ public class BeatmapDB {
// This class should not be instantiated. // This class should not be instantiated.
private BeatmapDB() {} private BeatmapDB() {}
private static Configuration config; // TODO
/** /**
* Initializes the database connection. * Initializes the database connection.
*/ */
public static void init() { public static void init(Configuration config) {
BeatmapDB.config = config;
// create a database connection // create a database connection
connection = DBController.createConnection(Options.BEATMAP_DB.getPath()); connection = DBController.createConnection(config.BEATMAP_DB.getPath());
if (connection == null) if (connection == null)
return; return;

View File

@ -19,6 +19,7 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
@ -34,7 +35,7 @@ public class DBController {
/** /**
* Initializes all databases. * Initializes all databases.
*/ */
public static void init() { public static void init(Configuration config) {
// load the sqlite-JDBC driver using the current class loader // load the sqlite-JDBC driver using the current class loader
try { try {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
@ -43,8 +44,8 @@ public class DBController {
} }
// initialize the databases // initialize the databases
BeatmapDB.init(); BeatmapDB.init(config);
ScoreDB.init(); ScoreDB.init(config);
} }
/** /**

View File

@ -18,10 +18,10 @@
package itdelatrisu.opsu.db; package itdelatrisu.opsu.db;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -83,9 +83,9 @@ public class ScoreDB {
/** /**
* Initializes the database connection. * Initializes the database connection.
*/ */
public static void init() { public static void init(Configuration config) {
// create a database connection // create a database connection
connection = DBController.createConnection(Options.SCORE_DB.getPath()); connection = DBController.createConnection(config.SCORE_DB.getPath());
if (connection == null) if (connection == null)
return; return;

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.downloads; package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.downloads.Download.DownloadListener; import itdelatrisu.opsu.downloads.Download.DownloadListener;
@ -35,13 +34,21 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import static yugecin.opsudance.options.Options.*;
/** /**
* Node containing song data and a Download object. * Node containing song data and a Download object.
*/ */
public class DownloadNode { public class DownloadNode {
@Inject
private Configuration config;
/** The associated Download object. */ /** The associated Download object. */
private Download download; private Download download;
@ -272,7 +279,7 @@ public class DownloadNode {
String url = server.getDownloadURL(beatmapSetID); String url = server.getDownloadURL(beatmapSetID);
if (url == null) if (url == null)
return; return;
String path = String.format("%s%c%d", Options.getOSZDir(), File.separatorChar, beatmapSetID); String path = String.format("%s%c%d", config.oszDir, File.separatorChar, beatmapSetID);
String rename = String.format("%d %s - %s.osz", beatmapSetID, artist, title); String rename = String.format("%d %s - %s.osz", beatmapSetID, artist, title);
Download download = new Download(url, path, rename); Download download = new Download(url, path, rename);
download.setListener(new DownloadListener() { download.setListener(new DownloadListener() {
@ -287,9 +294,10 @@ public class DownloadNode {
} }
}); });
this.download = download; this.download = download;
if (Options.useUnicodeMetadata()) // load glyphs if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.LARGE, getTitle()); Fonts.loadGlyphs(Fonts.LARGE, getTitle());
} }
}
/** /**
* Returns the associated download object, or null if none. * Returns the associated download object, or null if none.
@ -318,7 +326,7 @@ public class DownloadNode {
* If configured, the Unicode string will be returned instead. * If configured, the Unicode string will be returned instead.
*/ */
public String getTitle() { public String getTitle() {
return (Options.useUnicodeMetadata() && titleUnicode != null && !titleUnicode.isEmpty()) ? titleUnicode : title; return (OPTION_SHOW_UNICODE.state && titleUnicode != null && !titleUnicode.isEmpty()) ? titleUnicode : title;
} }
/** /**
@ -326,7 +334,7 @@ public class DownloadNode {
* If configured, the Unicode string will be returned instead. * If configured, the Unicode string will be returned instead.
*/ */
public String getArtist() { public String getArtist() {
return (Options.useUnicodeMetadata() && artistUnicode != null && !artistUnicode.isEmpty()) ? artistUnicode : artist; return (OPTION_SHOW_UNICODE.state && artistUnicode != null && !artistUnicode.isEmpty()) ? artistUnicode : artist;
} }
/** /**
@ -375,7 +383,7 @@ public class DownloadNode {
// text // text
// TODO: if the title/artist line is too long, shorten it (e.g. add "...") instead of just clipping // TODO: if the title/artist line is too long, shorten it (e.g. add "...") instead of just clipping
if (Options.useUnicodeMetadata()) { // load glyphs if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.BOLD, getTitle()); Fonts.loadGlyphs(Fonts.BOLD, getTitle());
Fonts.loadGlyphs(Fonts.BOLD, getArtist()); Fonts.loadGlyphs(Fonts.BOLD, getArtist());
} }

View File

@ -18,10 +18,8 @@
package itdelatrisu.opsu.downloads; package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.Download.DownloadListener; import itdelatrisu.opsu.downloads.Download.DownloadListener;
import itdelatrisu.opsu.ui.UI;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -39,23 +37,27 @@ import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Handles automatic program updates. * Handles automatic program updates.
*/ */
public class Updater { public class Updater {
/** The single instance of this class. */
private static Updater updater = new Updater(); @Inject
private Configuration config;
private static Updater updater;
public static Updater get() {
return updater;
}
/** The exit confirmation message. */ /** The exit confirmation message. */
public static final String EXIT_CONFIRMATION = "An opsu! update is being downloaded.\nAre you sure you want to quit opsu!?"; public static final String EXIT_CONFIRMATION = "An opsu! update is being downloaded.\nAre you sure you want to quit opsu!?";
/**
* Returns the single instance of this class.
*/
public static Updater get() { return updater; }
/** Updater status. */ /** Updater status. */
public enum Status { public enum Status {
INITIAL (""), INITIAL (""),
@ -117,11 +119,10 @@ public class Updater {
return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion(); return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion();
} }
/** @Inject
* Constructor. public Updater() {
*/
private Updater() {
status = Status.INITIAL; status = Status.INITIAL;
updater = this;
} }
/** /**
@ -144,7 +145,7 @@ public class Updater {
Date date = null; Date date = null;
try { try {
Properties props = new Properties(); Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE)); props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
String build = props.getProperty("build.date"); String build = props.getProperty("build.date");
if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}")) if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}"))
date = new Date(); date = new Date();
@ -206,23 +207,23 @@ public class Updater {
* @throws IOException if an I/O exception occurs * @throws IOException if an I/O exception occurs
*/ */
public void checkForUpdates() throws IOException { public void checkForUpdates() throws IOException {
if (status != Status.INITIAL || Options.USE_XDG) if (status != Status.INITIAL || config.USE_XDG)
return; return;
status = Status.CHECKING; status = Status.CHECKING;
// get current version // get current version
Properties props = new Properties(); Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE)); props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
if ((currentVersion = getVersion(props)) == null) if ((currentVersion = getVersion(props)) == null)
return; return;
// get latest version // get latest version
String s = null; String s = null;
try { try {
s = Utils.readDataFromUrl(new URL(Options.VERSION_REMOTE)); s = Utils.readDataFromUrl(new URL(config.VERSION_REMOTE));
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
Log.warn(String.format("Check for updates failed. Please check your internet connection, or your connection to %s.", Options.VERSION_REMOTE)); Log.warn(String.format("Check for updates failed. Please check your internet connection, or your connection to %s.", config.VERSION_REMOTE));
} }
if (s == null) { if (s == null) {
status = Status.CONNECTION_ERROR; status = Status.CONNECTION_ERROR;

View File

@ -34,11 +34,17 @@ import java.util.Date;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://bloodcat.com/osu/ * Download server: http://bloodcat.com/osu/
*/ */
public class BloodcatServer extends DownloadServer { public class BloodcatServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Bloodcat"; private static final String SERVER_NAME = "Bloodcat";
@ -54,8 +60,9 @@ public class BloodcatServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
/** Constructor. */ @Inject
public BloodcatServer() {} public BloodcatServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -82,12 +89,12 @@ public class BloodcatServer extends DownloadServer {
nodes = new DownloadNode[arr.length()]; nodes = new DownloadNode[arr.length()];
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {
JSONObject item = arr.getJSONObject(i); JSONObject item = arr.getJSONObject(i);
nodes[i] = new DownloadNode( nodes[i] = instanceContainer.injectFields(new DownloadNode(
item.getInt("id"), formatDate(item.getString("synced")), //"date" item.getInt("id"), formatDate(item.getString("synced")), //"date"
item.getString("title"), item.isNull("titleU") ? null : item.getString("titleU"), //"titleUnicode" item.getString("title"), item.isNull("titleU") ? null : item.getString("titleU"), //"titleUnicode"
item.getString("artist"), item.isNull("artistU") ? null : item.getString("artistU"), //"artistUnicode" item.getString("artist"), item.isNull("artistU") ? null : item.getString("artistU"), //"artistUnicode"
item.getString("creator") item.getString("creator")
); ));
} }
// store total result count // store total result count

View File

@ -30,6 +30,8 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: https://osu.hexide.com/ * Download server: https://osu.hexide.com/
@ -37,6 +39,10 @@ import yugecin.opsudance.core.errorhandling.ErrorHandler;
* <i>This server went offline in 2016.</i> * <i>This server went offline in 2016.</i>
*/ */
public class HexideServer extends DownloadServer { public class HexideServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Hexide"; private static final String SERVER_NAME = "Hexide";
@ -58,8 +64,9 @@ public class HexideServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
/** Constructor. */ @Inject
public HexideServer() {} public HexideServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -117,10 +124,10 @@ public class HexideServer extends DownloadServer {
artist = creator = "?"; artist = creator = "?";
} }
} }
nodes[i] = new DownloadNode( nodes[i] = instanceContainer.injectFields(new DownloadNode(
item.getInt("ranked_id"), item.getString("date"), item.getInt("ranked_id"), item.getString("date"),
title, null, artist, null, creator title, null, artist, null, creator
); ));
} }
// store total result count // store total result count

View File

@ -30,11 +30,17 @@ import java.net.URLEncoder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://osu.mengsky.net/ * Download server: http://osu.mengsky.net/
*/ */
public class MengSkyServer extends DownloadServer { public class MengSkyServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "MengSky"; private static final String SERVER_NAME = "MengSky";
@ -50,8 +56,9 @@ public class MengSkyServer extends DownloadServer {
/** Total result count from the last query. */ /** Total result count from the last query. */
private int totalResults = -1; private int totalResults = -1;
/** Constructor. */ @Inject
public MengSkyServer() {} public MengSkyServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -86,19 +93,18 @@ public class MengSkyServer extends DownloadServer {
// sometimes titleU is artistU instead of the proper title // sometimes titleU is artistU instead of the proper title
if (titleU.equals(artistU) && !titleU.equals(title)) if (titleU.equals(artistU) && !titleU.equals(title))
titleU = title; titleU = title;
nodes[i] = new DownloadNode( nodes[i] = instanceContainer.injectFields(new DownloadNode(
item.getInt("id"), item.getString("syncedDateTime"), item.getInt("id"), item.getString("syncedDateTime"),
title, titleU, artist, artistU, creator title, titleU, artist, artistU, creator
); ));
} }
// store total result count // store total result count
int pageTotal = json.getInt("pageTotal"); int pageTotal = json.getInt("pageTotal");
int resultCount = nodes.length; int resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
if (page == pageTotal) if (page == pageTotal) {
resultCount = nodes.length + (pageTotal - 1) * PAGE_LIMIT; resultCount += nodes.length - 1;
else }
resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
this.totalResults = resultCount; this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) { } catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show(); ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e).show();

View File

@ -21,6 +21,8 @@ package itdelatrisu.opsu.downloads.servers;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode; import itdelatrisu.opsu.downloads.DownloadNode;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -36,6 +38,10 @@ import java.util.regex.Pattern;
* Download server: http://osu.uu.gl/ * Download server: http://osu.uu.gl/
*/ */
public class MnetworkServer extends DownloadServer { public class MnetworkServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "Mnetwork"; private static final String SERVER_NAME = "Mnetwork";
@ -51,8 +57,9 @@ public class MnetworkServer extends DownloadServer {
/** Beatmap pattern. */ /** Beatmap pattern. */
private Pattern BEATMAP_PATTERN = Pattern.compile("^(\\d+) ([^-]+) - (.+)\\.osz$"); private Pattern BEATMAP_PATTERN = Pattern.compile("^(\\d+) ([^-]+) - (.+)\\.osz$");
/** Constructor. */ @Inject
public MnetworkServer() {} public MnetworkServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -112,7 +119,7 @@ public class MnetworkServer extends DownloadServer {
if (!m.matches()) if (!m.matches())
continue; continue;
nodeList.add(new DownloadNode(Integer.parseInt(m.group(1)), date, m.group(3), null, m.group(2), null, "")); nodeList.add(instanceContainer.injectFields(new DownloadNode(Integer.parseInt(m.group(1)), date, m.group(3), null, m.group(2), null, "")));
} }
nodes = nodeList.toArray(new DownloadNode[nodeList.size()]); nodes = nodeList.toArray(new DownloadNode[nodeList.size()]);

View File

@ -36,6 +36,8 @@ import java.util.TimeZone;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://loli.al/ * Download server: http://loli.al/
@ -43,6 +45,10 @@ import yugecin.opsudance.core.errorhandling.ErrorHandler;
* <i>This server went offline in August 2015.</i> * <i>This server went offline in August 2015.</i>
*/ */
public class OsuMirrorServer extends DownloadServer { public class OsuMirrorServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "osu!Mirror"; private static final String SERVER_NAME = "osu!Mirror";
@ -67,8 +73,9 @@ public class OsuMirrorServer extends DownloadServer {
/** Lookup table from beatmap set ID -> server download ID. */ /** Lookup table from beatmap set ID -> server download ID. */
private HashMap<Integer, Integer> idTable = new HashMap<Integer, Integer>(); private HashMap<Integer, Integer> idTable = new HashMap<Integer, Integer>();
/** Constructor. */ @Inject
public OsuMirrorServer() {} public OsuMirrorServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -106,12 +113,12 @@ public class OsuMirrorServer extends DownloadServer {
JSONObject item = arr.getJSONObject(i); JSONObject item = arr.getJSONObject(i);
int beatmapSetID = item.getInt("OSUSetid"); int beatmapSetID = item.getInt("OSUSetid");
int serverID = item.getInt("id"); int serverID = item.getInt("id");
nodes[i] = new DownloadNode( nodes[i] = instanceContainer.injectFields(new DownloadNode(
beatmapSetID, formatDate(item.getString("ModifyDate")), beatmapSetID, formatDate(item.getString("ModifyDate")),
item.getString("Title"), null, item.getString("Title"), null,
item.getString("Artist"), null, item.getString("Artist"), null,
item.getString("Mapper") item.getString("Mapper")
); ));
idTable.put(beatmapSetID, serverID); idTable.put(beatmapSetID, serverID);
if (serverID > maxServerID) if (serverID > maxServerID)
maxServerID = serverID; maxServerID = serverID;

View File

@ -34,11 +34,17 @@ import java.util.List;
import org.json.JSONObject; import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/** /**
* Download server: http://osu.yas-online.net/ * Download server: http://osu.yas-online.net/
*/ */
public class YaSOnlineServer extends DownloadServer { public class YaSOnlineServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */ /** Server name. */
private static final String SERVER_NAME = "YaS Online"; private static final String SERVER_NAME = "YaS Online";
@ -66,8 +72,9 @@ public class YaSOnlineServer extends DownloadServer {
/** Max server download ID seen (for approximating total pages). */ /** Max server download ID seen (for approximating total pages). */
private int maxServerID = 0; private int maxServerID = 0;
/** Constructor. */ @Inject
public YaSOnlineServer() {} public YaSOnlineServer() {
}
@Override @Override
public String getName() { return SERVER_NAME; } public String getName() { return SERVER_NAME; }
@ -176,7 +183,7 @@ public class YaSOnlineServer extends DownloadServer {
if (serverID > maxServerID) if (serverID > maxServerID)
maxServerID = serverID; maxServerID = serverID;
nodeList.add(new DownloadNode(item.getInt("mapid"), date, title, null, artist, null, "")); nodeList.add(instanceContainer.injectFields(new DownloadNode(item.getInt("mapid"), date, title, null, artist, null, "")));
} }
nodes = nodeList.toArray(new DownloadNode[nodeList.size()]); nodes = nodeList.toArray(new DownloadNode[nodeList.size()]);

View File

@ -20,9 +20,7 @@ package itdelatrisu.opsu.objects;
import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType; import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
@ -30,16 +28,20 @@ import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
/** /**
* Data type representing a circle object. * Data type representing a circle object.
*/ */
public class Circle extends GameObject { public class Circle extends GameObject {
/** The diameter of hit circles. */
public static float diameter; @Inject
private GameObjectRenderer gameObjectRenderer;
/** The associated HitObject. */ /** The associated HitObject. */
private HitObject hitObject; private HitObject hitObject;
@ -62,18 +64,6 @@ public class Circle extends GameObject {
private int comboColorIndex; private int comboColorIndex;
/**
* Initializes the Circle data type with map modifiers, images, and dimensions.
* @param circleDiameter the circle diameter
*/
public static void init(float circleDiameter) {
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
int diameterInt = (int) diameter;
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameterInt, diameterInt));
GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
}
/** /**
* Constructor. * Constructor.
* @param hitObject the associated HitObject * @param hitObject the associated HitObject
@ -129,16 +119,10 @@ public class Circle extends GameObject {
float oldAlpha = Colors.WHITE_FADE.a; float oldAlpha = Colors.WHITE_FADE.a;
Colors.WHITE_FADE.a = color.a = alpha; Colors.WHITE_FADE.a = color.a = alpha;
if (timeDiff >= 0 && !GameMod.HIDDEN.isActive() && Options.isDrawApproach()) if (timeDiff >= 0) {
GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(x, y, color); gameObjectRenderer.renderApproachCircle(x, y, color, approachScale);
GameImage.HITCIRCLE.getImage().drawCentered(x, y, color); }
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber(); gameObjectRenderer.renderHitCircle(x, y, color, hitObject.getComboNumber(), alpha);
if (!overlayAboveNumber)
GameImage.HITCIRCLE_OVERLAY.getImage().drawCentered(x, y, Colors.WHITE_FADE);
data.drawSymbolNumber(hitObject.getComboNumber(), x, y,
GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / data.getDefaultSymbolImage(0).getHeight(), alpha);
if (overlayAboveNumber)
GameImage.HITCIRCLE_OVERLAY.getImage().drawCentered(x, y, Colors.WHITE_FADE);
Colors.WHITE_FADE.a = oldAlpha; Colors.WHITE_FADE.a = oldAlpha;
@ -172,7 +156,7 @@ public class Circle extends GameObject {
@Override @Override
public boolean mousePressed(int x, int y, int trackPosition) { public boolean mousePressed(int x, int y, int trackPosition) {
double distance = Math.hypot(this.x - x, this.y - y); double distance = Math.hypot(this.x - x, this.y - y);
if (distance < diameter / 2) { if (distance < gameObjectRenderer.getCircleDiameter() / 2) {
int timeDiff = trackPosition - hitObject.getTime(); int timeDiff = trackPosition - hitObject.getTime();
int result = hitResult(timeDiff); int result = hitResult(timeDiff);
@ -195,7 +179,7 @@ public class Circle extends GameObject {
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) { if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
if (isAutoMod) {// "auto" mod: catch any missed notes due to lag if (isAutoMod) {// "auto" mod: catch any missed notes due to lag
data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false); data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
if (Options.isMirror() && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
float[] m = Utils.mirrorPoint(x, y); float[] m = Utils.mirrorPoint(x, y);
data.sendHitResult(time, GameData.HIT_300, m[0], m[1], mirrorColor, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false, false); data.sendHitResult(time, GameData.HIT_300, m[0], m[1], mirrorColor, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false, false);
} }
@ -210,7 +194,7 @@ public class Circle extends GameObject {
else if (isAutoMod) { else if (isAutoMod) {
if (Math.abs(trackPosition - time) < hitResultOffset[GameData.HIT_300]) { if (Math.abs(trackPosition - time) < hitResultOffset[GameData.HIT_300]) {
data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false); data.sendHitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false);
if (Options.isMirror() && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
float[] m = Utils.mirrorPoint(x, y); float[] m = Utils.mirrorPoint(x, y);
data.sendHitResult(time, GameData.HIT_300, m[0], m[1], mirrorColor, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false, false); data.sendHitResult(time, GameData.HIT_300, m[0], m[1], mirrorColor, comboEnd, hitObject, HitObjectType.CIRCLE, true, 0, null, false, false);
} }

View File

@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType; import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
@ -37,11 +36,23 @@ import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Data type representing a slider object. * Data type representing a slider object.
*/ */
public class Slider extends GameObject { public class Slider extends GameObject {
@Inject
private DisplayContainer displayContainer;
@Inject
private GameObjectRenderer gameObjectRenderer;
/** Slider ball frames. */ /** Slider ball frames. */
private static Image[] sliderBallImages; private static Image[] sliderBallImages;
@ -54,9 +65,6 @@ public class Slider extends GameObject {
/** Follow circle radius. */ /** Follow circle radius. */
private static float followRadius; private static float followRadius;
/** The diameter of hit circles. */
private static float diameter;
/** The associated HitObject. */ /** The associated HitObject. */
private HitObject hitObject; private HitObject hitObject;
@ -116,9 +124,6 @@ public class Slider extends GameObject {
private static final int FOLLOW_EXPAND_TIME = 150; private static final int FOLLOW_EXPAND_TIME = 150;
private static final int FOLLOW_SHRINK_TIME = 100; private static final int FOLLOW_SHRINK_TIME = 100;
/** Container dimensions. */
private static int containerWidth, containerHeight;
private int repeats; private int repeats;
private static Color curveColor = new Color(0, 0, 0, 20); private static Color curveColor = new Color(0, 0, 0, 20);
@ -134,14 +139,9 @@ public class Slider extends GameObject {
* @param circleDiameter the circle diameter * @param circleDiameter the circle diameter
* @param beatmap the associated beatmap * @param beatmap the associated beatmap
*/ */
public static void init(DisplayContainer displayContainer, float circleDiameter, Beatmap beatmap) { public static void init(float circleDiameter, Beatmap beatmap) {
containerWidth = displayContainer.width; followRadius = circleDiameter / 2 * 3f;
containerHeight = displayContainer.height; int diameterInt = (int) circleDiameter;
diameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
int diameterInt = (int) diameter;
followRadius = diameter / 2 * 3f;
// slider ball // slider ball
if (GameImage.SLIDER_BALL.hasBeatmapSkinImages() || if (GameImage.SLIDER_BALL.hasBeatmapSkinImages() ||
@ -216,11 +216,9 @@ public class Slider extends GameObject {
double fadeinScale = (timeDiff - approachTime + fadeInTime) / (double) fadeInTime; double fadeinScale = (timeDiff - approachTime + fadeInTime) / (double) fadeInTime;
float alpha = Utils.clamp(1 - (float) fadeinScale, 0, 1); float alpha = Utils.clamp(1 - (float) fadeinScale, 0, 1);
float decorationsAlpha = Utils.clamp(-2.0f * (float) fadeinScale, 0, 1); float decorationsAlpha = Utils.clamp(-2.0f * (float) fadeinScale, 0, 1);
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber(); boolean overlayAboveNumber = SkinService.skin.isHitCircleOverlayAboveNumber();
float oldAlpha = Colors.WHITE_FADE.a; float oldAlpha = Colors.WHITE_FADE.a;
Colors.WHITE_FADE.a = color.a = alpha; Colors.WHITE_FADE.a = color.a = alpha;
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
Image hitCircle = GameImage.HITCIRCLE.getImage();
Vec2f endPos = curve.pointAt(1); Vec2f endPos = curve.pointAt(1);
float oldWhiteFadeAlpha = Colors.WHITE_FADE.a; float oldWhiteFadeAlpha = Colors.WHITE_FADE.a;
@ -236,11 +234,11 @@ public class Slider extends GameObject {
color.a = alpha; color.a = alpha;
// end circle (only draw if ball still has to go there) // end circle (only draw if ball still has to go there)
if (Options.isDrawSliderEndCircles() && isCurveCompletelyDrawn && currentRepeats < repeatCount - (repeatCount % 2 == 0 ? 1 : 0)) { if (OPTION_DRAW_SLIDER_ENDCIRCLES.state && isCurveCompletelyDrawn && currentRepeats < repeatCount - (repeatCount % 2 == 0 ? 1 : 0)) {
Color circleColor = new Color(color); Color circleColor = new Color(color);
Color overlayColor = new Color(Colors.WHITE_FADE); Color overlayColor = new Color(Colors.WHITE_FADE);
if (currentRepeats == 0) { if (currentRepeats == 0) {
if (Options.isSliderSnaking()) { if (OPTION_SNAKING_SLIDERS.state) {
// fade in end circle using decorationsAlpha when snaking sliders are enabled // fade in end circle using decorationsAlpha when snaking sliders are enabled
circleColor.a = overlayColor.a = sliderAlpha * decorationsAlpha; circleColor.a = overlayColor.a = sliderAlpha * decorationsAlpha;
} }
@ -249,8 +247,8 @@ public class Slider extends GameObject {
circleColor.a = overlayColor.a = sliderAlpha * getCircleAlphaAfterRepeat(trackPosition, true); circleColor.a = overlayColor.a = sliderAlpha * getCircleAlphaAfterRepeat(trackPosition, true);
} }
Vec2f endCircPos = curve.pointAt(1f); Vec2f endCircPos = curve.pointAt(1f);
hitCircle.drawCentered(endCircPos.x, endCircPos.y, circleColor); gameObjectRenderer.renderHitCircleOnly(endCircPos.x, endCircPos.y, circleColor);
hitCircleOverlay.drawCentered(endCircPos.x, endCircPos.y, overlayColor); gameObjectRenderer.renderHitCircleOverlayOnly(endCircPos.x, endCircPos.y, overlayColor);
} }
g.pushTransform(); g.pushTransform();
@ -267,10 +265,11 @@ public class Slider extends GameObject {
} }
// start circle, only draw if ball still has to go there // start circle, only draw if ball still has to go there
if (!sliderClickedInitial || (Options.isDrawSliderEndCircles() && currentRepeats < repeatCount - (repeatCount % 2 == 1 ? 1 : 0))) { if (!sliderClickedInitial || (OPTION_DRAW_SLIDER_ENDCIRCLES.state && currentRepeats < repeatCount - (repeatCount % 2 == 1 ? 1 : 0))) {
hitCircle.drawCentered(x, y, firstCircleColor); gameObjectRenderer.renderHitCircleOnly(x, y, firstCircleColor);
if (!overlayAboveNumber || sliderClickedInitial) if (!overlayAboveNumber || sliderClickedInitial) {
hitCircleOverlay.drawCentered(x, y, startCircleOverlayColor); gameObjectRenderer.renderHitCircleOverlayOnly(x, y, startCircleOverlayColor);
}
} }
g.popTransform(); g.popTransform();
@ -297,12 +296,11 @@ public class Slider extends GameObject {
// draw combo number and overlay if not initially clicked // draw combo number and overlay if not initially clicked
if (!sliderClickedInitial) { if (!sliderClickedInitial) {
data.drawSymbolNumber(hitObject.getComboNumber(), x, y, gameObjectRenderer.renderComboNumberOnly(x, y, hitObject.getComboNumber(), alpha);
hitCircle.getWidth() * 0.40f / data.getDefaultSymbolImage(0).getHeight(), alpha);
if (overlayAboveNumber) { if (overlayAboveNumber) {
startCircleOverlayColor.a = sliderAlpha; startCircleOverlayColor.a = sliderAlpha;
hitCircleOverlay.drawCentered(x, y, startCircleOverlayColor); gameObjectRenderer.renderHitCircleOverlayOnly(x, y, startCircleOverlayColor);
} }
} }
@ -314,7 +312,7 @@ public class Slider extends GameObject {
Image arrow = GameImage.REVERSEARROW.getImage(); Image arrow = GameImage.REVERSEARROW.getImage();
arrow = arrow.getScaledCopy((float) (1 + 0.2d * ((trackPosition + sliderTime * tcurRepeat) % 292) / 292)); arrow = arrow.getScaledCopy((float) (1 + 0.2d * ((trackPosition + sliderTime * tcurRepeat) % 292) / 292));
if (tcurRepeat == 0) { if (tcurRepeat == 0) {
arrow.setAlpha(Options.isSliderSnaking() ? decorationsAlpha : 1f); arrow.setAlpha(OPTION_SNAKING_SLIDERS.state ? decorationsAlpha : 1f);
} else { } else {
if (!sliderClickedInitial) { if (!sliderClickedInitial) {
continue; continue;
@ -339,8 +337,8 @@ public class Slider extends GameObject {
if (mirror) { if (mirror) {
g.rotate(x, y, -180f); g.rotate(x, y, -180f);
} }
if (!GameMod.HIDDEN.isActive() && Options.isDrawApproach()) { if (!GameMod.HIDDEN.isActive() && OPTION_DANCE_DRAW_APPROACH.state) {
GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(x, y, color); gameObjectRenderer.renderApproachCircle(x, y, color, approachScale);
} }
g.popTransform(); g.popTransform();
} else { } else {
@ -362,7 +360,7 @@ public class Slider extends GameObject {
Image sliderBallFrame = sliderBallImages[(int) (t * sliderTime * 60 / 1000) % sliderBallImages.length]; Image sliderBallFrame = sliderBallImages[(int) (t * sliderTime * 60 / 1000) % sliderBallImages.length];
float angle = (float) (Math.atan2(c2.y - c.y, c2.x - c.x) * 180 / Math.PI); float angle = (float) (Math.atan2(c2.y - c.y, c2.x - c.x) * 180 / Math.PI);
sliderBallFrame.setRotation(angle); sliderBallFrame.setRotation(angle);
if (Options.getSkin().isAllowSliderBallTint()) { if (SkinService.skin.isAllowSliderBallTint()) {
sliderBallFrame.drawCentered(c.x, c.y, color); sliderBallFrame.drawCentered(c.x, c.y, color);
} else { } else {
sliderBallFrame.drawCentered(c.x, c.y); sliderBallFrame.drawCentered(c.x, c.y);
@ -373,13 +371,13 @@ public class Slider extends GameObject {
float followCircleScale = 1f + (tickExpandTime / (float) TICK_EXPAND_TIME) * 0.1f; float followCircleScale = 1f + (tickExpandTime / (float) TICK_EXPAND_TIME) * 0.1f;
float followAlpha = 1f; float followAlpha = 1f;
if (followCircleActive && followExpandTime < FOLLOW_EXPAND_TIME) { if (followCircleActive && followExpandTime < FOLLOW_EXPAND_TIME) {
followExpandTime += DisplayContainer.instance.renderDelta; followExpandTime += displayContainer.renderDelta;
followCircleScale *= 0.5f; followCircleScale *= 0.5f;
float progress = AnimationEquation.OUT_QUAD.calc((float) followExpandTime / FOLLOW_EXPAND_TIME); float progress = AnimationEquation.OUT_QUAD.calc((float) followExpandTime / FOLLOW_EXPAND_TIME);
followCircleScale = followCircleScale + followCircleScale * progress; followCircleScale = followCircleScale + followCircleScale * progress;
followAlpha = progress; followAlpha = progress;
} else if (!followCircleActive) { } else if (!followCircleActive) {
followExpandTime -= DisplayContainer.instance.renderDelta; followExpandTime -= displayContainer.renderDelta;
if (followExpandTime > FOLLOW_SHRINK_TIME) { if (followExpandTime > FOLLOW_SHRINK_TIME) {
followExpandTime = FOLLOW_SHRINK_TIME; followExpandTime = FOLLOW_SHRINK_TIME;
} }
@ -394,7 +392,7 @@ public class Slider extends GameObject {
float oldAlphaBlack = Colors.BLACK_ALPHA.a; float oldAlphaBlack = Colors.BLACK_ALPHA.a;
Colors.BLACK_ALPHA.a = 0.75f; Colors.BLACK_ALPHA.a = 0.75f;
g.setColor(Colors.BLACK_ALPHA); g.setColor(Colors.BLACK_ALPHA);
g.fillRect(0, 0, containerWidth, containerHeight); g.fillRect(0, 0, displayContainer.width, displayContainer.height);
Colors.BLACK_ALPHA.a = oldAlphaBlack; Colors.BLACK_ALPHA.a = oldAlphaBlack;
} }
} }
@ -459,27 +457,28 @@ public class Slider extends GameObject {
} }
private boolean drawSliderTrack(int trackPosition, double snakingSliderProgress) { private boolean drawSliderTrack(int trackPosition, double snakingSliderProgress) {
double curveIntervalTo = Options.isSliderSnaking() ? snakingSliderProgress : 1d; double curveIntervalTo = OPTION_SNAKING_SLIDERS.state ? snakingSliderProgress : 1d;
double curveIntervalFrom = 0d; double curveIntervalFrom = 0d;
if (Options.isShrinkingSliders()) { if (OPTION_SHRINKING_SLIDERS.state) {
double sliderprogress = (trackPosition - getTime() - ((double) sliderTime * (repeats - 1))) / (double) sliderTime; double sliderprogress = (trackPosition - getTime() - ((double) sliderTime * (repeats - 1))) / (double) sliderTime;
if (sliderprogress > 0) { if (sliderprogress > 0) {
curveIntervalFrom = sliderprogress; curveIntervalFrom = sliderprogress;
} }
} }
int curvelen = curve.getCurvePoints().length; int curvelen = curve.getCurvePoints().length;
if (Options.isMergingSliders()) { if (!OPTION_FALLBACK_SLIDERS.state && OPTION_MERGING_SLIDERS.state) {
if (Options.isShrinkingSliders() && curveIntervalFrom > 0) { if (OPTION_SHRINKING_SLIDERS.state && curveIntervalFrom > 0) {
if (repeats % 2 == 0) { if (hitObject.getRepeatCount() % 2 == 0) {
game.spliceSliderCurve(baseSliderFrom + (int) ((1d - curveIntervalFrom) * curvelen) - 1, baseSliderFrom + curvelen); game.addMergedSliderPointsToRender(baseSliderFrom, baseSliderFrom + (int) ((1d - curveIntervalFrom) * curvelen));
} else { } else {
game.setSlidercurveFrom(baseSliderFrom + (int) (curveIntervalFrom * curvelen) + 1); game.addMergedSliderPointsToRender(baseSliderFrom + (int) (curveIntervalFrom * curvelen) + 1, baseSliderFrom + (int) (curveIntervalTo * curve.getCurvePoints().length));
} }
}
game.setSlidercurveTo(baseSliderFrom + (int) (curveIntervalTo * curve.getCurvePoints().length));
} else { } else {
if (Options.isShrinkingSliders() && curveIntervalFrom > 0 && repeats % 2 == 0) { game.addMergedSliderPointsToRender(baseSliderFrom, baseSliderFrom + (int) (curveIntervalTo * curve.getCurvePoints().length));
if (Options.isFallbackSliders()) { }
} else {
if (OPTION_SHRINKING_SLIDERS.state && curveIntervalFrom > 0 && repeats % 2 == 0) {
if (OPTION_FALLBACK_SLIDERS.state) {
curveIntervalTo = 1d - curveIntervalFrom; curveIntervalTo = 1d - curveIntervalFrom;
} else { } else {
curve.splice((int) ((1d - curveIntervalFrom) * curvelen), curvelen); curve.splice((int) ((1d - curveIntervalFrom) * curvelen), curvelen);
@ -590,7 +589,7 @@ public class Slider extends GameObject {
data.sendHitResult(hitObject.getTime() + (int) sliderTimeTotal, result, data.sendHitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
cx, cy, color, comboEnd, hitObject, type, sliderHeldToEnd, cx, cy, color, comboEnd, hitObject, type, sliderHeldToEnd,
currentRepeats + 1, curve, sliderHeldToEnd); currentRepeats + 1, curve, sliderHeldToEnd);
if (Options.isMirror() && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
float[] m = Utils.mirrorPoint(cx, cy); float[] m = Utils.mirrorPoint(cx, cy);
data.sendHitResult(hitObject.getTime() + (int) sliderTimeTotal, result, data.sendHitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
m[0], m[1], mirrorColor, comboEnd, hitObject, type, sliderHeldToEnd, m[0], m[1], mirrorColor, comboEnd, hitObject, type, sliderHeldToEnd,
@ -606,7 +605,7 @@ public class Slider extends GameObject {
return false; return false;
double distance = Math.hypot(this.x - x, this.y - y); double distance = Math.hypot(this.x - x, this.y - y);
if (distance < diameter / 2) { if (distance < gameObjectRenderer.getCircleDiameter() / 2) {
int timeDiff = Math.abs(trackPosition - hitObject.getTime()); int timeDiff = Math.abs(trackPosition - hitObject.getTime());
int[] hitResultOffset = game.getHitResultOffsets(); int[] hitResultOffset = game.getHitResultOffsets();
@ -691,9 +690,6 @@ public class Slider extends GameObject {
// calculate and send slider result // calculate and send slider result
hitResult(); hitResult();
if (Options.isMergingSliders()) {
game.setSlidercurveFrom(baseSliderFrom + curve.getCurvePoints().length + 1);
}
return true; return true;
} }

View File

@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType; import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
@ -32,10 +31,10 @@ import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.skinning.SkinService;
/** /**
* Data type representing a spinner object. * Data type representing a spinner object.
@ -189,7 +188,7 @@ public class Spinner extends GameObject {
float alpha = Utils.clamp(1 - (float) timeDiff / fadeInTime, 0f, 1f); float alpha = Utils.clamp(1 - (float) timeDiff / fadeInTime, 0f, 1f);
// darken screen // darken screen
if (Options.getSkin().isSpinnerFadePlayfield()) { if (SkinService.skin.isSpinnerFadePlayfield()) {
float oldAlpha = Colors.BLACK_ALPHA.a; float oldAlpha = Colors.BLACK_ALPHA.a;
if (timeDiff > 0) if (timeDiff > 0)
Colors.BLACK_ALPHA.a *= alpha; Colors.BLACK_ALPHA.a *= alpha;

View File

@ -19,8 +19,6 @@
package itdelatrisu.opsu.objects.curves; package itdelatrisu.opsu.objects.curves;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.skins.Skin; import itdelatrisu.opsu.skins.Skin;
@ -31,7 +29,9 @@ import org.lwjgl.opengl.GLContext;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Representation of a curve. * Representation of a curve.
@ -43,7 +43,7 @@ public abstract class Curve {
protected static float CURVE_POINTS_SEPERATION = 2.5f; protected static float CURVE_POINTS_SEPERATION = 2.5f;
/** The curve border color. */ /** The curve border color. */
private static Color borderColor; protected static Color borderColor;
/** Whether mmsliders are supported. */ /** Whether mmsliders are supported. */
private static boolean mmsliderSupported = false; private static boolean mmsliderSupported = false;
@ -58,7 +58,7 @@ public abstract class Curve {
protected float[] sliderX, sliderY; protected float[] sliderX, sliderY;
/** Per-curve render-state used for the new style curve renders. */ /** Per-curve render-state used for the new style curve renders. */
private CurveRenderState renderState; protected CurveRenderState renderState;
/** Points along the curve (set by inherited classes). */ /** Points along the curve (set by inherited classes). */
public Vec2f[] curve; public Vec2f[] curve;
@ -103,10 +103,9 @@ public abstract class Curve {
ContextCapabilities capabilities = GLContext.getCapabilities(); ContextCapabilities capabilities = GLContext.getCapabilities();
mmsliderSupported = capabilities.OpenGL30; mmsliderSupported = capabilities.OpenGL30;
if (mmsliderSupported) if (mmsliderSupported) {
CurveRenderState.init(width, height, circleDiameter); CurveRenderState.init(width, height, circleDiameter);
else { } else if (SkinService.skin.getSliderStyle() != Skin.STYLE_PEPPYSLIDER) {
if (Options.getSkin().getSliderStyle() != Skin.STYLE_PEPPYSLIDER)
Log.warn("New slider style requires OpenGL 3.0."); Log.warn("New slider style requires OpenGL 3.0.");
} }
} }
@ -134,8 +133,8 @@ public abstract class Curve {
if (curve == null) if (curve == null)
return; return;
if (OPTION_FALLBACK_SLIDERS.state || SkinService.skin.getSliderStyle() == Skin.STYLE_PEPPYSLIDER || !mmsliderSupported) {
// peppysliders // peppysliders
if (Options.isFallbackSliders() || Options.getSkin().getSliderStyle() == Skin.STYLE_PEPPYSLIDER || !mmsliderSupported) {
Image hitCircle = GameImage.HITCIRCLE.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage();
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
for (int i = from; i < to; i++) for (int i = from; i < to; i++)
@ -145,19 +144,17 @@ public abstract class Curve {
for (int i = from; i < to; i++) for (int i = from; i < to; i++)
hitCircle.drawCentered(curve[i].x, curve[i].y, fallbackSliderColor); hitCircle.drawCentered(curve[i].x, curve[i].y, fallbackSliderColor);
fallbackSliderColor.a = a; fallbackSliderColor.a = a;
} } else {
// mmsliders // mmsliders
else {
if (renderState == null) if (renderState == null)
renderState = new CurveRenderState(hitObject, curve, this instanceof FakeCombinedCurve); renderState = new CurveRenderState(hitObject, curve, false);
renderState.draw(color, borderColor, from, to); renderState.draw(color, borderColor, from, to);
} }
} }
public void splice(int from, int to) { public void splice(int from, int to) {
if (renderState == null) if (renderState == null)
renderState = new CurveRenderState(hitObject, curve, this instanceof FakeCombinedCurve); renderState = new CurveRenderState(hitObject, curve, false);
renderState.splice(from, to); renderState.splice(from, to);
} }

View File

@ -18,15 +18,15 @@
package itdelatrisu.opsu.render; package itdelatrisu.opsu.render;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.List;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.EXTFramebufferObject; import org.lwjgl.opengl.EXTFramebufferObject;
@ -37,6 +37,9 @@ import org.lwjgl.opengl.GL20;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.render.GameObjectRenderer;
import static yugecin.opsudance.options.Options.*;
/** /**
* Hold the temporary render state that needs to be restored again after the new * Hold the temporary render state that needs to be restored again after the new
@ -69,6 +72,8 @@ public class CurveRenderState {
private int spliceFrom; private int spliceFrom;
private int spliceTo; private int spliceTo;
protected List<Integer> pointsToRender;
private final int mirrors; private final int mirrors;
/** /**
@ -96,7 +101,7 @@ public class CurveRenderState {
*/ */
public static void shutdown() { public static void shutdown() {
staticState.shutdown(); staticState.shutdown();
//FrameBufferCache.shutdown(); FrameBufferCache.shutdown();
} }
/** /**
@ -108,7 +113,7 @@ public class CurveRenderState {
this.hitObject = hitObject; this.hitObject = hitObject;
this.curve = curve; this.curve = curve;
if (isKnorkeSlider) { if (isKnorkeSlider) {
this.mirrors = Options.getMergingSlidersMirrorPool(); this.mirrors = OPTION_MERGING_SLIDERS_MIRROR_POOL.val;
} else { } else {
this.mirrors = 1; this.mirrors = 1;
} }
@ -138,6 +143,14 @@ public class CurveRenderState {
lastPointDrawn = -1; // force redraw lastPointDrawn = -1; // force redraw
} }
public void draw(Color color, Color borderColor, List<Integer> pointsToRender) {
lastPointDrawn = -1;
firstPointDrawn = -1;
this.pointsToRender = pointsToRender;
draw(color, borderColor, 0, curve.length);
this.pointsToRender = null;
}
/** /**
* Draw a curve to the screen that's tinted with `color`. The first time * Draw a curve to the screen that's tinted with `color`. The first time
* this is called this caches the image result of the curve and on subsequent * this is called this caches the image result of the curve and on subsequent
@ -303,7 +316,7 @@ public class CurveRenderState {
double diff_x = x - last_x; double diff_x = x - last_x;
double diff_y = y - last_y; double diff_y = y - last_y;
float dist = Utils.distance(x, y, last_x, last_y); float dist = Utils.distance(x, y, last_x, last_y);
if (dist < Circle.diameter / 8) { if (dist < GameObjectRenderer.instance.getCircleDiameter() / 8) {
x = (float) (x - diff_x / 2); x = (float) (x - diff_x / 2);
y = (float) (y - diff_y / 2); y = (float) (y - diff_y / 2);
} else { } else {
@ -344,11 +357,15 @@ public class CurveRenderState {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
} }
int max = mirrors; int max = mirrors;
if (!Options.isMirror()) { if (!OPTION_DANCE_MIRROR.state) {
max = 1; max = 1;
} }
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
if (pointsToRender == null) {
renderCurve(from, to, i); renderCurve(from, to, i);
} else {
renderCurve(i);
}
} }
GL11.glFlush(); GL11.glFlush();
GL20.glDisableVertexAttribArray(staticState.texCoordLoc); GL20.glDisableVertexAttribArray(staticState.texCoordLoc);
@ -370,6 +387,16 @@ public class CurveRenderState {
} }
} }
private void renderCurve(int mirror) {
Iterator<Integer> iter = pointsToRender.iterator();
while (iter.hasNext()) {
for (int i = iter.next() * 2, end = iter.next() * 2 - 1; i < end; ++i) {
final int index = i + curve.length * 2 * mirror;
GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, index * (NewCurveStyleState.DIVIDES + 2), NewCurveStyleState.DIVIDES + 2);
}
}
}
/** /**
* Fill {@code buff} with the texture coordinates and positions for a cone * Fill {@code buff} with the texture coordinates and positions for a cone
* that has its center at the coordinates {@code (x1,y1)}. * that has its center at the coordinates {@code (x1,y1)}.

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.replay; package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
@ -46,7 +45,9 @@ import org.newdawn.slick.util.Log;
import lzma.streams.LzmaOutputStream; import lzma.streams.LzmaOutputStream;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Captures osu! replay data. * Captures osu! replay data.
@ -55,6 +56,10 @@ import yugecin.opsudance.events.BubbleNotificationEvent;
* @author smoogipooo (https://github.com/smoogipooo/osu-Replay-API/) * @author smoogipooo (https://github.com/smoogipooo/osu-Replay-API/)
*/ */
public class Replay { public class Replay {
@Inject
public Configuration config;
/** The associated file. */ /** The associated file. */
private File file; private File file;
@ -272,16 +277,13 @@ public class Replay {
*/ */
public void save() { public void save() {
// create replay directory // create replay directory
File dir = Options.getReplayDir(); if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
if (!dir.isDirectory()) {
if (!dir.mkdir()) {
EventBus.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED)); EventBus.post(new BubbleNotificationEvent("Failed to create replay directory.", BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
}
// write file in new thread // write file in new thread
final File file = new File(dir, String.format("%s.osr", getReplayFilename())); final File file = new File(config.replayDir, String.format("%s.osr", getReplayFilename()));
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {

View File

@ -18,7 +18,6 @@
package itdelatrisu.opsu.replay; package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.db.ScoreDB; import itdelatrisu.opsu.db.ScoreDB;
@ -31,32 +30,42 @@ import java.nio.file.StandardCopyOption;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus; import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Importer for replay files. * Importer for replay files.
*/ */
public class ReplayImporter { public class ReplayImporter {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Configuration config;
/** The subdirectory (within the replay import directory) to move replays that could not be imported. */ /** The subdirectory (within the replay import directory) to move replays that could not be imported. */
private static final String FAILED_IMPORT_DIR = "failed"; private final String FAILED_IMPORT_DIR = "failed";
/** The index of the current file being imported. */ /** The index of the current file being imported. */
private static int fileIndex = -1; private int fileIndex = -1;
/** The total number of replays to import. */ /** The total number of replays to import. */
private static File[] files; private File[] files;
// This class should not be instantiated. @Inject
private ReplayImporter() {} public ReplayImporter() {
}
/** /**
* Invokes the importer for each OSR file in a directory, adding the replay * Invokes the importer for each OSR file in the replay import dir, adding the replay
* to the score database and moving the file into the replay directory. * to the score database and moving the file into the replay directory.
* @param dir the directory
*/ */
public static void importAllReplaysFromDir(File dir) { public void importAll() {
// find all OSR files // find all OSR files
files = dir.listFiles(new FilenameFilter() { files = config.replayImportDir.listFiles(new FilenameFilter() {
@Override @Override
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".osr"); return name.toLowerCase().endsWith(".osr");
@ -68,20 +77,17 @@ public class ReplayImporter {
} }
// get replay directory // get replay directory
File replayDir = Options.getReplayDir(); if (!config.replayDir.isDirectory() && !config.replayDir.mkdir()) {
if (!replayDir.isDirectory()) { String err = String.format("Failed to create replay directory '%s'.", config.replayDir.getAbsolutePath());
if (!replayDir.mkdir()) {
String err = String.format("Failed to create replay directory '%s'.", replayDir.getAbsolutePath());
Log.error(err); Log.error(err);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED)); EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
return; return;
} }
}
// import OSRs // import OSRs
for (File file : files) { for (File file : files) {
fileIndex++; fileIndex++;
Replay r = new Replay(file); Replay r = instanceContainer.injectFields(new Replay(file));
try { try {
r.loadHeader(); r.loadHeader();
} catch (IOException e) { } catch (IOException e) {
@ -97,11 +103,11 @@ public class ReplayImporter {
ScoreDB.addScore(r.getScoreData(beatmap)); ScoreDB.addScore(r.getScoreData(beatmap));
// move to replay directory // move to replay directory
File moveToFile = new File(replayDir, String.format("%s.osr", r.getReplayFilename())); File moveToFile = new File(config.replayDir, String.format("%s.osr", r.getReplayFilename()));
try { try {
Files.move(file.toPath(), moveToFile.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.move(file.toPath(), moveToFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) { } catch (IOException e) {
Log.warn(String.format("Failed to move replay '%s' to the replay directory '%s'.", file, replayDir), e); Log.warn(String.format("Failed to move replay '%s' to the replay directory '%s'.", file, config.replayDir), e);
} }
} else { } else {
moveToFailedDirectory(file); moveToFailedDirectory(file);
@ -119,8 +125,8 @@ public class ReplayImporter {
* Moves a replay file into the failed import directory. * Moves a replay file into the failed import directory.
* @param file the file to move * @param file the file to move
*/ */
private static void moveToFailedDirectory(File file) { private void moveToFailedDirectory(File file) {
File dir = new File(Options.getReplayImportDir(), FAILED_IMPORT_DIR); File dir = new File(config.replayImportDir, FAILED_IMPORT_DIR);
dir.mkdir(); dir.mkdir();
File moveToFile = new File(dir, file.getName()); File moveToFile = new File(dir, file.getName());
try { try {
@ -133,7 +139,7 @@ public class ReplayImporter {
/** /**
* Returns the name of the current file being imported, or null if none. * Returns the name of the current file being imported, or null if none.
*/ */
public static String getCurrentFileName() { public String getCurrentFileName() {
if (files == null || fileIndex == -1) if (files == null || fileIndex == -1)
return null; return null;
@ -144,10 +150,11 @@ public class ReplayImporter {
* Returns the progress of replay importing, or -1 if not importing. * Returns the progress of replay importing, or -1 if not importing.
* @return the completion percent [0, 100] or -1 * @return the completion percent [0, 100] or -1
*/ */
public static int getLoadingProgress() { public int getLoadingProgress() {
if (files == null || fileIndex == -1) if (files == null || fileIndex == -1)
return -1; return -1;
return (fileIndex + 1) * 100 / files.length; return (fileIndex + 1) * 100 / files.length;
} }
} }

View File

@ -20,9 +20,7 @@ package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData; import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
@ -60,6 +59,7 @@ import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
/** /**
* Downloads menu. * Downloads menu.
@ -72,6 +72,15 @@ public class DownloadsMenu extends ComplexOpsuState {
@Inject @Inject
private InstanceContainer instanceContainer; private InstanceContainer instanceContainer;
@Inject
private Configuration config;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** Delay time, in milliseconds, between each search. */ /** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 700; private static final int SEARCH_DELAY = 700;
@ -82,12 +91,7 @@ public class DownloadsMenu extends ComplexOpsuState {
private static final int MIN_REQUEST_INTERVAL = 300; private static final int MIN_REQUEST_INTERVAL = 300;
/** Available beatmap download servers. */ /** Available beatmap download servers. */
private static final DownloadServer[] SERVERS = { private final DownloadServer[] SERVERS;
new BloodcatServer(),
new YaSOnlineServer(),
new MnetworkServer(),
new MengSkyServer()
};
/** The current list of search results. */ /** The current list of search results. */
private DownloadNode[] resultList; private DownloadNode[] resultList;
@ -276,9 +280,9 @@ public class DownloadsMenu extends ComplexOpsuState {
/** Imports all packed beatmaps. */ /** Imports all packed beatmaps. */
private void importBeatmaps() { private void importBeatmaps() {
// invoke unpacker and parser // invoke unpacker and parser
File[] dirs = OszUnpacker.unpackAllFiles(Options.getOSZDir(), Options.getBeatmapDir()); File[] dirs = oszUnpacker.unpackAll();
if (dirs != null && dirs.length > 0) { if (dirs != null && dirs.length > 0) {
this.importedNode = BeatmapParser.parseDirectories(dirs); this.importedNode = beatmapParser.parseDirectories(dirs);
if (importedNode != null) { if (importedNode != null) {
// send notification // send notification
EventBus.post(new BarNotificationEvent((dirs.length == 1) ? "Imported 1 new song." : EventBus.post(new BarNotificationEvent((dirs.length == 1) ? "Imported 1 new song." :
@ -290,6 +294,16 @@ public class DownloadsMenu extends ComplexOpsuState {
} }
} }
@Inject
public DownloadsMenu(InstanceContainer instanceContainer) {
SERVERS = new DownloadServer[] {
instanceContainer.provide(BloodcatServer.class),
instanceContainer.provide(YaSOnlineServer.class),
instanceContainer.provide(MnetworkServer.class),
instanceContainer.provide(MengSkyServer.class),
};
}
@Override @Override
public void revalidate() { public void revalidate() {
super.revalidate(); super.revalidate();
@ -665,6 +679,7 @@ public class DownloadsMenu extends ComplexOpsuState {
try { try {
previewID = -1; previewID = -1;
boolean playing = SoundController.playTrack( boolean playing = SoundController.playTrack(
config,
url, url,
Integer.toString(node.getID()), Integer.toString(node.getID()),
true, true,

View File

@ -18,12 +18,7 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.*;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.HitSound; import itdelatrisu.opsu.audio.HitSound;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
@ -34,7 +29,6 @@ import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.db.ScoreDB; import itdelatrisu.opsu.db.ScoreDB;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.objects.Circle; import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.DummyObject; import itdelatrisu.opsu.objects.DummyObject;
import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.GameObject;
@ -76,11 +70,17 @@ import yugecin.opsudance.core.state.transitions.FadeOutTransitionState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.objects.curves.FakeCombinedCurve; import yugecin.opsudance.objects.curves.FakeCombinedCurve;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.options.Options;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.sbv2.MoveStoryboard; import yugecin.opsudance.sbv2.MoveStoryboard;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay;
import yugecin.opsudance.ui.StoryboardOverlay; import yugecin.opsudance.ui.StoryboardOverlay;
import yugecin.opsudance.utils.GLHelper; import yugecin.opsudance.utils.GLHelper;
import static yugecin.opsudance.options.Options.*;
/** /**
* "Game" state. * "Game" state.
*/ */
@ -89,6 +89,12 @@ public class Game extends ComplexOpsuState {
@Inject @Inject
private InstanceContainer instanceContainer; private InstanceContainer instanceContainer;
@Inject
private GameObjectRenderer gameObjectRenderer;
@Inject
private BeatmapParser beatmapParser;
public static boolean isInGame; // TODO delete this when #79 is fixed public static boolean isInGame; // TODO delete this when #79 is fixed
/** Game restart states. */ /** Game restart states. */
public enum Restart { public enum Restart {
@ -330,7 +336,7 @@ public class Game extends ComplexOpsuState {
super(); super();
mirrorCursor = new Cursor(true); mirrorCursor = new Cursor(true);
this.moveStoryboardOverlay = new MoveStoryboard(displayContainer); this.moveStoryboardOverlay = new MoveStoryboard(displayContainer);
this.optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.storyboardOptions); this.optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.storyboardOptions);
this.storyboardOverlay = new StoryboardOverlay(displayContainer, moveStoryboardOverlay, optionsOverlay, this); this.storyboardOverlay = new StoryboardOverlay(displayContainer, moveStoryboardOverlay, optionsOverlay, this);
storyboardOverlay.show(); storyboardOverlay.show();
moveStoryboardOverlay.show(); moveStoryboardOverlay.show();
@ -364,7 +370,8 @@ public class Game extends ComplexOpsuState {
scoreboardStarStream.setDurationSpread(700, 100); scoreboardStarStream.setDurationSpread(700, 100);
// create the associated GameData object // create the associated GameData object
data = new GameData(displayContainer.width, displayContainer.height); data = instanceContainer.injectFields(new GameData(displayContainer.width, displayContainer.height));
gameObjectRenderer.setGameData(data);
} }
@ -386,17 +393,6 @@ public class Game extends ComplexOpsuState {
if (objectIndex > 0) { if (objectIndex > 0) {
objectIndex--; objectIndex--;
} }
if (Options.isMergingSliders()) {
int obj = objectIndex;
while (obj < gameObjects.length) {
if (gameObjects[obj] instanceof Slider) {
slidercurveFrom = slidercurveTo = ((Slider) gameObjects[obj]).baseSliderFrom;
break;
}
obj++;
}
spliceSliderCurve(-1, -1);
}
Dancer.instance.setObjectIndex(objectIndex); Dancer.instance.setObjectIndex(objectIndex);
storyboardOverlay.updateIndex(objectIndex); storyboardOverlay.updateIndex(objectIndex);
lastReplayTime = beatmap.objects[objectIndex].getTime(); lastReplayTime = beatmap.objects[objectIndex].getTime();
@ -409,7 +405,7 @@ public class Game extends ComplexOpsuState {
int trackPosition = MusicController.getPosition(); int trackPosition = MusicController.getPosition();
if (isLeadIn()) { if (isLeadIn()) {
trackPosition -= leadInTime - currentMapMusicOffset - Options.getMusicOffset(); trackPosition -= leadInTime - currentMapMusicOffset - OPTION_MUSIC_OFFSET.val;
} }
if (pauseTime > -1) // returning from pause screen if (pauseTime > -1) // returning from pause screen
trackPosition = pauseTime; trackPosition = pauseTime;
@ -427,15 +423,15 @@ public class Game extends ComplexOpsuState {
} }
// background // background
if (!Options.isRemoveBG() && GameMod.AUTO.isActive()) { if (!OPTION_DANCE_REMOVE_BG.state && GameMod.AUTO.isActive()) {
float dimLevel = Options.getBackgroundDim(); float dimLevel = (100 - OPTION_BACKGROUND_DIM.val) / 100f;
if (trackPosition < firstObjectTime) { if (trackPosition < firstObjectTime) {
if (timeDiff < approachTime) if (timeDiff < approachTime)
dimLevel += (1f - dimLevel) * ((float) timeDiff / approachTime); dimLevel += (1f - dimLevel) * ((float) timeDiff / approachTime);
else else
dimLevel = 1f; dimLevel = 1f;
} }
if (Options.isDefaultPlayfieldForced() || !beatmap.drawBackground(width, height, dimLevel, false)) { if (OPTION_FORCE_DEFAULT_PLAYFIELD.state || !beatmap.drawBackground(width, height, dimLevel, false)) {
Image playfield = GameImage.PLAYFIELD.getImage(); Image playfield = GameImage.PLAYFIELD.getImage();
playfield.setAlpha(dimLevel); playfield.setAlpha(dimLevel);
playfield.draw(); playfield.draw();
@ -462,7 +458,7 @@ public class Game extends ComplexOpsuState {
if (GameMod.FLASHLIGHT.isActive()) { if (GameMod.FLASHLIGHT.isActive()) {
// render hit objects offscreen // render hit objects offscreen
Graphics.setCurrent(gOffscreen); Graphics.setCurrent(gOffscreen);
int trackPos = (isLeadIn()) ? (leadInTime - Options.getMusicOffset()) * -1 : trackPosition; int trackPos = (isLeadIn()) ? (leadInTime - OPTION_MUSIC_OFFSET.val) * -1 : trackPosition;
drawHitObjects(gOffscreen, trackPos); drawHitObjects(gOffscreen, trackPos);
// restore original graphics context // restore original graphics context
@ -519,7 +515,7 @@ public class Game extends ComplexOpsuState {
Colors.BLACK_ALPHA.a = a; Colors.BLACK_ALPHA.a = a;
} }
if (!Options.isHideUI() || !GameMod.AUTO.isActive()) { if (!OPTION_DANCE_HIDE_UI.state || !GameMod.AUTO.isActive()) {
data.drawGameElements(g, true, objectIndex == 0, 1f); data.drawGameElements(g, true, objectIndex == 0, 1f);
} }
@ -557,7 +553,7 @@ public class Game extends ComplexOpsuState {
// non-break // non-break
else { else {
if (!GameMod.AUTO.isActive() || !Options.isHideUI()) { if (!GameMod.AUTO.isActive() || !OPTION_DANCE_HIDE_UI.state) {
// game elements // game elements
float gameElementAlpha = 1f; float gameElementAlpha = 1f;
if (gameFinished) { if (gameFinished) {
@ -590,7 +586,7 @@ public class Game extends ComplexOpsuState {
} }
if (isLeadIn()) if (isLeadIn())
trackPosition = (leadInTime - Options.getMusicOffset()) * -1; // render approach circles during song lead-in trackPosition = (leadInTime - OPTION_MUSIC_OFFSET.val) * -1; // render approach circles during song lead-in
// countdown // countdown
if (beatmap.countdown > 0) { if (beatmap.countdown > 0) {
@ -691,15 +687,15 @@ public class Game extends ComplexOpsuState {
} }
} }
if (!Options.isHideUI() && GameMod.AUTO.isActive()) if (!OPTION_DANCE_HIDE_UI.state && GameMod.AUTO.isActive())
GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f); GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
// draw replay speed button // draw replay speed button
if (isReplay || (!Options.isHideUI()&& GameMod.AUTO.isActive())) if (isReplay || (!OPTION_DANCE_HIDE_UI.state&& GameMod.AUTO.isActive()))
playbackSpeed.getButton().draw(); playbackSpeed.getButton().draw();
// draw music position bar (for replay seeking) // draw music position bar (for replay seeking)
if (isReplay && Options.isReplaySeekingEnabled()) { if (isReplay && OPTION_REPLAY_SEEKING.state) {
g.setColor((musicPositionBarContains(displayContainer.mouseX, displayContainer.mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL); g.setColor((musicPositionBarContains(displayContainer.mouseX, displayContainer.mouseY)) ? MUSICBAR_HOVER : MUSICBAR_NORMAL);
g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4); g.fillRoundRect(musicBarX, musicBarY, musicBarWidth, musicBarHeight, 4);
if (!isLeadIn()) { if (!isLeadIn()) {
@ -729,7 +725,7 @@ public class Game extends ComplexOpsuState {
displayContainer.cursor.draw(replayKeyPressed); displayContainer.cursor.draw(replayKeyPressed);
} else if (GameMod.AUTO.isActive()) { } else if (GameMod.AUTO.isActive()) {
displayContainer.cursor.draw(autoMousePressed); displayContainer.cursor.draw(autoMousePressed);
if (Options.isMirror() && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
mirrorCursor.draw(autoMousePressed); mirrorCursor.draw(autoMousePressed);
} }
} else if (GameMod.AUTOPILOT.isActive()) { } else if (GameMod.AUTOPILOT.isActive()) {
@ -876,7 +872,7 @@ public class Game extends ComplexOpsuState {
} }
// update in-game scoreboard // update in-game scoreboard
if (!Options.isHideUI() && previousScores != null && trackPosition > firstObjectTime) { if (!OPTION_DANCE_HIDE_UI.state && previousScores != null && trackPosition > firstObjectTime) {
// show scoreboard if selected, and always in break // show scoreboard if selected, and always in break
// hide when game ends // hide when game ends
if ((scoreboardVisible || breakTime > 0) && !gameFinished) { if ((scoreboardVisible || breakTime > 0) && !gameFinished) {
@ -945,7 +941,7 @@ public class Game extends ComplexOpsuState {
displayContainer.cursor.setCursorPosition(displayContainer.delta, replayX, replayY); displayContainer.cursor.setCursorPosition(displayContainer.delta, replayX, replayY);
} else if (GameMod.AUTO.isActive()) { } else if (GameMod.AUTO.isActive()) {
displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y); displayContainer.cursor.setCursorPosition(displayContainer.delta, (int) autoMousePosition.x, (int) autoMousePosition.y);
if (Options.isMirror() && GameMod.AUTO.isActive()) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
double dx = autoMousePosition.x - Options.width / 2d; double dx = autoMousePosition.x - Options.width / 2d;
double dy = autoMousePosition.y - Options.height / 2d; double dy = autoMousePosition.y - Options.height / 2d;
double d = Math.sqrt(dx * dx + dy * dy); double d = Math.sqrt(dx * dx + dy * dy);
@ -970,7 +966,7 @@ public class Game extends ComplexOpsuState {
boolean complete = objectIndex >= gameObjects.length; boolean complete = objectIndex >= gameObjects.length;
if (GameMod.AUTO.isActive() && complete) { if (GameMod.AUTO.isActive() && complete) {
if (gameObjects.length > 0) { if (gameObjects.length > 0) {
complete = trackPosition >= gameObjects[gameObjects.length - 1].getEndTime() + Options.getMapEndDelay(); complete = trackPosition >= gameObjects[gameObjects.length - 1].getEndTime() + OPTION_MAP_END_DELAY.val * 100;
} }
} }
if (complete || (MusicController.trackEnded() && objectIndex > 0)) { if (complete || (MusicController.trackEnded() && objectIndex > 0)) {
@ -1115,7 +1111,9 @@ public class Game extends ComplexOpsuState {
objectIndex++; // done, so increment object index objectIndex++; // done, so increment object index
storyboardOverlay.updateIndex(objectIndex); storyboardOverlay.updateIndex(objectIndex);
if (objectIndex >= mirrorTo) { if (objectIndex >= mirrorTo) {
Options.setMirror(false); if (OPTION_DANCE_MIRROR.state) {
OPTION_DANCE_MIRROR.toggle();
}
} }
} else } else
break; break;
@ -1147,13 +1145,15 @@ public class Game extends ComplexOpsuState {
// game keys // game keys
if (!Keyboard.isRepeatEvent()) { if (!Keyboard.isRepeatEvent()) {
int keys = ReplayFrame.KEY_NONE; int keys = ReplayFrame.KEY_NONE;
if (key == Options.getGameKeyLeft()) if (key == OPTION_KEY_LEFT.intval) {
keys = ReplayFrame.KEY_K1; keys = ReplayFrame.KEY_K1;
else if (key == Options.getGameKeyRight()) } else if (key == OPTION_KEY_RIGHT.intval) {
keys = ReplayFrame.KEY_K2; keys = ReplayFrame.KEY_K2;
if (keys != ReplayFrame.KEY_NONE) }
if (keys != ReplayFrame.KEY_NONE) {
gameKeyPressed(keys, mouseX, mouseY, trackPosition); gameKeyPressed(keys, mouseX, mouseY, trackPosition);
} }
}
switch (key) { switch (key) {
case Input.KEY_ESCAPE: case Input.KEY_ESCAPE:
@ -1195,8 +1195,10 @@ public class Game extends ComplexOpsuState {
break; break;
} }
int position = (pauseTime > -1) ? pauseTime : trackPosition; int time = (pauseTime > -1) ? pauseTime : trackPosition;
if (Options.setCheckpoint(position / 1000)) { time /= 1000;
if (0 <= time && time < 3600) {
OPTION_CHECKPOINT.setValue(time);
SoundController.playSound(SoundEffect.MENUCLICK); SoundController.playSound(SoundEffect.MENUCLICK);
EventBus.post(new BarNotificationEvent("Checkpoint saved.")); EventBus.post(new BarNotificationEvent("Checkpoint saved."));
} }
@ -1205,7 +1207,7 @@ public class Game extends ComplexOpsuState {
case Input.KEY_L: case Input.KEY_L:
// load checkpoint // load checkpoint
if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) { if (displayContainer.input.isKeyDown(Input.KEY_RCONTROL) || displayContainer.input.isKeyDown(Input.KEY_LCONTROL)) {
int checkpoint = Options.getCheckpoint(); int checkpoint = OPTION_CHECKPOINT.val * 1000;
if (checkpoint == 0 || checkpoint > beatmap.endTime) if (checkpoint == 0 || checkpoint > beatmap.endTime)
break; // invalid checkpoint break; // invalid checkpoint
loadCheckpoint(checkpoint); loadCheckpoint(checkpoint);
@ -1227,31 +1229,29 @@ public class Game extends ComplexOpsuState {
UI.changeVolume(-1); UI.changeVolume(-1);
break; break;
case Input.KEY_TAB: case Input.KEY_TAB:
if (!Options.isHideUI()) { if (!OPTION_DANCE_HIDE_UI.state) {
scoreboardVisible = !scoreboardVisible; scoreboardVisible = !scoreboardVisible;
} }
break; break;
case Input.KEY_M: case Input.KEY_M:
if (Options.isMirror()) { if (OPTION_DANCE_MIRROR.state) {
mirrorTo = objectIndex; mirrorTo = objectIndex;
Options.setMirror(false);
} else { } else {
mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y); mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y);
mirrorFrom = objectIndex; mirrorFrom = objectIndex;
mirrorTo = gameObjects.length; mirrorTo = gameObjects.length;
Options.setMirror(true);
} }
OPTION_DANCE_MIRROR.toggle();
break; break;
case Input.KEY_P: case Input.KEY_P:
if (Options.isMirror()) { if (OPTION_DANCE_MIRROR.state) {
mirrorTo = objectIndex; mirrorTo = objectIndex;
Options.setMirror(false);
} else { } else {
mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y); mirrorCursor.resetLocations((int) autoMousePosition.x, (int) autoMousePosition.y);
mirrorFrom = objectIndex; mirrorFrom = objectIndex;
mirrorTo = mirrorFrom + 1; mirrorTo = mirrorFrom + 1;
Options.setMirror(true);
} }
OPTION_DANCE_MIRROR.toggle();
break; break;
case Input.KEY_MINUS: case Input.KEY_MINUS:
currentMapMusicOffset += 5; currentMapMusicOffset += 5;
@ -1301,7 +1301,7 @@ public class Game extends ComplexOpsuState {
} }
// replay seeking // replay seeking
else if (Options.isReplaySeekingEnabled() && !GameMod.AUTO.isActive() && musicPositionBarContains(x, y)) { else if (OPTION_REPLAY_SEEKING.state && !GameMod.AUTO.isActive() && musicPositionBarContains(x, y)) {
SoundController.mute(true); // mute sounds while seeking SoundController.mute(true); // mute sounds while seeking
float pos = (y - musicBarY) / musicBarHeight * beatmap.endTime; float pos = (y - musicBarY) / musicBarHeight * beatmap.endTime;
MusicController.setPosition((int) pos); MusicController.setPosition((int) pos);
@ -1310,12 +1310,12 @@ public class Game extends ComplexOpsuState {
return true; return true;
} }
if (Options.isMouseDisabled()) { if (OPTION_DISABLE_MOUSE_BUTTONS.state) {
return true; return true;
} }
// mouse wheel: pause the game // mouse wheel: pause the game
if (button == Input.MOUSE_MIDDLE_BUTTON && !Options.isMouseWheelDisabled()) { if (button == Input.MOUSE_MIDDLE_BUTTON && !OPTION_DISABLE_MOUSE_WHEEL.state) {
int trackPosition = MusicController.getPosition(); int trackPosition = MusicController.getPosition();
if (pauseTime < 0 && breakTime <= 0 && trackPosition >= beatmap.objects[0].getTime()) { if (pauseTime < 0 && breakTime <= 0 && trackPosition >= beatmap.objects[0].getTime()) {
pausedMousePosition = new Vec2f(x, y); pausedMousePosition = new Vec2f(x, y);
@ -1389,7 +1389,7 @@ public class Game extends ComplexOpsuState {
return true; return true;
} }
if (Options.isMouseDisabled()) { if (OPTION_DISABLE_MOUSE_BUTTONS.state) {
return true; return true;
} }
@ -1419,12 +1419,14 @@ public class Game extends ComplexOpsuState {
} }
int keys = ReplayFrame.KEY_NONE; int keys = ReplayFrame.KEY_NONE;
if (key == Options.getGameKeyLeft()) if (key == OPTION_KEY_LEFT.intval) {
keys = ReplayFrame.KEY_K1; keys = ReplayFrame.KEY_K1;
else if (key == Options.getGameKeyRight()) } else if (key == OPTION_KEY_RIGHT.intval) {
keys = ReplayFrame.KEY_K2; keys = ReplayFrame.KEY_K2;
if (keys != ReplayFrame.KEY_NONE) }
if (keys != ReplayFrame.KEY_NONE) {
gameKeyReleased(keys, displayContainer.input.getMouseX(), displayContainer.input.getMouseY(), MusicController.getPosition()); gameKeyReleased(keys, displayContainer.input.getMouseX(), displayContainer.input.getMouseY(), MusicController.getPosition());
}
return true; return true;
} }
@ -1449,7 +1451,7 @@ public class Game extends ComplexOpsuState {
return true; return true;
} }
if (Options.isMouseWheelDisabled()) { if (OPTION_DISABLE_MOUSE_WHEEL.state) {
return true; return true;
} }
@ -1461,7 +1463,7 @@ public class Game extends ComplexOpsuState {
@Override @Override
public void enter() { public void enter() {
overlays.clear(); overlays.clear();
if (Options.isEnableSB()) { if (OPTION_DANCE_ENABLE_SB.state) {
overlays.add(optionsOverlay); overlays.add(optionsOverlay);
overlays.add(moveStoryboardOverlay); overlays.add(moveStoryboardOverlay);
overlays.add(storyboardOverlay); overlays.add(storyboardOverlay);
@ -1537,7 +1539,7 @@ public class Game extends ComplexOpsuState {
} }
// load epilepsy warning img // load epilepsy warning img
epiImgTime = Options.getEpilepsyWarningLength(); epiImgTime = OPTION_EPILEPSY_WARNING.val * 100;
if (epiImgTime > 0) { if (epiImgTime > 0) {
epiImg = GameImage.EPILEPSY_WARNING.getImage(); epiImg = GameImage.EPILEPSY_WARNING.getImage();
float desWidth = displayContainer.width / 2; float desWidth = displayContainer.width / 2;
@ -1584,7 +1586,11 @@ public class Game extends ComplexOpsuState {
} }
// initialize object maps // initialize object maps
CursorColorOverrides.comboColors = ObjectColorOverrides.comboColors = beatmap.getComboColors(); Color[] comboColors = beatmap.getComboColors();
if (comboColors == null) {
comboColors = SkinService.skin.getComboColors();
}
CursorColorOverrides.comboColors = ObjectColorOverrides.comboColors = comboColors;
for (int i = 0; i < beatmap.objects.length; i++) { for (int i = 0; i < beatmap.objects.length; i++) {
HitObject hitObject = beatmap.objects[i]; HitObject hitObject = beatmap.objects[i];
@ -1604,12 +1610,13 @@ public class Game extends ComplexOpsuState {
} }
try { try {
if (hitObject.isCircle()) if (hitObject.isCircle()) {
gameObjects[i] = new Circle(hitObject, this, data, hitObject.getComboIndex(), comboEnd); gameObjects[i] = instanceContainer.injectFields(new Circle(hitObject, this, data, hitObject.getComboIndex(), comboEnd));
else if (hitObject.isSlider()) } else if (hitObject.isSlider()) {
gameObjects[i] = new Slider(hitObject, this, data, hitObject.getComboIndex(), comboEnd); gameObjects[i] = instanceContainer.injectFields(new Slider(hitObject, this, data, hitObject.getComboIndex(), comboEnd));
else if (hitObject.isSpinner()) } else if (hitObject.isSpinner()) {
gameObjects[i] = new Spinner(hitObject, this, data); gameObjects[i] = new Spinner(hitObject, this, data);
}
} catch (Exception e) { } catch (Exception e) {
String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString()); String message = String.format("Failed to create %s at index %d:\n%s", hitObject.getTypeName(), i, hitObject.toString());
Log.error(message, e); Log.error(message, e);
@ -1688,7 +1695,7 @@ public class Game extends ComplexOpsuState {
MusicController.pause(); MusicController.pause();
if (gameObjects.length > 0) { if (gameObjects.length > 0) {
int leadIntime = Options.getMapStartDelay() - gameObjects[0].getTime(); int leadIntime = OPTION_MAP_START_DELAY.val * 100 - gameObjects[0].getTime();
if (leadIntime > 0) { if (leadIntime > 0) {
this.leadInTime = Math.max(leadIntime, this.leadInTime); this.leadInTime = Math.max(leadIntime, this.leadInTime);
} }
@ -1696,7 +1703,10 @@ public class Game extends ComplexOpsuState {
this.leadInTime += epiImgTime; this.leadInTime += epiImgTime;
SoundController.mute(false); SoundController.mute(false);
if (Options.isMergingSliders()) { if (!OPTION_FALLBACK_SLIDERS.state && OPTION_MERGING_SLIDERS.state) {
if (!OPTION_SHRINKING_SLIDERS.state) {
knorkesliders = null; // workaround for issue-130
}
if (knorkesliders == null) { if (knorkesliders == null) {
// let's create knorkesliders // let's create knorkesliders
List<Vec2f> curvepoints = new ArrayList<>(); List<Vec2f> curvepoints = new ArrayList<>();
@ -1721,9 +1731,6 @@ public class Game extends ComplexOpsuState {
} }
} }
slidercurveFrom = 0;
slidercurveTo = 0;
Dancer.instance.setGameObjects(gameObjects); Dancer.instance.setGameObjects(gameObjects);
storyboardOverlay.setGameObjects(gameObjects); storyboardOverlay.setGameObjects(gameObjects);
if (!skippedToCheckpoint) { if (!skippedToCheckpoint) {
@ -1752,7 +1759,7 @@ public class Game extends ComplexOpsuState {
MusicController.setPitch(1f); MusicController.setPitch(1f);
MusicController.resume(); MusicController.resume();
if (Options.isEnableSB()) { if (OPTION_DANCE_ENABLE_SB.state) {
storyboardOverlay.onLeave(); storyboardOverlay.onLeave();
} }
@ -1779,19 +1786,8 @@ public class Game extends ComplexOpsuState {
GameMod.loadModState(previousMods); GameMod.loadModState(previousMods);
} }
private int slidercurveFrom; public void addMergedSliderPointsToRender(int from, int to) {
private int slidercurveTo; knorkesliders.addRange(from, to);
public void setSlidercurveFrom(int slidercurveFrom) {
this.slidercurveFrom = Math.max(slidercurveFrom, this.slidercurveFrom);
}
public void setSlidercurveTo(int slidercurveTo) {
this.slidercurveTo = Math.max(slidercurveTo, this.slidercurveTo);
}
public void spliceSliderCurve(int from, int to) {
this.knorkesliders.splice(from, to);
} }
/** /**
@ -1800,15 +1796,18 @@ public class Game extends ComplexOpsuState {
* @param trackPosition the track position * @param trackPosition the track position
*/ */
private void drawHitObjects(Graphics g, int trackPosition) { private void drawHitObjects(Graphics g, int trackPosition) {
gameObjectRenderer.initForFrame();
// draw result objects // draw result objects
if (!Options.isHideObjects()) { if (!OPTION_DANCE_HIDE_OBJECTS.state) {
data.drawHitResults(trackPosition); data.drawHitResults(trackPosition);
} }
if (Options.isMergingSliders() && knorkesliders != null) { if (!OPTION_FALLBACK_SLIDERS.state && OPTION_MERGING_SLIDERS.state && knorkesliders != null) {
knorkesliders.draw(Color.white, this.slidercurveFrom, this.slidercurveTo); knorkesliders.draw(Color.white);
knorkesliders.initForFrame();
/* /*
if (Options.isMirror()) { if (OPTION_DANCE_MIRROR.state) {
g.pushTransform(); g.pushTransform();
g.rotate(Options.width / 2f, Options.height / 2f, 180f); g.rotate(Options.width / 2f, Options.height / 2f, 180f);
knorkesliders.draw(Color.white, this.slidercurveFrom, this.slidercurveTo); knorkesliders.draw(Color.white, this.slidercurveFrom, this.slidercurveTo);
@ -1833,7 +1832,7 @@ public class Game extends ComplexOpsuState {
stack.add(index); stack.add(index);
// draw follow points // draw follow points
if (!Options.isFollowPointEnabled() || loseState || !Options.isHideObjects()) if (!OPTION_SHOW_FOLLOW_POINTS.state || loseState || !OPTION_DANCE_HIDE_OBJECTS.state)
continue; continue;
if (beatmap.objects[index].isSpinner()) { if (beatmap.objects[index].isSpinner()) {
lastObjectIndex = -1; lastObjectIndex = -1;
@ -1898,9 +1897,9 @@ public class Game extends ComplexOpsuState {
// normal case // normal case
if (!loseState) { if (!loseState) {
if (!Options.isHideObjects()) { if (!OPTION_DANCE_HIDE_OBJECTS.state) {
gameObj.draw(g, trackPosition, false); gameObj.draw(g, trackPosition, false);
if (Options.isMirror() && GameMod.AUTO.isActive() && idx < mirrorTo && idx >= mirrorFrom) { if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive() && idx < mirrorTo && idx >= mirrorFrom) {
g.pushTransform(); g.pushTransform();
g.rotate(Options.width / 2f, Options.height / 2f, 180f); g.rotate(Options.width / 2f, Options.height / 2f, 180f);
gameObj.draw(g, trackPosition, true); gameObj.draw(g, trackPosition, true);
@ -1950,9 +1949,10 @@ public class Game extends ComplexOpsuState {
} }
this.beatmap = beatmap; this.beatmap = beatmap;
Display.setTitle(String.format("opsu!dance - %s", beatmap.toString())); Display.setTitle(String.format("opsu!dance - %s", beatmap.toString()));
if (beatmap.breaks == null) if (beatmap.breaks == null) {
BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY); BeatmapDB.load(beatmap, BeatmapDB.LOAD_ARRAY);
BeatmapParser.parseHitObjects(beatmap); }
beatmapParser.parseHitObjects(beatmap);
HitSound.setDefaultSampleSet(beatmap.sampleSet); HitSound.setDefaultSampleSet(beatmap.sampleSet);
} }
@ -2053,22 +2053,26 @@ public class Game extends ComplexOpsuState {
* Set map modifiers. * Set map modifiers.
*/ */
private void setMapModifiers() { private void setMapModifiers() {
// fixed difficulty overrides
float circleSize = OPTION_FIXED_CS.val / 10f;
float approachRate = OPTION_FIXED_AR.val / 10f;
float overallDifficulty = OPTION_FIXED_OD.val / 10f;
float HPDrainRate = OPTION_FIXED_HP.val / 10f;
// map-based properties, re-initialized each game // map-based properties, re-initialized each game
float multiplier = GameMod.getDifficultyMultiplier(); float multiplier = GameMod.getDifficultyMultiplier();
float circleSize = Math.min(beatmap.circleSize * multiplier, 10f); if (circleSize == 0f) {
float approachRate = Math.min(beatmap.approachRate * multiplier, 10f); circleSize = Math.min(beatmap.circleSize * multiplier, 10f);
float overallDifficulty = Math.min(beatmap.overallDifficulty * multiplier, 10f); }
float HPDrainRate = Math.min(beatmap.HPDrainRate * multiplier, 10f); if (approachRate == 0f) {
approachRate = Math.min(beatmap.approachRate * multiplier, 10f);
// fixed difficulty overrides }
if (Options.getFixedCS() > 0f) if (overallDifficulty == 0f) {
circleSize = Options.getFixedCS(); overallDifficulty = Math.min(beatmap.overallDifficulty * multiplier, 10f);
if (Options.getFixedAR() > 0f) }
approachRate = Options.getFixedAR(); if (HPDrainRate == 0f) {
if (Options.getFixedOD() > 0f) HPDrainRate = Math.min(beatmap.HPDrainRate * multiplier, 10f);
overallDifficulty = Options.getFixedOD(); }
if (Options.getFixedHP() > 0f)
HPDrainRate = Options.getFixedHP();
// Stack modifier scales with hit object size // Stack modifier scales with hit object size
// StackOffset = HitObjectRadius / 10 // StackOffset = HitObjectRadius / 10
@ -2077,11 +2081,14 @@ public class Game extends ComplexOpsuState {
HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER); HitObject.setStackOffset(diameter * STACK_OFFSET_MODIFIER);
// initialize objects // initialize objects
Circle.init(diameter); gameObjectRenderer.initForGame(data, diameter);
Slider.init(displayContainer, diameter, beatmap); Slider.init(gameObjectRenderer.getCircleDiameter(), beatmap);
Spinner.init(displayContainer, overallDifficulty); Spinner.init(displayContainer, overallDifficulty);
Curve.init(displayContainer.width, displayContainer.height, diameter, (Options.isBeatmapSkinIgnored()) ? Color sliderBorderColor = SkinService.skin.getSliderBorderColor();
Options.getSkin().getSliderBorderColor() : beatmap.getSliderBorderColor()); if (!OPTION_IGNORE_BEATMAP_SKINS.state && beatmap.getSliderBorderColor() != null) {
sliderBorderColor = beatmap.getSliderBorderColor();
}
Curve.init(displayContainer.width, displayContainer.height, diameter, sliderBorderColor);
// approachRate (hit object approach time) // approachRate (hit object approach time)
if (approachRate < 5) if (approachRate < 5)

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
@ -35,6 +34,8 @@ import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import static yugecin.opsudance.options.Options.*;
/** /**
* "Game Pause/Fail" state. * "Game Pause/Fail" state.
* <p> * <p>
@ -93,9 +94,9 @@ public class GamePauseMenu extends BaseOpsuState {
// game keys // game keys
if (!Keyboard.isRepeatEvent()) { if (!Keyboard.isRepeatEvent()) {
if (key == Options.getGameKeyLeft()) { if (key == OPTION_KEY_LEFT.intval) {
mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); mousePressed(Input.MOUSE_LEFT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
} else if (key == Options.getGameKeyRight()) { } else if (key == OPTION_KEY_RIGHT.intval) {
mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY); mousePressed(Input.MOUSE_RIGHT_BUTTON, displayContainer.mouseX, displayContainer.mouseY);
} }
} }
@ -166,7 +167,7 @@ public class GamePauseMenu extends BaseOpsuState {
return true; return true;
} }
if (Options.isMouseWheelDisabled()) { if (OPTION_DISABLE_MOUSE_WHEEL.state) {
return true; return true;
} }

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
@ -54,6 +53,8 @@ import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent; import yugecin.opsudance.events.BubbleNotificationEvent;
import static yugecin.opsudance.options.Options.*;
/** /**
* "Main Menu" state. * "Main Menu" state.
* <p> * <p>
@ -64,6 +65,9 @@ public class MainMenu extends BaseOpsuState {
@Inject @Inject
private InstanceContainer instanceContainer; private InstanceContainer instanceContainer;
@Inject
private Updater updater;
/** Idle time, in milliseconds, before returning the logo to its original position. */ /** Idle time, in milliseconds, before returning the logo to its original position. */
private static final short LOGO_IDLE_DELAY = 10000; private static final short LOGO_IDLE_DELAY = 10000;
@ -232,7 +236,7 @@ public class MainMenu extends BaseOpsuState {
// draw background // draw background
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
if (Options.isDynamicBackgroundEnabled() && if (OPTION_DYNAMIC_BACKGROUND.state &&
beatmap != null && beatmap.drawBackground(width, height, bgAlpha.getValue(), true)) beatmap != null && beatmap.drawBackground(width, height, bgAlpha.getValue(), true))
; ;
else { else {
@ -262,7 +266,7 @@ public class MainMenu extends BaseOpsuState {
} }
// draw logo (pulsing) // draw logo (pulsing)
Color color = Options.isColorMainMenuLogo() ? Cursor.lastCursorColor : Color.white; Color color = OPTION_COLOR_MAIN_MENU_LOGO.state ? Cursor.lastCursorColor : Color.white;
Float position = MusicController.getBeatProgress(); Float position = MusicController.getBeatProgress();
boolean renderPiece = position != null; boolean renderPiece = position != null;
if (position == null) { if (position == null) {
@ -315,13 +319,14 @@ public class MainMenu extends BaseOpsuState {
} }
// draw update button // draw update button
if (Updater.get().showButton()) { if (updater.showButton()) {
Updater.Status status = Updater.get().getStatus(); Updater.Status status = updater.getStatus();
if (status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) if (status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) {
updateButton.draw(); updateButton.draw();
else if (status == Updater.Status.UPDATE_DOWNLOADED) } else if (status == Updater.Status.UPDATE_DOWNLOADED) {
restartButton.draw(); restartButton.draw();
} }
}
// draw text // draw text
float marginX = width * 0.015f, topMarginY = height * 0.01f, bottomMarginY = height * 0.015f; float marginX = width * 0.015f, topMarginY = height * 0.01f, bottomMarginY = height * 0.015f;
@ -329,10 +334,10 @@ public class MainMenu extends BaseOpsuState {
float lineHeight = Fonts.MEDIUM.getLineHeight() * 0.925f; float lineHeight = Fonts.MEDIUM.getLineHeight() * 0.925f;
g.drawString(String.format("Loaded %d songs and %d beatmaps.", g.drawString(String.format("Loaded %d songs and %d beatmaps.",
BeatmapSetList.get().getMapSetCount(), BeatmapSetList.get().getMapCount()), marginX, topMarginY); BeatmapSetList.get().getMapSetCount(), BeatmapSetList.get().getMapCount()), marginX, topMarginY);
if (MusicController.isTrackLoading()) if (MusicController.isTrackLoading()) {
g.drawString("Track loading...", marginX, topMarginY + lineHeight); g.drawString("Track loading...", marginX, topMarginY + lineHeight);
else if (MusicController.trackExists()) { } else if (MusicController.trackExists()) {
if (Options.useUnicodeMetadata()) { // load glyphs if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode); Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode);
Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.artistUnicode); Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.artistUnicode);
} }
@ -365,7 +370,7 @@ public class MainMenu extends BaseOpsuState {
repoButton.hoverUpdate(delta, mouseX, mouseY); repoButton.hoverUpdate(delta, mouseX, mouseY);
danceRepoButton.hoverUpdate(delta, mouseX, mouseY); danceRepoButton.hoverUpdate(delta, mouseX, mouseY);
} }
if (Updater.get().showButton()) { if (updater.showButton()) {
updateButton.autoHoverUpdate(delta, true); updateButton.autoHoverUpdate(delta, true);
restartButton.autoHoverUpdate(delta, false); restartButton.autoHoverUpdate(delta, false);
} }
@ -387,7 +392,7 @@ public class MainMenu extends BaseOpsuState {
// fade in background // fade in background
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
if (!(Options.isDynamicBackgroundEnabled() && beatmap != null && beatmap.isBackgroundLoading())) if (!(OPTION_DYNAMIC_BACKGROUND.state && beatmap != null && beatmap.isBackgroundLoading()))
bgAlpha.update(delta); bgAlpha.update(delta);
// check measure progress // check measure progress
@ -445,8 +450,8 @@ public class MainMenu extends BaseOpsuState {
UI.updateTooltip(delta, "Next track", false); UI.updateTooltip(delta, "Next track", false);
else if (musicPrevious.contains(mouseX, mouseY)) else if (musicPrevious.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Previous track", false); UI.updateTooltip(delta, "Previous track", false);
else if (Updater.get().showButton()) { else if (updater.showButton()) {
Updater.Status status = Updater.get().getStatus(); Updater.Status status = updater.getStatus();
if (((status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) && updateButton.contains(mouseX, mouseY)) || if (((status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) && updateButton.contains(mouseX, mouseY)) ||
(status == Updater.Status.UPDATE_DOWNLOADED && restartButton.contains(mouseX, mouseY))) (status == Updater.Status.UPDATE_DOWNLOADED && restartButton.contains(mouseX, mouseY)))
UI.updateTooltip(delta, status.getDescription(), true); UI.updateTooltip(delta, status.getDescription(), true);
@ -466,10 +471,10 @@ public class MainMenu extends BaseOpsuState {
UI.enter(); UI.enter();
if (!enterNotification) { if (!enterNotification) {
if (Updater.get().getStatus() == Updater.Status.UPDATE_AVAILABLE) { if (updater.getStatus() == Updater.Status.UPDATE_AVAILABLE) {
EventBus.post(new BarNotificationEvent("An opsu! update is available.")); EventBus.post(new BarNotificationEvent("An opsu! update is available."));
enterNotification = true; enterNotification = true;
} else if (Updater.get().justUpdated()) { } else if (updater.justUpdated()) {
EventBus.post(new BarNotificationEvent("opsu! is now up to date!")); EventBus.post(new BarNotificationEvent("opsu! is now up to date!"));
enterNotification = true; enterNotification = true;
} }
@ -547,10 +552,12 @@ public class MainMenu extends BaseOpsuState {
lastMeasureProgress = 0f; lastMeasureProgress = 0f;
if (!previous.isEmpty()) { if (!previous.isEmpty()) {
instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false); instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
if (Options.isDynamicBackgroundEnabled()) if (OPTION_DYNAMIC_BACKGROUND.state) {
bgAlpha.setTime(0); bgAlpha.setTime(0);
} else }
} else {
MusicController.setPosition(0); MusicController.setPosition(0);
}
EventBus.post(new BarNotificationEvent("<< Previous")); EventBus.post(new BarNotificationEvent("<< Previous"));
return true; return true;
} }
@ -565,7 +572,7 @@ public class MainMenu extends BaseOpsuState {
// repository button actions // repository button actions
if (repoButton != null && repoButton.contains(x, y)) { if (repoButton != null && repoButton.contains(x, y)) {
try { try {
Desktop.getDesktop().browse(Options.REPOSITORY_URI); Desktop.getDesktop().browse(config.REPOSITORY_URI);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened.")); EventBus.post(new BarNotificationEvent("The repository web page could not be opened."));
} catch (IOException e) { } catch (IOException e) {
@ -577,7 +584,7 @@ public class MainMenu extends BaseOpsuState {
if (danceRepoButton != null && danceRepoButton.contains(x, y)) { if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
try { try {
Desktop.getDesktop().browse(Options.DANCE_REPOSITORY_URI); Desktop.getDesktop().browse(config.DANCE_REPOSITORY_URI);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened.")); EventBus.post(new BarNotificationEvent("The repository web page could not be opened."));
} catch (IOException e) { } catch (IOException e) {
@ -588,11 +595,11 @@ public class MainMenu extends BaseOpsuState {
} }
// update button actions // update button actions
if (Updater.get().showButton()) { if (updater.showButton()) {
Updater.Status status = Updater.get().getStatus(); Updater.Status status = updater.getStatus();
if (updateButton.contains(x, y) && status == Updater.Status.UPDATE_AVAILABLE) { if (updateButton.contains(x, y) && status == Updater.Status.UPDATE_AVAILABLE) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
Updater.get().startDownload(); updater.startDownload();
updateButton.removeHoverEffects(); updateButton.removeHoverEffects();
updateButton.setHoverAnimationDuration(800); updateButton.setHoverAnimationDuration(800);
updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD); updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD);
@ -600,7 +607,7 @@ public class MainMenu extends BaseOpsuState {
return true; return true;
} else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) { } else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
Updater.get().prepareUpdate(); updater.prepareUpdate();
displayContainer.exitRequested = true; displayContainer.exitRequested = true;
return true; return true;
} }
@ -719,9 +726,10 @@ public class MainMenu extends BaseOpsuState {
if (!isTheme && !sameAudio) if (!isTheme && !sameAudio)
previous.add(node.index); previous.add(node.index);
} }
if (Options.isDynamicBackgroundEnabled() && !sameAudio && !MusicController.isThemePlaying()) if (OPTION_DYNAMIC_BACKGROUND.state && !sameAudio && !MusicController.isThemePlaying()) {
bgAlpha.setTime(0); bgAlpha.setTime(0);
} }
}
/** /**
* Enters the song menu, or the downloads menu if no beatmaps are loaded. * Enters the song menu, or the downloads menu if no beatmaps are loaded.

View File

@ -1,237 +0,0 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.Options.GameOption;
import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
public class OptionsMenu {
public static final OptionTab[] normalOptions = new OptionTab[] {
new OptionTab("GENERAL", null),
new OptionTab("GENERAL", new GameOption[]{
GameOption.DISABLE_UPDATER,
GameOption.ENABLE_WATCH_SERVICE
}),
new OptionTab("LANGUAGE", new GameOption[]{
GameOption.SHOW_UNICODE,
}),
new OptionTab("GRAPHICS", null),
new OptionTab("RENDERER", new GameOption[] {
GameOption.SCREEN_RESOLUTION,
GameOption.ALLOW_LARGER_RESOLUTIONS,
GameOption.FULLSCREEN,
GameOption.TARGET_UPS,
GameOption.TARGET_FPS,
GameOption.SHOW_FPS,
GameOption.USE_FPS_DELTAS,
GameOption.SCREENSHOT_FORMAT,
}),
new OptionTab("SLIDER OPTIONS", new GameOption[]{
GameOption.SNAKING_SLIDERS,
GameOption.FALLBACK_SLIDERS,
GameOption.SHRINKING_SLIDERS,
GameOption.MERGING_SLIDERS,
//GameOption.MERGING_SLIDERS_MIRROR_POOL,
GameOption.DRAW_SLIDER_ENDCIRCLES,
}),
new OptionTab("SKIN", null),
new OptionTab("SKIN", new GameOption[]{
GameOption.SKIN,
GameOption.IGNORE_BEATMAP_SKINS,
GameOption.DYNAMIC_BACKGROUND,
GameOption.LOAD_HD_IMAGES,
GameOption.LOAD_VERBOSE,
GameOption.COLOR_MAIN_MENU_LOGO,
}),
new OptionTab("CURSOR", new GameOption[]{
GameOption.CURSOR_SIZE,
GameOption.NEW_CURSOR,
GameOption.DISABLE_CURSOR
// TODO use combo colour as tint for slider ball option
}),
new OptionTab("AUDIO", null),
new OptionTab("VOLUME", new GameOption[]{
GameOption.MASTER_VOLUME,
GameOption.MUSIC_VOLUME,
GameOption.EFFECT_VOLUME,
GameOption.HITSOUND_VOLUME,
GameOption.SAMPLE_VOLUME_OVERRIDE,
}),
new OptionTab("MISC", new GameOption[] {
GameOption.MUSIC_OFFSET,
GameOption.DISABLE_SOUNDS,
GameOption.ENABLE_THEME_SONG
}),
new OptionTab("GAMEPLAY", null),
new OptionTab("GENERAL", new GameOption[] {
GameOption.BACKGROUND_DIM,
GameOption.FORCE_DEFAULT_PLAYFIELD,
GameOption.SHOW_HIT_LIGHTING,
GameOption.SHOW_HIT_ANIMATIONS,
GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT,
GameOption.SHOW_FOLLOW_POINTS,
GameOption.SHOW_HIT_ERROR_BAR,
GameOption.MAP_START_DELAY,
GameOption.MAP_END_DELAY,
GameOption.EPILEPSY_WARNING,
}),
new OptionTab("INPUT", null),
new OptionTab("KEY MAPPING", new GameOption[]{
GameOption.KEY_LEFT,
GameOption.KEY_RIGHT,
}),
new OptionTab("MOUSE", new GameOption[] {
GameOption.DISABLE_MOUSE_WHEEL,
GameOption.DISABLE_MOUSE_BUTTONS,
}),
new OptionTab("CUSTOM", null),
new OptionTab("DIFFICULTY", new GameOption[]{
GameOption.FIXED_CS,
GameOption.FIXED_HP,
GameOption.FIXED_AR,
GameOption.FIXED_OD,
}),
new OptionTab("MISC", new GameOption[] {
GameOption.CHECKPOINT,
GameOption.REPLAY_SEEKING,
}),
new OptionTab("DANCE", null),
new OptionTab("MOVER", new GameOption[]{
GameOption.DANCE_MOVER,
GameOption.DANCE_EXGON_DELAY,
GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_MOVER_DIRECTION,
GameOption.DANCE_SLIDER_MOVER_TYPE,
}),
new OptionTab("SPINNER", new GameOption[]{
GameOption.DANCE_SPINNER,
GameOption.DANCE_SPINNER_DELAY,
}),
new OptionTab("SLIDER OPTIONS", new GameOption[]{
GameOption.DANCE_LAZY_SLIDERS,
GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
}),
new OptionTab("CIRCLE MOVEMENTS", new GameOption[]{
GameOption.DANCE_CIRCLE_STREAMS,
GameOption.DANCE_ONLY_CIRCLE_STACKS,
}),
new OptionTab("MIRROR", new GameOption[] {
GameOption.DANCE_MIRROR,
}),
new OptionTab("ADVANCED DISPLAY", null),
new OptionTab("OBJECTS", new GameOption[]{
GameOption.DANCE_DRAW_APPROACH,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
GameOption.DANCE_RGB_OBJECT_INC,
GameOption.DANCE_HIDE_OBJECTS,
}),
new OptionTab("CURSOR", new GameOption[]{
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
GameOption.DANCE_RGB_CURSOR_INC,
GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
}),
new OptionTab("MISC", new GameOption[] {
GameOption.DANCE_HIDE_UI,
GameOption.DANCE_REMOVE_BG,
GameOption.DANCE_ENABLE_SB,
}),
new OptionTab ("PIPPI", null),
new OptionTab ("GENERAL", new GameOption[]{
GameOption.PIPPI_ENABLE,
GameOption.PIPPI_RADIUS_PERCENT,
}),
new OptionTab ("ANGLE MULTIPLIERS", new GameOption[]{
GameOption.PIPPI_ANGLE_INC_MUL,
GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
}),
new OptionTab ("MISC", new GameOption[] {
GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
})
};
public static final OptionTab[] storyboardOptions = new OptionTab[] {
new OptionTab("Gameplay", new GameOption[] {
GameOption.BACKGROUND_DIM,
GameOption.DANCE_REMOVE_BG,
GameOption.SNAKING_SLIDERS,
GameOption.SHRINKING_SLIDERS,
GameOption.SHOW_HIT_LIGHTING,
GameOption.SHOW_HIT_ANIMATIONS,
GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT,
GameOption.SHOW_FOLLOW_POINTS,
}),
new OptionTab("Input", new GameOption[] {
GameOption.CURSOR_SIZE,
GameOption.NEW_CURSOR,
GameOption.DISABLE_CURSOR
}),
new OptionTab("Dance", new GameOption[] {
GameOption.DANCE_MOVER,
GameOption.DANCE_EXGON_DELAY,
GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS,
GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS,
GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR,
GameOption.DANCE_MOVER_DIRECTION,
GameOption.DANCE_SLIDER_MOVER_TYPE,
GameOption.DANCE_SPINNER,
GameOption.DANCE_SPINNER_DELAY,
GameOption.DANCE_LAZY_SLIDERS,
GameOption.DANCE_CIRCLE_STREAMS,
GameOption.DANCE_ONLY_CIRCLE_STACKS,
GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS,
GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS,
GameOption.DANCE_MIRROR,
}),
new OptionTab("Dance display", new GameOption[] {
GameOption.DANCE_DRAW_APPROACH,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE,
GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED,
GameOption.DANCE_RGB_OBJECT_INC,
GameOption.DANCE_CURSOR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE,
GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL,
GameOption.DANCE_RGB_CURSOR_INC,
GameOption.DANCE_CURSOR_TRAIL_OVERRIDE,
GameOption.DANCE_HIDE_OBJECTS,
GameOption.DANCE_HIDE_UI,
}),
new OptionTab ("Pippi", new GameOption[] {
GameOption.PIPPI_ENABLE,
GameOption.PIPPI_RADIUS_PERCENT,
GameOption.PIPPI_ANGLE_INC_MUL,
GameOption.PIPPI_ANGLE_INC_MUL_SLIDER,
GameOption.PIPPI_SLIDER_FOLLOW_EXPAND,
GameOption.PIPPI_PREVENT_WOBBLY_STREAMS,
})
};
}

View File

@ -18,13 +18,8 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.*;
import itdelatrisu.opsu.GameData.Grade; import itdelatrisu.opsu.GameData.Grade;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MultiClip; import itdelatrisu.opsu.audio.MultiClip;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
@ -74,8 +69,12 @@ import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer; import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.core.state.ComplexOpsuState; import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotificationEvent; import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.ui.OptionsOverlay; import yugecin.opsudance.ui.OptionsOverlay;
import static yugecin.opsudance.options.Options.*;
/** /**
* "Song Selection" state. * "Song Selection" state.
* <p> * <p>
@ -87,6 +86,15 @@ public class SongMenu extends ComplexOpsuState {
@Inject @Inject
private InstanceContainer instanceContainer; private InstanceContainer instanceContainer;
@Inject
private Configuration config;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** The max number of song buttons to be shown on each screen. */ /** The max number of song buttons to be shown on each screen. */
public static final int MAX_SONG_BUTTONS = 6; public static final int MAX_SONG_BUTTONS = 6;
@ -236,18 +244,11 @@ public class SongMenu extends ComplexOpsuState {
/** Reloads all beatmaps. */ /** Reloads all beatmaps. */
private void reloadBeatmaps() { private void reloadBeatmaps() {
File beatmapDir = Options.getBeatmapDir();
if (fullReload) { if (fullReload) {
// clear the beatmap cache
BeatmapDB.clearDatabase(); BeatmapDB.clearDatabase();
oszUnpacker.unpackAll();
// invoke unpacker
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
} }
beatmapParser.parseAll();
// invoke parser
BeatmapParser.parseAllFiles(beatmapDir);
} }
} }
@ -329,7 +330,7 @@ public class SongMenu extends ComplexOpsuState {
public SongMenu(DisplayContainer displayContainer) { public SongMenu(DisplayContainer displayContainer) {
super(); super();
optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.normalOptions); optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions);
overlays.add(optionsOverlay); overlays.add(optionsOverlay);
} }
@ -599,7 +600,7 @@ public class SongMenu extends ComplexOpsuState {
// song info text // song info text
if (songInfo == null) { if (songInfo == null) {
songInfo = focusNode.getInfo(); songInfo = focusNode.getInfo();
if (Options.useUnicodeMetadata()) { // load glyphs if (OPTION_SHOW_UNICODE.state) {
Beatmap beatmap = focusNode.getBeatmapSet().get(0); Beatmap beatmap = focusNode.getBeatmapSet().get(0);
Fonts.loadGlyphs(Fonts.LARGE, beatmap.titleUnicode); Fonts.loadGlyphs(Fonts.LARGE, beatmap.titleUnicode);
Fonts.loadGlyphs(Fonts.LARGE, beatmap.artistUnicode); Fonts.loadGlyphs(Fonts.LARGE, beatmap.artistUnicode);
@ -740,7 +741,7 @@ public class SongMenu extends ComplexOpsuState {
// initialize song list // initialize song list
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} else } else
MusicController.playThemeSong(); MusicController.playThemeSong(config.themeBeatmap);
reloadThread = null; reloadThread = null;
} }
int mouseX = displayContainer.mouseX; int mouseX = displayContainer.mouseX;
@ -1028,7 +1029,7 @@ public class SongMenu extends ComplexOpsuState {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
if (button != Input.MOUSE_RIGHT_BUTTON) { if (button != Input.MOUSE_RIGHT_BUTTON) {
// view score // view score
instanceContainer.provide(GameRanking.class).setGameData(new GameData(focusScores[rank], displayContainer.width, displayContainer.height)); instanceContainer.provide(GameRanking.class).setGameData(instanceContainer.injectFields(new GameData(focusScores[rank], displayContainer.width, displayContainer.height)));
displayContainer.switchState(GameRanking.class); displayContainer.switchState(GameRanking.class);
} else { } else {
// score management // score management

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -28,8 +27,6 @@ import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.replay.ReplayImporter; import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import java.io.File;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input; import org.newdawn.slick.Input;
@ -38,6 +35,8 @@ import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.inject.Inject; import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.state.BaseOpsuState; import yugecin.opsudance.core.state.BaseOpsuState;
import static yugecin.opsudance.options.Options.*;
/** /**
* "Splash Screen" state. * "Splash Screen" state.
* <p> * <p>
@ -48,6 +47,15 @@ public class Splash extends BaseOpsuState {
@Inject @Inject
private SongMenu songMenu; private SongMenu songMenu;
@Inject
private ReplayImporter replayImporter;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** Whether or not loading has completed. */ /** Whether or not loading has completed. */
private boolean finished; private boolean finished;
@ -78,18 +86,10 @@ public class Splash extends BaseOpsuState {
thread = new Thread() { thread = new Thread() {
@Override @Override
public void run() { public void run() {
File beatmapDir = Options.getBeatmapDir(); oszUnpacker.unpackAll();
beatmapParser.parseAll();
replayImporter.importAll();
// unpack all OSZ archives
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
// parse song directory
BeatmapParser.parseAllFiles(beatmapDir);
// import replays
ReplayImporter.importAllReplaysFromDir(Options.getReplayImportDir());
// load sounds
SoundController.init(); SoundController.init();
finished = true; finished = true;
@ -108,14 +108,14 @@ public class Splash extends BaseOpsuState {
// initialize song list // initialize song list
if (BeatmapSetList.get().size() == 0) { if (BeatmapSetList.get().size() == 0) {
MusicController.playThemeSong(); MusicController.playThemeSong(config.themeBeatmap);
displayContainer.switchStateInstantly(MainMenu.class); displayContainer.switchStateInstantly(MainMenu.class);
return; return;
} }
BeatmapSetList.get().init(); BeatmapSetList.get().init();
if (Options.isThemeSongEnabled()) { if (OPTION_ENABLE_THEME_SONG.state) {
MusicController.playThemeSong(); MusicController.playThemeSong(config.themeBeatmap);
} else { } else {
songMenu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true); songMenu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} }

View File

@ -19,9 +19,7 @@
package itdelatrisu.opsu.ui; package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.skins.Skin;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Point; import java.awt.Point;
@ -29,6 +27,9 @@ import java.util.LinkedList;
import org.newdawn.slick.*; import org.newdawn.slick.*;
import yugecin.opsudance.Dancer; import yugecin.opsudance.Dancer;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
/** /**
* Updates and draws the cursor. * Updates and draws the cursor.
@ -66,11 +67,6 @@ public class Cursor {
private boolean isMirrored; private boolean isMirrored;
private Color filter;
/**
* Constructor.
*/
public Cursor() { public Cursor() {
this(false); this(false);
} }
@ -80,29 +76,25 @@ public class Cursor {
this.isMirrored = isMirrored; this.isMirrored = isMirrored;
} }
public Cursor(Color filter) {
this(false);
this.filter = filter;
}
/** /**
* Draws the cursor. * Draws the cursor.
* @param mousePressed whether or not the mouse button is pressed * @param mousePressed whether or not the mouse button is pressed
*/ */
public void draw(boolean mousePressed) { public void draw(boolean mousePressed) {
if (Options.isCursorDisabled()) if (OPTION_DISABLE_CURSOR.state) {
return; return;
}
// determine correct cursor image // determine correct cursor image
Image cursor, cursorMiddle = null, cursorTrail; Image cursor, cursorMiddle = null, cursorTrail;
boolean beatmapSkinned = GameImage.CURSOR.hasBeatmapSkinImage(); boolean beatmapSkinned = GameImage.CURSOR.hasBeatmapSkinImage();
boolean hasMiddle; boolean hasMiddle;
Skin skin = Options.getSkin();
if (beatmapSkinned) { if (beatmapSkinned) {
newStyle = true; // osu! currently treats all beatmap cursors as new-style cursors newStyle = true; // osu! currently treats all beatmap cursors as new-style cursors
hasMiddle = GameImage.CURSOR_MIDDLE.hasBeatmapSkinImage(); hasMiddle = GameImage.CURSOR_MIDDLE.hasBeatmapSkinImage();
} else } else {
newStyle = hasMiddle = Options.isNewCursorEnabled(); newStyle = hasMiddle = OPTION_NEW_CURSOR.state;
}
if (beatmapSkinned || newStyle) { if (beatmapSkinned || newStyle) {
cursor = GameImage.CURSOR.getImage(); cursor = GameImage.CURSOR.getImage();
cursorTrail = GameImage.CURSOR_TRAIL.getImage(); cursorTrail = GameImage.CURSOR_TRAIL.getImage();
@ -115,7 +107,7 @@ public class Cursor {
// scale cursor // scale cursor
float cursorScaleAnimated = 1f; float cursorScaleAnimated = 1f;
if (skin.isCursorExpanded()) { if (SkinService.skin.isCursorExpanded()) {
if (lastCursorPressState != mousePressed) { if (lastCursorPressState != mousePressed) {
lastCursorPressState = mousePressed; lastCursorPressState = mousePressed;
lastCursorPressTime = System.currentTimeMillis(); lastCursorPressTime = System.currentTimeMillis();
@ -125,7 +117,7 @@ public class Cursor {
Utils.clamp(System.currentTimeMillis() - lastCursorPressTime, 0, CURSOR_SCALE_TIME) / CURSOR_SCALE_TIME); Utils.clamp(System.currentTimeMillis() - lastCursorPressTime, 0, CURSOR_SCALE_TIME) / CURSOR_SCALE_TIME);
cursorScaleAnimated = 1f + ((mousePressed) ? cursorScaleChange : CURSOR_SCALE_CHANGE - cursorScaleChange); cursorScaleAnimated = 1f + ((mousePressed) ? cursorScaleChange : CURSOR_SCALE_CHANGE - cursorScaleChange);
} }
float cursorScale = cursorScaleAnimated * Options.getCursorScale(); float cursorScale = cursorScaleAnimated * OPTION_CURSOR_SIZE.val / 100f;
if (cursorScale != 1f) { if (cursorScale != 1f) {
cursor = cursor.getScaledCopy(cursorScale); cursor = cursor.getScaledCopy(cursorScale);
cursorTrail = cursorTrail.getScaledCopy(cursorScale); cursorTrail = cursorTrail.getScaledCopy(cursorScale);
@ -138,19 +130,15 @@ public class Cursor {
lastCursorColor = filter = Dancer.cursorColorOverride.getColor(); lastCursorColor = filter = Dancer.cursorColorOverride.getColor();
} }
if (this.filter != null) {
filter = this.filter;
}
// draw a fading trail // draw a fading trail
float alpha = 0f; float alpha = 0f;
float t = 2f / trail.size(); float t = 2f / trail.size();
int cursorTrailWidth = cursorTrail.getWidth(), cursorTrailHeight = cursorTrail.getHeight(); int cursorTrailWidth = cursorTrail.getWidth(), cursorTrailHeight = cursorTrail.getHeight();
float cursorTrailRotation = (skin.isCursorTrailRotated()) ? cursorAngle : 0; float cursorTrailRotation = (SkinService.skin.isCursorTrailRotated()) ? cursorAngle : 0;
cursorTrail.startUse(); cursorTrail.startUse();
for (Point p : trail) { for (Point p : trail) {
alpha += t; alpha += t;
cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha * 0.1f); cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha);
cursorTrail.drawEmbedded( cursorTrail.drawEmbedded(
p.x - (cursorTrailWidth / 2f), p.y - (cursorTrailHeight / 2f), p.x - (cursorTrailWidth / 2f), p.y - (cursorTrailHeight / 2f),
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation); cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
@ -161,11 +149,13 @@ public class Cursor {
cursorTrail.endUse(); cursorTrail.endUse();
// draw the other components // draw the other components
if (newStyle && skin.isCursorRotated()) if (newStyle && SkinService.skin.isCursorRotated()) {
cursor.setRotation(cursorAngle); cursor.setRotation(cursorAngle);
cursor.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter); }
if (hasMiddle) cursor.drawCentered(lastPosition.x, lastPosition.y, OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL.state ? Color.white : filter);
cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, Options.isCursorOnlyColorTrail() ? Color.white : filter); if (hasMiddle) {
cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL.state ? Color.white : filter);
}
} }
/** /**
@ -194,7 +184,7 @@ public class Cursor {
removeCount = trail.size() - max; removeCount = trail.size() - max;
} }
int cursortraillength = Options.getCursorTrailOverride(); int cursortraillength = OPTION_DANCE_CURSOR_TRAIL_OVERRIDE.val;
if (cursortraillength > 20) { if (cursortraillength > 20) {
removeCount = trail.size() - cursortraillength; removeCount = trail.size() - cursortraillength;
} }

View File

@ -93,8 +93,9 @@ public class DropdownMenu<E> extends Component {
* @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size * @throws IllegalArgumentException if {@code index} is negative or greater than or equal to size
*/ */
public void setSelectedIndex(int index) { public void setSelectedIndex(int index) {
if (index < 0 || index >= items.length) if (index < 0 || items.length <= index) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
}
this.selectedItemIndex = index; this.selectedItemIndex = index;
} }
@ -252,7 +253,7 @@ public class DropdownMenu<E> extends Component {
return; return;
} }
this.expanded = (idx == -1) && !expanded; this.expanded = (idx == -1) && !expanded;
if (idx >= 0 && selectedItemIndex != idx) { if (0 <= idx && idx < items.length && selectedItemIndex != idx) {
this.selectedItemIndex = idx; this.selectedItemIndex = idx;
itemSelected(idx, items[selectedItemIndex]); itemSelected(idx, items[selectedItemIndex]);
} }

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.ui; package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import java.awt.Font; import java.awt.Font;
import java.awt.FontFormatException; import java.awt.FontFormatException;
@ -35,6 +34,7 @@ import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.font.effects.Effect; import org.newdawn.slick.font.effects.Effect;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader; import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration;
/** /**
* Fonts used for drawing. * Fonts used for drawing.
@ -54,9 +54,9 @@ public class Fonts {
* @throws FontFormatException if any font stream data does not contain the required font tables * @throws FontFormatException if any font stream data does not contain the required font tables
* @throws IOException if a font stream cannot be completely read * @throws IOException if a font stream cannot be completely read
*/ */
public static void init() throws SlickException, FontFormatException, IOException { public static void init(Configuration config) throws SlickException, FontFormatException, IOException {
float fontBase = 12f * GameImage.getUIscale(); float fontBase = 12f * GameImage.getUIscale();
Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(Options.FONT_NAME)); Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(config.FONT_NAME));
Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3)); Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3));
DEFAULT = new UnicodeFont(font); DEFAULT = new UnicodeFont(font);
BOLD = new UnicodeFont(font.deriveFont(Font.BOLD)); BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.ui; package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -28,14 +27,14 @@ import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.ui.BackButton; import yugecin.opsudance.ui.BackButton;
import static yugecin.opsudance.options.Options.*;
/** /**
* Draws common UI components. * Draws common UI components.
*/ */
@ -70,9 +69,6 @@ public class UI {
*/ */
public static void init(DisplayContainer displayContainer) { public static void init(DisplayContainer displayContainer) {
UI.displayContainer = displayContainer; UI.displayContainer = displayContainer;
}
public static void revalidate() {
backButton = new BackButton(displayContainer); backButton = new BackButton(displayContainer);
} }
@ -151,7 +147,7 @@ public class UI {
img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f); img.drawCentered(displayContainer.width - img.getWidth() / 2f + xOffset, displayContainer.height / 2f);
float barHeight = img.getHeight() * 0.9f; float barHeight = img.getHeight() * 0.9f;
float volume = Options.getMasterVolume(); float volume = OPTION_MASTER_VOLUME.val / 100f;
g.setColor(Color.white); g.setColor(Color.white);
g.fillRoundRect( g.fillRoundRect(
displayContainer.width - (img.getWidth() * 0.368f) + xOffset, displayContainer.width - (img.getWidth() * 0.368f) + xOffset,
@ -179,7 +175,8 @@ public class UI {
*/ */
public static void changeVolume(int units) { public static void changeVolume(int units) {
final float UNIT_OFFSET = 0.05f; final float UNIT_OFFSET = 0.05f;
Options.setMasterVolume(Utils.clamp(Options.getMasterVolume() + (UNIT_OFFSET * units), 0f, 1f)); float volume = Utils.clamp(OPTION_MASTER_VOLUME.val / 100f + (UNIT_OFFSET * units), 0f, 1f);
OPTION_MASTER_VOLUME.setValue((int) (volume * 100f));
if (volumeDisplay == -1) if (volumeDisplay == -1)
volumeDisplay = 0; volumeDisplay = 0;
else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10) else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10)
@ -196,6 +193,9 @@ public class UI {
int progress; int progress;
// determine current action // determine current action
//
/*
TODO
if ((file = OszUnpacker.getCurrentFileName()) != null) { if ((file = OszUnpacker.getCurrentFileName()) != null) {
text = "Unpacking new beatmaps..."; text = "Unpacking new beatmaps...";
progress = OszUnpacker.getUnpackerProgress(); progress = OszUnpacker.getUnpackerProgress();
@ -211,12 +211,17 @@ public class UI {
progress = SoundController.getLoadingProgress(); progress = SoundController.getLoadingProgress();
} else } else
return; return;
*/
if (true) {
return; // TODO
}
// draw loading info // draw loading info
float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f; float marginX = displayContainer.width * 0.02f, marginY = displayContainer.height * 0.02f;
float lineY = displayContainer.height - marginY; float lineY = displayContainer.height - marginY;
int lineOffsetY = Fonts.MEDIUM.getLineHeight(); int lineOffsetY = Fonts.MEDIUM.getLineHeight();
if (Options.isLoadVerbose()) { if (OPTION_LOAD_VERBOSE.state) {
// verbose: display percentages and file names // verbose: display percentages and file names
Fonts.MEDIUM.drawString( Fonts.MEDIUM.drawString(
marginX, lineY - (lineOffsetY * 2), marginX, lineY - (lineOffsetY * 2),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -101,6 +101,11 @@ public abstract class Injector implements InstanceContainer, Binder {
return object; return object;
} }
@Override
public <T> T injectFields(T obj) {
return injectFields(obj, obj.getClass());
}
public final <T> Binder<T> bind(Class<T> type) { public final <T> Binder<T> bind(Class<T> type) {
lastType = type; lastType = type;
return this; return this;

View File

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

View File

@ -17,6 +17,10 @@
*/ */
package yugecin.opsudance.core.inject; package yugecin.opsudance.core.inject;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.states.*; import itdelatrisu.opsu.states.*;
import yugecin.opsudance.PreStartupInitializer; import yugecin.opsudance.PreStartupInitializer;
import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.DisplayContainer;
@ -27,10 +31,23 @@ import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
import yugecin.opsudance.core.state.transitions.FadeInTransitionState; import yugecin.opsudance.core.state.transitions.FadeInTransitionState;
import yugecin.opsudance.core.state.transitions.FadeOutTransitionState; import yugecin.opsudance.core.state.transitions.FadeOutTransitionState;
import yugecin.opsudance.core.errorhandling.ErrorHandler; import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionsService;
import yugecin.opsudance.render.GameObjectRenderer;
import yugecin.opsudance.skinning.SkinService;
public class OpsuDanceInjector extends Injector { public class OpsuDanceInjector extends Injector {
protected void configure() { protected void configure() {
bind(Configuration.class).asEagerSingleton();
bind(OptionsService.class).asLazySingleton();
bind(ReplayImporter.class).asLazySingleton();
bind(OszUnpacker.class).asLazySingleton();
bind(BeatmapParser.class).asLazySingleton();
bind(Updater.class).asLazySingleton();
bind(SkinService.class).asEagerSingleton();
bind(PreStartupInitializer.class).asEagerSingleton(); bind(PreStartupInitializer.class).asEagerSingleton();
bind(DisplayContainer.class).asEagerSingleton(); bind(DisplayContainer.class).asEagerSingleton();
@ -44,6 +61,8 @@ public class OpsuDanceInjector extends Injector {
bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton();
bind(FadeOutTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton();
bind(GameObjectRenderer.class).asEagerSingleton();
bind(Splash.class).asEagerSingleton(); bind(Splash.class).asEagerSingleton();
bind(MainMenu.class).asEagerSingleton(); bind(MainMenu.class).asEagerSingleton();
bind(ButtonMenu.class).asEagerSingleton(); bind(ButtonMenu.class).asEagerSingleton();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,40 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
public abstract class GenericOption extends Option {
public int intval;
public String strval;
public boolean boolval;
public GenericOption(String name, String configurationName, String description, int intval, String strval, boolean boolval) {
super(name, configurationName, description);
this.intval = intval;
this.strval = strval;
this.boolval = boolval;
}
@Override
public abstract String getValueString();
@Override
public abstract void read(String s);
@Override
public abstract String write();
}

View File

@ -0,0 +1,33 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
public abstract class ListOption extends Option {
public ListOption(String name, String configurationName, String description) {
super(name, configurationName, description);
}
@Override
public abstract String write();
@Override
public abstract void read(String s);
public abstract Object[] getListItems();
public abstract void clickListItem(int index);
}

View File

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

View File

@ -0,0 +1,102 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.InstanceContainer;
public class Option {
// keep a reference to the instancecontainer so that not every option instance needs to be injected
protected static InstanceContainer instanceContainer;
// caching some commonly used classes
protected static Configuration config;
protected static DisplayContainer displayContainer;
public static void setInstanceContainer(InstanceContainer instanceContainer) {
Option.instanceContainer = instanceContainer;
Option.config = instanceContainer.provide(Configuration.class);
Option.displayContainer = instanceContainer.provide(DisplayContainer.class);
}
public final String name;
public final String configurationName;
public final String description;
/**
* If this option should not be shown in the optionsmenu because it does
* not match the search string.
*/
private boolean filtered;
/**
* Constructor for internal options (not displayed in-game).
*/
public Option(String configurationName) {
this(null, configurationName, null);
}
public Option(String name, String configurationName, String description) {
this.name = name;
this.configurationName = configurationName;
this.description = description;
OptionsService.registerOption(this);
}
/**
* should the option be shown
* @return true if the option should be shown
*/
public boolean showCondition() {
return true;
}
public String getValueString() {
return "";
}
public String write() {
return getValueString();
}
public void read(String s) {
}
/**
* Update the filtered flag for this option based on the given searchString.
* @param searchString the searched string or null to reset the filtered flag
* @return true if this option does need to be filtered
*/
public boolean filter(String searchString) {
if (searchString == null || searchString.length() == 0) {
filtered = false;
return false;
}
filtered = !name.toLowerCase().contains(searchString) && !description.toLowerCase().contains(searchString);
return filtered;
}
/**
* Check if this option should be filtered (= not shown) because it does not
* match the search string.
* @return true if the option shouldn't be shown.
*/
public boolean isFiltered() {
return filtered;
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,118 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
import yugecin.opsudance.events.BubbleNotificationEvent;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class OptionsService {
@Inject
private Configuration config;
public static final HashMap<String, Option> optionMap = new HashMap<>();
@Inject
public OptionsService(InstanceContainer instanceContainer) {
Option.setInstanceContainer(instanceContainer);
}
public static void registerOption(Option option) {
optionMap.put(option.configurationName, option);
}
public void loadOptions() {
// if no config file, use default settings
if (!config.OPTIONS_FILE.isFile()) {
saveOptions();
return;
}
// read file
try (BufferedReader in = new BufferedReader(new FileReader(config.OPTIONS_FILE))) {
String line;
while ((line = in.readLine()) != null) {
line = line.trim();
if (line.length() < 2 || line.charAt(0) == '#') {
continue;
}
int index = line.indexOf('=');
if (index == -1) {
continue;
}
// read option
String name = line.substring(0, index).trim();
Option option = optionMap.get(name);
if (option != null) {
try {
String value = line.substring(index + 1).trim();
option.read(value);
} catch (Exception e) {
Log.warn(String.format("Format error in options file for line: '%s'.", line), e);
}
}
}
} catch (IOException e) {
String err = String.format("Failed to read option file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
}
config.loadDirectories();
}
public void saveOptions() {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(config.OPTIONS_FILE), "utf-8"))) {
// header
SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM dd, yyyy");
String date = dateFormat.format(new Date());
writer.write("# opsu! configuration");
writer.newLine();
writer.write("# last updated on ");
writer.write(date);
writer.newLine();
writer.newLine();
// options
for (Option option : optionMap.values()) {
writer.write(option.configurationName);
writer.write(" = ");
writer.write(option.write());
writer.newLine();
}
writer.close();
} catch (IOException e) {
String err = String.format("Failed to write to file '%s'.", config.OPTIONS_FILE.getAbsolutePath());
Log.error(err, e);
EventBus.post(new BubbleNotificationEvent(err, BubbleNotificationEvent.COMMONCOLOR_RED));
}
}
}

View File

@ -0,0 +1,43 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.options;
public class ToggleOption extends Option {
public boolean state;
public ToggleOption(String name, String configurationName, String description, boolean state) {
super(name, configurationName, description);
this.state = state;
}
public void toggle() {
this.state = !this.state;
}
@Override
public String write() {
return Boolean.toString(state);
}
@Override
public void read(String s) {
state = Boolean.parseBoolean(s);
}
}

View File

@ -0,0 +1,121 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.render;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.skinning.SkinService;
import static yugecin.opsudance.options.Options.*;
public class GameObjectRenderer {
@Inject
private DisplayContainer displayContainer;
private GameData gameData;
private float circleDiameter;
private int circleDiameterInt;
private Image hitcircle;
private Image hitcircleOverlay;
private Image approachCircle;
@Deprecated
public static GameObjectRenderer instance;
public GameObjectRenderer() {
instance = this; // TODO get rid of this
}
public void initForGame(GameData gameData, float circleDiameter) {
this.gameData = gameData;
this.circleDiameter = circleDiameter * HitObject.getXMultiplier(); // convert from Osupixels (640x480)
this.circleDiameterInt = (int) this.circleDiameter;
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(circleDiameterInt, circleDiameterInt));
hitcircle = GameImage.HITCIRCLE.getImage();
hitcircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
approachCircle = GameImage.APPROACHCIRCLE.getImage();
}
public float getCircleDiameter() {
return circleDiameter;
}
public void setGameData(GameData gameData) {
this.gameData = gameData;
}
public void initForFrame() {
if (!OPTION_DANCING_CIRCLES.state) {
return;
}
Float position = MusicController.getBeatProgress();
if (position == null) {
position = 0f;
}
int size = circleDiameterInt + (int) (circleDiameter * OPTION_DANCING_CIRCLES_MULTIPLIER.val / 1000f * AnimationEquation.IN_OUT_QUAD.calc(position));
hitcircle = GameImage.HITCIRCLE.getImage().getScaledCopy(size, size);
hitcircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(size, size);
}
public void renderHitCircle(float x, float y, Color color, int comboNumber, float comboNumberAlpha) {
renderHitCircleOnly(x, y, color);
boolean overlayAboveNumber = SkinService.skin.isHitCircleOverlayAboveNumber();
if (!overlayAboveNumber) {
renderHitCircleOverlayOnly(x, y, Colors.WHITE_FADE);
}
renderComboNumberOnly(x, y, comboNumber, comboNumberAlpha);
if (overlayAboveNumber) {
renderHitCircleOverlayOnly(x, y, Colors.WHITE_FADE);
}
}
public void renderHitCircleOnly(float x, float y, Color color) {
hitcircle.drawCentered(x, y, color);
}
public void renderHitCircleOverlayOnly(float x, float y, Color color) {
hitcircleOverlay.drawCentered(x, y, color);
}
public void renderComboNumberOnly(float x, float y, int number, float alpha) {
if (number > 0) {
gameData.drawSymbolNumber(number, x, y, GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / gameData.getDefaultSymbolImage(0).getHeight(), alpha);
}
}
public void renderApproachCircle(float x, float y, Color color, float approachScale) {
if (!GameMod.HIDDEN.isActive() && OPTION_DANCE_DRAW_APPROACH.state) {
approachCircle.getScaledCopy(approachScale).drawCentered(x, y, color);
}
}
}

View File

@ -0,0 +1,102 @@
/*
* opsu!dance - fork of opsu! with cursordance auto
* Copyright (C) 2017 yugecin
*
* opsu!dance is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu!dance is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
*/
package yugecin.opsudance.skinning;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.skins.Skin;
import itdelatrisu.opsu.skins.SkinLoader;
import org.newdawn.slick.util.ClasspathLocation;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.ResolutionOrSkinChangedEvent;
import yugecin.opsudance.options.Configuration;
import java.io.File;
/**
* @author itdelatrisu (https://github.com/itdelatrisu) most functions are copied from itdelatrisu.opsu.Options.java
*/
public class SkinService {
@Inject
private Configuration config;
public String[] availableSkinDirectories;
public String usedSkinName = "Default";
public static Skin skin;
@Inject
public SkinService() {
}
public void reloadSkin() {
loadSkin();
SoundController.init();
EventBus.post(new ResolutionOrSkinChangedEvent(usedSkinName, -1, -1));
}
/**
* Loads the skin given by the current skin directory.
* If the directory is invalid, the default skin will be loaded.
*/
public void loadSkin() {
File skinDir = getCurrentSkinDirectory();
if (skinDir == null) {
// invalid skin name
usedSkinName = Skin.DEFAULT_SKIN_NAME;
}
// create available skins list
File[] dirs = SkinLoader.getSkinDirectories(config.skinRootDir);
availableSkinDirectories = new String[dirs.length + 1];
availableSkinDirectories[0] = Skin.DEFAULT_SKIN_NAME;
for (int i = 0; i < dirs.length; i++) {
availableSkinDirectories[i + 1] = dirs[i].getName();
}
// set skin and modify resource locations
ResourceLoader.removeAllResourceLocations();
if (skinDir == null) {
skin = new Skin(null);
} else {
// load the skin
skin = SkinLoader.loadSkin(skinDir);
ResourceLoader.addResourceLocation(new FileSystemLocation(skinDir));
}
ResourceLoader.addResourceLocation(new ClasspathLocation());
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
}
/**
* Returns the current skin directory.
* <p>
* NOTE: This directory will differ from that of the currently loaded skin
* if {@link #loadSkin()} has not been called after a directory change.
* Use {@link Skin#getDirectory()} to get the directory of the currently
* loaded skin.
* @return the skin directory, or null for the default skin
*/
public File getCurrentSkinDirectory() {
File dir = new File(config.skinRootDir, usedSkinName);
return (dir.isDirectory()) ? dir : null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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