Draw cursor location in "auto" mod.
Moves the cursor between hit objects and along hit object paths (slider curves, spinner circles). - Added 'getPointAt(trackPosition)' and 'getEndTime()' methods to HitObject interface. - Unhide default cursor for "auto" plays. Other changes: - Don't save replays for unranked plays ("auto", "relax", "autopilot" mods). - For "auto" replays, don't parse replay frames: use default "auto" behavior instead. - Fixed cursor location data not being reset upon entering states. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
f23159d003
commit
e6206e52d4
|
@ -173,6 +173,7 @@ public class UI {
|
||||||
public static void enter() {
|
public static void enter() {
|
||||||
backButton.resetHover();
|
backButton.resetHover();
|
||||||
resetBarNotification();
|
resetBarNotification();
|
||||||
|
resetCursorLocations();
|
||||||
resetTooltip();
|
resetTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,10 +372,16 @@ public class UI {
|
||||||
GameImage.CURSOR_MIDDLE.destroySkinImage();
|
GameImage.CURSOR_MIDDLE.destroySkinImage();
|
||||||
GameImage.CURSOR_TRAIL.destroySkinImage();
|
GameImage.CURSOR_TRAIL.destroySkinImage();
|
||||||
cursorAngle = 0f;
|
cursorAngle = 0f;
|
||||||
|
GameImage.CURSOR.getImage().setRotation(0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all cursor location data.
|
||||||
|
*/
|
||||||
|
private static void resetCursorLocations() {
|
||||||
lastX = lastY = -1;
|
lastX = lastY = -1;
|
||||||
cursorX.clear();
|
cursorX.clear();
|
||||||
cursorY.clear();
|
cursorY.clear();
|
||||||
GameImage.CURSOR.getImage().setRotation(0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -180,4 +180,10 @@ public class Circle implements HitObject {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float[] getPointAt(int trackPosition) { return new float[] { x, y }; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEndTime() { return hitObject.getTime(); }
|
||||||
}
|
}
|
|
@ -49,4 +49,17 @@ public interface HitObject {
|
||||||
* @return true if a hit result was processed
|
* @return true if a hit result was processed
|
||||||
*/
|
*/
|
||||||
public boolean mousePressed(int x, int y);
|
public boolean mousePressed(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the coordinates of the hit object at a given track position.
|
||||||
|
* @param trackPosition the track position
|
||||||
|
* @return the [x,y] coordinates
|
||||||
|
*/
|
||||||
|
public float[] getPointAt(int trackPosition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the end time of the hit object.
|
||||||
|
* @return the end time, in milliseconds
|
||||||
|
*/
|
||||||
|
public int getEndTime();
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,6 +458,24 @@ public class Slider implements HitObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float[] getPointAt(int trackPosition) {
|
||||||
|
if (trackPosition <= hitObject.getTime())
|
||||||
|
return new float[] { x, y };
|
||||||
|
else if (trackPosition >= hitObject.getTime() + sliderTimeTotal) {
|
||||||
|
if (hitObject.getRepeatCount() % 2 == 0)
|
||||||
|
return new float[] { x, y };
|
||||||
|
else {
|
||||||
|
int lastIndex = hitObject.getSliderX().length;
|
||||||
|
return new float[] { curve.getX(lastIndex), curve.getY(lastIndex) };
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return curve.pointAt(getT(trackPosition, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEndTime() { return hitObject.getTime() + (int) sliderTimeTotal; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the t value based on the given track position.
|
* Returns the t value based on the given track position.
|
||||||
* @param trackPosition the current track position
|
* @param trackPosition the current track position
|
||||||
|
|
|
@ -51,7 +51,9 @@ public class Spinner implements HitObject {
|
||||||
private static final int FADE_IN_TIME = 500;
|
private static final int FADE_IN_TIME = 500;
|
||||||
|
|
||||||
/** PI constants. */
|
/** PI constants. */
|
||||||
private static final float TWO_PI = (float) (Math.PI * 2);
|
private static final float
|
||||||
|
TWO_PI = (float) (Math.PI * 2),
|
||||||
|
HALF_PI = (float) (Math.PI / 2);
|
||||||
|
|
||||||
/** The associated OsuHitObject. */
|
/** The associated OsuHitObject. */
|
||||||
private OsuHitObject hitObject;
|
private OsuHitObject hitObject;
|
||||||
|
@ -261,6 +263,30 @@ public class Spinner implements HitObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float[] getPointAt(int trackPosition) {
|
||||||
|
// get spinner time
|
||||||
|
int timeDiff;
|
||||||
|
float x = hitObject.getScaledX(), y = hitObject.getScaledY();
|
||||||
|
if (trackPosition <= hitObject.getTime())
|
||||||
|
timeDiff = 0;
|
||||||
|
else if (trackPosition >= hitObject.getEndTime())
|
||||||
|
timeDiff = hitObject.getEndTime() - hitObject.getTime();
|
||||||
|
else
|
||||||
|
timeDiff = trackPosition - hitObject.getTime();
|
||||||
|
|
||||||
|
// calculate point
|
||||||
|
float angle = timeDiff / 20f - HALF_PI;
|
||||||
|
final float r = height / 10f;
|
||||||
|
return new float[] {
|
||||||
|
(float) (x + r * Math.cos(angle)),
|
||||||
|
(float) (y + r * Math.sin(angle))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEndTime() { return hitObject.getEndTime(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates the spinner by an angle.
|
* Rotates the spinner by an angle.
|
||||||
* @param angle the angle to rotate (in radians)
|
* @param angle the angle to rotate (in radians)
|
||||||
|
|
|
@ -263,6 +263,67 @@ public class Game extends BasicGameState {
|
||||||
int firstObjectTime = osu.objects[0].getTime();
|
int firstObjectTime = osu.objects[0].getTime();
|
||||||
int timeDiff = firstObjectTime - trackPosition;
|
int timeDiff = firstObjectTime - trackPosition;
|
||||||
|
|
||||||
|
// "auto" mod: move cursor automatically
|
||||||
|
int autoMouseX = width / 2, autoMouseY = height / 2;
|
||||||
|
boolean autoMousePress = false;
|
||||||
|
if (GameMod.AUTO.isActive()) {
|
||||||
|
float[] autoXY = null;
|
||||||
|
if (isLeadIn()) {
|
||||||
|
// lead-in
|
||||||
|
float progress = Math.max((float) (leadInTime - osu.audioLeadIn) / approachTime, 0f);
|
||||||
|
autoMouseY = (int) (height / (2f - progress));
|
||||||
|
} else if (objectIndex == 0 && trackPosition < firstObjectTime) {
|
||||||
|
// before first object
|
||||||
|
timeDiff = firstObjectTime - trackPosition;
|
||||||
|
if (timeDiff < approachTime) {
|
||||||
|
float[] xy = hitObjects[0].getPointAt(trackPosition);
|
||||||
|
autoXY = getPointAt(autoMouseX, autoMouseY, xy[0], xy[1], 1f - ((float) timeDiff / approachTime));
|
||||||
|
}
|
||||||
|
} else if (objectIndex < osu.objects.length) {
|
||||||
|
// normal object
|
||||||
|
int objectTime = osu.objects[objectIndex].getTime();
|
||||||
|
if (trackPosition < objectTime) {
|
||||||
|
float[] xyStart = hitObjects[objectIndex - 1].getPointAt(trackPosition);
|
||||||
|
int startTime = hitObjects[objectIndex - 1].getEndTime();
|
||||||
|
if (osu.breaks != null && breakIndex < osu.breaks.size()) {
|
||||||
|
// starting a break: keep cursor at previous hit object position
|
||||||
|
if (breakTime > 0 || objectTime > osu.breaks.get(breakIndex))
|
||||||
|
autoXY = xyStart;
|
||||||
|
|
||||||
|
// after a break ends: move startTime to break end time
|
||||||
|
else if (breakIndex > 1) {
|
||||||
|
int lastBreakEndTime = osu.breaks.get(breakIndex - 1);
|
||||||
|
if (objectTime > lastBreakEndTime && startTime < lastBreakEndTime)
|
||||||
|
startTime = lastBreakEndTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (autoXY == null) {
|
||||||
|
float[] xyEnd = hitObjects[objectIndex].getPointAt(trackPosition);
|
||||||
|
int totalTime = objectTime - startTime;
|
||||||
|
autoXY = getPointAt(xyStart[0], xyStart[1], xyEnd[0], xyEnd[1], (float) (trackPosition - startTime) / totalTime);
|
||||||
|
|
||||||
|
// hit circles: show a mouse press
|
||||||
|
int offset300 = hitResultOffset[GameData.HIT_300];
|
||||||
|
if ((osu.objects[objectIndex].isCircle() && objectTime - trackPosition < offset300) ||
|
||||||
|
(osu.objects[objectIndex - 1].isCircle() && trackPosition - osu.objects[objectIndex - 1].getTime() < offset300))
|
||||||
|
autoMousePress = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
autoXY = hitObjects[objectIndex].getPointAt(trackPosition);
|
||||||
|
autoMousePress = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// last object
|
||||||
|
autoXY = hitObjects[objectIndex - 1].getPointAt(trackPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mouse coordinates
|
||||||
|
if (autoXY != null) {
|
||||||
|
autoMouseX = (int) autoXY[0];
|
||||||
|
autoMouseY = (int) autoXY[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "flashlight" mod: restricted view of hit objects around cursor
|
// "flashlight" mod: restricted view of hit objects around cursor
|
||||||
if (GameMod.FLASHLIGHT.isActive()) {
|
if (GameMod.FLASHLIGHT.isActive()) {
|
||||||
// render hit objects offscreen
|
// render hit objects offscreen
|
||||||
|
@ -281,6 +342,9 @@ public class Game extends BasicGameState {
|
||||||
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
|
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
|
||||||
mouseX = pausedMouseX;
|
mouseX = pausedMouseX;
|
||||||
mouseY = pausedMouseY;
|
mouseY = pausedMouseY;
|
||||||
|
} else if (GameMod.AUTO.isActive()) {
|
||||||
|
mouseX = autoMouseX;
|
||||||
|
mouseY = autoMouseY;
|
||||||
} else if (isReplay) {
|
} else if (isReplay) {
|
||||||
mouseX = replayX;
|
mouseX = replayX;
|
||||||
mouseY = replayY;
|
mouseY = replayY;
|
||||||
|
@ -445,7 +509,9 @@ public class Game extends BasicGameState {
|
||||||
cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY);
|
cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isReplay)
|
if (GameMod.AUTO.isActive())
|
||||||
|
UI.draw(g, autoMouseX, autoMouseY, autoMousePress);
|
||||||
|
else if (!isReplay)
|
||||||
UI.draw(g);
|
UI.draw(g);
|
||||||
else
|
else
|
||||||
UI.draw(g, replayX, replayY, replayKeyPressed);
|
UI.draw(g, replayX, replayY, replayKeyPressed);
|
||||||
|
@ -577,6 +643,7 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// go to ranking screen
|
// go to ranking screen
|
||||||
else {
|
else {
|
||||||
|
boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
|
||||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||||
if (isReplay)
|
if (isReplay)
|
||||||
data.setReplay(replay);
|
data.setReplay(replay);
|
||||||
|
@ -587,13 +654,13 @@ public class Game extends BasicGameState {
|
||||||
replayFrames.addFirst(ReplayFrame.getStartFrame(replaySkipTime));
|
replayFrames.addFirst(ReplayFrame.getStartFrame(replaySkipTime));
|
||||||
replayFrames.addFirst(ReplayFrame.getStartFrame(0));
|
replayFrames.addFirst(ReplayFrame.getStartFrame(0));
|
||||||
Replay r = data.getReplay(replayFrames.toArray(new ReplayFrame[replayFrames.size()]));
|
Replay r = data.getReplay(replayFrames.toArray(new ReplayFrame[replayFrames.size()]));
|
||||||
if (r != null)
|
if (r != null && !unranked)
|
||||||
r.save();
|
r.save();
|
||||||
}
|
}
|
||||||
ScoreData score = data.getScoreData(osu);
|
ScoreData score = data.getScoreData(osu);
|
||||||
|
|
||||||
// add score to database
|
// add score to database
|
||||||
if (!GameMod.AUTO.isActive() && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive() && !isReplay)
|
if (!unranked && !isReplay)
|
||||||
ScoreDB.addScore(score);
|
ScoreDB.addScore(score);
|
||||||
|
|
||||||
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
|
@ -984,11 +1051,12 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load replay frames
|
// unhide cursor for "auto" mod and replays
|
||||||
if (isReplay) {
|
if (GameMod.AUTO.isActive() || isReplay)
|
||||||
// unhide cursor
|
|
||||||
UI.showCursor();
|
UI.showCursor();
|
||||||
|
|
||||||
|
// load replay frames
|
||||||
|
if (isReplay) {
|
||||||
// load mods
|
// load mods
|
||||||
previousMods = GameMod.getModState();
|
previousMods = GameMod.getModState();
|
||||||
GameMod.loadModState(replay.mods);
|
GameMod.loadModState(replay.mods);
|
||||||
|
@ -1048,9 +1116,11 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (!GameMod.AUTO.isActive()) { // "auto" mod: ignore replay frames
|
||||||
replayThreadRunning = true;
|
replayThreadRunning = true;
|
||||||
replayThread.start();
|
replayThread.start();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// initialize replay-recording structures
|
// initialize replay-recording structures
|
||||||
else {
|
else {
|
||||||
|
@ -1073,10 +1143,13 @@ public class Game extends BasicGameState {
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
// container.setMouseGrabbed(false);
|
// container.setMouseGrabbed(false);
|
||||||
|
|
||||||
|
// re-hide cursor
|
||||||
|
if (GameMod.AUTO.isActive() || isReplay)
|
||||||
|
UI.hideCursor();
|
||||||
|
|
||||||
// replays
|
// replays
|
||||||
if (isReplay) {
|
if (isReplay) {
|
||||||
GameMod.loadModState(previousMods);
|
GameMod.loadModState(previousMods);
|
||||||
UI.hideCursor();
|
|
||||||
killReplayThread();
|
killReplayThread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1331,4 +1404,20 @@ public class Game extends BasicGameState {
|
||||||
int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier());
|
int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier());
|
||||||
replayFrames.add(new ReplayFrame(timeDiff, time, cx, cy, lastKeysPressed));
|
replayFrames.add(new ReplayFrame(timeDiff, time, cx, cy, lastKeysPressed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the point at the t value between a start and end point.
|
||||||
|
* @param startX the starting x coordinate
|
||||||
|
* @param startY the starting y coordinate
|
||||||
|
* @param endX the ending x coordinate
|
||||||
|
* @param endY the ending y coordinate
|
||||||
|
* @param t the t value [0, 1]
|
||||||
|
* @return the [x,y] coordinates
|
||||||
|
*/
|
||||||
|
private float[] getPointAt(float startX, float startY, float endX, float endY, float t) {
|
||||||
|
float[] xy = new float[2];
|
||||||
|
xy[0] = startX + (endX - startX) * t;
|
||||||
|
xy[1] = startY + (endY - startY) * t;
|
||||||
|
return xy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user