Skip to content

Commit

Permalink
Added "favorites" and "last played" beatmap groups, and more sorts.
Browse files Browse the repository at this point in the history
- New sorts: by date added, and most played.
- Sorts are moved to a dropdown menu.
- Tabs are now groupings (all songs, last played, favorites).
- Add/remove "favorite" beatmaps in the right-click menu.
- Beatmap database is now updateable like score database (no longer drops/recreates on every update).
- Various bug fixes.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
  • Loading branch information
itdelatrisu committed Dec 22, 2016
1 parent 4446487 commit ed06a8b
Show file tree
Hide file tree
Showing 12 changed files with 601 additions and 133 deletions.
4 changes: 4 additions & 0 deletions src/itdelatrisu/opsu/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.BeatmapGroup;
import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.beatmap.BeatmapSortOrder;
import itdelatrisu.opsu.beatmap.BeatmapWatchService;
import itdelatrisu.opsu.downloads.DownloadList;
import itdelatrisu.opsu.downloads.Updater;
Expand Down Expand Up @@ -145,6 +147,8 @@ private void close_sub() {
SoundController.stopTrack();

// reset BeatmapSetList data
BeatmapGroup.set(BeatmapGroup.ALL);
BeatmapSortOrder.set(BeatmapSortOrder.TITLE);
if (BeatmapSetList.get() != null)
BeatmapSetList.get().reset();

Expand Down
20 changes: 20 additions & 0 deletions src/itdelatrisu/opsu/beatmap/Beatmap.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ public void eldestRemoved(Map.Entry<File, ImageLoader> eldest) {
/** The star rating. */
public double starRating = -1;

/** The timestamp this beatmap was first loaded. */
public long dateAdded = 0;

/** Whether this beatmap is marked as a "favorite". */
public boolean favorite = false;

/** Total number of times this beatmap has been played. */
public int playCount = 0;

/** The last time this beatmap was played (timestamp). */
public long lastPlayed = 0;

/**
* [General]
*/
Expand Down Expand Up @@ -501,4 +513,12 @@ public void sliderBorderFromString(String s) {
String[] rgb = s.split(",");
this.sliderBorder = new Color(new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])));
}

/**
* Increments the play counter and last played time.
*/
public void incrementPlayCounter() {
this.playCount++;
this.lastPlayed = System.currentTimeMillis();
}
}
179 changes: 179 additions & 0 deletions src/itdelatrisu/opsu/beatmap/BeatmapGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package itdelatrisu.opsu.beatmap;

import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.UI;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.PriorityQueue;

import org.newdawn.slick.Image;

