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; + } + +}