diff --git a/src/awlex/ospu/FakeGameObject.java b/src/awlex/ospu/FakeGameObject.java new file mode 100644 index 00000000..cfb701ee --- /dev/null +++ b/src/awlex/ospu/FakeGameObject.java @@ -0,0 +1,104 @@ +package awlex.ospu; + +/** + * Created by Awlex on 10.10.2016. + */ + +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.objects.GameObject; +import itdelatrisu.opsu.objects.curves.Vec2f; +import org.newdawn.slick.Color; +import org.newdawn.slick.Graphics; + +/** + * This class is just a dummy {@link GameObject} to place in the middle of 2 GameObjects. + * Might as well ignore it + */ +public class FakeGameObject extends GameObject { + + //Time between the 2 constructor objects + private int halfTime; + + public FakeGameObject() { + this.start = new Vec2f(); + this.end = new Vec2f(); + this.start.x = this.end.x = Options.width / 2; + this.start.y = this.end.y = Options.height / 2; + } + + public FakeGameObject(GameObject start, GameObject end) { + halfTime = start.getEndTime() + (end.getTime() - start.getEndTime()) / 2; + this.start = new Vec2f(); + this.end = new Vec2f(); + this.start.x = this.end.x = (start.end.x + end.start.x) / 2; + this.start.y = this.end.y = (start.end.y + end.start.y) / 2; + } + + @Override + public void draw(Graphics g, int trackPosition, boolean mirrored) { + + } + + @Override + public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) { + return false; + } + + @Override + public boolean mousePressed(int x, int y, int trackPosition) { + return false; + } + + @Override + public Vec2f getPointAt(int trackPosition) { + return null; + } + + @Override + public int getEndTime() { + return halfTime; + } + + @Override + public int getTime() { + return halfTime; + } + + @Override + public void updatePosition() { + + } + + @Override + public void reset() { + } + + @Override + public boolean isCircle() { + return false; + } + + @Override + public boolean isSlider() { + return false; + } + + @Override + public boolean isSpinner() { + return false; + } + + @Override + public Color getColor() { + return null; + } + + @Override + public Color getMirroredColor() { + return null; + } + + public void setTime(int time) { + this.halfTime = time; + } +} \ No newline at end of file diff --git a/src/awlex/ospu/movers/CentralSpiralMover.java b/src/awlex/ospu/movers/CentralSpiralMover.java new file mode 100644 index 00000000..927c524d --- /dev/null +++ b/src/awlex/ospu/movers/CentralSpiralMover.java @@ -0,0 +1,46 @@ +package awlex.ospu.movers; + +import itdelatrisu.opsu.Utils; +import itdelatrisu.opsu.objects.GameObject; +import yugecin.opsudance.movers.Mover; + +/** + * Created by Alex Wieser on 09.10.2016. + * WHO DO YOU THINK I AM? + * + * This {@link Mover} starts the spiral from the start object + */ +public class CentralSpiralMover extends Mover { + + /** + * How many times the cursor goes around the center + * For satisfying results this musn't be a multiple of 2 + */ + private final int CIRCLENAVIGATIONS = 1; + + double startAng; + double startRad; + + public CentralSpiralMover(GameObject start, GameObject end, int dir) { + super(start, end, dir); + startAng = Math.atan2(endY - startY, endX - startX); + startRad = Utils.distance(startX, startY, endX, endY); + if (startRad < 20) + startRad = 20 + startRad / 20d; + } + + @Override + public double[] getPointAt(int time) { + double rad = startRad * getT(time); + double ang = CIRCLENAVIGATIONS * (startAng + 2d * Math.PI * getT(time) * dir); + return new double[]{ + startX + rad * Math.cos(ang), + startY + rad * Math.sin(ang) + }; + } + + @Override + public String getName() { + return "Spiral2"; + } +} diff --git a/src/awlex/ospu/movers/CombinedSpiralMover.java b/src/awlex/ospu/movers/CombinedSpiralMover.java new file mode 100644 index 00000000..55aa9db0 --- /dev/null +++ b/src/awlex/ospu/movers/CombinedSpiralMover.java @@ -0,0 +1,135 @@ +package awlex.ospu.movers; + +import awlex.ospu.FakeGameObject; +import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Utils; +import itdelatrisu.opsu.objects.GameObject; +import yugecin.opsudance.movers.Mover; +import yugecin.opsudance.movers.factories.AutoMoverFactory; + +/** + * Created by Alex Wieser on 09.10.2016. + * WHO DO YOU THINK I AM? + *
+ * This {@link Mover} exists for the sake of staying inbounds and is rarely instantiated.
+ * It creates 1 {@link FakeGameObject} between the 2 passed objects and then creates
+ * 2 inner {@link Mover}s around it. This class works recursively and might lead to
+ * high ram usage on very jumpy maps or even a StackOverFlow.
+ */
+public class CombinedSpiralMover extends Mover {
+
+ private Mover[] movers;
+ private GameObject fakeObject;
+ private int halfTime;
+ private int startTime;
+ private int endTime;
+
+ public CombinedSpiralMover(GameObject middle, GameObject start, GameObject end, int dir) {
+ super(start, end, dir);
+ fakeObject = middle != null ? middle : new FakeGameObject(start, end);
+
+ halfTime = fakeObject.getEndTime();
+ startTime = start.getEndTime();
+ endTime = end.getTime();
+
+ movers = new Mover[2];
+ movers[0] = bestPick(0, start, fakeObject, dir);
+ movers[1] = bestPick(1, fakeObject, end, dir);
+ }
+
+ public CombinedSpiralMover(GameObject start, GameObject end, int dir) {
+ this(null, start, end, dir);
+ }
+
+ /**
+ * Method to pick the 2 inner {@link Mover}s.
+ * Tries to pick a {@link SpiralToMover} first position and a
+ * {@link CombinedSpiralMover} for second position for the sake of good looks
+ *
+ * @param pos index of
+ * @param start start object
+ * @param end end object
+ * @param dir direction
+ * @return best fitting Mover
+ */
+ private Mover bestPick(int pos, GameObject start, GameObject end, int dir) {
+
+ if (endTime - startTime < 10 || Utils.distance(start.end.x, start.end.y, end.start.x, end.start.y) < 40)
+ return new AutoMoverFactory().create(start, end, dir);
+
+ SpiralToMover spiralTo = new SpiralToMover(start, end, dir);
+ CentralSpiralMover center = new CentralSpiralMover(start, end, dir);
+
+ if (pos == 0) {
+ if (inBounds1(spiralTo) || inBounds1(spiralTo = new SpiralToMover(start, end, -dir)))
+ return spiralTo;
+ else if (inBounds1(center) || inBounds1(center = new CentralSpiralMover(start, end, -dir)))
+ return center;
+ } else if (pos == 1) {
+ if (inBounds2(center) || inBounds2(center = new CentralSpiralMover(start, end, -dir)))
+ return center;
+ else if (inBounds2(spiralTo = new SpiralToMover(start, end, -dir)) || inBounds2(spiralTo = new SpiralToMover(start, end, dir)))
+ return spiralTo;
+ } else throw new IllegalStateException("Only 2 inner Movers allowed");
+
+ return new CombinedSpiralMover(start, end, dir);
+ }
+
+
+ @Override
+ public double[] getPointAt(int time) {
+ if (time < halfTime)
+ return movers[0].getPointAt(time);
+ else if (time > halfTime)
+ return movers[1].getPointAt(time);
+ else return new double[]{
+ fakeObject.start.x,
+ fakeObject.start.y
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "CombinedSpiralMover";
+ }
+
+ /**
+ * /**
+ * Check if the first object would be inbounds
+ *
+ * @param mover - the mover to check
+ * @return is mover always inbounds
+ */
+ public boolean inBounds1(Mover mover) {
+ boolean ret = true;
+ int middle = halfTime - startTime;
+ for (int i = 1; ret && i < 15; i++) {
+ ret = checkBounds(mover.getPointAt(startTime + (middle * i) / 16));
+ System.out.println("i: " + i + " = " + ret);
+ }
+
+ return ret;
+ }
+
+ /**
+ * /**
+ * Check if the second object would be inbounds
+ *
+ * @param mover - the mover to check
+ * @return is mover always inbounds
+ */
+ private boolean inBounds2(Mover mover) {
+ boolean ret = true;
+ int middle = endTime - halfTime;
+ for (int i = 1; ret && i < 15; i++) {
+ ret = checkBounds(mover.getPointAt(startTime + (middle * i) / 16));
+ System.out.println("i: " + i + " = " + ret);
+ }
+
+ return ret;
+ }
+
+ private boolean checkBounds(double[] pos) {
+ return 0 < pos[0] && pos[0] < Options.width && 0 < pos[1] && pos[1] < Options.height;
+ }
+}
diff --git a/src/awlex/ospu/movers/SpiralToMover.java b/src/awlex/ospu/movers/SpiralToMover.java
new file mode 100644
index 00000000..179d132b
--- /dev/null
+++ b/src/awlex/ospu/movers/SpiralToMover.java
@@ -0,0 +1,46 @@
+package awlex.ospu.movers;
+
+import itdelatrisu.opsu.Utils;
+import itdelatrisu.opsu.objects.GameObject;
+import yugecin.opsudance.movers.Mover;
+
+/**
+ * Created by Alex Wieser on 09.10.2016.
+ * WHO DO YOU THINK I AM?
+ *
+ * This {@link Mover} ends the spiral from the start object
+ */
+public class SpiralToMover extends Mover {
+
+ /**
+ * How many times the cursor goes around the center
+ * For satisfying results this musn't be a multiple of 2
+ */
+ private final int CIRCLENAVIGATIONS = 1;
+
+ double startAng;
+ double startRad;
+
+ public SpiralToMover(GameObject start, GameObject end, int dir) {
+ super(start, end, dir);
+ startAng = Math.atan2(endY - startY, endX - startX) + Math.PI;
+ startRad = Utils.distance(startX, startY, endX, endY);
+ if (startRad < 20)
+ startRad = 20 + startRad / 20d;
+ }
+
+ @Override
+ public double[] getPointAt(int time) {
+ double rad = startRad * (1 - getT(time));
+ double ang = CIRCLENAVIGATIONS * (startAng + 2d * Math.PI * (1 - getT(time)) * dir);
+ return new double[]{
+ endX + rad * Math.cos(ang),
+ endY + rad * Math.sin(ang)
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "Spiral2";
+ }
+}
diff --git a/src/awlex/ospu/movers/factories/CenterSpiralMoverFactory.java b/src/awlex/ospu/movers/factories/CenterSpiralMoverFactory.java
new file mode 100644
index 00000000..46e90730
--- /dev/null
+++ b/src/awlex/ospu/movers/factories/CenterSpiralMoverFactory.java
@@ -0,0 +1,27 @@
+package awlex.ospu.movers.factories;
+
+import awlex.ospu.FakeGameObject;
+import awlex.ospu.movers.CombinedSpiralMover;
+import itdelatrisu.opsu.objects.GameObject;
+import yugecin.opsudance.movers.Mover;
+import yugecin.opsudance.movers.factories.MoverFactory;
+
+/**
+ * Created by Alex Wieser on 10.10.2016.
+ */
+public class CenterSpiralMoverFactory implements MoverFactory {
+
+ private static FakeGameObject middle;
+ @Override
+ public Mover create(GameObject start, GameObject end, int dir) {
+ if (middle == null)
+ middle = new FakeGameObject();
+ middle.setTime(start.getEndTime() + (end.getTime() - start.getEndTime()) / 2);
+ return new CombinedSpiralMover(middle, start, end, dir);
+ }
+
+ @Override
+ public String toString() {
+ return "CentralSpiralSpin";
+ }
+}
diff --git a/src/awlex/ospu/movers/factories/SpiralMoverFactory.java b/src/awlex/ospu/movers/factories/SpiralMoverFactory.java
new file mode 100644
index 00000000..71ea7587
--- /dev/null
+++ b/src/awlex/ospu/movers/factories/SpiralMoverFactory.java
@@ -0,0 +1,97 @@
+package awlex.ospu.movers.factories;
+
+import itdelatrisu.opsu.Options;
+import itdelatrisu.opsu.Utils;
+import itdelatrisu.opsu.objects.GameObject;
+import awlex.ospu.movers.CentralSpiralMover;
+import awlex.ospu.movers.CombinedSpiralMover;
+import yugecin.opsudance.movers.Mover;
+import awlex.ospu.movers.SpiralToMover;
+import yugecin.opsudance.movers.factories.MoverFactory;
+
+/**
+ * Created by Alex Wieser on 09.10.2016.
+ * WHO DO YOU THINK I AM?
+ */
+public class SpiralMoverFactory implements MoverFactory {
+
+ private int startTime;
+ private int endTime;
+
+ /**
+ * This method will return either a {@link CentralSpiralMover}, {@link SpiralToMover} or
+ * {@link CombinedSpiralMover}, depending on the situation
+ *
+ * @param start
+ * @param end
+ * @param dir
+ * @return
+ */
+ @Override
+ public Mover create(GameObject start, GameObject end, int dir) {
+
+ startTime = start.getEndTime();
+ endTime = end.getTime();
+
+ SpiralToMover spiralTo = new SpiralToMover(start, end, dir);
+ CentralSpiralMover center = new CentralSpiralMover(start, end, dir);
+
+ if (Utils.distance(start.end.x, start.end.y, end.start.y, end.start.x) > 150) {
+ if (inBounds(spiralTo) || inBounds(spiralTo = new SpiralToMover(start, end, -dir)))
+ return spiralTo;
+ else if (inBounds(center) || inBounds(center = new CentralSpiralMover(start, end, -dir)))
+ return center;
+
+ } else {
+ if (inBounds(center) || inBounds(center = new CentralSpiralMover(start, end, -dir)))
+ return center;
+ else if (inBounds(spiralTo) || inBounds(spiralTo = new SpiralToMover(start, end, -dir)))
+ return spiralTo;
+ }
+ return new CombinedSpiralMover(start, end, dir);
+ }
+
+ /**
+ * Checks if the spiral is in bounds.
+ * TODO: Find a better algorithm and more effizient way to to this, by finding the crucial point in the arithmetic spiral. This also applies for other inBounds-methods
+ *
This method fails miserably in the beginning and on the maps with big and fast jumps (e.g.
+ *
+ * @param mover
+ * @return
+ */
+ public boolean inBounds(Mover mover) {
+ boolean ret = true;
+ int middle = endTime - startTime;
+ for (int i = 1; ret && i < 15; i++) {
+ ret = checkBounds(mover.getPointAt(startTime + (middle * i) / 16));
+ System.out.println("i: " + i + " = " + ret);
+ }
+
+ return ret;
+ /*return checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .9))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .8))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .7))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .6))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .5))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .4))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .3))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .2))) &&
+ checkBounds(mover.getPointAt((int) (startTime + (endTime - startTime) * .1)));
+ */
+ }
+
+ /**
+ * checks if a point is in bounds
+ *
+ * @param pos
+ * @return
+ */
+ private boolean checkBounds(double[] pos) {
+ return 0 < pos[0] && pos[0] < Options.width && 0 < pos[1] && pos[1] < Options.height;
+ }
+
+ @Override
+ public String toString() {
+ return "Spiral me right round, baby";
+ }
+}
diff --git a/src/awlex/ospu/spinners/SpiralSpinner.java b/src/awlex/ospu/spinners/SpiralSpinner.java
new file mode 100644
index 00000000..c6724f64
--- /dev/null
+++ b/src/awlex/ospu/spinners/SpiralSpinner.java
@@ -0,0 +1,95 @@
+package awlex.ospu.spinners;
+
+import itdelatrisu.opsu.Options;
+import itdelatrisu.opsu.Utils;
+import yugecin.opsudance.spinners.Spinner;
+
+/**
+ * Created by Alex Wieser on 09.10.2016.
+ * WHO DO YOU THINK I AM?
+ */
+public class SpiralSpinner extends Spinner {
+
+ /**
+ * How many points the Spinner uses.
+ * if there are not enough points the spinner looks angular.
+ * if there are too many points the spinner doesn't operate smooth
+ */
+ private final int SIZE = 100;
+
+ /**
+ * The density of the spinner
+ * Determines how many times the spinner will around the center.
+ * if the value is to high the spinner will become angular.
+ * if this value goes under 2 the spinner won't be complete
+ */
+ private final int DENSITY = 10;
+
+ /**
+ * How much the spinner will be rotated.
+ * Negative = clockwise
+ * Positive = counter clockwise
+ */
+ private final double DELTA = -Math.PI / 20;
+ private int MAX_RAD;
+ private int index;
+ double[][] points;
+ boolean down;
+
+ @Override
+ public void init() {
+ points = new double[SIZE][];
+ double ang;
+ double rad;
+ for (int i = 0; i < SIZE / 2; i++) {
+ MAX_RAD = (int) (Options.height * .35);
+ ang = (DENSITY * (Math.PI / SIZE) * i);
+ rad = (MAX_RAD / (SIZE / 2)) * i;
+ int offsetX = Options.width / 2;
+ int offsetY = Options.height / 2;
+ points[SIZE / 2 - 1 - i] = new double[]{
+ offsetX + rad * Math.cos(ang),
+ offsetY + rad * Math.sin(ang)
+ };
+ points[SIZE / 2 + i] = new double[]{
+ offsetX + rad * (Math.cos(ang) * Math.cos(Math.PI) - Math.sin(ang) * Math.sin(Math.PI)),
+ offsetY + rad * -Math.sin(ang)
+ };
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Spiralspinner";
+ }
+
+ @Override
+ public double[] getPoint() {
+ if (waitForDelay()) {
+ if (down) {
+ if (--index == 0)
+ down = !down;
+ } else if (++index == SIZE - 1)
+ down = !down;
+
+ if (!down && index == 1) {
+ rotatePointAroundCenter(points[0], DELTA);
+ } else if (down && index == SIZE - 2) {
+ rotatePointAroundCenter(points[SIZE - 1], DELTA);
+ }
+ }
+ rotatePointAroundCenter(points[index], DELTA);
+ return points[index];
+ }
+
+ private static void rotatePointAroundCenter(double[] point, double beta) {
+ double angle = Math.atan2(point[1] - Options.height / 2, point[0] - Options.width / 2);
+ double rad = Utils.distance(point[0], point[1], Options.width / 2, Options.height / 2);
+
+ //rotationMatrix
+ point[0] = Options.width / 2 + rad * (Math.cos(angle) * Math.cos(beta) - Math.sin(angle) * Math.sin(beta));
+ point[1] = Options.height / 2 + rad * (Math.cos(angle) * Math.sin(beta) + Math.sin(angle) * Math.cos(beta));
+ }
+
+
+}
diff --git a/src/yugecin/opsudance/Dancer.java b/src/yugecin/opsudance/Dancer.java
index 7d23785a..f9f8e7f5 100644
--- a/src/yugecin/opsudance/Dancer.java
+++ b/src/yugecin/opsudance/Dancer.java
@@ -17,6 +17,9 @@
*/
package yugecin.opsudance;
+import awlex.ospu.movers.factories.CenterSpiralMoverFactory;
+import awlex.ospu.movers.factories.SpiralMoverFactory;
+import awlex.ospu.spinners.SpiralSpinner;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.objects.Circle;
@@ -39,7 +42,9 @@ public class Dancer {
new HalfLowEllipseMoverFactory(),
new JumpMoverFactory(),
new LinearMoverFactory(),
- new QuartCircleMoverFactory()
+ new QuartCircleMoverFactory(),
+ new SpiralMoverFactory(),
+ new CenterSpiralMoverFactory()
};
public static Spinner[] spinners = new Spinner[] {
@@ -53,6 +58,7 @@ public class Dancer {
new LessThanThreeSpinner(),
new RektCircleSpinner(),
new ApproachCircleSpinner(),
+ new SpiralSpinner()
};
public static Dancer instance = new Dancer();