/**
* Beatmap groups.
*/
public enum BeatmapGroup {
/** All beatmaps (no filter). */
ALL (0, "All Songs", null),

/** Most recently played beatmaps. */
RECENT (1, "Last Played", "Your recently played beatmaps will appear in this list!") {
/** Number of elements to show. */
private static final int K = 20;

/** Returns the latest "last played" time in a beatmap set. */
private long lastPlayed(BeatmapSet set) {
long max = 0;
for (Beatmap beatmap : set) {
if (beatmap.lastPlayed > max)
max = beatmap.lastPlayed;
}
return max;
}

@Override
public ArrayList<BeatmapSetNode> filter(ArrayList<BeatmapSetNode> list) {
// find top K elements
PriorityQueue<BeatmapSetNode> pq = new PriorityQueue<BeatmapSetNode>(K, new Comparator<BeatmapSetNode>() {
@Override
public int compare(BeatmapSetNode v, BeatmapSetNode w) {
return Long.compare(lastPlayed(v.getBeatmapSet()), lastPlayed(w.getBeatmapSet()));
}
});
for (BeatmapSetNode node : list) {
long timestamp = lastPlayed(node.getBeatmapSet());
if (timestamp == 0)
continue; // skip unplayed beatmaps
if (pq.size() < K || timestamp > lastPlayed(pq.peek().getBeatmapSet())) {
if (pq.size() == K)
pq.poll();
pq.add(node);
}
}

// return as list
ArrayList<BeatmapSetNode> filteredList = new ArrayList<BeatmapSetNode>();
for (BeatmapSetNode node : pq)
filteredList.add(node);
return filteredList;
}
},

/** "Favorite" beatmaps. */
FAVORITE (2, "Favorites", "Right-click a beatmap to add it to your Favorites!") {
@Override
public ArrayList<BeatmapSetNode> filter(ArrayList<BeatmapSetNode> list) {
// find "favorite" beatmaps
ArrayList<BeatmapSetNode> filteredList = new ArrayList<BeatmapSetNode>();
for (BeatmapSetNode node : list) {
if (node.getBeatmapSet().isFavorite())
filteredList.add(node);
}
return filteredList;
}
};

/** The ID of the group (used for tab positioning). */
private final int id;

/** The name of the group. */
private final String name;

/** The message to display if this list is empty. */
private final String emptyMessage;

/** The tab associated with the group (displayed in Song Menu screen). */
private MenuButton tab;

/** Total number of groups. */
private static final int SIZE = values().length;

/** Array of BeatmapGroup objects in reverse order. */
public static final BeatmapGroup[] VALUES_REVERSED;
static {
VALUES_REVERSED = values();
Collections.reverse(Arrays.asList(VALUES_REVERSED));
}

/** Current group. */
private static BeatmapGroup currentGroup = ALL;

/**
* Returns the current group.
* @return the current group
*/
public static BeatmapGroup current() { return currentGroup; }

/**
* Sets a new group.
* @param group the new group
*/
public static void set(BeatmapGroup group) { currentGroup = group; }

/**
* Constructor.
* @param id the ID of the group (for tab positioning)
* @param name the group name
* @param emptyMessage the message to display if this list is empty
*/
BeatmapGroup(int id, String name, String emptyMessage) {
this.id = id;
this.name = name;
this.emptyMessage = emptyMessage;
}

/**
* Returns the message to display if this list is empty.
* @return the message, or null if none
*/
public String getEmptyMessage() { return emptyMessage; }

/**
* Returns a filtered list of beatmap set nodes.
* @param list the unfiltered list
* @return the filtered list
*/
public ArrayList<BeatmapSetNode> filter(ArrayList<BeatmapSetNode> list) {
return list;
}

/**
* Initializes the tab.
* @param containerWidth the container width
* @param bottomY the bottom y coordinate
*/
public void init(int containerWidth, float bottomY) {
Image tab = GameImage.MENU_TAB.getImage();
int tabWidth = tab.getWidth();
float buttonX = containerWidth / 2f;
float tabOffset = (containerWidth - buttonX - tabWidth) / (SIZE - 1);
if (tabOffset > tabWidth) { // prevent tabs from being spaced out
tabOffset = tabWidth;
buttonX = (containerWidth * 0.99f) - (tabWidth * SIZE);
}
this.tab = new MenuButton(tab,
(buttonX + (tabWidth / 2f)) + (id * tabOffset),
bottomY - (tab.getHeight() / 2f)
);
}

/**
* Checks if the coordinates are within the image bounds.
* @param x the x coordinate
* @param y the y coordinate
* @return true if within bounds
*/
public boolean contains(float x, float y) { return tab.contains(x, y); }

/**
* Draws the tab.
* @param selected whether the tab is selected (white) or not (red)
* @param isHover whether to include a hover effect (unselected only)
*/
public void draw(boolean selected, boolean isHover) {
UI.drawTab(tab.getX(), tab.getY(), name, selected, isHover);
}
}
2 changes: 2 additions & 0 deletions src/itdelatrisu/opsu/beatmap/BeatmapParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public static BeatmapSetNode parseDirectories(File[] dirs) {

// parse directories
BeatmapSetNode lastNode = null;
long timestamp = System.currentTimeMillis();
for (File dir : dirs) {
currentDirectoryIndex++;
if (!dir.isDirectory())
Expand Down Expand Up @@ -163,6 +164,7 @@ public boolean accept(File dir, String name) {

// add to parsed beatmap list
if (beatmap != null) {
beatmap.dateAdded = timestamp;
beatmaps.add(beatmap);
parsedBeatmaps.add(beatmap);
}
Expand Down
23 changes: 23 additions & 0 deletions src/itdelatrisu/opsu/beatmap/BeatmapSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package itdelatrisu.opsu.beatmap;

import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.db.BeatmapDB;

import java.text.DecimalFormat;
import java.text.NumberFormat;
Expand Down Expand Up @@ -185,4 +186,26 @@ public boolean matches(String type, String operator, float value) {

return false;
}

/**
* Returns whether this beatmap set is a "favorite".
*/
public boolean isFavorite() {
for (Beatmap map : beatmaps) {
if (map.favorite)
return true;
}
return false;
}

/**
* Sets the "favorite" status of this beatmap set.
* @param flag whether this beatmap set should have "favorite" status
*/
public void setFavorite(boolean flag) {
for (Beatmap map : beatmaps) {
map.favorite = flag;
BeatmapDB.updateFavoriteStatus(map);
}
}
}
14 changes: 9 additions & 5 deletions src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class BeatmapSetList {
/** Total number of beatmaps (i.e. Beatmap objects). */
private int mapCount = 0;

/** List containing all nodes in the current group. */
private ArrayList<BeatmapSetNode> groupNodes;

/** Current list of nodes (subset of parsedNodes, used for searches). */
private ArrayList<BeatmapSetNode> nodes;

Expand Down Expand Up @@ -97,7 +100,7 @@ private BeatmapSetList() {
* This does not erase any parsed nodes.
*/
public void reset() {
nodes = parsedNodes;
nodes = groupNodes = BeatmapGroup.current().filter(parsedNodes);
expandedIndex = -1;
expandedStartNode = expandedEndNode = null;
lastQuery = "";
Expand Down Expand Up @@ -168,6 +171,7 @@ else if (ePrev != null && ePrev.index == expandedIndex)
Beatmap beatmap = beatmapSet.get(0);
nodes.remove(index);
parsedNodes.remove(eCur);
groupNodes.remove(eCur);
mapCount -= beatmapSet.size();
if (beatmap.beatmapSetID > 0)
MSIDdb.remove(beatmap.beatmapSetID);
Expand Down Expand Up @@ -407,7 +411,7 @@ public void init() {
return;

// sort the list
Collections.sort(nodes, BeatmapSortOrder.getSort().getComparator());
Collections.sort(nodes, BeatmapSortOrder.current().getComparator());
expandedIndex = -1;
expandedStartNode = expandedEndNode = null;

Expand Down Expand Up @@ -444,7 +448,7 @@ public boolean search(String query) {

// if empty query, reset to original list
if (query.isEmpty() || terms.isEmpty()) {
nodes = parsedNodes;
nodes = groupNodes;
return true;
}

Expand Down Expand Up @@ -472,14 +476,14 @@ public boolean search(String query) {
String type = condType.remove();
String operator = condOperator.remove();
float value = condValue.remove();
for (BeatmapSetNode node : parsedNodes) {
for (BeatmapSetNode node : groupNodes) {
if (node.getBeatmapSet().matches(type, operator, value))
nodes.add(node);
}
} else {
// normal term
String term = terms.remove();
for (BeatmapSetNode node : parsedNodes) {
for (BeatmapSetNode node : groupNodes) {
if (node.getBeatmapSet().matches(term))
nodes.add(node);
}
Expand Down
Loading

0 comments on commit ed06a8b

Please sign in to comment.