Hit object refactoring.
- Moved the bulk of hit object parsing into the OsuHitObject constructor, and made all fields private. Only combo-related data is still set by OsuParser. - Added 'isCircle()', 'isSlider()', 'isSpinner()', and 'isNewCombo()' methods for convenience. Other changes: - Fixed difficulty overrides are no longer affected by game mods. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
2380b11f48
commit
717605564d
|
@ -500,7 +500,7 @@ public class GameScore {
|
||||||
g.drawOval(circleX, symbolHeight, circleDiameter, circleDiameter);
|
g.drawOval(circleX, symbolHeight, circleDiameter, circleDiameter);
|
||||||
|
|
||||||
OsuFile osu = MusicController.getOsuFile();
|
OsuFile osu = MusicController.getOsuFile();
|
||||||
int firstObjectTime = osu.objects[0].time;
|
int firstObjectTime = osu.objects[0].getTime();
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
if (trackPosition > firstObjectTime) {
|
if (trackPosition > firstObjectTime) {
|
||||||
// map progress (white)
|
// map progress (white)
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class OsuHitObject {
|
||||||
TYPE_SPINNER = 8;
|
TYPE_SPINNER = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hit sound types.
|
* Hit sound types (bits).
|
||||||
*/
|
*/
|
||||||
public static final byte
|
public static final byte
|
||||||
SOUND_NORMAL = 0,
|
SOUND_NORMAL = 0,
|
||||||
|
@ -53,39 +53,263 @@ public class OsuHitObject {
|
||||||
/**
|
/**
|
||||||
* Max hit object coordinates.
|
* Max hit object coordinates.
|
||||||
*/
|
*/
|
||||||
public static final int
|
private static final int
|
||||||
MAX_X = 512,
|
MAX_X = 512,
|
||||||
MAX_Y = 384;
|
MAX_Y = 384;
|
||||||
|
|
||||||
// parsed fields (coordinates are scaled)
|
/**
|
||||||
public float x, y; // start coordinates
|
* The x and y multipliers for hit object coordinates.
|
||||||
public int time; // start time, in ms
|
*/
|
||||||
public int type; // hit object type
|
private static float xMultiplier, yMultiplier;
|
||||||
public byte hitSound; // hit sound type
|
|
||||||
public char sliderType; // slider curve type (sliders only)
|
/**
|
||||||
public float[] sliderX; // slider x coordinate list (sliders only)
|
* The x and y offsets for hit object coordinates.
|
||||||
public float[] sliderY; // slider y coordinate list (sliders only)
|
*/
|
||||||
public int repeat; // slider repeat count (sliders only)
|
private static int
|
||||||
public float pixelLength; // slider pixel length (sliders only)
|
xOffset, // offset right of border
|
||||||
public int endTime; // end time, in ms (spinners only)
|
yOffset; // offset below health bar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting coordinates (scaled).
|
||||||
|
*/
|
||||||
|
private float x, y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start time (in ms).
|
||||||
|
*/
|
||||||
|
private int time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit object type (TYPE_* bitmask).
|
||||||
|
*/
|
||||||
|
private int type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit sound type (SOUND_* bitmask).
|
||||||
|
*/
|
||||||
|
private byte hitSound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slider curve type (SLIDER_* constant).
|
||||||
|
*/
|
||||||
|
private char sliderType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slider coordinate lists (scaled).
|
||||||
|
*/
|
||||||
|
private float[] sliderX, sliderY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slider repeat count.
|
||||||
|
*/
|
||||||
|
private int repeat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slider pixel length.
|
||||||
|
*/
|
||||||
|
private float pixelLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spinner end time (in ms).
|
||||||
|
*/
|
||||||
|
private int endTime;
|
||||||
|
|
||||||
// additional v10+ parameters not implemented...
|
// additional v10+ parameters not implemented...
|
||||||
// addition -> sampl:add:cust:vol:hitsound
|
// addition -> sampl:add:cust:vol:hitsound
|
||||||
// edge_hitsound, edge_addition (sliders only)
|
// edge_hitsound, edge_addition (sliders only)
|
||||||
|
|
||||||
// extra fields
|
/**
|
||||||
public int comboIndex; // current index in Color array
|
* Current index in combo color array.
|
||||||
public int comboNumber; // number to display in hit object
|
*/
|
||||||
|
private int comboIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with all required fields.
|
* Number to display in hit object.
|
||||||
*/
|
*/
|
||||||
public OsuHitObject(float x, float y, int time, int type, byte hitSound) {
|
private int comboNumber;
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
/**
|
||||||
this.time = time;
|
* Initializes the OsuHitObject data type with container dimensions.
|
||||||
this.type = type;
|
* @param width the container width
|
||||||
this.hitSound = hitSound;
|
* @param height the container height
|
||||||
|
*/
|
||||||
|
public static void init(int width, int height) {
|
||||||
|
xMultiplier = (width * 0.6f) / MAX_X;
|
||||||
|
yMultiplier = (height * 0.6f) / MAX_Y;
|
||||||
|
xOffset = width / 5;
|
||||||
|
yOffset = height / 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param line the line to be parsed
|
||||||
|
*/
|
||||||
|
public OsuHitObject(String line) {
|
||||||
|
/**
|
||||||
|
* [OBJECT FORMATS]
|
||||||
|
* Circles:
|
||||||
|
* x,y,time,type,hitSound,addition
|
||||||
|
* 256,148,9466,1,2,0:0:0:0:
|
||||||
|
*
|
||||||
|
* Sliders:
|
||||||
|
* x,y,time,type,hitSound,sliderType|curveX:curveY|...,repeat,pixelLength,edgeHitsound,edgeAddition,addition
|
||||||
|
* 300,68,4591,2,0,B|372:100|332:172|420:192,2,180,2|2|2,0:0|0:0|0:0,0:0:0:0:
|
||||||
|
*
|
||||||
|
* Spinners:
|
||||||
|
* x,y,time,type,hitSound,endTime,addition
|
||||||
|
* 256,192,654,12,0,4029,0:0:0:0:
|
||||||
|
*
|
||||||
|
* NOTE: 'addition' is optional, and defaults to "0:0:0:0:".
|
||||||
|
*/
|
||||||
|
String tokens[] = line.split(",");
|
||||||
|
|
||||||
|
// common fields
|
||||||
|
this.x = Integer.parseInt(tokens[0]) * xMultiplier + xOffset;
|
||||||
|
this.y = Integer.parseInt(tokens[1]) * yMultiplier + yOffset;
|
||||||
|
this.time = Integer.parseInt(tokens[2]);
|
||||||
|
this.type = Integer.parseInt(tokens[3]);
|
||||||
|
this.hitSound = Byte.parseByte(tokens[4]);
|
||||||
|
|
||||||
|
// type-specific fields
|
||||||
|
if ((type & OsuHitObject.TYPE_CIRCLE) > 0) {
|
||||||
|
/* 'addition' not implemented. */
|
||||||
|
|
||||||
|
} else if ((type & OsuHitObject.TYPE_SLIDER) > 0) {
|
||||||
|
// slider curve type and coordinates
|
||||||
|
String[] sliderTokens = tokens[5].split("\\|");
|
||||||
|
this.sliderType = sliderTokens[0].charAt(0);
|
||||||
|
this.sliderX = new float[sliderTokens.length - 1];
|
||||||
|
this.sliderY = new float[sliderTokens.length - 1];
|
||||||
|
for (int j = 1; j < sliderTokens.length; j++) {
|
||||||
|
String[] sliderXY = sliderTokens[j].split(":");
|
||||||
|
this.sliderX[j - 1] = Integer.parseInt(sliderXY[0]) * xMultiplier + xOffset;
|
||||||
|
this.sliderY[j - 1] = Integer.parseInt(sliderXY[1]) * yMultiplier + yOffset;
|
||||||
|
}
|
||||||
|
this.repeat = Integer.parseInt(tokens[6]);
|
||||||
|
this.pixelLength = Float.parseFloat(tokens[7]);
|
||||||
|
/* edge fields and 'addition' not implemented. */
|
||||||
|
|
||||||
|
} else { //if ((type & OsuHitObject.TYPE_SPINNER) > 0) {
|
||||||
|
// some 'endTime' fields contain a ':' character (?)
|
||||||
|
int index = tokens[5].indexOf(':');
|
||||||
|
if (index != -1)
|
||||||
|
tokens[5] = tokens[5].substring(0, index);
|
||||||
|
this.endTime = Integer.parseInt(tokens[5]);
|
||||||
|
/* 'addition' not implemented. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the starting x coordinate.
|
||||||
|
* @return the x coordinate
|
||||||
|
*/
|
||||||
|
public float getX() { return x; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the starting y coordinate.
|
||||||
|
* @return the y coordinate
|
||||||
|
*/
|
||||||
|
public float getY() { return y; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start time.
|
||||||
|
* @return the start time (in ms)
|
||||||
|
*/
|
||||||
|
public int getTime() { return time; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hit object type.
|
||||||
|
* @return the object type (TYPE_* bitmask)
|
||||||
|
*/
|
||||||
|
public int getType() { return type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hit sound type.
|
||||||
|
* @return the sound type (SOUND_* bitmask)
|
||||||
|
*/
|
||||||
|
public byte getHitSoundType() { return hitSound; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider type.
|
||||||
|
* @return the slider type (SLIDER_* constant)
|
||||||
|
*/
|
||||||
|
public char getSliderType() { return sliderType; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of slider x coordinates.
|
||||||
|
* @return the slider x coordinates
|
||||||
|
*/
|
||||||
|
public float[] getSliderX() { return sliderX; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of slider y coordinates.
|
||||||
|
* @return the slider y coordinates
|
||||||
|
*/
|
||||||
|
public float[] getSliderY() { return sliderY; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider repeat count.
|
||||||
|
* @return the repeat count
|
||||||
|
*/
|
||||||
|
public int getRepeatCount() { return repeat; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider pixel length.
|
||||||
|
* @return the pixel length
|
||||||
|
*/
|
||||||
|
public float getPixelLength() { return pixelLength; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the spinner end time.
|
||||||
|
* @return the end time (in ms)
|
||||||
|
*/
|
||||||
|
public int getEndTime() { return endTime; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current index in the combo color array.
|
||||||
|
* @param comboIndex the combo index
|
||||||
|
*/
|
||||||
|
public void setComboIndex(int comboIndex) { this.comboIndex = comboIndex; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current index in the combo color array.
|
||||||
|
* @return the combo index
|
||||||
|
*/
|
||||||
|
public int getComboIndex() { return comboIndex; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number to display in the hit object.
|
||||||
|
* @param comboNumber the combo number
|
||||||
|
*/
|
||||||
|
public void setComboNumber(int comboNumber) { this.comboNumber = comboNumber; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number to display in the hit object.
|
||||||
|
* @return the combo number
|
||||||
|
*/
|
||||||
|
public int getComboNumber() { return comboNumber; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the hit object is a circle.
|
||||||
|
* @return true if circle
|
||||||
|
*/
|
||||||
|
public boolean isCircle() { return (type & TYPE_CIRCLE) > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the hit object is a slider.
|
||||||
|
* @return true if slider
|
||||||
|
*/
|
||||||
|
public boolean isSlider() { return (type & TYPE_SLIDER) > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the hit object is a spinner.
|
||||||
|
* @return true if spinner
|
||||||
|
*/
|
||||||
|
public boolean isSpinner() { return (type & TYPE_SPINNER) > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the hit object starts a new combo.
|
||||||
|
* @return true if new combo
|
||||||
|
*/
|
||||||
|
public boolean isNewCombo() { return (type & TYPE_NEWCOMBO) > 0; }
|
||||||
}
|
}
|
|
@ -49,18 +49,6 @@ public class OsuParser {
|
||||||
*/
|
*/
|
||||||
private static int totalDirectories = -1;
|
private static int totalDirectories = -1;
|
||||||
|
|
||||||
/**
|
|
||||||
* The x and y multipliers for hit object coordinates.
|
|
||||||
*/
|
|
||||||
private static float xMultiplier, yMultiplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The x and y offsets for hit object coordinates.
|
|
||||||
*/
|
|
||||||
private static int
|
|
||||||
xOffset, // offset right of border
|
|
||||||
yOffset; // offset below health bar
|
|
||||||
|
|
||||||
// This class should not be instantiated.
|
// This class should not be instantiated.
|
||||||
private OsuParser() {}
|
private OsuParser() {}
|
||||||
|
|
||||||
|
@ -71,11 +59,8 @@ public class OsuParser {
|
||||||
* @param height the container height
|
* @param height the container height
|
||||||
*/
|
*/
|
||||||
public static void parseAllFiles(File root, int width, int height) {
|
public static void parseAllFiles(File root, int width, int height) {
|
||||||
// set coordinate modifiers
|
// initialize hit objects
|
||||||
xMultiplier = (width * 0.6f) / OsuHitObject.MAX_X;
|
OsuHitObject.init(width, height);
|
||||||
yMultiplier = (height * 0.6f) / OsuHitObject.MAX_Y;
|
|
||||||
xOffset = width / 5;
|
|
||||||
yOffset = height / 5;
|
|
||||||
|
|
||||||
// progress tracking
|
// progress tracking
|
||||||
File[] folders = root.listFiles();
|
File[] folders = root.listFiles();
|
||||||
|
@ -438,23 +423,7 @@ public class OsuParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses all hit objects in an OSU file.
|
* Parses all hit objects in an OSU file.
|
||||||
*
|
* @param osu the OsuFile to parse
|
||||||
* Object formats:
|
|
||||||
* - Circles [1]:
|
|
||||||
* x,y,time,type,hitSound,addition
|
|
||||||
* 256,148,9466,1,2,0:0:0:0:
|
|
||||||
*
|
|
||||||
* - Sliders [2]:
|
|
||||||
* x,y,time,type,hitSound,sliderType|curveX:curveY|...,repeat,pixelLength,edgeHitsound,edgeAddition,addition
|
|
||||||
* 300,68,4591,2,0,B|372:100|332:172|420:192,2,180,2|2|2,0:0|0:0|0:0,0:0:0:0:
|
|
||||||
*
|
|
||||||
* - Spinners [8]:
|
|
||||||
* x,y,time,type,hitSound,endTime,addition
|
|
||||||
* 256,192,654,12,0,4029,0:0:0:0:
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
* - 'addition' is optional, and defaults to "0:0:0:0:".
|
|
||||||
* - Field descriptions are located in OsuHitObject.java.
|
|
||||||
*/
|
*/
|
||||||
public static void parseHitObjects(OsuFile osu) {
|
public static void parseHitObjects(OsuFile osu) {
|
||||||
if (osu.objects != null) // already parsed
|
if (osu.objects != null) // already parsed
|
||||||
|
@ -467,72 +436,47 @@ public class OsuParser {
|
||||||
String line = in.readLine();
|
String line = in.readLine();
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if (!line.equals("[HitObjects]")) {
|
if (!line.equals("[HitObjects]"))
|
||||||
line = in.readLine();
|
line = in.readLine();
|
||||||
continue;
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line == null) {
|
||||||
|
Log.warn(String.format("No hit objects found in OsuFile '%s'.", osu.toString()));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0; // object index
|
// combo info
|
||||||
int comboIndex = 0; // color index
|
int comboIndex = 0; // color index
|
||||||
int comboNumber = 1; // combo number
|
int comboNumber = 1; // combo number
|
||||||
String tokens[], sliderTokens[], sliderXY[];
|
|
||||||
|
|
||||||
while ((line = in.readLine()) != null && i < osu.objects.length) {
|
int objectIndex = 0;
|
||||||
|
while ((line = in.readLine()) != null && objectIndex < osu.objects.length) {
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if (!isValidLine(line))
|
if (!isValidLine(line))
|
||||||
continue;
|
continue;
|
||||||
if (line.charAt(0) == '[')
|
if (line.charAt(0) == '[')
|
||||||
break;
|
break;
|
||||||
// create a new OsuHitObject for each line
|
|
||||||
tokens = line.split(",");
|
|
||||||
if (tokens.length < 5)
|
|
||||||
continue;
|
|
||||||
float scaledX = Integer.parseInt(tokens[0]) * xMultiplier + xOffset;
|
|
||||||
float scaledY = Integer.parseInt(tokens[1]) * yMultiplier + yOffset;
|
|
||||||
int type = Integer.parseInt(tokens[3]);
|
|
||||||
osu.objects[i] = new OsuHitObject(
|
|
||||||
scaledX, scaledY, Integer.parseInt(tokens[2]),
|
|
||||||
type, Byte.parseByte(tokens[4])
|
|
||||||
);
|
|
||||||
if ((type & OsuHitObject.TYPE_CIRCLE) > 0) {
|
|
||||||
/* 'addition' not implemented. */
|
|
||||||
} else if ((type & OsuHitObject.TYPE_SLIDER) > 0) {
|
|
||||||
// slider curve type and coordinates
|
|
||||||
sliderTokens = tokens[5].split("\\|");
|
|
||||||
osu.objects[i].sliderType = sliderTokens[0].charAt(0);
|
|
||||||
osu.objects[i].sliderX = new float[sliderTokens.length-1];
|
|
||||||
osu.objects[i].sliderY = new float[sliderTokens.length-1];
|
|
||||||
for (int j = 1; j < sliderTokens.length; j++) {
|
|
||||||
sliderXY = sliderTokens[j].split(":");
|
|
||||||
osu.objects[i].sliderX[j-1] = Integer.parseInt(sliderXY[0]) * xMultiplier + xOffset;
|
|
||||||
osu.objects[i].sliderY[j-1] = Integer.parseInt(sliderXY[1]) * yMultiplier + yOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
osu.objects[i].repeat = Integer.parseInt(tokens[6]);
|
// lines must have at minimum 5 parameters
|
||||||
osu.objects[i].pixelLength = Float.parseFloat(tokens[7]);
|
int tokenCount = line.length() - line.replace(",", "").length();
|
||||||
/* edge fields and 'addition' not implemented. */
|
if (tokenCount < 4)
|
||||||
} else { //if ((type & OsuHitObject.TYPE_SPINNER) > 0) {
|
continue;
|
||||||
// some 'endTime' fields contain a ':' character (?)
|
|
||||||
int index = tokens[5].indexOf(':');
|
// create a new OsuHitObject for each line
|
||||||
if (index != -1)
|
OsuHitObject hitObject = new OsuHitObject(line);
|
||||||
tokens[5] = tokens[5].substring(0, index);
|
|
||||||
osu.objects[i].endTime = Integer.parseInt(tokens[5]);
|
|
||||||
/* 'addition' not implemented. */
|
|
||||||
}
|
|
||||||
|
|
||||||
// set combo info
|
// set combo info
|
||||||
// - new combo: get next combo index, reset combo number
|
// - new combo: get next combo index, reset combo number
|
||||||
// - else: maintain combo index, increase combo number
|
// - else: maintain combo index, increase combo number
|
||||||
if ((osu.objects[i].type & OsuHitObject.TYPE_NEWCOMBO) > 0) {
|
if (hitObject.isNewCombo()) {
|
||||||
comboIndex = (comboIndex + 1) % osu.combo.length;
|
comboIndex = (comboIndex + 1) % osu.combo.length;
|
||||||
comboNumber = 1;
|
comboNumber = 1;
|
||||||
}
|
}
|
||||||
osu.objects[i].comboIndex = comboIndex;
|
hitObject.setComboIndex(comboIndex);
|
||||||
osu.objects[i].comboNumber = comboNumber++;
|
hitObject.setComboNumber(comboNumber++);
|
||||||
|
|
||||||
i++;
|
osu.objects[objectIndex++] = hitObject;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.error(String.format("Failed to read file '%s'.", osu.getFile().getAbsolutePath()), e);
|
Log.error(String.format("Failed to read file '%s'.", osu.getFile().getAbsolutePath()), e);
|
||||||
|
|
|
@ -94,15 +94,15 @@ public class Circle {
|
||||||
* @param trackPosition the current track position
|
* @param trackPosition the current track position
|
||||||
*/
|
*/
|
||||||
public void draw(int trackPosition) {
|
public void draw(int trackPosition) {
|
||||||
int timeDiff = hitObject.time - trackPosition;
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
|
|
||||||
if (timeDiff >= 0) {
|
if (timeDiff >= 0) {
|
||||||
|
float x = hitObject.getX(), y = hitObject.getY();
|
||||||
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
||||||
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale),
|
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color);
|
||||||
hitObject.x, hitObject.y, color);
|
Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Color.white);
|
||||||
Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), hitObject.x, hitObject.y, Color.white);
|
Utils.drawCentered(GameImage.HITCIRCLE.getImage(), x, y, color);
|
||||||
Utils.drawCentered(GameImage.HITCIRCLE.getImage(), hitObject.x, hitObject.y, color);
|
score.drawSymbolNumber(hitObject.getComboNumber(), x, y,
|
||||||
score.drawSymbolNumber(hitObject.comboNumber, hitObject.x, hitObject.y,
|
|
||||||
GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,13 +139,15 @@ public class Circle {
|
||||||
* @return true if a hit result was processed
|
* @return true if a hit result was processed
|
||||||
*/
|
*/
|
||||||
public boolean mousePressed(int x, int y) {
|
public boolean mousePressed(int x, int y) {
|
||||||
double distance = Math.hypot(hitObject.x - x, hitObject.y - y);
|
double distance = Math.hypot(hitObject.getX() - x, hitObject.getY() - y);
|
||||||
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
||||||
if (distance < circleRadius) {
|
if (distance < circleRadius) {
|
||||||
int result = hitResult(hitObject.time);
|
int result = hitResult(hitObject.getTime());
|
||||||
if (result > -1) {
|
if (result > -1) {
|
||||||
score.hitResult(hitObject.time, result, hitObject.x, hitObject.y,
|
score.hitResult(
|
||||||
color, comboEnd, hitObject.hitSound
|
hitObject.getTime(), result,
|
||||||
|
hitObject.getX(), hitObject.getY(),
|
||||||
|
color, comboEnd, hitObject.getHitSoundType()
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -159,26 +161,27 @@ public class Circle {
|
||||||
* @return true if a hit result (miss) was processed
|
* @return true if a hit result (miss) was processed
|
||||||
*/
|
*/
|
||||||
public boolean update(boolean overlap) {
|
public boolean update(boolean overlap) {
|
||||||
|
int time = hitObject.getTime();
|
||||||
|
float x = hitObject.getX(), y = hitObject.getY();
|
||||||
|
byte hitSound = hitObject.getHitSoundType();
|
||||||
|
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
int[] hitResultOffset = game.getHitResultOffsets();
|
int[] hitResultOffset = game.getHitResultOffsets();
|
||||||
boolean isAutoMod = GameMod.AUTO.isActive();
|
boolean isAutoMod = GameMod.AUTO.isActive();
|
||||||
|
|
||||||
if (overlap || trackPosition > hitObject.time + hitResultOffset[GameScore.HIT_50]) {
|
if (overlap || trackPosition > time + hitResultOffset[GameScore.HIT_50]) {
|
||||||
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
|
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_300,
|
score.hitResult(time, GameScore.HIT_300, x, y, color, comboEnd, hitSound);
|
||||||
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
|
||||||
|
|
||||||
else // no more points can be scored, so send a miss
|
else // no more points can be scored, so send a miss
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_MISS,
|
score.hitResult(time, GameScore.HIT_MISS, x, y, null, comboEnd, hitSound);
|
||||||
hitObject.x, hitObject.y, null, comboEnd, hitObject.hitSound);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "auto" mod: send a perfect hit result
|
// "auto" mod: send a perfect hit result
|
||||||
else if (isAutoMod) {
|
else if (isAutoMod) {
|
||||||
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
if (Math.abs(trackPosition - time) < hitResultOffset[GameScore.HIT_300]) {
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_300,
|
score.hitResult(time, GameScore.HIT_300, x, y, color, comboEnd, hitSound);
|
||||||
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,8 +155,8 @@ public class Slider {
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
public Bezier() {
|
public Bezier() {
|
||||||
this.order = hitObject.sliderX.length + 1;
|
this.order = hitObject.getSliderX().length + 1;
|
||||||
this.step = 5 / hitObject.pixelLength;
|
this.step = 5 / hitObject.getPixelLength();
|
||||||
|
|
||||||
// calculate curve points for drawing
|
// calculate curve points for drawing
|
||||||
int N = (int) (1 / step);
|
int N = (int) (1 / step);
|
||||||
|
@ -172,7 +172,7 @@ public class Slider {
|
||||||
curveY[N] = getY(order - 1);
|
curveY[N] = getY(order - 1);
|
||||||
|
|
||||||
// calculate angles (if needed)
|
// calculate angles (if needed)
|
||||||
if (hitObject.repeat > 1) {
|
if (hitObject.getRepeatCount() > 1) {
|
||||||
float[] c1 = pointAt(0f);
|
float[] c1 = pointAt(0f);
|
||||||
float[] c2 = pointAt(step);
|
float[] c2 = pointAt(step);
|
||||||
startAngle = (float) (Math.atan2(c2[1] - c1[1], c2[0] - c1[0]) * 180 / Math.PI);
|
startAngle = (float) (Math.atan2(c2[1] - c1[1], c2[0] - c1[0]) * 180 / Math.PI);
|
||||||
|
@ -186,14 +186,14 @@ public class Slider {
|
||||||
* Returns the x coordinate of the control point at index i.
|
* Returns the x coordinate of the control point at index i.
|
||||||
*/
|
*/
|
||||||
private float getX(int i) {
|
private float getX(int i) {
|
||||||
return (i == 0) ? hitObject.x : hitObject.sliderX[i - 1];
|
return (i == 0) ? hitObject.getX() : hitObject.getSliderX()[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the y coordinate of the control point at index i.
|
* Returns the y coordinate of the control point at index i.
|
||||||
*/
|
*/
|
||||||
private float getY(int i) {
|
private float getY(int i) {
|
||||||
return (i == 0) ? hitObject.y : hitObject.sliderY[i - 1];
|
return (i == 0) ? hitObject.getY() : hitObject.getSliderY()[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,7 +341,9 @@ public class Slider {
|
||||||
* @param currentObject true if this is the current hit object
|
* @param currentObject true if this is the current hit object
|
||||||
*/
|
*/
|
||||||
public void draw(int trackPosition, boolean currentObject) {
|
public void draw(int trackPosition, boolean currentObject) {
|
||||||
int timeDiff = hitObject.time - trackPosition;
|
float x = hitObject.getX(), y = hitObject.getY();
|
||||||
|
float[] sliderX = hitObject.getSliderX(), sliderY = hitObject.getSliderY();
|
||||||
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
|
|
||||||
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
|
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
|
||||||
Image hitCircle = GameImage.HITCIRCLE.getImage();
|
Image hitCircle = GameImage.HITCIRCLE.getImage();
|
||||||
|
@ -358,36 +360,35 @@ public class Slider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// end circle
|
// end circle
|
||||||
int lastIndex = hitObject.sliderX.length - 1;
|
int lastIndex = sliderX.length - 1;
|
||||||
Utils.drawCentered(hitCircleOverlay, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], Color.white);
|
Utils.drawCentered(hitCircleOverlay, sliderX[lastIndex], sliderY[lastIndex], Color.white);
|
||||||
Utils.drawCentered(hitCircle, hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], color);
|
Utils.drawCentered(hitCircle, sliderX[lastIndex], sliderY[lastIndex], color);
|
||||||
|
|
||||||
// start circle
|
// start circle
|
||||||
Utils.drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white);
|
Utils.drawCentered(hitCircleOverlay, x, y, Color.white);
|
||||||
Utils.drawCentered(hitCircle, hitObject.x, hitObject.y, color);
|
Utils.drawCentered(hitCircle, x, y, color);
|
||||||
if (sliderClicked)
|
if (sliderClicked)
|
||||||
; // don't draw current combo number if already clicked
|
; // don't draw current combo number if already clicked
|
||||||
else
|
else
|
||||||
score.drawSymbolNumber(hitObject.comboNumber, hitObject.x, hitObject.y,
|
score.drawSymbolNumber(hitObject.getComboNumber(), x, y,
|
||||||
hitCircle.getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
hitCircle.getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
||||||
|
|
||||||
// repeats
|
// repeats
|
||||||
if (hitObject.repeat - 1 > currentRepeats) {
|
if (hitObject.getRepeatCount() - 1 > currentRepeats) {
|
||||||
Image arrow = GameImage.REVERSEARROW.getImage();
|
Image arrow = GameImage.REVERSEARROW.getImage();
|
||||||
if (currentRepeats % 2 == 0) { // last circle
|
if (currentRepeats % 2 == 0) { // last circle
|
||||||
arrow.setRotation(bezier.getEndAngle());
|
arrow.setRotation(bezier.getEndAngle());
|
||||||
arrow.drawCentered(hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex]);
|
arrow.drawCentered(sliderX[lastIndex], sliderY[lastIndex]);
|
||||||
} else { // first circle
|
} else { // first circle
|
||||||
arrow.setRotation(bezier.getStartAngle());
|
arrow.setRotation(bezier.getStartAngle());
|
||||||
arrow.drawCentered(hitObject.x, hitObject.y);
|
arrow.drawCentered(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeDiff >= 0) {
|
if (timeDiff >= 0) {
|
||||||
// approach circle
|
// approach circle
|
||||||
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
||||||
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale),
|
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color);
|
||||||
hitObject.x, hitObject.y, color);
|
|
||||||
} else {
|
} else {
|
||||||
float[] c = bezier.pointAt(getT(trackPosition, false));
|
float[] c = bezier.pointAt(getT(trackPosition, false));
|
||||||
|
|
||||||
|
@ -407,7 +408,7 @@ public class Slider {
|
||||||
* @return the hit result (GameScore.HIT_* constants)
|
* @return the hit result (GameScore.HIT_* constants)
|
||||||
*/
|
*/
|
||||||
public int hitResult() {
|
public int hitResult() {
|
||||||
int lastIndex = hitObject.sliderX.length - 1;
|
int lastIndex = hitObject.getSliderX().length - 1;
|
||||||
float tickRatio = (float) ticksHit / tickIntervals;
|
float tickRatio = (float) ticksHit / tickIntervals;
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
|
@ -421,12 +422,12 @@ public class Slider {
|
||||||
result = GameScore.HIT_MISS;
|
result = GameScore.HIT_MISS;
|
||||||
|
|
||||||
if (currentRepeats % 2 == 0) // last circle
|
if (currentRepeats % 2 == 0) // last circle
|
||||||
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
score.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
|
||||||
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex],
|
hitObject.getSliderX()[lastIndex], hitObject.getSliderY()[lastIndex],
|
||||||
color, comboEnd, hitObject.hitSound);
|
color, comboEnd, hitObject.getHitSoundType());
|
||||||
else // first circle
|
else // first circle
|
||||||
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
score.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
|
||||||
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
hitObject.getX(), hitObject.getY(), color, comboEnd, hitObject.getHitSoundType());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -442,11 +443,11 @@ public class Slider {
|
||||||
if (sliderClicked) // first circle already processed
|
if (sliderClicked) // first circle already processed
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
double distance = Math.hypot(hitObject.x - x, hitObject.y - y);
|
double distance = Math.hypot(hitObject.getX() - x, hitObject.getY() - y);
|
||||||
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
||||||
if (distance < circleRadius) {
|
if (distance < circleRadius) {
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
int timeDiff = Math.abs(trackPosition - hitObject.time);
|
int timeDiff = Math.abs(trackPosition - hitObject.getTime());
|
||||||
int[] hitResultOffset = game.getHitResultOffsets();
|
int[] hitResultOffset = game.getHitResultOffsets();
|
||||||
|
|
||||||
int result = -1;
|
int result = -1;
|
||||||
|
@ -459,8 +460,8 @@ public class Slider {
|
||||||
|
|
||||||
if (result > -1) {
|
if (result > -1) {
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
score.sliderTickResult(hitObject.time, result,
|
score.sliderTickResult(hitObject.getTime(), result,
|
||||||
hitObject.x, hitObject.y, hitObject.hitSound);
|
hitObject.getX(), hitObject.getY(), hitObject.getHitSoundType());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,15 +477,17 @@ public class Slider {
|
||||||
* @return true if slider ended
|
* @return true if slider ended
|
||||||
*/
|
*/
|
||||||
public boolean update(boolean overlap, int delta, int mouseX, int mouseY) {
|
public boolean update(boolean overlap, int delta, int mouseX, int mouseY) {
|
||||||
|
int repeatCount = hitObject.getRepeatCount();
|
||||||
|
|
||||||
// slider time and tick calculations
|
// slider time and tick calculations
|
||||||
if (sliderTimeTotal == 0f) {
|
if (sliderTimeTotal == 0f) {
|
||||||
// slider time
|
// slider time
|
||||||
this.sliderTime = game.getBeatLength() * (hitObject.pixelLength / sliderMultiplier) / 100f;
|
this.sliderTime = game.getBeatLength() * (hitObject.getPixelLength() / sliderMultiplier) / 100f;
|
||||||
this.sliderTimeTotal = sliderTime * hitObject.repeat;
|
this.sliderTimeTotal = sliderTime * repeatCount;
|
||||||
|
|
||||||
// ticks
|
// ticks
|
||||||
float tickLengthDiv = 100f * sliderMultiplier / sliderTickRate / game.getTimingPointMultiplier();
|
float tickLengthDiv = 100f * sliderMultiplier / sliderTickRate / game.getTimingPointMultiplier();
|
||||||
int tickCount = (int) Math.ceil(hitObject.pixelLength / tickLengthDiv) - 1;
|
int tickCount = (int) Math.ceil(hitObject.getPixelLength() / tickLengthDiv) - 1;
|
||||||
if (tickCount > 0) {
|
if (tickCount > 0) {
|
||||||
this.ticksT = new float[tickCount];
|
this.ticksT = new float[tickCount];
|
||||||
float tickTOffset = 1f / (tickCount + 1);
|
float tickTOffset = 1f / (tickCount + 1);
|
||||||
|
@ -494,37 +497,40 @@ public class Slider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte hitSound = hitObject.getHitSoundType();
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
int[] hitResultOffset = game.getHitResultOffsets();
|
int[] hitResultOffset = game.getHitResultOffsets();
|
||||||
int lastIndex = hitObject.sliderX.length - 1;
|
int lastIndex = hitObject.getSliderX().length - 1;
|
||||||
boolean isAutoMod = GameMod.AUTO.isActive();
|
boolean isAutoMod = GameMod.AUTO.isActive();
|
||||||
|
|
||||||
if (!sliderClicked) {
|
if (!sliderClicked) {
|
||||||
|
int time = hitObject.getTime();
|
||||||
|
|
||||||
// start circle time passed
|
// start circle time passed
|
||||||
if (trackPosition > hitObject.time + hitResultOffset[GameScore.HIT_50]) {
|
if (trackPosition > time + hitResultOffset[GameScore.HIT_50]) {
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
if (isAutoMod) { // "auto" mod: catch any missed notes due to lag
|
if (isAutoMod) { // "auto" mod: catch any missed notes due to lag
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(time, GameScore.HIT_SLIDER30,
|
||||||
hitObject.x, hitObject.y, hitObject.hitSound);
|
hitObject.getX(), hitObject.getY(), hitSound);
|
||||||
} else
|
} else
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_MISS,
|
score.sliderTickResult(time, GameScore.HIT_MISS,
|
||||||
hitObject.x, hitObject.y, hitObject.hitSound);
|
hitObject.getX(), hitObject.getY(), hitSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "auto" mod: send a perfect hit result
|
// "auto" mod: send a perfect hit result
|
||||||
else if (isAutoMod) {
|
else if (isAutoMod) {
|
||||||
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
if (Math.abs(trackPosition - time) < hitResultOffset[GameScore.HIT_300]) {
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(time, GameScore.HIT_SLIDER30,
|
||||||
hitObject.x, hitObject.y, hitObject.hitSound);
|
hitObject.getX(), hitObject.getY(), hitSound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of slider
|
// end of slider
|
||||||
if (overlap || trackPosition > hitObject.time + sliderTimeTotal) {
|
if (overlap || trackPosition > hitObject.getTime() + sliderTimeTotal) {
|
||||||
tickIntervals++;
|
tickIntervals++;
|
||||||
|
|
||||||
// "auto" mod: send a perfect hit result
|
// "auto" mod: send a perfect hit result
|
||||||
|
@ -533,7 +539,7 @@ public class Slider {
|
||||||
|
|
||||||
// check if cursor pressed and within end circle
|
// check if cursor pressed and within end circle
|
||||||
else if (Utils.isGameKeyPressed()) {
|
else if (Utils.isGameKeyPressed()) {
|
||||||
double distance = Math.hypot(hitObject.sliderX[lastIndex] - mouseX, hitObject.sliderY[lastIndex] - mouseY);
|
double distance = Math.hypot(hitObject.getSliderX()[lastIndex] - mouseX, hitObject.getSliderY()[lastIndex] - mouseY);
|
||||||
int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
|
int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
|
||||||
if (distance < followCircleRadius)
|
if (distance < followCircleRadius)
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
|
@ -546,7 +552,7 @@ public class Slider {
|
||||||
|
|
||||||
// repeats
|
// repeats
|
||||||
boolean isNewRepeat = false;
|
boolean isNewRepeat = false;
|
||||||
if (hitObject.repeat - 1 > currentRepeats) {
|
if (repeatCount - 1 > currentRepeats) {
|
||||||
float t = getT(trackPosition, true);
|
float t = getT(trackPosition, true);
|
||||||
if (Math.floor(t) > currentRepeats) {
|
if (Math.floor(t) > currentRepeats) {
|
||||||
currentRepeats++;
|
currentRepeats++;
|
||||||
|
@ -558,8 +564,8 @@ public class Slider {
|
||||||
// ticks
|
// ticks
|
||||||
boolean isNewTick = false;
|
boolean isNewTick = false;
|
||||||
if (ticksT != null &&
|
if (ticksT != null &&
|
||||||
tickIntervals < (ticksT.length * (currentRepeats + 1)) + hitObject.repeat &&
|
tickIntervals < (ticksT.length * (currentRepeats + 1)) + repeatCount &&
|
||||||
tickIntervals < (ticksT.length * hitObject.repeat) + hitObject.repeat) {
|
tickIntervals < (ticksT.length * repeatCount) + repeatCount) {
|
||||||
float t = getT(trackPosition, true);
|
float t = getT(trackPosition, true);
|
||||||
if (t - Math.floor(t) >= ticksT[tickIndex]) {
|
if (t - Math.floor(t) >= ticksT[tickIndex]) {
|
||||||
tickIntervals++;
|
tickIntervals++;
|
||||||
|
@ -582,11 +588,10 @@ public class Slider {
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
if (currentRepeats % 2 > 0) // last circle
|
if (currentRepeats % 2 > 0) // last circle
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
||||||
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex],
|
hitObject.getSliderX()[lastIndex], hitObject.getSliderY()[lastIndex], hitSound);
|
||||||
hitObject.hitSound);
|
|
||||||
else // first circle
|
else // first circle
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
||||||
c[0], c[1], hitObject.hitSound);
|
c[0], c[1], hitSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// held during new tick
|
// held during new tick
|
||||||
|
@ -599,7 +604,7 @@ public class Slider {
|
||||||
followCircleActive = false;
|
followCircleActive = false;
|
||||||
|
|
||||||
if (isNewRepeat)
|
if (isNewRepeat)
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, hitObject.hitSound);
|
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, hitSound);
|
||||||
if (isNewTick)
|
if (isNewTick)
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, (byte) -1);
|
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, (byte) -1);
|
||||||
}
|
}
|
||||||
|
@ -614,7 +619,7 @@ public class Slider {
|
||||||
* @return the t value: raw [0, repeats] or looped [0, 1]
|
* @return the t value: raw [0, repeats] or looped [0, 1]
|
||||||
*/
|
*/
|
||||||
public float getT(int trackPosition, boolean raw) {
|
public float getT(int trackPosition, boolean raw) {
|
||||||
float t = (trackPosition - hitObject.time) / sliderTime;
|
float t = (trackPosition - hitObject.getTime()) / sliderTime;
|
||||||
if (raw)
|
if (raw)
|
||||||
return t;
|
return t;
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -78,7 +78,8 @@ public class Spinner {
|
||||||
|
|
||||||
Image spinnerCircle = GameImage.SPINNER_CIRCLE.getImage();
|
Image spinnerCircle = GameImage.SPINNER_CIRCLE.getImage();
|
||||||
GameImage.SPINNER_CIRCLE.setImage(spinnerCircle.getScaledCopy(height * 9 / 10, height * 9 / 10));
|
GameImage.SPINNER_CIRCLE.setImage(spinnerCircle.getScaledCopy(height * 9 / 10, height * 9 / 10));
|
||||||
GameImage.SPINNER_APPROACHCIRCLE.setImage(GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(spinnerCircle.getWidth(), spinnerCircle.getHeight()));
|
GameImage.SPINNER_APPROACHCIRCLE.setImage(GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(
|
||||||
|
spinnerCircle.getWidth(), spinnerCircle.getHeight()));
|
||||||
GameImage.SPINNER_METRE.setImage(GameImage.SPINNER_METRE.getImage().getScaledCopy(width, height));
|
GameImage.SPINNER_METRE.setImage(GameImage.SPINNER_METRE.getImage().getScaledCopy(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ public class Spinner {
|
||||||
|
|
||||||
// calculate rotations needed
|
// calculate rotations needed
|
||||||
float spinsPerMinute = 100 + (score.getDifficulty() * 15);
|
float spinsPerMinute = 100 + (score.getDifficulty() * 15);
|
||||||
rotationsNeeded = spinsPerMinute * (hitObject.endTime - hitObject.time) / 60000f;
|
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +104,7 @@ public class Spinner {
|
||||||
* @param g the graphics context
|
* @param g the graphics context
|
||||||
*/
|
*/
|
||||||
public void draw(int trackPosition, Graphics g) {
|
public void draw(int trackPosition, Graphics g) {
|
||||||
int timeDiff = hitObject.time - trackPosition;
|
int timeDiff = hitObject.getTime() - trackPosition;
|
||||||
boolean spinnerComplete = (rotations >= rotationsNeeded);
|
boolean spinnerComplete = (rotations >= rotationsNeeded);
|
||||||
|
|
||||||
// TODO: draw "OSU!" image after spinner ends
|
// TODO: draw "OSU!" image after spinner ends
|
||||||
|
@ -126,8 +127,9 @@ public class Spinner {
|
||||||
spinnerMetreSub.draw(0, height - spinnerMetreSub.getHeight());
|
spinnerMetreSub.draw(0, height - spinnerMetreSub.getHeight());
|
||||||
|
|
||||||
// main spinner elements
|
// main spinner elements
|
||||||
|
float approachScale = 1 - ((float) timeDiff / (hitObject.getTime() - hitObject.getEndTime()));
|
||||||
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2);
|
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2);
|
||||||
GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(1 - ((float) timeDiff / (hitObject.time - hitObject.endTime))).drawCentered(width / 2, height / 2);
|
GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(width / 2, height / 2);
|
||||||
GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4);
|
GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4);
|
||||||
|
|
||||||
if (spinnerComplete) {
|
if (spinnerComplete) {
|
||||||
|
@ -158,7 +160,7 @@ public class Spinner {
|
||||||
else
|
else
|
||||||
result = GameScore.HIT_MISS;
|
result = GameScore.HIT_MISS;
|
||||||
|
|
||||||
score.hitResult(hitObject.endTime, result, width / 2, height / 2,
|
score.hitResult(hitObject.getEndTime(), result, width / 2, height / 2,
|
||||||
Color.transparent, true, (byte) -1);
|
Color.transparent, true, (byte) -1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -177,7 +179,7 @@ public class Spinner {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// end of spinner
|
// end of spinner
|
||||||
if (trackPosition > hitObject.endTime) {
|
if (trackPosition > hitObject.getEndTime()) {
|
||||||
hitResult();
|
hitResult();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,8 +316,8 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// skip beginning
|
// skip beginning
|
||||||
if (objectIndex == 0 &&
|
if (objectIndex == 0 &&
|
||||||
osu.objects[0].time - SKIP_OFFSET > 5000 &&
|
osu.objects[0].getTime() - SKIP_OFFSET > 5000 &&
|
||||||
trackPosition < osu.objects[0].time - SKIP_OFFSET)
|
trackPosition < osu.objects[0].getTime() - SKIP_OFFSET)
|
||||||
skipButton.draw();
|
skipButton.draw();
|
||||||
|
|
||||||
if (isLeadIn())
|
if (isLeadIn())
|
||||||
|
@ -325,7 +325,7 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// countdown
|
// countdown
|
||||||
if (osu.countdown > 0) { // TODO: implement half/double rate settings
|
if (osu.countdown > 0) { // TODO: implement half/double rate settings
|
||||||
int timeDiff = osu.objects[0].time - trackPosition;
|
int timeDiff = osu.objects[0].getTime() - trackPosition;
|
||||||
if (timeDiff >= 500 && timeDiff < 3000) {
|
if (timeDiff >= 500 && timeDiff < 3000) {
|
||||||
if (timeDiff >= 1500) {
|
if (timeDiff >= 1500) {
|
||||||
GameImage.COUNTDOWN_READY.getImage().drawCentered(width / 2, height / 2);
|
GameImage.COUNTDOWN_READY.getImage().drawCentered(width / 2, height / 2);
|
||||||
|
@ -368,18 +368,18 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// draw hit objects in reverse order, or else overlapping objects are unreadable
|
// draw hit objects in reverse order, or else overlapping objects are unreadable
|
||||||
Stack<Integer> stack = new Stack<Integer>();
|
Stack<Integer> stack = new Stack<Integer>();
|
||||||
for (int i = objectIndex; i < osu.objects.length && osu.objects[i].time < trackPosition + approachTime; i++)
|
for (int i = objectIndex; i < osu.objects.length && osu.objects[i].getTime() < trackPosition + approachTime; i++)
|
||||||
stack.add(i);
|
stack.add(i);
|
||||||
|
|
||||||
while (!stack.isEmpty()) {
|
while (!stack.isEmpty()) {
|
||||||
int i = stack.pop();
|
int i = stack.pop();
|
||||||
OsuHitObject hitObject = osu.objects[i];
|
OsuHitObject hitObject = osu.objects[i];
|
||||||
|
|
||||||
if ((hitObject.type & OsuHitObject.TYPE_CIRCLE) > 0)
|
if (hitObject.isCircle())
|
||||||
circles.get(i).draw(trackPosition);
|
circles.get(i).draw(trackPosition);
|
||||||
else if ((hitObject.type & OsuHitObject.TYPE_SLIDER) > 0)
|
else if (hitObject.isSlider())
|
||||||
sliders.get(i).draw(trackPosition, stack.isEmpty());
|
sliders.get(i).draw(trackPosition, stack.isEmpty());
|
||||||
else if ((hitObject.type & OsuHitObject.TYPE_SPINNER) > 0) {
|
else if (hitObject.isSpinner()) {
|
||||||
if (stack.isEmpty()) // only draw spinner at objectIndex
|
if (stack.isEmpty()) // only draw spinner at objectIndex
|
||||||
spinners.get(i).draw(trackPosition, g);
|
spinners.get(i).draw(trackPosition, g);
|
||||||
else
|
else
|
||||||
|
@ -487,7 +487,7 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// song beginning
|
// song beginning
|
||||||
if (objectIndex == 0) {
|
if (objectIndex == 0) {
|
||||||
if (trackPosition < osu.objects[0].time)
|
if (trackPosition < osu.objects[0].getTime())
|
||||||
return; // nothing to do here
|
return; // nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,20 +544,20 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update objects (loop in unlikely event of any skipped indexes)
|
// update objects (loop in unlikely event of any skipped indexes)
|
||||||
while (objectIndex < osu.objects.length && trackPosition > osu.objects[objectIndex].time) {
|
while (objectIndex < osu.objects.length && trackPosition > osu.objects[objectIndex].getTime()) {
|
||||||
OsuHitObject hitObject = osu.objects[objectIndex];
|
OsuHitObject hitObject = osu.objects[objectIndex];
|
||||||
|
|
||||||
// check if we've already passed the next object's start time
|
// check if we've already passed the next object's start time
|
||||||
boolean overlap = (objectIndex + 1 < osu.objects.length &&
|
boolean overlap = (objectIndex + 1 < osu.objects.length &&
|
||||||
trackPosition > osu.objects[objectIndex + 1].time - hitResultOffset[GameScore.HIT_300]);
|
trackPosition > osu.objects[objectIndex + 1].getTime() - hitResultOffset[GameScore.HIT_300]);
|
||||||
|
|
||||||
// check completion status of the hit object
|
// check completion status of the hit object
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
if ((hitObject.type & OsuHitObject.TYPE_CIRCLE) > 0)
|
if (hitObject.isCircle())
|
||||||
done = circles.get(objectIndex).update(overlap);
|
done = circles.get(objectIndex).update(overlap);
|
||||||
else if ((hitObject.type & OsuHitObject.TYPE_SLIDER) > 0)
|
else if (hitObject.isSlider())
|
||||||
done = sliders.get(objectIndex).update(overlap, delta, input.getMouseX(), input.getMouseY());
|
done = sliders.get(objectIndex).update(overlap, delta, input.getMouseX(), input.getMouseY());
|
||||||
else if ((hitObject.type & OsuHitObject.TYPE_SPINNER) > 0)
|
else if (hitObject.isSpinner())
|
||||||
done = spinners.get(objectIndex).update(overlap, delta, input.getMouseX(), input.getMouseY());
|
done = spinners.get(objectIndex).update(overlap, delta, input.getMouseX(), input.getMouseY());
|
||||||
|
|
||||||
// increment object index?
|
// increment object index?
|
||||||
|
@ -586,7 +586,7 @@ public class Game extends BasicGameState {
|
||||||
// pause game
|
// pause game
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
if (pauseTime < 0 && breakTime <= 0 &&
|
if (pauseTime < 0 && breakTime <= 0 &&
|
||||||
trackPosition >= osu.objects[0].time &&
|
trackPosition >= osu.objects[0].getTime() &&
|
||||||
!GameMod.AUTO.isActive()) {
|
!GameMod.AUTO.isActive()) {
|
||||||
pausedMouseX = input.getMouseX();
|
pausedMouseX = input.getMouseX();
|
||||||
pausedMouseY = input.getMouseY();
|
pausedMouseY = input.getMouseY();
|
||||||
|
@ -642,7 +642,7 @@ public class Game extends BasicGameState {
|
||||||
// skip to checkpoint
|
// skip to checkpoint
|
||||||
MusicController.setPosition(checkpoint);
|
MusicController.setPosition(checkpoint);
|
||||||
while (objectIndex < osu.objects.length &&
|
while (objectIndex < osu.objects.length &&
|
||||||
osu.objects[objectIndex++].time <= MusicController.getPosition())
|
osu.objects[objectIndex++].getTime() <= MusicController.getPosition())
|
||||||
;
|
;
|
||||||
objectIndex--;
|
objectIndex--;
|
||||||
} catch (SlickException e) {
|
} catch (SlickException e) {
|
||||||
|
@ -692,14 +692,14 @@ public class Game extends BasicGameState {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// circles
|
// circles
|
||||||
if ((hitObject.type & OsuHitObject.TYPE_CIRCLE) > 0) {
|
if (hitObject.isCircle()) {
|
||||||
boolean hit = circles.get(objectIndex).mousePressed(x, y);
|
boolean hit = circles.get(objectIndex).mousePressed(x, y);
|
||||||
if (hit)
|
if (hit)
|
||||||
objectIndex++;
|
objectIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sliders
|
// sliders
|
||||||
else if ((hitObject.type & OsuHitObject.TYPE_SLIDER) > 0)
|
else if (hitObject.isSlider())
|
||||||
sliders.get(objectIndex).mousePressed(x, y);
|
sliders.get(objectIndex).mousePressed(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,15 +733,15 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// is this the last note in the combo?
|
// is this the last note in the combo?
|
||||||
boolean comboEnd = false;
|
boolean comboEnd = false;
|
||||||
if (i + 1 < osu.objects.length &&
|
if (i + 1 < osu.objects.length && osu.objects[i + 1].isNewCombo())
|
||||||
(osu.objects[i + 1].type & OsuHitObject.TYPE_NEWCOMBO) > 0)
|
|
||||||
comboEnd = true;
|
comboEnd = true;
|
||||||
|
|
||||||
if ((hitObject.type & OsuHitObject.TYPE_CIRCLE) > 0) {
|
Color color = osu.combo[hitObject.getComboIndex()];
|
||||||
circles.put(i, new Circle(hitObject, this, score, osu.combo[hitObject.comboIndex], comboEnd));
|
if (hitObject.isCircle()) {
|
||||||
} else if ((hitObject.type & OsuHitObject.TYPE_SLIDER) > 0) {
|
circles.put(i, new Circle(hitObject, this, score, color, comboEnd));
|
||||||
sliders.put(i, new Slider(hitObject, this, score, osu.combo[hitObject.comboIndex], comboEnd));
|
} else if (hitObject.isSlider()) {
|
||||||
} else if ((hitObject.type & OsuHitObject.TYPE_SPINNER) > 0) {
|
sliders.put(i, new Slider(hitObject, this, score, color, comboEnd));
|
||||||
|
} else if (hitObject.isSpinner()) {
|
||||||
spinners.put(i, new Spinner(hitObject, this, score));
|
spinners.put(i, new Spinner(hitObject, this, score));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,15 +794,16 @@ public class Game extends BasicGameState {
|
||||||
* @return true if skipped, false otherwise
|
* @return true if skipped, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean skipIntro() {
|
private boolean skipIntro() {
|
||||||
|
int firstObjectTime = osu.objects[0].getTime();
|
||||||
int trackPosition = MusicController.getPosition();
|
int trackPosition = MusicController.getPosition();
|
||||||
if (objectIndex == 0 &&
|
if (objectIndex == 0 &&
|
||||||
osu.objects[0].time - SKIP_OFFSET > 4000 &&
|
firstObjectTime - SKIP_OFFSET > 4000 &&
|
||||||
trackPosition < osu.objects[0].time - SKIP_OFFSET) {
|
trackPosition < firstObjectTime - SKIP_OFFSET) {
|
||||||
if (isLeadIn()) {
|
if (isLeadIn()) {
|
||||||
leadInTime = 0;
|
leadInTime = 0;
|
||||||
MusicController.resume();
|
MusicController.resume();
|
||||||
}
|
}
|
||||||
MusicController.setPosition(osu.objects[0].time - SKIP_OFFSET);
|
MusicController.setPosition(firstObjectTime - SKIP_OFFSET);
|
||||||
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -883,16 +884,6 @@ public class Game extends BasicGameState {
|
||||||
float overallDifficulty = osu.overallDifficulty;
|
float overallDifficulty = osu.overallDifficulty;
|
||||||
float HPDrainRate = osu.HPDrainRate;
|
float HPDrainRate = osu.HPDrainRate;
|
||||||
|
|
||||||
// fixed difficulty overrides
|
|
||||||
if (Options.getFixedCS() > 0f)
|
|
||||||
circleSize = Options.getFixedCS();
|
|
||||||
if (Options.getFixedAR() > 0f)
|
|
||||||
approachRate = Options.getFixedAR();
|
|
||||||
if (Options.getFixedOD() > 0f)
|
|
||||||
overallDifficulty = Options.getFixedOD();
|
|
||||||
if (Options.getFixedHP() > 0f)
|
|
||||||
HPDrainRate = Options.getFixedHP();
|
|
||||||
|
|
||||||
// "Hard Rock" modifiers
|
// "Hard Rock" modifiers
|
||||||
if (GameMod.HARD_ROCK.isActive()) {
|
if (GameMod.HARD_ROCK.isActive()) {
|
||||||
circleSize = Math.min(circleSize * 1.4f, 10);
|
circleSize = Math.min(circleSize * 1.4f, 10);
|
||||||
|
@ -909,6 +900,16 @@ public class Game extends BasicGameState {
|
||||||
HPDrainRate /= 2f;
|
HPDrainRate /= 2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixed difficulty overrides
|
||||||
|
if (Options.getFixedCS() > 0f)
|
||||||
|
circleSize = Options.getFixedCS();
|
||||||
|
if (Options.getFixedAR() > 0f)
|
||||||
|
approachRate = Options.getFixedAR();
|
||||||
|
if (Options.getFixedOD() > 0f)
|
||||||
|
overallDifficulty = Options.getFixedOD();
|
||||||
|
if (Options.getFixedHP() > 0f)
|
||||||
|
HPDrainRate = Options.getFixedHP();
|
||||||
|
|
||||||
// initialize objects
|
// initialize objects
|
||||||
Circle.init(container, circleSize);
|
Circle.init(container, circleSize);
|
||||||
Slider.init(container, circleSize, osu);
|
Slider.init(container, circleSize, osu);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user