Continuation of 53c79c5 - created a BeatmapSet class.

Moved the strictly beatmap-related parts of BeatmapSetNode into a new BeatmapSet class.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han
2015-05-16 22:49:18 -04:00
parent 250f7acc93
commit ee5bc4b616
7 changed files with 239 additions and 158 deletions

View File

@@ -0,0 +1,178 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014, 2015 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/
package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.GameMod;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/**
* Data type containing all beatmaps in a beatmap set.
*/
public class BeatmapSet {
/** List of associated beatmaps. */
private ArrayList<Beatmap> beatmaps;
/**
* Constructor.
* @param beatmaps the beatmaps in this set
*/
public BeatmapSet(ArrayList<Beatmap> beatmaps) {
this.beatmaps = beatmaps;
}
/**
* Returns the number of elements.
*/
public int size() { return beatmaps.size(); }
/**
* Returns the beatmap at the given index.
* @param index the beatmap index
* @throws IndexOutOfBoundsException
*/
public Beatmap get(int index) { return beatmaps.get(index); }
/**
* Removes the beatmap at the given index.
* @param index the beatmap index
* @return the removed beatmap
* @throws IndexOutOfBoundsException
*/
public Beatmap remove(int index) { return beatmaps.remove(index); }
/**
* Returns an array of strings containing beatmap information.
* <ul>
* <li>0: {Artist} - {Title} [{Version}]
* <li>1: Mapped by {Creator}
* <li>2: Length: {} BPM: {} Objects: {}
* <li>3: Circles: {} Sliders: {} Spinners: {}
* <li>4: CS:{} HP:{} AR:{} OD:{}
* </ul>
* @param index the beatmap index
* @throws IndexOutOfBoundsException
*/
public String[] getInfo(int index) {
Beatmap beatmap = beatmaps.get(index);
float speedModifier = GameMod.getSpeedMultiplier();
long endTime = (long) (beatmap.endTime / speedModifier);
int bpmMin = (int) (beatmap.bpmMin * speedModifier);
int bpmMax = (int) (beatmap.bpmMax * speedModifier);
float multiplier = GameMod.getDifficultyMultiplier();
String[] info = new String[5];
info[0] = beatmap.toString();
info[1] = String.format("Mapped by %s", beatmap.creator);
info[2] = String.format("Length: %d:%02d BPM: %s Objects: %d",
TimeUnit.MILLISECONDS.toMinutes(endTime),
TimeUnit.MILLISECONDS.toSeconds(endTime) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(endTime)),
(bpmMax <= 0) ? "--" : ((bpmMin == bpmMax) ? bpmMin : String.format("%d-%d", bpmMin, bpmMax)),
(beatmap.hitObjectCircle + beatmap.hitObjectSlider + beatmap.hitObjectSpinner));
info[3] = String.format("Circles: %d Sliders: %d Spinners: %d",
beatmap.hitObjectCircle, beatmap.hitObjectSlider, beatmap.hitObjectSpinner);
info[4] = String.format("CS:%.1f HP:%.1f AR:%.1f OD:%.1f",
Math.min(beatmap.circleSize * multiplier, 10f),
Math.min(beatmap.HPDrainRate * multiplier, 10f),
Math.min(beatmap.approachRate * multiplier, 10f),
Math.min(beatmap.overallDifficulty * multiplier, 10f));
return info;
}
/**
* Returns a formatted string for the beatmap set:
* "Artist - Title"
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
Beatmap beatmap = beatmaps.get(0);
return String.format("%s - %s", beatmap.getArtist(), beatmap.getTitle());
}
/**
* Checks whether the beatmap set matches a given search query.
* @param query the search term
* @return true if title, artist, creator, source, version, or tag matches query
*/
public boolean matches(String query) {
// search: title, artist, creator, source, version, tags (first beatmap)
Beatmap beatmap = beatmaps.get(0);
if (beatmap.title.toLowerCase().contains(query) ||
beatmap.titleUnicode.toLowerCase().contains(query) ||
beatmap.artist.toLowerCase().contains(query) ||
beatmap.artistUnicode.toLowerCase().contains(query) ||
beatmap.creator.toLowerCase().contains(query) ||
beatmap.source.toLowerCase().contains(query) ||
beatmap.version.toLowerCase().contains(query) ||
beatmap.tags.contains(query))
return true;
// search: version, tags (remaining beatmaps)
for (int i = 1; i < beatmaps.size(); i++) {
beatmap = beatmaps.get(i);
if (beatmap.version.toLowerCase().contains(query) ||
beatmap.tags.contains(query))
return true;
}
return false;
}
/**
* Checks whether the beatmap set matches a given condition.
* @param type the condition type (ar, cs, od, hp, bpm, length)
* @param operator the operator (=/==, >, >=, <, <=)
* @param value the value
* @return true if the condition is met
*/
public boolean matches(String type, String operator, float value) {
for (Beatmap beatmap : beatmaps) {
// get value
float v;
switch (type) {
case "ar": v = beatmap.approachRate; break;
case "cs": v = beatmap.circleSize; break;
case "od": v = beatmap.overallDifficulty; break;
case "hp": v = beatmap.HPDrainRate; break;
case "bpm": v = beatmap.bpmMax; break;
case "length": v = beatmap.endTime / 1000; break;
default: return false;
}
// get operator
boolean met;
switch (operator) {
case "=":
case "==": met = (v == value); break;
case ">": met = (v > value); break;
case ">=": met = (v >= value); break;
case "<": met = (v < value); break;
case "<=": met = (v <= value); break;
default: return false;
}
if (met)
return true;
}
return false;
}
}

