add some configurable options to replaystuff, fix count50, add misses, ..

This commit is contained in:
yugecin 2018-10-28 18:44:38 +01:00
parent 8725e0b31c
commit 4117357f31
No known key found for this signature in database
GPG Key ID: 2C5AC035A7068E44
3 changed files with 119 additions and 70 deletions

View File

@ -29,17 +29,17 @@ import yugecin.opsudance.core.Entrypoint;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import static itdelatrisu.opsu.GameData.*; import static itdelatrisu.opsu.GameData.*;
import static itdelatrisu.opsu.Utils.*; import static itdelatrisu.opsu.Utils.*;
import static itdelatrisu.opsu.ui.animations.AnimationEquation.*; import static itdelatrisu.opsu.ui.animations.AnimationEquation.*;
import static yugecin.opsudance.core.InstanceContainer.*; import static yugecin.opsudance.core.InstanceContainer.*;
import static yugecin.opsudance.options.Options.*;
public class ReplayPlayback { public class ReplayPlayback
{
private static final boolean HIDEMOUSEBTNS = true;
private final HitData hitdata; private final HitData hitdata;
public final Replay replay; public final Replay replay;
public ReplayFrame currentFrame; public ReplayFrame currentFrame;
@ -59,19 +59,20 @@ public class ReplayPlayback {
private String currentAcc; private String currentAcc;
private int currentAccWidth; private int currentAccWidth;
private final int ACCMAXWIDTH; private final int ACCMAXWIDTH;
private float failposx, failposy;
private int c300, c100, c50; private int c300, c100, c50, fakecmiss;
private Image hitImage; private Image hitImage;
private int hitImageTimer = 0; private int hitImageTimer = 0;
private boolean missed; private boolean knockedout;
private final LinkedList<MissIndicator> missIndicators;
private Image gradeImage; private Image gradeImage;
private static final Color missedColor = new Color(0.4f, 0.4f, 0.4f, 1f); private static final Color missedColor = new Color(0.4f, 0.4f, 0.4f, 1f);
public ReplayPlayback(Replay replay, HitData hitdata, Color color, ReplayCursor cursor) { public ReplayPlayback(Replay replay, HitData hitdata, Color color, ReplayCursor cursor) {
this.missIndicators = new LinkedList<>();
this.replay = replay; this.replay = replay;
this.hitdata = hitdata; this.hitdata = hitdata;
resetFrameIndex(); resetFrameIndex();
@ -133,17 +134,20 @@ public class ReplayPlayback {
} }
private void updateGradeImage() { private void updateGradeImage() {
if (missed) { if (knockedout || !OPTION_RP_SHOW_GRADES.state) {
gradeImage = null; gradeImage = null;
return; return;
} }
boolean silver = (replay.mods & 0x408) > 0 && (replay.mods & 0x200) == 0; boolean silver = (replay.mods & 0x408) > 0 && (replay.mods & 0x200) == 0;
GameData.Grade grade = GameData.getGrade(c300, c100, c50, 0, silver); GameData.Grade grade = GameData.getGrade(c300, c100, c50, fakecmiss, silver);
if (grade == GameData.Grade.NULL) { if (grade == GameData.Grade.NULL) {
gradeImage = null; if ((replay.mods & 0x8) > 0 && (replay.mods & 0x200) == 0) {
return; grade = GameData.Grade.SSH;
} else {
grade = GameData.Grade.SS;
}
} }
gradeImage = grade.getSmallImage().getScaledCopy(SQSIZE + 5, SQSIZE + 5); gradeImage = grade.getSmallImage().getScaledCopy(SQSIZE + 5, SQSIZE + 5);
} }
@ -160,16 +164,16 @@ public class ReplayPlayback {
} }
hitImageTimer += renderdelta; hitImageTimer += renderdelta;
if (!missed && hitImageTimer > HITIMAGETIMERFADEEND) { if (!knockedout && hitImageTimer > HITIMAGETIMERFADEEND) {
hitImage = null; hitImage = null;
return; return;
} }
Color color = new Color(1f, 1f, 1f, 1f); Color color = new Color(1f, 1f, 1f, 1f);
if (!missed && hitImageTimer > HITIMAGETIMERFADESTART) { if (!knockedout && hitImageTimer > HITIMAGETIMERFADESTART) {
color.a = (HITIMAGETIMERFADEEND - hitImageTimer) / HITIMAGETIMERFADEDELTA; color.a = (HITIMAGETIMERFADEEND - hitImageTimer) / HITIMAGETIMERFADEDELTA;
} }
if (missed) { if (knockedout) {
if (hitImageTimer > HITIMAGEDEADFADE) { if (hitImageTimer > HITIMAGEDEADFADE) {
this.color.a = color.a = 0f; this.color.a = color.a = 0f;
} else { } else {
@ -195,8 +199,8 @@ public class ReplayPlayback {
return UNITHEIGHT * (1f - AnimationEquation.OUT_QUART.calc((hitImageTimer - HITIMAGEDEADFADE) / SHRINKTIME)); return UNITHEIGHT * (1f - AnimationEquation.OUT_QUART.calc((hitImageTimer - HITIMAGEDEADFADE) / SHRINKTIME));
} }
public void render(int renderdelta, Graphics g, float ypos, int time) { public void render(int renderdelta, Graphics g, float ypos, int time)
{
while (nextFrame != null && nextFrame.getTime() < time) { while (nextFrame != null && nextFrame.getTime() < time) {
currentFrame = nextFrame; currentFrame = nextFrame;
processKeys(); processKeys();
@ -209,7 +213,7 @@ public class ReplayPlayback {
} }
processKeys(); processKeys();
g.setColor(color); g.setColor(color);
if (!missed) { if (!knockedout) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (keydelay[i] > 0) { if (keydelay[i] > 0) {
g.fillRect(SQSIZE * i, ypos + 5, SQSIZE, SQSIZE); g.fillRect(SQSIZE * i, ypos + 5, SQSIZE, SQSIZE);
@ -240,36 +244,44 @@ public class ReplayPlayback {
while (!hitdata.time50.isEmpty() && hitdata.time50.getFirst() <= time) { while (!hitdata.time50.isEmpty() && hitdata.time50.getFirst() <= time) {
hitdata.time50.removeFirst(); hitdata.time50.removeFirst();
hitImageTimer = 0; hitImageTimer = 0;
hitImage = GameData.hitResults[GameData.HIT_100].getScaledCopy(SQSIZE + 5, SQSIZE + 5); hitImage = GameData.hitResults[GameData.HIT_50].getScaledCopy(SQSIZE + 5, SQSIZE + 5);
c50++; c50++;
hitschanged = true; hitschanged = true;
} }
while (!hitdata.timeCombobreaks.isEmpty() && hitdata.timeCombobreaks.getFirst() <= time) {
hitdata.timeCombobreaks.removeFirst();
hitImageTimer = 0;
hitImage = GameData.hitResults[GameData.HIT_MISS].getScaledCopy(SQSIZE + 5, SQSIZE + 5);
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 (hitschanged) { if (hitschanged) {
updateGradeImage(); updateGradeImage();
} }
if (time >= hitdata.combobreaktime) {
if (!missed) {
failposx = currentFrame.getScaledX();
failposy = currentFrame.getScaledY();
if (hr) {
failposy = height - failposy;
}
}
missed = true;
color = new Color(missedColor);
hitImageTimer = 0;
hitImage = GameData.hitResults[GameData.HIT_MISS].getScaledCopy(SQSIZE + 5, SQSIZE + 5);
}
} }
int xpos = SQSIZE * (HIDEMOUSEBTNS ? 3 : 5); int xpos = SQSIZE * (OPTION_RP_SHOW_MOUSECOLUMN.state ? 5 : 3);
Fonts.SMALLBOLD.drawString(xpos + ACCMAXWIDTH - currentAccWidth - 10, ypos, currentAcc, new Color(.4f, .4f, .4f, color.a)); if (OPTION_RP_SHOW_ACC.state) {
xpos += ACCMAXWIDTH; Fonts.SMALLBOLD.drawString(xpos + ACCMAXWIDTH - currentAccWidth - 10, ypos, currentAcc, new Color(.4f, .4f, .4f, color.a));
if (!missed && gradeImage != null) { xpos += ACCMAXWIDTH;
}
if (gradeImage != null) {
gradeImage.draw(xpos, ypos); gradeImage.draw(xpos, ypos);
xpos += SQSIZE + 10;
} }
xpos += SQSIZE + 10;
Fonts.SMALLBOLD.drawString(xpos, ypos, this.player, color); Fonts.SMALLBOLD.drawString(xpos, ypos, this.player, color);
xpos += playerwidth; xpos += playerwidth;
if (!this.mods.isEmpty()) { if (!this.mods.isEmpty()) {
@ -277,18 +289,29 @@ public class ReplayPlayback {
xpos += modwidth; xpos += modwidth;
} }
xpos += 10; xpos += 10;
showHitImage(renderdelta, xpos, ypos); if (OPTION_RP_SHOW_HITS.state) {
if (missed) { showHitImage(renderdelta, xpos, ypos);
if (hitImageTimer < HITIMAGEDEADFADE) { }
float progress = (float) hitImageTimer / HITIMAGEDEADFADE; if (OPTION_RP_SHOW_MISSES.state) {
float failposy = this.failposy + 50f * OUT_QUART.calc(progress); 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); Color col = new Color(originalcolor);
col.a = 1f - IN_QUAD.calc(clamp(progress * 2f, 0f, 1f)); col.a = 1f - IN_QUAD.calc(clamp(progress * 2f, 0f, 1f));
Fonts.SMALLBOLD.drawString(failposx - playerwidth / 2, failposy, player, col); Fonts.SMALLBOLD.drawString(mi.posx - playerwidth / 2, failposy, player, col);
Color failimgcol = new Color(1f, 1f, 1f, col.a); Color failimgcol = new Color(1f, 1f, 1f, col.a);
Image failimg = hitResults[HIT_MISS].getScaledCopy(SQSIZE + 5, SQSIZE + 5); Image failimg = hitResults[HIT_MISS].getScaledCopy(SQSIZE + 5, SQSIZE + 5);
failimg.draw(failposx + playerwidth / 2 + 5, failposy + 2f, failimgcol); failimg.draw(mi.posx + playerwidth / 2 + 5, failposy + 2f, failimgcol);
mi.timer += renderdelta;
} }
}
if (knockedout) {
return; return;
} }
int y = currentFrame.getScaledY(); int y = currentFrame.getScaledY();
@ -300,32 +323,44 @@ public class ReplayPlayback {
public boolean shouldDrawCursor() public boolean shouldDrawCursor()
{ {
return !missed; return !knockedout;
} }
private void processKeys() { private void processKeys() {
int keys = currentFrame.getKeys(); int keys = currentFrame.getKeys();
int KEY_DELAY = 10;
if ((keys & 5) == 5) { if ((keys & 5) == 5) {
keydelay[0] = KEY_DELAY; keydelay[0] = OPTION_RP_KEYPRESS_DELAY.val;
} }
if ((keys & 10) == 10) { if ((keys & 10) == 10) {
keydelay[1] = KEY_DELAY; keydelay[1] = OPTION_RP_KEYPRESS_DELAY.val;
} }
if ((keys ^ 5) == 4) { if ((keys ^ 5) == 4) {
keydelay[2] = KEY_DELAY; keydelay[2] = OPTION_RP_KEYPRESS_DELAY.val;
} }
if ((keys ^ 10) == 8) { if ((keys ^ 10) == 8) {
keydelay[3] = KEY_DELAY; 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 { public static class HitData
{
int combobreaktime = -1;
LinkedList<Integer> time300 = new LinkedList<>(); LinkedList<Integer> time300 = new LinkedList<>();
LinkedList<Integer> time100 = new LinkedList<>(); LinkedList<Integer> time100 = new LinkedList<>();
LinkedList<Integer> time50 = new LinkedList<>(); LinkedList<Integer> time50 = new LinkedList<>();
LinkedList<Integer> timeCombobreaks = new LinkedList<>();
LinkedList<AccData> acc = new LinkedList<>(); LinkedList<AccData> acc = new LinkedList<>();
LinkedList<ComboData> combo = new LinkedList<>(); LinkedList<ComboData> combo = new LinkedList<>();
@ -339,11 +374,11 @@ public class ReplayPlayback {
while (true) { while (true) {
byte[] time = new byte[4]; byte[] time = new byte[4];
int rd = in.read(time); int rd = in.read(time);
if (rd == 0) { if (rd <= 0) {
break; break;
} }
if (rd != 4) { if (rd != 4) {
throw new RuntimeException(); throw new RuntimeException("expected 4 bytes, got " + rd);
} }
byte[] _time = { time[3], time[2], time[1], time[0] }; byte[] _time = { time[3], time[2], time[1], time[0] };
lasttime = ByteBuffer.wrap(_time).getInt(); lasttime = ByteBuffer.wrap(_time).getInt();
@ -379,30 +414,22 @@ public class ReplayPlayback {
int c = ByteBuffer.wrap(_time).getInt(); int c = ByteBuffer.wrap(_time).getInt();
combo.add(new ComboData(lasttime, c)); combo.add(new ComboData(lasttime, c));
if (c < lastcombo) { if (c < lastcombo) {
combobreaktime = lasttime; timeCombobreaks.add(lasttime);
} else {
lastcombo = c;
} }
lastcombo = c;
break; break;
default: default:
throw new RuntimeException(); throw new RuntimeException("unexpected data");
}
if (combobreaktime != -1) {
break;
} }
} }
if (combobreaktime == -1) { if (lasttime == -1) {
combobreaktime = lasttime;
}
if (combobreaktime == -1) {
throw new RuntimeException("nodata"); throw new RuntimeException("nodata");
} }
Entrypoint.sout(String.format( Entrypoint.sout(String.format(
"%s combobreak at %d, lastcombo %d lastacc %f", "%s lastcombo %d lasttime %d",
file.getName(), file.getName(),
combobreaktime,
lastcombo, lastcombo,
acc.getLast().acc lasttime
)); ));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -446,5 +473,4 @@ public class ReplayPlayback {
this.combo = combo; this.combo = combo;
} }
} }
} }

View File

@ -161,6 +161,14 @@ 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_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,

View File

@ -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);
}
};
} }