Follow-up to #52.

- Animate skip button during lead in.
- Update flashlight radius during lead in.
- Check if keys are set before calling gameKeyPressed().
- Deleted mouse moved/dragged events (no longer used).
- Renamed updateGameKeyPress().
- Added "auto"/"relax" mod checks to gameKeyPressed().

Also fixed a bug where the MenuButton class wasn't properly resetting image alpha/rotation in certain cases.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-03-19 03:04:35 -04:00
parent ba20953634
commit b46c589b97
8 changed files with 180 additions and 188 deletions

View File

@ -1236,12 +1236,12 @@ public class GameData {
/** /**
* Returns a Replay object encapsulating all game data. * Returns a Replay object encapsulating all game data.
* If a replay already exists and frames is null, the existing object will * If a replay already exists and frames is null, the existing object will be returned.
* be returned.
* @param frames the replay frames * @param frames the replay frames
* @param osu the associated OsuFile
* @return the Replay object, or null if none exists and frames is null * @return the Replay object, or null if none exists and frames is null
*/ */
public Replay getReplay(ReplayFrame[] frames, OsuFile file) { public Replay getReplay(ReplayFrame[] frames, OsuFile osu) {
if (replay != null && frames == null) if (replay != null && frames == null)
return replay; return replay;
@ -1251,7 +1251,7 @@ public class GameData {
replay = new Replay(); replay = new Replay();
replay.mode = OsuFile.MODE_OSU; replay.mode = OsuFile.MODE_OSU;
replay.version = Updater.get().getBuildDate(); replay.version = Updater.get().getBuildDate();
replay.beatmapHash = Utils.getMD5(file.getFile()); replay.beatmapHash = (osu == null) ? "" : Utils.getMD5(osu.getFile());
replay.playerName = ""; // TODO replay.playerName = ""; // TODO
replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO
replay.hit300 = (short) hitResultCount[HIT_300]; replay.hit300 = (short) hitResultCount[HIT_300];

View File

@ -200,7 +200,7 @@ public class MenuButton {
if ((hoverEffect & EFFECT_ROTATE) > 0) if ((hoverEffect & EFFECT_ROTATE) > 0)
image.setRotation(angle); image.setRotation(angle);
image.draw(x - xRadius, y - yRadius, filter); image.draw(x - xRadius, y - yRadius, filter);
if (image != this.img) { if (image == this.img) {
image.setAlpha(oldAlpha); image.setAlpha(oldAlpha);
image.setRotation(oldAngle); image.setRotation(oldAngle);
} }

View File

@ -541,36 +541,33 @@ public class Utils {
return s.useDelimiter("\\A").hasNext() ? s.next() : ""; return s.useDelimiter("\\A").hasNext() ? s.next() : "";
} }
} }
/** /**
* Returns the md5 hash of a file in hex form. * Returns the md5 hash of a file in hex form.
* @param file * @param file the file to hash
* @return the md5 hash * @return the md5 hash
*/ */
public static String getMD5(File file){ public static String getMD5(File file) {
try { try {
InputStream in = new BufferedInputStream(new FileInputStream(file)); InputStream in = new BufferedInputStream(new FileInputStream(file));
MessageDigest md = MessageDigest.getInstance("MD5"); MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buf = new byte[4096]; byte[] buf = new byte[4096];
while(true) { while (true) {
int len = in.read(buf); int len = in.read(buf);
if (len < 0) if (len < 0)
break; break;
md.update(buf, 0, len); md.update(buf, 0, len);
} }
in.close(); in.close();
byte[] md5byte = md.digest(); byte[] md5byte = md.digest();
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
for (byte b : md5byte) { for (byte b : md5byte)
result.append(String.format("%02x", b)); result.append(String.format("%02x", b));
}
return result.toString(); return result.toString();
} catch (NoSuchAlgorithmException | IOException e) { } catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace(); ErrorHandler.error("Failed to calculate MD5 hash.", e, true);
Log.error(e);
} }
return null; return null;
} }

View File

@ -23,7 +23,6 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.OsuHitObject; import itdelatrisu.opsu.OsuHitObject;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Game;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;

View File

@ -38,7 +38,7 @@ public interface HitObject {
* @param mouseX the x coordinate of the mouse * @param mouseX the x coordinate of the mouse
* @param mouseY the y coordinate of the mouse * @param mouseY the y coordinate of the mouse
* @param keyPressed whether or not a game key is currently pressed * @param keyPressed whether or not a game key is currently pressed
* @param trackPosition the track Position * @param trackPosition the track position
* @return true if object ended * @return true if object ended
*/ */
public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition); public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition);
@ -47,7 +47,7 @@ public interface HitObject {
* Processes a mouse click. * Processes a mouse click.
* @param x the x coordinate of the mouse * @param x the x coordinate of the mouse
* @param y the y coordinate of the mouse * @param y the y coordinate of the mouse
* @param trackPosition the track Position * @param trackPosition the track position
* @return true if a hit result was processed * @return true if a hit result was processed
*/ */
public boolean mousePressed(int x, int y, int trackPosition); public boolean mousePressed(int x, int y, int trackPosition);

View File

@ -38,9 +38,9 @@ import org.newdawn.slick.Image;
* Data type representing a slider object. * Data type representing a slider object.
*/ */
public class Slider implements HitObject { public class Slider implements HitObject {
/** Slider ball animation. */ /** Slider ball frames. */
private static Image[] sliderBallImages; private static Image[] sliderBallImages;
/** Slider movement speed multiplier. */ /** Slider movement speed multiplier. */
private static float sliderMultiplier = 1.0f; private static float sliderMultiplier = 1.0f;
@ -116,7 +116,7 @@ public class Slider implements HitObject {
sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() }; sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() };
for (int i = 0; i < sliderBallImages.length; i++) for (int i = 0; i < sliderBallImages.length; i++)
sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128); sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128);
GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128)); GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128));
GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter)); GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));
GameImage.SLIDER_TICK.setImage(GameImage.SLIDER_TICK.getImage().getScaledCopy(diameter / 4, diameter / 4)); GameImage.SLIDER_TICK.setImage(GameImage.SLIDER_TICK.getImage().getScaledCopy(diameter / 4, diameter / 4));
@ -148,7 +148,6 @@ public class Slider implements HitObject {
this.curve = new LinearBezier(hitObject, color); this.curve = new LinearBezier(hitObject, color);
} }
@SuppressWarnings("deprecation")
@Override @Override
public void draw(Graphics g, int trackPosition) { public void draw(Graphics g, int trackPosition) {
int timeDiff = hitObject.getTime() - trackPosition; int timeDiff = hitObject.getTime() - trackPosition;
@ -215,25 +214,24 @@ public class Slider implements HitObject {
} }
} }
} }
if (timeDiff >= 0) { if (timeDiff >= 0) {
// approach circle // approach circle
color.a = 1 - scale; color.a = 1 - scale;
GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(x, y, color); GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(x, y, color);
} else { } else {
//since update might not have run before drawing during replay, // Since update() might not have run before drawing during a replay, the
//the slider time may not have been calculated. // slider time may not have been calculated, which causes NAN numbers and flicker.
//Which will cause NAN numbers and cause flicker.
if (sliderTime == 0) if (sliderTime == 0)
return; return;
float[] c = curve.pointAt(getT(trackPosition, false)); float[] c = curve.pointAt(getT(trackPosition, false));
float[] c2 = curve.pointAt(getT(trackPosition, false) + 0.01f); float[] c2 = curve.pointAt(getT(trackPosition, false) + 0.01f);
float t = getT(trackPosition, false); float t = getT(trackPosition, false);
//float dis = hitObject.getPixelLength()*OsuHitObject.getXMultiplier() * (t -(int)t); // float dis = hitObject.getPixelLength() * OsuHitObject.getXMultiplier() * (t - (int) t);
//Image sliderBallFrame = sliderBallImages[ (int)(dis/ (diameter*Math.PI) *30)%sliderBallImages.length]; // Image sliderBallFrame = sliderBallImages[(int) (dis / (diameter * Math.PI) * 30) % sliderBallImages.length];
Image sliderBallFrame = sliderBallImages[(int) (t * sliderTime * 60 / 1000) % sliderBallImages.length]; Image sliderBallFrame = sliderBallImages[(int) (t * sliderTime * 60 / 1000) % sliderBallImages.length];
float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI); float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI);
sliderBallFrame.setRotation(angle); sliderBallFrame.setRotation(angle);
sliderBallFrame.drawCentered(c[0], c[1]); sliderBallFrame.drawCentered(c[0], c[1]);

