Merge remote-tracking branch 'orgin/master' into omaster
Conflicts: src/itdelatrisu/opsu/GameImage.java src/itdelatrisu/opsu/OsuGroupNode.java
|
@ -33,7 +33,8 @@ folder if placed in the `SongPacks` directory.
|
||||||
### First Run
|
### First Run
|
||||||
The `Music Offset` value will likely need to be adjusted when playing for the
|
The `Music Offset` value will likely need to be adjusted when playing for the
|
||||||
first time, or whenever hit objects are out of sync with the music. This and
|
first time, or whenever hit objects are out of sync with the music. This and
|
||||||
other game options can be accessed by clicking the wrench icon in the song menu.
|
other game options can be accessed by clicking the "Other Options" button in
|
||||||
|
the song menu.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
opsu! is distributed as a Maven project.
|
opsu! is distributed as a Maven project.
|
||||||
|
|
BIN
res/options.png
Before Width: | Height: | Size: 2.0 KiB |
BIN
res/search.png
Before Width: | Height: | Size: 2.5 KiB |
BIN
res/selection-mods-over.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
res/selection-mods.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
res/selection-options-over.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
res/selection-options.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
res/selection-random-over.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
res/selection-random.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
res/selection-selectoptions-over.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
res/selection-selectoptions.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
|
@ -1065,13 +1065,6 @@ public class GameData {
|
||||||
if (hitValue > 0) {
|
if (hitValue > 0) {
|
||||||
SoundController.playHitSound(hitSound);
|
SoundController.playHitSound(hitSound);
|
||||||
|
|
||||||
// game mod score multipliers
|
|
||||||
float modMultiplier = 1f;
|
|
||||||
for (GameMod mod : GameMod.values()) {
|
|
||||||
if (mod.isActive())
|
|
||||||
modMultiplier *= mod.getMultiplier();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [SCORE FORMULA]
|
* [SCORE FORMULA]
|
||||||
* Score = Hit Value + Hit Value * (Combo * Difficulty * Mod) / 25
|
* Score = Hit Value + Hit Value * (Combo * Difficulty * Mod) / 25
|
||||||
|
@ -1080,7 +1073,7 @@ public class GameData {
|
||||||
* - Difficulty: the beatmap difficulty
|
* - Difficulty: the beatmap difficulty
|
||||||
* - Mod: mod multipliers
|
* - Mod: mod multipliers
|
||||||
*/
|
*/
|
||||||
score += (hitValue + (hitValue * (Math.max(combo - 1, 0) * difficulty * modMultiplier) / 25));
|
score += (hitValue + (hitValue * (Math.max(combo - 1, 0) * difficulty * GameMod.getScoreMultiplier()) / 25));
|
||||||
incrementComboStreak();
|
incrementComboStreak();
|
||||||
}
|
}
|
||||||
hitResultCount[result]++;
|
hitResultCount[result]++;
|
||||||
|
|
|
@ -204,61 +204,123 @@ public enum GameImage {
|
||||||
MOD_EASY ("selection-mod-easy", "png", false, false) {
|
MOD_EASY ("selection-mod-easy", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return img.getScaledCopy((h / 12f) / img.getHeight());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_NO_FAIL ("selection-mod-nofail", "png", false, false) {
|
MOD_NO_FAIL ("selection-mod-nofail", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_HARD_ROCK ("selection-mod-hardrock", "png", false, false) {
|
MOD_HARD_ROCK ("selection-mod-hardrock", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_SUDDEN_DEATH ("selection-mod-suddendeath", "png", false, false) {
|
MOD_SUDDEN_DEATH ("selection-mod-suddendeath", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_SPUN_OUT ("selection-mod-spunout", "png", false, false) {
|
MOD_SPUN_OUT ("selection-mod-spunout", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_AUTO ("selection-mod-autoplay", "png", false, false) {
|
MOD_AUTO ("selection-mod-autoplay", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_HALF_TIME ("selection-mod-halftime", "png", false, false) {
|
MOD_HALF_TIME ("selection-mod-halftime", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_DOUBLE_TIME ("selection-mod-doubletime", "png", false, false) {
|
MOD_DOUBLE_TIME ("selection-mod-doubletime", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_HIDDEN ("selection-mod-hidden", "png", false, false) {
|
MOD_HIDDEN ("selection-mod-hidden", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MOD_FLASHLIGHT ("selection-mod-flashlight", "png", false, false) {
|
MOD_FLASHLIGHT ("selection-mod-flashlight", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h * 0.12f) / img.getHeight());
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MOD_RELAX ("selection-mod-relax", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MOD_AUTOPILOT ("selection-mod-relax2", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return MOD_EASY.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Selection Buttons
|
||||||
|
SELECTION_MODS ("selection-mods", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return img.getScaledCopy((h * 0.115f) / img.getHeight());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_MODS_OVERLAY ("selection-mods-over", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_RANDOM ("selection-random", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_RANDOM_OVERLAY ("selection-random-over", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_OPTIONS ("selection-options", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_OPTIONS_OVERLAY ("selection-options-over", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_OTHER_OPTIONS ("selection-selectoptions", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SELECTION_OTHER_OPTIONS_OVERLAY ("selection-selectoptions-over", "png", false, false) {
|
||||||
|
@Override
|
||||||
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
return SELECTION_MODS.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -277,18 +339,6 @@ public enum GameImage {
|
||||||
return img.getScaledCopy((h * 0.033f) / img.getHeight());
|
return img.getScaledCopy((h * 0.033f) / img.getHeight());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MENU_SEARCH ("search", "png", false, false) {
|
|
||||||
@Override
|
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
|
||||||
return img.getScaledCopy(Utils.FONT_BOLD.getLineHeight() * 2f / img.getHeight());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MENU_OPTIONS ("options", "png", false, false) {
|
|
||||||
@Override
|
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
|
||||||
return img.getScaledCopy(Utils.FONT_BOLD.getLineHeight() * 2f / img.getHeight());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MENU_MUSICNOTE ("music-note", "png", false, false) {
|
MENU_MUSICNOTE ("music-note", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
@ -331,6 +381,8 @@ public enum GameImage {
|
||||||
MENU_BUTTON_MID ("button-middle", "png", false, false),
|
MENU_BUTTON_MID ("button-middle", "png", false, false),
|
||||||
MENU_BUTTON_LEFT ("button-left", "png", false, false),
|
MENU_BUTTON_LEFT ("button-left", "png", false, false),
|
||||||
MENU_BUTTON_RIGHT ("button-right", "png", false, false),
|
MENU_BUTTON_RIGHT ("button-right", "png", false, false),
|
||||||
|
|
||||||
|
// Music Player Buttons
|
||||||
MUSIC_PLAY ("music-play", "png", false, false) {
|
MUSIC_PLAY ("music-play", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
@ -340,19 +392,19 @@ public enum GameImage {
|
||||||
MUSIC_PAUSE ("music-pause", "png", false, false) {
|
MUSIC_PAUSE ("music-pause", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h / 18f) / img.getHeight());
|
return MUSIC_PLAY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MUSIC_NEXT ("music-next", "png", false, false) {
|
MUSIC_NEXT ("music-next", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h / 18f) / img.getHeight());
|
return MUSIC_PLAY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MUSIC_PREVIOUS ("music-previous", "png", false, false) {
|
MUSIC_PREVIOUS ("music-previous", "png", false, false) {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
return img.getScaledCopy((h / 18f) / img.getHeight());
|
return MUSIC_PLAY.process_sub(img, w, h);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RANKING_RETRY ("ranking-retry", "png", false, false),
|
RANKING_RETRY ("ranking-retry", "png", false, false),
|
||||||
|
|
|
@ -21,6 +21,7 @@ package itdelatrisu.opsu;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.Image;
|
import org.newdawn.slick.Image;
|
||||||
import org.newdawn.slick.Input;
|
import org.newdawn.slick.Input;
|
||||||
|
|
||||||
|
@ -28,29 +29,103 @@ import org.newdawn.slick.Input;
|
||||||
* Game mods.
|
* Game mods.
|
||||||
*/
|
*/
|
||||||
public enum GameMod {
|
public enum GameMod {
|
||||||
EASY (0, GameImage.MOD_EASY, "EZ", 2, Input.KEY_Q, 0.5f,
|
EASY (Category.EASY, 0, GameImage.MOD_EASY, "EZ", 2, Input.KEY_Q, 0.5f,
|
||||||
"Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."),
|
"Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."),
|
||||||
NO_FAIL (1, GameImage.MOD_NO_FAIL, "NF", 1, Input.KEY_W, 0.5f,
|
NO_FAIL (Category.EASY, 1, GameImage.MOD_NO_FAIL, "NF", 1, Input.KEY_W, 0.5f,
|
||||||
"You can't fail. No matter what."),
|
"You can't fail. No matter what."),
|
||||||
HARD_ROCK (2, GameImage.MOD_HARD_ROCK, "HR", 16, Input.KEY_A, 1.06f,
|
HALF_TIME (Category.EASY, 2, GameImage.MOD_HALF_TIME, "HT", 256, Input.KEY_E, 0.3f, false,
|
||||||
|
"Less zoom."),
|
||||||
|
HARD_ROCK (Category.HARD, 0, GameImage.MOD_HARD_ROCK, "HR", 16, Input.KEY_A, 1.06f,
|
||||||
"Everything just got a bit harder..."),
|
"Everything just got a bit harder..."),
|
||||||
SUDDEN_DEATH (3, GameImage.MOD_SUDDEN_DEATH, "SD", 32, Input.KEY_S,
|
SUDDEN_DEATH (Category.HARD, 1, GameImage.MOD_SUDDEN_DEATH, "SD", 32, Input.KEY_S, 1f,
|
||||||
"Miss a note and fail."),
|
"Miss a note and fail."),
|
||||||
SPUN_OUT (4, GameImage.MOD_SPUN_OUT, "SO", 4096, Input.KEY_V, 0.9f,
|
// PERFECT (Category.HARD, 1, GameImage.MOD_PERFECT, "PF", 64, Input.KEY_S, 1f,
|
||||||
|
// "SS or quit."),
|
||||||
|
DOUBLE_TIME (Category.HARD, 2, GameImage.MOD_DOUBLE_TIME, "DT", 64, Input.KEY_D, 1.12f, false,
|
||||||
|
"Zoooooooooom."),
|
||||||
|
// NIGHTCORE (Category.HARD, 2, GameImage.MOD_NIGHTCORE, "NT", 64, Input.KEY_D, 1.12f,
|
||||||
|
// "uguuuuuuuu"),
|
||||||
|
HIDDEN (Category.HARD, 3, GameImage.MOD_HIDDEN, "HD", 8, Input.KEY_F, 1.06f, false,
|
||||||
|
"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, false,
|
||||||
|
"Restricted view area."),
|
||||||
|
RELAX (Category.SPECIAL, 0, GameImage.MOD_RELAX, "RL", 128, Input.KEY_Z, 0f, false,
|
||||||
|
"You don't need to click. Give your clicking/tapping finger a break from the heat of things. **UNRANKED**"),
|
||||||
|
AUTOPILOT (Category.SPECIAL, 1, GameImage.MOD_AUTOPILOT, "AP", 8192, Input.KEY_X, 0f, false,
|
||||||
|
"Automatic cursor movement - just follow the rhythm. **UNRANKED**"),
|
||||||
|
SPUN_OUT (Category.SPECIAL, 2, GameImage.MOD_SPUN_OUT, "SO", 4096, Input.KEY_V, 0.9f,
|
||||||
"Spinners will be automatically completed."),
|
"Spinners will be automatically completed."),
|
||||||
AUTO (5, GameImage.MOD_AUTO, "", 2048, Input.KEY_B,
|
AUTO (Category.SPECIAL, 3, GameImage.MOD_AUTO, "", 2048, Input.KEY_B, 1f,
|
||||||
"Watch a perfect automated play through the song.");
|
"Watch a perfect automated play through the song.");
|
||||||
// HALF_TIME (6, GameImage.MOD_HALF_TIME, "HT", 256, Input.KEY_E, 0.3f,
|
|
||||||
// "Less zoom."),
|
|
||||||
// DOUBLE_TIME (7, GameImage.MOD_DOUBLE_TIME, "DT", 64, Input.KEY_D, 1.12f,
|
|
||||||
// "Zoooooooooom."),
|
|
||||||
// HIDDEN (8, GameImage.MOD_HIDDEN, "HD", 8, Input.KEY_F, 1.06f,
|
|
||||||
// "Play with no approach circles and fading notes for a slight score advantage."),
|
|
||||||
// FLASHLIGHT (9, GameImage.MOD_FLASHLIGHT, "FL", 1024, Input.KEY_G, 1.12f,
|
|
||||||
// "Restricted view area.");
|
|
||||||
|
|
||||||
/** The ID of the mod (used for positioning). */
|
/** Mod categories. */
|
||||||
private int id;
|
public enum Category {
|
||||||
|
EASY (0, "Difficulty Reduction", Color.green),
|
||||||
|
HARD (1, "Difficulty Increase", Color.red),
|
||||||
|
SPECIAL (2, "Special", Color.white);
|
||||||
|
|
||||||
|
/** Drawing index. */
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
/** Category name. */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** Text color. */
|
||||||
|
private Color color;
|
||||||
|
|
||||||
|
/** The coordinates of the category. */
|
||||||
|
private float x, y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param index the drawing index
|
||||||
|
* @param name the category name
|
||||||
|
* @param color the text color
|
||||||
|
*/
|
||||||
|
Category(int index, String name, Color color) {
|
||||||
|
this.index = index;
|
||||||
|
this.name = name;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the category.
|
||||||
|
* @param width the container width
|
||||||
|
* @param height the container height
|
||||||
|
*/
|
||||||
|
public void init(int width, int height) {
|
||||||
|
float multY = Utils.FONT_LARGE.getLineHeight() * 2 + height * 0.06f;
|
||||||
|
float offsetY = GameImage.MOD_EASY.getImage().getHeight() * 1.5f;
|
||||||
|
this.x = width / 30f;
|
||||||
|
this.y = multY + Utils.FONT_LARGE.getLineHeight() * 3f + offsetY * index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the category name.
|
||||||
|
*/
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text color.
|
||||||
|
*/
|
||||||
|
public Color getColor() { return color; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the x coordinate of the category.
|
||||||
|
*/
|
||||||
|
public float getX() { return x; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the y coordinate of the category.
|
||||||
|
*/
|
||||||
|
public float getY() { return y; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The category for the mod. */
|
||||||
|
private Category category;
|
||||||
|
|
||||||
|
/** The index in the category (for positioning). */
|
||||||
|
private int categoryIndex;
|
||||||
|
|
||||||
/** The file name of the mod image. */
|
/** The file name of the mod image. */
|
||||||
private GameImage image;
|
private GameImage image;
|
||||||
|
@ -58,18 +133,21 @@ public enum GameMod {
|
||||||
/** The abbreviation for the mod. */
|
/** The abbreviation for the mod. */
|
||||||
private String abbrev;
|
private String abbrev;
|
||||||
|
|
||||||
/** Bit value associated with the mod. */
|
|
||||||
private int bit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The shortcut key associated with the mod.
|
* Bit value associated with the mod.
|
||||||
* See the osu! API: https://github.com/peppy/osu-api/wiki#mods
|
* See the osu! API: https://github.com/peppy/osu-api/wiki#mods
|
||||||
*/
|
*/
|
||||||
|
private int bit;
|
||||||
|
|
||||||
|
/** The shortcut key associated with the mod. */
|
||||||
private int key;
|
private int key;
|
||||||
|
|
||||||
/** The score multiplier. */
|
/** The score multiplier. */
|
||||||
private float multiplier;
|
private float multiplier;
|
||||||
|
|
||||||
|
/** Whether or not the mod is implemented. */
|
||||||
|
private boolean implemented;
|
||||||
|
|
||||||
/** The description of the mod. */
|
/** The description of the mod. */
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
|
@ -89,28 +167,56 @@ public enum GameMod {
|
||||||
Collections.reverse(Arrays.asList(VALUES_REVERSED));
|
Collections.reverse(Arrays.asList(VALUES_REVERSED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The last calculated score multiplier, or -1f if it must be recalculated. */
|
||||||
|
private static float scoreMultiplier = -1f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Initializes the game mods.
|
||||||
* @param id the ID of the mod (for positioning).
|
* @param width the container width
|
||||||
* @param image the GameImage
|
* @param height the container height
|
||||||
* @param abbrev the two-letter abbreviation
|
|
||||||
* @param bit the bit
|
|
||||||
* @param key the shortcut key
|
|
||||||
* @param description the description
|
|
||||||
*/
|
*/
|
||||||
GameMod(int id, GameImage image, String abbrev, int bit, int key, String description) {
|
public static void init(int width, int height) {
|
||||||
this.id = id;
|
// initialize categories
|
||||||
this.image = image;
|
for (Category c : Category.values())
|
||||||
this.abbrev = abbrev;
|
c.init(width, height);
|
||||||
this.bit = bit;
|
|
||||||
this.key = key;
|
// create buttons
|
||||||
this.multiplier = 1f;
|
float baseX = Category.EASY.getX() + Utils.FONT_LARGE.getWidth(Category.EASY.getName()) * 1.25f;
|
||||||
this.description = description;
|
float offsetX = GameImage.MOD_EASY.getImage().getWidth() * 2.1f;
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
Image img = mod.image.getImage();
|
||||||
|
mod.button = new MenuButton(img,
|
||||||
|
baseX + (offsetX * mod.categoryIndex) + img.getWidth() / 2f,
|
||||||
|
mod.category.getY());
|
||||||
|
mod.button.setHoverExpand(1.2f);
|
||||||
|
mod.button.setHoverRotate(10f);
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
mod.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreMultiplier = -1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current score multiplier from all active mods.
|
||||||
|
*/
|
||||||
|
public static float getScoreMultiplier() {
|
||||||
|
if (scoreMultiplier < 0f) {
|
||||||
|
float multiplier = 1f;
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
if (mod.isActive())
|
||||||
|
multiplier *= mod.getMultiplier();
|
||||||
|
}
|
||||||
|
scoreMultiplier = multiplier;
|
||||||
|
}
|
||||||
|
return scoreMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param id the ID of the mod (for positioning).
|
* @param category the category for the mod
|
||||||
|
* @param categoryIndex the index in the category
|
||||||
* @param image the GameImage
|
* @param image the GameImage
|
||||||
* @param abbrev the two-letter abbreviation
|
* @param abbrev the two-letter abbreviation
|
||||||
* @param bit the bit
|
* @param bit the bit
|
||||||
|
@ -118,37 +224,36 @@ public enum GameMod {
|
||||||
* @param multiplier the score multiplier
|
* @param multiplier the score multiplier
|
||||||
* @param description the description
|
* @param description the description
|
||||||
*/
|
*/
|
||||||
GameMod(int id, GameImage image, String abbrev, int bit, int key, float multiplier, String description) {
|
GameMod(Category category, int categoryIndex, GameImage image, String abbrev,
|
||||||
this.id = id;
|
int bit, int key, float multiplier, String description) {
|
||||||
|
this(category, categoryIndex, image, abbrev, bit, key, 1f, true, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param category the category for the mod
|
||||||
|
* @param categoryIndex the index in the category
|
||||||
|
* @param image the GameImage
|
||||||
|
* @param abbrev the two-letter abbreviation
|
||||||
|
* @param bit the bit
|
||||||
|
* @param key the shortcut key
|
||||||
|
* @param multiplier the score multiplier
|
||||||
|
* @param implemented whether the mod is implemented
|
||||||
|
* @param description the description
|
||||||
|
*/
|
||||||
|
GameMod(Category category, int categoryIndex, GameImage image, String abbrev,
|
||||||
|
int bit, int key, float multiplier, boolean implemented, String description) {
|
||||||
|
this.category = category;
|
||||||
|
this.categoryIndex = categoryIndex;
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.abbrev = abbrev;
|
this.abbrev = abbrev;
|
||||||
this.bit = bit;
|
this.bit = bit;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.multiplier = multiplier;
|
this.multiplier = multiplier;
|
||||||
|
this.implemented = implemented;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the game mod.
|
|
||||||
* @param width the container width
|
|
||||||
* @param height the container height
|
|
||||||
*/
|
|
||||||
public void init(int width, int height) {
|
|
||||||
Image img = image.getImage();
|
|
||||||
|
|
||||||
// find coordinates
|
|
||||||
float offsetX = img.getWidth() * 1.5f;
|
|
||||||
float x = (width / 2f) - (offsetX * SIZE / 2.75f);
|
|
||||||
float y = (height * 0.8f) + (img.getHeight() / 2);
|
|
||||||
|
|
||||||
// create button
|
|
||||||
this.button = new MenuButton(img, x + (offsetX * id), y);
|
|
||||||
this.button.setHoverExpand(1.15f);
|
|
||||||
|
|
||||||
// reset state
|
|
||||||
this.active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the abbreviated name of the mod.
|
* Returns the abbreviated name of the mod.
|
||||||
* @return the two-letter abbreviation
|
* @return the two-letter abbreviation
|
||||||
|
@ -185,7 +290,11 @@ public enum GameMod {
|
||||||
* @param checkInverse if true, perform checks for mutual exclusivity
|
* @param checkInverse if true, perform checks for mutual exclusivity
|
||||||
*/
|
*/
|
||||||
public void toggle(boolean checkInverse) {
|
public void toggle(boolean checkInverse) {
|
||||||
|
if (!implemented)
|
||||||
|
return;
|
||||||
|
|
||||||
active = !active;
|
active = !active;
|
||||||
|
scoreMultiplier = -1f;
|
||||||
|
|
||||||
if (checkInverse) {
|
if (checkInverse) {
|
||||||
if (AUTO.isActive()) {
|
if (AUTO.isActive()) {
|
||||||
|
@ -230,10 +339,12 @@ public enum GameMod {
|
||||||
* Draws the game mod.
|
* Draws the game mod.
|
||||||
*/
|
*/
|
||||||
public void draw() {
|
public void draw() {
|
||||||
if (!active)
|
if (!implemented) {
|
||||||
button.getImage().setAlpha(0.5f);
|
button.getImage().setAlpha(0.2f);
|
||||||
|
button.draw();
|
||||||
|
button.getImage().setAlpha(1f);
|
||||||
|
} else
|
||||||
button.draw();
|
button.draw();
|
||||||
button.getImage().setAlpha(1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -244,6 +355,16 @@ public enum GameMod {
|
||||||
*/
|
*/
|
||||||
public boolean contains(float x, float y) { return button.contains(x, y); }
|
public boolean contains(float x, float y) { return button.contains(x, y); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the center x coordinate of the button.
|
||||||
|
*/
|
||||||
|
public float getButtonX() { return button.getX(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the center y coordinate of the button.
|
||||||
|
*/
|
||||||
|
public float getButtonY() { return button.getY(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the hover fields for the button.
|
* Resets the hover fields for the button.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,9 +24,8 @@ import org.newdawn.slick.Font;
|
||||||
import org.newdawn.slick.Image;
|
import org.newdawn.slick.Image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience class for menu buttons.
|
* A convenience class for menu buttons, consisting of an image or animation
|
||||||
* Consists of an image or animation and coordinates.
|
* and coordinates. Multi-part images currently do not support effects.
|
||||||
* Multi-part images and animations currently do not support hover updates.
|
|
||||||
*/
|
*/
|
||||||
public class MenuButton {
|
public class MenuButton {
|
||||||
/** The image associated with the button. */
|
/** The image associated with the button. */
|
||||||
|
@ -53,24 +52,30 @@ public class MenuButton {
|
||||||
/** The color to draw the text with. */
|
/** The color to draw the text with. */
|
||||||
private Color color;
|
private Color color;
|
||||||
|
|
||||||
/** Hover action types. */
|
/** Effect types. */
|
||||||
private enum HoverAction { NONE, EXPAND, FADE };
|
private static final int
|
||||||
|
EFFECT_EXPAND = 1,
|
||||||
|
EFFECT_FADE = 2,
|
||||||
|
EFFECT_ROTATE = 4;
|
||||||
|
|
||||||
/** The hover action for this button. */
|
/** The hover actions for this button. */
|
||||||
private HoverAction hoverAction = HoverAction.NONE;
|
private int hoverEffect = 0;
|
||||||
|
|
||||||
/** The current and max scale of the button (for hovering). */
|
/** The current and max scale of the button. */
|
||||||
private float scale = 1f, hoverScale = 1.25f;
|
private float scale = 1f, hoverScale = 1.25f;
|
||||||
|
|
||||||
/** The current and base alpha level of the button (for hovering). */
|
/** The current and base alpha level of the button. */
|
||||||
private float alpha = 1f, baseAlpha = 0.75f;
|
private float alpha = 1f, baseAlpha = 0.75f;
|
||||||
|
|
||||||
/** The scaled expansion direction for the button (for hovering). */
|
/** The scaled expansion direction for the button. */
|
||||||
private Expand dir = Expand.CENTER;
|
private Expand dir = Expand.CENTER;
|
||||||
|
|
||||||
/** Scaled expansion directions (for hovering). */
|
/** Scaled expansion directions. */
|
||||||
public enum Expand { CENTER, UP, RIGHT, LEFT, DOWN, UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT; }
|
public enum Expand { CENTER, UP, RIGHT, LEFT, DOWN, UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT; }
|
||||||
|
|
||||||
|
/** The current and max rotation angles of the button. */
|
||||||
|
private float angle = 0f, maxAngle = 30f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new button from an Image.
|
* Creates a new button from an Image.
|
||||||
* @param img the image
|
* @param img the image
|
||||||
|
@ -162,43 +167,7 @@ public class MenuButton {
|
||||||
/**
|
/**
|
||||||
* Draws the button.
|
* Draws the button.
|
||||||
*/
|
*/
|
||||||
public void draw() {
|
public void draw() { draw(null); }
|
||||||
if (img != null) {
|
|
||||||
if (imgL == null) {
|
|
||||||
if (hoverAction == HoverAction.EXPAND) {
|
|
||||||
Image imgScaled = (scale == 1f) ? img : img.getScaledCopy(scale);
|
|
||||||
imgScaled.setAlpha(img.getAlpha());
|
|
||||||
imgScaled.draw(x - xRadius, y - yRadius);
|
|
||||||
} else if (hoverAction == HoverAction.FADE) {
|
|
||||||
float a = img.getAlpha();
|
|
||||||
img.setAlpha(alpha);
|
|
||||||
img.draw(x - xRadius, y - yRadius);
|
|
||||||
img.setAlpha(a);
|
|
||||||
} else
|
|
||||||
img.draw(x - xRadius, y - yRadius);
|
|
||||||
} else {
|
|
||||||
if (hoverAction == HoverAction.FADE) {
|
|
||||||
float a = img.getAlpha(), aL = imgL.getAlpha(), aR = imgR.getAlpha();
|
|
||||||
img.setAlpha(alpha);
|
|
||||||
imgL.setAlpha(alpha);
|
|
||||||
imgR.setAlpha(alpha);
|
|
||||||
img.draw(x - xRadius + imgL.getWidth(), y - yRadius);
|
|
||||||
imgL.draw(x - xRadius, y - yRadius);
|
|
||||||
imgR.draw(x + xRadius - imgR.getWidth(), y - yRadius);
|
|
||||||
img.setAlpha(a);
|
|
||||||
imgL.setAlpha(aL);
|
|
||||||
imgR.setAlpha(aR);
|
|
||||||
} else {
|
|
||||||
img.draw(x - xRadius + imgL.getWidth(), y - yRadius);
|
|
||||||
imgL.draw(x - xRadius, y - yRadius);
|
|
||||||
imgR.draw(x + xRadius - imgR.getWidth(), y - yRadius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
anim.draw(x - xRadius, y - yRadius);
|
|
||||||
if (text != null)
|
|
||||||
font.drawString(x - font.getWidth(text) / 2f, y - font.getLineHeight() / 2f, text, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the button with a color filter.
|
* Draw the button with a color filter.
|
||||||
|
@ -207,37 +176,48 @@ public class MenuButton {
|
||||||
public void draw(Color filter) {
|
public void draw(Color filter) {
|
||||||
if (img != null) {
|
if (img != null) {
|
||||||
if (imgL == null) {
|
if (imgL == null) {
|
||||||
if (hoverAction == HoverAction.EXPAND) {
|
if (hoverEffect == 0)
|
||||||
Image imgScaled = (scale == 1f) ? img : img.getScaledCopy(scale);
|
Utils.draw(img, x - xRadius, y - yRadius, filter);
|
||||||
imgScaled.setAlpha(img.getAlpha());
|
else {
|
||||||
imgScaled.draw(x - xRadius, y - yRadius, filter);
|
Image hoverImg = img;
|
||||||
} else if (hoverAction == HoverAction.FADE) {
|
float oldAlpha = img.getAlpha();
|
||||||
float a = img.getAlpha();
|
float oldAngle = img.getRotation();
|
||||||
img.setAlpha(alpha);
|
if ((hoverEffect & EFFECT_EXPAND) > 0) {
|
||||||
img.draw(x - xRadius, y - yRadius, filter);
|
if (scale != 1f) {
|
||||||
img.setAlpha(a);
|
hoverImg = hoverImg.getScaledCopy(scale);
|
||||||
} else
|
hoverImg.setAlpha(oldAlpha);
|
||||||
img.draw(x - xRadius, y - yRadius, filter);
|
}
|
||||||
|
}
|
||||||
|
if ((hoverEffect & EFFECT_FADE) > 0)
|
||||||
|
hoverImg.setAlpha(alpha);
|
||||||
|
if ((hoverEffect & EFFECT_ROTATE) > 0)
|
||||||
|
hoverImg.setRotation(angle);
|
||||||
|
Utils.draw(hoverImg, x - xRadius, y - yRadius, filter);
|
||||||
|
if (hoverImg != img) {
|
||||||
|
hoverImg.setAlpha(oldAlpha);
|
||||||
|
hoverImg.setRotation(oldAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hoverAction == HoverAction.FADE) {
|
if (hoverEffect == 0) {
|
||||||
|
Utils.draw(img, x - xRadius + imgL.getWidth(), y - yRadius, filter);
|
||||||
|
Utils.draw(imgL, x - xRadius, y - yRadius, filter);
|
||||||
|
Utils.draw(imgR, x + xRadius - imgR.getWidth(), y - yRadius, filter);
|
||||||
|
} else if ((hoverEffect & EFFECT_FADE) > 0) {
|
||||||
float a = img.getAlpha(), aL = imgL.getAlpha(), aR = imgR.getAlpha();
|
float a = img.getAlpha(), aL = imgL.getAlpha(), aR = imgR.getAlpha();
|
||||||
img.setAlpha(alpha);
|
img.setAlpha(alpha);
|
||||||
imgL.setAlpha(alpha);
|
imgL.setAlpha(alpha);
|
||||||
imgR.setAlpha(alpha);
|
imgR.setAlpha(alpha);
|
||||||
img.draw(x - xRadius + imgL.getWidth(), y - yRadius, filter);
|
Utils.draw(img, x - xRadius + imgL.getWidth(), y - yRadius, filter);
|
||||||
imgL.draw(x - xRadius, y - yRadius, filter);
|
Utils.draw(imgL, x - xRadius, y - yRadius, filter);
|
||||||
imgR.draw(x + xRadius - imgR.getWidth(), y - yRadius, filter);
|
Utils.draw(imgR, x + xRadius - imgR.getWidth(), y - yRadius, filter);
|
||||||
img.setAlpha(a);
|
img.setAlpha(a);
|
||||||
imgL.setAlpha(aL);
|
imgL.setAlpha(aL);
|
||||||
imgR.setAlpha(aR);
|
imgR.setAlpha(aR);
|
||||||
} else {
|
|
||||||
img.draw(x - xRadius + imgL.getWidth(), y - yRadius, filter);
|
|
||||||
imgL.draw(x - xRadius, y - yRadius, filter);
|
|
||||||
imgR.draw(x + xRadius - imgR.getWidth(), y - yRadius, filter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
anim.draw(x - xRadius, y - yRadius, filter);
|
Utils.draw(anim, x - xRadius, y - yRadius, filter);
|
||||||
if (text != null)
|
if (text != null)
|
||||||
font.drawString(x - font.getWidth(text) / 2f, y - font.getLineHeight() / 2f, text, color);
|
font.drawString(x - font.getWidth(text) / 2f, y - font.getLineHeight() / 2f, text, color);
|
||||||
}
|
}
|
||||||
|
@ -256,55 +236,77 @@ public class MenuButton {
|
||||||
* Resets the hover fields for the button.
|
* Resets the hover fields for the button.
|
||||||
*/
|
*/
|
||||||
public void resetHover() {
|
public void resetHover() {
|
||||||
if (hoverAction == HoverAction.EXPAND) {
|
if ((hoverEffect & EFFECT_EXPAND) > 0) {
|
||||||
this.scale = 1f;
|
this.scale = 1f;
|
||||||
setHoverRadius();
|
setHoverRadius();
|
||||||
} else if (hoverAction == HoverAction.FADE)
|
}
|
||||||
|
if ((hoverEffect & EFFECT_FADE) > 0)
|
||||||
this.alpha = baseAlpha;
|
this.alpha = baseAlpha;
|
||||||
|
if ((hoverEffect & EFFECT_ROTATE) > 0)
|
||||||
|
this.angle = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "expand".
|
* Removes all hover effects that have been set for the button.
|
||||||
*/
|
*/
|
||||||
public void setHoverExpand() { this.hoverAction = HoverAction.EXPAND; }
|
public void removeHoverEffects() { hoverEffect = 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "expand".
|
* Sets the "expand" hover effect.
|
||||||
|
*/
|
||||||
|
public void setHoverExpand() { hoverEffect |= EFFECT_EXPAND; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "expand" hover effect.
|
||||||
* @param scale the maximum scale factor (default 1.25f)
|
* @param scale the maximum scale factor (default 1.25f)
|
||||||
*/
|
*/
|
||||||
public void setHoverExpand(float scale) { setHoverExpand(scale, this.dir); }
|
public void setHoverExpand(float scale) { setHoverExpand(scale, this.dir); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "expand".
|
* Sets the "expand" hover effect.
|
||||||
* @param dir the expansion direction
|
* @param dir the expansion direction
|
||||||
*/
|
*/
|
||||||
public void setHoverExpand(Expand dir) { setHoverExpand(this.hoverScale, dir); }
|
public void setHoverExpand(Expand dir) { setHoverExpand(this.hoverScale, dir); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "expand".
|
* Sets the "expand" hover effect.
|
||||||
* @param scale the maximum scale factor (default 1.25f)
|
* @param scale the maximum scale factor (default 1.25f)
|
||||||
* @param dir the expansion direction
|
* @param dir the expansion direction
|
||||||
*/
|
*/
|
||||||
public void setHoverExpand(float scale, Expand dir) {
|
public void setHoverExpand(float scale, Expand dir) {
|
||||||
this.hoverAction = HoverAction.EXPAND;
|
hoverEffect |= EFFECT_EXPAND;
|
||||||
this.hoverScale = scale;
|
this.hoverScale = scale;
|
||||||
this.dir = dir;
|
this.dir = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "fade".
|
* Sets the "fade" hover effect.
|
||||||
*/
|
*/
|
||||||
public void setHoverFade() { this.hoverAction = HoverAction.FADE; }
|
public void setHoverFade() { hoverEffect |= EFFECT_FADE; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the hover action to "fade".
|
* Sets the "fade" hover effect.
|
||||||
* @param baseAlpha the base alpha level to fade in from (default 0.7f)
|
* @param baseAlpha the base alpha level to fade in from (default 0.7f)
|
||||||
*/
|
*/
|
||||||
public void setHoverFade(float baseAlpha) {
|
public void setHoverFade(float baseAlpha) {
|
||||||
this.hoverAction = HoverAction.FADE;
|
hoverEffect |= EFFECT_FADE;
|
||||||
this.baseAlpha = baseAlpha;
|
this.baseAlpha = baseAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "rotate" hover effect.
|
||||||
|
*/
|
||||||
|
public void setHoverRotate() { hoverEffect |= EFFECT_ROTATE; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "rotate" hover effect.
|
||||||
|
* @param maxAngle the maximum rotation angle, in degrees (default 30f)
|
||||||
|
*/
|
||||||
|
public void setHoverRotate(float maxAngle) {
|
||||||
|
hoverEffect |= EFFECT_ROTATE;
|
||||||
|
this.maxAngle = maxAngle;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a hover action depending on whether or not the cursor
|
* Processes a hover action depending on whether or not the cursor
|
||||||
* is hovering over the button.
|
* is hovering over the button.
|
||||||
|
@ -313,32 +315,49 @@ public class MenuButton {
|
||||||
* @param cy the y coordinate
|
* @param cy the y coordinate
|
||||||
*/
|
*/
|
||||||
public void hoverUpdate(int delta, float cx, float cy) {
|
public void hoverUpdate(int delta, float cx, float cy) {
|
||||||
if (hoverAction == HoverAction.NONE)
|
if (hoverEffect == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boolean isHover = contains(cx, cy);
|
boolean isHover = contains(cx, cy);
|
||||||
if (hoverAction == HoverAction.EXPAND) {
|
|
||||||
// scale the button
|
// scale the button
|
||||||
int sign;
|
if ((hoverEffect & EFFECT_EXPAND) > 0) {
|
||||||
|
int sign = 0;
|
||||||
if (isHover && scale < hoverScale)
|
if (isHover && scale < hoverScale)
|
||||||
sign = 1;
|
sign = 1;
|
||||||
else if (!isHover && scale > 1f)
|
else if (!isHover && scale > 1f)
|
||||||
sign = -1;
|
sign = -1;
|
||||||
else
|
if (sign != 0) {
|
||||||
return;
|
|
||||||
scale = Utils.getBoundedValue(scale, sign * (hoverScale - 1f) * delta / 100f, 1, hoverScale);
|
scale = Utils.getBoundedValue(scale, sign * (hoverScale - 1f) * delta / 100f, 1, hoverScale);
|
||||||
setHoverRadius();
|
setHoverRadius();
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fade the button
|
// fade the button
|
||||||
int sign;
|
if ((hoverEffect & EFFECT_FADE) > 0) {
|
||||||
|
int sign = 0;
|
||||||
if (isHover && alpha < 1f)
|
if (isHover && alpha < 1f)
|
||||||
sign = 1;
|
sign = 1;
|
||||||
else if (!isHover && alpha > baseAlpha)
|
else if (!isHover && alpha > baseAlpha)
|
||||||
sign = -1;
|
sign = -1;
|
||||||
else
|
if (sign != 0)
|
||||||
return;
|
|
||||||
alpha = Utils.getBoundedValue(alpha, sign * (1f - baseAlpha) * delta / 200f, baseAlpha, 1f);
|
alpha = Utils.getBoundedValue(alpha, sign * (1f - baseAlpha) * delta / 200f, baseAlpha, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rotate the button
|
||||||
|
if ((hoverEffect & EFFECT_ROTATE) > 0) {
|
||||||
|
int sign = 0;
|
||||||
|
boolean right = (maxAngle > 0);
|
||||||
|
if (isHover && angle != maxAngle)
|
||||||
|
sign = (right) ? 1 : -1;
|
||||||
|
else if (!isHover && angle != 0)
|
||||||
|
sign = (right) ? -1 : 1;
|
||||||
|
if (sign != 0) {
|
||||||
|
float diff = sign * Math.abs(maxAngle) * delta / 125f;
|
||||||
|
angle = (right) ?
|
||||||
|
Utils.getBoundedValue(angle, diff, 0, maxAngle) :
|
||||||
|
Utils.getBoundedValue(angle, diff, maxAngle, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -54,38 +54,55 @@ public class OsuGroupNode {
|
||||||
* Draws the button.
|
* Draws the button.
|
||||||
* @param x the x coordinate
|
* @param x the x coordinate
|
||||||
* @param y the y coordinate
|
* @param y the y coordinate
|
||||||
|
* @param headerY the header end y coordinate (for cropping)
|
||||||
|
* @param footerY the footer start y coordinate (for cropping)
|
||||||
* @param grade the highest grade, if any
|
* @param grade the highest grade, if any
|
||||||
* @param focus true if this is the focused node
|
* @param focus true if this is the focused node
|
||||||
*/
|
*/
|
||||||
public void draw(float x, float y, Grade grade, boolean focus) {
|
public void draw(float x, float y, float headerY, float footerY, Grade grade, boolean focus) {
|
||||||
boolean expanded = (osuFileIndex > -1);
|
boolean expanded = (osuFileIndex > -1);
|
||||||
float xOffset = 0f;
|
float xOffset = 0f;
|
||||||
OsuFile osu;
|
OsuFile osu;
|
||||||
Color textColor = Color.lightGray;
|
|
||||||
Image bg = GameImage.MENU_BUTTON_BG.getImage();
|
Image bg = GameImage.MENU_BUTTON_BG.getImage();
|
||||||
bg.setAlpha(0.9f);
|
bg.setAlpha(0.9f);
|
||||||
|
Color bgColor;
|
||||||
|
Color textColor = Color.lightGray;
|
||||||
|
|
||||||
if (expanded) { // expanded
|
if (expanded) { // expanded
|
||||||
xOffset = bg.getWidth() / 10f;
|
x -= bg.getWidth() / 10f;
|
||||||
if (focus) {
|
if (focus) {
|
||||||
bg.draw(x - xOffset, y, Color.white);
|
bgColor = Color.white;
|
||||||
textColor = Color.white;
|
textColor = Color.white;
|
||||||
} else
|
} else
|
||||||
bg.draw(x - xOffset, y, Utils.COLOR_BLUE_BUTTON);
|
bgColor = Utils.COLOR_BLUE_BUTTON;
|
||||||
osu = osuFiles.get(osuFileIndex);
|
osu = osuFiles.get(osuFileIndex);
|
||||||
} else {
|
} else {
|
||||||
bg.draw(x, y, Utils.COLOR_ORANGE_BUTTON);
|
bgColor = Utils.COLOR_ORANGE_BUTTON;
|
||||||
osu = osuFiles.get(0);
|
osu = osuFiles.get(0);
|
||||||
}
|
}
|
||||||
|
// crop image if necessary
|
||||||
|
if (y < headerY) {
|
||||||
|
int cropHeight = (int) (headerY - y);
|
||||||
|
Image bgCropped = bg.getSubImage(0, cropHeight, bg.getWidth(), bg.getHeight() - cropHeight);
|
||||||
|
bgCropped.draw(x, headerY, bgColor);
|
||||||
|
} else if (y + bg.getHeight() > footerY) {
|
||||||
|
int cropHeight = (int) (footerY - y);
|
||||||
|
Image bgCropped = bg.getSubImage(0, 0, bg.getWidth(), cropHeight);
|
||||||
|
bgCropped.draw(x, y, bgColor);
|
||||||
|
} else
|
||||||
|
bg.draw(x, y, bgColor);
|
||||||
|
|
||||||
float cx = x + (bg.getWidth() * 0.05f) - xOffset;
|
float cx = x + (bg.getWidth() * 0.05f);
|
||||||
float cy = y + (bg.getHeight() * 0.2f) - 3;
|
float cy = y + (bg.getHeight() * 0.2f) - 3;
|
||||||
|
|
||||||
|
// draw grade
|
||||||
if (grade != Grade.NULL) {
|
if (grade != Grade.NULL) {
|
||||||
Image gradeImg = grade.getMenuImage();
|
Image gradeImg = grade.getMenuImage();
|
||||||
gradeImg.drawCentered(cx - bg.getWidth() * 0.01f + gradeImg.getWidth() / 2f, y + bg.getHeight() / 2.2f);
|
gradeImg.drawCentered(cx - bg.getWidth() * 0.01f + gradeImg.getWidth() / 2f, y + bg.getHeight() / 2.2f);
|
||||||
cx += gradeImg.getWidth();
|
cx += gradeImg.getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw text
|
||||||
Utils.FONT_MEDIUM.drawString(cx, cy, osu.getTitle(), textColor);
|
Utils.FONT_MEDIUM.drawString(cx, cy, osu.getTitle(), textColor);
|
||||||
Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 4,
|
Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 4,
|
||||||
String.format("%s // %s", osu.getArtist(), osu.creator), textColor);
|
String.format("%s // %s", osu.getArtist(), osu.creator), textColor);
|
||||||
|
|
|
@ -78,13 +78,13 @@ public class ScoreData implements Comparable<ScoreData> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the base coordinates for drawing.
|
* Initializes the base coordinates for drawing.
|
||||||
* @param width the container width
|
* @param containerWidth the container width
|
||||||
* @param height the container height
|
* @param topY the top y coordinate
|
||||||
*/
|
*/
|
||||||
public static void init(int width, int height) {
|
public static void init(int containerWidth, float topY) {
|
||||||
baseX = width * 0.01f;
|
baseX = containerWidth * 0.01f;
|
||||||
baseY = height * 0.16f;
|
baseY = topY;
|
||||||
buttonWidth = width * 0.4f;
|
buttonWidth = containerWidth * 0.4f;
|
||||||
float gradeHeight = GameImage.MENU_BUTTON_BG.getImage().getHeight() * 0.45f;
|
float gradeHeight = GameImage.MENU_BUTTON_BG.getImage().getHeight() * 0.45f;
|
||||||
buttonHeight = Math.max(gradeHeight, Utils.FONT_DEFAULT.getLineHeight() * 3.03f);
|
buttonHeight = Math.max(gradeHeight, Utils.FONT_DEFAULT.getLineHeight() * 3.03f);
|
||||||
buttonOffset = buttonHeight + gradeHeight / 10f;
|
buttonOffset = buttonHeight + gradeHeight / 10f;
|
||||||
|
|
|
@ -145,21 +145,21 @@ public enum SongSort {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the sort tab.
|
* Initializes the sort tab.
|
||||||
* @param width the container width
|
* @param containerWidth the container width
|
||||||
* @param height the container height
|
* @param bottomY the bottom y coordinate
|
||||||
*/
|
*/
|
||||||
public void init(int width, int height) {
|
public void init(int containerWidth, float bottomY) {
|
||||||
Image tab = GameImage.MENU_TAB.getImage();
|
Image tab = GameImage.MENU_TAB.getImage();
|
||||||
int tabWidth = tab.getWidth();
|
int tabWidth = tab.getWidth();
|
||||||
float buttonX = width / 2f;
|
float buttonX = containerWidth / 2f;
|
||||||
float tabOffset = (width - buttonX - tabWidth) / (SIZE - 1);
|
float tabOffset = (containerWidth - buttonX - tabWidth) / (SIZE - 1);
|
||||||
if (tabOffset > tabWidth) { // prevent tabs from being spaced out
|
if (tabOffset > tabWidth) { // prevent tabs from being spaced out
|
||||||
tabOffset = tabWidth;
|
tabOffset = tabWidth;
|
||||||
buttonX = (width * 0.99f) - (tabWidth * SIZE);
|
buttonX = (containerWidth * 0.99f) - (tabWidth * SIZE);
|
||||||
}
|
}
|
||||||
this.tab = new MenuButton(tab,
|
this.tab = new MenuButton(tab,
|
||||||
(buttonX + (tabWidth / 2f)) + (id * tabOffset),
|
(buttonX + (tabWidth / 2f)) + (id * tabOffset),
|
||||||
(height * 0.15f) - (tab.getHeight() / 2f) - 2f
|
bottomY - (tab.getHeight() / 2f)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,8 @@ public class Utils {
|
||||||
COLOR_GREEN = new Color(137, 201, 79),
|
COLOR_GREEN = new Color(137, 201, 79),
|
||||||
COLOR_LIGHT_ORANGE = new Color(255,192,128),
|
COLOR_LIGHT_ORANGE = new Color(255,192,128),
|
||||||
COLOR_LIGHT_GREEN = new Color(128,255,128),
|
COLOR_LIGHT_GREEN = new Color(128,255,128),
|
||||||
COLOR_LIGHT_BLUE = new Color(128,128,255);
|
COLOR_LIGHT_BLUE = new Color(128,128,255),
|
||||||
|
COLOR_GREEN_SEARCH = new Color(173, 255, 47);
|
||||||
|
|
||||||
/** The default map colors, used when a map does not provide custom colors. */
|
/** The default map colors, used when a map does not provide custom colors. */
|
||||||
public static final Color[] DEFAULT_COMBO = {
|
public static final Color[] DEFAULT_COMBO = {
|
||||||
|
@ -212,19 +213,11 @@ public class Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize game mods
|
// initialize game mods
|
||||||
for (GameMod mod : GameMod.values())
|
GameMod.init(width, height);
|
||||||
mod.init(width, height);
|
|
||||||
|
|
||||||
// initialize sorts
|
|
||||||
for (SongSort sort : SongSort.values())
|
|
||||||
sort.init(width, height);
|
|
||||||
|
|
||||||
// initialize hit objects
|
// initialize hit objects
|
||||||
OsuHitObject.init(width, height);
|
OsuHitObject.init(width, height);
|
||||||
|
|
||||||
// initialize score data buttons
|
|
||||||
ScoreData.init(width, height);
|
|
||||||
|
|
||||||
// initialize download nodes
|
// initialize download nodes
|
||||||
DownloadNode.init(width, height);
|
DownloadNode.init(width, height);
|
||||||
|
|
||||||
|
@ -297,6 +290,34 @@ public class Utils {
|
||||||
anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f));
|
anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an image at the given location.
|
||||||
|
* @param img the image to draw
|
||||||
|
* @param x the x coordinate
|
||||||
|
* @param y the y coordinate
|
||||||
|
* @param color the color filter to apply
|
||||||
|
*/
|
||||||
|
public static void draw(Image img, float x, float y, Color color) {
|
||||||
|
if (color == null)
|
||||||
|
img.draw(x, y);
|
||||||
|
else
|
||||||
|
img.draw(x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an animation at the given location.
|
||||||
|
* @param anim the animation to draw
|
||||||
|
* @param x the x coordinate
|
||||||
|
* @param y the y coordinate
|
||||||
|
* @param color the color filter to apply
|
||||||
|
*/
|
||||||
|
public static void draw(Animation anim, float x, float y, Color color) {
|
||||||
|
if (color == null)
|
||||||
|
anim.draw(x, y);
|
||||||
|
else
|
||||||
|
anim.draw(x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a bounded value for a base value and displacement.
|
* Returns a bounded value for a base value and displacement.
|
||||||
* @param base the initial value
|
* @param base the initial value
|
||||||
|
|
|
@ -248,11 +248,16 @@ public class MusicController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays the current track.
|
* Plays the current track.
|
||||||
|
* @param loop whether or not to loop the track
|
||||||
*/
|
*/
|
||||||
public static void play() {
|
public static void play(boolean loop) {
|
||||||
if (trackExists())
|
if (trackExists()) {
|
||||||
|
if (loop)
|
||||||
|
player.loop();
|
||||||
|
else
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the music volume.
|
* Sets the music volume.
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package itdelatrisu.opsu.states;
|
package itdelatrisu.opsu.states;
|
||||||
|
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
|
import itdelatrisu.opsu.GameMod;
|
||||||
import itdelatrisu.opsu.MenuButton;
|
import itdelatrisu.opsu.MenuButton;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
import itdelatrisu.opsu.OsuGroupList;
|
import itdelatrisu.opsu.OsuGroupList;
|
||||||
|
@ -121,6 +122,95 @@ public class ButtonMenu extends BasicGameState {
|
||||||
public void leave(GameContainer container, StateBasedGame game) {
|
public void leave(GameContainer container, StateBasedGame game) {
|
||||||
Button.CLOSE.click(container, game);
|
Button.CLOSE.click(container, game);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
MODS (new Button[] { Button.RESET_MODS, Button.CLOSE }) {
|
||||||
|
@Override
|
||||||
|
public String[] getTitle(GameContainer container, StateBasedGame game) {
|
||||||
|
return new String[] {
|
||||||
|
"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getBaseY(GameContainer container, StateBasedGame game) {
|
||||||
|
return container.getHeight() * 2f / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter(GameContainer container, StateBasedGame game) {
|
||||||
|
super.enter(container, game);
|
||||||
|
for (GameMod mod : GameMod.values())
|
||||||
|
mod.resetHover();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leave(GameContainer container, StateBasedGame game) {
|
||||||
|
Button.CLOSE.click(container, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(GameContainer container, StateBasedGame game, Graphics g) {
|
||||||
|
super.draw(container, game, g);
|
||||||
|
|
||||||
|
int width = container.getWidth();
|
||||||
|
int height = container.getHeight();
|
||||||
|
|
||||||
|
// score multiplier (TODO: fade in color changes)
|
||||||
|
float mult = GameMod.getScoreMultiplier();
|
||||||
|
String multString = String.format("Score Multiplier: %.2fx", mult);
|
||||||
|
Color multColor = (mult == 1f) ? Color.white : (mult > 1f) ? Color.green : Color.red;
|
||||||
|
float multY = Utils.FONT_LARGE.getLineHeight() * 2 + height * 0.06f;
|
||||||
|
Utils.FONT_LARGE.drawString(
|
||||||
|
(width - Utils.FONT_LARGE.getWidth(multString)) / 2f,
|
||||||
|
multY, multString, multColor);
|
||||||
|
|
||||||
|
// category text
|
||||||
|
for (GameMod.Category category : GameMod.Category.values()) {
|
||||||
|
Utils.FONT_LARGE.drawString(category.getX(),
|
||||||
|
category.getY() - Utils.FONT_LARGE.getLineHeight() / 2f,
|
||||||
|
category.getName(), category.getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
// buttons (TODO: draw descriptions when hovering)
|
||||||
|
for (GameMod mod : GameMod.values())
|
||||||
|
mod.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(GameContainer container, int delta, int mouseX, int mouseY) {
|
||||||
|
super.update(container, delta, mouseX, mouseY);
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
if (mod.isActive())
|
||||||
|
mod.hoverUpdate(delta, mod.getButtonX(), mod.getButtonY());
|
||||||
|
else
|
||||||
|
mod.hoverUpdate(delta, -1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) {
|
||||||
|
super.keyPress(container, game, key, c);
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
if (key == mod.getKey()) {
|
||||||
|
mod.toggle(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void click(GameContainer container, StateBasedGame game, int cx, int cy) {
|
||||||
|
super.click(container, game, cx, cy);
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
if (mod.contains(cx, cy)) {
|
||||||
|
boolean prevState = mod.isActive();
|
||||||
|
mod.toggle(true);
|
||||||
|
if (mod.isActive() != prevState)
|
||||||
|
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The buttons in the state. */
|
/** The buttons in the state. */
|
||||||
|
@ -153,23 +243,29 @@ public class ButtonMenu extends BasicGameState {
|
||||||
*/
|
*/
|
||||||
public void init(GameContainer container, StateBasedGame game, Image button, Image buttonL, Image buttonR) {
|
public void init(GameContainer container, StateBasedGame game, Image button, Image buttonL, Image buttonR) {
|
||||||
float center = container.getWidth() / 2f;
|
float center = container.getWidth() / 2f;
|
||||||
float centerOffset = container.getWidth() * OFFSET_WIDTH_RATIO;
|
float baseY = getBaseY(container, game);
|
||||||
float baseY = container.getHeight() * 0.2f;
|
|
||||||
baseY += ((getTitle(container, game).length - 1) * Utils.FONT_LARGE.getLineHeight());
|
|
||||||
float offsetY = button.getHeight() * 1.25f;
|
float offsetY = button.getHeight() * 1.25f;
|
||||||
|
|
||||||
menuButtons = new MenuButton[buttons.length];
|
menuButtons = new MenuButton[buttons.length];
|
||||||
for (int i = 0; i < buttons.length; i++) {
|
for (int i = 0; i < buttons.length; i++) {
|
||||||
MenuButton b = new MenuButton(button, buttonL, buttonR,
|
MenuButton b = new MenuButton(button, buttonL, buttonR, center, baseY + (i * offsetY));
|
||||||
center + ((i % 2 == 0) ? centerOffset * -1 : centerOffset),
|
b.setText(String.format("%d. %s", i + 1, buttons[i].getText()), Utils.FONT_XLARGE, Color.white);
|
||||||
baseY + (i * offsetY));
|
|
||||||
b.setText(String.format("%d. %s", i + 1, buttons[i].getText()),
|
|
||||||
Utils.FONT_XLARGE, Color.white);
|
|
||||||
b.setHoverFade();
|
b.setHoverFade();
|
||||||
menuButtons[i] = b;
|
menuButtons[i] = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base Y coordinate for the buttons.
|
||||||
|
* @param container the game container
|
||||||
|
* @param game the game
|
||||||
|
*/
|
||||||
|
protected float getBaseY(GameContainer container, StateBasedGame game) {
|
||||||
|
float baseY = container.getHeight() * 0.2f;
|
||||||
|
baseY += ((getTitle(container, game).length - 1) * Utils.FONT_LARGE.getLineHeight());
|
||||||
|
return baseY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the title and buttons to the graphics context.
|
* Draws the title and buttons to the graphics context.
|
||||||
* @param container the game container
|
* @param container the game container
|
||||||
|
@ -231,13 +327,14 @@ public class ButtonMenu extends BasicGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a key press action (numeric digits only).
|
* Processes a key press action.
|
||||||
* @param container the game container
|
* @param container the game container
|
||||||
* @param game the game
|
* @param game the game
|
||||||
* @param digit the digit pressed
|
* @param key the key code that was pressed (see {@link org.newdawn.slick.Input})
|
||||||
|
* @param c the character of the key that was pressed
|
||||||
*/
|
*/
|
||||||
public void keyPress(GameContainer container, StateBasedGame game, int digit) {
|
public void keyPress(GameContainer container, StateBasedGame game, int key, char c) {
|
||||||
int index = digit - 1;
|
int index = Character.getNumericValue(c) - 1;
|
||||||
if (index >= 0 && index < buttons.length)
|
if (index >= 0 && index < buttons.length)
|
||||||
buttons[index].click(container, game);
|
buttons[index].click(container, game);
|
||||||
}
|
}
|
||||||
|
@ -384,6 +481,16 @@ public class ButtonMenu extends BasicGameState {
|
||||||
public void click(GameContainer container, StateBasedGame game) {
|
public void click(GameContainer container, StateBasedGame game) {
|
||||||
CANCEL.click(container, game);
|
CANCEL.click(container, game);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
RESET_MODS ("Reset All Mods", Color.red) {
|
||||||
|
@Override
|
||||||
|
public void click(GameContainer container, StateBasedGame game) {
|
||||||
|
SoundController.playSound(SoundEffect.MENUHIT);
|
||||||
|
for (GameMod mod : GameMod.values()) {
|
||||||
|
if (mod.isActive())
|
||||||
|
mod.toggle(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The text to show on the button. */
|
/** The text to show on the button. */
|
||||||
|
@ -500,7 +607,7 @@ public class ButtonMenu extends BasicGameState {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (menuState != null)
|
if (menuState != null)
|
||||||
menuState.keyPress(container, game, Character.getNumericValue(c));
|
menuState.keyPress(container, game, key, c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,7 @@ public class DownloadsMenu extends BasicGameState {
|
||||||
|
|
||||||
// search
|
// search
|
||||||
g.setColor(Color.white);
|
g.setColor(Color.white);
|
||||||
|
g.setLineWidth(2f);
|
||||||
search.render(container, g);
|
search.render(container, g);
|
||||||
Utils.FONT_BOLD.drawString(
|
Utils.FONT_BOLD.drawString(
|
||||||
search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f,
|
search.getX() + search.getWidth() * 0.01f, search.getY() + search.getHeight() * 1.3f,
|
||||||
|
|
|
@ -701,7 +701,7 @@ public class Game extends BasicGameState {
|
||||||
resetGameData();
|
resetGameData();
|
||||||
|
|
||||||
// needs to play before setting position to resume without lag later
|
// needs to play before setting position to resume without lag later
|
||||||
MusicController.play();
|
MusicController.play(false);
|
||||||
MusicController.setPosition(0);
|
MusicController.setPosition(0);
|
||||||
MusicController.pause();
|
MusicController.pause();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package itdelatrisu.opsu.states;
|
package itdelatrisu.opsu.states;
|
||||||
|
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
import itdelatrisu.opsu.GameMod;
|
|
||||||
import itdelatrisu.opsu.MenuButton;
|
import itdelatrisu.opsu.MenuButton;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
import itdelatrisu.opsu.Options;
|
import itdelatrisu.opsu.Options;
|
||||||
|
@ -171,10 +170,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
int width = container.getWidth();
|
int width = container.getWidth();
|
||||||
int height = container.getHeight();
|
int height = container.getHeight();
|
||||||
|
|
||||||
// game option coordinate modifiers
|
|
||||||
textY = 20 + (Utils.FONT_XLARGE.getLineHeight() * 3 / 2);
|
|
||||||
offsetY = (int) (((height * 0.8f) - textY) / maxOptionsScreen);
|
|
||||||
|
|
||||||
// option tabs
|
// option tabs
|
||||||
Image tabImage = GameImage.MENU_TAB.getImage();
|
Image tabImage = GameImage.MENU_TAB.getImage();
|
||||||
int subtextWidth = Utils.FONT_DEFAULT.getWidth("Click or drag an option to change it.");
|
int subtextWidth = Utils.FONT_DEFAULT.getWidth("Click or drag an option to change it.");
|
||||||
|
@ -184,6 +179,10 @@ public class OptionsMenu extends BasicGameState {
|
||||||
(width/2) / OptionTab.SIZE);
|
(width/2) / OptionTab.SIZE);
|
||||||
for (OptionTab tab : OptionTab.values())
|
for (OptionTab tab : OptionTab.values())
|
||||||
tab.button = new MenuButton(tabImage, tabX + (tab.ordinal() * tabOffset), tabY);
|
tab.button = new MenuButton(tabImage, tabX + (tab.ordinal() * tabOffset), tabY);
|
||||||
|
|
||||||
|
// game option coordinate modifiers
|
||||||
|
textY = (int) (tabY + tabImage.getHeight());
|
||||||
|
offsetY = (height - textY - GameImage.MENU_BACK.getImage().getHeight()) / maxOptionsScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -232,21 +231,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
g.drawLine(0, lineY, width, lineY);
|
g.drawLine(0, lineY, width, lineY);
|
||||||
g.resetLineWidth();
|
g.resetLineWidth();
|
||||||
|
|
||||||
// game mods
|
|
||||||
Utils.FONT_LARGE.drawString(width / 30, height * 0.8f, "Game Mods:", Color.white);
|
|
||||||
boolean descDrawn = false;
|
|
||||||
for (GameMod mod : GameMod.values()) {
|
|
||||||
mod.draw();
|
|
||||||
if (!descDrawn && mod.contains(mouseX, mouseY)) {
|
|
||||||
Utils.FONT_DEFAULT.drawString(
|
|
||||||
(width - Utils.FONT_DEFAULT.getWidth(mod.getDescription())) / 2,
|
|
||||||
height * 0.975f - Utils.FONT_DEFAULT.getLineHeight(),
|
|
||||||
mod.getDescription(), Color.white
|
|
||||||
);
|
|
||||||
descDrawn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.getBackButton().draw();
|
Utils.getBackButton().draw();
|
||||||
|
|
||||||
// key entry state
|
// key entry state
|
||||||
|
@ -272,8 +256,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
Utils.updateVolumeDisplay(delta);
|
Utils.updateVolumeDisplay(delta);
|
||||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||||
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||||
for (GameMod mod : GameMod.values())
|
|
||||||
mod.hoverUpdate(delta, mouseX, mouseY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -309,17 +291,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// game mods
|
|
||||||
for (GameMod mod : GameMod.values()) {
|
|
||||||
if (mod.contains(x, y)) {
|
|
||||||
boolean prevState = mod.isActive();
|
|
||||||
mod.toggle(true);
|
|
||||||
if (mod.isActive() != prevState)
|
|
||||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// options (click only)
|
// options (click only)
|
||||||
GameOption option = getClickedOption(y);
|
GameOption option = getClickedOption(y);
|
||||||
if (option != GameOption.NULL)
|
if (option != GameOption.NULL)
|
||||||
|
@ -400,15 +371,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
currentTab = currentTab.next();
|
currentTab = currentTab.next();
|
||||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
// check mod shortcut keys
|
|
||||||
for (GameMod mod : GameMod.values()) {
|
|
||||||
if (key == mod.getKey()) {
|
|
||||||
mod.toggle(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,8 +379,6 @@ public class OptionsMenu extends BasicGameState {
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
currentTab = OptionTab.DISPLAY;
|
currentTab = OptionTab.DISPLAY;
|
||||||
Utils.getBackButton().resetHover();
|
Utils.getBackButton().resetHover();
|
||||||
for (GameMod mod : GameMod.values())
|
|
||||||
mod.resetHover();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -83,6 +83,12 @@ public class SongMenu extends BasicGameState {
|
||||||
/** Maximum x offset of song buttons for mouse hover, in pixels. */
|
/** Maximum x offset of song buttons for mouse hover, in pixels. */
|
||||||
private static final float MAX_HOVER_OFFSET = 30f;
|
private static final float MAX_HOVER_OFFSET = 30f;
|
||||||
|
|
||||||
|
/** Time, in milliseconds, for the search bar to fade in or out. */
|
||||||
|
private static final int SEARCH_TRANSITION_TIME = 250;
|
||||||
|
|
||||||
|
/** Line width of the header/footer divider. */
|
||||||
|
private static final int DIVIDER_LINE_WIDTH = 4;
|
||||||
|
|
||||||
/** Song node class representing an OsuGroupNode and file index. */
|
/** Song node class representing an OsuGroupNode and file index. */
|
||||||
private static class SongNode {
|
private static class SongNode {
|
||||||
/** Song node. */
|
/** Song node. */
|
||||||
|
@ -136,8 +142,8 @@ public class SongMenu extends BasicGameState {
|
||||||
/** Current index of hovered song button. */
|
/** Current index of hovered song button. */
|
||||||
private int hoverIndex = -1;
|
private int hoverIndex = -1;
|
||||||
|
|
||||||
/** The options button (to enter the "Game Options" menu). */
|
/** The selection buttons. */
|
||||||
private MenuButton optionsButton;
|
private MenuButton selectModsButton, selectRandomButton, selectMapOptionsButton, selectOptionsButton;
|
||||||
|
|
||||||
/** The search textfield. */
|
/** The search textfield. */
|
||||||
private TextField search;
|
private TextField search;
|
||||||
|
@ -146,10 +152,10 @@ public class SongMenu extends BasicGameState {
|
||||||
* Delay timer, in milliseconds, before running another search.
|
* Delay timer, in milliseconds, before running another search.
|
||||||
* This is overridden by character entry (reset) and 'esc' (immediate search).
|
* This is overridden by character entry (reset) and 'esc' (immediate search).
|
||||||
*/
|
*/
|
||||||
private int searchTimer;
|
private int searchTimer = 0;
|
||||||
|
|
||||||
/** Information text to display based on the search query. */
|
/** Information text to display based on the search query. */
|
||||||
private String searchResultString;
|
private String searchResultString = null;
|
||||||
|
|
||||||
/** Loader animation. */
|
/** Loader animation. */
|
||||||
private Animation loader;
|
private Animation loader;
|
||||||
|
@ -184,6 +190,12 @@ public class SongMenu extends BasicGameState {
|
||||||
/** Current start score (topmost score entry). */
|
/** Current start score (topmost score entry). */
|
||||||
private int startScore = 0;
|
private int startScore = 0;
|
||||||
|
|
||||||
|
/** Header and footer end and start y coordinates, respectively. */
|
||||||
|
private float headerY, footerY;
|
||||||
|
|
||||||
|
/** Time, in milliseconds, for fading the search bar. */
|
||||||
|
private int searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||||
|
|
||||||
// game-related variables
|
// game-related variables
|
||||||
private GameContainer container;
|
private GameContainer container;
|
||||||
private StateBasedGame game;
|
private StateBasedGame game;
|
||||||
|
@ -204,26 +216,35 @@ public class SongMenu extends BasicGameState {
|
||||||
int width = container.getWidth();
|
int width = container.getWidth();
|
||||||
int height = container.getHeight();
|
int height = container.getHeight();
|
||||||
|
|
||||||
|
// header/footer coordinates
|
||||||
|
headerY = height * 0.0075f + GameImage.MENU_MUSICNOTE.getImage().getHeight() +
|
||||||
|
Utils.FONT_BOLD.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() +
|
||||||
|
Utils.FONT_SMALL.getLineHeight();
|
||||||
|
footerY = height - GameImage.SELECTION_MODS.getImage().getHeight();
|
||||||
|
|
||||||
|
// initialize sorts
|
||||||
|
for (SongSort sort : SongSort.values())
|
||||||
|
sort.init(width, headerY - SongMenu.DIVIDER_LINE_WIDTH / 2);
|
||||||
|
|
||||||
|
// initialize score data buttons
|
||||||
|
ScoreData.init(width, headerY + height * 0.01f);
|
||||||
|
|
||||||
// song button background & graphics context
|
// song button background & graphics context
|
||||||
Image menuBackground = GameImage.MENU_BUTTON_BG.getImage();
|
Image menuBackground = GameImage.MENU_BUTTON_BG.getImage();
|
||||||
|
|
||||||
// song button coordinates
|
// song button coordinates
|
||||||
buttonX = width * 0.6f;
|
buttonX = width * 0.6f;
|
||||||
buttonY = height * 0.16f;
|
buttonY = headerY;
|
||||||
buttonWidth = menuBackground.getWidth();
|
buttonWidth = menuBackground.getWidth();
|
||||||
buttonHeight = menuBackground.getHeight();
|
buttonHeight = menuBackground.getHeight();
|
||||||
buttonOffset = (height * 0.8f) / MAX_SONG_BUTTONS;
|
buttonOffset = (footerY - headerY - DIVIDER_LINE_WIDTH) / MAX_SONG_BUTTONS;
|
||||||
|
|
||||||
// search
|
// search
|
||||||
searchTimer = 0;
|
int textFieldX = (int) (width * 0.7125f + Utils.FONT_BOLD.getWidth("Search: "));
|
||||||
searchResultString = "Type to search!";
|
int textFieldY = (int) (headerY + Utils.FONT_BOLD.getLineHeight() / 2);
|
||||||
Image searchIcon = GameImage.MENU_SEARCH.getImage();
|
|
||||||
Image tab = GameImage.MENU_TAB.getImage();
|
|
||||||
search = new TextField(
|
search = new TextField(
|
||||||
container, Utils.FONT_DEFAULT,
|
container, Utils.FONT_BOLD, textFieldX, textFieldY,
|
||||||
(int) buttonX + (tab.getWidth() / 2) + searchIcon.getWidth(),
|
(int) (width * 0.99f) - textFieldX, Utils.FONT_BOLD.getLineHeight()
|
||||||
(int) ((height * 0.15f) - (tab.getHeight() * 2.5f)),
|
|
||||||
(int) (buttonWidth / 2), Utils.FONT_DEFAULT.getLineHeight()
|
|
||||||
);
|
);
|
||||||
search.setBackgroundColor(Color.transparent);
|
search.setBackgroundColor(Color.transparent);
|
||||||
search.setBorderColor(Color.transparent);
|
search.setBorderColor(Color.transparent);
|
||||||
|
@ -231,10 +252,22 @@ public class SongMenu extends BasicGameState {
|
||||||
search.setConsumeEvents(false);
|
search.setConsumeEvents(false);
|
||||||
search.setMaxLength(60);
|
search.setMaxLength(60);
|
||||||
|
|
||||||
// options button
|
// selection buttons
|
||||||
Image optionsIcon = GameImage.MENU_OPTIONS.getImage();
|
float selectX = GameImage.MENU_BACK.getImage().getWidth() * 1.75f;
|
||||||
optionsButton = new MenuButton(optionsIcon, search.getX() - (optionsIcon.getWidth() * 1.5f), search.getY());
|
float selectY = height - GameImage.SELECTION_MODS.getImage().getHeight() / 2f;
|
||||||
optionsButton.setHoverExpand(1.75f);
|
float selectOffset = GameImage.SELECTION_MODS.getImage().getWidth() * 1.05f;
|
||||||
|
selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(),
|
||||||
|
selectX, selectY);
|
||||||
|
selectRandomButton = new MenuButton(GameImage.SELECTION_RANDOM_OVERLAY.getImage(),
|
||||||
|
selectX + selectOffset, selectY);
|
||||||
|
selectMapOptionsButton = new MenuButton(GameImage.SELECTION_OPTIONS_OVERLAY.getImage(),
|
||||||
|
selectX + selectOffset * 2f, selectY);
|
||||||
|
selectOptionsButton = new MenuButton(GameImage.SELECTION_OTHER_OPTIONS_OVERLAY.getImage(),
|
||||||
|
selectX + selectOffset * 3f, selectY);
|
||||||
|
selectModsButton.setHoverFade(0f);
|
||||||
|
selectRandomButton.setHoverFade(0f);
|
||||||
|
selectMapOptionsButton.setHoverFade(0f);
|
||||||
|
selectOptionsButton.setHoverFade(0f);
|
||||||
|
|
||||||
// loader
|
// loader
|
||||||
int loaderDim = GameImage.MENU_MUSICNOTE.getImage().getWidth();
|
int loaderDim = GameImage.MENU_MUSICNOTE.getImage().getWidth();
|
||||||
|
@ -258,19 +291,19 @@ public class SongMenu extends BasicGameState {
|
||||||
GameImage.PLAYFIELD.getImage().draw();
|
GameImage.PLAYFIELD.getImage().draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// header setup
|
// top/bottom bars
|
||||||
float lowerBound = height * 0.15f;
|
g.setColor(Color.black);
|
||||||
g.setColor(Utils.COLOR_BLACK_ALPHA);
|
g.fillRect(0, 0, width, headerY);
|
||||||
g.fillRect(0, 0, width, lowerBound);
|
g.fillRect(0, footerY, width, height - footerY);
|
||||||
g.setColor(Utils.COLOR_BLUE_DIVIDER);
|
g.setColor(Utils.COLOR_BLUE_DIVIDER);
|
||||||
g.setLineWidth(2f);
|
g.setLineWidth(DIVIDER_LINE_WIDTH);
|
||||||
g.drawLine(0, lowerBound, width, lowerBound);
|
g.drawLine(0, headerY, width, headerY);
|
||||||
|
g.drawLine(0, footerY - DIVIDER_LINE_WIDTH / 2, width, footerY - DIVIDER_LINE_WIDTH / 2);
|
||||||
g.resetLineWidth();
|
g.resetLineWidth();
|
||||||
|
|
||||||
// header
|
// header
|
||||||
if (focusNode != null) {
|
if (focusNode != null) {
|
||||||
float marginX = width * 0.005f, marginY = height * 0.005f;
|
float marginX = width * 0.005f, marginY = height * 0.005f;
|
||||||
|
|
||||||
Image musicNote = GameImage.MENU_MUSICNOTE.getImage();
|
Image musicNote = GameImage.MENU_MUSICNOTE.getImage();
|
||||||
if (MusicController.isTrackLoading())
|
if (MusicController.isTrackLoading())
|
||||||
loader.draw(marginX, marginY);
|
loader.draw(marginX, marginY);
|
||||||
|
@ -282,14 +315,14 @@ public class SongMenu extends BasicGameState {
|
||||||
if (songInfo == null)
|
if (songInfo == null)
|
||||||
songInfo = focusNode.getInfo();
|
songInfo = focusNode.getInfo();
|
||||||
marginX += 5;
|
marginX += 5;
|
||||||
Utils.FONT_LARGE.drawString(marginX + iconWidth, marginY, songInfo[0], Color.white);
|
Utils.FONT_LARGE.drawString(marginX + iconWidth * 1.05f, marginY, songInfo[0], Color.white);
|
||||||
Utils.FONT_DEFAULT.drawString(marginX + iconWidth, marginY + Utils.FONT_LARGE.getLineHeight() * 0.75f, songInfo[1], Color.white);
|
Utils.FONT_DEFAULT.drawString(marginX + iconWidth * 1.05f, marginY + Utils.FONT_LARGE.getLineHeight() * 0.75f, songInfo[1], Color.white);
|
||||||
float headerY = marginY + iconHeight;
|
float headerTextY = marginY + iconHeight;
|
||||||
Utils.FONT_BOLD.drawString(marginX, headerY, songInfo[2], Color.white);
|
Utils.FONT_BOLD.drawString(marginX, headerTextY, songInfo[2], Color.white);
|
||||||
headerY += Utils.FONT_BOLD.getLineHeight() - 6;
|
headerTextY += Utils.FONT_BOLD.getLineHeight() - 6;
|
||||||
Utils.FONT_DEFAULT.drawString(marginX, headerY, songInfo[3], Color.white);
|
Utils.FONT_DEFAULT.drawString(marginX, headerTextY, songInfo[3], Color.white);
|
||||||
headerY += Utils.FONT_DEFAULT.getLineHeight() - 4;
|
headerTextY += Utils.FONT_DEFAULT.getLineHeight() - 4;
|
||||||
Utils.FONT_SMALL.drawString(marginX, headerY, songInfo[4], Color.white);
|
Utils.FONT_SMALL.drawString(marginX, headerTextY, songInfo[4], Color.white);
|
||||||
}
|
}
|
||||||
|
|
||||||
// song buttons
|
// song buttons
|
||||||
|
@ -300,6 +333,7 @@ public class SongMenu extends BasicGameState {
|
||||||
ScoreData[] scores = getScoreDataForNode(node, false);
|
ScoreData[] scores = getScoreDataForNode(node, false);
|
||||||
node.draw(
|
node.draw(
|
||||||
buttonX - offset, buttonY + (i*buttonOffset),
|
buttonX - offset, buttonY + (i*buttonOffset),
|
||||||
|
headerY + DIVIDER_LINE_WIDTH / 2, footerY - DIVIDER_LINE_WIDTH,
|
||||||
(scores == null) ? Grade.NULL : scores[0].getGrade(),
|
(scores == null) ? Grade.NULL : scores[0].getGrade(),
|
||||||
(node == focusNode)
|
(node == focusNode)
|
||||||
);
|
);
|
||||||
|
@ -324,8 +358,15 @@ public class SongMenu extends BasicGameState {
|
||||||
ScoreData.drawScrollbar(g, startScore, focusScores.length);
|
ScoreData.drawScrollbar(g, startScore, focusScores.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// options button
|
// selection buttons
|
||||||
optionsButton.draw();
|
GameImage.SELECTION_MODS.getImage().drawCentered(selectModsButton.getX(), selectModsButton.getY());
|
||||||
|
selectModsButton.draw();
|
||||||
|
GameImage.SELECTION_RANDOM.getImage().drawCentered(selectRandomButton.getX(), selectRandomButton.getY());
|
||||||
|
selectRandomButton.draw();
|
||||||
|
GameImage.SELECTION_OPTIONS.getImage().drawCentered(selectMapOptionsButton.getX(), selectMapOptionsButton.getY());
|
||||||
|
selectMapOptionsButton.draw();
|
||||||
|
GameImage.SELECTION_OTHER_OPTIONS.getImage().drawCentered(selectOptionsButton.getX(), selectOptionsButton.getY());
|
||||||
|
selectOptionsButton.draw();
|
||||||
|
|
||||||
// sorting tabs
|
// sorting tabs
|
||||||
SongSort currentSort = SongSort.getSort();
|
SongSort currentSort = SongSort.getSort();
|
||||||
|
@ -343,15 +384,37 @@ public class SongMenu extends BasicGameState {
|
||||||
currentSort.draw(true, false);
|
currentSort.draw(true, false);
|
||||||
|
|
||||||
// search
|
// search
|
||||||
Image searchIcon = GameImage.MENU_SEARCH.getImage();
|
boolean searchEmpty = search.getText().isEmpty();
|
||||||
Utils.FONT_BOLD.drawString(
|
int searchX = search.getX(), searchY = search.getY();
|
||||||
search.getX(), search.getY() - Utils.FONT_BOLD.getLineHeight(),
|
float searchBaseX = width * 0.7f;
|
||||||
searchResultString, Color.white
|
float searchTextX = width * 0.7125f;
|
||||||
);
|
float searchRectHeight = Utils.FONT_BOLD.getLineHeight() * 2;
|
||||||
searchIcon.draw(search.getX() - searchIcon.getWidth(),
|
float searchExtraHeight = Utils.FONT_DEFAULT.getLineHeight() * 0.7f;
|
||||||
search.getY() - Utils.FONT_DEFAULT.getLineHeight());
|
float searchProgress = (searchTransitionTimer < SEARCH_TRANSITION_TIME) ?
|
||||||
|
((float) searchTransitionTimer / SEARCH_TRANSITION_TIME) : 1f;
|
||||||
|
float oldAlpha = Utils.COLOR_BLACK_ALPHA.a;
|
||||||
|
if (searchEmpty) {
|
||||||
|
searchRectHeight += (1f - searchProgress) * searchExtraHeight;
|
||||||
|
Utils.COLOR_BLACK_ALPHA.a = 0.5f - searchProgress * 0.3f;
|
||||||
|
} else {
|
||||||
|
searchRectHeight += searchProgress * searchExtraHeight;
|
||||||
|
Utils.COLOR_BLACK_ALPHA.a = 0.2f + searchProgress * 0.3f;
|
||||||
|
}
|
||||||
|
g.setColor(Utils.COLOR_BLACK_ALPHA);
|
||||||
|
g.fillRect(searchBaseX, headerY + DIVIDER_LINE_WIDTH / 2, width - searchBaseX, searchRectHeight);
|
||||||
|
Utils.COLOR_BLACK_ALPHA.a = oldAlpha;
|
||||||
|
Utils.FONT_BOLD.drawString(searchTextX, searchY, "Search:", Utils.COLOR_GREEN_SEARCH);
|
||||||
|
if (searchEmpty)
|
||||||
|
Utils.FONT_BOLD.drawString(searchX, searchY, "Type to search!", Color.white);
|
||||||
|
else {
|
||||||
g.setColor(Color.white);
|
g.setColor(Color.white);
|
||||||
|
// TODO: why is this needed to correctly position the TextField?
|
||||||
|
search.setLocation(searchX - 3, searchY - 1);
|
||||||
search.render(container, g);
|
search.render(container, g);
|
||||||
|
search.setLocation(searchX, searchY);
|
||||||
|
Utils.FONT_DEFAULT.drawString(searchTextX, searchY + Utils.FONT_BOLD.getLineHeight(),
|
||||||
|
(searchResultString == null) ? "Searching..." : searchResultString, Color.white);
|
||||||
|
}
|
||||||
|
|
||||||
// scroll bar
|
// scroll bar
|
||||||
if (focusNode != null) {
|
if (focusNode != null) {
|
||||||
|
@ -364,7 +427,7 @@ public class SongMenu extends BasicGameState {
|
||||||
else if (startNode.index == focusNode.index)
|
else if (startNode.index == focusNode.index)
|
||||||
startIndex += startNode.osuFileIndex;
|
startIndex += startNode.osuFileIndex;
|
||||||
Utils.drawScrollbar(g, startIndex, totalNodes, MAX_SONG_BUTTONS,
|
Utils.drawScrollbar(g, startIndex, totalNodes, MAX_SONG_BUTTONS,
|
||||||
width, height * 0.16f, 0, buttonHeight, buttonOffset,
|
width, headerY + DIVIDER_LINE_WIDTH / 2, 0, buttonOffset - DIVIDER_LINE_WIDTH * 1.5f, buttonOffset,
|
||||||
Utils.COLOR_BLACK_ALPHA, Color.white, true);
|
Utils.COLOR_BLACK_ALPHA, Color.white, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,7 +457,10 @@ public class SongMenu extends BasicGameState {
|
||||||
Utils.updateVolumeDisplay(delta);
|
Utils.updateVolumeDisplay(delta);
|
||||||
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
||||||
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY);
|
||||||
optionsButton.hoverUpdate(delta, mouseX, mouseY);
|
selectModsButton.hoverUpdate(delta, mouseX, mouseY);
|
||||||
|
selectRandomButton.hoverUpdate(delta, mouseX, mouseY);
|
||||||
|
selectMapOptionsButton.hoverUpdate(delta, mouseX, mouseY);
|
||||||
|
selectOptionsButton.hoverUpdate(delta, mouseX, mouseY);
|
||||||
|
|
||||||
// beatmap menu timer
|
// beatmap menu timer
|
||||||
if (beatmapMenuTimer > -1) {
|
if (beatmapMenuTimer > -1) {
|
||||||
|
@ -425,7 +491,7 @@ public class SongMenu extends BasicGameState {
|
||||||
|
|
||||||
// empty search
|
// empty search
|
||||||
if (search.getText().isEmpty())
|
if (search.getText().isEmpty())
|
||||||
searchResultString = "Type to search!";
|
searchResultString = null;
|
||||||
|
|
||||||
// search produced new list: re-initialize it
|
// search produced new list: re-initialize it
|
||||||
startNode = focusNode = null;
|
startNode = focusNode = null;
|
||||||
|
@ -450,23 +516,28 @@ public class SongMenu extends BasicGameState {
|
||||||
searchResultString = "No matches found. Hit 'esc' to reset.";
|
searchResultString = "No matches found. Hit 'esc' to reset.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (searchTransitionTimer < SEARCH_TRANSITION_TIME) {
|
||||||
|
searchTransitionTimer += delta;
|
||||||
|
if (searchTransitionTimer > SEARCH_TRANSITION_TIME)
|
||||||
|
searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
// slide buttons
|
// slide buttons
|
||||||
int height = container.getHeight();
|
int height = container.getHeight();
|
||||||
float targetY = height * 0.16f;
|
if (buttonY > headerY) {
|
||||||
if (buttonY > targetY) {
|
|
||||||
buttonY -= height * delta / 20000f;
|
buttonY -= height * delta / 20000f;
|
||||||
if (buttonY < targetY)
|
if (buttonY < headerY)
|
||||||
buttonY = targetY;
|
buttonY = headerY;
|
||||||
} else if (buttonY < targetY) {
|
} else if (buttonY < headerY) {
|
||||||
buttonY += height * delta / 20000f;
|
buttonY += height * delta / 20000f;
|
||||||
if (buttonY > targetY)
|
if (buttonY > headerY)
|
||||||
buttonY = targetY;
|
buttonY = headerY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mouse hover
|
// mouse hover
|
||||||
OsuGroupNode node = startNode;
|
|
||||||
boolean isHover = false;
|
boolean isHover = false;
|
||||||
|
if (mouseY > headerY && mouseY < footerY) {
|
||||||
|
OsuGroupNode node = startNode;
|
||||||
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
|
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
|
||||||
float cx = (node.index == OsuGroupList.get().getExpandedIndex()) ? buttonX * 0.9f : buttonX;
|
float cx = (node.index == OsuGroupList.get().getExpandedIndex()) ? buttonX * 0.9f : buttonX;
|
||||||
if ((mouseX > cx && mouseX < cx + buttonWidth) &&
|
if ((mouseX > cx && mouseX < cx + buttonWidth) &&
|
||||||
|
@ -485,6 +556,7 @@ public class SongMenu extends BasicGameState {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!isHover) {
|
if (!isHover) {
|
||||||
hoverOffset = 0f;
|
hoverOffset = 0f;
|
||||||
hoverIndex = -1;
|
hoverIndex = -1;
|
||||||
|
@ -512,8 +584,17 @@ public class SongMenu extends BasicGameState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// options
|
// selection buttons
|
||||||
if (optionsButton.contains(x, y)) {
|
if (selectModsButton.contains(x, y)) {
|
||||||
|
this.keyPressed(Input.KEY_F1, '\0');
|
||||||
|
return;
|
||||||
|
} else if (selectRandomButton.contains(x, y)) {
|
||||||
|
this.keyPressed(Input.KEY_F2, '\0');
|
||||||
|
return;
|
||||||
|
} else if (selectMapOptionsButton.contains(x, y)) {
|
||||||
|
this.keyPressed(Input.KEY_F3, '\0');
|
||||||
|
return;
|
||||||
|
} else if (selectOptionsButton.contains(x, y)) {
|
||||||
SoundController.playSound(SoundEffect.MENUHIT);
|
SoundController.playSound(SoundEffect.MENUHIT);
|
||||||
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
||||||
return;
|
return;
|
||||||
|
@ -539,6 +620,7 @@ public class SongMenu extends BasicGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// song buttons
|
// song buttons
|
||||||
|
if (y > headerY && y < footerY) {
|
||||||
int expandedIndex = OsuGroupList.get().getExpandedIndex();
|
int expandedIndex = OsuGroupList.get().getExpandedIndex();
|
||||||
OsuGroupNode node = startNode;
|
OsuGroupNode node = startNode;
|
||||||
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
|
for (int i = 0; i < MAX_SONG_BUTTONS && node != null; i++, node = node.next) {
|
||||||
|
@ -581,6 +663,7 @@ public class SongMenu extends BasicGameState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// score buttons
|
// score buttons
|
||||||
if (focusScores != null && ScoreData.areaContains(x, y)) {
|
if (focusScores != null && ScoreData.areaContains(x, y)) {
|
||||||
|
@ -621,6 +704,7 @@ public class SongMenu extends BasicGameState {
|
||||||
// clear search text
|
// clear search text
|
||||||
search.setText("");
|
search.setText("");
|
||||||
searchTimer = SEARCH_DELAY;
|
searchTimer = SEARCH_DELAY;
|
||||||
|
searchTransitionTimer = 0;
|
||||||
} else {
|
} else {
|
||||||
// return to main menu
|
// return to main menu
|
||||||
SoundController.playSound(SoundEffect.MENUBACK);
|
SoundController.playSound(SoundEffect.MENUBACK);
|
||||||
|
@ -630,11 +714,13 @@ public class SongMenu extends BasicGameState {
|
||||||
break;
|
break;
|
||||||
case Input.KEY_F1:
|
case Input.KEY_F1:
|
||||||
SoundController.playSound(SoundEffect.MENUHIT);
|
SoundController.playSound(SoundEffect.MENUHIT);
|
||||||
game.enterState(Opsu.STATE_OPTIONSMENU, new EmptyTransition(), new FadeInTransition(Color.black));
|
((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.MODS);
|
||||||
|
game.enterState(Opsu.STATE_BUTTONMENU);
|
||||||
break;
|
break;
|
||||||
case Input.KEY_F2:
|
case Input.KEY_F2:
|
||||||
if (focusNode == null)
|
if (focusNode == null)
|
||||||
break;
|
break;
|
||||||
|
SoundController.playSound(SoundEffect.MENUHIT);
|
||||||
if (input.isKeyDown(Input.KEY_RSHIFT) || input.isKeyDown(Input.KEY_LSHIFT)) {
|
if (input.isKeyDown(Input.KEY_RSHIFT) || input.isKeyDown(Input.KEY_LSHIFT)) {
|
||||||
// shift key: previous random track
|
// shift key: previous random track
|
||||||
SongNode prev;
|
SongNode prev;
|
||||||
|
@ -718,8 +804,16 @@ public class SongMenu extends BasicGameState {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// wait for user to finish typing
|
// wait for user to finish typing
|
||||||
if (Character.isLetterOrDigit(c) || key == Input.KEY_BACK)
|
// TODO: accept all characters (current conditions are from TextField class)
|
||||||
|
if ((c > 31 && c < 127) || key == Input.KEY_BACK) {
|
||||||
searchTimer = 0;
|
searchTimer = 0;
|
||||||
|
int textLength = search.getText().length();
|
||||||
|
if (key == Input.KEY_BACK) {
|
||||||
|
if (textLength == 0)
|
||||||
|
searchTransitionTimer = 0;
|
||||||
|
} else if (textLength == 1)
|
||||||
|
searchTransitionTimer = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -782,11 +876,15 @@ public class SongMenu extends BasicGameState {
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
Display.setTitle(game.getTitle());
|
Display.setTitle(game.getTitle());
|
||||||
Utils.getBackButton().resetHover();
|
Utils.getBackButton().resetHover();
|
||||||
optionsButton.resetHover();
|
selectModsButton.resetHover();
|
||||||
|
selectRandomButton.resetHover();
|
||||||
|
selectMapOptionsButton.resetHover();
|
||||||
|
selectOptionsButton.resetHover();
|
||||||
hoverOffset = 0f;
|
hoverOffset = 0f;
|
||||||
hoverIndex = -1;
|
hoverIndex = -1;
|
||||||
startScore = 0;
|
startScore = 0;
|
||||||
beatmapMenuTimer = -1;
|
beatmapMenuTimer = -1;
|
||||||
|
searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||||
|
|
||||||
// reset song stack
|
// reset song stack
|
||||||
randomStack = new Stack<SongNode>();
|
randomStack = new Stack<SongNode>();
|
||||||
|
@ -920,7 +1018,8 @@ public class SongMenu extends BasicGameState {
|
||||||
hoverIndex = -1;
|
hoverIndex = -1;
|
||||||
search.setText("");
|
search.setText("");
|
||||||
searchTimer = SEARCH_DELAY;
|
searchTimer = SEARCH_DELAY;
|
||||||
searchResultString = "Type to search!";
|
searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||||
|
searchResultString = null;
|
||||||
|
|
||||||
// reload songs in new thread
|
// reload songs in new thread
|
||||||
reloadThread = new Thread() {
|
reloadThread = new Thread() {
|
||||||
|
@ -977,16 +1076,16 @@ public class SongMenu extends BasicGameState {
|
||||||
if (n < 0 && startNode.prev != null) {
|
if (n < 0 && startNode.prev != null) {
|
||||||
startNode = startNode.prev;
|
startNode = startNode.prev;
|
||||||
buttonY += buttonOffset / 4;
|
buttonY += buttonOffset / 4;
|
||||||
if (buttonY > height * 0.18f)
|
if (buttonY > headerY + height * 0.02f)
|
||||||
buttonY = height * 0.18f;
|
buttonY = headerY + height * 0.02f;
|
||||||
n++;
|
n++;
|
||||||
shifted = true;
|
shifted = true;
|
||||||
} else if (n > 0 && startNode.next != null &&
|
} else if (n > 0 && startNode.next != null &&
|
||||||
OsuGroupList.get().getNode(startNode, MAX_SONG_BUTTONS) != null) {
|
OsuGroupList.get().getNode(startNode, MAX_SONG_BUTTONS) != null) {
|
||||||
startNode = startNode.next;
|
startNode = startNode.next;
|
||||||
buttonY -= buttonOffset / 4;
|
buttonY -= buttonOffset / 4;
|
||||||
if (buttonY < height * 0.14f)
|
if (buttonY < headerY - height * 0.02f)
|
||||||
buttonY = height * 0.14f;
|
buttonY = headerY - height * 0.02f;
|
||||||
n--;
|
n--;
|
||||||
shifted = true;
|
shifted = true;
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.newdawn.slick.util.Log;
|
||||||
* @author kevin
|
* @author kevin
|
||||||
* @author Nathan Sweet <misc@n4te.com>
|
* @author Nathan Sweet <misc@n4te.com>
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public class Music {
|
public class Music {
|
||||||
/** The music currently being played or null if none */
|
/** The music currently being played or null if none */
|
||||||
private static Music currentMusic;
|
private static Music currentMusic;
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.newdawn.slick.util.ResourceLoader;
|
||||||
* @author Kevin Glass
|
* @author Kevin Glass
|
||||||
* @author Rockstar setVolume cleanup
|
* @author Rockstar setVolume cleanup
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
|
||||||
public class SoundStore {
|
public class SoundStore {
|
||||||
|
|
||||||
/** The single instance of this class */
|
/** The single instance of this class */
|
||||||
|
@ -318,6 +319,7 @@ public class SoundStore {
|
||||||
inited = true;
|
inited = true;
|
||||||
|
|
||||||
AccessController.doPrivileged(new PrivilegedAction() {
|
AccessController.doPrivileged(new PrivilegedAction() {
|
||||||
|
@Override
|
||||||
public Object run() {
|
public Object run() {
|
||||||
try {
|
try {
|
||||||
AL.create();
|
AL.create();
|
||||||
|
|