diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java
index ada3cc92..56cab90a 100644
--- a/src/itdelatrisu/opsu/states/Game.java
+++ b/src/itdelatrisu/opsu/states/Game.java
@@ -72,6 +72,7 @@ import org.newdawn.slick.state.transition.EmptyTransition;
import org.newdawn.slick.state.transition.FadeInTransition;
import yugecin.opsudance.*;
import yugecin.opsudance.objects.curves.FakeCombinedCurve;
+import yugecin.opsudance.sbv2.MoveStoryboard;
import yugecin.opsudance.ui.SBOverlay;
/**
@@ -313,6 +314,7 @@ public class Game extends BasicGameState {
private final int state;
private final Cursor mirrorCursor;
+ private MoveStoryboard msb;
private SBOverlay sbOverlay;
private FakeCombinedCurve knorkesliders;
@@ -363,7 +365,8 @@ public class Game extends BasicGameState {
@Override
public void init(GameContainer container, StateBasedGame game)
throws SlickException {
- this.sbOverlay = new SBOverlay(this, container);
+ this.msb = new MoveStoryboard(container);
+ this.sbOverlay = new SBOverlay(this, msb, container);
this.container = container;
this.game = game;
input = container.getInput();
@@ -470,6 +473,12 @@ public class Game extends BasicGameState {
}
}
+ float[] sbPosition = sbOverlay.getPoint(trackPosition);
+ if (sbPosition != null) {
+ autoPoint.x = sbPosition[0];
+ autoPoint.y = sbPosition[1];
+ }
+
// set mouse coordinates
autoMousePosition.set(autoPoint.x, autoPoint.y);
}
diff --git a/src/yugecin/opsudance/sbv2/StoryboardMove.java b/src/yugecin/opsudance/sbv2/StoryboardMove.java
new file mode 100644
index 00000000..8ef9c11a
--- /dev/null
+++ b/src/yugecin/opsudance/sbv2/StoryboardMove.java
@@ -0,0 +1,33 @@
+/*
+ * opsu!dance - fork of opsu! with cursordance auto
+ * Copyright (C) 2016 yugecin
+ *
+ * opsu!dance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu!dance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!dance. If not, see .
+ */
+package yugecin.opsudance.sbv2;
+
+import org.newdawn.slick.Graphics;
+import yugecin.opsudance.sbv2.movers.StoryboardMover;
+
+public interface StoryboardMove {
+
+ void add(StoryboardMover mover);
+ float[] getPointAt(float t);
+ void update(int delta, int x, int y);
+ void mousePressed(int x, int y);
+ void mouseReleased(int x, int y);
+ void recalculateTimes();
+ void render(Graphics g);
+
+}
diff --git a/src/yugecin/opsudance/sbv2/StoryboardMoveImpl.java b/src/yugecin/opsudance/sbv2/StoryboardMoveImpl.java
new file mode 100644
index 00000000..a8613327
--- /dev/null
+++ b/src/yugecin/opsudance/sbv2/StoryboardMoveImpl.java
@@ -0,0 +1,146 @@
+/*
+ * opsu!dance - fork of opsu! with cursordance auto
+ * Copyright (C) 2016 yugecin
+ *
+ * opsu!dance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu!dance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!dance. If not, see .
+ */
+package yugecin.opsudance.sbv2;
+
+import itdelatrisu.opsu.objects.curves.Vec2f;
+import org.newdawn.slick.Color;
+import org.newdawn.slick.Graphics;
+import yugecin.opsudance.sbv2.movers.StoryboardMover;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StoryboardMoveImpl implements StoryboardMove {
+
+ private static final int POINTSIZE = 3;
+
+ private Vec2f start;
+ private Vec2f end;
+ private List movers;
+ private List midPoints;
+
+ private StoryboardMover nextMover;
+ private Vec2f currentPoint;
+ private StoryboardMover prevMover;
+
+ private float totalLength;
+
+ public StoryboardMoveImpl(Vec2f start, Vec2f end) {
+ this.start = start;
+ this.end = end;
+ movers = new ArrayList<>();
+ midPoints = new ArrayList<>();
+ }
+
+ @Override
+ public void add(StoryboardMover mover) {
+ mover.end = end;
+ if (movers.size() == 0) {
+ mover.start = start;
+ } else {
+ StoryboardMover lastMover = movers.get(movers.size() - 1);
+ Vec2f mid = new Vec2f(
+ (lastMover.start.x + lastMover.end.x) / 2f,
+ (lastMover.start.y + lastMover.end.y) / 2f
+ );
+ midPoints.add(mid);
+ lastMover.end = mid;
+ totalLength -= lastMover.getLength();
+ lastMover.recalculateLength();
+ totalLength += lastMover.getLength();
+ mover.start = mid;
+ }
+ movers.add(mover);
+ recalculateTimes();
+ }
+
+ @Override
+ public float[] getPointAt(float t) {
+ if (movers.size() == 0) {
+ return new float[] { end.x, end.y };
+ }
+ float cumulativeTime = 0f;
+ for (StoryboardMover mover : movers) {
+ cumulativeTime += mover.timeLengthPercentOfTotalTime;
+ if (cumulativeTime > t) {
+ return mover.getPointAt((t - (cumulativeTime - mover.timeLengthPercentOfTotalTime)) / mover.timeLengthPercentOfTotalTime);
+ }
+ }
+ return new float[] { end.x, end.y };
+ }
+
+ @Override
+ public void update(int delta, int x, int y) {
+ if (currentPoint != null) {
+ moveCurrentPoint(x, y);
+ }
+ }
+
+ @Override
+ public void mousePressed(int x, int y) {
+ int i = 0;
+ for (Vec2f point : midPoints) {
+ if (point.x - POINTSIZE <= x && x <= point.x + POINTSIZE && point.y - POINTSIZE <= y && y <= point.y + POINTSIZE) {
+ currentPoint = point;
+ prevMover = movers.get(i);
+ nextMover = movers.get(i + 1);
+ break;
+ }
+ i++;
+ }
+ }
+
+ @Override
+ public void mouseReleased(int x, int y) {
+ if (currentPoint != null) {
+ moveCurrentPoint(x, y);
+ totalLength -= prevMover.getLength() + nextMover.getLength();
+ prevMover.recalculateLength();
+ nextMover.recalculateLength();
+ totalLength += prevMover.getLength() + nextMover.getLength();
+ currentPoint = null;
+ recalculateTimes();
+ }
+ }
+
+ @Override
+ public void recalculateTimes() {
+ for (StoryboardMover mover : movers) {
+ mover.timeLengthPercentOfTotalTime = mover.getLength() / totalLength;
+ }
+ }
+
+ private void moveCurrentPoint(int x, int y) {
+ currentPoint.x = x;
+ currentPoint.y = y;
+ prevMover.end = currentPoint;
+ nextMover.start = currentPoint;
+ }
+
+ @Override
+ public void render(Graphics g) {
+ for (StoryboardMover mover : movers) {
+ mover.render(g);
+ }
+ g.setColor(Color.cyan);
+ for (Vec2f point : midPoints) {
+ g.fillRect(point.x - POINTSIZE, point.y - POINTSIZE, POINTSIZE * 2 + 1, POINTSIZE * 2 + 1);
+ }
+ }
+
+}
diff --git a/src/yugecin/opsudance/sbv2/movers/LinearStoryboardMover.java b/src/yugecin/opsudance/sbv2/movers/LinearStoryboardMover.java
new file mode 100644
index 00000000..1742e06f
--- /dev/null
+++ b/src/yugecin/opsudance/sbv2/movers/LinearStoryboardMover.java
@@ -0,0 +1,47 @@
+/*
+ * opsu!dance - fork of opsu! with cursordance auto
+ * Copyright (C) 2016 yugecin
+ *
+ * opsu!dance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu!dance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!dance. If not, see .
+ */
+package yugecin.opsudance.sbv2.movers;
+
+import itdelatrisu.opsu.Utils;
+import org.newdawn.slick.Color;
+
+public class LinearStoryboardMover extends StoryboardMover {
+
+ public LinearStoryboardMover() {
+ super(Color.red);
+ }
+
+ @Override
+ public float[] getPointAt(float t) {
+ return new float[] {
+ Utils.lerp(start.x, end.x, t),
+ Utils.lerp(start.y, end.y, t),
+ };
+ }
+
+ @Override
+ public float getLength() {
+ return length;
+ }
+
+ @Override
+ public void recalculateLength() {
+ length = Utils.distance(start.x, start.y, end.x, end.y);
+ }
+
+}
diff --git a/src/yugecin/opsudance/sbv2/movers/StoryboardMover.java b/src/yugecin/opsudance/sbv2/movers/StoryboardMover.java
new file mode 100644
index 00000000..b869614c
--- /dev/null
+++ b/src/yugecin/opsudance/sbv2/movers/StoryboardMover.java
@@ -0,0 +1,53 @@
+/*
+ * opsu!dance - fork of opsu! with cursordance auto
+ * Copyright (C) 2016 yugecin
+ *
+ * opsu!dance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu!dance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!dance. If not, see .
+ */
+package yugecin.opsudance.sbv2.movers;
+
+import itdelatrisu.opsu.objects.curves.Vec2f;
+import org.newdawn.slick.Color;
+import org.newdawn.slick.Graphics;
+
+public abstract class StoryboardMover {
+
+ public static final float CALC_DRAW_INTERVAL = 0.05f;
+
+ protected float length;
+ public Vec2f start;
+ public Vec2f end;
+ private Color renderColor;
+ public float timeLengthPercentOfTotalTime;
+
+ public StoryboardMover(Color renderColor) {
+ this.renderColor = renderColor;
+ }
+
+ public abstract float[] getPointAt(float t);
+ public abstract void recalculateLength();
+
+ public float getLength() {
+ return length;
+ }
+
+ public void render(Graphics g) {
+ g.setColor(renderColor);
+ for (float t = 0; t <= 1f; t += StoryboardMover.CALC_DRAW_INTERVAL) {
+ float[] p = getPointAt(t);
+ g.fillRect(p[0] - 1, p[1] - 1, 3, 3);
+ }
+ }
+
+}
diff --git a/src/yugecin/opsudance/ui/SBOverlay.java b/src/yugecin/opsudance/ui/SBOverlay.java
index d7672f74..c3c4a9db 100644
--- a/src/yugecin/opsudance/ui/SBOverlay.java
+++ b/src/yugecin/opsudance/ui/SBOverlay.java
@@ -28,6 +28,7 @@ import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import yugecin.opsudance.ObjectColorOverrides;
+import yugecin.opsudance.sbv2.MoveStoryboard;
import yugecin.opsudance.ui.OptionsOverlay.OptionTab;
import java.util.*;
@@ -109,6 +110,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
private int index;
private final Game game;
+ private final MoveStoryboard msb;
private final OptionsOverlay overlay;
static {
@@ -117,8 +119,9 @@ public class SBOverlay implements OptionsOverlay.Parent {
}
}
- public SBOverlay(Game game, GameContainer container) {
+ public SBOverlay(Game game, MoveStoryboard msb, GameContainer container) {
this.game = game;
+ this.msb = msb;
initialOptions = new HashMap<>();
overlay = new OptionsOverlay(this, options, 2, container);
this.width = container.getWidth();
@@ -131,6 +134,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
if (!Options.isEnableSB() || hide) {
return;
}
+ msb.render(g);
int lh = Fonts.SMALL.getLineHeight();
Fonts.SMALL.drawString(10, height - 50 + lh, "save position: ctrl+s, load position: ctrl+l", Color.cyan);
Fonts.SMALL.drawString(10, height - 50, "speed: C " + (speed / 10f) + " V", Color.cyan);
@@ -163,6 +167,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
if (Options.isEnableSB() && menu) {
overlay.update(delta, mouseX, mouseY);
}
+ msb.update(delta, mouseX, mouseY);
}
public boolean keyPressed(int key, char c) {
@@ -234,6 +239,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
public void setGameObjects(GameObject[] gameObjects) {
if (this.gameObjects.length != gameObjects.length) {
optionsMap = new HashMap[gameObjects.length];
+ msb.setGameObjects(gameObjects);
}
if (optionsMap.length > 0) {
// copy all current settings in first obj map
@@ -246,6 +252,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
}
public boolean mousePressed(int button, int x, int y) {
+ msb.mousePressed(x, y);
if (!menu) {
return false;
}
@@ -270,6 +277,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
if (index >= optionsMap.length) {
return;
}
+ msb.setIndex(index);
for (; this.index <= index; this.index++) {
HashMap options = optionsMap[this.index];
if (options == null) {
@@ -289,6 +297,7 @@ public class SBOverlay implements OptionsOverlay.Parent {
overlay.mouseReleased(button, x, y);
return true;
}
+ msb.mouseReleased(x, y);
if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) {
return false;
}
@@ -348,6 +357,10 @@ public class SBOverlay implements OptionsOverlay.Parent {
}
}
+ public float[] getPoint(int trackPosition) {
+ return msb.getPoint(trackPosition);
+ }
+
@Override
public void onLeave() {
menu = false;
diff --git a/src/yugecin/opsudance/ui/SimpleButton.java b/src/yugecin/opsudance/ui/SimpleButton.java
new file mode 100644
index 00000000..a7c68d89
--- /dev/null
+++ b/src/yugecin/opsudance/ui/SimpleButton.java
@@ -0,0 +1,72 @@
+/*
+ * opsu!dance - fork of opsu! with cursordance auto
+ * Copyright (C) 2016 yugecin
+ *
+ * opsu!dance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu!dance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!dance. If not, see .
+ */
+package yugecin.opsudance.ui;
+
+
+import org.newdawn.slick.Color;
+import org.newdawn.slick.Font;
+import org.newdawn.slick.Graphics;
+
+import java.awt.Rectangle;
+
+public class SimpleButton {
+
+ private final Color bg;
+ private final Color fg;
+ private final Color border;
+ private final Color hoverBorder;
+ private final Font font;
+ private String text;
+ private int textY;
+ private Rectangle hitbox;
+ private boolean isHovered;
+
+ public SimpleButton(int x, int y, int width, int height, Font font, String text, Color bg, Color fg, Color border, Color hoverBorder) {
+ this.bg = bg;
+ this.fg = fg;
+ this.border = border;
+ this.hoverBorder = hoverBorder;
+ this.hitbox = new Rectangle(x, y, width, height);
+ this.font = font;
+ this.text = text;
+ this.textY = y + (height - font.getLineHeight()) / 2;
+ }
+
+ public void render(Graphics g) {
+ g.setLineWidth(2f);
+ g.setColor(bg);
+ g.fillRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
+ g.setColor(fg);
+ font.drawString(hitbox.x + 20, textY, text);
+ if (isHovered) {
+ g.setColor(hoverBorder);
+ } else {
+ g.setColor(border);
+ }
+ g.drawRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
+ }
+
+ public void update(int x, int y) {
+ isHovered = hitbox.contains(x, y);
+ }
+
+ public boolean isHovered() {
+ return isHovered;
+ }
+
+}