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..de0696d8 --- /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..fa5bc442 --- /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..41d5935a --- /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 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..1b2e6066 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();