2014-07-02 01:32:03 +02:00
|
|
|
/*
|
|
|
|
* opsu! - an open-source osu! client
|
2015-01-16 18:05:44 +01:00
|
|
|
* Copyright (C) 2014, 2015 Jeffrey Han
|
2014-07-02 01:32:03 +02:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2015-01-08 01:29:51 +01:00
|
|
|
import itdelatrisu.opsu.audio.SoundController;
|
|
|
|
import itdelatrisu.opsu.audio.SoundEffect;
|
2015-02-01 08:10:17 +01:00
|
|
|
import itdelatrisu.opsu.downloads.DownloadNode;
|
2014-07-02 01:32:03 +02:00
|
|
|
|
|
|
|
import java.awt.Font;
|
2015-01-20 06:54:12 +01:00
|
|
|
import java.awt.image.BufferedImage;
|
2014-07-02 01:32:03 +02:00
|
|
|
import java.io.File;
|
2015-02-12 08:27:33 +01:00
|
|
|
import java.io.IOException;
|
2015-01-20 06:54:12 +01:00
|
|
|
import java.nio.ByteBuffer;
|
2014-12-20 21:10:46 +01:00
|
|
|
import java.nio.IntBuffer;
|
2014-07-02 01:32:03 +02:00
|
|
|
import java.text.SimpleDateFormat;
|
2015-02-13 08:43:34 +01:00
|
|
|
import java.util.ArrayList;
|
2015-02-02 06:15:16 +01:00
|
|
|
import java.util.Arrays;
|
2014-07-02 01:32:03 +02:00
|
|
|
import java.util.Date;
|
2014-08-25 05:48:52 +02:00
|
|
|
import java.util.HashSet;
|
2014-07-02 01:32:03 +02:00
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.LinkedList;
|
2015-02-13 08:43:34 +01:00
|
|
|
import java.util.List;
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-20 06:54:12 +01:00
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
import org.lwjgl.BufferUtils;
|
|
|
|
import org.lwjgl.LWJGLException;
|
|
|
|
import org.lwjgl.input.Cursor;
|
2015-01-20 06:54:12 +01:00
|
|
|
import org.lwjgl.opengl.Display;
|
|
|
|
import org.lwjgl.opengl.GL11;
|
2014-07-02 01:32:03 +02:00
|
|
|
import org.newdawn.slick.Animation;
|
|
|
|
import org.newdawn.slick.Color;
|
|
|
|
import org.newdawn.slick.GameContainer;
|
2015-01-20 20:52:02 +01:00
|
|
|
import org.newdawn.slick.Graphics;
|
2014-07-02 01:32:03 +02:00
|
|
|
import org.newdawn.slick.Image;
|
|
|
|
import org.newdawn.slick.Input;
|
|
|
|
import org.newdawn.slick.SlickException;
|
2014-08-24 20:48:27 +02:00
|
|
|
import org.newdawn.slick.UnicodeFont;
|
|
|
|
import org.newdawn.slick.font.effects.ColorEffect;
|
2015-01-18 18:39:30 +01:00
|
|
|
import org.newdawn.slick.font.effects.Effect;
|
2014-07-02 01:32:03 +02:00
|
|
|
import org.newdawn.slick.state.StateBasedGame;
|
|
|
|
import org.newdawn.slick.util.Log;
|
2014-08-25 18:47:10 +02:00
|
|
|
import org.newdawn.slick.util.ResourceLoader;
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-02-12 08:27:33 +01:00
|
|
|
import com.sun.jna.platform.FileUtils;
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
/**
|
|
|
|
* Contains miscellaneous utilities.
|
|
|
|
*/
|
|
|
|
public class Utils {
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Game colors. */
|
2014-07-02 01:32:03 +02:00
|
|
|
public static final Color
|
|
|
|
COLOR_BLACK_ALPHA = new Color(0, 0, 0, 0.5f),
|
2014-07-03 00:24:19 +02:00
|
|
|
COLOR_WHITE_ALPHA = new Color(255, 255, 255, 0.5f),
|
2014-07-02 01:32:03 +02:00
|
|
|
COLOR_BLUE_DIVIDER = new Color(49, 94, 237),
|
|
|
|
COLOR_BLUE_BACKGROUND = new Color(74, 130, 255),
|
2015-02-16 17:34:35 +01:00
|
|
|
COLOR_BLUE_BUTTON = new Color(40, 129, 237),
|
|
|
|
COLOR_ORANGE_BUTTON = new Color(200, 90, 3),
|
2014-07-02 01:32:03 +02:00
|
|
|
COLOR_GREEN_OBJECT = new Color(26, 207, 26),
|
|
|
|
COLOR_BLUE_OBJECT = new Color(46, 136, 248),
|
|
|
|
COLOR_RED_OBJECT = new Color(243, 48, 77),
|
2014-07-18 03:16:15 +02:00
|
|
|
COLOR_ORANGE_OBJECT = new Color(255, 200, 32),
|
2014-12-24 08:45:43 +01:00
|
|
|
COLOR_YELLOW_ALPHA = new Color(255, 255, 0, 0.4f),
|
2015-01-11 05:14:22 +01:00
|
|
|
COLOR_WHITE_FADE = new Color(255, 255, 255, 1f),
|
2015-02-01 08:10:17 +01:00
|
|
|
COLOR_RED_HOVER = new Color(255, 112, 112),
|
2015-02-14 19:45:14 +01:00
|
|
|
COLOR_GREEN = new Color(137, 201, 79),
|
|
|
|
COLOR_LIGHT_ORANGE = new Color(255,192,128),
|
|
|
|
COLOR_LIGHT_GREEN = new Color(128,255,128),
|
2015-02-16 03:38:54 +01:00
|
|
|
COLOR_LIGHT_BLUE = new Color(128,128,255),
|
|
|
|
COLOR_GREEN_SEARCH = new Color(173, 255, 47);
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** The default map colors, used when a map does not provide custom colors. */
|
2014-07-02 01:32:03 +02:00
|
|
|
public static final Color[] DEFAULT_COMBO = {
|
2015-02-19 01:35:26 +01:00
|
|
|
COLOR_ORANGE_OBJECT, COLOR_GREEN_OBJECT,
|
|
|
|
COLOR_BLUE_OBJECT, COLOR_RED_OBJECT,
|
2014-07-02 01:32:03 +02:00
|
|
|
};
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Game fonts. */
|
2014-08-24 20:48:27 +02:00
|
|
|
public static UnicodeFont
|
2014-07-02 01:32:03 +02:00
|
|
|
FONT_DEFAULT, FONT_BOLD,
|
|
|
|
FONT_XLARGE, FONT_LARGE, FONT_MEDIUM, FONT_SMALL;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Back button (shared by other states). */
|
2014-12-30 06:00:58 +01:00
|
|
|
private static MenuButton backButton;
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Last cursor coordinates. */
|
2014-07-02 01:32:03 +02:00
|
|
|
private static int lastX = -1, lastY = -1;
|
2015-01-16 21:44:13 +01:00
|
|
|
|
2015-01-25 04:23:49 +01:00
|
|
|
/** Cursor rotation angle. */
|
|
|
|
private static float cursorAngle = 0f;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Stores all previous cursor locations to display a trail. */
|
2014-07-02 01:32:03 +02:00
|
|
|
private static LinkedList<Integer>
|
|
|
|
cursorX = new LinkedList<Integer>(),
|
|
|
|
cursorY = new LinkedList<Integer>();
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Time to show volume image, in milliseconds. */
|
2015-01-20 20:52:02 +01:00
|
|
|
private static final int VOLUME_DISPLAY_TIME = 1500;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Volume display elapsed time. */
|
2015-01-20 20:52:02 +01:00
|
|
|
private static int volumeDisplay = -1;
|
|
|
|
|
2015-01-22 06:44:45 +01:00
|
|
|
/** Set of all Unicode strings already loaded. */
|
2014-08-25 05:48:52 +02:00
|
|
|
private static HashSet<String> loadedGlyphs = new HashSet<String>();
|
|
|
|
|
2015-02-02 06:15:16 +01:00
|
|
|
/**
|
|
|
|
* List of illegal filename characters.
|
|
|
|
* @see #cleanFileName(String, char)
|
|
|
|
*/
|
|
|
|
private final static int[] illegalChars = {
|
|
|
|
34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
|
|
|
24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47
|
|
|
|
};
|
|
|
|
static {
|
|
|
|
Arrays.sort(illegalChars);
|
|
|
|
}
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
// game-related variables
|
|
|
|
private static GameContainer container;
|
2014-07-07 05:13:33 +02:00
|
|
|
private static StateBasedGame game;
|
2014-07-02 01:32:03 +02:00
|
|
|
private static Input input;
|
|
|
|
|
|
|
|
// This class should not be instantiated.
|
|
|
|
private Utils() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes game settings and class data.
|
|
|
|
* @param container the game container
|
|
|
|
* @param game the game object
|
|
|
|
* @throws SlickException
|
|
|
|
*/
|
|
|
|
public static void init(GameContainer container, StateBasedGame game)
|
|
|
|
throws SlickException {
|
|
|
|
Utils.container = container;
|
2014-07-07 05:13:33 +02:00
|
|
|
Utils.game = game;
|
2014-07-02 01:32:03 +02:00
|
|
|
Utils.input = container.getInput();
|
|
|
|
|
|
|
|
// game settings
|
|
|
|
container.setTargetFrameRate(Options.getTargetFPS());
|
2015-01-16 20:10:42 +01:00
|
|
|
container.setVSync(Options.getTargetFPS() == 60);
|
2015-01-21 05:56:10 +01:00
|
|
|
container.setMusicVolume(Options.getMusicVolume() * Options.getMasterVolume());
|
2014-07-02 01:32:03 +02:00
|
|
|
container.setShowFPS(false);
|
|
|
|
container.getInput().enableKeyRepeat();
|
|
|
|
container.setAlwaysRender(true);
|
|
|
|
|
2014-07-16 22:01:36 +02:00
|
|
|
int width = container.getWidth();
|
|
|
|
int height = container.getHeight();
|
|
|
|
|
2014-07-05 01:59:57 +02:00
|
|
|
// set the cursor
|
2014-07-02 01:32:03 +02:00
|
|
|
try {
|
2014-07-05 01:59:57 +02:00
|
|
|
// hide the native cursor
|
2014-12-20 21:30:20 +01:00
|
|
|
int min = Cursor.getMinCursorSize();
|
|
|
|
IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
|
|
|
|
Cursor emptyCursor = new Cursor(min, min, min/2, min/2, 1, tmp, null);
|
|
|
|
container.setMouseCursor(emptyCursor, 0, 0);
|
2014-07-02 01:32:03 +02:00
|
|
|
} catch (LWJGLException e) {
|
2015-01-16 03:55:26 +01:00
|
|
|
ErrorHandler.error("Failed to set the cursor.", e, true);
|
2014-07-02 01:32:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// create fonts
|
|
|
|
float fontBase;
|
|
|
|
if (height <= 600)
|
|
|
|
fontBase = 10f;
|
2014-08-25 05:48:52 +02:00
|
|
|
else if (height < 800)
|
|
|
|
fontBase = 11f;
|
2014-07-02 01:32:03 +02:00
|
|
|
else if (height <= 900)
|
2014-08-25 05:48:52 +02:00
|
|
|
fontBase = 13f;
|
|
|
|
else
|
|
|
|
fontBase = 15f;
|
|
|
|
|
2014-08-25 18:47:10 +02:00
|
|
|
try {
|
|
|
|
Font javaFont = Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(Options.FONT_NAME));
|
|
|
|
Font font = javaFont.deriveFont(Font.PLAIN, (int) (fontBase * 4 / 3));
|
|
|
|
FONT_DEFAULT = new UnicodeFont(font);
|
|
|
|
FONT_BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));
|
2015-02-13 08:43:34 +01:00
|
|
|
FONT_XLARGE = new UnicodeFont(font.deriveFont(fontBase * 3));
|
2014-08-25 18:47:10 +02:00
|
|
|
FONT_LARGE = new UnicodeFont(font.deriveFont(fontBase * 2));
|
|
|
|
FONT_MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2));
|
|
|
|
FONT_SMALL = new UnicodeFont(font.deriveFont(fontBase));
|
|
|
|
ColorEffect colorEffect = new ColorEffect();
|
|
|
|
loadFont(FONT_DEFAULT, 2, colorEffect);
|
|
|
|
loadFont(FONT_BOLD, 2, colorEffect);
|
|
|
|
loadFont(FONT_XLARGE, 4, colorEffect);
|
|
|
|
loadFont(FONT_LARGE, 4, colorEffect);
|
|
|
|
loadFont(FONT_MEDIUM, 3, colorEffect);
|
|
|
|
loadFont(FONT_SMALL, 1, colorEffect);
|
|
|
|
} catch (Exception e) {
|
2015-01-16 03:55:26 +01:00
|
|
|
ErrorHandler.error("Failed to load fonts.", e, true);
|
2014-08-25 18:47:10 +02:00
|
|
|
}
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-15 05:10:19 +01:00
|
|
|
// initialize game images
|
2015-01-08 04:36:39 +01:00
|
|
|
GameImage.init(width, height);
|
2015-01-18 23:01:13 +01:00
|
|
|
for (GameImage img : GameImage.values()) {
|
|
|
|
if (img.isPreload())
|
|
|
|
img.setDefaultImage();
|
|
|
|
}
|
2014-07-19 02:33:41 +02:00
|
|
|
|
|
|
|
// initialize game mods
|
2015-02-16 23:53:24 +01:00
|
|
|
GameMod.init(width, height);
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-21 01:01:18 +01:00
|
|
|
// initialize hit objects
|
|
|
|
OsuHitObject.init(width, height);
|
|
|
|
|
2015-02-01 08:10:17 +01:00
|
|
|
// initialize download nodes
|
|
|
|
DownloadNode.init(width, height);
|
|
|
|
|
2015-01-08 05:45:21 +01:00
|
|
|
// back button
|
2015-02-19 01:35:26 +01:00
|
|
|
if (GameImage.MENU_BACK.getImages() != null) {
|
2015-02-18 04:03:11 +01:00
|
|
|
Animation back = GameImage.MENU_BACK.getAnimation(200);
|
2015-02-19 01:35:26 +01:00
|
|
|
backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f));
|
|
|
|
} else {
|
2015-02-18 04:03:11 +01:00
|
|
|
Image back = GameImage.MENU_BACK.getImage();
|
2015-02-19 01:35:26 +01:00
|
|
|
backButton = new MenuButton(back, back.getWidth() / 2f, height - (back.getHeight() / 2f));
|
2015-02-18 04:03:11 +01:00
|
|
|
}
|
2015-02-02 06:15:16 +01:00
|
|
|
backButton.setHoverExpand(MenuButton.Expand.UP_RIGHT);
|
2015-01-08 05:45:21 +01:00
|
|
|
}
|
2014-07-03 00:24:19 +02:00
|
|
|
|
|
|
|
/**
|
2014-12-30 06:00:58 +01:00
|
|
|
* Returns the 'menu-back' MenuButton.
|
2014-07-02 01:32:03 +02:00
|
|
|
*/
|
2014-12-30 06:00:58 +01:00
|
|
|
public static MenuButton getBackButton() { return backButton; }
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-11 04:49:23 +01:00
|
|
|
/**
|
|
|
|
* Draws a tab image and text centered at a location.
|
|
|
|
* @param x the center x coordinate
|
|
|
|
* @param y the center y coordinate
|
|
|
|
* @param text the text to draw inside the tab
|
|
|
|
* @param selected whether the tab is selected (white) or not (red)
|
2015-01-11 05:14:22 +01:00
|
|
|
* @param isHover whether to include a hover effect (unselected only)
|
2015-01-11 04:49:23 +01:00
|
|
|
*/
|
2015-01-11 05:14:22 +01:00
|
|
|
public static void drawTab(float x, float y, String text, boolean selected, boolean isHover) {
|
2015-01-11 04:49:23 +01:00
|
|
|
Image tabImage = GameImage.MENU_TAB.getImage();
|
|
|
|
float tabTextX = x - (Utils.FONT_MEDIUM.getWidth(text) / 2);
|
|
|
|
float tabTextY = y - (tabImage.getHeight() / 2f) +
|
|
|
|
Math.max((tabImage.getHeight() - Utils.FONT_MEDIUM.getLineHeight()) / 1.5f, 0);
|
|
|
|
Color filter, textColor;
|
|
|
|
if (selected) {
|
|
|
|
filter = Color.white;
|
|
|
|
textColor = Color.black;
|
|
|
|
} else {
|
2015-01-11 05:14:22 +01:00
|
|
|
filter = (isHover) ? Utils.COLOR_RED_HOVER : Color.red;
|
2015-01-11 04:49:23 +01:00
|
|
|
textColor = Color.white;
|
|
|
|
}
|
|
|
|
Utils.drawCentered(tabImage, x, y, filter);
|
|
|
|
Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
|
|
|
|
}
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
/**
|
|
|
|
* Draws an image based on its center with a color filter.
|
|
|
|
* @param img the image to draw
|
|
|
|
* @param x the center x coordinate
|
|
|
|
* @param y the center y coordinate
|
|
|
|
* @param color the color filter to apply
|
|
|
|
*/
|
|
|
|
public static void drawCentered(Image img, float x, float y, Color color) {
|
|
|
|
img.draw(x - (img.getWidth() / 2f), y - (img.getHeight() / 2f), color);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws an animation based on its center.
|
|
|
|
* @param anim the animation to draw
|
|
|
|
* @param x the center x coordinate
|
|
|
|
* @param y the center y coordinate
|
|
|
|
*/
|
|
|
|
public static void drawCentered(Animation anim, float x, float y) {
|
|
|
|
anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f));
|
|
|
|
}
|
|
|
|
|
2015-02-16 23:53:24 +01:00
|
|
|
/**
|
|
|
|
* Draws an image at the given location.
|
|
|
|
* @param img the image to draw
|
|
|
|
* @param x the x coordinate
|
|
|
|
* @param y the y coordinate
|
|
|
|
* @param color the color filter to apply
|
|
|
|
*/
|
|
|
|
public static void draw(Image img, float x, float y, Color color) {
|
|
|
|
if (color == null)
|
|
|
|
img.draw(x, y);
|
|
|
|
else
|
|
|
|
img.draw(x, y, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws an animation at the given location.
|
|
|
|
* @param anim the animation to draw
|
|
|
|
* @param x the x coordinate
|
|
|
|
* @param y the y coordinate
|
|
|
|
* @param color the color filter to apply
|
|
|
|
*/
|
|
|
|
public static void draw(Animation anim, float x, float y, Color color) {
|
|
|
|
if (color == null)
|
|
|
|
anim.draw(x, y);
|
|
|
|
else
|
|
|
|
anim.draw(x, y, color);
|
|
|
|
}
|
|
|
|
|
2015-01-11 19:05:32 +01:00
|
|
|
/**
|
|
|
|
* Returns a bounded value for a base value and displacement.
|
|
|
|
* @param base the initial value
|
|
|
|
* @param diff the value change
|
|
|
|
* @param min the minimum value
|
|
|
|
* @param max the maximum value
|
|
|
|
* @return the bounded value
|
|
|
|
*/
|
|
|
|
public static int getBoundedValue(int base, int diff, int min, int max) {
|
|
|
|
int val = base + diff;
|
|
|
|
if (val < min)
|
|
|
|
val = min;
|
|
|
|
else if (val > max)
|
|
|
|
val = max;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a bounded value for a base value and displacement.
|
|
|
|
* @param base the initial value
|
|
|
|
* @param diff the value change
|
|
|
|
* @param min the minimum value
|
|
|
|
* @param max the maximum value
|
|
|
|
* @return the bounded value
|
|
|
|
*/
|
|
|
|
public static float getBoundedValue(float base, float diff, float min, float max) {
|
|
|
|
float val = base + diff;
|
|
|
|
if (val < min)
|
|
|
|
val = min;
|
|
|
|
else if (val > max)
|
|
|
|
val = max;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2015-02-15 07:40:01 +01:00
|
|
|
/**
|
|
|
|
* Clamps a value between a lower and upper bound.
|
|
|
|
* @param val the value to clamp
|
|
|
|
* @param low the lower bound
|
|
|
|
* @param high the upper bound
|
|
|
|
* @return the clamped value
|
|
|
|
* @author fluddokt
|
|
|
|
*/
|
|
|
|
public static float clamp(float val, int low, int high) {
|
|
|
|
if (val < low)
|
|
|
|
return low;
|
|
|
|
if (val > high)
|
|
|
|
return high;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
/**
|
|
|
|
* Draws the cursor.
|
|
|
|
*/
|
|
|
|
public static void drawCursor() {
|
2015-01-25 04:23:49 +01:00
|
|
|
// determine correct cursor image
|
|
|
|
// TODO: most beatmaps don't skin CURSOR_MIDDLE, so how to determine style?
|
|
|
|
Image cursor = null, cursorMiddle = null, cursorTrail = null;
|
|
|
|
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();
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2015-01-25 04:23:49 +01:00
|
|
|
int mouseX = input.getMouseX(), mouseY = input.getMouseY();
|
2014-07-02 01:32:03 +02:00
|
|
|
int removeCount = 0;
|
|
|
|
int FPSmod = (Options.getTargetFPS() / 60);
|
|
|
|
|
2015-01-25 04:23:49 +01:00
|
|
|
// TODO: use an image buffer
|
|
|
|
if (newStyle) {
|
|
|
|
// new style: add all points between cursor movements
|
2014-07-02 01:32:03 +02:00
|
|
|
if (lastX < 0) {
|
2015-01-25 04:23:49 +01:00
|
|
|
lastX = mouseX;
|
|
|
|
lastY = mouseY;
|
2014-07-02 01:32:03 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-01-25 04:23:49 +01:00
|
|
|
addCursorPoints(lastX, lastY, mouseX, mouseY);
|
|
|
|
lastX = mouseX;
|
|
|
|
lastY = mouseY;
|
2014-07-02 01:32:03 +02:00
|
|
|
|
|
|
|
removeCount = (cursorX.size() / (6 * FPSmod)) + 1;
|
2015-01-25 04:23:49 +01:00
|
|
|
} else {
|
|
|
|
// old style: sample one point at a time
|
|
|
|
cursorX.add(mouseX);
|
|
|
|
cursorY.add(mouseY);
|
2014-07-02 01:32:03 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
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);
|
|
|
|
}
|
2015-01-25 04:23:49 +01:00
|
|
|
cursorTrail.drawCentered(mouseX, mouseY);
|
2014-07-02 01:32:03 +02:00
|
|
|
|
2014-07-07 05:13:33 +02:00
|
|
|
// increase the cursor size if pressed
|
2015-01-25 04:23:49 +01:00
|
|
|
final float scale = 1.25f;
|
2014-07-18 05:58:37 +02:00
|
|
|
int state = game.getCurrentStateID();
|
|
|
|
if (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && isGameKeyPressed()) ||
|
2015-01-25 04:23:49 +01:00
|
|
|
(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))) {
|
|
|
|
cursor = cursor.getScaledCopy(scale);
|
|
|
|
if (newStyle)
|
|
|
|
cursorMiddle = cursorMiddle.getScaledCopy(scale);
|
|
|
|
}
|
2014-07-07 05:13:33 +02:00
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
// draw the other components
|
2015-01-25 04:23:49 +01:00
|
|
|
if (newStyle)
|
|
|
|
cursor.setRotation(cursorAngle);
|
|
|
|
cursor.drawCentered(mouseX, mouseY);
|
|
|
|
if (newStyle)
|
|
|
|
cursorMiddle.drawCentered(mouseX, mouseY);
|
2014-07-02 01:32:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2015-01-16 21:44:13 +01:00
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-07 05:13:33 +02:00
|
|
|
/**
|
|
|
|
* 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 static void updateCursor(int delta) {
|
2015-01-25 04:23:49 +01:00
|
|
|
cursorAngle += delta / 40f;
|
|
|
|
cursorAngle %= 360;
|
|
|
|
}
|
2014-07-07 05:13:33 +02:00
|
|
|
|
2015-01-25 04:23:49 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
2014-07-07 05:13:33 +02:00
|
|
|
}
|
|
|
|
|
2014-07-18 05:58:37 +02:00
|
|
|
/**
|
|
|
|
* Returns true if a game input key is pressed (mouse/keyboard left/right).
|
|
|
|
* @return true if pressed
|
|
|
|
*/
|
|
|
|
public static boolean isGameKeyPressed() {
|
|
|
|
return (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) ||
|
|
|
|
input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON) ||
|
|
|
|
input.isKeyDown(Options.getGameKeyLeft()) ||
|
|
|
|
input.isKeyDown(Options.getGameKeyRight()));
|
|
|
|
}
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
/**
|
|
|
|
* Draws the FPS at the bottom-right corner of the game container.
|
|
|
|
* If the option is not activated, this will do nothing.
|
|
|
|
*/
|
|
|
|
public static void drawFPS() {
|
|
|
|
if (!Options.isFPSCounterEnabled())
|
|
|
|
return;
|
|
|
|
|
2015-01-18 18:39:30 +01:00
|
|
|
String fps = String.format("%dFPS", container.getFPS());
|
|
|
|
FONT_BOLD.drawString(
|
|
|
|
container.getWidth() * 0.997f - FONT_BOLD.getWidth(fps),
|
|
|
|
container.getHeight() * 0.997f - FONT_BOLD.getHeight(fps),
|
|
|
|
Integer.toString(container.getFPS()), Color.white
|
|
|
|
);
|
2014-07-02 01:32:03 +02:00
|
|
|
FONT_DEFAULT.drawString(
|
2015-01-18 18:39:30 +01:00
|
|
|
container.getWidth() * 0.997f - FONT_BOLD.getWidth("FPS"),
|
|
|
|
container.getHeight() * 0.997f - FONT_BOLD.getHeight("FPS"),
|
|
|
|
"FPS", Color.white
|
2014-07-02 01:32:03 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-01-20 20:52:02 +01:00
|
|
|
/**
|
|
|
|
* Draws the volume bar on the middle right-hand side of the game container.
|
|
|
|
* Only draws if the volume has recently been changed using with {@link #changeVolume(int)}.
|
|
|
|
* @param g the graphics context
|
|
|
|
*/
|
|
|
|
public static void drawVolume(Graphics g) {
|
|
|
|
if (volumeDisplay == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int width = container.getWidth(), height = container.getHeight();
|
|
|
|
Image img = GameImage.VOLUME.getImage();
|
|
|
|
|
|
|
|
// move image in/out
|
|
|
|
float xOffset = 0;
|
|
|
|
float ratio = (float) volumeDisplay / VOLUME_DISPLAY_TIME;
|
|
|
|
if (ratio <= 0.1f)
|
|
|
|
xOffset = img.getWidth() * (1 - (ratio * 10f));
|
|
|
|
else if (ratio >= 0.9f)
|
|
|
|
xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f));
|
|
|
|
|
|
|
|
img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f);
|
|
|
|
float barHeight = img.getHeight() * 0.9f;
|
|
|
|
float volume = Options.getMasterVolume();
|
|
|
|
g.setColor(Color.white);
|
|
|
|
g.fillRoundRect(
|
|
|
|
width - (img.getWidth() * 0.368f) + xOffset,
|
|
|
|
(height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
|
|
|
|
img.getWidth() * 0.15f, barHeight * volume, 3
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates volume display by a delta interval.
|
|
|
|
* @param delta the delta interval since the last call
|
|
|
|
*/
|
|
|
|
public static void updateVolumeDisplay(int delta) {
|
|
|
|
if (volumeDisplay == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
volumeDisplay += delta;
|
|
|
|
if (volumeDisplay > VOLUME_DISPLAY_TIME)
|
|
|
|
volumeDisplay = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the master volume by a unit (positive or negative).
|
|
|
|
* @param units the number of units
|
|
|
|
*/
|
|
|
|
public static void changeVolume(int units) {
|
|
|
|
final float UNIT_OFFSET = 0.05f;
|
|
|
|
Options.setMasterVolume(container, Utils.getBoundedValue(Options.getMasterVolume(), UNIT_OFFSET * units, 0f, 1f));
|
|
|
|
if (volumeDisplay == -1)
|
|
|
|
volumeDisplay = 0;
|
|
|
|
else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10)
|
|
|
|
volumeDisplay = VOLUME_DISPLAY_TIME / 10;
|
|
|
|
}
|
|
|
|
|
2015-01-21 07:38:02 +01:00
|
|
|
/**
|
|
|
|
* Draws loading progress (OSZ unpacking, OsuFile parsing, sound loading)
|
|
|
|
* at the bottom of the screen.
|
|
|
|
*/
|
|
|
|
public static void drawLoadingProgress(Graphics g) {
|
|
|
|
String text, file;
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
// determine current action
|
|
|
|
if ((file = OszUnpacker.getCurrentFileName()) != null) {
|
|
|
|
text = "Unpacking new beatmaps...";
|
|
|
|
progress = OszUnpacker.getUnpackerProgress();
|
|
|
|
} else if ((file = OsuParser.getCurrentFileName()) != null) {
|
|
|
|
text = "Loading beatmaps...";
|
|
|
|
progress = OsuParser.getParserProgress();
|
|
|
|
} else if ((file = SoundController.getCurrentFileName()) != null) {
|
|
|
|
text = "Loading sounds...";
|
|
|
|
progress = SoundController.getLoadingProgress();
|
|
|
|
} else
|
|
|
|
return;
|
|
|
|
|
|
|
|
// draw loading info
|
|
|
|
float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f;
|
|
|
|
float lineY = container.getHeight() - marginY;
|
|
|
|
int lineOffsetY = Utils.FONT_MEDIUM.getLineHeight();
|
|
|
|
if (Options.isLoadVerbose()) {
|
|
|
|
// verbose: display percentages and file names
|
|
|
|
Utils.FONT_MEDIUM.drawString(
|
|
|
|
marginX, lineY - (lineOffsetY * 2),
|
|
|
|
String.format("%s (%d%%)", text, progress), Color.white);
|
|
|
|
Utils.FONT_MEDIUM.drawString(marginX, lineY - lineOffsetY, file, Color.white);
|
|
|
|
} else {
|
|
|
|
// draw loading bar
|
|
|
|
Utils.FONT_MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white);
|
|
|
|
g.setColor(Color.white);
|
|
|
|
g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f),
|
|
|
|
(container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-15 06:25:07 +01:00
|
|
|
/**
|
|
|
|
* Draws a scroll bar.
|
|
|
|
* @param g the graphics context
|
|
|
|
* @param unitIndex the unit index
|
|
|
|
* @param totalUnits the total number of units
|
|
|
|
* @param maxShown the maximum number of units shown at one time
|
|
|
|
* @param unitBaseX the base x coordinate of the units
|
|
|
|
* @param unitBaseY the base y coordinate of the units
|
|
|
|
* @param unitWidth the width of a unit
|
|
|
|
* @param unitHeight the height of a unit
|
|
|
|
* @param unitOffsetY the y offset between units
|
|
|
|
* @param bgColor the scroll bar area background color (null if none)
|
|
|
|
* @param scrollbarColor the scroll bar color
|
|
|
|
* @param right whether or not to place the scroll bar on the right side of the unit
|
|
|
|
*/
|
|
|
|
public static void drawScrollbar(
|
|
|
|
Graphics g, int unitIndex, int totalUnits, int maxShown,
|
|
|
|
float unitBaseX, float unitBaseY, float unitWidth, float unitHeight, float unitOffsetY,
|
|
|
|
Color bgColor, Color scrollbarColor, boolean right
|
|
|
|
) {
|
|
|
|
float scrollbarWidth = container.getWidth() * 0.00347f;
|
|
|
|
float heightRatio = (float) (2.6701f * Math.exp(-0.81 * Math.log(totalUnits)));
|
|
|
|
float scrollbarHeight = container.getHeight() * heightRatio;
|
|
|
|
float scrollAreaHeight = unitHeight + unitOffsetY * (maxShown - 1);
|
|
|
|
float offsetY = (scrollAreaHeight - scrollbarHeight) * ((float) unitIndex / (totalUnits - maxShown));
|
|
|
|
float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0);
|
|
|
|
if (bgColor != null) {
|
|
|
|
g.setColor(bgColor);
|
|
|
|
g.fillRect(scrollbarX, unitBaseY, scrollbarWidth, scrollAreaHeight);
|
|
|
|
}
|
|
|
|
g.setColor(scrollbarColor);
|
|
|
|
g.fillRect(scrollbarX, unitBaseY + offsetY, scrollbarWidth, scrollbarHeight);
|
|
|
|
}
|
|
|
|
|
2014-07-02 01:32:03 +02:00
|
|
|
/**
|
|
|
|
* Takes a screenshot.
|
2015-01-20 06:54:12 +01:00
|
|
|
* @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots
|
2014-07-02 01:32:03 +02:00
|
|
|
*/
|
2015-01-20 06:54:12 +01:00
|
|
|
public static void takeScreenShot() {
|
|
|
|
// create the screenshot directory
|
|
|
|
File dir = Options.getScreenshotDir();
|
|
|
|
if (!dir.isDirectory()) {
|
|
|
|
if (!dir.mkdir()) {
|
|
|
|
ErrorHandler.error("Failed to create screenshot directory.", null, false);
|
|
|
|
return;
|
2014-07-02 01:32:03 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-20 06:54:12 +01:00
|
|
|
|
|
|
|
// create file name
|
|
|
|
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
|
|
|
final File file = new File(dir, String.format("screenshot_%s.%s",
|
|
|
|
date.format(new Date()), Options.getScreenshotFormat()));
|
|
|
|
|
|
|
|
SoundController.playSound(SoundEffect.SHUTTER);
|
|
|
|
|
|
|
|
// copy the screen to file
|
|
|
|
final int width = Display.getWidth();
|
|
|
|
final int height = Display.getHeight();
|
|
|
|
final int bpp = 3; // assuming a 32-bit display with a byte each for red, green, blue, and alpha
|
|
|
|
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
|
|
|
GL11.glReadBuffer(GL11.GL_FRONT);
|
|
|
|
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
|
|
|
GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
|
|
|
|
new Thread() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
int i = (x + (width * y)) * bpp;
|
|
|
|
int r = buffer.get(i) & 0xFF;
|
|
|
|
int g = buffer.get(i + 1) & 0xFF;
|
|
|
|
int b = buffer.get(i + 2) & 0xFF;
|
|
|
|
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImageIO.write(image, Options.getScreenshotFormat(), file);
|
|
|
|
} catch (Exception e) {
|
|
|
|
ErrorHandler.error("Failed to take a screenshot.", e, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.start();
|
2014-07-02 01:32:03 +02:00
|
|
|
}
|
2014-08-25 05:48:52 +02:00
|
|
|
|
2014-08-25 18:47:10 +02:00
|
|
|
/**
|
|
|
|
* Loads a Unicode font.
|
|
|
|
* @param font the font to load
|
|
|
|
* @param padding the top and bottom padding
|
2015-01-18 18:39:30 +01:00
|
|
|
* @param effect the font effect
|
2014-08-25 18:47:10 +02:00
|
|
|
* @throws SlickException
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
private static void loadFont(UnicodeFont font, int padding,
|
2015-01-18 18:39:30 +01:00
|
|
|
Effect effect) throws SlickException {
|
2014-08-25 18:47:10 +02:00
|
|
|
font.setPaddingTop(padding);
|
|
|
|
font.setPaddingBottom(padding);
|
|
|
|
font.addAsciiGlyphs();
|
2015-01-18 18:39:30 +01:00
|
|
|
font.getEffects().add(effect);
|
2014-08-25 18:47:10 +02:00
|
|
|
font.loadGlyphs();
|
|
|
|
}
|
|
|
|
|
2014-08-25 05:48:52 +02:00
|
|
|
/**
|
|
|
|
* Adds and loads glyphs for an OsuFile's Unicode title and artist strings.
|
|
|
|
* @param osu the OsuFile
|
|
|
|
*/
|
|
|
|
public static void loadGlyphs(OsuFile osu) {
|
2015-01-08 18:03:39 +01:00
|
|
|
if (!Options.useUnicodeMetadata())
|
|
|
|
return;
|
|
|
|
|
2014-08-25 05:48:52 +02:00
|
|
|
boolean glyphsAdded = false;
|
|
|
|
if (!osu.titleUnicode.isEmpty() && !loadedGlyphs.contains(osu.titleUnicode)) {
|
|
|
|
Utils.FONT_LARGE.addGlyphs(osu.titleUnicode);
|
|
|
|
Utils.FONT_MEDIUM.addGlyphs(osu.titleUnicode);
|
|
|
|
Utils.FONT_DEFAULT.addGlyphs(osu.titleUnicode);
|
|
|
|
loadedGlyphs.add(osu.titleUnicode);
|
|
|
|
glyphsAdded = true;
|
|
|
|
}
|
|
|
|
if (!osu.artistUnicode.isEmpty() && !loadedGlyphs.contains(osu.artistUnicode)) {
|
|
|
|
Utils.FONT_LARGE.addGlyphs(osu.artistUnicode);
|
|
|
|
Utils.FONT_MEDIUM.addGlyphs(osu.artistUnicode);
|
|
|
|
Utils.FONT_DEFAULT.addGlyphs(osu.artistUnicode);
|
|
|
|
loadedGlyphs.add(osu.artistUnicode);
|
|
|
|
glyphsAdded = true;
|
|
|
|
}
|
2015-01-08 18:03:39 +01:00
|
|
|
if (glyphsAdded) {
|
2014-08-25 05:48:52 +02:00
|
|
|
try {
|
|
|
|
Utils.FONT_LARGE.loadGlyphs();
|
|
|
|
Utils.FONT_MEDIUM.loadGlyphs();
|
|
|
|
Utils.FONT_DEFAULT.loadGlyphs();
|
|
|
|
} catch (SlickException e) {
|
|
|
|
Log.warn("Failed to load glyphs.", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-01 08:10:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a human-readable representation of a given number of bytes.
|
|
|
|
* @param bytes the number of bytes
|
|
|
|
* @return the string representation
|
|
|
|
* @author aioobe (http://stackoverflow.com/a/3758880)
|
|
|
|
*/
|
|
|
|
public static String bytesToString(long bytes) {
|
|
|
|
if (bytes < 1024)
|
|
|
|
return bytes + " B";
|
|
|
|
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
|
|
|
char pre = "KMGTPE".charAt(exp - 1);
|
|
|
|
return String.format("%.1f %cB", bytes / Math.pow(1024, exp), pre);
|
|
|
|
}
|
2015-02-02 06:15:16 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cleans a file name.
|
|
|
|
* @param badFileName the original name string
|
|
|
|
* @param replace the character to replace illegal characters with (or 0 if none)
|
|
|
|
* @return the cleaned file name
|
|
|
|
* @author Sarel Botha (http://stackoverflow.com/a/5626340)
|
|
|
|
*/
|
|
|
|
public static String cleanFileName(String badFileName, char replace) {
|
|
|
|
boolean doReplace = (replace > 0 && Arrays.binarySearch(illegalChars, replace) < 0);
|
|
|
|
StringBuilder cleanName = new StringBuilder();
|
|
|
|
for (int i = 0, n = badFileName.length(); i < n; i++) {
|
|
|
|
int c = badFileName.charAt(i);
|
|
|
|
if (Arrays.binarySearch(illegalChars, c) < 0)
|
|
|
|
cleanName.append((char) c);
|
|
|
|
else if (doReplace)
|
|
|
|
cleanName.append(replace);
|
|
|
|
}
|
|
|
|
return cleanName.toString();
|
|
|
|
}
|
2015-02-12 08:27:33 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes a file or directory. If a system trash directory is available,
|
|
|
|
* the file or directory will be moved there instead.
|
|
|
|
* @param file the file or directory to delete
|
|
|
|
* @return true if moved to trash, and false if deleted
|
|
|
|
* @throws IOException if given file does not exist
|
|
|
|
*/
|
|
|
|
public static boolean deleteToTrash(File file) throws IOException {
|
|
|
|
if (file == null)
|
|
|
|
throw new IOException("File cannot be null.");
|
|
|
|
if (!file.exists())
|
|
|
|
throw new IOException(String.format("File '%s' does not exist.", file.getAbsolutePath()));
|
|
|
|
|
|
|
|
// move to system trash, if possible
|
|
|
|
FileUtils fileUtils = FileUtils.getInstance();
|
|
|
|
if (fileUtils.hasTrash()) {
|
|
|
|
try {
|
|
|
|
fileUtils.moveToTrash(new File[] { file });
|
|
|
|
return true;
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.warn(String.format("Failed to move file '%s' to trash.", file.getAbsolutePath()), e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete otherwise
|
|
|
|
if (file.isDirectory())
|
|
|
|
deleteDirectory(file);
|
|
|
|
else
|
|
|
|
file.delete();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively deletes all files and folders in a directory, then
|
|
|
|
* deletes the directory itself.
|
|
|
|
* @param dir the directory to delete
|
|
|
|
*/
|
|
|
|
private static void deleteDirectory(File dir) {
|
|
|
|
if (dir == null || !dir.isDirectory())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// recursively delete contents of directory
|
|
|
|
File[] files = dir.listFiles();
|
|
|
|
if (files != null && files.length > 0) {
|
|
|
|
for (File file : files) {
|
|
|
|
if (file.isDirectory())
|
|
|
|
deleteDirectory(file);
|
|
|
|
else
|
|
|
|
file.delete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete the directory
|
|
|
|
dir.delete();
|
|
|
|
}
|
2015-02-13 08:43:34 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wraps the given string into a list of split lines based on the width.
|
|
|
|
* @param text the text to split
|
|
|
|
* @param font the font used to draw the string
|
|
|
|
* @param width the maximum width of a line
|
|
|
|
* @return the list of split strings
|
|
|
|
* @author davedes (http://slick.ninjacave.com/forum/viewtopic.php?t=3778)
|
|
|
|
*/
|
|
|
|
public static List<String> wrap(String text, org.newdawn.slick.Font font, int width) {
|
|
|
|
List<String> list = new ArrayList<String>();
|
|
|
|
String str = text;
|
|
|
|
String line = "";
|
|
|
|
int i = 0;
|
|
|
|
int lastSpace = -1;
|
|
|
|
while (i < str.length()) {
|
|
|
|
char c = str.charAt(i);
|
|
|
|
if (Character.isWhitespace(c))
|
|
|
|
lastSpace = i;
|
|
|
|
String append = line + c;
|
|
|
|
if (font.getWidth(append) > width) {
|
|
|
|
int split = (lastSpace != -1) ? lastSpace : i;
|
|
|
|
int splitTrimmed = split;
|
|
|
|
if (lastSpace != -1 && split < str.length() - 1)
|
|
|
|
splitTrimmed++;
|
|
|
|
list.add(str.substring(0, split));
|
|
|
|
str = str.substring(splitTrimmed);
|
|
|
|
line = "";
|
|
|
|
i = 0;
|
|
|
|
lastSpace = -1;
|
|
|
|
} else {
|
|
|
|
line = append;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (str.length() != 0)
|
|
|
|
list.add(str);
|
|
|
|
return list;
|
|
|
|
}
|
2015-02-18 04:49:19 +01:00
|
|
|
}
|