diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 00f7c640..f60a4050 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -4,3 +4,11 @@
- APK: https://github.com/fluddokt/opsu/issues
- Play Store: https://github.com/AnirudhRahul/opsu-Android/issues
-->
+
+**Operations:** What did you do
+
+**Expected behavior:** What do you expect the program to do
+
+**Actual behaviour:** What did the program actually do
+
+**Extra information:** Platform, version, screenshots, logs, etc.
diff --git a/.gitignore b/.gitignore
index 77a7f941..148c5b18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
/SongPacks/
/Songs/
/Temp/
+*.log
/.opsu.log
/.opsu.cfg
/.opsu.db*
diff --git a/README.md b/README.md
index cd70e83b..6aa806d8 100644
--- a/README.md
+++ b/README.md
@@ -51,8 +51,7 @@ The following files and folders will be created by opsu! as needed:
## Building
-opsu! is distributed as both a [Maven](https://maven.apache.org/) and
-[Gradle](https://gradle.org/) project.
+opsu! is distributed as both a [Maven](https://maven.apache.org/) and [Gradle](https://gradle.org/) project.
### Maven
diff --git a/build.gradle b/build.gradle
index 5f8756ff..7ca0c974 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ apply plugin: 'application'
import org.apache.tools.ant.filters.*
group = 'itdelatrisu'
-version = '0.16.1'
+version = '0.16.2'
mainClassName = 'itdelatrisu.opsu.Opsu'
buildDir = new File(rootProject.projectDir, "build/")
diff --git a/pom.xml b/pom.xml
index f73e8eb9..882a9d9a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
itdelatrisu
opsu
- 0.16.1
+ 0.16.2
${project.version}
${maven.build.timestamp}
diff --git a/res/search-background.jpg b/res/search-background.jpg
deleted file mode 100644
index aee382af..00000000
Binary files a/res/search-background.jpg and /dev/null differ
diff --git a/res/selection-mod-cinema.png b/res/selection-mod-cinema.png
new file mode 100644
index 00000000..035d4051
Binary files /dev/null and b/res/selection-mod-cinema.png differ
diff --git a/res/selection-mod-nightcore.png b/res/selection-mod-nightcore.png
new file mode 100644
index 00000000..8df73d48
Binary files /dev/null and b/res/selection-mod-nightcore.png differ
diff --git a/res/selection-mod-perfect.png b/res/selection-mod-perfect.png
new file mode 100644
index 00000000..36e1965a
Binary files /dev/null and b/res/selection-mod-perfect.png differ
diff --git a/res/selection-mod-target.png b/res/selection-mod-target.png
new file mode 100644
index 00000000..7a2b3c1c
Binary files /dev/null and b/res/selection-mod-target.png differ
diff --git a/res/version b/res/version
index 399a7f01..48ed926d 100644
--- a/res/version
+++ b/res/version
@@ -1,3 +1,3 @@
version=${version}
-file=https://github.com/itdelatrisu/opsu/releases/download/${version}/opsu-${version}.jar
+file=https://github.com/clonewith/opsu/releases/download/${version}/opsu-${version}.jar
build.date=${timestamp}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java
index 1e794f59..b8e3d3e8 100644
--- a/src/itdelatrisu/opsu/GameData.java
+++ b/src/itdelatrisu/opsu/GameData.java
@@ -1402,7 +1402,12 @@ private void resetComboStreak() {
SoundController.playSound(SoundEffect.COMBOBREAK);
}
combo = 0;
- if (GameMod.SUDDEN_DEATH.isActive())
+ if (GameMod.SUDDEN_DEATH.isActive() || GameMod.PERFECT.isActive())
+ health.setHealth(0f);
+ }
+
+ private void detectPerfectStreak() {
+ if (GameMod.PERFECT.isActive())
health.setHealth(0f);
}
@@ -1577,10 +1582,12 @@ private int handleHitResult(int time, int result, float x, float y, Color color,
case HIT_100:
hitValue = 100;
comboEnd |= 1;
+ detectPerfectStreak();
break;
case HIT_50:
hitValue = 50;
comboEnd |= 2;
+ detectPerfectStreak();
break;
case HIT_MISS:
hitValue = 0;
diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java
index 23a50906..80b5cf14 100644
--- a/src/itdelatrisu/opsu/GameImage.java
+++ b/src/itdelatrisu/opsu/GameImage.java
@@ -34,6 +34,9 @@
* Game images.
*/
public enum GameImage {
+ // Launch
+ // WELCOME_TEXT ("welcome-text", "png"),
+
// Cursor
CURSOR ("cursor", "png"),
CURSOR_MIDDLE ("cursormiddle", "png"),
@@ -81,6 +84,7 @@ protected Image process_sub(Image img, int w, int h) {
FOLLOWPOINT ("followpoint", "png"),
// Game Pause/Fail
+ // PAUSE_ARROW ("arrow-pause", "png"),
PAUSE_CONTINUE ("pause-continue", "png"),
PAUSE_RETRY ("pause-retry", "png"),
PAUSE_BACK ("pause-back", "png"),
@@ -113,7 +117,7 @@ protected Image process_sub(Image img, int w, int h) {
REVERSEARROW ("reversearrow", "png"),
SLIDER_TICK ("sliderscorepoint", "png"),
- // Spinner
+ // Old Spinner
SPINNER_CIRCLE ("spinner-circle", "png"),
SPINNER_APPROACHCIRCLE ("spinner-approachcircle", "png") {
@Override
@@ -137,6 +141,12 @@ protected Image process_sub(Image img, int w, int h) {
}
},
+ // New Spinner
+ // SPINNER_TOP ("spinner-top", "png"),
+ // SPINNER_MIDDLE ("spinner-middle", "png"),
+ // SPINNER_BOTTOM ("spinner-bottom", "png"),
+ // SPINNER_GROW ("spinner-grow", "png"),
+
// Game Data
COMBO_BURST ("comboburst", "comboburst-%d", "png"),
SCOREBAR_BG ("scorebar-bg", "png"),
@@ -207,14 +217,18 @@ protected Image process_sub(Image img, int w, int h) {
MOD_NO_FAIL ("selection-mod-nofail", "png", false, false),
MOD_HARD_ROCK ("selection-mod-hardrock", "png", false, false),
MOD_SUDDEN_DEATH ("selection-mod-suddendeath", "png", false, false),
+ MOD_PERFECT ("selection-mod-perfect", "png", false, false),
MOD_SPUN_OUT ("selection-mod-spunout", "png", false, false),
MOD_AUTO ("selection-mod-autoplay", "png", false, false),
MOD_HALF_TIME ("selection-mod-halftime", "png", false, false),
MOD_DOUBLE_TIME ("selection-mod-doubletime", "png", false, false),
+ MOD_NIGHTCORE ("selection-mod-nightcore", "png", false, false),
MOD_HIDDEN ("selection-mod-hidden", "png", false, false),
MOD_FLASHLIGHT ("selection-mod-flashlight", "png", false, false),
MOD_RELAX ("selection-mod-relax", "png", false, false),
MOD_AUTOPILOT ("selection-mod-relax2", "png", false, false),
+ MOD_CINEMA ("selection-mod-cinema", "png", false, false),
+ MOD_TARGET ("selection-mod-target", "png", false, false),
// Selection Buttons
SELECTION_MODS ("selection-mods", "png", false, false),
@@ -316,13 +330,6 @@ protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy((h * 0.45f) / img.getHeight());
}
},
- SEARCH_BG ("search-background", "png|jpg", false, true) {
- @Override
- protected Image process_sub(Image img, int w, int h) {
- img.setAlpha(0.8f);
- return img.getScaledCopy(w, h);
- }
- },
DELETE ("delete", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
@@ -864,4 +871,4 @@ private void process() {
} else
setImage(process_sub(getImage(), unscaledWidth, UNSCALED_HEIGHT).getScaledCopy(getUIscale()));
}
-}
\ No newline at end of file
+}
diff --git a/src/itdelatrisu/opsu/GameMod.java b/src/itdelatrisu/opsu/GameMod.java
index 5e390a56..1fd3e43c 100644
--- a/src/itdelatrisu/opsu/GameMod.java
+++ b/src/itdelatrisu/opsu/GameMod.java
@@ -33,6 +33,7 @@
* Game mods.
*/
public enum GameMod {
+ // TODO: Process PF, NC and TP approxmiately
EASY (Category.EASY, 0, GameImage.MOD_EASY, "EZ", 2, Input.KEY_Q, 0.5f,
"Easy", "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."),
NO_FAIL (Category.EASY, 1, GameImage.MOD_NO_FAIL, "NF", 1, Input.KEY_W, 0.5f,
@@ -43,24 +44,26 @@ public enum GameMod {
"HardRock", "Everything just got a bit harder..."),
SUDDEN_DEATH (Category.HARD, 1, GameImage.MOD_SUDDEN_DEATH, "SD", 32, Input.KEY_S, 1f,
"SuddenDeath", "Miss a note and fail."),
-// PERFECT (Category.HARD, 1, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_S, 1f,
-// "Perfect", "SS or quit."),
- DOUBLE_TIME (Category.HARD, 2, GameImage.MOD_DOUBLE_TIME, "DT", 64, Input.KEY_D, 1.12f,
+ PERFECT (Category.HARD, 2, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_P, 1f, "Perfect", "SS or quit."),
+ DOUBLE_TIME (Category.HARD, 3, GameImage.MOD_DOUBLE_TIME, "DT", 64, Input.KEY_D, 1.12f,
"DoubleTime", "Zoooooooooom."),
-// NIGHTCORE (Category.HARD, 2, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_D, 1.12f,
-// "Nightcore", "uguuuuuuuu"),
- HIDDEN (Category.HARD, 3, GameImage.MOD_HIDDEN, "HD", 8, Input.KEY_F, 1.06f,
+ NIGHTCORE (Category.HARD, 4, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_N, 1.12f, "Nightcore", "uguuuuuuuu"),
+ HIDDEN (Category.HARD, 5, GameImage.MOD_HIDDEN, "HD", 8, Input.KEY_F, 1.06f,
"Hidden", "Play with no approach circles and fading notes for a slight score advantage."),
- FLASHLIGHT (Category.HARD, 4, GameImage.MOD_FLASHLIGHT, "FL", 1024, Input.KEY_G, 1.12f,
+ FLASHLIGHT (Category.HARD, 6, GameImage.MOD_FLASHLIGHT, "FL", 1024, Input.KEY_G, 1.12f,
"Flashlight", "Restricted view area."),
RELAX (Category.SPECIAL, 0, GameImage.MOD_RELAX, "RL", 128, Input.KEY_Z, 0f,
"Relax", "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.\n**UNRANKED**"),
AUTOPILOT (Category.SPECIAL, 1, GameImage.MOD_AUTOPILOT, "AP", 8192, Input.KEY_X, 0f,
"Relax2", "Automatic cursor movement - just follow the rhythm.\n**UNRANKED**"),
- SPUN_OUT (Category.SPECIAL, 2, GameImage.MOD_SPUN_OUT, "SO", 4096, Input.KEY_C, 0.9f,
+ Target (Category.SPECIAL, 2, GameImage.MOD_TARGET, "AP", 8388608, Input.KEY_T, 1f,
+ "Target", "Timing practice!"),
+ SPUN_OUT (Category.SPECIAL, 3, GameImage.MOD_SPUN_OUT, "SO", 4096, Input.KEY_C, 0.9f,
"SpunOut", "Spinners will be automatically completed."),
- AUTO (Category.SPECIAL, 3, GameImage.MOD_AUTO, "", 2048, Input.KEY_V, 1f,
- "Autoplay", "Watch a perfect automated play through the song.");
+ AUTO (Category.SPECIAL, 4, GameImage.MOD_AUTO, "", 2048, Input.KEY_V, 1f,
+ "Autoplay", "Watch a perfect automated play through the song."),
+ CINEMA (Category.SPECIAL, 5, GameImage.MOD_CINEMA, "", 4194304, Input.KEY_M, 1f, "Autoplay", "Watch the video without being distubed by objects.");
+
/** Mod categories. */
public enum Category {
@@ -192,7 +195,7 @@ public static void init(int width, int height) {
// create buttons
float baseX = Category.EASY.getX() + Fonts.LARGE.getWidth(Category.EASY.getName()) * 1.25f;
- float offsetX = GameImage.MOD_EASY.getImage().getWidth() * 2.1f;
+ float offsetX = GameImage.MOD_EASY.getImage().getWidth() * 1.1f;
for (GameMod mod : GameMod.values()) {
Image img = mod.image.getImage();
mod.button = new MenuButton(img,
@@ -371,16 +374,20 @@ public void toggle(boolean checkInverse) {
if (this == AUTO) {
SPUN_OUT.active = false;
SUDDEN_DEATH.active = false;
+ PERFECT.active = false;
RELAX.active = false;
AUTOPILOT.active = false;
- } else if (this == SPUN_OUT || this == SUDDEN_DEATH || this == RELAX || this == AUTOPILOT)
+ CINEMA.active = false;
+ } else if (this == SPUN_OUT || this == SUDDEN_DEATH || this == PERFECT || this == RELAX || this == AUTOPILOT || this == CINEMA)
this.active = false;
}
- if (active && (this == SUDDEN_DEATH || this == NO_FAIL || this == RELAX || this == AUTOPILOT)) {
+ if (active && (this == SUDDEN_DEATH || this == PERFECT || this == NO_FAIL || this == RELAX || this == AUTOPILOT)) {
SUDDEN_DEATH.active = false;
+ PERFECT.active = false;
NO_FAIL.active = false;
RELAX.active = false;
AUTOPILOT.active = false;
+ CINEMA.active = false;
active = true;
}
if (AUTOPILOT.isActive() && SPUN_OUT.isActive()) {
@@ -395,12 +402,20 @@ public void toggle(boolean checkInverse) {
else
EASY.active = false;
}
- if (HALF_TIME.isActive() && DOUBLE_TIME.isActive()) {
- if (this == HALF_TIME)
+ if (HALF_TIME.isActive() && (DOUBLE_TIME.isActive() || NIGHTCORE.isActive())) {
+ if (this == HALF_TIME){
DOUBLE_TIME.active = false;
+ NIGHTCORE.active = false;
+ }
else
HALF_TIME.active = false;
}
+ if (DOUBLE_TIME.isActive() && NIGHTCORE.isActive()){
+ if (this == DOUBLE_TIME)
+ NIGHTCORE.active = false;
+ else
+ DOUBLE_TIME.active = false;
+ }
}
}
diff --git a/src/itdelatrisu/opsu/OpsuConstants.java b/src/itdelatrisu/opsu/OpsuConstants.java
index 44090ccb..610e0f1a 100644
--- a/src/itdelatrisu/opsu/OpsuConstants.java
+++ b/src/itdelatrisu/opsu/OpsuConstants.java
@@ -30,25 +30,25 @@ public class OpsuConstants {
public static final String PROJECT_NAME = "opsu!";
/** Project author. */
- public static final String PROJECT_AUTHOR = "@itdelatrisu";
+ public static final String PROJECT_AUTHOR = "@itdelatrisu + @CloneWith";
/** Website address. */
- public static final URI WEBSITE_URI = URI.create("https://itdelatrisu.github.io/opsu/");
+ public static final URI WEBSITE_URI = URI.create("https://clonewith.github.io/opsu/");
/** Repository address. */
- public static final URI REPOSITORY_URI = URI.create("https://github.com/itdelatrisu/opsu");
+ public static final URI REPOSITORY_URI = URI.create("https://github.com/clonewith/opsu");
/** Credits address. */
- public static final URI CREDITS_URI = URI.create("https://github.com/itdelatrisu/opsu/blob/master/CREDITS.md");
+ public static final URI CREDITS_URI = URI.create("https://github.com/clonewith/opsu/blob/master/CREDITS.md");
/** Issue reporting address. */
- public static final String ISSUES_URL = "https://github.com/itdelatrisu/opsu/issues/new?title=%s&body=%s";
+ public static final String ISSUES_URL = "https://github.com/clonewith/opsu/issues/new?title=%s&body=%s";
/** Address containing the latest version file. */
public static final String VERSION_REMOTE = "https://raw.githubusercontent.com/itdelatrisu/opsu/gh-pages/version";
/** Changelog address. */
- private static final String CHANGELOG_URL = "https://github.com/itdelatrisu/opsu/releases/tag/%s";
+ private static final String CHANGELOG_URL = "https://github.com/clonewith/opsu/releases/tag/%s";
/** Returns the changelog URI for the given version. */
public static URI getChangelogURI(String version) {
diff --git a/src/itdelatrisu/opsu/audio/MusicController.java b/src/itdelatrisu/opsu/audio/MusicController.java
index e006172f..db78f338 100644
--- a/src/itdelatrisu/opsu/audio/MusicController.java
+++ b/src/itdelatrisu/opsu/audio/MusicController.java
@@ -321,6 +321,17 @@ public static void resume() {
}
}
+ /**
+ * Always play the current track from the start.
+ */
+ public static void replay() {
+ if (trackExists()) {
+ pauseTime = 0f;
+ player.resume();
+ player.setVolume(1.0f);
+ }
+ }
+
/**
* Stops the current track.
*/
diff --git a/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java b/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java
index e01f1df2..1103c7a8 100644
--- a/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java
+++ b/src/itdelatrisu/opsu/downloads/servers/DownloadServer.java
@@ -27,6 +27,10 @@
* Abstract class for beatmap download servers.
*/
public abstract class DownloadServer {
+ /**
+ * For mirror download servers, there may be a better solution.
+ * For example, use visible .conf (maybe) files instead coding servers insider, just for convenience and need for maintenance.
+ */
/** Track preview URL. */
private static final String PREVIEW_URL = "http://b.ppy.sh/preview/%d.mp3";
diff --git a/src/itdelatrisu/opsu/options/Options.java b/src/itdelatrisu/opsu/options/Options.java
index 3c52eeee..39fc7b7c 100644
--- a/src/itdelatrisu/opsu/options/Options.java
+++ b/src/itdelatrisu/opsu/options/Options.java
@@ -365,17 +365,26 @@ public void toggle(GameContainer container) {
},
SKIN ("Skin", "Skin", "") {
private String[] itemList = null;
+ private String[] DirList = null;
@Override
public boolean isRestartRequired() { return true; }
+ // Can we directly load skins without restart here?
/** Creates the list of available skins. */
private void createSkinList() {
File[] dirs = SkinLoader.getSkinDirectories(getSkinRootDir());
+ // Here we use skin directory names as display names, need to change it.
itemList = new String[dirs.length + 1];
+ DirList = new String[dirs.length + 1];
itemList[0] = Skin.DEFAULT_SKIN_NAME;
- for (int i = 0; i < dirs.length; i++)
- itemList[i + 1] = dirs[i].getName();
+ DirList[0] = null;
+ for (int i = 0; i < dirs.length; i++){
+ Skin r = SkinLoader.loadSkin(dirs[i]);
+ // itemList[i + 1] = r.getName() + "(" + r.getAuthor() + ")";
+ itemList[i + 1] = r.getName();
+ DirList[i + 1] = dirs[i].getName();
+ }
}
@Override
@@ -968,6 +977,9 @@ public boolean hasFullscreenDisplayMode() {
/** The current skin. */
private static Skin skin;
+ /** The directory of the skin. */
+ // private static File skinDir = skin.getDirectory();
+
/** Frame limiters. */
private static final int[] targetFPS = { 60, 120, 240, -1 /* Unlimited */ };
@@ -1519,6 +1531,9 @@ public static void loadSkin() {
else {
// load the skin
skin = SkinLoader.loadSkin(skinDir);
+ // String info = skin.getName() + " (" + skin.getAuthor() + ")";
+ // ErrorHandler.error(String.format("You are using the skin '%s'.", info), null, false);
+ // Show the real name and author in skin.ini
ResourceLoader.addResourceLocation(new FileSystemLocation(skinDir));
}
ResourceLoader.addResourceLocation(new ClasspathLocation());
@@ -1544,6 +1559,9 @@ public static void loadSkin() {
public static File getSkinDir() {
File root = getSkinRootDir();
File dir = new File(root, skinName);
+ // File dir = skinDir;
+ // File dir = new File(root, skinDir);
+ // TODO: Need to adjust
return (dir.isDirectory()) ? dir : null;
}
diff --git a/src/itdelatrisu/opsu/skins/Skin.java b/src/itdelatrisu/opsu/skins/Skin.java
index f2655628..0468cb47 100644
--- a/src/itdelatrisu/opsu/skins/Skin.java
+++ b/src/itdelatrisu/opsu/skins/Skin.java
@@ -28,9 +28,20 @@
* Skin configuration (skin.ini).
*/
public class Skin {
+ // TODO: Add playwarning arrow tinting support (on/off)
+ // TODO: Pulsing effect for arrows and pass/fail indicator
+ // TODO: Animation improvement for combo bursts
+ // TODO: Clarify usage of star2 (partof / zoom)
+ // TODO: Difference between hit results and results
+ // TODO: Spinner versions
+ // TODO: Adjust locations and anchors of elements
+ // TODO: Improve support for transparent images
/** The default skin name. */
public static final String DEFAULT_SKIN_NAME = "Default";
+ /** The status of skin.ini. If it exists it is true. */
+ public Boolean INI_STATUS = false;
+
/** Slider styles. */
public static final byte
STYLE_PEPPYSLIDER = 1, // fallback
@@ -87,7 +98,7 @@ public class Skin {
protected String name = OpsuConstants.PROJECT_NAME + " Default Skin";
/** The skin author. */
- protected String author = "[various authors]";
+ protected String author = "various authors";
/** The skin version. */
protected float version = LATEST_VERSION;
@@ -206,6 +217,11 @@ public Skin(File dir) {
*/
public File getDirectory() { return dir; }
+ /**
+ * Returns the skin directory.
+ */
+ public Boolean getINIStatus() { return INI_STATUS; }
+
/**
* Returns the name of the skin.
*/
diff --git a/src/itdelatrisu/opsu/skins/SkinLoader.java b/src/itdelatrisu/opsu/skins/SkinLoader.java
index ddc4c49f..22868124 100644
--- a/src/itdelatrisu/opsu/skins/SkinLoader.java
+++ b/src/itdelatrisu/opsu/skins/SkinLoader.java
@@ -20,6 +20,9 @@
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils;
+import itdelatrisu.opsu.ui.Colors;
+import itdelatrisu.opsu.ui.NotificationManager;
+import itdelatrisu.opsu.ui.UI;
import java.io.BufferedReader;
import java.io.File;
@@ -66,18 +69,22 @@ public static File[] getSkinDirectories(File root) {
public static Skin loadSkin(File dir) {
File skinFile = new File(dir, CONFIG_FILENAME);
Skin skin = new Skin(dir);
- if (!skinFile.isFile()) // missing skin.ini
+ if (!skinFile.isFile()){ // missing skin.ini
return skin;
+ }
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(skinFile), "UTF-8"))) {
String line = in.readLine();
String tokens[] = null;
+ skin.INI_STATUS = true;
while (line != null) {
line = line.trim();
if (!isValidLine(line)) {
line = in.readLine();
continue;
}
+ // TODO: This implied minimal skin settings. Read osu!wiki for details and additions.
+ // TODO: can add the use of commands "Author" and "Name" to show accurate skin name and author instead of folder name
switch (line) {
case "[General]":
while ((line = in.readLine()) != null) {
@@ -173,6 +180,7 @@ public static Skin loadSkin(File dir) {
break;
if ((tokens = tokenize(line)) == null)
continue;
+ // TODO: Is there valid support for RGBA format? don't know
try {
String[] rgb = tokens[1].split(",");
Color color = new Color(
diff --git a/src/itdelatrisu/opsu/states/DownloadsMenu.java b/src/itdelatrisu/opsu/states/DownloadsMenu.java
index 6579fd19..df841ec3 100644
--- a/src/itdelatrisu/opsu/states/DownloadsMenu.java
+++ b/src/itdelatrisu/opsu/states/DownloadsMenu.java
@@ -25,6 +25,7 @@
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
+import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSetNode;
@@ -45,6 +46,8 @@
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.NotificationManager.NotificationListener;
import itdelatrisu.opsu.ui.UI;
+import itdelatrisu.opsu.ui.animations.AnimatedValue;
+import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Desktop;
import java.io.File;
@@ -76,6 +79,13 @@
* from this state.
*/
public class DownloadsMenu extends BasicGameState {
+
+ /** Max alpha level of the menu background. */
+ private static final float BG_MAX_ALPHA = 0.5f;
+
+ /** Background alpha level (for fade-in effect). */
+ private AnimatedValue bgAlpha = new AnimatedValue(1100, 0f, BG_MAX_ALPHA, AnimationEquation.LINEAR);
+
/** Delay time, in milliseconds, between each search. */
private static final int SEARCH_DELAY = 700;
@@ -422,15 +432,28 @@ public void render(GameContainer container, StateBasedGame game, Graphics g)
boolean inDropdownMenu = serverMenu.contains(mouseX, mouseY);
// background
- Image bg = GameImage.SEARCH_BG.getImage();
+ Beatmap beatmap = MusicController.getBeatmap();
+
+ float parallaxX = 0, parallaxY = 0;
if (Options.isParallaxEnabled()) {
int offset = (int) (height * (GameImage.PARALLAX_SCALE - 1f));
- float parallaxX = -offset / 2f * (mouseX - width / 2) / (width / 2);
- float parallaxY = -offset / 2f * (mouseY - height / 2) / (height / 2);
- bg = bg.getScaledCopy(GameImage.PARALLAX_SCALE);
- bg.drawCentered(width / 2 + parallaxX, height / 2 + parallaxY);
- } else
- bg.drawCentered(width / 2, height / 2);
+ parallaxX = -offset / 2f * (mouseX - width / 2) / (width / 2);
+ parallaxY = -offset / 2f * (mouseY - height / 2) / (height / 2);
+ }
+ if (Options.isDynamicBackgroundEnabled() && beatmap != null &&
+ beatmap.drawBackground(width, height, parallaxX, parallaxY, bgAlpha.getValue(), true))
+ ;
+ else {
+ Image bg = GameImage.MENU_BG.getImage();
+ if (Options.isParallaxEnabled()) {
+ bg = bg.getScaledCopy(GameImage.PARALLAX_SCALE);
+ bg.setAlpha(bgAlpha.getValue());
+ bg.drawCentered(width / 2 + parallaxX, height / 2 + parallaxY);
+ } else {
+ bg.setAlpha(bgAlpha.getValue());
+ bg.drawCentered(width / 2, height / 2);
+ }
+ }
// title
Fonts.LARGE.drawString(width * 0.024f, height * 0.03f, "Download Beatmaps!", Color.white);
@@ -618,6 +641,11 @@ else if (rankedButton.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Toggle the display of unranked maps.\nSome download servers may not support this option.", true);
else if (serverMenu.baseContains(mouseX, mouseY))
UI.updateTooltip(delta, "Select a download server.", false);
+
+ // fade in background
+ Beatmap beatmap = MusicController.getBeatmap();
+ if (!(Options.isDynamicBackgroundEnabled() && beatmap != null && beatmap.isBackgroundLoading()))
+ bgAlpha.update(delta);
}
@Override
diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java
index e97c18d5..44765e6f 100644
--- a/src/itdelatrisu/opsu/states/Game.java
+++ b/src/itdelatrisu/opsu/states/Game.java
@@ -976,6 +976,25 @@ else if (!gameFinished) {
}
}
+ private void retry() {
+ // TODO: need another step of process
+ int trackPosition = MusicController.getPosition(true);
+ if (gameFinished)
+ return;
+ try {
+ if (trackPosition < beatmap.objects[0].getTime())
+ retries--; // don't count this retry (cancel out later increment)
+ playState = PlayState.RETRY;
+ game.enterState(Opsu.STATE_GAME,
+ new DelayedFadeOutTransition(Color.black, LOSE_FADEOUT_TIME, 0),
+ new FadeInTransition());
+ enter(container, game);
+ if(!GameMod.PERFECT.isActive()) skipIntro();
+ } catch (SlickException e) {
+ ErrorHandler.error("Failed to restart game.", e, false);
+ }
+ }
+
/**
* Updates the game.
* @param mouseX the mouse x coordinate
@@ -1102,8 +1121,22 @@ else if (replayFrames != null) {
}
// game over, force a restart
+ // TODO: If using PF mod, needs a quick restart
if (!isReplay) {
+ // record to stats
+ User user = UserList.get().getCurrentUser();
+ user.add(data.getScore());
+ ScoreDB.updateUser(user);
+
if (playState != PlayState.LOSE) {
+ if (GameMod.PERFECT.isActive()) {
+ // game.enterState(Opsu.STATE_GAME,
+ // new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME),
+ // new FadeInTransition());
+ // new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME);
+ retry();
+ return;
+ }
playState = PlayState.LOSE;
failTime = System.currentTimeMillis();
failTrackTime = MusicController.getPosition(true);
@@ -1112,11 +1145,6 @@ else if (replayFrames != null) {
rotations = new IdentityHashMap();
SoundController.playSound(SoundEffect.FAIL);
- // record to stats
- User user = UserList.get().getCurrentUser();
- user.add(data.getScore());
- ScoreDB.updateUser(user);
-
// fade to pause menu
game.enterState(Opsu.STATE_GAMEPAUSEMENU,
new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME),
@@ -1219,17 +1247,7 @@ else if (key == Options.getGameKeyRight())
// fall through
case Input.KEY_GRAVE:
// restart
- if (gameFinished)
- break;
- try {
- if (trackPosition < beatmap.objects[0].getTime())
- retries--; // don't count this retry (cancel out later increment)
- playState = PlayState.RETRY;
- enter(container, game);
- skipIntro();
- } catch (SlickException e) {
- ErrorHandler.error("Failed to restart game.", e, false);
- }
+ retry();
break;
case Input.KEY_S:
// save checkpoint
diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java
index d98aa781..8542c213 100644
--- a/src/itdelatrisu/opsu/states/MainMenu.java
+++ b/src/itdelatrisu/opsu/states/MainMenu.java
@@ -99,7 +99,7 @@ private enum LogoState { DEFAULT, OPENING, OPEN, CLOSING }
private MenuButton playButton, exitButton;
/** Music control buttons. */
- private MenuButton musicPlay, musicPause, musicNext, musicPrevious;
+ private MenuButton musicPlay, musicPause, musicNext, musicPrevious, musicReplay;
/** Button linking to Downloads menu. */
private MenuButton downloadsButton;
@@ -230,10 +230,12 @@ public void init(GameContainer container, StateBasedGame game)
int musicInfoOffset = (int) (musicInfoHeight * 0.6f);
int musicWidth = GameImage.MUSIC_PLAY.getImage().getWidth();
int musicHeight = GameImage.MUSIC_PLAY.getImage().getHeight();
+ musicReplay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), width - (3 * musicWidth), musicInfoOffset + musicHeight / 1.5f);
musicPlay = new MenuButton(GameImage.MUSIC_PLAY.getImage(), width - (2 * musicWidth), musicInfoOffset + musicHeight / 1.5f);
musicPause = new MenuButton(GameImage.MUSIC_PAUSE.getImage(), width - (2 * musicWidth), musicInfoOffset + musicHeight / 1.5f);
musicNext = new MenuButton(GameImage.MUSIC_NEXT.getImage(), width - musicWidth, musicInfoOffset + musicHeight / 1.5f);
- musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), width - (3 * musicWidth), musicInfoOffset + musicHeight / 1.5f);
+ musicPrevious = new MenuButton(GameImage.MUSIC_PREVIOUS.getImage(), width - (4 * musicWidth), musicInfoOffset + musicHeight / 1.5f);
+ musicReplay.setHoverExpand(1.5f);
musicPlay.setHoverExpand(1.5f);
musicPause.setHoverExpand(1.5f);
musicNext.setHoverExpand(1.5f);
@@ -417,6 +419,7 @@ public void render(GameContainer container, StateBasedGame game, Graphics g)
musicPause.draw();
else
musicPlay.draw();
+ musicReplay.draw();
musicNext.draw();
musicPrevious.draw();
@@ -520,6 +523,7 @@ public void update(GameContainer container, StateBasedGame game, int delta)
// ensure only one button is in hover state at once
boolean noHoverUpdate = musicPositionBarContains(mouseX, mouseY);
boolean contains = musicPlay.contains(mouseX, mouseY);
+ musicReplay.hoverUpdate(delta, !noHoverUpdate && musicReplay.contains(mouseX, mouseY));
musicPlay.hoverUpdate(delta, !noHoverUpdate && contains);
musicPause.hoverUpdate(delta, !noHoverUpdate && contains);
noHoverUpdate |= contains;
@@ -619,8 +623,10 @@ public void update(GameContainer container, StateBasedGame game, int delta)
// tooltips
if (musicPositionBarContains(mouseX, mouseY))
UI.updateTooltip(delta, "Click to seek to a specific point in the song.", false);
+ else if (musicReplay.contains(mouseX, mouseY))
+ UI.updateTooltip(delta, "Play", false);
else if (musicPlay.contains(mouseX, mouseY))
- UI.updateTooltip(delta, (MusicController.isPlaying()) ? "Pause" : "Play", false);
+ UI.updateTooltip(delta, (MusicController.isPlaying()) ? "Pause" : "Resume", false);
else if (musicNext.contains(mouseX, mouseY))
UI.updateTooltip(delta, "Next track", false);
else if (musicPrevious.contains(mouseX, mouseY))
@@ -686,6 +692,8 @@ public void click() {
playButton.resetHover();
if (!exitButton.contains(mouseX, mouseY, 0.25f))
exitButton.resetHover();
+ if (!musicReplay.contains(mouseX, mouseY))
+ musicReplay.resetHover();
if (!musicPlay.contains(mouseX, mouseY))
musicPlay.resetHover();
if (!musicPause.contains(mouseX, mouseY))
@@ -754,7 +762,7 @@ public void mousePressed(int button, int x, int y) {
UI.getNotificationManager().sendBarNotification("Pause");
} else if (!MusicController.isTrackLoading()) {
MusicController.resume();
- UI.getNotificationManager().sendBarNotification("Play");
+ UI.getNotificationManager().sendBarNotification("Resume");
}
return;
} else if (musicNext.contains(x, y)) {
@@ -765,6 +773,10 @@ public void mousePressed(int button, int x, int y) {
previousTrack();
UI.getNotificationManager().sendBarNotification("<< Prev");
return;
+ } else if (musicReplay.contains(x, y)) {
+ MusicController.playAt(0, false);
+ musicInfoProgress.setTime(0);
+ UI.getNotificationManager().sendBarNotification("Play");
}
// downloads button actions
@@ -946,6 +958,7 @@ public void reset() {
logo.resetHover();
playButton.resetHover();
exitButton.resetHover();
+ musicReplay.resetHover();
musicPlay.resetHover();
musicPause.resetHover();
musicNext.resetHover();
diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java
index 75ca7130..cfe61e3f 100644
--- a/src/itdelatrisu/opsu/states/SongMenu.java
+++ b/src/itdelatrisu/opsu/states/SongMenu.java
@@ -460,11 +460,13 @@ public boolean menuClicked(int index) {
search.setMaxLength(60);
// selection buttons
- Image selectionMods = GameImage.SELECTION_MODS.getImage();
- float selectX = width * 0.183f + selectionMods.getWidth() / 2f;
- float selectY = height - selectionMods.getHeight() / 2f;
- float selectOffset = selectionMods.getWidth() * 1.05f;
- selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(),
+ // TODO: For special cases (e.g. 5 digit skin), need to process each image separately
+ // Image selectionMods = GameImage.SELECTION_MODS.getImage();
+ Image selectionModsOverlay = GameImage.SELECTION_MODS_OVERLAY.getImage();
+ float selectX = width * 0.183f + selectionModsOverlay.getWidth() / 2f;
+ float selectY = height - selectionModsOverlay.getHeight() / 2f;
+ float selectOffset = selectionModsOverlay.getWidth() * 1.05f;
+ selectModsButton = new MenuButton(selectionModsOverlay,
selectX, selectY);
selectRandomButton = new MenuButton(GameImage.SELECTION_RANDOM_OVERLAY.getImage(),
selectX + selectOffset, selectY);
diff --git a/src/itdelatrisu/opsu/ui/VisualSetting.java b/src/itdelatrisu/opsu/ui/VisualSetting.java
new file mode 100644
index 00000000..6631539b
--- /dev/null
+++ b/src/itdelatrisu/opsu/ui/VisualSetting.java
@@ -0,0 +1,48 @@
+/*
+ * opsu! - an open-source osu! client
+ * Copyright (C) 2014-2017 Jeffrey Han
+ * Copyright (C) 2023-2024 CloneWith
+ *
+ * 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 .
+ */
+
+package itdelatrisu.opsu.ui;
+
+import itdelatrisu.opsu.GameImage;
+import itdelatrisu.opsu.Opsu;
+import itdelatrisu.opsu.Utils;
+import itdelatrisu.opsu.audio.SoundController;
+import itdelatrisu.opsu.options.Options;
+import itdelatrisu.opsu.replay.ReplayImporter;
+import itdelatrisu.opsu.skins.SkinUnpacker;
+import itdelatrisu.opsu.ui.animations.AnimatedValue;
+import itdelatrisu.opsu.ui.animations.AnimationEquation;
+
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
+
+import org.newdawn.slick.Color;
+import org.newdawn.slick.GameContainer;
+import org.newdawn.slick.Graphics;
+import org.newdawn.slick.Image;
+import org.newdawn.slick.Input;
+import org.newdawn.slick.state.StateBasedGame;
+import org.newdawn.slick.util.Log;
+
+/**
+ * Draws the visual settings panel.
+ */
+public class VisualSetting {
+
+}
diff --git a/src/itdelatrisu/opsu/user/UserSelectOverlay.java b/src/itdelatrisu/opsu/user/UserSelectOverlay.java
index 0b608abe..94548430 100644
--- a/src/itdelatrisu/opsu/user/UserSelectOverlay.java
+++ b/src/itdelatrisu/opsu/user/UserSelectOverlay.java
@@ -382,7 +382,9 @@ private void renderUserEdit(Graphics g, float alpha) {
editUserButton.draw(g, alpha);
// delete button
- deleteUserButton.draw(g, alpha);
+ if (UserList.get().size() != 1) {
+ deleteUserButton.draw(g, alpha);
+ }
// user icons
int cy = (int) (y + usersStartY + (UserButton.getHeight() + usersPaddingY) * 2);
@@ -511,14 +513,10 @@ public void mouseReleased(int button, int x, int y) {
String name = user.getName();
if (button == Input.MOUSE_RIGHT_BUTTON) {
// right click: edit user
- if (name.equals(UserList.DEFAULT_USER_NAME)) {
- UI.getNotificationManager().sendBarNotification("This user can't be edited.");
- } else {
- state = State.EDIT_USER;
- prevState = State.USER_SELECT;
- stateChangeProgress.setTime(0);
- prepareUserEdit(selectedButton.getUser());
- }
+ state = State.EDIT_USER;
+ prevState = State.USER_SELECT;
+ stateChangeProgress.setTime(0);
+ prepareUserEdit(selectedButton.getUser());
} else {
// left click: select user
if (!name.equals(UserList.get().getCurrentUser().getName())) {