View File

@ -23,7 +23,6 @@ import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.OsuHitObject; import itdelatrisu.opsu.OsuHitObject;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Game;

View File

@ -173,10 +173,10 @@ public class Game extends BasicGameState {
/** The last replay frame time. */ /** The last replay frame time. */
private int lastReplayTime = 0; private int lastReplayTime = 0;
/** The keys from the previous replayFrame */ /** The keys from the previous replay frame. */
private int lastReplayKeys = 0; private int lastReplayKeys = 0;
/** The last game keys pressed. */ /** The last game keys pressed. */
private int lastKeysPressed = ReplayFrame.KEY_NONE; private int lastKeysPressed = ReplayFrame.KEY_NONE;
@ -418,7 +418,7 @@ public class Game extends BasicGameState {
else { else {
// game elements // game elements
data.drawGameElements(g, false, objectIndex == 0); data.drawGameElements(g, false, objectIndex == 0);
// skip beginning // skip beginning
if (objectIndex == 0 && if (objectIndex == 0 &&
trackPosition < osu.objects[0].getTime() - SKIP_OFFSET) trackPosition < osu.objects[0].getTime() - SKIP_OFFSET)
@ -519,36 +519,37 @@ public class Game extends BasicGameState {
UI.draw(g, autoMouseX, autoMouseY, Utils.isGameKeyPressed()); UI.draw(g, autoMouseX, autoMouseY, Utils.isGameKeyPressed());
else else
UI.draw(g); UI.draw(g);
} }
@Override @Override
public void update(GameContainer container, StateBasedGame game, int delta) public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException { throws SlickException {
UI.update(delta); UI.update(delta);
int mouseX, mouseY; int mouseX = input.getMouseX(), mouseY = input.getMouseY();
mouseX = input.getMouseX(); skipButton.hoverUpdate(delta, mouseX, mouseY);
mouseY = input.getMouseY(); int trackPosition = MusicController.getPosition();
if (isLeadIn()) { // stop updating during song lead-in
// stop updating during song lead-in
if (isLeadIn()) {
leadInTime -= delta; leadInTime -= delta;
if (!isLeadIn()) if (!isLeadIn())
MusicController.resume(); MusicController.resume();
updateFlashlightRadius(delta, trackPosition);
return; return;
} }
//hover updates only the real mouse position // normal game update
//but doesn't hover the replays if (!isReplay)
skipButton.hoverUpdate(delta, mouseX, mouseY);
int trackPosition = MusicController.getPosition();
if (!isReplay) {
addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition); addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition);
} else {
//out of frames, use previous data // watching replay
if (replayIndex >= replay.frames.length){ else {
// out of frames, use previous data
if (replayIndex >= replay.frames.length)
updateGame(replayX, replayY, delta, MusicController.getPosition(), lastKeysPressed); updateGame(replayX, replayY, delta, MusicController.getPosition(), lastKeysPressed);
}
// update and run replay frames
while (replayIndex < replay.frames.length && trackPosition >= replay.frames[replayIndex].getTime()) { while (replayIndex < replay.frames.length && trackPosition >= replay.frames[replayIndex].getTime()) {
ReplayFrame frame = replay.frames[replayIndex]; ReplayFrame frame = replay.frames[replayIndex];
replayX = frame.getScaledX(); replayX = frame.getScaledX();
@ -556,68 +557,14 @@ public class Game extends BasicGameState {
replayKeyPressed = frame.isKeyPressed(); replayKeyPressed = frame.isKeyPressed();
lastKeysPressed = frame.getKeys(); lastKeysPressed = frame.getKeys();
runReplayFrame(frame); runReplayFrame(frame);
replayIndex++; replayIndex++;
} }
mouseX = replayX; mouseX = replayX;
mouseY = replayY; mouseY = replayY;
} }
// "flashlight" mod: calculate visible area radius
if (GameMod.FLASHLIGHT.isActive()) {
int width = container.getWidth(), height = container.getHeight();
boolean firstObject = (objectIndex == 0 && trackPosition < osu.objects[0].getTime());
if (isLeadIn()) {
// lead-in: expand area
float progress = Math.max((float) (leadInTime - osu.audioLeadIn) / approachTime, 0f);
flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress);
} else if (firstObject) {
// before first object: shrink area
int timeDiff = osu.objects[0].getTime() - trackPosition;
flashlightRadius = width;
if (timeDiff < approachTime) {
float progress = (float) timeDiff / approachTime;
flashlightRadius -= (width - (height * 2 / 3)) * (1 - progress);
}
} else {
// gameplay: size based on combo
int targetRadius;
int combo = data.getComboStreak();
if (combo < 100)
targetRadius = height * 2 / 3;
else if (combo < 200)
targetRadius = height / 2;
else
targetRadius = height / 3;
if (osu.breaks != null && breakIndex < osu.breaks.size() && breakTime > 0) {
// breaks: expand at beginning, shrink at end
flashlightRadius = targetRadius;
int endTime = osu.breaks.get(breakIndex);
int breakLength = endTime - breakTime;
if (breakLength > approachTime * 3) {
float progress = 1f;
if (trackPosition - breakTime < approachTime)
progress = (float) (trackPosition - breakTime) / approachTime;
else if (endTime - trackPosition < approachTime)
progress = (float) (endTime - trackPosition) / approachTime;
flashlightRadius += (width - flashlightRadius) * progress;
}
} else if (flashlightRadius != targetRadius) {
// radius size change
float radiusDiff = height * delta / 2000f;
if (flashlightRadius > targetRadius) {
flashlightRadius -= radiusDiff;
if (flashlightRadius < targetRadius)
flashlightRadius = targetRadius;
} else {
flashlightRadius += radiusDiff;
if (flashlightRadius > targetRadius)
flashlightRadius = targetRadius;
}
}
}
}
// "flashlight" mod: calculate visible area radius
updateFlashlightRadius(delta, trackPosition);
// returning from pause screen: must click previous mouse position // returning from pause screen: must click previous mouse position
if (pauseTime > -1) { if (pauseTime > -1) {
@ -643,18 +590,14 @@ public class Game extends BasicGameState {
} }
return; return;
} }
data.updateDisplays(delta);
// replays
if (isReplay) {
// skip intro
if (replaySkipTime > 0 && trackPosition > replaySkipTime) {
skipIntro();
replaySkipTime = -1;
}
}
data.updateDisplays(delta);
// replays: skip intro
if (isReplay && replaySkipTime > 0 && trackPosition > replaySkipTime) {
skipIntro();
replaySkipTime = -1;
}
// pause game if focus lost // pause game if focus lost
if (!container.hasFocus() && !GameMod.AUTO.isActive() && !isReplay) { if (!container.hasFocus() && !GameMod.AUTO.isActive() && !isReplay) {
@ -667,19 +610,17 @@ public class Game extends BasicGameState {
pauseTime = trackPosition; pauseTime = trackPosition;
game.enterState(Opsu.STATE_GAMEPAUSEMENU); game.enterState(Opsu.STATE_GAMEPAUSEMENU);
} }
} }
/** /**
* Updates the game * Updates the game.
* @param mouseX the mouse x position * @param mouseX the mouse x coordinate
* @param mouseY * @param mouseY the mouse y coordinate
* @param delta * @param delta the delta interval
* @param trackPosition the track position * @param trackPosition the track position
* @param keysPressed the keys that are pressed * @param keys the keys that are pressed
*/ */
private void updateGame(int mouseX, int mouseY, int delta, int trackPosition, int keysPressed) { private void updateGame(int mouseX, int mouseY, int delta, int trackPosition, int keys) {
// "Easy" mod: multiple "lives" // "Easy" mod: multiple "lives"
if (GameMod.EASY.isActive() && deathTime > -1) { if (GameMod.EASY.isActive() && deathTime > -1) {
if (data.getHealth() < 99f) if (data.getHealth() < 99f)
@ -690,7 +631,6 @@ public class Game extends BasicGameState {
} }
} }
// map complete! // map complete!
if (objectIndex >= hitObjects.length || (MusicController.trackEnded() && objectIndex > 0)) { if (objectIndex >= hitObjects.length || (MusicController.trackEnded() && objectIndex > 0)) {
// track ended before last object was processed: force a hit result // track ended before last object was processed: force a hit result
@ -787,7 +727,7 @@ public class Game extends BasicGameState {
} }
// update objects (loop in unlikely event of any skipped indexes) // update objects (loop in unlikely event of any skipped indexes)
boolean keyPressed = keysPressed != ReplayFrame.KEY_NONE; boolean keyPressed = keys != ReplayFrame.KEY_NONE;
while (objectIndex < hitObjects.length && trackPosition > osu.objects[objectIndex].getTime()) { while (objectIndex < hitObjects.length && trackPosition > osu.objects[objectIndex].getTime()) {
// check if we've already passed the next object's start time // check if we've already passed the next object's start time
boolean overlap = (objectIndex + 1 < hitObjects.length && boolean overlap = (objectIndex + 1 < hitObjects.length &&
@ -807,17 +747,18 @@ public class Game extends BasicGameState {
@Override @Override
public void keyPressed(int key, char c) { public void keyPressed(int key, char c) {
int trackPosition = MusicController.getPosition(); int trackPosition = MusicController.getPosition();
// game keys
int mouseX = input.getMouseX(); int mouseX = input.getMouseX();
int mouseY = input.getMouseY(); int mouseY = input.getMouseY();
// game keys
if (!Keyboard.isRepeatEvent()) { if (!Keyboard.isRepeatEvent()) {
int keys = 0; int keys = ReplayFrame.KEY_NONE;
if (key == Options.getGameKeyLeft()) if (key == Options.getGameKeyLeft())
keys = ReplayFrame.KEY_K1; keys = ReplayFrame.KEY_K1;
else if (key == Options.getGameKeyRight()) else if (key == Options.getGameKeyRight())
keys = ReplayFrame.KEY_K2; keys = ReplayFrame.KEY_K2;
gameKeyPressed(keys, mouseX, mouseY, trackPosition); if (keys != ReplayFrame.KEY_NONE)
gameKeyPressed(keys, mouseX, mouseY, trackPosition);
} }
switch (key) { switch (key) {
@ -942,16 +883,15 @@ public class Game extends BasicGameState {
game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_GAMEPAUSEMENU, new EmptyTransition(), new FadeInTransition(Color.black));
return; return;
} }
int keys = 0; // game keys
int keys = ReplayFrame.KEY_NONE;
if (button == Input.MOUSE_LEFT_BUTTON) if (button == Input.MOUSE_LEFT_BUTTON)
keys = ReplayFrame.KEY_M1; keys = ReplayFrame.KEY_M1;
else if (button == Input.MOUSE_RIGHT_BUTTON) else if (button == Input.MOUSE_RIGHT_BUTTON)
keys = ReplayFrame.KEY_M2; keys = ReplayFrame.KEY_M2;
if (keys != ReplayFrame.KEY_NONE)
gameKeyPressed(keys, x, y, MusicController.getPosition()); gameKeyPressed(keys, x, y, MusicController.getPosition());
} }
/** /**
@ -959,7 +899,7 @@ public class Game extends BasicGameState {
* @param keys the game keys pressed * @param keys the game keys pressed
* @param x the mouse x coordinate * @param x the mouse x coordinate
* @param y the mouse y coordinate * @param y the mouse y coordinate
* @param trackPosition the track Position * @param trackPosition the track position
*/ */
private void gameKeyPressed(int keys, int x, int y, int trackPosition) { private void gameKeyPressed(int keys, int x, int y, int trackPosition) {
// returning from pause screen // returning from pause screen
@ -976,20 +916,24 @@ public class Game extends BasicGameState {
} }
return; return;
} }
// skip beginning // skip beginning
if (skipButton.contains(x, y)) { if (skipButton.contains(x, y)) {
if (skipIntro()) if (skipIntro())
return; // successfully skipped return; // successfully skipped
} }
if(!isReplay && keys > 0) { // "auto" and "relax" mods: ignore user actions
lastKeysPressed |= keys; //sets keys bits if (GameMod.AUTO.isActive() || GameMod.RELAX.isActive())
return;
// send a game key press
if (!isReplay && keys != ReplayFrame.KEY_NONE) {
lastKeysPressed |= keys; // set keys bits
addReplayFrameAndRun(x, y, lastKeysPressed, trackPosition); addReplayFrameAndRun(x, y, lastKeysPressed, trackPosition);
} }
} }
@Override @Override
public void mouseReleased(int button, int x, int y) { public void mouseReleased(int button, int x, int y) {
if (Options.isMouseDisabled()) if (Options.isMouseDisabled())
@ -998,46 +942,40 @@ public class Game extends BasicGameState {
if (button == Input.MOUSE_MIDDLE_BUTTON) if (button == Input.MOUSE_MIDDLE_BUTTON)
return; return;
int keys = 0; int keys = ReplayFrame.KEY_NONE;
if (button == Input.MOUSE_LEFT_BUTTON) if (button == Input.MOUSE_LEFT_BUTTON)
keys = ReplayFrame.KEY_M1; keys = ReplayFrame.KEY_M1;
else if (button == Input.MOUSE_RIGHT_BUTTON) else if (button == Input.MOUSE_RIGHT_BUTTON)
keys = ReplayFrame.KEY_M2; keys = ReplayFrame.KEY_M2;
gameKeyReleased(keys, x, y, MusicController.getPosition()); if (keys != ReplayFrame.KEY_NONE)
gameKeyReleased(keys, x, y, MusicController.getPosition());
} }
@Override @Override
public void keyReleased(int key, char c) { public void keyReleased(int key, char c) {
int keys = 0; int keys = ReplayFrame.KEY_NONE;
if (key == Options.getGameKeyLeft()) if (key == Options.getGameKeyLeft())
keys = ReplayFrame.KEY_K1; keys = ReplayFrame.KEY_K1;
else if (key == Options.getGameKeyRight()) else if (key == Options.getGameKeyRight())
keys = ReplayFrame.KEY_K2; keys = ReplayFrame.KEY_K2;
gameKeyReleased(keys, input.getMouseX(), input.getMouseY(), MusicController.getPosition()); if (keys != ReplayFrame.KEY_NONE)
gameKeyReleased(keys, input.getMouseX(), input.getMouseY(), MusicController.getPosition());
} }
/** /**
* Handles a game key Released event. * Handles a game key released event.
* @param keys the game keys released * @param keys the game keys released
* @param x the mouse x coordinate * @param x the mouse x coordinate
* @param y the mouse y coordinate * @param y the mouse y coordinate
* @param trackPosition the track Position * @param trackPosition the track position
*/ */
private void gameKeyReleased(int keys, int x, int y, int trackPosition) { private void gameKeyReleased(int keys, int x, int y, int trackPosition) {
if (!isReplay && keys > 0) { if (!isReplay && keys != ReplayFrame.KEY_NONE) {
lastKeysPressed &= ~keys; //clears keys bits lastKeysPressed &= ~keys; // clear keys bits
addReplayFrameAndRun(x, y, lastKeysPressed, trackPosition); addReplayFrameAndRun(x, y, lastKeysPressed, trackPosition);
} }
} }
@Override
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
}
@Override
public void mouseDragged(int oldx, int oldy, int newx, int newy) {
}
@Override @Override
public void mouseWheelMoved(int newValue) { public void mouseWheelMoved(int newValue) {
if (Options.isMouseWheelDisabled() || Options.isMouseDisabled()) if (Options.isMouseWheelDisabled() || Options.isMouseDisabled())
@ -1111,7 +1049,6 @@ public class Game extends BasicGameState {
if (GameMod.AUTO.isActive() || isReplay) if (GameMod.AUTO.isActive() || isReplay)
UI.showCursor(); UI.showCursor();
lastReplayTime = 0;
// load replay frames // load replay frames
if (isReplay) { if (isReplay) {
// load mods // load mods
@ -1287,6 +1224,7 @@ public class Game extends BasicGameState {
deaths = 0; deaths = 0;
deathTime = -1; deathTime = -1;
replayFrames = null; replayFrames = null;
lastReplayTime = 0;
autoMouseX = 0; autoMouseX = 0;
autoMouseY = 0; autoMouseY = 0;
autoMousePressed = false; autoMousePressed = false;
@ -1440,7 +1378,6 @@ public class Game extends BasicGameState {
*/ */
public float getTimingPointMultiplier() { return beatLength / beatLengthBase; } public float getTimingPointMultiplier() { return beatLength / beatLengthBase; }
/** /**
* Sets a replay to view, or resets the replay if null. * Sets a replay to view, or resets the replay if null.
* @param replay the replay * @param replay the replay
@ -1458,52 +1395,53 @@ public class Game extends BasicGameState {
this.replay = replay; this.replay = replay;
} }
} }
/** /**
* Adds a replay frame to the list if able and runs it. * Adds a replay frame to the list, if possible, and runs it.
* @param x the cursor x coordinate * @param x the cursor x coordinate
* @param y the cursor y coordinate * @param y the cursor y coordinate
* @param keys the keys pressed * @param keys the keys pressed
* @param time the time of the replay Frame * @param time the time of the replay Frame
*/ */
public synchronized void addReplayFrameAndRun(int x, int y, int keys, int time){ public synchronized void addReplayFrameAndRun(int x, int y, int keys, int time){
// "auto" and "autopilot" mods: use automatic cursor coordinates
if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) {
x = autoMouseX; x = autoMouseX;
y = autoMouseY; y = autoMouseY;
} }
ReplayFrame frame = addReplayFrame(x, y, keys, time); ReplayFrame frame = addReplayFrame(x, y, keys, time);
if(frame != null) if (frame != null)
runReplayFrame(frame); runReplayFrame(frame);
} }
/** /**
* Runs a replay Frame * Runs a replay frame.
* @param frame the frame to run * @param frame the frame to run
*/ */
private void runReplayFrame(ReplayFrame frame){ private void runReplayFrame(ReplayFrame frame){
int keys = frame.getKeys(); int keys = frame.getKeys();
int replayX = frame.getScaledX(); int replayX = frame.getScaledX();
int replayY = frame.getScaledY(); int replayY = frame.getScaledY();
int deltaKeys = (keys & ~lastReplayKeys ); //keys that turned on int deltaKeys = (keys & ~lastReplayKeys); // keys that turned on
if (deltaKeys > 0) { // send a key press if (deltaKeys != ReplayFrame.KEY_NONE) // send a key press
updateGameKeyPress(deltaKeys, replayX, replayY, frame.getTime()); sendGameKeyPress(deltaKeys, replayX, replayY, frame.getTime());
} else if(keys != lastReplayKeys){ else if (keys != lastReplayKeys)
} else { ; // do nothing
else
updateGame(replayX, replayY, frame.getTimeDiff(), frame.getTime(), keys); updateGame(replayX, replayY, frame.getTimeDiff(), frame.getTime(), keys);
}
lastReplayKeys = keys; lastReplayKeys = keys;
} }
/** /**
* Updates the games mouse pressed * Sends a game key press and updates the hit objects.
* @param mouseX the mouse x position
* @param mouseY
* @param trackPosition the track position * @param trackPosition the track position
* @param keysPressed the keys that are pressed * @param x the cursor x coordinate
* @param y the cursor y coordinate
* @param keys the keys that are pressed
*/ */
private void updateGameKeyPress(int keys, int x, int y, int trackPosition) { private void sendGameKeyPress(int keys, int x, int y, int trackPosition) {
if (objectIndex >= hitObjects.length) // nothing left to do here if (objectIndex >= hitObjects.length) // nothing to do here
return; return;
OsuHitObject hitObject = osu.objects[objectIndex]; OsuHitObject hitObject = osu.objects[objectIndex];
@ -1515,26 +1453,25 @@ public class Game extends BasicGameState {
// sliders // sliders
else if (hitObject.isSlider()) else if (hitObject.isSlider())
hitObjects[objectIndex].mousePressed(x, y, trackPosition); hitObjects[objectIndex].mousePressed(x, y, trackPosition);
} }
/** /**
* Adds a replay frame to the list if able. * Adds a replay frame to the list, if possible.
* @param x the cursor x coordinate * @param x the cursor x coordinate
* @param y the cursor y coordinate * @param y the cursor y coordinate
* @param keys the keys pressed * @param keys the keys pressed
* @param time the time of the replay Frame * @param time the time of the replay frame
* @return a Replay Frame representing the data * @return a ReplayFrame representing the data
*/ */
private ReplayFrame addReplayFrame(int x, int y, int keys, int time) { private ReplayFrame addReplayFrame(int x, int y, int keys, int time) {
int timeDiff = time - lastReplayTime; int timeDiff = time - lastReplayTime;
lastReplayTime = time; lastReplayTime = time;
int cx = (int) ((x - OsuHitObject.getXOffset()) / OsuHitObject.getXMultiplier()); int cx = (int) ((x - OsuHitObject.getXOffset()) / OsuHitObject.getXMultiplier());
int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier()); int cy = (int) ((y - OsuHitObject.getYOffset()) / OsuHitObject.getYMultiplier());
ReplayFrame tFrame = new ReplayFrame(timeDiff, time, cx, cy, keys); ReplayFrame frame = new ReplayFrame(timeDiff, time, cx, cy, keys);
if(replayFrames != null) if (replayFrames != null)
replayFrames.add(tFrame); replayFrames.add(frame);
return tFrame; return frame;
} }
/** /**
@ -1556,4 +1493,66 @@ public class Game extends BasicGameState {
xy[1] = startY + (endY - startY) * t; xy[1] = startY + (endY - startY) * t;
return xy; return xy;
} }
/**
* Updates the current visible area radius (if the "flashlight" mod is enabled).
* @param delta the delta interval
* @param trackPosition the track position
*/
private void updateFlashlightRadius(int delta, int trackPosition) {
if (!GameMod.FLASHLIGHT.isActive())
return;
int width = container.getWidth(), height = container.getHeight();
boolean firstObject = (objectIndex == 0 && trackPosition < osu.objects[0].getTime());
if (isLeadIn()) {
// lead-in: expand area
float progress = Math.max((float) (leadInTime - osu.audioLeadIn) / approachTime, 0f);
flashlightRadius = width - (int) ((width - (height * 2 / 3)) * progress);
} else if (firstObject) {
// before first object: shrink area
int timeDiff = osu.objects[0].getTime() - trackPosition;
flashlightRadius = width;
if (timeDiff < approachTime) {
float progress = (float) timeDiff / approachTime;
flashlightRadius -= (width - (height * 2 / 3)) * (1 - progress);
}
} else {
// gameplay: size based on combo
int targetRadius;
int combo = data.getComboStreak();
if (combo < 100)
targetRadius = height * 2 / 3;
else if (combo < 200)
targetRadius = height / 2;
else
targetRadius = height / 3;
if (osu.breaks != null && breakIndex < osu.breaks.size() && breakTime > 0) {
// breaks: expand at beginning, shrink at end
flashlightRadius = targetRadius;
int endTime = osu.breaks.get(breakIndex);
int breakLength = endTime - breakTime;
if (breakLength > approachTime * 3) {
float progress = 1f;
if (trackPosition - breakTime < approachTime)
progress = (float) (trackPosition - breakTime) / approachTime;
else if (endTime - trackPosition < approachTime)
progress = (float) (endTime - trackPosition) / approachTime;
flashlightRadius += (width - flashlightRadius) * progress;
}
} else if (flashlightRadius != targetRadius) {
// radius size change
float radiusDiff = height * delta / 2000f;
if (flashlightRadius > targetRadius) {
flashlightRadius -= radiusDiff;
if (flashlightRadius < targetRadius)
flashlightRadius = targetRadius;
} else {
flashlightRadius += radiusDiff;
if (flashlightRadius > targetRadius)
flashlightRadius = targetRadius;
}
}
}
}
} }