Implement hit object stacking algorithm.
This commit is contained in:
@@ -80,12 +80,18 @@ public class Game extends BasicGameState {
|
||||
/** Replay. */
|
||||
REPLAY,
|
||||
/** Health is zero: no-continue/force restart. */
|
||||
LOSE;
|
||||
LOSE
|
||||
}
|
||||
|
||||
/** Minimum time before start of song, in milliseconds, to process skip-related actions. */
|
||||
private static final int SKIP_OFFSET = 2000;
|
||||
|
||||
/** Tolerance in case if hit object is not snapped to the grid */
|
||||
private static final float STACK_LENIENCE = 3f;
|
||||
|
||||
/** Stack time window of the previous object in ms. */
|
||||
private static final int STACK_TIMEOUT = 1000;
|
||||
|
||||
/** The associated OsuFile object. */
|
||||
private OsuFile osu;
|
||||
|
||||
@@ -1027,6 +1033,21 @@ public class Game extends BasicGameState {
|
||||
|
||||
Color color = osu.combo[hitObject.getComboIndex()];
|
||||
|
||||
// pass beatLength to hit objects
|
||||
int hitObjectTime = hitObject.getTime();
|
||||
int timingPointIndex = 0;
|
||||
while (timingPointIndex < osu.timingPoints.size()) {
|
||||
OsuTimingPoint timingPoint = osu.timingPoints.get(timingPointIndex);
|
||||
if (timingPoint.getTime() <= hitObjectTime) {
|
||||
if (!timingPoint.isInherited())
|
||||
beatLengthBase = beatLength = timingPoint.getBeatLength();
|
||||
else
|
||||
beatLength = beatLengthBase * timingPoint.getSliderMultiplier();
|
||||
} else
|
||||
break;
|
||||
timingPointIndex++;
|
||||
}
|
||||
|
||||
try {
|
||||
if (hitObject.isCircle())
|
||||
hitObjects[i] = new Circle(hitObject, this, data, color, comboEnd);
|
||||
@@ -1043,6 +1064,72 @@ public class Game extends BasicGameState {
|
||||
}
|
||||
}
|
||||
|
||||
// stack calculation
|
||||
for (int i = hitObjects.length - 1; i > 0; i--) {
|
||||
OsuHitObject hitObjectI = hitObjects[i].getHitObject();
|
||||
|
||||
// already calculated
|
||||
if (hitObjectI.getStack() != 0 || hitObjectI.isSpinner())
|
||||
continue;
|
||||
|
||||
// search for hit objects in stack
|
||||
for (int n = i -1; n >= 0; n--) {
|
||||
OsuHitObject hitObjectN = hitObjects[n].getHitObject();
|
||||
|
||||
if (hitObjectN.isSpinner())
|
||||
continue;
|
||||
|
||||
// check if in range stack calculation
|
||||
float timeI = hitObjectI.getTime() - (STACK_TIMEOUT * osu.stackLeniency);
|
||||
float timeN = hitObjectN.isSlider() ? hitObjects[n].getEndTime() : hitObjectN.getTime();
|
||||
if (timeI > timeN)
|
||||
break;
|
||||
|
||||
if (hitObjectN.isSlider()) {
|
||||
// possible special case. if slider end in the stack, all next hit objects in stack moves right down
|
||||
Slider slider = (Slider) hitObjects[n];
|
||||
float x1 = hitObjects[i].getPointAt(hitObjectI.getTime())[0];
|
||||
float y1 = hitObjects[i].getPointAt(hitObjectI.getTime())[1];
|
||||
float x2 = slider.getPointAt(slider.getEndTime())[0];
|
||||
float y2 = slider.getPointAt(slider.getEndTime())[1];
|
||||
float distance = Utils.distance(x1, y1, x2, y2);
|
||||
|
||||
// check if hit object part of this stack
|
||||
if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier()) {
|
||||
int offset = hitObjectI.getStack() - hitObjectN.getStack() + 1;
|
||||
for (int j = n + 1; j <= i; j++) {
|
||||
OsuHitObject hitObjectJ = hitObjects[j].getHitObject();
|
||||
x1 = hitObjects[j].getPointAt(hitObjectJ.getTime())[0];
|
||||
y1 = hitObjects[j].getPointAt(hitObjectJ.getTime())[1];
|
||||
distance = Utils.distance(x1, y1, x2, y2);
|
||||
|
||||
if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier()) {
|
||||
// hit object below slider end
|
||||
hitObjectJ.setStack(hitObjectJ.getStack() - offset);
|
||||
}
|
||||
}
|
||||
// slider end always start of the stack. reset calculation
|
||||
break;
|
||||
}
|
||||
}
|
||||
// no special case. stack moves up left
|
||||
float x1 = hitObjectI.getX();
|
||||
float y1 = hitObjectI.getY();
|
||||
float x2 = hitObjectN.getX();
|
||||
float y2 = hitObjectN.getY();
|
||||
float distance = Utils.distance(x1, y1, x2, y2);
|
||||
if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier()) {
|
||||
hitObjectN.setStack(hitObjectI.getStack() + 1);
|
||||
hitObjectI = hitObjectN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update hit objects positions
|
||||
for (int i = 0; i < hitObjects.length; i++) {
|
||||
hitObjects[i].updatePosition();
|
||||
}
|
||||
|
||||
// load the first timingPoint
|
||||
if (!osu.timingPoints.isEmpty()) {
|
||||
OsuTimingPoint timingPoint = osu.timingPoints.get(0);
|
||||
@@ -1331,6 +1418,10 @@ public class Game extends BasicGameState {
|
||||
if (Options.getFixedHP() > 0f)
|
||||
HPDrainRate = Options.getFixedHP();
|
||||
|
||||
// Stack modifier scales with hit object size
|
||||
int diameter = (int) (104 - (circleSize * 8));
|
||||
OsuHitObject.setStackOffset(diameter * 0.05f);
|
||||
|
||||
// initialize objects
|
||||
Circle.init(container, circleSize);
|
||||
Slider.init(container, circleSize, osu);
|
||||
|
||||
Reference in New Issue
Block a user