Merge pull request #214 from yugecin/pusing-logo
Pulse main menu logo to the beat, add pulsing logo in song menu
This commit is contained in:
commit
0f936d32cb
|
@ -20,6 +20,7 @@ package itdelatrisu.opsu;
|
|||
|
||||
import itdelatrisu.opsu.audio.MusicController;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.TimingPoint;
|
||||
import itdelatrisu.opsu.skins.Skin;
|
||||
import itdelatrisu.opsu.skins.SkinLoader;
|
||||
import itdelatrisu.opsu.ui.Fonts;
|
||||
|
@ -34,6 +35,7 @@ import java.io.IOException;
|
|||
import java.io.OutputStreamWriter;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
@ -1340,6 +1342,9 @@ public class Options {
|
|||
beatmap.audioFilename = new File(tokens[0]);
|
||||
beatmap.title = tokens[1];
|
||||
beatmap.artist = tokens[2];
|
||||
// add a timingpoint so the logo can be pulsed in the mainmenu
|
||||
beatmap.timingPoints = new ArrayList<>(1);
|
||||
beatmap.timingPoints.add(new TimingPoint("-44,631.578947368421,4,1,0,100,1,0"));
|
||||
try {
|
||||
beatmap.endTime = Integer.parseInt(tokens[3]);
|
||||
} catch (NumberFormatException e) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import itdelatrisu.opsu.ErrorHandler;
|
|||
import itdelatrisu.opsu.Options;
|
||||
import itdelatrisu.opsu.beatmap.Beatmap;
|
||||
import itdelatrisu.opsu.beatmap.BeatmapParser;
|
||||
import itdelatrisu.opsu.beatmap.TimingPoint;
|
||||
import itdelatrisu.opsu.ui.UI;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -183,6 +184,40 @@ public class MusicController {
|
|||
*/
|
||||
public static Beatmap getBeatmap() { return lastBeatmap; }
|
||||
|
||||
/**
|
||||
* Gets the progress of the current beat.
|
||||
* @return null if music is paused or no timingpoints are available, or progress as a value in
|
||||
* [0, 1], where 0 means a beat just happened and 1 means the next beat is coming right now.
|
||||
*/
|
||||
public static Float getBeatProgress() {
|
||||
if (!isPlaying() || getBeatmap() == null) {
|
||||
return null;
|
||||
}
|
||||
Beatmap map = getBeatmap();
|
||||
if (map.timingPoints == null) {
|
||||
return null;
|
||||
}
|
||||
int trackposition = getPosition();
|
||||
TimingPoint p = null;
|
||||
double beatlen = 0d;
|
||||
int time = 0;
|
||||
for (TimingPoint pts : map.timingPoints) {
|
||||
if (pts.getTime() > getPosition()) {
|
||||
break;
|
||||
}
|
||||
p = pts;
|
||||
if (!p.isInherited() && p.getBeatLength() > 0) {
|
||||
beatlen = p.getBeatLength();
|
||||
time = p.getTime();
|
||||
}
|
||||
}
|
||||
if (p == null) {
|
||||
return null;
|
||||
}
|
||||
double beatLength = beatlen * 100d;
|
||||
return (float) ((((trackposition - time) * 100) % beatLength) / beatLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current track is playing.
|
||||
*/
|
||||
|
|
|
@ -212,6 +212,45 @@ public class BeatmapParser {
|
|||
return lastNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the timingpoints for a beatmap.
|
||||
* @param map the map to parse timingpoints for
|
||||
*/
|
||||
public static void parseOnlyTimingPoints(Beatmap map) {
|
||||
if (map == null || map.getFile() == null || !map.getFile().exists()) {
|
||||
return;
|
||||
}
|
||||
if (map.timingPoints == null) {
|
||||
map.timingPoints = new ArrayList<TimingPoint>();
|
||||
}
|
||||
try (
|
||||
InputStream bis = new BufferedInputStream(new FileInputStream(map.getFile()));
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(bis, "UTF-8"));
|
||||
) {
|
||||
String line;
|
||||
boolean found = false;
|
||||
while((line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if(!isValidLine(line)) {
|
||||
continue;
|
||||
}
|
||||
if ("[TimingPoints]".equals(line)) {
|
||||
found = true;
|
||||
continue;
|
||||
}
|
||||
if (found) {
|
||||
if (line.startsWith("[")) {
|
||||
break;
|
||||
}
|
||||
parseSectionTimingPoints(map, line);
|
||||
}
|
||||
}
|
||||
map.timingPoints.trimToSize();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Failed to read file '%s'.", map.getFile().getAbsolutePath()), e, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a beatmap.
|
||||
* @param file the file to parse
|
||||
|
@ -493,21 +532,7 @@ public class BeatmapParser {
|
|||
break;
|
||||
|
||||
try {
|
||||
// parse timing point
|
||||
TimingPoint timingPoint = new TimingPoint(line);
|
||||
|
||||
// calculate BPM
|
||||
if (!timingPoint.isInherited()) {
|
||||
int bpm = Math.round(60000 / timingPoint.getBeatLength());
|
||||
if (beatmap.bpmMin == 0)
|
||||
beatmap.bpmMin = beatmap.bpmMax = bpm;
|
||||
else if (bpm < beatmap.bpmMin)
|
||||
beatmap.bpmMin = bpm;
|
||||
else if (bpm > beatmap.bpmMax)
|
||||
beatmap.bpmMax = bpm;
|
||||
}
|
||||
|
||||
beatmap.timingPoints.add(timingPoint);
|
||||
parseSectionTimingPoints(beatmap, line);
|
||||
} catch (Exception e) {
|
||||
Log.warn(String.format("Failed to read timing point '%s' for file '%s'.",
|
||||
line, file.getAbsolutePath()), e);
|
||||
|
@ -624,6 +649,26 @@ public class BeatmapParser {
|
|||
return beatmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a timing point in the timingpoints section of a beatmap file
|
||||
* @param beatmap the beatmap to add the timingpoint to
|
||||
* @param line the line with timingpoint to parse
|
||||
*/
|
||||
private static void parseSectionTimingPoints(Beatmap beatmap, String line) {
|
||||
TimingPoint timingPoint = new TimingPoint(line);
|
||||
if(!timingPoint.isInherited()) {
|
||||
int bpm = Math.round(60000 / timingPoint.getBeatLength());
|
||||
if( beatmap.bpmMin == 0 ) {
|
||||
beatmap.bpmMin = beatmap.bpmMax = bpm;
|
||||
} else if( bpm < beatmap.bpmMin ) {
|
||||
beatmap.bpmMin = bpm;
|
||||
} else if( bpm > beatmap.bpmMax ) {
|
||||
beatmap.bpmMax = bpm;
|
||||
}
|
||||
}
|
||||
beatmap.timingPoints.add(timingPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all hit objects in a beatmap.
|
||||
* @param beatmap the beatmap to parse
|
||||
|
|
|
@ -256,7 +256,18 @@ public class MainMenu extends BasicGameState {
|
|||
playButton.draw();
|
||||
exitButton.draw();
|
||||
}
|
||||
logo.draw();
|
||||
|
||||
// logo
|
||||
Float position = MusicController.getBeatProgress();
|
||||
if (position == null) {
|
||||
position = System.currentTimeMillis() % 1000 / 1000f;
|
||||
}
|
||||
float scale = 1f + position * 0.05f;
|
||||
logo.draw(Color.white, scale);
|
||||
Image ghostLogo = GameImage.MENU_LOGO.getImage().getScaledCopy(logo.getCurrentScale() / scale * 1.05f);
|
||||
float scaleposmodx = ghostLogo.getWidth() / 2;
|
||||
float scaleposmody = ghostLogo.getHeight() / 2;
|
||||
ghostLogo.draw(logo.getX() - scaleposmodx, logo.getY() - scaleposmody, Colors.GHOST_LOGO);
|
||||
|
||||
// draw music buttons
|
||||
if (MusicController.isPlaying())
|
||||
|
|
|
@ -259,6 +259,15 @@ public class SongMenu extends BasicGameState {
|
|||
/** Header and footer end and start y coordinates, respectively. */
|
||||
private float headerY, footerY;
|
||||
|
||||
/** Footer pulsing logo button. */
|
||||
private MenuButton footerLogoButton;
|
||||
|
||||
/** Size of the footer pulsing logo. */
|
||||
private float footerLogoSize;
|
||||
|
||||
/** Whether the cursor hovers over the footer logo. */
|
||||
private boolean bottomLogoHovered;
|
||||
|
||||
/** Time, in milliseconds, for fading the search bar. */
|
||||
private int searchTransitionTimer = SEARCH_TRANSITION_TIME;
|
||||
|
||||
|
@ -335,6 +344,15 @@ public class SongMenu extends BasicGameState {
|
|||
Fonts.SMALL.getLineHeight();
|
||||
footerY = height - GameImage.SELECTION_MODS.getImage().getHeight();
|
||||
|
||||
// logo coordinates
|
||||
float footerHeight = height - footerY;
|
||||
footerLogoSize = footerHeight * 3.25f;
|
||||
Image logo = GameImage.MENU_LOGO.getImage();
|
||||
logo = logo.getScaledCopy(footerLogoSize / logo.getWidth());
|
||||
footerLogoButton = new MenuButton(logo, width - footerHeight * 0.8f, height - footerHeight * 0.65f);
|
||||
footerLogoButton.setHoverAnimationDuration(1);
|
||||
footerLogoButton.setHoverExpand(1.2f);
|
||||
|
||||
// initialize sorts
|
||||
for (BeatmapSortOrder sort : BeatmapSortOrder.values())
|
||||
sort.init(width, headerY - SongMenu.DIVIDER_LINE_WIDTH / 2);
|
||||
|
@ -498,6 +516,26 @@ public class SongMenu extends BasicGameState {
|
|||
g.drawLine(0, footerY, width, footerY);
|
||||
g.resetLineWidth();
|
||||
|
||||
// opsu logo in bottom bar
|
||||
Float position = MusicController.getBeatProgress();
|
||||
if (position == null) {
|
||||
position = System.currentTimeMillis() % 1000 / 1000f;
|
||||
}
|
||||
if (bottomLogoHovered) {
|
||||
footerLogoButton.draw();
|
||||
} else {
|
||||
float expand = position * 0.15f;
|
||||
footerLogoButton.draw(Color.white, 1f - expand);
|
||||
Image ghostLogo = GameImage.MENU_LOGO.getImage();
|
||||
ghostLogo = ghostLogo.getScaledCopy((1f + expand) * footerLogoSize / ghostLogo.getWidth());
|
||||
float scaleposmodx = ghostLogo.getWidth() / 2;
|
||||
float scaleposmody = ghostLogo.getHeight() / 2;
|
||||
float a = Colors.GHOST_LOGO.a;
|
||||
Colors.GHOST_LOGO.a *= (1f - position);
|
||||
ghostLogo.draw(footerLogoButton.getX() - scaleposmodx, footerLogoButton.getY() - scaleposmody, Colors.GHOST_LOGO);
|
||||
Colors.GHOST_LOGO.a = a;
|
||||
}
|
||||
|
||||
// header
|
||||
if (focusNode != null) {
|
||||
// music/loader icon
|
||||
|
@ -753,7 +791,18 @@ public class SongMenu extends BasicGameState {
|
|||
}
|
||||
updateDrawnSongPosition();
|
||||
|
||||
// mouse hover
|
||||
// mouse hover (logo)
|
||||
if (footerLogoButton.contains(mouseX, mouseY, 0.25f)) {
|
||||
footerLogoButton.hoverUpdate(delta, true);
|
||||
bottomLogoHovered = true;
|
||||
// reset beatmap node hover
|
||||
hoverIndex = null;
|
||||
return;
|
||||
}
|
||||
footerLogoButton.hoverUpdate(delta, false);
|
||||
bottomLogoHovered = false;
|
||||
|
||||
// mouse hover (beatmap nodes)
|
||||
BeatmapSetNode node = getNodeAtPosition(mouseX, mouseY);
|
||||
if (node != null) {
|
||||
if (node == hoverIndex)
|
||||
|
@ -796,6 +845,11 @@ public class SongMenu extends BasicGameState {
|
|||
if (isScrollingToFocusNode)
|
||||
return;
|
||||
|
||||
if (bottomLogoHovered) {
|
||||
startGame();
|
||||
return;
|
||||
}
|
||||
|
||||
songScrolling.pressed();
|
||||
startScorePos.pressed();
|
||||
}
|
||||
|
@ -1390,6 +1444,10 @@ public class SongMenu extends BasicGameState {
|
|||
|
||||
focusNode = BeatmapSetList.get().getNode(node, beatmapIndex);
|
||||
Beatmap beatmap = focusNode.getSelectedBeatmap();
|
||||
if (beatmap.timingPoints == null) {
|
||||
// parse the timingpoints so we can pulse the main menu logo and bottom right logo in songmenu
|
||||
BeatmapParser.parseOnlyTimingPoints(beatmap);
|
||||
}
|
||||
MusicController.play(beatmap, false, preview);
|
||||
|
||||
// load scores
|
||||
|
|
|
@ -45,7 +45,8 @@ public class Colors {
|
|||
BLUE_SCOREBOARD = new Color(133, 208, 212),
|
||||
BLACK_BG_NORMAL = new Color(0, 0, 0, 0.25f),
|
||||
BLACK_BG_HOVER = new Color(0, 0, 0, 0.5f),
|
||||
BLACK_BG_FOCUS = new Color(0, 0, 0, 0.75f);
|
||||
BLACK_BG_FOCUS = new Color(0, 0, 0, 0.75f),
|
||||
GHOST_LOGO = new Color(1.0f, 1.0f, 1.0f, 0.25f);
|
||||
|
||||
// This class should not be instantiated.
|
||||
private Colors() {}
|
||||
|
|
|
@ -98,6 +98,9 @@ public class MenuButton {
|
|||
/** The default max rotation angle of the button. */
|
||||
private static final float DEFAULT_ANGLE_MAX = 30f;
|
||||
|
||||
/** The current scale of the drawn button */
|
||||
private float currentScale = 1f;
|
||||
|
||||
/**
|
||||
* Creates a new button from an Image.
|
||||
* @param img the image
|
||||
|
@ -166,6 +169,11 @@ public class MenuButton {
|
|||
*/
|
||||
public float getY() { return y; }
|
||||
|
||||
/**
|
||||
* Returns the current scale.
|
||||
*/
|
||||
public float getCurrentScale() { return currentScale; }
|
||||
|
||||
/**
|
||||
* Sets text to draw in the middle of the button.
|
||||
* @param text the text to draw
|
||||
|
@ -191,14 +199,21 @@ public class MenuButton {
|
|||
/**
|
||||
* Draws the button.
|
||||
*/
|
||||
public void draw() { draw(Color.white); }
|
||||
public void draw() { draw(Color.white, 1f); }
|
||||
|
||||
/**
|
||||
* Draws the button with a color filter.
|
||||
* @param filter the color to filter with when drawing
|
||||
*/
|
||||
public void draw(Color filter) { draw(filter, 1f); }
|
||||
|
||||
/**
|
||||
* Draw the button with a color filter.
|
||||
* @param filter the color to filter with when drawing
|
||||
* @param scaleOverride the scale to use when drawing, works only for normal images
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void draw(Color filter) {
|
||||
public void draw(Color filter, float scaleOverride) {
|
||||
// animations: get current frame
|
||||
Image image = this.img;
|
||||
if (image == null) {
|
||||
|
@ -208,6 +223,14 @@ public class MenuButton {
|
|||
|
||||
// normal images
|
||||
if (imgL == null) {
|
||||
float scalePosModX = 0;
|
||||
float scalePosModY = 0;
|
||||
if (scaleOverride != 1f) {
|
||||
image = image.getScaledCopy(scaleOverride);
|
||||
scalePosModX = image.getWidth() / 2 - xRadius;
|
||||
scalePosModY = image.getHeight() / 2 - yRadius;
|
||||
}
|
||||
currentScale = scaleOverride;
|
||||
if (hoverEffect == 0)
|
||||
image.draw(x - xRadius, y - yRadius, filter);
|
||||
else {
|
||||
|
@ -217,13 +240,16 @@ public class MenuButton {
|
|||
if (scale.getValue() != 1f) {
|
||||
image = image.getScaledCopy(scale.getValue());
|
||||
image.setAlpha(oldAlpha);
|
||||
scalePosModX = image.getWidth() / 2 - xRadius;
|
||||
scalePosModY = image.getHeight() / 2 - yRadius;
|
||||
currentScale *= scale.getValue();
|
||||
}
|
||||
}
|
||||
if ((hoverEffect & EFFECT_FADE) > 0)
|
||||
image.setAlpha(alpha.getValue());
|
||||
if ((hoverEffect & EFFECT_ROTATE) > 0)
|
||||
image.setRotation(angle.getValue());
|
||||
image.draw(x - xRadius, y - yRadius, filter);
|
||||
image.draw(x - xRadius - scalePosModX, y - yRadius - scalePosModY, filter);
|
||||
if (image == this.img) {
|
||||
image.setAlpha(oldAlpha);
|
||||
image.setRotation(oldAngle);
|
||||
|
|
Loading…
Reference in New Issue
Block a user