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)
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.
###Downloads
### Downloads
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).
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!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.**
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.
*/
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.curves.Vec2f;
import org.newdawn.slick.Color;
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.

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package awlex.ospu.spinners;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import yugecin.opsudance.options.Options;
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.Graphics;
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 static yugecin.opsudance.options.Options.*;
/**
* Holds game data and renders all related elements.
*/
public class GameData {
@Inject
private Configuration config;
@Inject
private InstanceContainer instanceContainer;
/** Delta multiplier for steady HP drain. */
public static final float HP_DRAIN_MULTIPLIER = 1 / 200f;
@ -383,7 +396,7 @@ public class GameData {
hitResultCount[HIT_100K] = s.katu;
hitResultCount[HIT_MISS] = s.miss;
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();
}
@ -515,7 +528,7 @@ public class GameData {
if (digitWidth <= 1f) {
return;
}
digitWidth = (digitWidth - Options.getSkin().getHitCircleFontOverlap()) * scale;
digitWidth = (digitWidth - SkinService.skin.getHitCircleFontOverlap()) * scale;
float cx = x + ((length - 1) * (digitWidth / 2));
for (int i = 0; i < length; i++) {
@ -544,7 +557,7 @@ public class GameData {
Image digit = getScoreSymbolImage(c[i]);
if (scale != 1.0f)
digit = digit.getScaledCopy(scale);
cx -= digit.getWidth() + Options.getSkin().getScoreFontOverlap();
cx -= digit.getWidth() + SkinService.skin.getScoreFontOverlap();
digit.setAlpha(alpha);
digit.draw(cx, y);
digit.setAlpha(1f);
@ -557,7 +570,7 @@ public class GameData {
digit.setAlpha(alpha);
digit.draw(cx, y);
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
if (Options.isHitErrorBarEnabled() && !hitErrorList.isEmpty()) {
if (OPTION_SHOW_HIT_ERROR_BAR.state && !hitErrorList.isEmpty()) {
// fade out with last tick
float hitErrorAlpha = 1f;
Color white = new Color(Color.white);
@ -914,10 +927,8 @@ public class GameData {
spinnerOsu.setAlpha(hitResult.alpha);
spinnerOsu.drawCentered(width / 2, height / 4);
spinnerOsu.setAlpha(1f);
}
} else if (OPTION_SHOW_HIT_LIGHTING.state && !hitResult.hideResult && hitResult.result != HIT_MISS &&
// hit lighting
else if (Options.isHitLightingEnabled() && !hitResult.hideResult && hitResult.result != HIT_MISS &&
hitResult.result != HIT_SLIDER30 && hitResult.result != HIT_SLIDER10) {
// TODO: add particle system
Image lighting = GameImage.LIGHTING.getImage();
@ -965,14 +976,14 @@ public class GameData {
private void drawHitAnimations(HitObjectResult hitResult, int trackPosition) {
// fade out slider curve
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) Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME) / HITCIRCLE_FADE_TIME);
float alpha = 1f - progress;
float oldWhiteAlpha = Colors.WHITE_FADE.a;
float oldColorAlpha = hitResult.color.a;
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;
hitResult.color.a = oldColorAlpha;
}
@ -987,7 +998,7 @@ public class GameData {
fc.drawCentered(hitResult.x, hitResult.y);
}
if (!Options.isDrawSliderEndCircles()) {
if (!OPTION_DRAW_SLIDER_ENDCIRCLES.state) {
return;
}
}
@ -1024,7 +1035,7 @@ public class GameData {
}
scaledRepeat.rotate(ang);
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_OVERLAY.getImage().draw(-1000, -1000);
return;
@ -1186,7 +1197,7 @@ public class GameData {
}
// combo burst
if (comboBurstIndex > -1 && Options.isComboBurstEnabled()) {
if (comboBurstIndex > -1 && OPTION_SHOW_COMBO_BURSTS.state) {
int leftX = 0;
int rightX = width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) {
@ -1210,7 +1221,7 @@ public class GameData {
comboPopTime = COMBO_POP_TIME;
// hit error bar
if (Options.isHitErrorBarEnabled()) {
if (OPTION_SHOW_HIT_ERROR_BAR.state) {
int trackPosition = MusicController.getPosition();
Iterator<HitErrorInfo> iter = hitErrorList.iterator();
while (iter.hasNext()) {
@ -1237,23 +1248,24 @@ public class GameData {
comboMax = combo;
// combo bursts (at 30, 60, 100+50x)
if (Options.isComboBurstEnabled() &&
(combo == 30 || combo == 60 || (combo >= 100 && combo % 50 == 0))) {
if (Options.getSkin().isComboBurstRandom())
if (OPTION_SHOW_COMBO_BURSTS.state && (combo == 30 || combo == 60 || (combo >= 100 && combo % 50 == 0))) {
if (SkinService.skin.isComboBurstRandom()) {
comboBurstIndex = (int) (Math.random() * comboBurstImages.length);
else {
if (combo == 30)
} else {
if (combo == 30) {
comboBurstIndex = 0;
else
} else {
comboBurstIndex = (comboBurstIndex + 1) % comboBurstImages.length;
}
}
comboBurstAlpha = 0.8f;
if ((comboBurstIndex % 2) == 0)
if ((comboBurstIndex % 2) == 0) {
comboBurstX = width;
else
} else {
comboBurstX = comboBurstImages[0].getWidth() * -1;
}
}
}
/**
* 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) {
hitResultList.add(new HitObjectResult(time, HIT_SLIDER_REPEAT, x, y, color, type, curve, true, true));
if (!Options.isMirror()) {
if (!OPTION_DANCE_MIRROR.state) {
return;
}
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) {
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;
}
float[] m = Utils.mirrorPoint(x, y);
@ -1338,11 +1350,10 @@ public class GameData {
score += hitValue;
incrementComboStreak();
if (!Options.isPerfectHitBurstEnabled())
; // hide perfect hit results
else
if (OPTION_SHOW_PERFECT_HIT.state) {
hitResultList.add(new HitObjectResult(time, result, x, y, null, HitObjectType.SLIDERTICK, null, false, false));
}
}
fullObjectCount++;
}
@ -1527,7 +1538,7 @@ public class GameData {
if (hitResult == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()))
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));
}

View File

@ -32,8 +32,11 @@ import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.skinning.SkinService;
import yugecin.opsudance.utils.SlickUtil;
import static yugecin.opsudance.options.Options.*;
/**
* Game images.
*/
@ -546,7 +549,7 @@ public enum GameImage {
* and UI scale.
*/
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.
*/
public void setDefaultImage() {
if (defaultImage != null || defaultImages != null || Options.getSkin() == null)
if (defaultImage != null || defaultImages != null || SkinService.skin == null) {
return;
}
// try to load skin images
File skinDir = Options.getSkin().getDirectory();
File skinDir = SkinService.skin.getDirectory();
if (filenameFormat != null) {
if (skinDir != null && ((defaultImages = loadImageArray(skinDir)) != null)) {
isSkinned = true;
@ -739,15 +743,17 @@ public enum GameImage {
* @return true if a new skin image is loaded, false otherwise
*/
public boolean setBeatmapSkinImage(File dir) {
if (dir == null)
if (dir == null) {
return false;
}
// destroy the existing images, if any
destroyBeatmapSkinImage();
// beatmap skins disabled
if (Options.isBeatmapSkinIgnored())
if (OPTION_IGNORE_BEATMAP_SKINS.state) {
return false;
}
// try to load multiple images
if ((skinImages = loadImageArray(dir)) != null) {

File diff suppressed because it is too large Load Diff

View File

@ -18,17 +18,8 @@
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.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.BufferedReader;
import java.io.File;
@ -41,18 +32,13 @@ import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Scanner;
import java.util.jar.JarFile;
import javax.imageio.ImageIO;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
@ -61,21 +47,15 @@ import javax.net.ssl.X509TrustManager;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Animation;
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 com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Options;
/**
* Contains miscellaneous utilities.
@ -104,32 +84,6 @@ public class Utils {
// TODO clean this up
// 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;
}
/**
* 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.
@ -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:
* {@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;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.states.Game;
@ -50,6 +49,8 @@ import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import static yugecin.opsudance.options.Options.*;
/**
* Controller for all music.
*/
@ -109,7 +110,7 @@ public class MusicController {
reset();
System.gc();
switch (BeatmapParser.getExtension(beatmap.audioFilename.getName())) {
switch (Utils.getFileExtension(beatmap.audioFilename.getName())) {
case "ogg":
case "mp3":
trackLoader = new Thread() {
@ -168,7 +169,7 @@ public class MusicController {
*/
public static void playAt(final int position, final boolean loop) {
if (trackExists()) {
setVolume(Options.getMusicVolume() * Options.getMasterVolume());
setVolume(OPTION_MUSIC_VOLUME.val / 100f * OPTION_MASTER_VOLUME.val / 100f);
trackEnded = false;
pauseTime = 0f;
resetTimingPoint();
@ -348,9 +349,9 @@ public class MusicController {
*/
public static int getPosition() {
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())
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
return 0;
}
@ -443,13 +444,9 @@ public class MusicController {
playAt((preview) ? lastBeatmap.previewTime : 0, false);
}
/**
* Plays the theme song.
*/
public static void playThemeSong() {
Beatmap beatmap = Options.getThemeBeatmap();
if (beatmap != null) {
play(beatmap, false, false);
public static void playThemeSong(Beatmap themeBeatmap) {
if (themeBeatmap != null) {
play(themeBeatmap, false, false);
themePlaying = true;
}
}
@ -470,7 +467,7 @@ public class MusicController {
* @param multiplier the volume multiplier when the track is dimmed
*/
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;
trackDimmed = !trackDimmed;
setVolume(volume);

View File

@ -18,12 +18,10 @@
package itdelatrisu.opsu.audio;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.HitSound.SampleSet;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.Download.DownloadListener;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.io.IOException;
@ -44,6 +42,10 @@ import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.events.BarNotificationEvent;
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.
@ -190,7 +192,7 @@ public class SoundController {
*/
private static String getSoundFileName(String 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) {
File skinWAV = new File(skinDir, wav), skinMP3 = new File(skinDir, mp3);
if (skinWAV.isFile())
@ -209,8 +211,9 @@ public class SoundController {
* Loads all sound files.
*/
public static void init() {
if (Options.isSoundDisabled())
if (OPTION_DISABLE_SOUNDS.state) {
return;
}
currentFileIndex = 0;
@ -290,7 +293,7 @@ public class SoundController {
* @param s the sound effect
*/
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)
return;
if (Options.getSampleVolumeOverride() > 0) {
sampleVolumeMultiplier = Options.getSampleVolumeOverride();
if (OPTION_SAMPLE_VOLUME_OVERRIDE.val > 0) {
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)
return;
// play all sounds
if (hitSound == HitObject.SOUND_NORMAL || Options.getSkin().isLayeredHitSounds()) {
if (hitSound == HitObject.SOUND_NORMAL || SkinService.skin.isLayeredHitSounds()) {
HitSound.setSampleSet(sampleSet);
playClip(HitSound.NORMAL.getClip(), volume, null);
}
@ -333,7 +336,7 @@ public class SoundController {
* @param s the hit sound
*/
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
* @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 {
// stop previous track
stopTrack();
// download new track
File dir = Options.TEMP_DIR;
if (!dir.isDirectory())
File dir = config.TEMP_DIR;
if (!dir.isDirectory()) {
dir.mkdir();
}
String filename = String.format("%s.%s", name, isMP3 ? "mp3" : "wav");
final File downloadFile = new File(dir, filename);
boolean complete;
@ -406,7 +410,7 @@ public class SoundController {
try {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(downloadFile);
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;
} catch (Exception e) {
throw new SlickException(String.format("Failed to load clip '%s'.", url));

View File

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

View File

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

View File

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

View File

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

View File

@ -18,8 +18,6 @@
package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.Options;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
@ -42,7 +40,11 @@ import java.util.concurrent.Executors;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.InstanceContainer;
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.
@ -88,14 +90,14 @@ public class BeatmapWatchService {
* Creates a new watch service instance (overwriting any previous instance),
* registers the beatmap directory, and starts processing events.
*/
public static void create() {
public static void create(InstanceContainer instanceContainer) {
// close the existing watch service
destroy();
// create a new watch service
try {
ws = new BeatmapWatchService();
ws.register(Options.getBeatmapDir().toPath());
ws = instanceContainer.provide(BeatmapWatchService.class);
ws.register(instanceContainer.provide(Configuration.class).beatmapDir.toPath());
} catch (IOException e) {
Log.error("Could not create watch service", e);
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. */
public interface BeatmapWatchServiceListener {

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.downloads.Download.DownloadListener;
@ -35,13 +34,21 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import yugecin.opsudance.options.Configuration;
import static yugecin.opsudance.options.Options.*;
/**
* Node containing song data and a Download object.
*/
public class DownloadNode {
@Inject
private Configuration config;
/** The associated Download object. */
private Download download;
@ -272,7 +279,7 @@ public class DownloadNode {
String url = server.getDownloadURL(beatmapSetID);
if (url == null)
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);
Download download = new Download(url, path, rename);
download.setListener(new DownloadListener() {
@ -287,9 +294,10 @@ public class DownloadNode {
}
});
this.download = download;
if (Options.useUnicodeMetadata()) // load glyphs
if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.LARGE, getTitle());
}
}
/**
* 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.
*/
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.
*/
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
// 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, getArtist());
}

View File

@ -18,10 +18,8 @@
package itdelatrisu.opsu.downloads;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.Download.DownloadListener;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import java.io.IOException;
@ -39,23 +37,27 @@ import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.events.EventBus;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
/**
* Handles automatic program updates.
*/
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. */
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. */
public enum Status {
INITIAL (""),
@ -117,11 +119,10 @@ public class Updater {
return currentVersion.getMajorVersion() + "." + currentVersion.getMinorVersion() + "." + currentVersion.getIncrementalVersion();
}
/**
* Constructor.
*/
private Updater() {
@Inject
public Updater() {
status = Status.INITIAL;
updater = this;
}
/**
@ -144,7 +145,7 @@ public class Updater {
Date date = null;
try {
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
String build = props.getProperty("build.date");
if (build == null || build.equals("${timestamp}") || build.equals("${maven.build.timestamp}"))
date = new Date();
@ -206,23 +207,23 @@ public class Updater {
* @throws IOException if an I/O exception occurs
*/
public void checkForUpdates() throws IOException {
if (status != Status.INITIAL || Options.USE_XDG)
if (status != Status.INITIAL || config.USE_XDG)
return;
status = Status.CHECKING;
// get current version
Properties props = new Properties();
props.load(ResourceLoader.getResourceAsStream(Options.VERSION_FILE));
props.load(ResourceLoader.getResourceAsStream(config.VERSION_FILE));
if ((currentVersion = getVersion(props)) == null)
return;
// get latest version
String s = null;
try {
s = Utils.readDataFromUrl(new URL(Options.VERSION_REMOTE));
s = Utils.readDataFromUrl(new URL(config.VERSION_REMOTE));
} 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) {
status = Status.CONNECTION_ERROR;

View File

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

View File

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

View File

@ -30,11 +30,17 @@ import java.net.URLEncoder;
import org.json.JSONArray;
import org.json.JSONObject;
import yugecin.opsudance.core.errorhandling.ErrorHandler;
import yugecin.opsudance.core.inject.Inject;
import yugecin.opsudance.core.inject.InstanceContainer;
/**
* Download server: http://osu.mengsky.net/
*/
public class MengSkyServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */
private static final String SERVER_NAME = "MengSky";
@ -50,8 +56,9 @@ public class MengSkyServer extends DownloadServer {
/** Total result count from the last query. */
private int totalResults = -1;
/** Constructor. */
public MengSkyServer() {}
@Inject
public MengSkyServer() {
}
@Override
public String getName() { return SERVER_NAME; }
@ -86,19 +93,18 @@ public class MengSkyServer extends DownloadServer {
// sometimes titleU is artistU instead of the proper title
if (titleU.equals(artistU) && !titleU.equals(title))
titleU = title;
nodes[i] = new DownloadNode(
nodes[i] = instanceContainer.injectFields(new DownloadNode(
item.getInt("id"), item.getString("syncedDateTime"),
title, titleU, artist, artistU, creator
);
));
}
// store total result count
int pageTotal = json.getInt("pageTotal");
int resultCount = nodes.length;
if (page == pageTotal)
resultCount = nodes.length + (pageTotal - 1) * PAGE_LIMIT;
else
resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
int resultCount = 1 + (pageTotal - 1) * PAGE_LIMIT;
if (page == pageTotal) {
resultCount += nodes.length - 1;
}
this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) {
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.downloads.DownloadNode;
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.UnsupportedEncodingException;
@ -36,6 +38,10 @@ import java.util.regex.Pattern;
* Download server: http://osu.uu.gl/
*/
public class MnetworkServer extends DownloadServer {
@Inject
private InstanceContainer instanceContainer;
/** Server name. */
private static final String SERVER_NAME = "Mnetwork";
@ -51,8 +57,9 @@ public class MnetworkServer extends DownloadServer {
/** Beatmap pattern. */
private Pattern BEATMAP_PATTERN = Pattern.compile("^(\\d+) ([^-]+) - (.+)\\.osz$");
/** Constructor. */
public MnetworkServer() {}
@Inject
public MnetworkServer() {
}
@Override
public String getName() { return SERVER_NAME; }
@ -112,7 +119,7 @@ public class MnetworkServer extends DownloadServer {
if (!m.matches())
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()]);

View File

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

View File

@ -34,11 +34,17 @@ import java.util.List;
import org.json.JSONObject;
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/
*/
public class YaSOnlineServer extends DownloadServer {
@Inject
public InstanceContainer instanceContainer;
/** Server name. */
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). */
private int maxServerID = 0;
/** Constructor. */
public YaSOnlineServer() {}
@Inject
public YaSOnlineServer() {
}
@Override
public String getName() { return SERVER_NAME; }
@ -176,7 +183,7 @@ public class YaSOnlineServer extends DownloadServer {
if (serverID > maxServerID)
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()]);

View File

@ -20,9 +20,7 @@ package itdelatrisu.opsu.objects;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.curves.Vec2f;
@ -30,16 +28,20 @@ import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
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.
*/
public class Circle extends GameObject {
/** The diameter of hit circles. */
public static float diameter;
@Inject
private GameObjectRenderer gameObjectRenderer;
/** The associated HitObject. */
private HitObject hitObject;
@ -62,18 +64,6 @@ public class Circle extends GameObject {
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.
* @param hitObject the associated HitObject
@ -129,16 +119,10 @@ public class Circle extends GameObject {
float oldAlpha = Colors.WHITE_FADE.a;
Colors.WHITE_FADE.a = color.a = alpha;
if (timeDiff >= 0 && !GameMod.HIDDEN.isActive() && Options.isDrawApproach())
GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(x, y, color);
GameImage.HITCIRCLE.getImage().drawCentered(x, y, color);
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber();
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);
if (timeDiff >= 0) {
gameObjectRenderer.renderApproachCircle(x, y, color, approachScale);
}
gameObjectRenderer.renderHitCircle(x, y, color, hitObject.getComboNumber(), alpha);
Colors.WHITE_FADE.a = oldAlpha;
@ -172,7 +156,7 @@ public class Circle extends GameObject {
@Override
public boolean mousePressed(int x, int y, int trackPosition) {
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 result = hitResult(timeDiff);
@ -195,7 +179,7 @@ public class Circle extends GameObject {
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
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);
if (Options.isMirror() && GameMod.AUTO.isActive()) {
if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
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);
}
@ -210,7 +194,7 @@ public class Circle extends GameObject {
else if (isAutoMod) {
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);
if (Options.isMirror() && GameMod.AUTO.isActive()) {
if (OPTION_DANCE_MIRROR.state && GameMod.AUTO.isActive()) {
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);
}

View File

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

View File

@ -22,7 +22,6 @@ import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
@ -32,10 +31,10 @@ import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Colors;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.skinning.SkinService;
/**
* 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);
// darken screen
if (Options.getSkin().isSpinnerFadePlayfield()) {
if (SkinService.skin.isSpinnerFadePlayfield()) {
float oldAlpha = Colors.BLACK_ALPHA.a;
if (timeDiff > 0)
Colors.BLACK_ALPHA.a *= alpha;

View File

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

View File

@ -18,15 +18,15 @@
package itdelatrisu.opsu.render;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.curves.Vec2f;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.EXTFramebufferObject;
@ -37,6 +37,9 @@ import org.lwjgl.opengl.GL20;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
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
@ -69,6 +72,8 @@ public class CurveRenderState {
private int spliceFrom;
private int spliceTo;
protected List<Integer> pointsToRender;
private final int mirrors;
/**
@ -96,7 +101,7 @@ public class CurveRenderState {
*/
public static void shutdown() {
staticState.shutdown();
//FrameBufferCache.shutdown();
FrameBufferCache.shutdown();
}
/**
@ -108,7 +113,7 @@ public class CurveRenderState {
this.hitObject = hitObject;
this.curve = curve;
if (isKnorkeSlider) {
this.mirrors = Options.getMergingSlidersMirrorPool();
this.mirrors = OPTION_MERGING_SLIDERS_MIRROR_POOL.val;
} else {
this.mirrors = 1;
}
@ -138,6 +143,14 @@ public class CurveRenderState {
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
* 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_y = y - 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);
y = (float) (y - diff_y / 2);
} else {
@ -344,11 +357,15 @@ public class CurveRenderState {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
}
int max = mirrors;
if (!Options.isMirror()) {
if (!OPTION_DANCE_MIRROR.state) {
max = 1;
}
for (int i = 0; i < max; i++) {
if (pointsToRender == null) {
renderCurve(from, to, i);
} else {
renderCurve(i);
}
}
GL11.glFlush();
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
* that has its center at the coordinates {@code (x1,y1)}.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
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.state.BaseOpsuState;
import static yugecin.opsudance.options.Options.*;
/**
* "Game Pause/Fail" state.
* <p>
@ -93,9 +94,9 @@ public class GamePauseMenu extends BaseOpsuState {
// game keys
if (!Keyboard.isRepeatEvent()) {
if (key == Options.getGameKeyLeft()) {
if (key == OPTION_KEY_LEFT.intval) {
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);
}
}
@ -166,7 +167,7 @@ public class GamePauseMenu extends BaseOpsuState {
return true;
}
if (Options.isMouseWheelDisabled()) {
if (OPTION_DISABLE_MOUSE_WHEEL.state) {
return true;
}

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
@ -54,6 +53,8 @@ import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.events.BubbleNotificationEvent;
import static yugecin.opsudance.options.Options.*;
/**
* "Main Menu" state.
* <p>
@ -64,6 +65,9 @@ public class MainMenu extends BaseOpsuState {
@Inject
private InstanceContainer instanceContainer;
@Inject
private Updater updater;
/** Idle time, in milliseconds, before returning the logo to its original position. */
private static final short LOGO_IDLE_DELAY = 10000;
@ -232,7 +236,7 @@ public class MainMenu extends BaseOpsuState {
// draw background
Beatmap beatmap = MusicController.getBeatmap();
if (Options.isDynamicBackgroundEnabled() &&
if (OPTION_DYNAMIC_BACKGROUND.state &&
beatmap != null && beatmap.drawBackground(width, height, bgAlpha.getValue(), true))
;
else {
@ -262,7 +266,7 @@ public class MainMenu extends BaseOpsuState {
}
// 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();
boolean renderPiece = position != null;
if (position == null) {
@ -315,13 +319,14 @@ public class MainMenu extends BaseOpsuState {
}
// draw update button
if (Updater.get().showButton()) {
Updater.Status status = Updater.get().getStatus();
if (status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING)
if (updater.showButton()) {
Updater.Status status = updater.getStatus();
if (status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) {
updateButton.draw();
else if (status == Updater.Status.UPDATE_DOWNLOADED)
} else if (status == Updater.Status.UPDATE_DOWNLOADED) {
restartButton.draw();
}
}
// draw text
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;
g.drawString(String.format("Loaded %d songs and %d beatmaps.",
BeatmapSetList.get().getMapSetCount(), BeatmapSetList.get().getMapCount()), marginX, topMarginY);
if (MusicController.isTrackLoading())
if (MusicController.isTrackLoading()) {
g.drawString("Track loading...", marginX, topMarginY + lineHeight);
else if (MusicController.trackExists()) {
if (Options.useUnicodeMetadata()) { // load glyphs
} else if (MusicController.trackExists()) {
if (OPTION_SHOW_UNICODE.state) {
Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.titleUnicode);
Fonts.loadGlyphs(Fonts.MEDIUM, beatmap.artistUnicode);
}
@ -365,7 +370,7 @@ public class MainMenu extends BaseOpsuState {
repoButton.hoverUpdate(delta, mouseX, mouseY);
danceRepoButton.hoverUpdate(delta, mouseX, mouseY);
}
if (Updater.get().showButton()) {
if (updater.showButton()) {
updateButton.autoHoverUpdate(delta, true);
restartButton.autoHoverUpdate(delta, false);
}
@ -387,7 +392,7 @@ public class MainMenu extends BaseOpsuState {
// fade in background
Beatmap beatmap = MusicController.getBeatmap();
if (!(Options.isDynamicBackgroundEnabled() && beatmap != null && beatmap.isBackgroundLoading()))
if (!(OPTION_DYNAMIC_BACKGROUND.state && beatmap != null && beatmap.isBackgroundLoading()))
bgAlpha.update(delta);
// check measure progress
@ -445,8 +450,8 @@ public class MainMenu extends BaseOpsuState {
UI.updateTooltip(delta, "Next track", false);
else if (musicPrevious.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Previous track", false);
else if (Updater.get().showButton()) {
Updater.Status status = Updater.get().getStatus();
else if (updater.showButton()) {
Updater.Status status = updater.getStatus();
if (((status == Updater.Status.UPDATE_AVAILABLE || status == Updater.Status.UPDATE_DOWNLOADING) && updateButton.contains(mouseX, mouseY)) ||
(status == Updater.Status.UPDATE_DOWNLOADED && restartButton.contains(mouseX, mouseY)))
UI.updateTooltip(delta, status.getDescription(), true);
@ -466,10 +471,10 @@ public class MainMenu extends BaseOpsuState {
UI.enter();
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."));
enterNotification = true;
} else if (Updater.get().justUpdated()) {
} else if (updater.justUpdated()) {
EventBus.post(new BarNotificationEvent("opsu! is now up to date!"));
enterNotification = true;
}
@ -547,10 +552,12 @@ public class MainMenu extends BaseOpsuState {
lastMeasureProgress = 0f;
if (!previous.isEmpty()) {
instanceContainer.provide(SongMenu.class).setFocus(BeatmapSetList.get().getBaseNode(previous.pop()), -1, true, false);
if (Options.isDynamicBackgroundEnabled())
if (OPTION_DYNAMIC_BACKGROUND.state) {
bgAlpha.setTime(0);
} else
}
} else {
MusicController.setPosition(0);
}
EventBus.post(new BarNotificationEvent("<< Previous"));
return true;
}
@ -565,7 +572,7 @@ public class MainMenu extends BaseOpsuState {
// repository button actions
if (repoButton != null && repoButton.contains(x, y)) {
try {
Desktop.getDesktop().browse(Options.REPOSITORY_URI);
Desktop.getDesktop().browse(config.REPOSITORY_URI);
} catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened."));
} catch (IOException e) {
@ -577,7 +584,7 @@ public class MainMenu extends BaseOpsuState {
if (danceRepoButton != null && danceRepoButton.contains(x, y)) {
try {
Desktop.getDesktop().browse(Options.DANCE_REPOSITORY_URI);
Desktop.getDesktop().browse(config.DANCE_REPOSITORY_URI);
} catch (UnsupportedOperationException e) {
EventBus.post(new BarNotificationEvent("The repository web page could not be opened."));
} catch (IOException e) {
@ -588,11 +595,11 @@ public class MainMenu extends BaseOpsuState {
}
// update button actions
if (Updater.get().showButton()) {
Updater.Status status = Updater.get().getStatus();
if (updater.showButton()) {
Updater.Status status = updater.getStatus();
if (updateButton.contains(x, y) && status == Updater.Status.UPDATE_AVAILABLE) {
SoundController.playSound(SoundEffect.MENUHIT);
Updater.get().startDownload();
updater.startDownload();
updateButton.removeHoverEffects();
updateButton.setHoverAnimationDuration(800);
updateButton.setHoverAnimationEquation(AnimationEquation.IN_OUT_QUAD);
@ -600,7 +607,7 @@ public class MainMenu extends BaseOpsuState {
return true;
} else if (restartButton.contains(x, y) && status == Updater.Status.UPDATE_DOWNLOADED) {
SoundController.playSound(SoundEffect.MENUHIT);
Updater.get().prepareUpdate();
updater.prepareUpdate();
displayContainer.exitRequested = true;
return true;
}
@ -719,9 +726,10 @@ public class MainMenu extends BaseOpsuState {
if (!isTheme && !sameAudio)
previous.add(node.index);
}
if (Options.isDynamicBackgroundEnabled() && !sameAudio && !MusicController.isThemePlaying())
if (OPTION_DYNAMIC_BACKGROUND.state && !sameAudio && !MusicController.isThemePlaying()) {
bgAlpha.setTime(0);
}
}
/**
* 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;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.*;
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.MusicController;
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.state.ComplexOpsuState;
import yugecin.opsudance.events.BarNotificationEvent;
import yugecin.opsudance.options.Configuration;
import yugecin.opsudance.options.OptionGroups;
import yugecin.opsudance.ui.OptionsOverlay;
import static yugecin.opsudance.options.Options.*;
/**
* "Song Selection" state.
* <p>
@ -87,6 +86,15 @@ public class SongMenu extends ComplexOpsuState {
@Inject
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. */
public static final int MAX_SONG_BUTTONS = 6;
@ -236,18 +244,11 @@ public class SongMenu extends ComplexOpsuState {
/** Reloads all beatmaps. */
private void reloadBeatmaps() {
File beatmapDir = Options.getBeatmapDir();
if (fullReload) {
// clear the beatmap cache
BeatmapDB.clearDatabase();
// invoke unpacker
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
oszUnpacker.unpackAll();
}
// invoke parser
BeatmapParser.parseAllFiles(beatmapDir);
beatmapParser.parseAll();
}
}
@ -329,7 +330,7 @@ public class SongMenu extends ComplexOpsuState {
public SongMenu(DisplayContainer displayContainer) {
super();
optionsOverlay = new OptionsOverlay(displayContainer, OptionsMenu.normalOptions);
optionsOverlay = new OptionsOverlay(displayContainer, OptionGroups.normalOptions);
overlays.add(optionsOverlay);
}
@ -599,7 +600,7 @@ public class SongMenu extends ComplexOpsuState {
// song info text
if (songInfo == null) {
songInfo = focusNode.getInfo();
if (Options.useUnicodeMetadata()) { // load glyphs
if (OPTION_SHOW_UNICODE.state) {
Beatmap beatmap = focusNode.getBeatmapSet().get(0);
Fonts.loadGlyphs(Fonts.LARGE, beatmap.titleUnicode);
Fonts.loadGlyphs(Fonts.LARGE, beatmap.artistUnicode);
@ -740,7 +741,7 @@ public class SongMenu extends ComplexOpsuState {
// initialize song list
setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
} else
MusicController.playThemeSong();
MusicController.playThemeSong(config.themeBeatmap);
reloadThread = null;
}
int mouseX = displayContainer.mouseX;
@ -1028,7 +1029,7 @@ public class SongMenu extends ComplexOpsuState {
SoundController.playSound(SoundEffect.MENUHIT);
if (button != Input.MOUSE_RIGHT_BUTTON) {
// 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);
} else {
// score management

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.BeatmapParser;
@ -28,8 +27,6 @@ import itdelatrisu.opsu.beatmap.OszUnpacker;
import itdelatrisu.opsu.replay.ReplayImporter;
import itdelatrisu.opsu.ui.UI;
import java.io.File;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
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.state.BaseOpsuState;
import static yugecin.opsudance.options.Options.*;
/**
* "Splash Screen" state.
* <p>
@ -48,6 +47,15 @@ public class Splash extends BaseOpsuState {
@Inject
private SongMenu songMenu;
@Inject
private ReplayImporter replayImporter;
@Inject
private OszUnpacker oszUnpacker;
@Inject
private BeatmapParser beatmapParser;
/** Whether or not loading has completed. */
private boolean finished;
@ -78,18 +86,10 @@ public class Splash extends BaseOpsuState {
thread = new Thread() {
@Override
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();
finished = true;
@ -108,14 +108,14 @@ public class Splash extends BaseOpsuState {
// initialize song list
if (BeatmapSetList.get().size() == 0) {
MusicController.playThemeSong();
MusicController.playThemeSong(config.themeBeatmap);
displayContainer.switchStateInstantly(MainMenu.class);
return;
}
BeatmapSetList.get().init();
if (Options.isThemeSongEnabled()) {
MusicController.playThemeSong();
if (OPTION_ENABLE_THEME_SONG.state) {
MusicController.playThemeSong(config.themeBeatmap);
} else {
songMenu.setFocus(BeatmapSetList.get().getRandomNode(), -1, true, true);
}

View File

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

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.ui;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import java.awt.Font;
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.util.Log;
import org.newdawn.slick.util.ResourceLoader;
import yugecin.opsudance.options.Configuration;
/**
* 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 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();
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));
DEFAULT = new UnicodeFont(font);
BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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