From aeaaa9af4f3ee8497ed78a81b8d65a60e81e9046 Mon Sep 17 00:00:00 2001 From: fd Date: Sat, 14 Mar 2015 08:11:33 -0400 Subject: [PATCH] @2x size images. ComboBurst speed scaling. CircumscribedCircle drawing optimization. --- src/itdelatrisu/opsu/GameData.java | 6 +- src/itdelatrisu/opsu/GameImage.java | 167 ++++++++++-------- src/itdelatrisu/opsu/Options.java | 14 ++ src/itdelatrisu/opsu/objects/Slider.java | 2 +- .../objects/curves/CircumscribedCircle.java | 15 +- src/itdelatrisu/opsu/states/OptionsMenu.java | 3 +- 6 files changed, 125 insertions(+), 82 deletions(-) diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index d05423b4..c0674719 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -164,7 +164,7 @@ public class GameData { private float comboBurstAlpha; /** Current x coordinate of the combo burst image (for sliding animation). */ - private int comboBurstX; + private float comboBurstX; /** Time offsets for obtaining each hit result (indexed by HIT_* constants). */ private int[] hitResultOffset; @@ -999,11 +999,11 @@ public class GameData { int leftX = 0; int rightX = width - comboBurstImages[comboBurstIndex].getWidth(); if (comboBurstX < leftX) { - comboBurstX += (delta / 2f); + comboBurstX += (delta / 2f) * GameImage.getUIscale(); if (comboBurstX > leftX) comboBurstX = leftX; } else if (comboBurstX > rightX) { - comboBurstX -= (delta / 2f); + comboBurstX -= (delta / 2f) * GameImage.getUIscale(); if (comboBurstX < rightX) comboBurstX = rightX; } else if (comboBurstAlpha > 0f) { diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index b50c49c6..738fc0a1 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -372,6 +372,9 @@ public enum GameImage { /** The unscaled container height that uiscale is based on. */ private static final int UNSCALED_HEIGHT = 768; + + /** Image Sizes suffixes to try to load images with */ + private static String[] imageSizeSuffix; /** * Initializes the GameImage class with container dimensions. @@ -382,6 +385,11 @@ public enum GameImage { containerWidth = width; containerHeight = height; uiscale = (float) containerHeight / UNSCALED_HEIGHT; + if (Options.is2xImagesEnabled() && uiscale >= 1) { + imageSizeSuffix = new String[]{"@2x",""}; + } else { + imageSizeSuffix = new String[]{""}; + } } /** @@ -563,52 +571,33 @@ public enum GameImage { public void setDefaultImage() { if (defaultImage != null || defaultImages != null) return; - - // load image array + if (filenameFormat != null) { - List list = new ArrayList(); - File dir = Options.getSkinDir(); - int i = 0; - while (true) { - // look for next image - String filenameFormatted = String.format(filenameFormat, i++); - String name = getImageFileName(filenameFormatted, dir, type, true); - if (i == 1 && name == null) { // first image: check other location - dir = null; - name = getImageFileName(filenameFormatted, dir, type, true); - } - if (name == null) - break; - - // add image to list - try { - Image img = new Image(name); - list.add(img); - } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set default image '%s'.", name), null, false); - break; - } + defaultImages = getMutliImages(Options.getSkinDir()); + if (defaultImages != null) { + process(); + return; } - if (!list.isEmpty()) { - this.defaultImages = list.toArray(new Image[list.size()]); + defaultImages = getMutliImages(null); + if (defaultImages != null) { process(); return; } } - - // load single image - String name = getImageFileName(filename, Options.getSkinDir(), type, false); - if (name == null) { - ErrorHandler.error(String.format("Could not find image '%s'.", filename), null, false); + + defaultImage = getSingleImage(Options.getSkinDir()); + if (defaultImage != null) { + process(); return; } - try { - Image img = new Image(name); - this.defaultImage = img; + + defaultImage = getSingleImage(null); + if (defaultImage != null) { process(); - } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set default image '%s'.", filename), null, false); + return; } + + ErrorHandler.error(String.format("Could not find default image '%s'.", filename), null, false); } /** @@ -627,48 +616,80 @@ public enum GameImage { if (Options.isBeatmapSkinIgnored()) return false; - // look for multiple skin images - if (filenameFormat != null) { - List list = new ArrayList(); - int i = 0; - while (true) { - // look for next image - String filenameFormatted = String.format(filenameFormat, i++); - String name = getImageFileName(filenameFormatted, dir, type, true); - if (name == null) - break; - - // add image to list - try { - Image img = new Image(name); - list.add(img); - } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to set skin image '%s'.", name), null, false); - break; - } - } - if (!list.isEmpty()) { - this.skinImages = list.toArray(new Image[list.size()]); - process(); - return true; - } - } - - // look for a skin image - String name = getImageFileName(filename, dir, type, true); - if (name == null) - return false; - try { - Image img = new Image(name); - this.skinImage = img; + skinImages = getMutliImages(dir); + if (skinImages != null) { process(); return true; - } catch (SlickException e) { - skinImage = null; - ErrorHandler.error(String.format("Failed to set skin image '%s'.", name), null, false); } + + skinImage = getSingleImage(dir); + if (skinImage != null) { + process(); + return true; + } + return false; } + + /** + * Attempts to load GameImage with multiple images. + * @return an array of images or null if not found. + */ + public Image[] getMutliImages(File dir){ + // load image array + if (filenameFormat != null) { + for (String suf : imageSizeSuffix) { + List list = new ArrayList(); + int i = 0; + while (true) { + // look for next image + String filenameFormatted = String.format(filenameFormat+suf, i++); + String name = getImageFileName(filenameFormatted, dir, type, true); + if (name == null) + break; + + // add image to list + try { + System.out.println(name); + Image img = new Image(name); + if(suf.equals("@2x")) + img = img.getScaledCopy(0.5f); + list.add(img); + } catch (SlickException e) { + ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false); + break; + } + } + if (!list.isEmpty()) { + return list.toArray(new Image[list.size()]); + } + } + } + return null; + } + + /** + * Attempts to load GameImage with a single image. + * @return an image or null if not found. + */ + public Image getSingleImage(File dir){ + for (String suf : imageSizeSuffix) { + // load single image + String name = getImageFileName(filename+suf, dir, type, true); + if (name != null) { + try { + Image img = new Image(name); + System.out.println(name); + if(suf.equals("@2x")) + img = img.getScaledCopy(0.5f); + return img; + } catch (SlickException e) { + ErrorHandler.error(String.format("Failed to set image '%s'.", filename), null, false); + } + } + } + return null; + } /** * Returns whether a skin image is currently loaded. diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index b4ef7add..5c1aab5b 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -257,6 +257,7 @@ public class Options { }, ENABLE_THEME_SONG ("Enable Theme Song", "Whether to play the theme song upon starting opsu!", true), SHOW_HIT_ERROR_BAR ("Show Hit Error Bar", "Shows precisely how accurate you were with each hit.", false), + USE_2X_IMAGE ("Use @2x Images", "Uses @2x images if available.", true), DISABLE_MOUSE_WHEEL ("Disable mouse wheel in play mode", "During play, you can use the mouse wheel to adjust the volume and pause the game.\nThis will disable that functionality.", false), DISABLE_MOUSE_BUTTONS ("Disable mouse buttons in play mode", "This option will disable all mouse buttons.\nSpecifically for people who use their keyboard to click.", false); @@ -693,6 +694,14 @@ public class Options { * @return true if enabled */ public static boolean isHitErrorBarEnabled() { return GameOption.SHOW_HIT_ERROR_BAR.getBooleanValue(); } + + + /** + * Returns whether or not to use 2x Images + * @return true if ignored + */ + public static boolean is2xImagesEnabled() { return GameOption.USE_2X_IMAGE.getBooleanValue(); } + /** * Returns whether or not the mouse wheel is disabled during gameplay. @@ -1021,6 +1030,9 @@ public class Options { case "ScoreMeter": GameOption.SHOW_HIT_ERROR_BAR.setValue(Boolean.parseBoolean(value)); break; + case "Use2xImage": + GameOption.USE_2X_IMAGE.setValue(Boolean.parseBoolean(value)); + break; case "FixedCS": GameOption.FIXED_CS.setValue((int) (Float.parseFloat(value) * 10f)); break; @@ -1133,6 +1145,8 @@ public class Options { writer.newLine(); writer.write(String.format("ScoreMeter = %b", isHitErrorBarEnabled())); writer.newLine(); + writer.write(String.format("Use2xImage = %b", is2xImagesEnabled())); + writer.newLine(); writer.write(String.format(Locale.US, "FixedCS = %.1f", getFixedCS())); writer.newLine(); writer.write(String.format(Locale.US, "FixedHP = %.1f", getFixedHP())); diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index 541fddf3..9b3aadb0 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -140,7 +140,7 @@ public class Slider implements HitObject { this.color = color; this.comboEnd = comboEnd; - if (hitObject.getSliderType() == 'P' && hitObject.getSliderX().length == 2) + if (hitObject.getSliderType() == OsuHitObject.SLIDER_PASSTHROUGH && hitObject.getSliderX().length == 2) this.curve = new CircumscribedCircle(hitObject, color); else this.curve = new LinearBezier(hitObject, color); diff --git a/src/itdelatrisu/opsu/objects/curves/CircumscribedCircle.java b/src/itdelatrisu/opsu/objects/curves/CircumscribedCircle.java index 743f0974..4577b969 100644 --- a/src/itdelatrisu/opsu/objects/curves/CircumscribedCircle.java +++ b/src/itdelatrisu/opsu/objects/curves/CircumscribedCircle.java @@ -55,6 +55,9 @@ public class CircumscribedCircle extends Curve { /** The number of steps in the curve to draw. */ private float step; + + /** Points along the curve. */ + private Vec2f[] curve; /** * Constructor. @@ -120,6 +123,12 @@ public class CircumscribedCircle extends Curve { // finds the angles to draw for repeats this.drawEndAngle = (float) ((endAng + (startAng > endAng ? HALF_PI : -HALF_PI)) * 180 / Math.PI); this.drawStartAngle = (float) ((startAng + (startAng > endAng ? -HALF_PI : HALF_PI)) * 180 / Math.PI); + + curve = new Vec2f[(int) step + 1]; + for (int i = 0; i < curve.length; i++) { + float[] xy = pointAt(i / step); + curve[i] = new Vec2f(xy[0], xy[1]); + } } /** @@ -172,13 +181,11 @@ public class CircumscribedCircle extends Curve { public void draw() { Image hitCircle = GameImage.HITCIRCLE.getImage(); Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); - float[][] xy = new float[(int) step + 1][]; for (int i = 0; i < step; i++) { - xy[i] = pointAt(i / step); - hitCircleOverlay.drawCentered(xy[i][0], xy[i][1], Utils.COLOR_WHITE_FADE); + hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE); } for (int i = 0; i < step; i++) - hitCircle.drawCentered(xy[i][0], xy[i][1], color); + hitCircle.drawCentered(curve[i].x, curve[i].y, color); } @Override diff --git a/src/itdelatrisu/opsu/states/OptionsMenu.java b/src/itdelatrisu/opsu/states/OptionsMenu.java index 10c0a583..b1e6409b 100644 --- a/src/itdelatrisu/opsu/states/OptionsMenu.java +++ b/src/itdelatrisu/opsu/states/OptionsMenu.java @@ -89,7 +89,8 @@ public class OptionsMenu extends BasicGameState { GameOption.FIXED_HP, GameOption.FIXED_AR, GameOption.FIXED_OD, - GameOption.CHECKPOINT + GameOption.CHECKPOINT, + GameOption.USE_2X_IMAGE }); /** Total number of tabs. */