Compare commits

..

59 Commits

Author SHA1 Message Date
f46ee19838 make shader work tm 2018-11-16 23:39:12 +01:00
9a3ecc06aa Shader tm 2018-11-15 20:37:51 +01:00
yugecin
02448503c7
uncomment accidentally commented stuff 2018-10-29 00:06:41 +01:00
yugecin
10e5375231
adjust replay cursor trail length 2018-10-28 23:52:26 +01:00
yugecin
db5299a32a
adjust player list size and stuff 2018-10-28 21:43:49 +01:00
yugecin
f01728156a
add missing option to optiongroups 2018-10-28 20:44:57 +01:00
yugecin
4117357f31
add some configurable options to replaystuff, fix count50, add misses, .. 2018-10-28 18:44:38 +01:00
yugecin
8725e0b31c
post-merge fixes 2018-10-28 17:33:23 +01:00
yugecin
6ca1f6e800
Merge branch 'master' into kockout-wdata 2018-10-28 17:32:59 +01:00
yugecin
6076e6e9c9
fix texture bindings the 'correct' way 2018-10-26 19:45:38 +02:00
yugecin
3debd40aec
trail alpha adjustments 2018-10-26 00:28:24 +02:00
yugecin
0a6412e03f
fix sorting replays messing up hitdata 2018-10-26 00:22:15 +02:00
yugecin
2b83657907
refactor replaystuff cursor drawing 2018-10-26 00:05:59 +02:00
yugecin
a3c1cfad9e
remove leftover code 2018-10-25 21:35:37 +02:00
yugecin
f92270fa22
more small cleanups 2018-10-23 02:07:54 +02:00
yugecin
2ae438ced2
play around with hueshifts 2018-10-23 01:57:49 +02:00
yugecin
e169b0b1f5
use slightly less saturation for player colors 2018-10-23 01:54:52 +02:00
yugecin
ec375965a2
clean up code 2018-10-23 01:49:07 +02:00
yugecin
949bc62b47
let replaycursor respect cursor size setting 2018-10-23 00:56:51 +02:00
yugecin
ee14ac9b2d
attempt to make a better cursortrail for replay stuff 2018-10-23 00:43:10 +02:00
yugecin
5a268bc713 post-merge fixes 2018-10-22 22:10:43 +02:00
yugecin
3d470cbe7a Merge branch 'master' into kockout-wdata
# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
#	src/itdelatrisu/opsu/states/Splash.java
2018-10-22 21:59:11 +02:00
yugecin
3913abfcaf fade out player name at fail location 2018-04-14 21:12:09 +02:00
yugecin
8df9732bf6 fade out faster after miss 2018-04-14 20:32:43 +02:00
yugecin
3b82c7797e don't draw grades when the player missed 2018-04-14 20:29:37 +02:00
yugecin
a8cacc690f better timing of hitdata 2018-04-14 20:17:29 +02:00
yugecin
eec3ae3ecd hide mouse buttons 2018-04-14 20:01:08 +02:00
yugecin
f398b961d7 offset the hitdata by 50ms 2018-04-14 18:36:59 +02:00
yugecin
29640a4ef7 show grades next to players 2018-04-14 18:21:59 +02:00
yugecin
9628149de0 slightly adjust hit result image position 2018-04-14 17:55:59 +02:00
yugecin
8fed11ba86 use the hitdata for replay hitresults and show per player acc 2018-04-14 17:55:19 +02:00
yugecin
9af2b01657 read and parse hitdata from file 2018-04-14 17:22:22 +02:00
yugecin
4f68669add show notif and leave game when directory containing replays does not exist 2018-01-14 22:17:09 +01:00
yugecin
8471c43ed5 center names and shrink when a name is fully faded out 2017-12-22 22:22:16 +01:00
yugecin
b4498333df render mods separate from the playernames in white 2017-12-22 13:07:28 +01:00
yugecin
c0f122c74d fix replay key overlay flickering 2017-12-22 12:53:14 +01:00
yugecin
df1afd98c3 more color variation in smaller groups 2017-12-22 12:52:22 +01:00
yugecin
22d2b0be86 hide the replay key overlay background bars 2017-12-22 12:34:05 +01:00
yugecin
085cd719fa fade out players after they missed 2017-12-22 12:33:51 +01:00
yugecin
3bccdc5540 mark as missed when combo exceeds max combo in replay file 2017-12-22 12:28:10 +01:00
yugecin
992ec7afd8 revert 'don't clone spinners for earrape purposes' because it messes up combo 2017-12-22 12:18:20 +01:00
yugecin
60de7a26c1 don't clone spinners for earrape purposes 2017-12-22 12:05:32 +01:00
yugecin
eaa46bddd9 color players grey and stop updating when they miss 2017-12-22 00:47:38 +01:00
yugecin
92dc59f7b6 show non-300 hit results next to player's name with small animations 2017-12-22 00:10:15 +01:00
yugecin
408c162741 fix replay hit results 2017-12-21 23:55:16 +01:00
yugecin
35769b31c7 attempt to keep track of scoring for each player 2017-12-21 22:43:42 +01:00
yugecin
5704ebb0c1 slightly less transparent cursor trail 2017-12-16 17:09:00 +01:00
yugecin
e8d0199705 post-merge fixes 2017-12-16 17:07:05 +01:00
yugecin
9320767f21 Merge branch 'master' into replaystuff
# Conflicts:
#	src/itdelatrisu/opsu/states/Game.java
2017-12-16 16:35:11 +01:00
yugecin
9d9f2df5d8 align names and squares, order the rainbow names again and let cursor size be 0.1x min 2017-03-30 01:20:17 +02:00
yugecin
5f334ca4cb more transparant trail and faster keydelay 2017-03-30 00:19:32 +02:00
yugecin
9df139bded post merge stuff 2017-03-30 00:06:13 +02:00
yugecin
3629dfd4d7 Merge branch 'master' into replaystuff
# Conflicts:
#	src/itdelatrisu/opsu/Options.java
#	src/itdelatrisu/opsu/ui/Cursor.java
2017-03-30 00:05:45 +02:00
yugecin
e20b55ba59 put some space between names in the playerlist 2017-03-30 00:00:00 +02:00
yugecin
e270aeb92e sort the playerlist by score 2017-03-29 23:59:38 +02:00
yugecin
00f34296fb Merge branch 'master' into replaystuff 2017-02-26 00:34:21 +01:00
yugecin
0e8bf0b31e Merge branch 'master' into replaystuff 2017-02-26 00:03:47 +01:00
yugecin
a84786987a Merge branch 'master' into replaystuff 2017-02-25 23:53:05 +01:00
yugecin
ba2aa51b7b multiple replay display stuff 2017-02-25 23:05:41 +01:00
24 changed files with 1724 additions and 71 deletions

