Cursor updates.

- Supported loading beatmap-skinned cursors.
- Moved cursor loading into GameImage.
- Removed unnecessary scalings/rotations when drawing.
This commit is contained in:
Jeffrey Han 2015-01-24 22:23:49 -05:00
parent d2b3249e2c
commit 0c1b86de62
7 changed files with 102 additions and 109 deletions

View File

@ -29,6 +29,38 @@ import org.newdawn.slick.SlickException;
* Game images. * Game images.
*/ */
public enum GameImage { public enum GameImage {
// Cursor
CURSOR ("cursor", "png") {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(1 + ((h - 600) / 1000f));
}
},
CURSOR_MIDDLE ("cursormiddle", "png") {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(1 + ((h - 600) / 1000f));
}
},
CURSOR_TRAIL ("cursortrail", "png") {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(1 + ((h - 600) / 1000f));
}
},
CURSOR_OLD ("cursor2", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(1 + ((h - 600) / 1000f));
}
},
CURSOR_TRAIL_OLD ("cursortrail2", "png", false, false) {
@Override
protected Image process_sub(Image img, int w, int h) {
return img.getScaledCopy(1 + ((h - 600) / 1000f));
}
},
// Game // Game
SECTION_PASS ("section-pass", "png"), SECTION_PASS ("section-pass", "png"),
SECTION_FAIL ("section-fail", "png"), SECTION_FAIL ("section-fail", "png"),
@ -438,9 +470,6 @@ public enum GameImage {
/** Container dimensions. */ /** Container dimensions. */
private static int containerWidth, containerHeight; private static int containerWidth, containerHeight;
/** Whether a skin image has been loaded. */
private static boolean skinImageLoaded = false;
/** /**
* Initializes the GameImage class with container dimensions. * Initializes the GameImage class with container dimensions.
* @param width the container width * @param width the container width
@ -462,19 +491,6 @@ public enum GameImage {
} }
} }
/**
* Destroys all skin images, if any have been loaded.
*/
public static void destroySkinImages() {
if (skinImageLoaded) {
for (GameImage img : GameImage.values()) {
if (img.isSkinnable())
img.destroySkinImage();
}
skinImageLoaded = false;
}
}
/** /**
* Returns the bitmask image type from a type string. * Returns the bitmask image type from a type string.
* @param type the type string * @param type the type string
@ -698,7 +714,6 @@ public enum GameImage {
if (!list.isEmpty()) { if (!list.isEmpty()) {
this.skinImages = list.toArray(new Image[list.size()]); this.skinImages = list.toArray(new Image[list.size()]);
process(); process();
skinImageLoaded = true;
return true; return true;
} }
} }
@ -716,7 +731,6 @@ public enum GameImage {
// image successfully loaded // image successfully loaded
this.skinImage = img; this.skinImage = img;
process(); process();
skinImageLoaded = true;
return true; return true;
} catch (SlickException | RuntimeException e) { } catch (SlickException | RuntimeException e) {
errorFile = file.getAbsolutePath(); errorFile = file.getAbsolutePath();
@ -744,7 +758,7 @@ public enum GameImage {
/** /**
* Destroys the associated skin image(s), if any. * Destroys the associated skin image(s), if any.
*/ */
private void destroySkinImage() { public void destroySkinImage() {
if (skinImage == null && skinImages == null) if (skinImage == null && skinImages == null)
return; return;
try { try {

View File

@ -176,6 +176,7 @@ public class Opsu extends StateBasedGame {
MusicController.resume(); MusicController.resume();
} else } else
songMenu.resetTrackOnLoad(); songMenu.resetTrackOnLoad();
Utils.resetCursor();
this.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); this.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
return false; return false;
} }

View File

@ -213,11 +213,7 @@ public class Options {
@Override @Override
public void click(GameContainer container) { public void click(GameContainer container) {
newCursor = !newCursor; newCursor = !newCursor;
try { Utils.resetCursor();
Utils.loadCursor();
} catch (SlickException e) {
ErrorHandler.error("Failed to load cursor.", e, true);
}
} }
}, },
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.") { DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.") {

View File

@ -87,12 +87,12 @@ public class Utils {
/** Back button (shared by other states). */ /** Back button (shared by other states). */
private static MenuButton backButton; private static MenuButton backButton;
/** Cursor image and trail. */
private static Image cursor, cursorTrail, cursorMiddle;
/** Last cursor coordinates. */ /** Last cursor coordinates. */
private static int lastX = -1, lastY = -1; private static int lastX = -1, lastY = -1;
/** Cursor rotation angle. */
private static float cursorAngle = 0f;
/** Stores all previous cursor locations to display a trail. */ /** Stores all previous cursor locations to display a trail. */
private static LinkedList<Integer> private static LinkedList<Integer>
cursorX = new LinkedList<Integer>(), cursorX = new LinkedList<Integer>(),
@ -148,7 +148,6 @@ public class Utils {
} catch (LWJGLException e) { } catch (LWJGLException e) {
ErrorHandler.error("Failed to set the cursor.", e, true); ErrorHandler.error("Failed to set the cursor.", e, true);
} }
loadCursor();
// create fonts // create fonts
float fontBase; float fontBase;
@ -292,84 +291,46 @@ public class Utils {
return val; return val;
} }
/**
* Loads the cursor images.
* @throws SlickException
*/
public static void loadCursor() throws SlickException {
// destroy old cursors, if they exist
if (cursor != null && !cursor.isDestroyed())
cursor.destroy();
if (cursorTrail != null && !cursorTrail.isDestroyed())
cursorTrail.destroy();
if (cursorMiddle != null && !cursorMiddle.isDestroyed())
cursorMiddle.destroy();
cursor = cursorTrail = cursorMiddle = null;
// TODO: cleanup
boolean skinCursor = new File(Options.getSkinDir(), "cursor.png").isFile();
if (Options.isNewCursorEnabled()) {
// load new cursor type
// if skin cursor exists but middle part does not, don't load default middle
if (skinCursor && !new File(Options.getSkinDir(), "cursormiddle.png").isFile())
;
else {
cursorMiddle = new Image("cursormiddle.png");
cursor = new Image("cursor.png");
cursorTrail = new Image("cursortrail.png");
}
}
if (cursorMiddle == null) {
// load old cursor type
// default is stored as *2.png, but load skin cursor if it exists
if (skinCursor)
cursor = new Image("cursor.png");
else
cursor = new Image("cursor2.png");
if (new File(Options.getSkinDir(), "cursortrail.png").isFile())
cursorTrail = new Image("cursortrail.png");
else
cursorTrail = new Image("cursortrail2.png");
}
// scale the cursor
float scale = 1 + ((container.getHeight() - 600) / 1000f);
cursor = cursor.getScaledCopy(scale);
cursorTrail = cursorTrail.getScaledCopy(scale);
if (cursorMiddle != null)
cursorMiddle = cursorMiddle.getScaledCopy(scale);
}
/** /**
* Draws the cursor. * Draws the cursor.
*/ */
public static void drawCursor() { public static void drawCursor() {
// TODO: use an image buffer // determine correct cursor image
// TODO: most beatmaps don't skin CURSOR_MIDDLE, so how to determine style?
int x = input.getMouseX(); Image cursor = null, cursorMiddle = null, cursorTrail = null;
int y = input.getMouseY(); boolean skinned = GameImage.CURSOR.hasSkinImage();
boolean newStyle = (skinned) ? true : Options.isNewCursorEnabled();
if (skinned || newStyle) {
cursor = GameImage.CURSOR.getImage();
cursorTrail = GameImage.CURSOR_TRAIL.getImage();
} else {
cursor = GameImage.CURSOR_OLD.getImage();
cursorTrail = GameImage.CURSOR_TRAIL_OLD.getImage();
}
if (newStyle)
cursorMiddle = GameImage.CURSOR_MIDDLE.getImage();
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
int removeCount = 0; int removeCount = 0;
int FPSmod = (Options.getTargetFPS() / 60); int FPSmod = (Options.getTargetFPS() / 60);
// if middle exists, add all points between cursor movements // TODO: use an image buffer
if (cursorMiddle != null) { if (newStyle) {
// new style: add all points between cursor movements
if (lastX < 0) { if (lastX < 0) {
lastX = x; lastX = mouseX;
lastY = y; lastY = mouseY;
return; return;
} }
addCursorPoints(lastX, lastY, x, y); addCursorPoints(lastX, lastY, mouseX, mouseY);
lastX = x; lastX = mouseX;
lastY = y; lastY = mouseY;
removeCount = (cursorX.size() / (6 * FPSmod)) + 1; removeCount = (cursorX.size() / (6 * FPSmod)) + 1;
} } else {
// old style: sample one point at a time
// else, sample one point at a time cursorX.add(mouseX);
else { cursorY.add(mouseY);
cursorX.add(x);
cursorY.add(y);
int max = 10 * FPSmod; int max = 10 * FPSmod;
if (cursorX.size() > max) if (cursorX.size() > max)
@ -395,24 +356,24 @@ public class Utils {
// if (cx != x || cy != y) // if (cx != x || cy != y)
cursorTrail.drawCentered(cx, cy); cursorTrail.drawCentered(cx, cy);
} }
cursorTrail.drawCentered(x, y); cursorTrail.drawCentered(mouseX, mouseY);
// increase the cursor size if pressed // increase the cursor size if pressed
final float scale = 1.25f;
int state = game.getCurrentStateID(); int state = game.getCurrentStateID();
float scale = 1f;
if (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && isGameKeyPressed()) || if (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && isGameKeyPressed()) ||
(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))) (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))) {
scale = 1.25f; cursor = cursor.getScaledCopy(scale);
if (newStyle)
cursorMiddle = cursorMiddle.getScaledCopy(scale);
}
// draw the other components // draw the other components
Image cursorScaled = cursor.getScaledCopy(scale); if (newStyle)
cursorScaled.setRotation(cursor.getRotation()); cursor.setRotation(cursorAngle);
cursorScaled.drawCentered(x, y); cursor.drawCentered(mouseX, mouseY);
if (cursorMiddle != null) { if (newStyle)
Image cursorMiddleScaled = cursorMiddle.getScaledCopy(scale); cursorMiddle.drawCentered(mouseX, mouseY);
cursorMiddleScaled.setRotation(cursorMiddle.getRotation());
cursorMiddleScaled.drawCentered(x, y);
}
} }
/** /**
@ -472,10 +433,22 @@ public class Utils {
* @param delta the delta interval since the last call * @param delta the delta interval since the last call
*/ */
public static void updateCursor(int delta) { public static void updateCursor(int delta) {
if (cursorMiddle == null) cursorAngle += delta / 40f;
return; cursorAngle %= 360;
}
cursor.rotate(delta / 40f); /**
* Resets all cursor data and skins.
*/
public static void resetCursor() {
GameImage.CURSOR.destroySkinImage();
GameImage.CURSOR_MIDDLE.destroySkinImage();
GameImage.CURSOR_TRAIL.destroySkinImage();
cursorAngle = 0f;
lastX = lastY = -1;
cursorX.clear();
cursorY.clear();
GameImage.CURSOR.getImage().setRotation(0f);
} }
/** /**

View File

@ -135,6 +135,7 @@ public class GamePauseMenu extends BasicGameState {
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.getOsuFile().previewTime, true); MusicController.playAt(MusicController.getOsuFile().previewTime, true);
Utils.resetCursor();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} else { } else {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
@ -181,6 +182,7 @@ public class GamePauseMenu extends BasicGameState {
MusicController.playAt(MusicController.getOsuFile().previewTime, true); MusicController.playAt(MusicController.getOsuFile().previewTime, true);
else else
MusicController.resume(); MusicController.resume();
Utils.resetCursor();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} }
} }

View File

@ -150,6 +150,7 @@ public class GameRanking extends BasicGameState {
SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
songMenu.resetGameDataOnLoad(); songMenu.resetGameDataOnLoad();
songMenu.resetTrackOnLoad(); songMenu.resetTrackOnLoad();
Utils.resetCursor();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
break; break;
case Input.KEY_F12: case Input.KEY_F12:
@ -174,12 +175,14 @@ public class GameRanking extends BasicGameState {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset(); ((MainMenu) game.getState(Opsu.STATE_MAINMENU)).reset();
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad(); ((SongMenu) game.getState(Opsu.STATE_SONGMENU)).resetGameDataOnLoad();
Utils.resetCursor();
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} else if (Utils.getBackButton().contains(x, y)) { } else if (Utils.getBackButton().contains(x, y)) {
SoundController.playSound(SoundEffect.MENUBACK); SoundController.playSound(SoundEffect.MENUBACK);
SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU); SongMenu songMenu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
songMenu.resetGameDataOnLoad(); songMenu.resetGameDataOnLoad();
songMenu.resetTrackOnLoad(); songMenu.resetTrackOnLoad();
Utils.resetCursor();
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
} }
} }

View File

@ -703,7 +703,11 @@ public class SongMenu extends BasicGameState {
// reset game data // reset game data
if (resetGame) { if (resetGame) {
((Game) game.getState(Opsu.STATE_GAME)).resetGameData(); ((Game) game.getState(Opsu.STATE_GAME)).resetGameData();
GameImage.destroySkinImages(); // destroy skin images, if any // destroy skin images, if any
for (GameImage img : GameImage.values()) {
if (img.isSkinnable())
img.destroySkinImage();
}
resetGame = false; resetGame = false;
} }
} }