Merge pull request #49 from fluddokt/omaster

@2x size images, combo burst speed scaling, CircumscribedCircle drawing optimization.
This commit is contained in:
Jeffrey Han 2015-03-14 22:52:56 -04:00
commit 637f5dc8d4
6 changed files with 125 additions and 82 deletions

View File

@ -164,7 +164,7 @@ public class GameData {
private float comboBurstAlpha; private float comboBurstAlpha;
/** Current x coordinate of the combo burst image (for sliding animation). */ /** 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). */ /** Time offsets for obtaining each hit result (indexed by HIT_* constants). */
private int[] hitResultOffset; private int[] hitResultOffset;
@ -999,11 +999,11 @@ public class GameData {
int leftX = 0; int leftX = 0;
int rightX = width - comboBurstImages[comboBurstIndex].getWidth(); int rightX = width - comboBurstImages[comboBurstIndex].getWidth();
if (comboBurstX < leftX) { if (comboBurstX < leftX) {
comboBurstX += (delta / 2f); comboBurstX += (delta / 2f) * GameImage.getUIscale();
if (comboBurstX > leftX) if (comboBurstX > leftX)
comboBurstX = leftX; comboBurstX = leftX;
} else if (comboBurstX > rightX) { } else if (comboBurstX > rightX) {
comboBurstX -= (delta / 2f); comboBurstX -= (delta / 2f) * GameImage.getUIscale();
if (comboBurstX < rightX) if (comboBurstX < rightX)
comboBurstX = rightX; comboBurstX = rightX;
} else if (comboBurstAlpha > 0f) { } else if (comboBurstAlpha > 0f) {

View File

@ -373,6 +373,9 @@ public enum GameImage {
/** The unscaled container height that uiscale is based on. */ /** The unscaled container height that uiscale is based on. */
private static final int UNSCALED_HEIGHT = 768; 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. * Initializes the GameImage class with container dimensions.
* @param width the container width * @param width the container width
@ -382,6 +385,11 @@ public enum GameImage {
containerWidth = width; containerWidth = width;
containerHeight = height; containerHeight = height;
uiscale = (float) containerHeight / UNSCALED_HEIGHT; uiscale = (float) containerHeight / UNSCALED_HEIGHT;
if (Options.is2xImagesEnabled() && uiscale >= 1) {
imageSizeSuffix = new String[]{"@2x",""};
} else {
imageSizeSuffix = new String[]{""};
}
} }
/** /**
@ -564,51 +572,32 @@ public enum GameImage {
if (defaultImage != null || defaultImages != null) if (defaultImage != null || defaultImages != null)
return; return;
// load image array
if (filenameFormat != null) { if (filenameFormat != null) {
List<Image> list = new ArrayList<Image>(); defaultImages = getMutliImages(Options.getSkinDir());
File dir = Options.getSkinDir(); if (defaultImages != null) {
int i = 0; process();
while (true) { return;
// 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) defaultImages = getMutliImages(null);
break; if (defaultImages != null) {
// 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;
}
}
if (!list.isEmpty()) {
this.defaultImages = list.toArray(new Image[list.size()]);
process(); process();
return; return;
} }
} }
// load single image defaultImage = getSingleImage(Options.getSkinDir());
String name = getImageFileName(filename, Options.getSkinDir(), type, false); if (defaultImage != null) {
if (name == null) { process();
ErrorHandler.error(String.format("Could not find image '%s'.", filename), null, false);
return; return;
} }
try {
Image img = new Image(name); defaultImage = getSingleImage(null);
this.defaultImage = img; if (defaultImage != null) {
process(); process();
} catch (SlickException e) { return;
ErrorHandler.error(String.format("Failed to set default image '%s'.", filename), null, false);
} }
ErrorHandler.error(String.format("Could not find default image '%s'.", filename), null, false);
} }
/** /**
@ -627,47 +616,79 @@ public enum GameImage {
if (Options.isBeatmapSkinIgnored()) if (Options.isBeatmapSkinIgnored())
return false; return false;
// look for multiple skin images skinImages = getMutliImages(dir);
if (skinImages != null) {
process();
return true;
}
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) { if (filenameFormat != null) {
for (String suf : imageSizeSuffix) {
List<Image> list = new ArrayList<Image>(); List<Image> list = new ArrayList<Image>();
int i = 0; int i = 0;
while (true) { while (true) {
// look for next image // look for next image
String filenameFormatted = String.format(filenameFormat, i++); String filenameFormatted = String.format(filenameFormat+suf, i++);
String name = getImageFileName(filenameFormatted, dir, type, true); String name = getImageFileName(filenameFormatted, dir, type, true);
if (name == null) if (name == null)
break; break;
// add image to list // add image to list
try { try {
System.out.println(name);
Image img = new Image(name); Image img = new Image(name);
if(suf.equals("@2x"))
img = img.getScaledCopy(0.5f);
list.add(img); list.add(img);
} catch (SlickException e) { } catch (SlickException e) {
ErrorHandler.error(String.format("Failed to set skin image '%s'.", name), null, false); ErrorHandler.error(String.format("Failed to set image '%s'.", name), null, false);
break; break;
} }
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
this.skinImages = list.toArray(new Image[list.size()]); return list.toArray(new Image[list.size()]);
process();
return true;
} }
} }
}
return null;
}
// look for a skin image /**
String name = getImageFileName(filename, dir, type, true); * Attempts to load GameImage with a single image.
if (name == null) * @return an image or null if not found.
return false; */
public Image getSingleImage(File dir){
for (String suf : imageSizeSuffix) {
// load single image
String name = getImageFileName(filename+suf, dir, type, true);
if (name != null) {
try { try {
Image img = new Image(name); Image img = new Image(name);
this.skinImage = img; System.out.println(name);
process(); if(suf.equals("@2x"))
return true; img = img.getScaledCopy(0.5f);
return img;
} catch (SlickException e) { } catch (SlickException e) {
skinImage = null; ErrorHandler.error(String.format("Failed to set image '%s'.", filename), null, false);
ErrorHandler.error(String.format("Failed to set skin image '%s'.", name), null, false);
} }
return false; }
}
return null;
} }
/** /**

View File

@ -257,6 +257,7 @@ public class Options {
}, },
ENABLE_THEME_SONG ("Enable Theme Song", "Whether to play the theme song upon starting opsu!", true), 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), 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_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); 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);
@ -694,6 +695,14 @@ public class Options {
*/ */
public static boolean isHitErrorBarEnabled() { return GameOption.SHOW_HIT_ERROR_BAR.getBooleanValue(); } 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. * Returns whether or not the mouse wheel is disabled during gameplay.
* @return true if disabled * @return true if disabled
@ -1021,6 +1030,9 @@ public class Options {
case "ScoreMeter": case "ScoreMeter":
GameOption.SHOW_HIT_ERROR_BAR.setValue(Boolean.parseBoolean(value)); GameOption.SHOW_HIT_ERROR_BAR.setValue(Boolean.parseBoolean(value));
break; break;
case "Use2xImage":
GameOption.USE_2X_IMAGE.setValue(Boolean.parseBoolean(value));
break;
case "FixedCS": case "FixedCS":
GameOption.FIXED_CS.setValue((int) (Float.parseFloat(value) * 10f)); GameOption.FIXED_CS.setValue((int) (Float.parseFloat(value) * 10f));
break; break;
@ -1133,6 +1145,8 @@ public class Options {
writer.newLine(); writer.newLine();
writer.write(String.format("ScoreMeter = %b", isHitErrorBarEnabled())); writer.write(String.format("ScoreMeter = %b", isHitErrorBarEnabled()));
writer.newLine(); writer.newLine();
writer.write(String.format("Use2xImage = %b", is2xImagesEnabled()));
writer.newLine();
writer.write(String.format(Locale.US, "FixedCS = %.1f", getFixedCS())); writer.write(String.format(Locale.US, "FixedCS = %.1f", getFixedCS()));
writer.newLine(); writer.newLine();
writer.write(String.format(Locale.US, "FixedHP = %.1f", getFixedHP())); writer.write(String.format(Locale.US, "FixedHP = %.1f", getFixedHP()));

View File

@ -140,7 +140,7 @@ public class Slider implements HitObject {
this.color = color; this.color = color;
this.comboEnd = comboEnd; 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); this.curve = new CircumscribedCircle(hitObject, color);
else else
this.curve = new LinearBezier(hitObject, color); this.curve = new LinearBezier(hitObject, color);

View File

@ -56,6 +56,9 @@ public class CircumscribedCircle extends Curve {
/** The number of steps in the curve to draw. */ /** The number of steps in the curve to draw. */
private float step; private float step;
/** Points along the curve. */
private Vec2f[] curve;
/** /**
* Constructor. * Constructor.
* @param hitObject the associated OsuHitObject * @param hitObject the associated OsuHitObject
@ -120,6 +123,12 @@ public class CircumscribedCircle extends Curve {
// finds the angles to draw for repeats // finds the angles to draw for repeats
this.drawEndAngle = (float) ((endAng + (startAng > endAng ? HALF_PI : -HALF_PI)) * 180 / Math.PI); 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); 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() { public void draw() {
Image hitCircle = GameImage.HITCIRCLE.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage();
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
float[][] xy = new float[(int) step + 1][];
for (int i = 0; i < step; i++) { for (int i = 0; i < step; i++) {
xy[i] = pointAt(i / step); hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE);
hitCircleOverlay.drawCentered(xy[i][0], xy[i][1], Utils.COLOR_WHITE_FADE);
} }
for (int i = 0; i < step; i++) 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 @Override

View File

@ -89,7 +89,8 @@ public class OptionsMenu extends BasicGameState {
GameOption.FIXED_HP, GameOption.FIXED_HP,
GameOption.FIXED_AR, GameOption.FIXED_AR,
GameOption.FIXED_OD, GameOption.FIXED_OD,
GameOption.CHECKPOINT GameOption.CHECKPOINT,
GameOption.USE_2X_IMAGE
}); });
/** Total number of tabs. */ /** Total number of tabs. */