diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java
index 4bf15158..2f29b582 100644
--- a/src/itdelatrisu/opsu/states/Game.java
+++ b/src/itdelatrisu/opsu/states/Game.java
@@ -51,7 +51,6 @@ import java.util.*;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
-import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
@@ -1500,6 +1499,11 @@ public class Game extends ComplexOpsuState {
}
});
+ if (replays != null) {
+ for (ReplayPlayback r : replays) {
+ r.cursor.destroy();
+ }
+ }
replays = new LinkedList<>();
float hueshift = 360f / files.length + 18f;
float hue = 0;
@@ -1533,7 +1537,7 @@ public class Game extends ComplexOpsuState {
for (ReplayPlayback r : replays) {
Color c = new Color(java.awt.Color.getHSBColor((hue) / 360f, 1.0f, 1.0f).getRGB());
r.color = c;
- r.cursor = new Cursor(c);
+ r.cursor.filter = c;
hue += hueshift;
}
@@ -1807,6 +1811,13 @@ public class Game extends ComplexOpsuState {
knorkesliders = null;
+ if (replays != null) {
+ for (ReplayPlayback r : replays) {
+ r.cursor.destroy();
+ }
+ replays.clear();
+ }
+
Dancer.instance.setGameObjects(null);
Cursor.lastObjColor = Color.white;
diff --git a/src/yugecin/opsudance/ReplayCursor.java b/src/yugecin/opsudance/ReplayCursor.java
new file mode 100644
index 00000000..e9123019
--- /dev/null
+++ b/src/yugecin/opsudance/ReplayCursor.java
@@ -0,0 +1,267 @@
+/*
+ * opsu! - an open-source osu! client
+ * Copyright (C) 2014, 2015 Jeffrey Han
+ *
+ * opsu! 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! 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!. If not, see .
+ */
+
+package yugecin.opsudance;
+
+import java.awt.Point;
+import java.nio.IntBuffer;
+import java.util.Iterator;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.*;
+import org.newdawn.slick.*;
+import org.newdawn.slick.opengl.Texture;
+
+import itdelatrisu.opsu.render.Rendertarget;
+
+import static itdelatrisu.opsu.GameImage.*;
+import static yugecin.opsudance.core.InstanceContainer.*;
+
+public class ReplayCursor
+{
+ private final Point lastPosition;
+ private TrailList trail = new TrailList();
+
+ public Color filter;
+
+ private Rendertarget fbo;
+
+ private static long nowtime;
+
+ public ReplayCursor(Color filter) {
+ this.filter = filter;
+ this.lastPosition = new Point(width2, 0);
+ this.fbo = Rendertarget.createRTTFramebuffer(width, height);
+ }
+
+ public void draw()
+ {
+ nowtime = System.currentTimeMillis();
+
+ // stuff copied from CurveRenderState and stuff, I don't know what I'm doing
+ int oldFb = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT);
+ int oldTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
+ //glGetInteger requires a buffer of size 16, even though just 4
+ //values are returned in this specific case
+ IntBuffer oldViewport = BufferUtils.createIntBuffer(16);
+ GL11.glGetInteger(GL11.GL_VIEWPORT, oldViewport);
+ EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbo.getID());
+ GL11.glViewport(0, 0, fbo.width, fbo.height);
+ // render
+ GL11.glClearColor(0f, 0f, 0f, 0f);
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
+ //GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ // render2
+
+ renderstuff();
+
+ //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, oldTex);
+ EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, oldFb);
+ GL11.glViewport(oldViewport.get(0), oldViewport.get(1), oldViewport.get(2), oldViewport.get(3));
+
+ //GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glDisable(GL11.GL_TEXTURE_1D);
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, fbo.getTextureID());
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glColor4f(1f, 1f, 1f, 1f);
+ GL11.glTexCoord2f(1f, 1f);
+ GL11.glVertex2i(fbo.width, 0);
+ GL11.glTexCoord2f(0f, 1f);
+ GL11.glVertex2i(0, 0);
+ GL11.glTexCoord2f(0f, 0f);
+ GL11.glVertex2i(0, fbo.height);
+ GL11.glTexCoord2f(1f, 0f);
+ GL11.glVertex2i(fbo.width, fbo.height);
+ GL11.glEnd();
+ GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ CURSOR.getImage().drawCentered(lastPosition.x, lastPosition.y, filter);
+ CURSOR_MIDDLE.getImage().drawCentered(lastPosition.x, lastPosition.y, filter);
+ }
+
+ private void renderstuff()
+ {
+ GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, fbo.getVbo());
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glEnable(GL11.GL_TEXTURE_1D);
+
+ final Image img = CURSOR_TRAIL.getImage();
+ final Texture txt = img.getTexture();
+ Color.white.bind();
+ txt.bind();
+
+ float alpha = 0f;
+ float alphaIncrease = .5f / trail.size;
+ float trailwidth2 = img.getWidth() / 2f;
+ float trailheight2 = img.getHeight() / 2f;
+ float txtwidth = txt.getWidth();
+ float txtheight = txt.getHeight();
+ GL11.glBegin(GL11.GL_QUADS);
+ final Point lastpoint = new Point(-1, -1);
+ for (Trailpart p : trail) {
+ alpha += alphaIncrease;
+ GL11.glColor4f(filter.r, filter.g, filter.b, alpha);
+ GL11.glTexCoord2f(0f, 0f);
+ GL11.glVertex3f(p.x - trailwidth2, p.y - trailheight2, 0f);
+ GL11.glTexCoord2f(txtwidth, 0);
+ GL11.glVertex3f(p.x + trailwidth2, p.y - trailheight2, 0f);
+ GL11.glTexCoord2f(txtwidth, txtheight);
+ GL11.glVertex3f(p.x + trailwidth2, p.y + trailheight2, 0f);
+ GL11.glTexCoord2f(0f, txtheight);
+ GL11.glVertex3f(p.x - trailwidth2, p.y + trailheight2, 0f);
+ lastpoint.x = p.x;
+ lastpoint.y = p.y;
+ }
+ GL11.glEnd();
+ }
+
+ /**
+ * Sets the cursor position to given point and updates trail.
+ * @param mouseX x coordinate to set position to
+ * @param mouseY y coordinate to set position to
+ */
+ public void setCursorPosition(int delta, int mouseX, int mouseY) {
+ nowtime = System.currentTimeMillis();
+
+ if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) {
+ //trail.add(new Trailpart(mouseX, mouseY));
+ }
+ lastPosition.move(mouseX, mouseY);
+
+ int removecount = 0;
+ TrailNode newfirst = trail.first;
+ while (newfirst != null && newfirst.value.time < nowtime - 400) {
+ newfirst = newfirst.next;
+ removecount++;
+ }
+ trail.first = newfirst;
+ if (newfirst == null) {
+ trail.last = null;
+ }
+ trail.size -= removecount;
+ }
+
+ /**
+ * Adds all points between (x1, y1) and (x2, y2) to the cursor point lists.
+ * @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
+ */
+ private boolean addCursorPoints(int x1, int y1, int x2, int y2) {
+ int size = trail.size;
+ // delta of exact value and rounded value of the dependent variable
+ int d = 0;
+ int dy = Math.abs(y2 - y1);
+ int dx = Math.abs(x2 - x1);
+
+ int dy2 = (dy << 1); // slope scaling factors to avoid floating
+ int dx2 = (dx << 1); // point
+ int ix = x1 < x2 ? 1 : -1; // increment direction
+ int iy = y1 < y2 ? 1 : -1;
+
+ if (dy <= dx) {
+ for (;;) {
+ if (x1 == x2)
+ break;
+ trail.add(new Trailpart(x1, y1));
+ x1 += ix;
+ d += dy2;
+ if (d > dx) {
+ y1 += iy;
+ d -= dx2;
+ }
+ }
+ } else {
+ for (;;) {
+ if (y1 == y2)
+ break;
+ trail.add(new Trailpart(x1, y1));
+ y1 += iy;
+ d += dx2;
+ if (d > dy) {
+ x1 += ix;
+ d -= dy2;
+ }
+ }
+ }
+ return trail.size != size;
+ }
+
+ public void destroy()
+ {
+ this.fbo.destroyRTT();
+ }
+
+ private static class TrailList implements Iterable
+ {
+ TrailNode first;
+ TrailNode last;
+ int size;
+
+ public void add(Trailpart t)
+ {
+ if (last == null) {
+ last = first = new TrailNode(t);
+ } else {
+ TrailNode n = new TrailNode(t);
+ last.next = n;
+ last = n;
+ }
+ size++;
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator() {
+ TrailNode node = first;
+ @Override
+ public boolean hasNext() {
+ return node != null;
+ }
+
+ @Override
+ public Trailpart next() {
+ Trailpart v = node.value;
+ node = node.next;
+ return v;
+ }
+ };
+ }
+ }
+
+ private static class TrailNode
+ {
+ TrailNode next;
+ Trailpart value;
+ TrailNode(Trailpart t) {
+ value = t;
+ }
+ }
+
+ private static class Trailpart {
+ int x, y;
+ long time;
+ Trailpart(int x, int y) {
+ this.x = x;
+ this.y = y;
+ this.time = nowtime;
+ }
+ }
+}
diff --git a/src/yugecin/opsudance/ReplayPlayback.java b/src/yugecin/opsudance/ReplayPlayback.java
index 7e6147bc..6374e449 100644
--- a/src/yugecin/opsudance/ReplayPlayback.java
+++ b/src/yugecin/opsudance/ReplayPlayback.java
@@ -20,7 +20,6 @@ package yugecin.opsudance;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.replay.ReplayFrame;
-import itdelatrisu.opsu.ui.Cursor;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color;
@@ -48,7 +47,7 @@ public class ReplayPlayback {
private int frameIndex;
public Color color;
public Color originalcolor;
- public Cursor cursor;
+ public final ReplayCursor cursor;
private int keydelay[];
public static final int SQSIZE = 15;
public static final int UNITHEIGHT = SQSIZE + 5;
@@ -80,7 +79,7 @@ public class ReplayPlayback {
this.originalcolor = new Color(color);
Color cursorcolor = new Color(color);
//cursorcolor.a = 0.5f;
- cursor = new Cursor(cursorcolor);
+ cursor = new ReplayCursor(cursorcolor);
keydelay = new int[4];
this.player = replay.playerName;
this.playerwidth = Fonts.SMALLBOLD.getWidth(this.player);
@@ -299,7 +298,7 @@ public class ReplayPlayback {
y = height - y;
}
cursor.setCursorPosition(renderdelta, currentFrame.getScaledX(), y);
- cursor.draw(false);
+ cursor.draw();
}
private void processKeys() {
@@ -322,11 +321,11 @@ public class ReplayPlayback {
public static class HitData {
int combobreaktime = -1;
- LinkedList time300 = new LinkedList();
- LinkedList time100 = new LinkedList();
- LinkedList time50 = new LinkedList();
- LinkedList acc = new LinkedList();
- LinkedList combo = new LinkedList();
+ LinkedList time300 = new LinkedList<>();
+ LinkedList time100 = new LinkedList<>();
+ LinkedList time50 = new LinkedList<>();
+ LinkedList acc = new LinkedList<>();
+ LinkedList combo = new LinkedList<>();
public HitData(File file) {
try (InputStream in = new FileInputStream(file)) {