Follow-up to #150: delayed fade-out, minor style changes.
Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
b55b6c3079
commit
fb789c8fc5
|
@ -54,9 +54,9 @@ import itdelatrisu.opsu.ui.UI;
|
||||||
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.IdentityHashMap;
|
|
||||||
|
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
import org.lwjgl.opengl.Display;
|
import org.lwjgl.opengl.Display;
|
||||||
|
@ -69,6 +69,7 @@ import org.newdawn.slick.Input;
|
||||||
import org.newdawn.slick.SlickException;
|
import org.newdawn.slick.SlickException;
|
||||||
import org.newdawn.slick.state.BasicGameState;
|
import org.newdawn.slick.state.BasicGameState;
|
||||||
import org.newdawn.slick.state.StateBasedGame;
|
import org.newdawn.slick.state.StateBasedGame;
|
||||||
|
import org.newdawn.slick.state.transition.DelayedFadeOutTransition;
|
||||||
import org.newdawn.slick.state.transition.EmptyTransition;
|
import org.newdawn.slick.state.transition.EmptyTransition;
|
||||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||||
import org.newdawn.slick.state.transition.FadeOutTransition;
|
import org.newdawn.slick.state.transition.FadeOutTransition;
|
||||||
|
@ -92,10 +93,13 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Music fade-out time, in milliseconds. */
|
/** Music fade-out time, in milliseconds. */
|
||||||
private static final int FADEOUT_TIME = 2000;
|
private static final int MUSIC_FADEOUT_TIME = 2000;
|
||||||
|
|
||||||
/** Maximal rotation over fade out, in degrees */
|
/** Screen fade-out time, in milliseconds, when health hits zero. */
|
||||||
private static final float MAX_ROTATION = 90.0f;
|
private static final int LOSE_FADEOUT_TIME = 500;
|
||||||
|
|
||||||
|
/** Maximum rotation, in degrees, over fade out upon death. */
|
||||||
|
private static final float MAX_ROTATION = 90f;
|
||||||
|
|
||||||
/** Minimum time before start of song, in milliseconds, to process skip-related actions. */
|
/** Minimum time before start of song, in milliseconds, to process skip-related actions. */
|
||||||
private static final int SKIP_OFFSET = 2000;
|
private static final int SKIP_OFFSET = 2000;
|
||||||
|
@ -185,8 +189,11 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
/** System time position at death. */
|
/** System time position at death. */
|
||||||
private long failTime;
|
private long failTime;
|
||||||
|
|
||||||
|
/** Track time position at death. */
|
||||||
private int failTrackTime;
|
private int failTrackTime;
|
||||||
|
|
||||||
|
/** Rotations for game objects at death. */
|
||||||
private IdentityHashMap<GameObject, Float> rotations;
|
private IdentityHashMap<GameObject, Float> rotations;
|
||||||
|
|
||||||
/** Number of retries. */
|
/** Number of retries. */
|
||||||
|
@ -663,7 +670,6 @@ public class Game extends BasicGameState {
|
||||||
deathTime = -1;
|
deathTime = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// normal game update
|
// normal game update
|
||||||
if (!isReplay)
|
if (!isReplay)
|
||||||
addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition);
|
addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition);
|
||||||
|
@ -823,24 +829,26 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// game over, force a restart
|
// game over, force a restart
|
||||||
if (!isReplay) {
|
if (!isReplay) {
|
||||||
if (restart != Restart.LOSE) {
|
if (restart != Restart.LOSE) {
|
||||||
restart = Restart.LOSE;
|
restart = Restart.LOSE;
|
||||||
failTime = System.currentTimeMillis();
|
failTime = System.currentTimeMillis();
|
||||||
failTrackTime = MusicController.getPosition();
|
failTrackTime = MusicController.getPosition();
|
||||||
MusicController.fadeOut(FADEOUT_TIME);
|
MusicController.fadeOut(MUSIC_FADEOUT_TIME);
|
||||||
MusicController.pitchFadeOut(FADEOUT_TIME);
|
MusicController.pitchFadeOut(MUSIC_FADEOUT_TIME);
|
||||||
rotations = new IdentityHashMap<GameObject, Float>();
|
rotations = new IdentityHashMap<GameObject, Float>();
|
||||||
SoundController.playSound(SoundEffect.FAIL);
|
SoundController.playSound(SoundEffect.FAIL);
|
||||||
//fade to pause menu
|
|
||||||
game.enterState(Opsu.STATE_GAMEPAUSEMENU, new FadeOutTransition(Color.black, FADEOUT_TIME), new FadeInTransition(Color.black));
|
// fade to pause menu
|
||||||
|
game.enterState(Opsu.STATE_GAMEPAUSEMENU,
|
||||||
|
new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME),
|
||||||
|
new FadeInTransition(Color.black));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//don't process hit results when already lost
|
// don't process hit results when already lost
|
||||||
if (restart != Restart.LOSE) {
|
if (restart != Restart.LOSE) {
|
||||||
// update objects (loop in unlikely event of any skipped indexes)
|
// update objects (loop in unlikely event of any skipped indexes)
|
||||||
boolean keyPressed = keys != ReplayFrame.KEY_NONE;
|
boolean keyPressed = keys != ReplayFrame.KEY_NONE;
|
||||||
|
@ -1303,17 +1311,17 @@ public class Game extends BasicGameState {
|
||||||
trackPosition < beatmap.objects[objectIndex].getTime() && !beatmap.objects[objectIndex - 1].isSpinner())
|
trackPosition < beatmap.objects[objectIndex].getTime() && !beatmap.objects[objectIndex - 1].isSpinner())
|
||||||
lastObjectIndex = objectIndex - 1;
|
lastObjectIndex = objectIndex - 1;
|
||||||
|
|
||||||
if (restart == Restart.LOSE) {
|
boolean loseState = (restart == Restart.LOSE);
|
||||||
trackPosition = failTrackTime + (int)(System.currentTimeMillis() - failTime);
|
if (loseState)
|
||||||
}
|
trackPosition = failTrackTime + (int) (System.currentTimeMillis() - failTime);
|
||||||
|
|
||||||
// draw hit objects in reverse order, or else overlapping objects are unreadable
|
// get hit objects in reverse order, or else overlapping objects are unreadable
|
||||||
Stack<Integer> stack = new Stack<Integer>();
|
Stack<Integer> stack = new Stack<Integer>();
|
||||||
for (int index = objectIndex; index < gameObjects.length && beatmap.objects[index].getTime() < trackPosition + approachTime; index++) {
|
for (int index = objectIndex; index < gameObjects.length && beatmap.objects[index].getTime() < trackPosition + approachTime; index++) {
|
||||||
stack.add(index);
|
stack.add(index);
|
||||||
|
|
||||||
// draw follow points
|
// draw follow points
|
||||||
if (!Options.isFollowPointEnabled())
|
if (!Options.isFollowPointEnabled() || loseState)
|
||||||
continue;
|
continue;
|
||||||
if (beatmap.objects[index].isSpinner()) {
|
if (beatmap.objects[index].isSpinner()) {
|
||||||
lastObjectIndex = -1;
|
lastObjectIndex = -1;
|
||||||
|
@ -1371,40 +1379,47 @@ public class Game extends BasicGameState {
|
||||||
lastObjectIndex = index;
|
lastObjectIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw hit objects
|
||||||
while (!stack.isEmpty()){
|
while (!stack.isEmpty()){
|
||||||
if (restart == Restart.LOSE){
|
int idx = stack.pop();
|
||||||
int ix = stack.pop();
|
GameObject gameObj = gameObjects[idx];
|
||||||
GameObject obj = gameObjects[ix];
|
|
||||||
// the time the object began falling
|
// normal case
|
||||||
int objTime = Math.max( beatmap.objects[ix].getTime() - approachTime, failTrackTime );
|
if (!loseState)
|
||||||
float dt = (trackPosition - objTime)/(float)(FADEOUT_TIME);
|
gameObj.draw(g, trackPosition);
|
||||||
|
|
||||||
|
// death: make objects "fall" off the screen
|
||||||
|
else {
|
||||||
|
// time the object began falling
|
||||||
|
int objTime = Math.max(beatmap.objects[idx].getTime() - approachTime, failTrackTime);
|
||||||
|
float dt = (trackPosition - objTime) / (float) (MUSIC_FADEOUT_TIME);
|
||||||
|
|
||||||
// would the object already be visible?
|
// would the object already be visible?
|
||||||
if (dt > 0) {
|
if (dt <= 0)
|
||||||
float rotSpeed;
|
continue;
|
||||||
//generate rotation speeds for each objects
|
|
||||||
if (rotations.containsKey(obj)){
|
|
||||||
rotSpeed = rotations.get(obj);
|
|
||||||
} else {
|
|
||||||
rotSpeed = (float)(2.0f*(Math.random()-0.5f)*MAX_ROTATION);
|
|
||||||
rotations.put(obj, rotSpeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
g.pushTransform();
|
// generate rotation speeds for each objects
|
||||||
|
final float rotSpeed;
|
||||||
g.translate(0, dt*dt * container.getHeight());
|
if (rotations.containsKey(gameObj)) {
|
||||||
Vec2f rotationCenter = obj.getPointAt(beatmap.objects[ix].getTime());
|
rotSpeed = rotations.get(gameObj);
|
||||||
g.rotate(rotationCenter.x, rotationCenter.y, rotSpeed * dt);
|
} else {
|
||||||
gameObjects[ix].draw(g, trackPosition);
|
rotSpeed = (float) (2.0f * (Math.random() - 0.5f) * MAX_ROTATION);
|
||||||
|
rotations.put(gameObj, rotSpeed);
|
||||||
g.popTransform();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gameObjects[stack.pop()].draw(g, trackPosition);
|
g.pushTransform();
|
||||||
|
|
||||||
|
// translate and rotate the object
|
||||||
|
g.translate(0, dt * dt * container.getHeight());
|
||||||
|
Vec2f rotationCenter = gameObj.getPointAt(beatmap.objects[idx].getTime());
|
||||||
|
g.rotate(rotationCenter.x, rotationCenter.y, rotSpeed * dt);
|
||||||
|
gameObj.draw(g, trackPosition);
|
||||||
|
|
||||||
|
g.popTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw OsuHitObjectResult objects
|
// draw result objects
|
||||||
data.drawHitResults(trackPosition);
|
data.drawHitResults(trackPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,13 +119,10 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY());
|
mousePressed(Input.MOUSE_RIGHT_BUTTON, input.getMouseX(), input.getMouseY());
|
||||||
}
|
}
|
||||||
|
|
||||||
// if music faded out (i.e. health is zero), don't process any state changes before FADEOUT_TIME
|
|
||||||
boolean loseState = (gameState.getRestart() == Game.Restart.LOSE);
|
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Input.KEY_ESCAPE:
|
case Input.KEY_ESCAPE:
|
||||||
// 'esc' will normally unpause, but will return to song menu if health is zero
|
// 'esc' will normally unpause, but will return to song menu if health is zero
|
||||||
if (loseState) {
|
if (gameState.getRestart() == Game.Restart.LOSE) {
|
||||||
SoundController.playSound(SoundEffect.MENUBACK);
|
SoundController.playSound(SoundEffect.MENUBACK);
|
||||||
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
|
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
|
||||||
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
||||||
|
@ -163,8 +160,6 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boolean loseState = (gameState.getRestart() == Game.Restart.LOSE);
|
boolean loseState = (gameState.getRestart() == Game.Restart.LOSE);
|
||||||
|
|
||||||
|
|
||||||
if (continueButton.contains(x, y) && !loseState) {
|
if (continueButton.contains(x, y) && !loseState) {
|
||||||
SoundController.playSound(SoundEffect.MENUBACK);
|
SoundController.playSound(SoundEffect.MENUBACK);
|
||||||
gameState.setRestart(Game.Restart.FALSE);
|
gameState.setRestart(Game.Restart.FALSE);
|
||||||
|
@ -205,7 +200,6 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
backButton.resetHover();
|
backButton.resetHover();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all game pause/fail menu images.
|
* Loads all game pause/fail menu images.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package org.newdawn.slick.state.transition;
|
||||||
|
|
||||||
|
import org.newdawn.slick.Color;
|
||||||
|
import org.newdawn.slick.GameContainer;
|
||||||
|
import org.newdawn.slick.Graphics;
|
||||||
|
import org.newdawn.slick.state.GameState;
|
||||||
|
import org.newdawn.slick.state.StateBasedGame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A transition to fade out to a given colour after a delay.
|
||||||
|
*
|
||||||
|
* @author kevin (base)
|
||||||
|
*/
|
||||||
|
public class DelayedFadeOutTransition implements Transition {
|
||||||
|
/** The color to fade to */
|
||||||
|
private final Color color;
|
||||||
|
/** The time it takes the fade to happen */
|
||||||
|
private final int fadeTime;
|
||||||
|
/** The time it takes before the fade starts */
|
||||||
|
private final int delay;
|
||||||
|
/** The elapsed time */
|
||||||
|
private int elapsedTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new delayed fade out transition.
|
||||||
|
*/
|
||||||
|
public DelayedFadeOutTransition() { this(Color.black, 500, 0); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new delayed fade out transition.
|
||||||
|
*
|
||||||
|
* @param color The color we're going to fade out to
|
||||||
|
*/
|
||||||
|
public DelayedFadeOutTransition(Color color) { this(color, 500, 0); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new delayed fade out transition
|
||||||
|
*
|
||||||
|
* @param color The color we're going to fade out to
|
||||||
|
* @param fadeTime The time it takes the fade to occur
|
||||||
|
* @param delay The time before the fade starts (must be less than {@code fadeTime})
|
||||||
|
*/
|
||||||
|
public DelayedFadeOutTransition(Color color, int fadeTime, int delay) {
|
||||||
|
if (delay > fadeTime)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
this.color = new Color(color);
|
||||||
|
this.color.a = 0;
|
||||||
|
this.fadeTime = fadeTime;
|
||||||
|
this.delay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete() { return (color.a >= 1); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postRender(StateBasedGame game, GameContainer container, Graphics g) {
|
||||||
|
Color old = g.getColor();
|
||||||
|
g.setColor(color);
|
||||||
|
g.fillRect(0, 0, container.getWidth() * 2, container.getHeight() * 2);
|
||||||
|
g.setColor(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(StateBasedGame game, GameContainer container, int delta) {
|
||||||
|
if (elapsedTime < delay) {
|
||||||
|
elapsedTime += delta;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
color.a += delta * (1.0f / (fadeTime - delay));
|
||||||
|
if (color.a > 1)
|
||||||
|
color.a = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRender(StateBasedGame game, GameContainer container, Graphics g) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(GameState firstState, GameState secondState) {}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user