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() {
|
||||
backButton.resetHover();
|
||||
resetBarNotification();
|
||||
resetCursorLocations();
|
||||
resetTooltip();
|
||||
}
|
||||
|
||||
|
@ -371,10 +372,16 @@ public class UI {
|
|||
GameImage.CURSOR_MIDDLE.destroySkinImage();
|
||||
GameImage.CURSOR_TRAIL.destroySkinImage();
|
||||
cursorAngle = 0f;
|
||||
GameImage.CURSOR.getImage().setRotation(0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all cursor location data.
|
||||
*/
|
||||
private static void resetCursorLocations() {
|
||||
lastX = lastY = -1;
|
||||
cursorX.clear();
|
||||
cursorY.clear();
|
||||
GameImage.CURSOR.getImage().setRotation(0f);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -180,4 +180,10 @@ public class Circle implements HitObject {
|
|||
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@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.
|
||||
* @param trackPosition the current track position
|
||||
|
|
|
@ -51,7 +51,9 @@ public class Spinner implements HitObject {
|
|||
private static final int FADE_IN_TIME = 500;
|
||||
|
||||
/** 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. */
|
||||
private OsuHitObject hitObject;
|
||||
|
@ -261,6 +263,30 @@ public class Spinner implements HitObject {
|
|||
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.
|
||||
* @param angle the angle to rotate (in radians)
|
||||
|
|
|
@ -263,6 +263,67 @@ public class Game extends BasicGameState {
|
|||
int firstObjectTime = osu.objects[0].getTime();
|
||||
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
|
||||
if (GameMod.FLASHLIGHT.isActive()) {
|
||||
// render hit objects offscreen
|
||||
|
@ -281,6 +342,9 @@ public class Game extends BasicGameState {
|
|||
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
|
||||
mouseX = pausedMouseX;
|
||||
mouseY = pausedMouseY;
|
||||
} else if (GameMod.AUTO.isActive()) {
|
||||
mouseX = autoMouseX;
|
||||
mouseY = autoMouseY;
|
||||
} else if (isReplay) {
|
||||
mouseX = replayX;
|
||||
mouseY = replayY;
|
||||
|
@ -445,7 +509,9 @@ public class Game extends BasicGameState {
|
|||
cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY);
|
||||
}
|
||||
|
||||
if (!isReplay)
|
||||
if (GameMod.AUTO.isActive())
|
||||
UI.draw(g, autoMouseX, autoMouseY, autoMousePress);
|
||||
else if (!isReplay)
|
||||
UI.draw(g);
|
||||
else
|
||||
UI.draw(g, replayX, replayY, replayKeyPressed);
|
||||
|
@ -577,6 +643,7 @@ public class Game extends BasicGameState {
|
|||
|
||||
// go to ranking screen
|
||||
else {
|
||||
boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
|
||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||
if (isReplay)
|
||||
data.setReplay(replay);
|
||||
|
@ -587,13 +654,13 @@ public class Game extends BasicGameState {
|
|||
replayFrames.addFirst(ReplayFrame.getStartFrame(replaySkipTime));
|
||||
replayFrames.addFirst(ReplayFrame.getStartFrame(0));
|
||||
Replay r = data.getReplay(replayFrames.toArray(new ReplayFrame[replayFrames.size()]));
|
||||
if (r != null)
|
||||
if (r != null && !unranked)
|
||||
r.save();
|
||||
}
|
||||
ScoreData score = data.getScoreData(osu);
|
||||
|
||||
// add score to database
|
||||
if (!GameMod.AUTO.isActive() && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive() && !isReplay)
|
||||
if (!unranked && !isReplay)
|
||||
ScoreDB.addScore(score);
|
||||
|
||||
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
|
||||
if (isReplay) {
|
||||
// unhide cursor
|
||||
// unhide cursor for "auto" mod and replays
|
||||
if (GameMod.AUTO.isActive() || isReplay)
|
||||
UI.showCursor();
|
||||
|
||||
// load replay frames
|
||||
if (isReplay) {
|
||||
// load mods
|
||||
previousMods = GameMod.getModState();
|
||||
GameMod.loadModState(replay.mods);
|
||||
|
@ -1048,8 +1116,10 @@ public class Game extends BasicGameState {
|
|||
}
|
||||
}
|
||||
};
|
||||
replayThreadRunning = true;
|
||||
replayThread.start();
|
||||
if (!GameMod.AUTO.isActive()) { // "auto" mod: ignore replay frames
|
||||
replayThreadRunning = true;
|
||||
replayThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
// initialize replay-recording structures
|
||||
|
@ -1073,10 +1143,13 @@ public class Game extends BasicGameState {
|
|||
throws SlickException {
|
||||
// container.setMouseGrabbed(false);
|
||||
|
||||
// re-hide cursor
|
||||
if (GameMod.AUTO.isActive() || isReplay)
|
||||
UI.hideCursor();
|
||||
|
||||
// replays
|
||||
if (isReplay) {
|
||||
GameMod.loadModState(previousMods);
|
||||
UI.hideCursor();
|
||||
killReplayThread();
|
||||
}
|
||||
}
|
||||
|
@ -1331,4 +1404,20 @@ public class Game extends BasicGameState {
|
|||
int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier());
|
||||
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