Compare commits
59 Commits
master
...
kockout-wd
Author | SHA1 | Date | |
---|---|---|---|
f46ee19838 | |||
9a3ecc06aa | |||
|
02448503c7 | ||
|
10e5375231 | ||
|
db5299a32a | ||
|
f01728156a | ||
|
4117357f31 | ||
|
8725e0b31c | ||
|
6ca1f6e800 | ||
|
6076e6e9c9 | ||
|
3debd40aec | ||
|
0a6412e03f | ||
|
2b83657907 | ||
|
a3c1cfad9e | ||
|
f92270fa22 | ||
|
2ae438ced2 | ||
|
e169b0b1f5 | ||
|
ec375965a2 | ||
|
949bc62b47 | ||
|
ee14ac9b2d | ||
|
5a268bc713 | ||
|
3d470cbe7a | ||
|
3913abfcaf | ||
|
8df9732bf6 | ||
|
3b82c7797e | ||
|
a8cacc690f | ||
|
eec3ae3ecd | ||
|
f398b961d7 | ||
|
29640a4ef7 | ||
|
9628149de0 | ||
|
8fed11ba86 | ||
|
9af2b01657 | ||
|
4f68669add | ||
|
8471c43ed5 | ||
|
b4498333df | ||
|
c0f122c74d | ||
|
df1afd98c3 | ||
|
22d2b0be86 | ||
|
085cd719fa | ||
|
3bccdc5540 | ||
|
992ec7afd8 | ||
|
60de7a26c1 | ||
|
eaa46bddd9 | ||
|
92dc59f7b6 | ||
|
408c162741 | ||
|
35769b31c7 | ||
|
5704ebb0c1 | ||
|
e8d0199705 | ||
|
9320767f21 | ||
|
9d9f2df5d8 | ||
|
5f334ca4cb | ||
|
9df139bded | ||
|
3629dfd4d7 | ||
|
e20b55ba59 | ||
|
e270aeb92e | ||
|
00f34296fb | ||
|
0e8bf0b31e | ||
|
a84786987a | ||
|
ba2aa51b7b |
15
res/sh_Cursor.vertex
Normal file
15
res/sh_Cursor.vertex
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
attribute vec4 m_Position;
|
||||||
|
attribute vec2 m_TexCoord;
|
||||||
|
attribute float m_Time;
|
||||||
|
|
||||||
|
varying vec4 v_Color;
|
||||||
|
varying vec2 v_TexCoord;
|
||||||
|
|
||||||
|
uniform float g_FadeClock;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; //m_Position;
|
||||||
|
v_Color = vec4(1.0, 0.0, 0.0, g_FadeClock);
|
||||||
|
v_TexCoord = m_TexCoord;
|
||||||
|
}
|
13
res/sh_Texture.frag
Normal file
13
res/sh_Texture.frag
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying vec4 v_Color;
|
||||||
|
varying vec2 v_TexCoord;
|
||||||
|
|
||||||
|
uniform sampler2D m_Sampler;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_FragColor = v_Color * texture2D(m_Sampler, v_TexCoord);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ package awlex.ospu;
|
||||||
* Created by Awlex on 10.10.2016.
|
* Created by Awlex on 10.10.2016.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.GameData;
|
||||||
import itdelatrisu.opsu.objects.GameObject;
|
import itdelatrisu.opsu.objects.GameObject;
|
||||||
import itdelatrisu.opsu.objects.curves.Vec2f;
|
import itdelatrisu.opsu.objects.curves.Vec2f;
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
|
@ -35,6 +36,15 @@ public class FakeGameObject extends GameObject {
|
||||||
this.start.y = this.end.y = (start.end.y + end.start.y) / 2;
|
this.start.y = this.end.y = (start.end.y + end.start.y) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameObject clone(GameData a) {
|
||||||
|
FakeGameObject o = new FakeGameObject();
|
||||||
|
o.halfTime = this.halfTime;
|
||||||
|
o.start = this.start;
|
||||||
|
o.end = this.end;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition, boolean mirrored) {
|
public void draw(Graphics g, int trackPosition, boolean mirrored) {
|
||||||
|
|
||||||
|
|
92
src/com/osufx/sunpy/PropertyCollection.java
Normal file
92
src/com/osufx/sunpy/PropertyCollection.java
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package com.osufx.sunpy;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class PropertyCollection {
|
||||||
|
private HashMap<String, ShaderProperty> properties = new HashMap<>();
|
||||||
|
private Shader owner;
|
||||||
|
|
||||||
|
private boolean changed = false;
|
||||||
|
|
||||||
|
public PropertyCollection(Shader owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
this.changed = true;
|
||||||
|
this.properties.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(ShaderProperty prop) {
|
||||||
|
this.properties.put(prop.Name, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Replace(ShaderProperty prop) {
|
||||||
|
this.properties.replace(prop.Name, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Replace(String key, Object value) {
|
||||||
|
if (!this.properties.containsKey(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ShaderProperty prop = this.properties.get(key);
|
||||||
|
|
||||||
|
if (value.getClass() != prop.Type){
|
||||||
|
System.out.println(String.format("%s variable %s does not take value type %s", this.owner.name, key, value.getClass()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.Value != null && prop.Value == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prop.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(String key) {
|
||||||
|
this.properties.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set() {
|
||||||
|
if (!this.changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (ShaderProperty prop : this.properties.values()) {
|
||||||
|
prop.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderProperty get(String propName) {
|
||||||
|
if (!this.properties.containsKey(propName))
|
||||||
|
return null;
|
||||||
|
return this.properties.get(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String propName, Object value) {
|
||||||
|
if (!this.owner.loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.properties.containsKey(propName)) {
|
||||||
|
System.out.println(String.format("Property %s does not exist!", propName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProperty prop = this.properties.get(propName);
|
||||||
|
|
||||||
|
if (value.getClass() != prop.Type){
|
||||||
|
System.out.println(String.format("%s variable %s does not take value type %s", this.owner.name, propName, value.getClass()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.Value != null && prop.Value == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prop.Value = value;
|
||||||
|
|
||||||
|
if (this.owner.started)
|
||||||
|
prop.Set();
|
||||||
|
else
|
||||||
|
this.changed = true;
|
||||||
|
}
|
||||||
|
}
|
213
src/com/osufx/sunpy/Shader.java
Normal file
213
src/com/osufx/sunpy/Shader.java
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
package com.osufx.sunpy;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.opengl.GL20;
|
||||||
|
import org.newdawn.slick.util.Log;
|
||||||
|
import org.newdawn.slick.util.ResourceLoader;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Shader {
|
||||||
|
public String name;
|
||||||
|
public int programId = -1;
|
||||||
|
public boolean loaded = false;
|
||||||
|
public boolean started = false;
|
||||||
|
|
||||||
|
public PropertyCollection Properties;
|
||||||
|
|
||||||
|
public static HashMap<String, Object> globalProperties = new HashMap<>();
|
||||||
|
public static List<Shader> loadedShaders = new ArrayList<>();
|
||||||
|
public static int currentShader = 0;
|
||||||
|
|
||||||
|
public HashMap<String, Integer> attribLoc = new HashMap<>();
|
||||||
|
private static final String[] attribs = new String[] {
|
||||||
|
"m_Position",
|
||||||
|
"m_Color",
|
||||||
|
"m_TexCoord",
|
||||||
|
"m_Time",
|
||||||
|
"m_Direction"
|
||||||
|
};
|
||||||
|
|
||||||
|
private HashMap<Integer, String> shaderParts = new HashMap<>();
|
||||||
|
private int previousShader;
|
||||||
|
|
||||||
|
public Shader(String vertexShader, String fragmentShader) {
|
||||||
|
String vertexString;
|
||||||
|
String fragmentString;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream vertexStream = ResourceLoader.getResourceAsStream(vertexShader);
|
||||||
|
vertexString = convertStreamToString(vertexStream);
|
||||||
|
|
||||||
|
InputStream fragmentStream = ResourceLoader.getResourceAsStream(fragmentShader);
|
||||||
|
fragmentString = convertStreamToString(fragmentStream);
|
||||||
|
} catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = String.format("%s/%s", vertexShader, fragmentShader);
|
||||||
|
this.Properties = new PropertyCollection(this);
|
||||||
|
|
||||||
|
this.programId = GL20.glCreateProgram();
|
||||||
|
|
||||||
|
this.shaderParts.put(GL20.glCreateShader(GL20.GL_VERTEX_SHADER), vertexString);
|
||||||
|
this.shaderParts.put(GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER), fragmentString);
|
||||||
|
this.CompileParts();
|
||||||
|
this.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shader(String shader, final int shaderType) {
|
||||||
|
if (shaderType != GL20.GL_FRAGMENT_SHADER && shaderType != GL20.GL_VERTEX_SHADER){
|
||||||
|
System.out.println("Invalid shader type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String shaderString;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream shaderStream = ResourceLoader.getResourceAsStream(shader);
|
||||||
|
shaderString = convertStreamToString(shaderStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = String.format("%s/%s",
|
||||||
|
shaderType == GL20.GL_FRAGMENT_SHADER ? "Fragment" : "Vertex",
|
||||||
|
shader);
|
||||||
|
this.Properties = new PropertyCollection(this);
|
||||||
|
|
||||||
|
this.programId = GL20.glCreateProgram();
|
||||||
|
|
||||||
|
this.shaderParts.put(GL20.glCreateShader(shaderType), shaderString);
|
||||||
|
this.CompileParts();
|
||||||
|
this.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CompileParts() {
|
||||||
|
for (Map.Entry<Integer, String> entry : this.shaderParts.entrySet()) {
|
||||||
|
int handle = entry.getKey();
|
||||||
|
String source = entry.getValue();
|
||||||
|
|
||||||
|
GL20.glShaderSource(handle, source);
|
||||||
|
GL20.glCompileShader(handle);
|
||||||
|
|
||||||
|
int res = GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS);
|
||||||
|
if (res != GL11.GL_TRUE) {
|
||||||
|
String error = GL20.glGetShaderInfoLog(handle, 1024);
|
||||||
|
Log.error("Shader compilation failed.", new Exception(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compile() {
|
||||||
|
this.Properties.Clear();
|
||||||
|
|
||||||
|
for (int handler : this.shaderParts.keySet()) {
|
||||||
|
GL20.glAttachShader(this.programId, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL20.glLinkProgram(this.programId);
|
||||||
|
|
||||||
|
int res = GL20.glGetProgrami(this.programId, GL20.GL_LINK_STATUS);
|
||||||
|
if (res != GL11.GL_TRUE) {
|
||||||
|
String error = GL20.glGetProgramInfoLog(this.programId, 1024);
|
||||||
|
Log.error("Program linking failed.", new Exception(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int handler : this.shaderParts.keySet()) {
|
||||||
|
GL20.glDeleteShader(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < attribs.length; i++) {
|
||||||
|
int attrib = GL20.glGetAttribLocation(this.programId, attribs[i]);
|
||||||
|
this.attribLoc.put(attribs[i], attrib);
|
||||||
|
//GL20.glBindAttribLocation(this.programId, i, attribLoc[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*for (int handler : this.shaderParts.keySet()) {
|
||||||
|
GL20.glDetachShader(this.programId, handler);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
this.loaded = res == GL11.GL_TRUE;
|
||||||
|
|
||||||
|
if (this.loaded) {
|
||||||
|
int uniCount = GL20.glGetProgrami(this.programId, GL20.GL_ACTIVE_UNIFORMS);
|
||||||
|
|
||||||
|
for (int i = 0; i < uniCount; i++) {
|
||||||
|
IntBuffer length = BufferUtils.createIntBuffer(1);
|
||||||
|
IntBuffer size = BufferUtils.createIntBuffer(1);
|
||||||
|
IntBuffer type = BufferUtils.createIntBuffer(1);
|
||||||
|
ByteBuffer name = ByteBuffer.allocateDirect(64);
|
||||||
|
|
||||||
|
GL20.glGetActiveUniform(this.programId, i, length, size, type, name);
|
||||||
|
|
||||||
|
|
||||||
|
byte[] buf = new byte[length.get(0)];
|
||||||
|
|
||||||
|
name.get(buf, 0, buf.length);
|
||||||
|
String propName = new String(buf);
|
||||||
|
int propLocation = GL20.glGetUniformLocation(programId, propName);
|
||||||
|
|
||||||
|
UniformType propUniformType = UniformType.map.get(type.get(0));
|
||||||
|
Class propType = ShaderProperty.ConvertType(propUniformType.name());
|
||||||
|
|
||||||
|
System.out.println(String.format("propName:%s PropUniformType:%s PropType:%s", propName, propUniformType, propType));
|
||||||
|
|
||||||
|
this.Properties.Add(new ShaderProperty(
|
||||||
|
propName,
|
||||||
|
propLocation,
|
||||||
|
propType,
|
||||||
|
propUniformType
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : globalProperties.entrySet()) {
|
||||||
|
this.Properties.Replace(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedShaders.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Begin(){
|
||||||
|
if (this.started || !this.loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GL20.glUseProgram(this.programId);
|
||||||
|
this.previousShader = currentShader;
|
||||||
|
currentShader = this.programId;
|
||||||
|
|
||||||
|
GL20.glEnableVertexAttribArray(this.attribLoc.get("m_Position"));
|
||||||
|
GL20.glEnableVertexAttribArray(this.attribLoc.get("m_TexCoord"));
|
||||||
|
|
||||||
|
//this.Properties.set("m_Sampler", 0);
|
||||||
|
|
||||||
|
this.started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End() {
|
||||||
|
if (!this.started)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GL11.glFlush();
|
||||||
|
GL20.glDisableVertexAttribArray(this.attribLoc.get("m_TexCoord"));
|
||||||
|
GL20.glDisableVertexAttribArray(this.attribLoc.get("m_Position"));
|
||||||
|
|
||||||
|
GL20.glUseProgram(this.previousShader);
|
||||||
|
currentShader = this.previousShader;
|
||||||
|
|
||||||
|
this.started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String convertStreamToString(InputStream is) {
|
||||||
|
Scanner s = new Scanner(is).useDelimiter("\\A");
|
||||||
|
return s.hasNext() ? s.next() : "";
|
||||||
|
}
|
||||||
|
}
|
134
src/com/osufx/sunpy/ShaderProperty.java
Normal file
134
src/com/osufx/sunpy/ShaderProperty.java
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package com.osufx.sunpy;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public class ShaderProperty {
|
||||||
|
public String Name;
|
||||||
|
public int Location;
|
||||||
|
|
||||||
|
public Class Type;
|
||||||
|
public UniformType ActiveUniformType;
|
||||||
|
|
||||||
|
public Object Value;
|
||||||
|
|
||||||
|
public ShaderProperty(String Name, int Location, Class Type, UniformType ActiveUniformType) {
|
||||||
|
this.Name = Name;
|
||||||
|
this.Location = Location;
|
||||||
|
this.Type = Type;
|
||||||
|
this.ActiveUniformType = ActiveUniformType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set() {
|
||||||
|
if (Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (this.ActiveUniformType) {
|
||||||
|
case Bool:
|
||||||
|
GL20.glUniform1(this.Location, IntBuffer.wrap( new int[]{ (boolean)Value ? 1 : 0 } ));
|
||||||
|
break;
|
||||||
|
case Int:
|
||||||
|
GL20.glUniform1(this.Location, IntBuffer.wrap( new int[]{ (int)Value } ));
|
||||||
|
break;
|
||||||
|
case Float:
|
||||||
|
GL20.glUniform1f(this.Location, (float)Value);
|
||||||
|
break;
|
||||||
|
case BoolVec2:
|
||||||
|
GL20.glUniform2(this.Location, IntBuffer.wrap( boolArrayToIntArray( (boolean[])Value ) ));
|
||||||
|
break;
|
||||||
|
case IntVec2:
|
||||||
|
GL20.glUniform2(this.Location, IntBuffer.wrap( (int[])Value ));
|
||||||
|
break;
|
||||||
|
case FloatVec2:
|
||||||
|
float[] value = (float[])Value;
|
||||||
|
GL20.glUniform2f(this.Location, value[0], value[1]);
|
||||||
|
break;
|
||||||
|
case FloatMat2:
|
||||||
|
GL20.glUniformMatrix2(this.Location, false, FloatBuffer.wrap( (float[])Value ));
|
||||||
|
break;
|
||||||
|
case BoolVec3:
|
||||||
|
GL20.glUniform3(this.Location, IntBuffer.wrap( boolArrayToIntArray( (boolean[])Value ) ));
|
||||||
|
break;
|
||||||
|
case IntVec3:
|
||||||
|
GL20.glUniform3(this.Location, IntBuffer.wrap( (int[])Value ));
|
||||||
|
break;
|
||||||
|
case FloatVec3:
|
||||||
|
GL20.glUniform3(this.Location, FloatBuffer.wrap( (float[])Value ));
|
||||||
|
break;
|
||||||
|
case FloatMat3:
|
||||||
|
GL20.glUniformMatrix3(this.Location, false, FloatBuffer.wrap( (float[])Value ));
|
||||||
|
break;
|
||||||
|
case BoolVec4:
|
||||||
|
GL20.glUniform4(this.Location, IntBuffer.wrap( boolArrayToIntArray( (boolean[])Value ) ));
|
||||||
|
break;
|
||||||
|
case IntVec4:
|
||||||
|
GL20.glUniform4(this.Location, IntBuffer.wrap( (int[])Value ));
|
||||||
|
break;
|
||||||
|
case FloatVec4:
|
||||||
|
GL20.glUniform4(this.Location, FloatBuffer.wrap( (float[])Value ));
|
||||||
|
break;
|
||||||
|
case FloatMat4:
|
||||||
|
GL20.glUniformMatrix4(this.Location, false, FloatBuffer.wrap( (float[])Value ));
|
||||||
|
break;
|
||||||
|
case Sampler2D:
|
||||||
|
GL20.glUniform1i(this.Location, (int) Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class ConvertType(String type) {
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
//throw new Exception(String.format("Uniform type %s is not supported.", type));
|
||||||
|
System.out.println(String.format("Uniform type %s is not supported.", type));
|
||||||
|
return null;
|
||||||
|
case "Bool":
|
||||||
|
return Boolean.class;
|
||||||
|
case "Int":
|
||||||
|
return Integer.class;
|
||||||
|
case "Float":
|
||||||
|
return Float.class;
|
||||||
|
case "BoolVec2":
|
||||||
|
return boolean[].class;
|
||||||
|
case "IntVec2":
|
||||||
|
return int[].class;
|
||||||
|
case "FloatVec2":
|
||||||
|
return float[].class;
|
||||||
|
case "BoolVec3":
|
||||||
|
return boolean[].class;
|
||||||
|
case "IntVec3":
|
||||||
|
return int[].class;
|
||||||
|
case "FloatVec3":
|
||||||
|
return float[].class;
|
||||||
|
case "BoolVec4":
|
||||||
|
return boolean[].class;
|
||||||
|
case "IntVec4":
|
||||||
|
return int[].class;
|
||||||
|
case "FloatVec4":
|
||||||
|
return float[].class;
|
||||||
|
case "FloatMat2":
|
||||||
|
return float[].class;
|
||||||
|
case "FloatMat3":
|
||||||
|
return float[].class;
|
||||||
|
case "FloatMat4":
|
||||||
|
return float[].class;
|
||||||
|
case "Sampler2D":
|
||||||
|
case "SamplerCube":
|
||||||
|
return Integer.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public static Type ConvertType(int type) {
|
||||||
|
(UniformType)24;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static int[] boolArrayToIntArray(boolean[] array) {
|
||||||
|
int[] value = new int[array.length];
|
||||||
|
for (int i = 0; i < array.length; i++)
|
||||||
|
value[i] = array[i] ? 1 : 0;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
39
src/com/osufx/sunpy/UniformType.java
Normal file
39
src/com/osufx/sunpy/UniformType.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package com.osufx.sunpy;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public enum UniformType {
|
||||||
|
Int(GL11.GL_INT),
|
||||||
|
Float(GL11.GL_FLOAT),
|
||||||
|
FloatVec2(GL20.GL_FLOAT_VEC2),
|
||||||
|
FloatVec3(GL20.GL_FLOAT_VEC3),
|
||||||
|
FloatVec4(GL20.GL_FLOAT_VEC4),
|
||||||
|
IntVec2(GL20.GL_INT_VEC2),
|
||||||
|
IntVec3(GL20.GL_INT_VEC3),
|
||||||
|
IntVec4(GL20.GL_INT_VEC4),
|
||||||
|
Bool(GL20.GL_BOOL),
|
||||||
|
BoolVec2(GL20.GL_BOOL_VEC2),
|
||||||
|
BoolVec3(GL20.GL_BOOL_VEC3),
|
||||||
|
BoolVec4(GL20.GL_BOOL_VEC4),
|
||||||
|
FloatMat2(GL20.GL_FLOAT_MAT2),
|
||||||
|
FloatMat3(GL20.GL_FLOAT_MAT3),
|
||||||
|
FloatMat4(GL20.GL_FLOAT_MAT4),
|
||||||
|
Sampler2D(GL20.GL_SAMPLER_2D),
|
||||||
|
SamplerCube(GL20.GL_SAMPLER_CUBE);
|
||||||
|
|
||||||
|
private final int glIndex;
|
||||||
|
|
||||||
|
public static HashMap<Integer, UniformType> map = new HashMap<>();
|
||||||
|
static {
|
||||||
|
for (UniformType v : UniformType.values()) {
|
||||||
|
map.put(v.glIndex, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformType(int glIndex) {
|
||||||
|
this.glIndex = glIndex;
|
||||||
|
}
|
||||||
|
}
|
|
@ -170,7 +170,7 @@ public class GameData {
|
||||||
HIT_ANIMATION_RESULT = 12; // not a hit result
|
HIT_ANIMATION_RESULT = 12; // not a hit result
|
||||||
|
|
||||||
/** Hit result-related images (indexed by HIT_* constants to HIT_MAX). */
|
/** Hit result-related images (indexed by HIT_* constants to HIT_MAX). */
|
||||||
private Image[] hitResults;
|
public static Image[] hitResults;
|
||||||
|
|
||||||
/** Counts of each hit result so far (indexed by HIT_* constants to HIT_MAX). */
|
/** Counts of each hit result so far (indexed by HIT_* constants to HIT_MAX). */
|
||||||
private int[] hitResultCount;
|
private int[] hitResultCount;
|
||||||
|
@ -1222,7 +1222,7 @@ public class GameData {
|
||||||
/**
|
/**
|
||||||
* Increases the combo streak by one.
|
* Increases the combo streak by one.
|
||||||
*/
|
*/
|
||||||
private void incrementComboStreak() {
|
protected void incrementComboStreak() {
|
||||||
combo++;
|
combo++;
|
||||||
comboPopTime = 0;
|
comboPopTime = 0;
|
||||||
if (combo > comboMax)
|
if (combo > comboMax)
|
||||||
|
@ -1404,7 +1404,7 @@ public class GameData {
|
||||||
* @param noIncrementCombo if the combo should not be incremented by this result
|
* @param noIncrementCombo if the combo should not be incremented by this result
|
||||||
* @return the actual hit result (HIT_* constants)
|
* @return the actual hit result (HIT_* constants)
|
||||||
*/
|
*/
|
||||||
private int handleHitResult(int time, int result, float x, float y, Color color, boolean end,
|
protected int handleHitResult(int time, int result, float x, float y, Color color, boolean end,
|
||||||
HitObject hitObject, HitObjectType hitResultType, int repeat, boolean noIncrementCombo) {
|
HitObject hitObject, HitObjectType hitResultType, int repeat, boolean noIncrementCombo) {
|
||||||
// update health, score, and combo streak based on hit result
|
// update health, score, and combo streak based on hit result
|
||||||
int hitValue = 0;
|
int hitValue = 0;
|
||||||
|
|
|
@ -83,6 +83,13 @@ public class Circle extends GameObject {
|
||||||
super.updateStartEndPositions(time);
|
super.updateStartEndPositions(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameObject clone(GameData data) {
|
||||||
|
Circle c = new Circle(hitObject, game, data, comboColorIndex, comboEnd);
|
||||||
|
c.isreplay = true;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
||||||
Color orig = color;
|
Color orig = color;
|
||||||
|
@ -165,12 +172,13 @@ public class Circle extends GameObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isreplay;
|
||||||
@Override
|
@Override
|
||||||
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
|
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
|
||||||
int time = hitObject.getTime();
|
int time = hitObject.getTime();
|
||||||
|
|
||||||
int[] hitResultOffset = game.getHitResultOffsets();
|
int[] hitResultOffset = game.getHitResultOffsets();
|
||||||
boolean isAutoMod = GameMod.AUTO.isActive();
|
boolean isAutoMod = !isreplay && GameMod.AUTO.isActive();
|
||||||
|
|
||||||
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
|
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
|
||||||
if (isAutoMod) {// "auto" mod: catch any missed notes due to lag
|
if (isAutoMod) {// "auto" mod: catch any missed notes due to lag
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu.objects;
|
package itdelatrisu.opsu.objects;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.GameData;
|
||||||
import itdelatrisu.opsu.beatmap.HitObject;
|
import itdelatrisu.opsu.beatmap.HitObject;
|
||||||
import itdelatrisu.opsu.objects.curves.Vec2f;
|
import itdelatrisu.opsu.objects.curves.Vec2f;
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ import org.newdawn.slick.Graphics;
|
||||||
*/
|
*/
|
||||||
public class DummyObject extends GameObject {
|
public class DummyObject extends GameObject {
|
||||||
/** The associated HitObject. */
|
/** The associated HitObject. */
|
||||||
private HitObject hitObject;
|
public final HitObject hitObject;
|
||||||
|
|
||||||
/** The scaled starting x, y coordinates. */
|
/** The scaled starting x, y coordinates. */
|
||||||
private float x, y;
|
private float x, y;
|
||||||
|
@ -49,6 +50,11 @@ public class DummyObject extends GameObject {
|
||||||
updateStartEndPositions(0);
|
updateStartEndPositions(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameObject clone(GameData a) {
|
||||||
|
return new DummyObject(this.hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition, boolean mirror) {}
|
public void draw(Graphics g, int trackPosition, boolean mirror) {}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu.objects;
|
package itdelatrisu.opsu.objects;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.GameData;
|
||||||
import itdelatrisu.opsu.objects.curves.Vec2f;
|
import itdelatrisu.opsu.objects.curves.Vec2f;
|
||||||
|
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
|
@ -114,4 +115,6 @@ public abstract class GameObject {
|
||||||
return hue;
|
return hue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract GameObject clone(GameData data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,13 @@ public class Slider extends GameObject {
|
||||||
repeats = hitObject.getRepeatCount();
|
repeats = hitObject.getRepeatCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameObject clone(GameData data) {
|
||||||
|
Slider s = new Slider(hitObject, game, data, comboColorIndex, comboEnd);
|
||||||
|
s.isreplay = true;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
||||||
if (trackPosition > getEndTime()) {
|
if (trackPosition > getEndTime()) {
|
||||||
|
@ -630,11 +637,12 @@ public class Slider extends GameObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isreplay;
|
||||||
@Override
|
@Override
|
||||||
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
|
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
|
||||||
int repeatCount = hitObject.getRepeatCount();
|
int repeatCount = hitObject.getRepeatCount();
|
||||||
int[] hitResultOffset = game.getHitResultOffsets();
|
int[] hitResultOffset = game.getHitResultOffsets();
|
||||||
boolean isAutoMod = GameMod.AUTO.isActive();
|
boolean isAutoMod = !isreplay && GameMod.AUTO.isActive();
|
||||||
|
|
||||||
if (!sliderClickedInitial) {
|
if (!sliderClickedInitial) {
|
||||||
int time = hitObject.getTime();
|
int time = hitObject.getTime();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu.objects;
|
package itdelatrisu.opsu.objects;
|
||||||
|
|
||||||
|
import awlex.ospu.FakeGameObject;
|
||||||
import itdelatrisu.opsu.GameData;
|
import itdelatrisu.opsu.GameData;
|
||||||
import itdelatrisu.opsu.GameData.HitObjectType;
|
import itdelatrisu.opsu.GameData.HitObjectType;
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
|
@ -170,6 +171,12 @@ public class Spinner extends GameObject {
|
||||||
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
|
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameObject clone(GameData data) {
|
||||||
|
//return new DummyObject(hitObject);
|
||||||
|
return new Spinner(hitObject, game, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
public void draw(Graphics g, int trackPosition, boolean mirror) {
|
||||||
if (mirror) {
|
if (mirror) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package itdelatrisu.opsu.render;
|
package itdelatrisu.opsu.render;
|
||||||
|
|
||||||
|
import com.osufx.sunpy.Shader;
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
import itdelatrisu.opsu.Utils;
|
import itdelatrisu.opsu.Utils;
|
||||||
import itdelatrisu.opsu.beatmap.HitObject;
|
import itdelatrisu.opsu.beatmap.HitObject;
|
||||||
|
@ -248,7 +249,7 @@ public class CurveRenderState {
|
||||||
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||||
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
|
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
|
||||||
|
|
||||||
GL20.glUseProgram(0);
|
GL20.glUseProgram(Shader.currentShader);
|
||||||
|
|
||||||
GL11.glMatrixMode(GL11.GL_PROJECTION);
|
GL11.glMatrixMode(GL11.GL_PROJECTION);
|
||||||
GL11.glPushMatrix();
|
GL11.glPushMatrix();
|
||||||
|
|
|
@ -45,6 +45,8 @@ import itdelatrisu.opsu.ui.animations.AnimatedValue;
|
||||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
|
@ -57,6 +59,7 @@ import org.newdawn.slick.Input;
|
||||||
import org.newdawn.slick.SlickException;
|
import org.newdawn.slick.SlickException;
|
||||||
import org.newdawn.slick.util.Log;
|
import org.newdawn.slick.util.Log;
|
||||||
import yugecin.opsudance.*;
|
import yugecin.opsudance.*;
|
||||||
|
import yugecin.opsudance.ReplayPlayback.HitData;
|
||||||
import yugecin.opsudance.core.state.ComplexOpsuState;
|
import yugecin.opsudance.core.state.ComplexOpsuState;
|
||||||
import yugecin.opsudance.objects.curves.FakeCombinedCurve;
|
import yugecin.opsudance.objects.curves.FakeCombinedCurve;
|
||||||
import yugecin.opsudance.options.OptionGroups;
|
import yugecin.opsudance.options.OptionGroups;
|
||||||
|
@ -304,7 +307,7 @@ public class Game extends ComplexOpsuState {
|
||||||
MUSICBAR_HOVER = new Color(12, 9, 10, 0.35f),
|
MUSICBAR_HOVER = new Color(12, 9, 10, 0.35f),
|
||||||
MUSICBAR_FILL = new Color(255, 255, 255, 0.75f);
|
MUSICBAR_FILL = new Color(255, 255, 255, 0.75f);
|
||||||
|
|
||||||
private final Cursor mirrorCursor;
|
private Cursor mirrorCursor;
|
||||||
private final MoveStoryboard moveStoryboardOverlay;
|
private final MoveStoryboard moveStoryboardOverlay;
|
||||||
private final StoryboardOverlay storyboardOverlay;
|
private final StoryboardOverlay storyboardOverlay;
|
||||||
private final OptionsOverlay optionsOverlay;
|
private final OptionsOverlay optionsOverlay;
|
||||||
|
@ -315,7 +318,7 @@ public class Game extends ComplexOpsuState {
|
||||||
|
|
||||||
public Game() {
|
public Game() {
|
||||||
super();
|
super();
|
||||||
mirrorCursor = new Cursor(true);
|
//mirrorCursor = new Cursor(true);
|
||||||
this.moveStoryboardOverlay = new MoveStoryboard();
|
this.moveStoryboardOverlay = new MoveStoryboard();
|
||||||
this.optionsOverlay = new OptionsOverlay(OptionGroups.storyboardOptions);
|
this.optionsOverlay = new OptionsOverlay(OptionGroups.storyboardOptions);
|
||||||
this.storyboardOverlay = new StoryboardOverlay(moveStoryboardOverlay, optionsOverlay, this);
|
this.storyboardOverlay = new StoryboardOverlay(moveStoryboardOverlay, optionsOverlay, this);
|
||||||
|
@ -722,6 +725,28 @@ public class Game extends ComplexOpsuState {
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.draw(g);
|
UI.draw(g);
|
||||||
|
|
||||||
|
if (replayCursors == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//g.setColor(new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
//g.fillRect(0, 0, ReplayPlayback.SQSIZE * 2, displayContainer.height);
|
||||||
|
//g.setColor(Color.black);
|
||||||
|
//g.fillRect(ReplayPlayback.SQSIZE * 2, 0, ReplayPlayback.SQSIZE * 2, displayContainer.height);
|
||||||
|
float totalHeight = 0f;
|
||||||
|
for (ReplayPlayback replayPlayback : replays) {
|
||||||
|
totalHeight += replayPlayback.getHeight();
|
||||||
|
}
|
||||||
|
float ypos = (height - totalHeight) / 2f - ReplayPlayback.lineHeight;
|
||||||
|
for (ReplayPlayback replayPlayback : replays) {
|
||||||
|
float h = replayPlayback.getHeight();
|
||||||
|
ypos += h;
|
||||||
|
//if (h > 0f) {
|
||||||
|
replayPlayback.render(renderDelta, g, ypos, trackPosition);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
replayCursors.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -910,6 +935,7 @@ public class Game extends ComplexOpsuState {
|
||||||
|
|
||||||
// set mouse coordinates
|
// set mouse coordinates
|
||||||
autoMousePosition.set(autoPoint.x, autoPoint.y);
|
autoMousePosition.set(autoPoint.x, autoPoint.y);
|
||||||
|
autoMousePosition.set(-100, -100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isReplay) {
|
if (isReplay) {
|
||||||
|
@ -1454,6 +1480,16 @@ public class Game extends ComplexOpsuState {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ReplayData {
|
||||||
|
final HitData hitdata;
|
||||||
|
final Replay replay;
|
||||||
|
ReplayData(HitData hitdata, Replay replay) {
|
||||||
|
this.hitdata = hitdata;
|
||||||
|
this.replay = replay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private ReplayPlayback[] replays;
|
||||||
|
private ReplayCursors replayCursors;
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
overlays.clear();
|
overlays.clear();
|
||||||
|
@ -1465,7 +1501,84 @@ public class Game extends ComplexOpsuState {
|
||||||
|
|
||||||
super.enter();
|
super.enter();
|
||||||
|
|
||||||
displayContainer.drawCursor = false;
|
/*
|
||||||
|
File replaydir = new File("d:/Users/Robin/games/osu/osr-stuff-master/xi/");
|
||||||
|
if (!replaydir.exists()) {
|
||||||
|
bubNotifs.sendf(Colors.BUB_RED, "replay folder '%s' does not exist", replaydir.getAbsolutePath());
|
||||||
|
displayContainer.switchStateInstantly(songMenuState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = replaydir.listFiles(new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname) {
|
||||||
|
return pathname.getName().endsWith(".osr");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (replayCursors != null) {
|
||||||
|
replayCursors.destroy();
|
||||||
|
}
|
||||||
|
final ArrayList<ReplayData> rdata = new ArrayList<>(50);
|
||||||
|
for (File file : files) {
|
||||||
|
final String datafilename = file.getName().substring(0, file.getName().length() - 3) + "ope";
|
||||||
|
final File hitdatafile = new File(file.getParentFile(), datafilename);
|
||||||
|
if (!hitdatafile.exists()) {
|
||||||
|
bubNotifs.sendf(Colors.BUB_RED, "no hitdata file for %s", file.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final ReplayPlayback.HitData hitdata;
|
||||||
|
try {
|
||||||
|
hitdata = new ReplayPlayback.HitData(hitdatafile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
bubNotifs.sendf(
|
||||||
|
Colors.BUB_RED,
|
||||||
|
"cannot parse hitdata for '%s': %s",
|
||||||
|
hitdatafile.getName(),
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Replay r = new Replay(file);
|
||||||
|
try {
|
||||||
|
r.load();
|
||||||
|
} catch (IOException e) {
|
||||||
|
bubNotifs.sendf(Colors.BUB_RED, "could not load replay %s", file.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rdata.add(new ReplayData(hitdata, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
rdata.sort(new Comparator<ReplayData>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ReplayData o1, ReplayData o2) {
|
||||||
|
return Integer.compare(o2.replay.score, o1.replay.score);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
replayCursors = new ReplayCursors(rdata.size());
|
||||||
|
replays = new ReplayPlayback[rdata.size()];
|
||||||
|
|
||||||
|
float hueshift = 360f / rdata.size();
|
||||||
|
float hue = 180;
|
||||||
|
|
||||||
|
int replayidx = 0;
|
||||||
|
for (ReplayData d : rdata) {
|
||||||
|
final Color c = new Color(java.awt.Color.HSBtoRGB((hue) / 360f, .7f, 1.0f));
|
||||||
|
final ReplayCursor cursor = new ReplayCursor(c);
|
||||||
|
replays[replayidx] = new ReplayPlayback(
|
||||||
|
d.replay,
|
||||||
|
d.hitdata,
|
||||||
|
c,
|
||||||
|
cursor
|
||||||
|
);
|
||||||
|
replayCursors.playbacks[replayidx] = replays[replayidx];
|
||||||
|
|
||||||
|
hue += hueshift;
|
||||||
|
replayidx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayContainer.drawCursor = false;*/
|
||||||
|
|
||||||
isInGame = true;
|
isInGame = true;
|
||||||
if (!skippedToCheckpoint) {
|
if (!skippedToCheckpoint) {
|
||||||
|
@ -1749,6 +1862,12 @@ public class Game extends ComplexOpsuState {
|
||||||
|
|
||||||
knorkesliders = null;
|
knorkesliders = null;
|
||||||
|
|
||||||
|
if (replayCursors != null) {
|
||||||
|
replayCursors.destroy();
|
||||||
|
replayCursors = null;
|
||||||
|
replays = null;
|
||||||
|
}
|
||||||
|
|
||||||
Dancer.instance.setGameObjects(null);
|
Dancer.instance.setGameObjects(null);
|
||||||
|
|
||||||
Cursor.lastObjColor = Color.white;
|
Cursor.lastObjColor = Color.white;
|
||||||
|
@ -1766,10 +1885,6 @@ public class Game extends ComplexOpsuState {
|
||||||
GameMod.loadModState(previousMods);
|
GameMod.loadModState(previousMods);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjusts the beatmap's local music offset.
|
|
||||||
* @param sign the sign (multiplier)
|
|
||||||
*/
|
|
||||||
public void adjustLocalMusicOffset(int amount) {
|
public void adjustLocalMusicOffset(int amount) {
|
||||||
int newOffset = beatmap.localMusicOffset + amount;
|
int newOffset = beatmap.localMusicOffset + amount;
|
||||||
barNotifs.send(String.format("Local beatmap offset set to %dms", newOffset));
|
barNotifs.send(String.format("Local beatmap offset set to %dms", newOffset));
|
||||||
|
|
|
@ -18,18 +18,31 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu.ui;
|
package itdelatrisu.opsu.ui;
|
||||||
|
|
||||||
|
import com.osufx.sunpy.Shader;
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
import itdelatrisu.opsu.Utils;
|
import itdelatrisu.opsu.Utils;
|
||||||
|
import itdelatrisu.opsu.render.Rendertarget;
|
||||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||||
|
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
import org.newdawn.slick.*;
|
import org.newdawn.slick.*;
|
||||||
|
import org.newdawn.slick.opengl.Texture;
|
||||||
|
import org.newdawn.slick.opengl.TextureImpl;
|
||||||
|
|
||||||
import yugecin.opsudance.Dancer;
|
import yugecin.opsudance.Dancer;
|
||||||
|
import yugecin.opsudance.ReplayPlayback;
|
||||||
import yugecin.opsudance.skinning.SkinService;
|
import yugecin.opsudance.skinning.SkinService;
|
||||||
|
|
||||||
import static yugecin.opsudance.options.Options.*;
|
import static yugecin.opsudance.options.Options.*;
|
||||||
|
import static itdelatrisu.opsu.GameImage.CURSOR;
|
||||||
|
import static itdelatrisu.opsu.GameImage.CURSOR_MIDDLE;
|
||||||
|
import static itdelatrisu.opsu.GameImage.CURSOR_TRAIL;
|
||||||
import static yugecin.opsudance.core.InstanceContainer.*;
|
import static yugecin.opsudance.core.InstanceContainer.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,8 +68,7 @@ public class Cursor {
|
||||||
/** The time it takes for the cursor to scale, in milliseconds. */
|
/** The time it takes for the cursor to scale, in milliseconds. */
|
||||||
private static final float CURSOR_SCALE_TIME = 125;
|
private static final float CURSOR_SCALE_TIME = 125;
|
||||||
|
|
||||||
/** Stores all previous cursor locations to display a trail. */
|
private TrailList trail = new TrailList();
|
||||||
private LinkedList<Point> trail = new LinkedList<>();
|
|
||||||
|
|
||||||
private boolean newStyle;
|
private boolean newStyle;
|
||||||
|
|
||||||
|
@ -68,6 +80,12 @@ public class Cursor {
|
||||||
|
|
||||||
private boolean isMirrored;
|
private boolean isMirrored;
|
||||||
|
|
||||||
|
private Color filter;
|
||||||
|
|
||||||
|
private Rendertarget fbo;
|
||||||
|
|
||||||
|
private Shader cursorTrailShader;
|
||||||
|
|
||||||
public Cursor() {
|
public Cursor() {
|
||||||
this(false);
|
this(false);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +93,14 @@ public class Cursor {
|
||||||
public Cursor(boolean isMirrored) {
|
public Cursor(boolean isMirrored) {
|
||||||
resetLocations(0, 0);
|
resetLocations(0, 0);
|
||||||
this.isMirrored = isMirrored;
|
this.isMirrored = isMirrored;
|
||||||
|
this.lastPosition = new Point(width2, 0);
|
||||||
|
this.filter = new Color(255,255,255);
|
||||||
|
this.cursorTrailShader = new Shader("sh_Cursor.vertex", "sh_Texture.frag");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cursor(Color filter) {
|
||||||
|
this(false);
|
||||||
|
this.filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,6 +108,10 @@ public class Cursor {
|
||||||
* @param mousePressed whether or not the mouse button is pressed
|
* @param mousePressed whether or not the mouse button is pressed
|
||||||
*/
|
*/
|
||||||
public void draw(boolean mousePressed) {
|
public void draw(boolean mousePressed) {
|
||||||
|
if (fbo == null) {
|
||||||
|
this.fbo = Rendertarget.createRTTFramebuffer(width, height);
|
||||||
|
}
|
||||||
|
/*
|
||||||
if (OPTION_DISABLE_CURSOR.state) {
|
if (OPTION_DISABLE_CURSOR.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +161,10 @@ public class Cursor {
|
||||||
lastCursorColor = filter = Dancer.cursorColorOverride.getColor();
|
lastCursorColor = filter = Dancer.cursorColorOverride.getColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.filter != null) {
|
||||||
|
filter = this.filter;
|
||||||
|
}
|
||||||
|
|
||||||
// draw a fading trail
|
// draw a fading trail
|
||||||
float alpha = 0f;
|
float alpha = 0f;
|
||||||
float t = 2f / trail.size();
|
float t = 2f / trail.size();
|
||||||
|
@ -139,7 +173,7 @@ public class Cursor {
|
||||||
cursorTrail.startUse();
|
cursorTrail.startUse();
|
||||||
for (Point p : trail) {
|
for (Point p : trail) {
|
||||||
alpha += t;
|
alpha += t;
|
||||||
cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha);
|
cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha * 0.25f);
|
||||||
cursorTrail.drawEmbedded(
|
cursorTrail.drawEmbedded(
|
||||||
p.x - (cursorTrailWidth / 2f), p.y - (cursorTrailHeight / 2f),
|
p.x - (cursorTrailWidth / 2f), p.y - (cursorTrailHeight / 2f),
|
||||||
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
|
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
|
||||||
|
@ -157,6 +191,110 @@ public class Cursor {
|
||||||
if (hasMiddle) {
|
if (hasMiddle) {
|
||||||
cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL.state ? Color.white : filter);
|
cursorMiddle.drawCentered(lastPosition.x, lastPosition.y, OPTION_DANCE_CURSOR_ONLY_COLOR_TRAIL.state ? Color.white : filter);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
final Image img = CURSOR_TRAIL.getImage();
|
||||||
|
final Texture txt = img.getTexture();
|
||||||
|
|
||||||
|
final float trailw2 = img.getWidth() * OPTION_CURSOR_SIZE.val / 100f / 2f;
|
||||||
|
final float trailh2 = img.getHeight() * OPTION_CURSOR_SIZE.val / 100f / 2f;
|
||||||
|
float txtw = txt.getWidth();
|
||||||
|
float txth = txt.getHeight();
|
||||||
|
|
||||||
|
//img.draw(0f,0f);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// 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);
|
||||||
|
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);
|
||||||
|
GL11.glClearColor(0f, 0f, 0f, 0f);
|
||||||
|
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||||
|
*/
|
||||||
|
//Color.white.bind();
|
||||||
|
|
||||||
|
|
||||||
|
//GL20.glVertexAttrib4f(cursorTrailShader.attribLoc.get("m_Position"), 1f, 1f, 1f, 1f);
|
||||||
|
|
||||||
|
|
||||||
|
//GL11.glBindTexture(GL11.GL_TEXTURE_2D, );
|
||||||
|
//cursorTrailShader.Properties.set("m_Sampler", txt.getTextureID());
|
||||||
|
|
||||||
|
//GL20.glUniform1i(cursorTrailShader.attribLoc.get("m_Sampler"), txt.getTextureID());
|
||||||
|
//IntBuffer buf = BufferUtils.createIntBuffer(1).put(txt.getTextureID());
|
||||||
|
|
||||||
|
cursorTrailShader.Begin();
|
||||||
|
GL13.glActiveTexture(GL13.GL_TEXTURE5);
|
||||||
|
//GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
//GL11.glGenTextures();
|
||||||
|
|
||||||
|
|
||||||
|
//txt.bind();
|
||||||
|
//TextureImpl.unbind();
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, txt.getTextureID());
|
||||||
|
//GL33.glBindSampler(GL20.GL_SAMPLER_2D, txt.getTextureID());
|
||||||
|
//GL20.glUniform1i(cursorTrailShader.attribLoc.get("m_Sampler"), 0);
|
||||||
|
|
||||||
|
//cursorTrailShader.Properties.set("m_TexCoord", new float[] { txtw, txth });
|
||||||
|
int idd = GL20.glGetAttribLocation(cursorTrailShader.programId, "m_TexCoord");
|
||||||
|
|
||||||
|
cursorTrailShader.Properties.set("m_Sampler", 5);
|
||||||
|
//GL20.glUniform1i(cursorTrailShader.Properties.get("m_Sampler").Location, 5);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cursorTrailShader.Properties.set("g_FadeClock", 0.04f);
|
||||||
|
GL11.glBegin(GL11.GL_QUADS);
|
||||||
|
float alpha = 0f;
|
||||||
|
float alphaIncrease = .4f / trail.size;
|
||||||
|
for (Trailpart p : trail) {
|
||||||
|
//alpha += alphaIncrease;
|
||||||
|
//GL11.glColor4f(filter.r, filter.g, filter.b, 1f);
|
||||||
|
GL11.glTexCoord2f(0f, 0f);
|
||||||
|
GL20.glVertexAttrib2f(idd, 0f, 0f);
|
||||||
|
GL11.glVertex3f(p.x - trailw2, p.y - trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(txtw, 0);
|
||||||
|
GL20.glVertexAttrib2f(idd, txtw, 0);
|
||||||
|
GL11.glVertex3f(p.x + trailw2, p.y - trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(txtw, txth);
|
||||||
|
GL20.glVertexAttrib2f(idd, txtw, txth);
|
||||||
|
GL11.glVertex3f(p.x + trailw2, p.y + trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(0f, txth);
|
||||||
|
GL20.glVertexAttrib2f(idd, 0f, txth);
|
||||||
|
GL11.glVertex3f(p.x - trailw2, p.y + trailh2, 0f);
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
GL11.glEnd();
|
||||||
|
cursorTrailShader.End();
|
||||||
|
|
||||||
|
GL13.glActiveTexture(GL13.GL_TEXTURE0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
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));
|
||||||
|
|
||||||
|
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.getScaledImage(OPTION_CURSOR_SIZE.val / 100f).drawCentered(lastPosition.x, lastPosition.y, filter);
|
||||||
|
CURSOR_MIDDLE.getScaledImage(OPTION_CURSOR_SIZE.val / 100f).drawCentered(lastPosition.x, lastPosition.y, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,34 +303,22 @@ public class Cursor {
|
||||||
* @param mouseY y coordinate to set position to
|
* @param mouseY y coordinate to set position to
|
||||||
*/
|
*/
|
||||||
public void setCursorPosition(int delta, int mouseX, int mouseY) {
|
public void setCursorPosition(int delta, int mouseX, int mouseY) {
|
||||||
// TODO: use an image buffer
|
nowtime = System.currentTimeMillis();
|
||||||
int removeCount = 0;
|
|
||||||
float FPSmod = Math.max(1000 / Math.max(delta, 1), 1) / 30f; // TODO
|
|
||||||
if (newStyle) {
|
|
||||||
// new style: add all points between cursor movements
|
|
||||||
if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) {
|
|
||||||
trail.add(new Point(mouseX, mouseY));
|
|
||||||
}
|
|
||||||
lastPosition.move(mouseX, mouseY);
|
|
||||||
|
|
||||||
removeCount = (int) (trail.size() / (6 * FPSmod)) + 1;
|
addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY);
|
||||||
} else {
|
lastPosition.move(mouseX, mouseY);
|
||||||
// old style: sample one point at a time
|
|
||||||
trail.add(new Point(mouseX, mouseY));
|
|
||||||
|
|
||||||
int max = (int) (10 * FPSmod);
|
int removecount = 0;
|
||||||
if (trail.size() > max)
|
TrailNode newfirst = trail.first;
|
||||||
removeCount = trail.size() - max;
|
while (newfirst != null && newfirst.value.time < nowtime - 175) {
|
||||||
|
newfirst = newfirst.next;
|
||||||
|
removecount++;
|
||||||
}
|
}
|
||||||
|
trail.first = newfirst;
|
||||||
int cursortraillength = OPTION_DANCE_CURSOR_TRAIL_OVERRIDE.val;
|
if (newfirst == null) {
|
||||||
if (cursortraillength > 20) {
|
trail.last = null;
|
||||||
removeCount = trail.size() - cursortraillength;
|
|
||||||
}
|
}
|
||||||
|
trail.size -= removecount;
|
||||||
// remove points from the lists
|
|
||||||
for (int i = 0; i < removeCount && !trail.isEmpty(); i++)
|
|
||||||
trail.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,8 +326,8 @@ public class Cursor {
|
||||||
* @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
|
* @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
|
||||||
*/
|
*/
|
||||||
private boolean addCursorPoints(int x1, int y1, int x2, int y2) {
|
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
|
// delta of exact value and rounded value of the dependent variable
|
||||||
boolean added = false;
|
|
||||||
int d = 0;
|
int d = 0;
|
||||||
int dy = Math.abs(y2 - y1);
|
int dy = Math.abs(y2 - y1);
|
||||||
int dx = Math.abs(x2 - x1);
|
int dx = Math.abs(x2 - x1);
|
||||||
|
@ -211,16 +337,11 @@ public class Cursor {
|
||||||
int ix = x1 < x2 ? 1 : -1; // increment direction
|
int ix = x1 < x2 ? 1 : -1; // increment direction
|
||||||
int iy = y1 < y2 ? 1 : -1;
|
int iy = y1 < y2 ? 1 : -1;
|
||||||
|
|
||||||
int k = 5; // sample size
|
|
||||||
if (dy <= dx) {
|
if (dy <= dx) {
|
||||||
for (int i = 0; ; i++) {
|
for (;;) {
|
||||||
if (i == k) {
|
|
||||||
trail.add(new Point(x1, y1));
|
|
||||||
added = true;
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
if (x1 == x2)
|
if (x1 == x2)
|
||||||
break;
|
break;
|
||||||
|
trail.add(new Trailpart(x1, y1));
|
||||||
x1 += ix;
|
x1 += ix;
|
||||||
d += dy2;
|
d += dy2;
|
||||||
if (d > dx) {
|
if (d > dx) {
|
||||||
|
@ -229,14 +350,10 @@ public class Cursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; ; i++) {
|
for (;;) {
|
||||||
if (i == k) {
|
|
||||||
trail.add(new Point(x1, y1));
|
|
||||||
added = true;
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
if (y1 == y2)
|
if (y1 == y2)
|
||||||
break;
|
break;
|
||||||
|
trail.add(new Trailpart(x1, y1));
|
||||||
y1 += iy;
|
y1 += iy;
|
||||||
d += dx2;
|
d += dx2;
|
||||||
if (d > dy) {
|
if (d > dy) {
|
||||||
|
@ -245,7 +362,64 @@ public class Cursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return added;
|
return trail.size != size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TrailList implements Iterable<Trailpart>
|
||||||
|
{
|
||||||
|
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<Trailpart> iterator()
|
||||||
|
{
|
||||||
|
return new Iterator<Trailpart>() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,11 +452,7 @@ public class Cursor {
|
||||||
* Resets all cursor location data.
|
* Resets all cursor location data.
|
||||||
*/
|
*/
|
||||||
public void resetLocations(int mouseX, int mouseY) {
|
public void resetLocations(int mouseX, int mouseY) {
|
||||||
trail.clear();
|
|
||||||
lastPosition = new Point(mouseX, mouseY);
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
trail.add(new Point(lastPosition));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
191
src/yugecin/opsudance/ReplayCursor.java
Normal file
191
src/yugecin/opsudance/ReplayCursor.java
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package yugecin.opsudance;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
|
import org.newdawn.slick.*;
|
||||||
|
|
||||||
|
import static itdelatrisu.opsu.GameImage.*;
|
||||||
|
import static yugecin.opsudance.core.InstanceContainer.*;
|
||||||
|
import static yugecin.opsudance.options.Options.*;
|
||||||
|
|
||||||
|
public class ReplayCursor
|
||||||
|
{
|
||||||
|
private final Point lastPosition;
|
||||||
|
private TrailList trail = new TrailList();
|
||||||
|
|
||||||
|
public Color filter;
|
||||||
|
|
||||||
|
public ReplayCursor(Color filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
this.lastPosition = new Point(width2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawTrail(float trailw2, float trailh2, float txtw, float txth)
|
||||||
|
{
|
||||||
|
float alpha = 0f;
|
||||||
|
float alphaIncrease = .4f / trail.size;
|
||||||
|
for (Trailpart p : trail) {
|
||||||
|
alpha += alphaIncrease;
|
||||||
|
GL11.glColor4f(filter.r, filter.g, filter.b, alpha);
|
||||||
|
GL11.glTexCoord2f(0f, 0f);
|
||||||
|
GL11.glVertex3f(p.x - trailw2, p.y - trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(txtw, 0);
|
||||||
|
GL11.glVertex3f(p.x + trailw2, p.y - trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(txtw, txth);
|
||||||
|
GL11.glVertex3f(p.x + trailw2, p.y + trailh2, 0f);
|
||||||
|
GL11.glTexCoord2f(0f, txth);
|
||||||
|
GL11.glVertex3f(p.x - trailw2, p.y + trailh2, 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawCursor()
|
||||||
|
{
|
||||||
|
CURSOR.getScaledImage(OPTION_CURSOR_SIZE.val / 100f).drawCentered(lastPosition.x, lastPosition.y, filter);
|
||||||
|
CURSOR_MIDDLE.getScaledImage(OPTION_CURSOR_SIZE.val / 100f).drawCentered(lastPosition.x, lastPosition.y, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY);
|
||||||
|
lastPosition.move(mouseX, mouseY);
|
||||||
|
|
||||||
|
int removecount = 0;
|
||||||
|
TrailNode newfirst = trail.first;
|
||||||
|
while (newfirst != null && newfirst.value.time < nowtime - 175) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TrailList implements Iterable<Trailpart>
|
||||||
|
{
|
||||||
|
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<Trailpart> iterator()
|
||||||
|
{
|
||||||
|
return new Iterator<Trailpart>() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/yugecin/opsudance/ReplayCursors.java
Normal file
111
src/yugecin/opsudance/ReplayCursors.java
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* opsu!dance - fork of opsu! with cursordance auto
|
||||||
|
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package yugecin.opsudance;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
|
import org.newdawn.slick.*;
|
||||||
|
import org.newdawn.slick.opengl.Texture;
|
||||||
|
import org.newdawn.slick.opengl.TextureImpl;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.render.Rendertarget;
|
||||||
|
|
||||||
|
import static itdelatrisu.opsu.GameImage.*;
|
||||||
|
import static yugecin.opsudance.core.InstanceContainer.*;
|
||||||
|
import static yugecin.opsudance.options.Options.*;
|
||||||
|
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public class ReplayCursors
|
||||||
|
{
|
||||||
|
public final ReplayPlayback[] playbacks;
|
||||||
|
|
||||||
|
private final Rendertarget fbo;
|
||||||
|
|
||||||
|
public ReplayCursors(int amount)
|
||||||
|
{
|
||||||
|
this.playbacks = new ReplayPlayback[amount];
|
||||||
|
this.fbo = Rendertarget.createRTTFramebuffer(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw()
|
||||||
|
{
|
||||||
|
final Image img = CURSOR_TRAIL.getImage();
|
||||||
|
final Texture txt = img.getTexture();
|
||||||
|
|
||||||
|
final float trailw2 = img.getWidth() * OPTION_CURSOR_SIZE.val / 100f / 2f;
|
||||||
|
final float trailh2 = img.getHeight() * OPTION_CURSOR_SIZE.val / 100f / 2f;
|
||||||
|
float txtw = txt.getWidth();
|
||||||
|
float txth = txt.getHeight();
|
||||||
|
|
||||||
|
for (ReplayPlayback p : playbacks) {
|
||||||
|
if (!p.shouldDrawCursor()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
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);
|
||||||
|
GL11.glClearColor(0f, 0f, 0f, 0f);
|
||||||
|
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
txt.bind();
|
||||||
|
TextureImpl.unbind();
|
||||||
|
GL11.glBegin(GL11.GL_QUADS);
|
||||||
|
p.cursor.drawTrail(trailw2, trailh2, txtw, txth);
|
||||||
|
GL11.glEnd();
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ReplayPlayback p : playbacks) {
|
||||||
|
if (!p.shouldDrawCursor()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p.cursor.drawCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy()
|
||||||
|
{
|
||||||
|
this.fbo.destroyRTT();
|
||||||
|
}
|
||||||
|
}
|
490
src/yugecin/opsudance/ReplayPlayback.java
Normal file
490
src/yugecin/opsudance/ReplayPlayback.java
Normal file
|
@ -0,0 +1,490 @@
|
||||||
|
/*
|
||||||
|
* opsu!dance - fork of opsu! with cursordance auto
|
||||||
|
* Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package yugecin.opsudance;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.GameData;
|
||||||
|
import itdelatrisu.opsu.replay.Replay;
|
||||||
|
import itdelatrisu.opsu.replay.ReplayFrame;
|
||||||
|
import itdelatrisu.opsu.ui.Fonts;
|
||||||
|
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||||
|
import org.newdawn.slick.Color;
|
||||||
|
import org.newdawn.slick.Graphics;
|
||||||
|
import org.newdawn.slick.Image;
|
||||||
|
import yugecin.opsudance.core.Entrypoint;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import static itdelatrisu.opsu.GameData.*;
|
||||||
|
import static itdelatrisu.opsu.Utils.*;
|
||||||
|
import static itdelatrisu.opsu.ui.animations.AnimationEquation.*;
|
||||||
|
import static yugecin.opsudance.core.InstanceContainer.*;
|
||||||
|
import static yugecin.opsudance.options.Options.*;
|
||||||
|
|
||||||
|
public class ReplayPlayback
|
||||||
|
{
|
||||||
|
private final HitData hitdata;
|
||||||
|
public final Replay replay;
|
||||||
|
public ReplayFrame currentFrame;
|
||||||
|
public ReplayFrame nextFrame;
|
||||||
|
private int frameIndex;
|
||||||
|
private Color color;
|
||||||
|
private final Color originalcolor;
|
||||||
|
public final ReplayCursor cursor;
|
||||||
|
private int keydelay[];
|
||||||
|
public final int PADDING = 3;
|
||||||
|
public final int sqsize;
|
||||||
|
public final int unitHeight;
|
||||||
|
public static int lineHeight;
|
||||||
|
private boolean hr;
|
||||||
|
private String player;
|
||||||
|
private String mods;
|
||||||
|
private int playerwidth;
|
||||||
|
private int modwidth;
|
||||||
|
private String currentAcc;
|
||||||
|
private int currentAccWidth;
|
||||||
|
private final int ACCMAXWIDTH;
|
||||||
|
|
||||||
|
private int c300, c100, c50, fakecmiss;
|
||||||
|
|
||||||
|
private Image hitImage;
|
||||||
|
private int hitImageTimer = 0;
|
||||||
|
private boolean knockedout;
|
||||||
|
private final LinkedList<MissIndicator> missIndicators;
|
||||||
|
|
||||||
|
private Image gradeImage;
|
||||||
|
|
||||||
|
private static final Color missedColor = new Color(0.4f, 0.4f, 0.4f, 1f);
|
||||||
|
|
||||||
|
public ReplayPlayback(Replay replay, HitData hitdata, Color color, ReplayCursor cursor) {
|
||||||
|
this.missIndicators = new LinkedList<>();
|
||||||
|
this.replay = replay;
|
||||||
|
this.hitdata = hitdata;
|
||||||
|
resetFrameIndex();
|
||||||
|
this.color = new Color(color);
|
||||||
|
this.originalcolor = color;
|
||||||
|
this.cursor = cursor;
|
||||||
|
keydelay = new int[4];
|
||||||
|
this.player = replay.playerName;
|
||||||
|
this.playerwidth = Fonts.SMALLBOLD.getWidth(this.player);
|
||||||
|
this.mods = "";
|
||||||
|
this.currentAcc = "100,00%";
|
||||||
|
this.currentAccWidth = Fonts.SMALLBOLD.getWidth(currentAcc);
|
||||||
|
this.ACCMAXWIDTH = currentAccWidth + 10;
|
||||||
|
this.unitHeight = (int) (Fonts.SMALLBOLD.getLineHeight() * 0.9f);
|
||||||
|
this.sqsize = unitHeight - PADDING;
|
||||||
|
lineHeight = this.unitHeight;
|
||||||
|
if ((replay.mods & 0x1) > 0) {
|
||||||
|
this.mods += "NF";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x2) > 0) {
|
||||||
|
this.mods += "EZ";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x8) > 0 && (replay.mods & 0x200) == 0) {
|
||||||
|
this.mods += "HD";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x10) > 0) {
|
||||||
|
this.mods += "HR";
|
||||||
|
hr = true;
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x20) > 0) {
|
||||||
|
this.mods += "SD";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x40) > 0) {
|
||||||
|
this.mods += "DT";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x80) > 0) {
|
||||||
|
this.mods += "RL";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x100) > 0) {
|
||||||
|
this.mods += "HT";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x200) > 0) {
|
||||||
|
this.mods += "NC";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x400) > 0) {
|
||||||
|
this.mods += "FL";
|
||||||
|
}
|
||||||
|
if ((replay.mods & 0x4000) > 0) {
|
||||||
|
this.mods += "PF";
|
||||||
|
}
|
||||||
|
if (this.mods.length() > 0) {
|
||||||
|
this.mods = " +" + this.mods;
|
||||||
|
this.modwidth = Fonts.SMALLBOLD.getWidth(this.mods);
|
||||||
|
}
|
||||||
|
updateGradeImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetFrameIndex() {
|
||||||
|
frameIndex = 0;
|
||||||
|
currentFrame = replay.frames[frameIndex++];
|
||||||
|
nextFrame = replay.frames[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGradeImage() {
|
||||||
|
if (knockedout || !OPTION_RP_SHOW_GRADES.state) {
|
||||||
|
gradeImage = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean silver = (replay.mods & 0x408) > 0 && (replay.mods & 0x200) == 0;
|
||||||
|
GameData.Grade grade = GameData.getGrade(c300, c100, c50, fakecmiss, silver);
|
||||||
|
|
||||||
|
if (grade == GameData.Grade.NULL) {
|
||||||
|
if ((replay.mods & 0x8) > 0 && (replay.mods & 0x200) == 0) {
|
||||||
|
grade = GameData.Grade.SSH;
|
||||||
|
} else {
|
||||||
|
grade = GameData.Grade.SS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradeImage = grade.getSmallImage().getScaledCopy(unitHeight, unitHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int HITIMAGETIMEREXPAND = 250;
|
||||||
|
private int HITIMAGETIMERFADESTART = 500;
|
||||||
|
private int HITIMAGETIMERFADEEND = 700;
|
||||||
|
private float HITIMAGETIMERFADEDELTA = HITIMAGETIMERFADEEND - HITIMAGETIMERFADESTART;
|
||||||
|
private int HITIMAGEDEADFADE = 4000;
|
||||||
|
private float SHRINKTIME = 500f;
|
||||||
|
private void showHitImage(int renderdelta, int xpos, float ypos) {
|
||||||
|
if (hitImage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hitImageTimer += renderdelta;
|
||||||
|
if (!knockedout && hitImageTimer > HITIMAGETIMERFADEEND) {
|
||||||
|
hitImage = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color = new Color(1f, 1f, 1f, 1f);
|
||||||
|
if (!knockedout && hitImageTimer > HITIMAGETIMERFADESTART) {
|
||||||
|
color.a = (HITIMAGETIMERFADEEND - hitImageTimer) / HITIMAGETIMERFADEDELTA;
|
||||||
|
}
|
||||||
|
if (knockedout) {
|
||||||
|
if (hitImageTimer > HITIMAGEDEADFADE) {
|
||||||
|
this.color.a = color.a = 0f;
|
||||||
|
} else {
|
||||||
|
this.color.a = color.a = 1f - AnimationEquation.IN_CIRC.calc((float) hitImageTimer / HITIMAGEDEADFADE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float scale = 1f;
|
||||||
|
float offset = 0f;
|
||||||
|
if (hitImageTimer < HITIMAGETIMEREXPAND) {
|
||||||
|
scale = AnimationEquation.OUT_EXPO.calc((float) hitImageTimer / HITIMAGETIMEREXPAND);
|
||||||
|
offset = unitHeight / 2f * (1f - scale);
|
||||||
|
}
|
||||||
|
hitImage.draw(xpos, 2f + ypos + offset, scale, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getHeight() {
|
||||||
|
if (hitImageTimer < HITIMAGEDEADFADE) {
|
||||||
|
return unitHeight;
|
||||||
|
}
|
||||||
|
if (hitImageTimer >= HITIMAGEDEADFADE + SHRINKTIME) {
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
return unitHeight * (1f - AnimationEquation.OUT_QUART.calc((hitImageTimer - HITIMAGEDEADFADE) / SHRINKTIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(int renderdelta, Graphics g, float ypos, int time)
|
||||||
|
{
|
||||||
|
while (nextFrame != null && nextFrame.getTime() < time) {
|
||||||
|
currentFrame = nextFrame;
|
||||||
|
processKeys();
|
||||||
|
frameIndex++;
|
||||||
|
if (frameIndex >= replay.frames.length) {
|
||||||
|
nextFrame = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nextFrame = replay.frames[frameIndex];
|
||||||
|
}
|
||||||
|
processKeys();
|
||||||
|
g.setColor(color);
|
||||||
|
if (!knockedout) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (keydelay[i] > 0) {
|
||||||
|
g.fillRect(sqsize * i, ypos + PADDING, sqsize, sqsize);
|
||||||
|
}
|
||||||
|
keydelay[i] -= renderdelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hitschanged = false;
|
||||||
|
while (!hitdata.acc.isEmpty() && hitdata.acc.getFirst().time <= time) {
|
||||||
|
currentAcc = String.format("%.2f%%", hitdata.acc.removeFirst().acc).replace('.', ',');
|
||||||
|
currentAccWidth = Fonts.SMALLBOLD.getWidth(currentAcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!hitdata.time300.isEmpty() && hitdata.time300.getFirst() <= time) {
|
||||||
|
hitdata.time300.removeFirst();
|
||||||
|
c300++;
|
||||||
|
hitschanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!hitdata.time100.isEmpty() && hitdata.time100.getFirst() <= time) {
|
||||||
|
hitdata.time100.removeFirst();
|
||||||
|
hitImageTimer = 0;
|
||||||
|
hitImage = GameData.hitResults[GameData.HIT_100];
|
||||||
|
c100++;
|
||||||
|
hitschanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!hitdata.time50.isEmpty() && hitdata.time50.getFirst() <= time) {
|
||||||
|
hitdata.time50.removeFirst();
|
||||||
|
hitImageTimer = 0;
|
||||||
|
hitImage = GameData.hitResults[GameData.HIT_50];
|
||||||
|
c50++;
|
||||||
|
hitschanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!hitdata.timeCombobreaks.isEmpty() && hitdata.timeCombobreaks.getFirst() <= time) {
|
||||||
|
hitdata.timeCombobreaks.removeFirst();
|
||||||
|
hitImageTimer = 0;
|
||||||
|
hitImage = GameData.hitResults[GameData.HIT_MISS];
|
||||||
|
fakecmiss++;
|
||||||
|
hitschanged = true;
|
||||||
|
if (OPTION_RP_SHOW_MISSES.state) {
|
||||||
|
float posx = currentFrame.getScaledX();
|
||||||
|
float posy = currentFrame.getScaledY();
|
||||||
|
if (hr) {
|
||||||
|
posy = height - posy;
|
||||||
|
}
|
||||||
|
this.missIndicators.add(new MissIndicator(posx, posy));
|
||||||
|
}
|
||||||
|
if (OPTION_RP_KNOCKOUT.state) {
|
||||||
|
knockedout = true;
|
||||||
|
color = new Color(missedColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitImage != null) {
|
||||||
|
final float h = hitImage.getHeight();
|
||||||
|
if (h == 0) {
|
||||||
|
hitImage = null;
|
||||||
|
} else {
|
||||||
|
hitImage = hitImage.getScaledCopy(unitHeight / h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitschanged) {
|
||||||
|
updateGradeImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int xpos = sqsize * (OPTION_RP_SHOW_MOUSECOLUMN.state ? 5 : 3);
|
||||||
|
if (OPTION_RP_SHOW_ACC.state) {
|
||||||
|
Fonts.SMALLBOLD.drawString(xpos + ACCMAXWIDTH - currentAccWidth - 10, ypos, currentAcc, new Color(.4f, .4f, .4f, color.a));
|
||||||
|
xpos += ACCMAXWIDTH;
|
||||||
|
}
|
||||||
|
if (gradeImage != null) {
|
||||||
|
gradeImage.draw(xpos, ypos);
|
||||||
|
xpos += sqsize + 10;
|
||||||
|
}
|
||||||
|
Fonts.SMALLBOLD.drawString(xpos, ypos, this.player, color);
|
||||||
|
xpos += playerwidth;
|
||||||
|
if (!this.mods.isEmpty()) {
|
||||||
|
Fonts.SMALLBOLD.drawString(xpos, ypos, this.mods, new Color(1f, 1f, 1f, color.a));
|
||||||
|
xpos += modwidth;
|
||||||
|
}
|
||||||
|
xpos += 10;
|
||||||
|
if (OPTION_RP_SHOW_HITS.state) {
|
||||||
|
showHitImage(renderdelta, xpos, ypos);
|
||||||
|
}
|
||||||
|
if (OPTION_RP_SHOW_MISSES.state) {
|
||||||
|
final Iterator<MissIndicator> iter = this.missIndicators.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
final MissIndicator mi = iter.next();
|
||||||
|
if (mi.timer >= HITIMAGEDEADFADE) {
|
||||||
|
iter.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float progress = (float) mi.timer / HITIMAGEDEADFADE;
|
||||||
|
float failposy = mi.posy + 50f * OUT_QUART.calc(progress);
|
||||||
|
Color col = new Color(originalcolor);
|
||||||
|
col.a = 1f - IN_QUAD.calc(clamp(progress * 2f, 0f, 1f));
|
||||||
|
Fonts.SMALLBOLD.drawString(mi.posx - playerwidth / 2, failposy, player, col);
|
||||||
|
Color failimgcol = new Color(1f, 1f, 1f, col.a);
|
||||||
|
Image failimg = hitResults[HIT_MISS].getScaledCopy(unitHeight, unitHeight);
|
||||||
|
failimg.draw(mi.posx + playerwidth / 2 + 5, failposy + 2f, failimgcol);
|
||||||
|
mi.timer += renderdelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (knockedout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int y = currentFrame.getScaledY();
|
||||||
|
if (hr) {
|
||||||
|
y = height - y;
|
||||||
|
}
|
||||||
|
cursor.setCursorPosition(renderdelta, currentFrame.getScaledX(), y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldDrawCursor()
|
||||||
|
{
|
||||||
|
return !knockedout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processKeys() {
|
||||||
|
int keys = currentFrame.getKeys();
|
||||||
|
if ((keys & 5) == 5) {
|
||||||
|
keydelay[0] = OPTION_RP_KEYPRESS_DELAY.val;
|
||||||
|
}
|
||||||
|
if ((keys & 10) == 10) {
|
||||||
|
keydelay[1] = OPTION_RP_KEYPRESS_DELAY.val;
|
||||||
|
}
|
||||||
|
if ((keys ^ 5) == 4) {
|
||||||
|
keydelay[2] = OPTION_RP_KEYPRESS_DELAY.val;
|
||||||
|
}
|
||||||
|
if ((keys ^ 10) == 8) {
|
||||||
|
keydelay[3] = OPTION_RP_KEYPRESS_DELAY.val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MissIndicator
|
||||||
|
{
|
||||||
|
private float posx, posy;
|
||||||
|
private int timer;
|
||||||
|
|
||||||
|
private MissIndicator(float posx, float posy)
|
||||||
|
{
|
||||||
|
this.posx = posx;
|
||||||
|
this.posy = posy;
|
||||||
|
this.timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HitData
|
||||||
|
{
|
||||||
|
LinkedList<Integer> time300 = new LinkedList<>();
|
||||||
|
LinkedList<Integer> time100 = new LinkedList<>();
|
||||||
|
LinkedList<Integer> time50 = new LinkedList<>();
|
||||||
|
LinkedList<Integer> timeCombobreaks = new LinkedList<>();
|
||||||
|
LinkedList<AccData> acc = new LinkedList<>();
|
||||||
|
LinkedList<ComboData> combo = new LinkedList<>();
|
||||||
|
|
||||||
|
public HitData(File file) {
|
||||||
|
try (InputStream in = new FileInputStream(file)) {
|
||||||
|
int lasttime = -1;
|
||||||
|
int lastcombo = 0;
|
||||||
|
int last300 = 0;
|
||||||
|
int last100 = 0;
|
||||||
|
int last50 = 0;
|
||||||
|
while (true) {
|
||||||
|
byte[] time = new byte[4];
|
||||||
|
int rd = in.read(time);
|
||||||
|
if (rd <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rd != 4) {
|
||||||
|
throw new RuntimeException("expected 4 bytes, got " + rd);
|
||||||
|
}
|
||||||
|
byte[] _time = { time[3], time[2], time[1], time[0] };
|
||||||
|
lasttime = ByteBuffer.wrap(_time).getInt();
|
||||||
|
lasttime += 200;
|
||||||
|
int type = in.read();
|
||||||
|
if (type == -1) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
if (in.read(time) != 4) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
_time = new byte[] { time[3], time[2], time[1], time[0] };
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
int this100 = ByteBuffer.wrap(_time).getInt();
|
||||||
|
spread(time100, lasttime, this100 - last100);
|
||||||
|
last100 = this100;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
int this300 = ByteBuffer.wrap(_time).getInt();
|
||||||
|
spread(time300, lasttime, this300 - last300);
|
||||||
|
last300 = this300;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
int this50 = ByteBuffer.wrap(_time).getInt();
|
||||||
|
spread(time50, lasttime, this50 - last50);
|
||||||
|
last50 = this50;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
acc.add(new AccData(lasttime, ByteBuffer.wrap(_time).getFloat()));
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
int c = ByteBuffer.wrap(_time).getInt();
|
||||||
|
combo.add(new ComboData(lasttime, c));
|
||||||
|
if (c < lastcombo) {
|
||||||
|
timeCombobreaks.add(lasttime);
|
||||||
|
}
|
||||||
|
lastcombo = c;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("unexpected data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lasttime == -1) {
|
||||||
|
throw new RuntimeException("nodata");
|
||||||
|
}
|
||||||
|
Entrypoint.sout(String.format(
|
||||||
|
"%s lastcombo %d lasttime %d",
|
||||||
|
file.getName(),
|
||||||
|
lastcombo,
|
||||||
|
lasttime
|
||||||
|
));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spread(LinkedList<Integer> list, int time, int d) {
|
||||||
|
if (list.isEmpty() || d <= 1) {
|
||||||
|
list.add(time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dtime = time - list.getLast();
|
||||||
|
int inc = dtime / d;
|
||||||
|
int ttime = list.getLast();
|
||||||
|
for (int i = 0; i < d; i++) {
|
||||||
|
ttime += inc;
|
||||||
|
if (i == d - 1) {
|
||||||
|
ttime = time;
|
||||||
|
}
|
||||||
|
list.add(ttime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AccData {
|
||||||
|
public int time;
|
||||||
|
public float acc;
|
||||||
|
public AccData(int time, float acc) {
|
||||||
|
this.time = time;
|
||||||
|
this.acc = acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ComboData {
|
||||||
|
public int time;
|
||||||
|
public int combo;
|
||||||
|
public ComboData(int time, int combo) {
|
||||||
|
this.time = time;
|
||||||
|
this.combo = combo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
|
||||||
|
|
||||||
private long exitconfirmation;
|
private long exitconfirmation;
|
||||||
|
|
||||||
public final Cursor cursor;
|
public Cursor cursor;
|
||||||
public boolean drawCursor;
|
public boolean drawCursor;
|
||||||
|
|
||||||
private final List<ResolutionChangedListener> resolutionChangedListeners;
|
private final List<ResolutionChangedListener> resolutionChangedListeners;
|
||||||
|
@ -106,8 +106,6 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
|
||||||
public DisplayContainer()
|
public DisplayContainer()
|
||||||
{
|
{
|
||||||
this.resolutionChangedListeners = new ArrayList<>();
|
this.resolutionChangedListeners = new ArrayList<>();
|
||||||
this.cursor = new Cursor();
|
|
||||||
drawCursor = true;
|
|
||||||
|
|
||||||
skinservice.addSkinChangedListener(this);
|
skinservice.addSkinChangedListener(this);
|
||||||
|
|
||||||
|
@ -174,6 +172,7 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
|
||||||
|
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) {
|
while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) {
|
||||||
|
nowtime = System.currentTimeMillis();
|
||||||
delta = getDelta();
|
delta = getDelta();
|
||||||
|
|
||||||
timeSinceLastRender += delta;
|
timeSinceLastRender += delta;
|
||||||
|
@ -270,6 +269,8 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
|
||||||
glVersion = GL11.glGetString(GL11.GL_VERSION);
|
glVersion = GL11.glGetString(GL11.GL_VERSION);
|
||||||
glVendor = GL11.glGetString(GL11.GL_VENDOR);
|
glVendor = GL11.glGetString(GL11.GL_VENDOR);
|
||||||
GLHelper.hideNativeCursor();
|
GLHelper.hideNativeCursor();
|
||||||
|
this.cursor = new Cursor();
|
||||||
|
drawCursor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move this elsewhere
|
// TODO: move this elsewhere
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class Entrypoint {
|
||||||
try {
|
try {
|
||||||
InstanceContainer.kickstart();
|
InstanceContainer.kickstart();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
JOptionPane.showMessageDialog(null, e.getMessage(), "Cannot start " + PROJECT_NAME, JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, e.getMessage(), "Cannot start " + PROJECT_NAME, JOptionPane.ERROR_MESSAGE);
|
||||||
// TODO replace with errorhandler
|
// TODO replace with errorhandler
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ public class InstanceContainer {
|
||||||
public static boolean isWidescreen;
|
public static boolean isWidescreen;
|
||||||
public static int mouseX, mouseY;
|
public static int mouseX, mouseY;
|
||||||
public static int renderDelta;
|
public static int renderDelta;
|
||||||
|
public static long nowtime;
|
||||||
|
|
||||||
public static void kickstart() {
|
public static void kickstart() {
|
||||||
updater = new Updater();
|
updater = new Updater();
|
||||||
|
|
|
@ -161,6 +161,15 @@ public class OptionGroups {
|
||||||
OPTION_DANCE_RGB_CURSOR_INC,
|
OPTION_DANCE_RGB_CURSOR_INC,
|
||||||
OPTION_DANCE_CURSOR_TRAIL_OVERRIDE,
|
OPTION_DANCE_CURSOR_TRAIL_OVERRIDE,
|
||||||
}),
|
}),
|
||||||
|
new OptionTab("REPLAYSTUFF", new Option[] {
|
||||||
|
OPTION_RP_KNOCKOUT,
|
||||||
|
OPTION_RP_SHOW_MISSES,
|
||||||
|
OPTION_RP_SHOW_GRADES,
|
||||||
|
OPTION_RP_SHOW_HITS,
|
||||||
|
OPTION_RP_SHOW_ACC,
|
||||||
|
OPTION_RP_SHOW_MOUSECOLUMN,
|
||||||
|
OPTION_RP_KEYPRESS_DELAY,
|
||||||
|
}),
|
||||||
new OptionTab("MISC", new Option[] {
|
new OptionTab("MISC", new Option[] {
|
||||||
OPTION_DANCE_HIDE_UI,
|
OPTION_DANCE_HIDE_UI,
|
||||||
OPTION_DANCE_REMOVE_BG,
|
OPTION_DANCE_REMOVE_BG,
|
||||||
|
|
|
@ -373,7 +373,7 @@ public class Options {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final NumericOption OPTION_CURSOR_SIZE = new NumericOption("Size", "CursorSize", "Change the cursor scale.", 100, 50, 200) {
|
public static final NumericOption OPTION_CURSOR_SIZE = new NumericOption("Size", "CursorSize", "Change the cursor scale.", 100, 10, 200) {
|
||||||
@Override
|
@Override
|
||||||
public String getValueString () {
|
public String getValueString () {
|
||||||
return String.format("%.2fx", val / 100f);
|
return String.format("%.2fx", val / 100f);
|
||||||
|
@ -998,4 +998,19 @@ public class Options {
|
||||||
public static final ToggleOption OPTION_PIPPI_SLIDER_FOLLOW_EXPAND = new ToggleOption("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false);
|
public static final ToggleOption OPTION_PIPPI_SLIDER_FOLLOW_EXPAND = new ToggleOption("Followcircle expand", "PippiFollowExpand", "Increase radius in followcircles", false);
|
||||||
public static final ToggleOption OPTION_PIPPI_PREVENT_WOBBLY_STREAMS = new ToggleOption("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true);
|
public static final ToggleOption OPTION_PIPPI_PREVENT_WOBBLY_STREAMS = new ToggleOption("Prevent wobbly streams", "PippiPreventWobblyStreams", "Force linear mover while doing streams to prevent wobbly pippi", true);
|
||||||
|
|
||||||
|
public static final ToggleOption
|
||||||
|
OPTION_RP_KNOCKOUT = new ToggleOption("Knockout", "ReplayKnockout", "Remove replays on combobreaks.", true),
|
||||||
|
OPTION_RP_SHOW_MISSES = new ToggleOption("Show misses", "ReplayShowMiss", "Show falling miss indicators.", true),
|
||||||
|
OPTION_RP_SHOW_GRADES = new ToggleOption("Show grades", "ReplayShowGrades", "Show grades next to players.", true),
|
||||||
|
OPTION_RP_SHOW_HITS = new ToggleOption("Show hits", "ReplayShowHits", "Show miss, 50, 100 hits next to players.", true),
|
||||||
|
OPTION_RP_SHOW_MOUSECOLUMN = new ToggleOption("Mouse buttons column", "ReplayShowMouseColumn", "Preserve space for mouse button columns.", true),
|
||||||
|
OPTION_RP_SHOW_ACC = new ToggleOption("Show accuracy", "ReplayShowAcc", "Show accuracy next to players.", true);
|
||||||
|
|
||||||
|
public static final NumericOption
|
||||||
|
OPTION_RP_KEYPRESS_DELAY = new NumericOption("Key indicator delay", "ReplayKeyDelay", "How long the key indicator should show after the key being released (ms).", 10, 1, 50) {
|
||||||
|
@Override
|
||||||
|
public String getValueString() {
|
||||||
|
return String.valueOf(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user