diff --git a/src/itdelatrisu/opsu/OsuHitObject.java b/src/itdelatrisu/opsu/OsuHitObject.java index 3ada129b..008fee07 100644 --- a/src/itdelatrisu/opsu/OsuHitObject.java +++ b/src/itdelatrisu/opsu/OsuHitObject.java @@ -72,27 +72,24 @@ public class OsuHitObject { /** The container height. */ private static int containerHeight; + /** The offset per stack. */ + private static float stackOffset; + /** - * Return stack position modifier in pixels. - * @return stack position modifier. + * Returns the stack position modifier, in pixels. + * @return stack position modifier */ public static float getStackOffset() { return stackOffset; } /** - * Sets stack position modifier in pixels - * @param offset position modifier. + * Sets the stack position modifier. + * @param offset stack position modifier, in pixels */ public static void setStackOffset(float offset) { stackOffset = offset; } - /** The offset per stack. */ - private static float stackOffset; - /** Starting coordinates. */ private float x, y; - /** Hit object index in current stack. */ - private int stack; - /** Start time (in ms). */ private int time; @@ -132,6 +129,9 @@ public class OsuHitObject { /** Number to display in hit object. */ private int comboNumber; + /** Hit object index in the current stack. */ + private int stack; + /** * Initializes the OsuHitObject data type with container dimensions. * @param width the container width @@ -279,18 +279,6 @@ public class OsuHitObject { return (y - stack * stackOffset) * yMultiplier + yOffset; } - /** - * Sets the hit object index in current stack. - * @param stack index in stack - */ - public void setStack(int stack) { this.stack = stack; } - - /** - * Returns hit object index in current stack. - * @return index in stack - */ - public int getStack() { return stack; } - /** * Returns the start time. * @return the start time (in ms) @@ -481,6 +469,18 @@ public class OsuHitObject { return 0; } + /** + * Sets the hit object index in the current stack. + * @param stack index in the stack + */ + public void setStack(int stack) { this.stack = stack; } + + /** + * Returns the hit object index in the current stack. + * @return index in the stack + */ + public int getStack() { return stack; } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index a16d1311..61baf19a 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -251,12 +251,12 @@ public class Utils { } /** - * - * @param x1 The x-component of the first point - * @param y1 The y-component of the first point - * @param x2 The x-component of the second point - * @param y2 The y-component of the second point - * @return Euclidean distance between points (x1,y1) and (x2,y2) + * Returns the distance between two points. + * @param x1 the x-component of the first point + * @param y1 the y-component of the first point + * @param x2 the x-component of the second point + * @param y2 the y-component of the second point + * @return the Euclidean distance between points (x1,y1) and (x2,y2) */ public static float distance(float x1, float y1, float x2, float y2) { float v1 = Math.abs(x1 - x2); diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index f783253e..3c41af89 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -74,12 +74,11 @@ public class Circle implements HitObject { */ public Circle(OsuHitObject hitObject, Game game, GameData data, Color color, boolean comboEnd) { this.hitObject = hitObject; - this.x = hitObject.getScaledX(); - this.y = hitObject.getScaledY(); this.game = game; this.data = data; this.color = color; this.comboEnd = comboEnd; + updatePosition(); } @Override diff --git a/src/itdelatrisu/opsu/objects/DummyObject.java b/src/itdelatrisu/opsu/objects/DummyObject.java index 4934e199..321d7dda 100644 --- a/src/itdelatrisu/opsu/objects/DummyObject.java +++ b/src/itdelatrisu/opsu/objects/DummyObject.java @@ -38,8 +38,7 @@ public class DummyObject implements HitObject { */ public DummyObject(OsuHitObject hitObject) { this.hitObject = hitObject; - this.x = hitObject.getScaledX(); - this.y = hitObject.getScaledY(); + updatePosition(); } @Override @@ -53,15 +52,15 @@ public class DummyObject implements HitObject { @Override public boolean mousePressed(int x, int y, int trackPosition) { return false; } - @Override - public void updatePosition() { - this.x = hitObject.getScaledX(); - this.y = hitObject.getScaledY(); - } - @Override public float[] getPointAt(int trackPosition) { return new float[] { x, y }; } @Override public int getEndTime() { return hitObject.getTime(); } + + @Override + public void updatePosition() { + this.x = hitObject.getScaledX(); + this.y = hitObject.getScaledY(); + } } diff --git a/src/itdelatrisu/opsu/objects/HitObject.java b/src/itdelatrisu/opsu/objects/HitObject.java index ee5db7c6..b87247be 100644 --- a/src/itdelatrisu/opsu/objects/HitObject.java +++ b/src/itdelatrisu/opsu/objects/HitObject.java @@ -18,7 +18,6 @@ package itdelatrisu.opsu.objects; -import itdelatrisu.opsu.OsuHitObject; import org.newdawn.slick.Graphics; /** @@ -67,7 +66,7 @@ public interface HitObject { public int getEndTime(); /** - * Updates position of hit object. + * Updates the position of the hit object. */ public void updatePosition(); } diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index edc83749..a4c6f0be 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -135,17 +135,11 @@ public class Slider implements HitObject { */ public Slider(OsuHitObject hitObject, Game game, GameData data, Color color, boolean comboEnd) { this.hitObject = hitObject; - this.x = hitObject.getScaledX(); - this.y = hitObject.getScaledY(); this.game = game; this.data = data; this.color = color; this.comboEnd = comboEnd; - - if (hitObject.getSliderType() == OsuHitObject.SLIDER_PASSTHROUGH && hitObject.getSliderX().length == 2) - this.curve = new CircumscribedCircle(hitObject, color); - else - this.curve = new LinearBezier(hitObject, color); + updatePosition(); // slider time calculations this.sliderTime = game.getBeatLength() * (hitObject.getPixelLength() / sliderMultiplier) / 100f; diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index ed3a4836..00e247a6 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -86,10 +86,10 @@ public class Game extends BasicGameState { /** 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 */ + /** 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. */ + /** Stack time window of the previous object, in ms. */ private static final int STACK_TIMEOUT = 1000; /** Stack position offset modifier. */ @@ -1044,10 +1044,9 @@ public class Game extends BasicGameState { int timingPointIndex = 0; while (timingPointIndex < osu.timingPoints.size()) { OsuTimingPoint timingPoint = osu.timingPoints.get(timingPointIndex); - if (timingPoint.getTime() <= hitObjectTime) { - setBeatLength(timingPoint); - } else + if (timingPoint.getTime() > hitObjectTime) break; + setBeatLength(timingPoint); timingPointIndex++; } @@ -1067,72 +1066,8 @@ public class Game extends BasicGameState { } } - // stack calculation - // more info: https://gist.github.com/peppy/1167470 - // @author peppy - for (int i = hitObjects.length - 1; i > 0; i--) { - OsuHitObject hitObjectI = osu.objects[i]; - - // 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 = osu.objects[n]; - - 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 = osu.objects[j]; - 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; - } - } - // not a special case. stack moves up left - float distance = Utils.distance(hitObjectI.getX(), hitObjectI.getY(), - hitObjectN.getX(), hitObjectN.getY()); - if (distance < STACK_LENIENCE) { - hitObjectN.setStack(hitObjectI.getStack() + 1); - hitObjectI = hitObjectN; - } - } - } - - // update hit objects positions - for (int i = 0; i < hitObjects.length; i++) { - if(osu.objects[i].getStack() != 0) { - hitObjects[i].updatePosition(); - } - } + // stack calculations + calculateStacks(); // load the first timingPoint if (!osu.timingPoints.isEmpty()) { @@ -1670,4 +1605,77 @@ public class Game extends BasicGameState { } } } + + /** + * Performs stacking calculations on all hit objects, and updates their + * positions if necessary. + * https://gist.github.com/peppy/1167470 + * @author peppy + */ + private void calculateStacks() { + // reverse pass for stack calculation + for (int i = hitObjects.length - 1; i > 0; i--) { + OsuHitObject hitObjectI = osu.objects[i]; + + // 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 = osu.objects[n]; + 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; + + // possible special case: if slider end in the stack, + // all next hit objects in stack move right down + if (hitObjectN.isSlider()) { + 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 = osu.objects[j]; + x1 = hitObjects[j].getPointAt(hitObjectJ.getTime())[0]; + y1 = hitObjects[j].getPointAt(hitObjectJ.getTime())[1]; + distance = Utils.distance(x1, y1, x2, y2); + + // hit object below slider end + if (distance < STACK_LENIENCE * OsuHitObject.getXMultiplier()) + hitObjectJ.setStack(hitObjectJ.getStack() - offset); + } + break; // slider end always start of the stack: reset calculation + } + } + + // not a special case: stack moves up left + float distance = Utils.distance( + hitObjectI.getX(), hitObjectI.getY(), + hitObjectN.getX(), hitObjectN.getY() + ); + if (distance < STACK_LENIENCE) { + hitObjectN.setStack(hitObjectI.getStack() + 1); + hitObjectI = hitObjectN; + } + } + } + + // update hit object positions + for (int i = 0; i < hitObjects.length; i++) { + if (osu.objects[i].getStack() != 0) + hitObjects[i].updatePosition(); + } + } }