View File

@@ -109,7 +109,8 @@ public class BeatmapSetList {
* @return the new BeatmapSetNode
*/
public BeatmapSetNode addSongGroup(ArrayList<Beatmap> beatmaps) {
BeatmapSetNode node = new BeatmapSetNode(beatmaps);
BeatmapSet beatmapSet = new BeatmapSet(beatmaps);
BeatmapSetNode node = new BeatmapSetNode(beatmapSet);
parsedNodes.add(node);
mapCount += beatmaps.size();
@@ -152,10 +153,10 @@ public class BeatmapSetList {
}
// remove all node references
Beatmap beatmap = node.beatmaps.get(0);
Beatmap beatmap = node.getBeatmapSet().get(0);
nodes.remove(index);
parsedNodes.remove(eCur);
mapCount -= node.beatmaps.size();
mapCount -= node.getBeatmapSet().size();
if (beatmap.beatmapSetID > 0)
MSIDdb.remove(beatmap.beatmapSetID);
@@ -168,7 +169,7 @@ public class BeatmapSetList {
} else if (expandedIndex > index) {
expandedIndex--;
BeatmapSetNode expandedNode = expandedStartNode;
for (int i = 0, size = expandedNode.beatmaps.size();
for (int i = 0, size = expandedNode.getBeatmapSet().size();
i < size && expandedNode != null;
i++, expandedNode = expandedNode.next)
expandedNode.index = expandedIndex;
@@ -210,8 +211,8 @@ public class BeatmapSetList {
return false;
// last song in group?
int size = node.beatmaps.size();
if (node.beatmaps.size() == 1)
int size = node.getBeatmapSet().size();
if (size == 1)
return deleteSongGroup(node);
// reset indices
@@ -222,7 +223,7 @@ public class BeatmapSetList {
expandedNode.beatmapIndex--;
// remove song reference
Beatmap beatmap = node.beatmaps.remove(node.beatmapIndex);
Beatmap beatmap = node.getBeatmapSet().remove(node.beatmapIndex);
mapCount--;
// re-link nodes
@@ -314,11 +315,11 @@ public class BeatmapSetList {
expandedStartNode = expandedEndNode = null;
// create new nodes
ArrayList<Beatmap> beatmaps = node.beatmaps;
BeatmapSet beatmapSet = node.getBeatmapSet();
BeatmapSetNode prevNode = node.prev;
BeatmapSetNode nextNode = node.next;
for (int i = 0, size = node.beatmaps.size(); i < size; i++) {
BeatmapSetNode newNode = new BeatmapSetNode(beatmaps);
for (int i = 0, size = beatmapSet.size(); i < size; i++) {
BeatmapSetNode newNode = new BeatmapSetNode(beatmapSet);
newNode.index = index;
newNode.beatmapIndex = i;
newNode.prev = node;
@@ -443,14 +444,14 @@ public class BeatmapSetList {
String operator = condOperator.remove();
float value = condValue.remove();
for (BeatmapSetNode node : parsedNodes) {
if (node.matches(type, operator, value))
if (node.getBeatmapSet().matches(type, operator, value))
nodes.add(node);
}
} else {
// normal term
String term = terms.remove();
for (BeatmapSetNode node : parsedNodes) {
if (node.matches(term))
if (node.getBeatmapSet().matches(term))
nodes.add(node);
}
}
@@ -466,7 +467,7 @@ public class BeatmapSetList {
Iterator<BeatmapSetNode> nodeIter = nodes.iterator();
while (nodeIter.hasNext()) {
BeatmapSetNode node = nodeIter.next();
if (!node.matches(term))
if (!node.getBeatmapSet().matches(term))
nodeIter.remove();
}
}
@@ -484,7 +485,7 @@ public class BeatmapSetList {
Iterator<BeatmapSetNode> nodeIter = nodes.iterator();
while (nodeIter.hasNext()) {
BeatmapSetNode node = nodeIter.next();
if (!node.matches(type, operator, value))
if (!node.getBeatmapSet().matches(type, operator, value))
nodeIter.remove();
}
}

View File

@@ -20,40 +20,42 @@ package itdelatrisu.opsu.beatmap;
import itdelatrisu.opsu.GameData.Grade;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
/**
* Node in an BeatmapSetList representing a group of beatmaps.
* Node in an BeatmapSetList representing a beatmap set.
*/
public class BeatmapSetNode {
/** List of associated beatmaps. */
public ArrayList<Beatmap> beatmaps;
/** Index of this node. */
public int index = 0;
/** The associated beatmap set. */
private BeatmapSet beatmapSet;
/** Index of the selected beatmap (-1 if not focused). */
public int beatmapIndex = -1;
/** Index of this node. */
public int index = 0;
/** Links to other nodes. */
public BeatmapSetNode prev, next;
/**
* Constructor.
* @param beatmaps the beatmaps in this group
* @param beatmapSet the beatmap set
*/
public BeatmapSetNode(ArrayList<Beatmap> beatmaps) {
this.beatmaps = beatmaps;
public BeatmapSetNode(BeatmapSet beatmapSet) {
this.beatmapSet = beatmapSet;
}
/**
* Returns the associated beatmap set.
* @return the beatmap set
*/
public BeatmapSet getBeatmapSet() { return beatmapSet; }
/**
* Draws the button.
* @param x the x coordinate
@@ -77,10 +79,10 @@ public class BeatmapSetNode {
textColor = Color.white;
} else
bgColor = Utils.COLOR_BLUE_BUTTON;
beatmap = beatmaps.get(beatmapIndex);
beatmap = beatmapSet.get(beatmapIndex);
} else {
bgColor = Utils.COLOR_ORANGE_BUTTON;
beatmap = beatmaps.get(0);
beatmap = beatmapSet.get(0);
}
bg.draw(x, y, bgColor);
@@ -102,49 +104,17 @@ public class BeatmapSetNode {
Utils.FONT_MEDIUM.drawString(cx, cy, beatmap.getTitle(), textColor);
Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 2,
String.format("%s // %s", beatmap.getArtist(), beatmap.creator), textColor);
if (expanded || beatmaps.size() == 1)
if (expanded || beatmapSet.size() == 1)
Utils.FONT_BOLD.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() - 4,
beatmap.version, textColor);
}
/**
* Returns an array of strings containing song information.
* <ul>
* <li>0: {Artist} - {Title} [{Version}]
* <li>1: Mapped by {Creator}
* <li>2: Length: {} BPM: {} Objects: {}
* <li>3: Circles: {} Sliders: {} Spinners: {}
* <li>4: CS:{} HP:{} AR:{} OD:{}
* </ul>
* Returns an array of strings containing beatmap information for the
* selected beatmap, or null if none selected.
* @see BeatmapSet#getInfo(int)
*/
public String[] getInfo() {
if (beatmapIndex < 0)
return null;
Beatmap beatmap = beatmaps.get(beatmapIndex);
float speedModifier = GameMod.getSpeedMultiplier();
long endTime = (long) (beatmap.endTime / speedModifier);
int bpmMin = (int) (beatmap.bpmMin * speedModifier);
int bpmMax = (int) (beatmap.bpmMax * speedModifier);
float multiplier = GameMod.getDifficultyMultiplier();
String[] info = new String[5];
info[0] = beatmap.toString();
info[1] = String.format("Mapped by %s", beatmap.creator);
info[2] = String.format("Length: %d:%02d BPM: %s Objects: %d",
TimeUnit.MILLISECONDS.toMinutes(endTime),
TimeUnit.MILLISECONDS.toSeconds(endTime) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(endTime)),
(bpmMax <= 0) ? "--" : ((bpmMin == bpmMax) ? bpmMin : String.format("%d-%d", bpmMin, bpmMax)),
(beatmap.hitObjectCircle + beatmap.hitObjectSlider + beatmap.hitObjectSpinner));
info[3] = String.format("Circles: %d Sliders: %d Spinners: %d",
beatmap.hitObjectCircle, beatmap.hitObjectSlider, beatmap.hitObjectSpinner);
info[4] = String.format("CS:%.1f HP:%.1f AR:%.1f OD:%.1f",
Math.min(beatmap.circleSize * multiplier, 10f),
Math.min(beatmap.HPDrainRate * multiplier, 10f),
Math.min(beatmap.approachRate * multiplier, 10f),
Math.min(beatmap.overallDifficulty * multiplier, 10f));
return info;
}
public String[] getInfo() { return (beatmapIndex < 0) ? null : beatmapSet.getInfo(beatmapIndex); }
/**
* Returns a formatted string for the beatmap at {@code beatmapIndex}:
@@ -154,78 +124,8 @@ public class BeatmapSetNode {
@Override
public String toString() {
if (beatmapIndex == -1)
return String.format("%s - %s", beatmaps.get(0).getArtist(), beatmaps.get(0).getTitle());
return beatmapSet.toString();
else
return beatmaps.get(beatmapIndex).toString();
}
/**
* Checks whether the node matches a given search query.
* @param query the search term
* @return true if title, artist, creator, source, version, or tag matches query
*/
public boolean matches(String query) {
Beatmap beatmap = beatmaps.get(0);
// search: title, artist, creator, source, version, tags (first beatmap)
if (beatmap.title.toLowerCase().contains(query) ||
beatmap.titleUnicode.toLowerCase().contains(query) ||
beatmap.artist.toLowerCase().contains(query) ||
beatmap.artistUnicode.toLowerCase().contains(query) ||
beatmap.creator.toLowerCase().contains(query) ||
beatmap.source.toLowerCase().contains(query) ||
beatmap.version.toLowerCase().contains(query) ||
beatmap.tags.contains(query))
return true;
// search: version, tags (remaining beatmaps)
for (int i = 1; i < beatmaps.size(); i++) {
beatmap = beatmaps.get(i);
if (beatmap.version.toLowerCase().contains(query) ||
beatmap.tags.contains(query))
return true;
}
return false;
}
/**
* Checks whether the node matches a given condition.
* @param type the condition type (ar, cs, od, hp, bpm, length)
* @param operator the operator (=/==, >, >=, <, <=)
* @param value the value
* @return true if the condition is met
*/
public boolean matches(String type, String operator, float value) {
for (Beatmap beatmap : beatmaps) {
// get value
float v;
switch (type) {
case "ar": v = beatmap.approachRate; break;
case "cs": v = beatmap.circleSize; break;
case "od": v = beatmap.overallDifficulty; break;
case "hp": v = beatmap.HPDrainRate; break;
case "bpm": v = beatmap.bpmMax; break;
case "length": v = beatmap.endTime / 1000; break;
default: return false;
}
// get operator
boolean met;
switch (operator) {
case "=":
case "==": met = (v == value); break;
case ">": met = (v > value); break;
case ">=": met = (v >= value); break;
case "<": met = (v < value); break;
case "<=": met = (v <= value); break;
default: return false;
}
if (met)
return true;
}
return false;
return beatmapSet.get(beatmapIndex).toString();
}
}