Merge remote-tracking branch 'orgin/master' into omaster

Conflicts:
	src/itdelatrisu/opsu/GameImage.java
	src/itdelatrisu/opsu/OsuGroupNode.java
This commit is contained in:
fd 2015-02-17 22:49:19 -05:00
commit 65e538fbdc
27 changed files with 803 additions and 404 deletions

View File

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/selection-mods-over.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
res/selection-mods.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
res/selection-options.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
res/selection-random.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -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]++;

View File

@ -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),

View File

@ -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.
*/ */

View File

@ -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);
}
}
} }
/** /**

View File

@ -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);

View File

@ -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;

View File

@ -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)
); );
} }

View File

@ -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

View File

@ -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.

View File

@ -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;
} }
} }

View File

@ -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,

View File

@ -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();

View File

@ -19,7 +19,6 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.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();
} }
/** /**

View File

@ -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

View File

@ -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;

View File

@ -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();