15
res/sh_Cursor.vertex Normal file
View 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
View 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);
}

View File

@ -4,6 +4,7 @@ package awlex.ospu;
* Created by Awlex on 10.10.2016.
*/
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.objects.GameObject;
import itdelatrisu.opsu.objects.curves.Vec2f;
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;
}
@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
public void draw(Graphics g, int trackPosition, boolean mirrored) {

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

View 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() : "";
}
}

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

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

View File

@ -170,7 +170,7 @@ public class GameData {
HIT_ANIMATION_RESULT = 12; // not a hit result
/** 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). */
private int[] hitResultCount;
@ -1222,7 +1222,7 @@ public class GameData {
/**
* Increases the combo streak by one.
*/
private void incrementComboStreak() {
protected void incrementComboStreak() {
combo++;
comboPopTime = 0;
if (combo > comboMax)
@ -1404,7 +1404,7 @@ public class GameData {
* @param noIncrementCombo if the combo should not be incremented by this result
* @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) {
// update health, score, and combo streak based on hit result
int hitValue = 0;

View File

@ -83,6 +83,13 @@ public class Circle extends GameObject {
super.updateStartEndPositions(time);
}
@Override
public GameObject clone(GameData data) {
Circle c = new Circle(hitObject, game, data, comboColorIndex, comboEnd);
c.isreplay = true;
return c;
}
@Override
public void draw(Graphics g, int trackPosition, boolean mirror) {
Color orig = color;
@ -165,12 +172,13 @@ public class Circle extends GameObject {
return false;
}
private boolean isreplay;
@Override
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
int time = hitObject.getTime();
int[] hitResultOffset = game.getHitResultOffsets();
boolean isAutoMod = GameMod.AUTO.isActive();
boolean isAutoMod = !isreplay && GameMod.AUTO.isActive();
if (trackPosition > time + hitResultOffset[GameData.HIT_50]) {
if (isAutoMod) {// "auto" mod: catch any missed notes due to lag

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu.objects;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.curves.Vec2f;
@ -29,7 +30,7 @@ import org.newdawn.slick.Graphics;
*/
public class DummyObject extends GameObject {
/** The associated HitObject. */
private HitObject hitObject;
public final HitObject hitObject;
/** The scaled starting x, y coordinates. */
private float x, y;
@ -49,6 +50,11 @@ public class DummyObject extends GameObject {
updateStartEndPositions(0);
}
@Override
public GameObject clone(GameData a) {
return new DummyObject(this.hitObject);
}
@Override
public void draw(Graphics g, int trackPosition, boolean mirror) {}

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu.objects;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.objects.curves.Vec2f;
import org.newdawn.slick.Color;
@ -114,4 +115,6 @@ public abstract class GameObject {
return hue;
}
public abstract GameObject clone(GameData data);
}

View File

@ -189,6 +189,13 @@ public class Slider extends GameObject {
repeats = hitObject.getRepeatCount();
}
@Override
public GameObject clone(GameData data) {
Slider s = new Slider(hitObject, game, data, comboColorIndex, comboEnd);
s.isreplay = true;
return s;
}
@Override
public void draw(Graphics g, int trackPosition, boolean mirror) {
if (trackPosition > getEndTime()) {
@ -630,11 +637,12 @@ public class Slider extends GameObject {
return false;
}
boolean isreplay;
@Override
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition) {
int repeatCount = hitObject.getRepeatCount();
int[] hitResultOffset = game.getHitResultOffsets();
boolean isAutoMod = GameMod.AUTO.isActive();
boolean isAutoMod = !isreplay && GameMod.AUTO.isActive();
if (!sliderClickedInitial) {
int time = hitObject.getTime();

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu.objects;
import awlex.ospu.FakeGameObject;
import itdelatrisu.opsu.GameData;
import itdelatrisu.opsu.GameData.HitObjectType;
import itdelatrisu.opsu.GameImage;
@ -170,6 +171,12 @@ public class Spinner extends GameObject {
rotationsNeeded = spinsPerMinute * (hitObject.getEndTime() - hitObject.getTime()) / 60000f;
}
@Override
public GameObject clone(GameData data) {
//return new DummyObject(hitObject);
return new Spinner(hitObject, game, data);
}
@Override
public void draw(Graphics g, int trackPosition, boolean mirror) {
if (mirror) {

View File

@ -17,6 +17,7 @@
*/
package itdelatrisu.opsu.render;
import com.osufx.sunpy.Shader;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils;
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_WRAP_S, GL11.GL_CLAMP);
GL20.glUseProgram(0);
GL20.glUseProgram(Shader.currentShader);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();

View File

@ -45,6 +45,8 @@ import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.*;
import org.lwjgl.input.Keyboard;
@ -57,6 +59,7 @@ import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.*;
import yugecin.opsudance.ReplayPlayback.HitData;
import yugecin.opsudance.core.state.ComplexOpsuState;
import yugecin.opsudance.objects.curves.FakeCombinedCurve;
import yugecin.opsudance.options.OptionGroups;
@ -304,7 +307,7 @@ public class Game extends ComplexOpsuState {
MUSICBAR_HOVER = new Color(12, 9, 10, 0.35f),
MUSICBAR_FILL = new Color(255, 255, 255, 0.75f);
private final Cursor mirrorCursor;
private Cursor mirrorCursor;
private final MoveStoryboard moveStoryboardOverlay;
private final StoryboardOverlay storyboardOverlay;
private final OptionsOverlay optionsOverlay;
@ -315,7 +318,7 @@ public class Game extends ComplexOpsuState {
public Game() {
super();
mirrorCursor = new Cursor(true);
//mirrorCursor = new Cursor(true);
this.moveStoryboardOverlay = new MoveStoryboard();
this.optionsOverlay = new OptionsOverlay(OptionGroups.storyboardOptions);
this.storyboardOverlay = new StoryboardOverlay(moveStoryboardOverlay, optionsOverlay, this);
@ -711,7 +714,7 @@ public class Game extends ComplexOpsuState {
} else {
displayContainer.cursor.draw(Utils.isGameKeyPressed());
}
super.render(g);
if (OPTION_DANCE_ENABLE_SB.state) {
@ -722,12 +725,34 @@ public class Game extends ComplexOpsuState {
}
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
public void preRenderUpdate() {
super.preRenderUpdate();
if (OPTION_DANCE_ENABLE_SB.state) {
optionsOverlay.preRenderUpdate();
if (optionsOverlay.isActive()) {
@ -910,6 +935,7 @@ public class Game extends ComplexOpsuState {
// set mouse coordinates
autoMousePosition.set(autoPoint.x, autoPoint.y);
autoMousePosition.set(-100, -100);
}
if (isReplay) {
@ -1404,7 +1430,7 @@ public class Game extends ComplexOpsuState {
if (super.keyReleased(key, c)) {
return true;
}
if (gameFinished) {
return true;
}
@ -1441,7 +1467,7 @@ public class Game extends ComplexOpsuState {
if (OPTION_DANCE_ENABLE_SB.state && optionsOverlay.mouseWheelMoved(newValue)) {
return true;
}
if (super.mouseWheelMoved(newValue)) {
return true;
}
@ -1454,6 +1480,16 @@ public class Game extends ComplexOpsuState {
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
public void enter() {
overlays.clear();
@ -1465,7 +1501,84 @@ public class Game extends ComplexOpsuState {
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;
if (!skippedToCheckpoint) {
@ -1740,7 +1853,7 @@ public class Game extends ComplexOpsuState {
if (OPTION_DANCE_ENABLE_SB.state) {
storyboardOverlay.onLeave();
}
optionsOverlay.hide();
isInGame = false;
@ -1749,6 +1862,12 @@ public class Game extends ComplexOpsuState {
knorkesliders = null;
if (replayCursors != null) {
replayCursors.destroy();
replayCursors = null;
replays = null;
}
Dancer.instance.setGameObjects(null);
Cursor.lastObjColor = Color.white;
@ -1765,11 +1884,7 @@ public class Game extends ComplexOpsuState {
if (isReplay)
GameMod.loadModState(previousMods);
}
/**
* Adjusts the beatmap's local music offset.
* @param sign the sign (multiplier)
*/
public void adjustLocalMusicOffset(int amount) {
int newOffset = beatmap.localMusicOffset + amount;
barNotifs.send(String.format("Local beatmap offset set to %dms", newOffset));

View File

@ -18,18 +18,31 @@
package itdelatrisu.opsu.ui;
import com.osufx.sunpy.Shader;
import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.render.Rendertarget;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.awt.Point;
import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.LinkedList;
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 yugecin.opsudance.Dancer;
import yugecin.opsudance.ReplayPlayback;
import yugecin.opsudance.skinning.SkinService;
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.*;
/**
@ -55,8 +68,7 @@ public class Cursor {
/** The time it takes for the cursor to scale, in milliseconds. */
private static final float CURSOR_SCALE_TIME = 125;
/** Stores all previous cursor locations to display a trail. */
private LinkedList<Point> trail = new LinkedList<>();
private TrailList trail = new TrailList();
private boolean newStyle;
@ -68,6 +80,12 @@ public class Cursor {
private boolean isMirrored;
private Color filter;
private Rendertarget fbo;
private Shader cursorTrailShader;
public Cursor() {
this(false);
}
@ -75,6 +93,14 @@ public class Cursor {
public Cursor(boolean isMirrored) {
resetLocations(0, 0);
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
*/
public void draw(boolean mousePressed) {
if (fbo == null) {
this.fbo = Rendertarget.createRTTFramebuffer(width, height);
}
/*
if (OPTION_DISABLE_CURSOR.state) {
return;
}
@ -131,6 +161,10 @@ public class Cursor {
lastCursorColor = filter = Dancer.cursorColorOverride.getColor();
}
if (this.filter != null) {
filter = this.filter;
}
// draw a fading trail
float alpha = 0f;
float t = 2f / trail.size();
@ -139,7 +173,7 @@ public class Cursor {
cursorTrail.startUse();
for (Point p : trail) {
alpha += t;
cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha);
cursorTrail.setImageColor(filter.r, filter.g, filter.b, alpha * 0.25f);
cursorTrail.drawEmbedded(
p.x - (cursorTrailWidth / 2f), p.y - (cursorTrailHeight / 2f),
cursorTrailWidth, cursorTrailHeight, cursorTrailRotation);
@ -157,6 +191,110 @@ public class Cursor {
if (hasMiddle) {
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
*/
public void setCursorPosition(int delta, int mouseX, int mouseY) {
// TODO: use an image buffer
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);
nowtime = System.currentTimeMillis();
removeCount = (int) (trail.size() / (6 * FPSmod)) + 1;
} else {
// old style: sample one point at a time
trail.add(new Point(mouseX, mouseY));
addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY);
lastPosition.move(mouseX, mouseY);
int max = (int) (10 * FPSmod);
if (trail.size() > max)
removeCount = trail.size() - max;
int removecount = 0;
TrailNode newfirst = trail.first;
while (newfirst != null && newfirst.value.time < nowtime - 175) {
newfirst = newfirst.next;
removecount++;
}
int cursortraillength = OPTION_DANCE_CURSOR_TRAIL_OVERRIDE.val;
if (cursortraillength > 20) {
removeCount = trail.size() - cursortraillength;
trail.first = newfirst;
if (newfirst == null) {
trail.last = null;
}
// remove points from the lists
for (int i = 0; i < removeCount && !trail.isEmpty(); i++)
trail.remove();
trail.size -= removecount;
}
/**
@ -200,8 +326,8 @@ public class Cursor {
* @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
boolean added = false;
int d = 0;
int dy = Math.abs(y2 - y1);
int dx = Math.abs(x2 - x1);
@ -211,16 +337,11 @@ public class Cursor {
int ix = x1 < x2 ? 1 : -1; // increment direction
int iy = y1 < y2 ? 1 : -1;
int k = 5; // sample size
if (dy <= dx) {
for (int i = 0; ; i++) {
if (i == k) {
trail.add(new Point(x1, y1));
added = true;
i = 0;
}
for (;;) {
if (x1 == x2)
break;
trail.add(new Trailpart(x1, y1));
x1 += ix;
d += dy2;
if (d > dx) {
@ -229,14 +350,10 @@ public class Cursor {
}
}
} else {
for (int i = 0; ; i++) {
if (i == k) {
trail.add(new Point(x1, y1));
added = true;
i = 0;
}
for (;;) {
if (y1 == y2)
break;
trail.add(new Trailpart(x1, y1));
y1 += iy;
d += dx2;
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.
*/
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));
}
}
/**

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

View 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();
}
}

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

View File

@ -92,7 +92,7 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
private long exitconfirmation;
public final Cursor cursor;
public Cursor cursor;
public boolean drawCursor;
private final List<ResolutionChangedListener> resolutionChangedListeners;
@ -106,8 +106,6 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
public DisplayContainer()
{
this.resolutionChangedListeners = new ArrayList<>();
this.cursor = new Cursor();
drawCursor = true;
skinservice.addSkinChangedListener(this);
@ -174,6 +172,7 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
public void run() throws Exception {
while(!exitRequested && !(Display.isCloseRequested() && state.onCloseRequest()) || !confirmExit()) {
nowtime = System.currentTimeMillis();
delta = getDelta();
timeSinceLastRender += delta;
@ -270,6 +269,8 @@ public class DisplayContainer implements ErrorDumpable, SkinChangedListener {
glVersion = GL11.glGetString(GL11.GL_VERSION);
glVendor = GL11.glGetString(GL11.GL_VENDOR);
GLHelper.hideNativeCursor();
this.cursor = new Cursor();
drawCursor = true;
}
// TODO: move this elsewhere

View File

@ -35,6 +35,7 @@ public class Entrypoint {
try {
InstanceContainer.kickstart();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, e.getMessage(), "Cannot start " + PROJECT_NAME, JOptionPane.ERROR_MESSAGE);
// TODO replace with errorhandler
}

View File

@ -82,6 +82,7 @@ public class InstanceContainer {
public static boolean isWidescreen;
public static int mouseX, mouseY;
public static int renderDelta;
public static long nowtime;
public static void kickstart() {
updater = new Updater();

View File

@ -161,6 +161,15 @@ public class OptionGroups {
OPTION_DANCE_RGB_CURSOR_INC,
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[] {
OPTION_DANCE_HIDE_UI,
OPTION_DANCE_REMOVE_BG,

View File

@ -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
public String getValueString () {
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_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);
}
};
}