Added a separate Game Mods menu.

- Replaces the mods being shown in the options menu.
- Added all the (base) mod icons to the menu, with the unimplemented ones grayed out.
- Added a rotation effect to MenuButton, and now multiple effects can be set at once.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-02-16 17:53:24 -05:00
parent 329d54c230
commit 69f5aa5748
9 changed files with 471 additions and 232 deletions

View File

@ -1007,13 +1007,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
@ -1022,7 +1015,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

@ -309,7 +309,7 @@ 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) {
@ -366,6 +366,18 @@ public enum GameImage {
return MOD_EASY.process_sub(img, w, h); 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 Buttons
SELECTION_MODS ("selection-mods", "png", false, false) { SELECTION_MODS ("selection-mods", "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

@ -213,8 +213,7 @@ public class Utils {
} }
// initialize game mods // initialize game mods
for (GameMod mod : GameMod.values()) GameMod.init(width, height);
mod.init(width, height);
// initialize hit objects // initialize hit objects
OsuHitObject.init(width, height); OsuHitObject.init(width, height);
@ -281,6 +280,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

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

@ -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 - subtextWidth - tabImage.getWidth()) / 2) / OptionTab.SIZE); ((width - subtextWidth - tabImage.getWidth()) / 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

@ -359,9 +359,8 @@ public class SongMenu extends BasicGameState {
} }
// selection buttons // selection buttons
// TODO GameImage.SELECTION_MODS.getImage().drawCentered(selectModsButton.getX(), selectModsButton.getY());
// GameImage.SELECTION_MODS.getImage().drawCentered(selectModsButton.getX(), selectModsButton.getY()); selectModsButton.draw();
// selectModsButton.draw();
GameImage.SELECTION_RANDOM.getImage().drawCentered(selectRandomButton.getX(), selectRandomButton.getY()); GameImage.SELECTION_RANDOM.getImage().drawCentered(selectRandomButton.getX(), selectRandomButton.getY());
selectRandomButton.draw(); selectRandomButton.draw();
GameImage.SELECTION_OPTIONS.getImage().drawCentered(selectMapOptionsButton.getX(), selectMapOptionsButton.getY()); GameImage.SELECTION_OPTIONS.getImage().drawCentered(selectMapOptionsButton.getX(), selectMapOptionsButton.getY());
@ -714,9 +713,9 @@ public class SongMenu extends BasicGameState {
} }
break; break;
case Input.KEY_F1: case Input.KEY_F1:
// TODO: mods menu SoundController.playSound(SoundEffect.MENUHIT);
// SoundController.playSound(SoundEffect.MENUHIT); ((ButtonMenu) game.getState(Opsu.STATE_BUTTONMENU)).setMenuState(MenuState.MODS);
// game.enterState(); game.enterState(Opsu.STATE_BUTTONMENU);
break; break;
case Input.KEY_F2: case Input.KEY_F2:
if (focusNode == null) if (focusNode == null)