Cursor refactoring.
Moved cursor-related code out of UI and into a new non-static "Cursor" class. Also, the cursor is no longer reset after gameplay if it wasn't skinned. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
00a08f1327
commit
0c42f1ed01
|
@ -116,7 +116,7 @@ public class Container extends AppGameContainer {
|
||||||
Options.saveOptions();
|
Options.saveOptions();
|
||||||
|
|
||||||
// reset cursor
|
// reset cursor
|
||||||
UI.resetCursor();
|
UI.getCursor().reset();
|
||||||
|
|
||||||
// destroy images
|
// destroy images
|
||||||
InternalTextureLoader.get().clear();
|
InternalTextureLoader.get().clear();
|
||||||
|
|
|
@ -202,7 +202,8 @@ public class Opsu extends StateBasedGame {
|
||||||
} else
|
} else
|
||||||
songMenu.resetTrackOnLoad();
|
songMenu.resetTrackOnLoad();
|
||||||
}
|
}
|
||||||
UI.resetCursor();
|
if (UI.getCursor().isSkinned())
|
||||||
|
UI.getCursor().reset();
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,7 +225,7 @@ public class Options {
|
||||||
@Override
|
@Override
|
||||||
public void click(GameContainer container) {
|
public void click(GameContainer container) {
|
||||||
super.click(container);
|
super.click(container);
|
||||||
UI.resetCursor();
|
UI.getCursor().reset();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.", true),
|
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.", true),
|
||||||
|
|
|
@ -544,7 +544,6 @@ 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
|
||||||
|
@ -1105,7 +1104,7 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// unhide cursor for "auto" mod and replays
|
// unhide cursor for "auto" mod and replays
|
||||||
if (GameMod.AUTO.isActive() || isReplay)
|
if (GameMod.AUTO.isActive() || isReplay)
|
||||||
UI.showCursor();
|
UI.getCursor().show();
|
||||||
|
|
||||||
// load replay frames
|
// load replay frames
|
||||||
if (isReplay) {
|
if (isReplay) {
|
||||||
|
@ -1163,7 +1162,7 @@ public class Game extends BasicGameState {
|
||||||
|
|
||||||
// re-hide cursor
|
// re-hide cursor
|
||||||
if (GameMod.AUTO.isActive() || isReplay)
|
if (GameMod.AUTO.isActive() || isReplay)
|
||||||
UI.hideCursor();
|
UI.getCursor().hide();
|
||||||
|
|
||||||
// replays
|
// replays
|
||||||
if (isReplay)
|
if (isReplay)
|
||||||
|
|
|
@ -133,7 +133,8 @@ 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.getBeatmap().previewTime, true);
|
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
||||||
UI.resetCursor();
|
if (UI.getCursor().isSkinned())
|
||||||
|
UI.getCursor().reset();
|
||||||
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);
|
||||||
|
@ -186,7 +187,8 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
MusicController.playAt(MusicController.getBeatmap().previewTime, true);
|
||||||
else
|
else
|
||||||
MusicController.resume();
|
MusicController.resume();
|
||||||
UI.resetCursor();
|
if (UI.getCursor().isSkinned())
|
||||||
|
UI.getCursor().reset();
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,8 @@ public class GameRanking extends BasicGameState {
|
||||||
songMenu.resetGameDataOnLoad();
|
songMenu.resetGameDataOnLoad();
|
||||||
songMenu.resetTrackOnLoad();
|
songMenu.resetTrackOnLoad();
|
||||||
}
|
}
|
||||||
UI.resetCursor();
|
if (UI.getCursor().isSkinned())
|
||||||
|
UI.getCursor().reset();
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
309
src/itdelatrisu/opsu/ui/Cursor.java
Normal file
309
src/itdelatrisu/opsu/ui/Cursor.java
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
/*
|
||||||
|
* opsu! - an open-source osu! client
|
||||||
|
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||||
|
*
|
||||||
|
* opsu! is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* opsu! is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package itdelatrisu.opsu.ui;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
import itdelatrisu.opsu.GameImage;
|
||||||
|
import itdelatrisu.opsu.Opsu;
|
||||||
|
import itdelatrisu.opsu.Options;
|
||||||
|
import itdelatrisu.opsu.Utils;
|
||||||
|
import itdelatrisu.opsu.skins.Skin;
|
||||||
|
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.LWJGLException;
|
||||||
|
import org.newdawn.slick.GameContainer;
|
||||||
|
import org.newdawn.slick.Image;
|
||||||
|
import org.newdawn.slick.Input;
|
||||||
|
import org.newdawn.slick.SlickException;
|
||||||
|
import org.newdawn.slick.state.StateBasedGame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates and draws the cursor.
|
||||||
|
*/
|
||||||
|
public class Cursor {
|
||||||
|
/** Empty cursor. */
|
||||||
|
private static org.lwjgl.input.Cursor emptyCursor;
|
||||||
|
|
||||||
|
/** Last cursor coordinates. */
|
||||||
|
private int lastX = -1, lastY = -1;
|
||||||
|
|
||||||
|
/** Cursor rotation angle. */
|
||||||
|
private float cursorAngle = 0f;
|
||||||
|
|
||||||
|
/** Stores all previous cursor locations to display a trail. */
|
||||||
|
private LinkedList<Integer> cursorX, cursorY;
|
||||||
|
|
||||||
|
// game-related variables
|
||||||
|
private static GameContainer container;
|
||||||
|
private static StateBasedGame game;
|
||||||
|
private static Input input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the class.
|
||||||
|
* @param container the game container
|
||||||
|
* @param game the game object
|
||||||
|
*/
|
||||||
|
public static void init(GameContainer container, StateBasedGame game) {
|
||||||
|
Cursor.container = container;
|
||||||
|
Cursor.game = game;
|
||||||
|
Cursor.input = container.getInput();
|
||||||
|
|
||||||
|
// create empty cursor to simulate hiding the cursor
|
||||||
|
try {
|
||||||
|
int min = org.lwjgl.input.Cursor.getMinCursorSize();
|
||||||
|
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
|
||||||
|
emptyCursor = new org.lwjgl.input.Cursor(min, min, min/2, min/2, 1, tmp, null);
|
||||||
|
} catch (LWJGLException e) {
|
||||||
|
ErrorHandler.error("Failed to create hidden cursor.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public Cursor() {
|
||||||
|
cursorX = new LinkedList<Integer>();
|
||||||
|
cursorY = new LinkedList<Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the cursor.
|
||||||
|
*/
|
||||||
|
public void draw() {
|
||||||
|
int state = game.getCurrentStateID();
|
||||||
|
boolean mousePressed =
|
||||||
|
(((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) ||
|
||||||
|
((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) &&
|
||||||
|
!(state == Opsu.STATE_GAME && Options.isMouseDisabled())));
|
||||||
|
draw(input.getMouseX(), input.getMouseY(), mousePressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the cursor.
|
||||||
|
* @param mouseX the mouse x coordinate
|
||||||
|
* @param mouseY the mouse y coordinate
|
||||||
|
* @param mousePressed whether or not the mouse button is pressed
|
||||||
|
*/
|
||||||
|
public void draw(int mouseX, int mouseY, boolean mousePressed) {
|
||||||
|
// determine correct cursor image
|
||||||
|
Image cursor = null, cursorMiddle = null, cursorTrail = null;
|
||||||
|
boolean skinned = GameImage.CURSOR.hasSkinImage();
|
||||||
|
boolean newStyle, hasMiddle;
|
||||||
|
if (skinned) {
|
||||||
|
newStyle = true; // osu! currently treats all beatmap cursors as new-style cursors
|
||||||
|
hasMiddle = GameImage.CURSOR_MIDDLE.hasSkinImage();
|
||||||
|
} else
|
||||||
|
newStyle = hasMiddle = 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 (hasMiddle)
|
||||||
|
cursorMiddle = GameImage.CURSOR_MIDDLE.getImage();
|
||||||
|
|
||||||
|
int removeCount = 0;
|
||||||
|
int FPSmod = (Options.getTargetFPS() / 60);
|
||||||
|
Skin skin = Options.getSkin();
|
||||||
|
|
||||||
|
// TODO: use an image buffer
|
||||||
|
if (newStyle) {
|
||||||
|
// new style: add all points between cursor movements
|
||||||
|
if (lastX < 0) {
|
||||||
|
lastX = mouseX;
|
||||||
|
lastY = mouseY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addCursorPoints(lastX, lastY, mouseX, mouseY);
|
||||||
|
lastX = mouseX;
|
||||||
|
lastY = mouseY;
|
||||||
|
|
||||||
|
removeCount = (cursorX.size() / (6 * FPSmod)) + 1;
|
||||||
|
} else {
|
||||||
|
// old style: sample one point at a time
|
||||||
|
cursorX.add(mouseX);
|
||||||
|
cursorY.add(mouseY);
|
||||||
|
|
||||||
|
int max = 10 * FPSmod;
|
||||||
|
if (cursorX.size() > max)
|
||||||
|
removeCount = cursorX.size() - max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove points from the lists
|
||||||
|
for (int i = 0; i < removeCount && !cursorX.isEmpty(); i++) {
|
||||||
|
cursorX.remove();
|
||||||
|
cursorY.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw a fading trail
|
||||||
|
float alpha = 0f;
|
||||||
|
float t = 2f / cursorX.size();
|
||||||
|
if (skin.isCursorTrailRotated())
|
||||||
|
cursorTrail.setRotation(cursorAngle);
|
||||||
|
Iterator<Integer> iterX = cursorX.iterator();
|
||||||
|
Iterator<Integer> iterY = cursorY.iterator();
|
||||||
|
while (iterX.hasNext()) {
|
||||||
|
int cx = iterX.next();
|
||||||
|
int cy = iterY.next();
|
||||||
|
alpha += t;
|
||||||
|
cursorTrail.setAlpha(alpha);
|
||||||
|
// if (cx != x || cy != y)
|
||||||
|
cursorTrail.drawCentered(cx, cy);
|
||||||
|
}
|
||||||
|
cursorTrail.drawCentered(mouseX, mouseY);
|
||||||
|
|
||||||
|
// increase the cursor size if pressed
|
||||||
|
if (mousePressed && skin.isCursorExpanded()) {
|
||||||
|
final float scale = 1.25f;
|
||||||
|
cursor = cursor.getScaledCopy(scale);
|
||||||
|
if (hasMiddle)
|
||||||
|
cursorMiddle = cursorMiddle.getScaledCopy(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the other components
|
||||||
|
if (newStyle && skin.isCursorRotated())
|
||||||
|
cursor.setRotation(cursorAngle);
|
||||||
|
cursor.drawCentered(mouseX, mouseY);
|
||||||
|
if (hasMiddle)
|
||||||
|
cursorMiddle.drawCentered(mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all points between (x1, y1) and (x2, y2) to the cursor point lists.
|
||||||
|
* @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
|
||||||
|
*/
|
||||||
|
private void addCursorPoints(int x1, int y1, int x2, int y2) {
|
||||||
|
// delta of exact value and rounded value of the dependent variable
|
||||||
|
int d = 0;
|
||||||
|
int dy = Math.abs(y2 - y1);
|
||||||
|
int dx = Math.abs(x2 - x1);
|
||||||
|
|
||||||
|
int dy2 = (dy << 1); // slope scaling factors to avoid floating
|
||||||
|
int dx2 = (dx << 1); // point
|
||||||
|
int ix = x1 < x2 ? 1 : -1; // increment direction
|
||||||
|
int iy = y1 < y2 ? 1 : -1;
|
||||||
|
|
||||||
|
int k = 5; // sample size
|
||||||
|
if (dy <= dx) {
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
if (i == k) {
|
||||||
|
cursorX.add(x1);
|
||||||
|
cursorY.add(y1);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
if (x1 == x2)
|
||||||
|
break;
|
||||||
|
x1 += ix;
|
||||||
|
d += dy2;
|
||||||
|
if (d > dx) {
|
||||||
|
y1 += iy;
|
||||||
|
d -= dx2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
if (i == k) {
|
||||||
|
cursorX.add(x1);
|
||||||
|
cursorY.add(y1);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
if (y1 == y2)
|
||||||
|
break;
|
||||||
|
y1 += iy;
|
||||||
|
d += dx2;
|
||||||
|
if (d > dy) {
|
||||||
|
x1 += ix;
|
||||||
|
d -= dy2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates the cursor by a degree determined by a delta interval.
|
||||||
|
* If the old style cursor is being used, this will do nothing.
|
||||||
|
* @param delta the delta interval since the last call
|
||||||
|
*/
|
||||||
|
public void update(int delta) {
|
||||||
|
cursorAngle += delta / 40f;
|
||||||
|
cursorAngle %= 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all cursor data and skins.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
// destroy skin images
|
||||||
|
GameImage.CURSOR.destroySkinImage();
|
||||||
|
GameImage.CURSOR_MIDDLE.destroySkinImage();
|
||||||
|
GameImage.CURSOR_TRAIL.destroySkinImage();
|
||||||
|
|
||||||
|
// reset locations
|
||||||
|
resetLocations();
|
||||||
|
|
||||||
|
// reset angles
|
||||||
|
cursorAngle = 0f;
|
||||||
|
GameImage.CURSOR.getImage().setRotation(0f);
|
||||||
|
GameImage.CURSOR_TRAIL.getImage().setRotation(0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all cursor location data.
|
||||||
|
*/
|
||||||
|
public void resetLocations() {
|
||||||
|
lastX = lastY = -1;
|
||||||
|
cursorX.clear();
|
||||||
|
cursorY.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the cursor is skinned.
|
||||||
|
*/
|
||||||
|
public boolean isSkinned() {
|
||||||
|
return (GameImage.CURSOR.hasSkinImage() ||
|
||||||
|
GameImage.CURSOR_MIDDLE.hasSkinImage() ||
|
||||||
|
GameImage.CURSOR_TRAIL.hasSkinImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the cursor, if possible.
|
||||||
|
*/
|
||||||
|
public void hide() {
|
||||||
|
if (emptyCursor != null) {
|
||||||
|
try {
|
||||||
|
container.setMouseCursor(emptyCursor, 0, 0);
|
||||||
|
} catch (SlickException e) {
|
||||||
|
ErrorHandler.error("Failed to hide the cursor.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unhides the cursor.
|
||||||
|
*/
|
||||||
|
public void show() {
|
||||||
|
container.setDefaultMouseCursor();
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,24 +20,15 @@ package itdelatrisu.opsu.ui;
|
||||||
|
|
||||||
import itdelatrisu.opsu.ErrorHandler;
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
import itdelatrisu.opsu.GameImage;
|
import itdelatrisu.opsu.GameImage;
|
||||||
import itdelatrisu.opsu.Opsu;
|
|
||||||
import itdelatrisu.opsu.Options;
|
import itdelatrisu.opsu.Options;
|
||||||
import itdelatrisu.opsu.OszUnpacker;
|
import itdelatrisu.opsu.OszUnpacker;
|
||||||
import itdelatrisu.opsu.Utils;
|
import itdelatrisu.opsu.Utils;
|
||||||
import itdelatrisu.opsu.audio.SoundController;
|
import itdelatrisu.opsu.audio.SoundController;
|
||||||
import itdelatrisu.opsu.beatmap.BeatmapParser;
|
import itdelatrisu.opsu.beatmap.BeatmapParser;
|
||||||
import itdelatrisu.opsu.skins.Skin;
|
|
||||||
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
import org.lwjgl.BufferUtils;
|
|
||||||
import org.lwjgl.LWJGLException;
|
|
||||||
import org.lwjgl.input.Cursor;
|
|
||||||
import org.newdawn.slick.Animation;
|
import org.newdawn.slick.Animation;
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.GameContainer;
|
import org.newdawn.slick.GameContainer;
|
||||||
|
@ -48,26 +39,15 @@ import org.newdawn.slick.SlickException;
|
||||||
import org.newdawn.slick.state.StateBasedGame;
|
import org.newdawn.slick.state.StateBasedGame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class primarily used for drawing UI components.
|
* Draws common UI components.
|
||||||
*/
|
*/
|
||||||
public class UI {
|
public class UI {
|
||||||
|
/** Cursor. */
|
||||||
|
private static Cursor cursor = new Cursor();
|
||||||
|
|
||||||
/** Back button. */
|
/** Back button. */
|
||||||
private static MenuButton backButton;
|
private static MenuButton backButton;
|
||||||
|
|
||||||
/** Empty cursor. */
|
|
||||||
private static Cursor emptyCursor;
|
|
||||||
|
|
||||||
/** Last cursor coordinates. */
|
|
||||||
private static int lastX = -1, lastY = -1;
|
|
||||||
|
|
||||||
/** Cursor rotation angle. */
|
|
||||||
private static float cursorAngle = 0f;
|
|
||||||
|
|
||||||
/** Stores all previous cursor locations to display a trail. */
|
|
||||||
private static LinkedList<Integer>
|
|
||||||
cursorX = new LinkedList<Integer>(),
|
|
||||||
cursorY = new LinkedList<Integer>();
|
|
||||||
|
|
||||||
/** Time to show volume image, in milliseconds. */
|
/** Time to show volume image, in milliseconds. */
|
||||||
private static final int VOLUME_DISPLAY_TIME = 1500;
|
private static final int VOLUME_DISPLAY_TIME = 1500;
|
||||||
|
|
||||||
|
@ -97,7 +77,6 @@ public class UI {
|
||||||
|
|
||||||
// game-related variables
|
// game-related variables
|
||||||
private static GameContainer container;
|
private static GameContainer container;
|
||||||
private static StateBasedGame game;
|
|
||||||
private static Input input;
|
private static Input input;
|
||||||
|
|
||||||
// This class should not be instantiated.
|
// This class should not be instantiated.
|
||||||
|
@ -112,18 +91,11 @@ public class UI {
|
||||||
public static void init(GameContainer container, StateBasedGame game)
|
public static void init(GameContainer container, StateBasedGame game)
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
UI.container = container;
|
UI.container = container;
|
||||||
UI.game = game;
|
|
||||||
UI.input = container.getInput();
|
UI.input = container.getInput();
|
||||||
|
|
||||||
// hide native cursor
|
// initialize cursor
|
||||||
try {
|
Cursor.init(container, game);
|
||||||
int min = Cursor.getMinCursorSize();
|
cursor.hide();
|
||||||
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
|
|
||||||
emptyCursor = new Cursor(min, min, min/2, min/2, 1, tmp, null);
|
|
||||||
hideCursor();
|
|
||||||
} catch (LWJGLException e) {
|
|
||||||
ErrorHandler.error("Failed to create hidden cursor.", e, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// back button
|
// back button
|
||||||
if (GameImage.MENU_BACK.getImages() != null) {
|
if (GameImage.MENU_BACK.getImages() != null) {
|
||||||
|
@ -141,7 +113,7 @@ public class UI {
|
||||||
* @param delta the delta interval since the last call.
|
* @param delta the delta interval since the last call.
|
||||||
*/
|
*/
|
||||||
public static void update(int delta) {
|
public static void update(int delta) {
|
||||||
updateCursor(delta);
|
cursor.update(delta);
|
||||||
updateVolumeDisplay(delta);
|
updateVolumeDisplay(delta);
|
||||||
updateBarNotification(delta);
|
updateBarNotification(delta);
|
||||||
if (tooltipTimer > 0)
|
if (tooltipTimer > 0)
|
||||||
|
@ -156,7 +128,7 @@ public class UI {
|
||||||
drawBarNotification(g);
|
drawBarNotification(g);
|
||||||
drawVolume(g);
|
drawVolume(g);
|
||||||
drawFPS();
|
drawFPS();
|
||||||
drawCursor();
|
cursor.draw();
|
||||||
drawTooltip(g);
|
drawTooltip(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +143,7 @@ public class UI {
|
||||||
drawBarNotification(g);
|
drawBarNotification(g);
|
||||||
drawVolume(g);
|
drawVolume(g);
|
||||||
drawFPS();
|
drawFPS();
|
||||||
drawCursor(mouseX, mouseY, mousePressed);
|
cursor.draw(mouseX, mouseY, mousePressed);
|
||||||
drawTooltip(g);
|
drawTooltip(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +152,16 @@ public class UI {
|
||||||
*/
|
*/
|
||||||
public static void enter() {
|
public static void enter() {
|
||||||
backButton.resetHover();
|
backButton.resetHover();
|
||||||
|
cursor.resetLocations();
|
||||||
resetBarNotification();
|
resetBarNotification();
|
||||||
resetCursorLocations();
|
|
||||||
resetTooltip();
|
resetTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the game cursor.
|
||||||
|
*/
|
||||||
|
public static Cursor getCursor() { return cursor; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the 'menu-back' MenuButton.
|
* Returns the 'menu-back' MenuButton.
|
||||||
*/
|
*/
|
||||||
|
@ -214,212 +191,6 @@ public class UI {
|
||||||
Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
|
Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the cursor.
|
|
||||||
*/
|
|
||||||
public static void drawCursor() {
|
|
||||||
int state = game.getCurrentStateID();
|
|
||||||
boolean mousePressed =
|
|
||||||
(((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) ||
|
|
||||||
((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) &&
|
|
||||||
!(state == Opsu.STATE_GAME && Options.isMouseDisabled())));
|
|
||||||
drawCursor(input.getMouseX(), input.getMouseY(), mousePressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the cursor.
|
|
||||||
* @param mouseX the mouse x coordinate
|
|
||||||
* @param mouseY the mouse y coordinate
|
|
||||||
* @param mousePressed whether or not the mouse button is pressed
|
|
||||||
*/
|
|
||||||
public static void drawCursor(int mouseX, int mouseY, boolean mousePressed) {
|
|
||||||
// determine correct cursor image
|
|
||||||
Image cursor = null, cursorMiddle = null, cursorTrail = null;
|
|
||||||
boolean skinned = GameImage.CURSOR.hasSkinImage();
|
|
||||||
boolean newStyle, hasMiddle;
|
|
||||||
if (skinned) {
|
|
||||||
newStyle = true; // osu! currently treats all beatmap cursors as new-style cursors
|
|
||||||
hasMiddle = GameImage.CURSOR_MIDDLE.hasSkinImage();
|
|
||||||
} else
|
|
||||||
newStyle = hasMiddle = 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 (hasMiddle)
|
|
||||||
cursorMiddle = GameImage.CURSOR_MIDDLE.getImage();
|
|
||||||
|
|
||||||
int removeCount = 0;
|
|
||||||
int FPSmod = (Options.getTargetFPS() / 60);
|
|
||||||
Skin skin = Options.getSkin();
|
|
||||||
|
|
||||||
// TODO: use an image buffer
|
|
||||||
if (newStyle) {
|
|
||||||
// new style: add all points between cursor movements
|
|
||||||
if (lastX < 0) {
|
|
||||||
lastX = mouseX;
|
|
||||||
lastY = mouseY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addCursorPoints(lastX, lastY, mouseX, mouseY);
|
|
||||||
lastX = mouseX;
|
|
||||||
lastY = mouseY;
|
|
||||||
|
|
||||||
removeCount = (cursorX.size() / (6 * FPSmod)) + 1;
|
|
||||||
} else {
|
|
||||||
// old style: sample one point at a time
|
|
||||||
cursorX.add(mouseX);
|
|
||||||
cursorY.add(mouseY);
|
|
||||||
|
|
||||||
int max = 10 * FPSmod;
|
|
||||||
if (cursorX.size() > max)
|
|
||||||
removeCount = cursorX.size() - max;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove points from the lists
|
|
||||||
for (int i = 0; i < removeCount && !cursorX.isEmpty(); i++) {
|
|
||||||
cursorX.remove();
|
|
||||||
cursorY.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw a fading trail
|
|
||||||
float alpha = 0f;
|
|
||||||
float t = 2f / cursorX.size();
|
|
||||||
if (skin.isCursorTrailRotated())
|
|
||||||
cursorTrail.setRotation(cursorAngle);
|
|
||||||
Iterator<Integer> iterX = cursorX.iterator();
|
|
||||||
Iterator<Integer> iterY = cursorY.iterator();
|
|
||||||
while (iterX.hasNext()) {
|
|
||||||
int cx = iterX.next();
|
|
||||||
int cy = iterY.next();
|
|
||||||
alpha += t;
|
|
||||||
cursorTrail.setAlpha(alpha);
|
|
||||||
// if (cx != x || cy != y)
|
|
||||||
cursorTrail.drawCentered(cx, cy);
|
|
||||||
}
|
|
||||||
cursorTrail.drawCentered(mouseX, mouseY);
|
|
||||||
|
|
||||||
// increase the cursor size if pressed
|
|
||||||
if (mousePressed && skin.isCursorExpanded()) {
|
|
||||||
final float scale = 1.25f;
|
|
||||||
cursor = cursor.getScaledCopy(scale);
|
|
||||||
if (hasMiddle)
|
|
||||||
cursorMiddle = cursorMiddle.getScaledCopy(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw the other components
|
|
||||||
if (newStyle && skin.isCursorRotated())
|
|
||||||
cursor.setRotation(cursorAngle);
|
|
||||||
cursor.drawCentered(mouseX, mouseY);
|
|
||||||
if (hasMiddle)
|
|
||||||
cursorMiddle.drawCentered(mouseX, mouseY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds all points between (x1, y1) and (x2, y2) to the cursor point lists.
|
|
||||||
* @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
|
|
||||||
*/
|
|
||||||
private static void addCursorPoints(int x1, int y1, int x2, int y2) {
|
|
||||||
// delta of exact value and rounded value of the dependent variable
|
|
||||||
int d = 0;
|
|
||||||
int dy = Math.abs(y2 - y1);
|
|
||||||
int dx = Math.abs(x2 - x1);
|
|
||||||
|
|
||||||
int dy2 = (dy << 1); // slope scaling factors to avoid floating
|
|
||||||
int dx2 = (dx << 1); // point
|
|
||||||
int ix = x1 < x2 ? 1 : -1; // increment direction
|
|
||||||
int iy = y1 < y2 ? 1 : -1;
|
|
||||||
|
|
||||||
int k = 5; // sample size
|
|
||||||
if (dy <= dx) {
|
|
||||||
for (int i = 0; ; i++) {
|
|
||||||
if (i == k) {
|
|
||||||
cursorX.add(x1);
|
|
||||||
cursorY.add(y1);
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
if (x1 == x2)
|
|
||||||
break;
|
|
||||||
x1 += ix;
|
|
||||||
d += dy2;
|
|
||||||
if (d > dx) {
|
|
||||||
y1 += iy;
|
|
||||||
d -= dx2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; ; i++) {
|
|
||||||
if (i == k) {
|
|
||||||
cursorX.add(x1);
|
|
||||||
cursorY.add(y1);
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
if (y1 == y2)
|
|
||||||
break;
|
|
||||||
y1 += iy;
|
|
||||||
d += dx2;
|
|
||||||
if (d > dy) {
|
|
||||||
x1 += ix;
|
|
||||||
d -= dy2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotates the cursor by a degree determined by a delta interval.
|
|
||||||
* If the old style cursor is being used, this will do nothing.
|
|
||||||
* @param delta the delta interval since the last call
|
|
||||||
*/
|
|
||||||
private static void updateCursor(int delta) {
|
|
||||||
cursorAngle += delta / 40f;
|
|
||||||
cursorAngle %= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets all cursor data and skins.
|
|
||||||
*/
|
|
||||||
public static void resetCursor() {
|
|
||||||
GameImage.CURSOR.destroySkinImage();
|
|
||||||
GameImage.CURSOR_MIDDLE.destroySkinImage();
|
|
||||||
GameImage.CURSOR_TRAIL.destroySkinImage();
|
|
||||||
cursorAngle = 0f;
|
|
||||||
GameImage.CURSOR.getImage().setRotation(0f);
|
|
||||||
GameImage.CURSOR_TRAIL.getImage().setRotation(0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets all cursor location data.
|
|
||||||
*/
|
|
||||||
private static void resetCursorLocations() {
|
|
||||||
lastX = lastY = -1;
|
|
||||||
cursorX.clear();
|
|
||||||
cursorY.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the cursor, if possible.
|
|
||||||
*/
|
|
||||||
public static void hideCursor() {
|
|
||||||
if (emptyCursor != null) {
|
|
||||||
try {
|
|
||||||
container.setMouseCursor(emptyCursor, 0, 0);
|
|
||||||
} catch (SlickException e) {
|
|
||||||
ErrorHandler.error("Failed to hide the cursor.", e, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unhides the cursor.
|
|
||||||
*/
|
|
||||||
public static void showCursor() {
|
|
||||||
container.setDefaultMouseCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the FPS at the bottom-right corner of the game container.
|
* Draws the FPS at the bottom-right corner of the game container.
|
||||||
* If the option is not activated, this will do nothing.
|
* If the option is not activated, this will do nothing.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user