diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index d7b56f24..05af3b91 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -104,6 +104,7 @@ protected Image process_sub(Image img, int w, int h) { APPROACHCIRCLE ("approachcircle", "png"), // Slider + SLIDER_BALL ("sliderb", "sliderb%d", "png"), SLIDER_FOLLOWCIRCLE ("sliderfollowcircle", "png"), REVERSEARROW ("reversearrow", "png"), SLIDER_TICK ("sliderscorepoint", "png"), @@ -117,6 +118,7 @@ protected Image process_sub(Image img, int w, int h) { SPINNER_OSU ("spinner-osu", "png"), // Game Score + COMBO_BURST ("comboburst", "comboburst-%d", "png"), SCOREBAR_BG ("scorebar-bg", "png") { @Override protected Image process_sub(Image img, int w, int h) { @@ -395,6 +397,11 @@ protected Image process_sub(Image img, int w, int h) { */ private String filename; + /** + * The formatted file name string (for loading multiple images). + */ + private String filenameFormat; + /** * Image file type. */ @@ -416,11 +423,21 @@ protected Image process_sub(Image img, int w, int h) { */ private Image defaultImage; + /** + * The default image array. + */ + private Image[] defaultImages; + /** * The beatmap skin image (optional, temporary). */ private Image skinImage; + /** + * The beatmap skin image array (optional, temporary). + */ + private Image[] skinImages; + /** * Container dimensions. */ @@ -446,8 +463,10 @@ public static void init(int width, int height) { * This does NOT destroy images, so be careful of memory leaks! */ public static void clearReferences() { - for (GameImage img : GameImage.values()) + for (GameImage img : GameImage.values()) { img.defaultImage = img.skinImage = null; + img.defaultImages = img.skinImages = null; + } } /** @@ -480,6 +499,19 @@ else if (s[i].equals("jpg")) return b; } + /** + * Returns a list of possible filenames (with extensions). + * @return filename list + */ + private static List getFileNames(String filename, byte type) { + List list = new ArrayList(2); + if ((type & IMG_PNG) != 0) + list.add(String.format("%s.png", filename)); + if ((type & IMG_JPG) != 0) + list.add(String.format("%s.jpg", filename)); + return list; + } + /** * Constructor for game-related images (skinnable and preloaded). * @param filename the image file name @@ -492,6 +524,20 @@ else if (s[i].equals("jpg")) this.preload = true; } + /** + * Constructor for an array of game-related images (skinnable and preloaded). + * @param filename the image file name + * @param filenameFormat the formatted file name string (for loading multiple images) + * @param type the file types (separated by '|') + */ + GameImage(String filename, String filenameFormat, String type) { + this.filename = filename; + this.filenameFormat = filenameFormat; + this.type = getType(type); + this.skinnable = true; + this.preload = true; + } + /** * Constructor for general images. * @param filename the image file name @@ -523,11 +569,19 @@ else if (s[i].equals("jpg")) * The skin image takes priority over the default image. */ public Image getImage() { - if (defaultImage == null) - setDefaultImage(); + setDefaultImage(); return (skinImage != null) ? skinImage : defaultImage; } + /** + * Returns the image array associated with this resource. + * The skin images takes priority over the default images. + */ + public Image[] getImages() { + setDefaultImage(); + return (skinImages != null) ? skinImages : defaultImages; + } + /** * Sets the image associated with this resource to another image. * The skin image takes priority over the default image. @@ -540,16 +594,17 @@ public void setImage(Image img) { } /** - * Returns a list of possible filenames (with extensions). - * @return filename list + * Sets an image associated with this resource to another image. + * The skin image takes priority over the default image. */ - private List getFileNames() { - List list = new ArrayList(2); - if ((type & IMG_PNG) != 0) - list.add(String.format("%s.png", filename)); - if ((type & IMG_JPG) != 0) - list.add(String.format("%s.jpg", filename)); - return list; + public void setImage(Image img, int index) { + if (skinImages != null) { + if (index < skinImages.length) + this.skinImages[index] = img; + } else { + if (index < defaultImages.length) + this.defaultImages[index] = img; + } } /** @@ -557,9 +612,40 @@ private List getFileNames() { * If the default image has already been loaded, this will do nothing. */ public void setDefaultImage() { - if (defaultImage != null) + if (defaultImage != null || defaultImages != null) return; - for (String name : getFileNames()) { + + // load image array + if (filenameFormat != null) { + List list = new ArrayList(); + int i = 0; + boolean loaded; + do { + loaded = false; + for (String name : getFileNames(String.format(filenameFormat, i), type)) { + try { + // try loading the image + Image img = new Image(name); + + // image successfully loaded + list.add(img); + loaded = true; + break; + } catch (SlickException | RuntimeException e) { + continue; + } + } + i++; + } while (loaded); + if (!list.isEmpty()) { + this.defaultImages = list.toArray(new Image[list.size()]); + process(); + return; + } + } + + // load single image + for (String name : getFileNames(filename, type)) { try { // try loading the image Image img = new Image(name); @@ -584,16 +670,49 @@ public boolean setSkinImage(File dir) { if (dir == null) return false; - // destroy the existing image, if any + // destroy the existing images, if any destroySkinImage(); // beatmap skins disabled if (Options.isBeatmapSkinIgnored()) return false; + // look for multiple skin images + if (filenameFormat != null) { + List list = new ArrayList(); + int i = 0; + boolean loaded; + do { + loaded = false; + for (String name : getFileNames(String.format(filenameFormat, i), type)) { + File file = new File(dir, name); + if (!file.isFile()) + continue; + try { + // try loading the image + Image img = new Image(file.getAbsolutePath()); + + // image successfully loaded + list.add(img); + loaded = true; + break; + } catch (SlickException | RuntimeException e) { + continue; + } + } + i++; + } while (loaded); + if (!list.isEmpty()) { + this.skinImages = list.toArray(new Image[list.size()]); + process(); + skinImageLoaded = true; + return true; + } + } + // look for a skin image String errorFile = null; - for (String name : getFileNames()) { + for (String name : getFileNames(filename, type)) { File file = new File(dir, name); if (!file.isFile()) continue; @@ -624,17 +743,32 @@ public boolean setSkinImage(File dir) { public boolean hasSkinImage() { return (skinImage != null && !skinImage.isDestroyed()); } /** - * Destroys the associated skin image, if any. + * Returns whether skin images are currently loaded. + * @return true if any skin image exists + */ + public boolean hasSkinImages() { return (skinImages != null); } + + /** + * Destroys the associated skin image(s), if any. */ private void destroySkinImage() { - if (skinImage == null) + if (skinImage == null && skinImages == null) return; try { - if (!skinImage.isDestroyed()) - skinImage.destroy(); - skinImage = null; + if (skinImage != null) { + if (!skinImage.isDestroyed()) + skinImage.destroy(); + skinImage = null; + } + if (skinImages != null) { + for (int i = 0; i < skinImages.length; i++) { + if (!skinImages[i].isDestroyed()) + skinImages[i].destroy(); + } + skinImages = null; + } } catch (SlickException e) { - ErrorHandler.error(String.format("Failed to destroy skin image for '%s'.", this.name()), e, true); + ErrorHandler.error(String.format("Failed to destroy skin images for '%s'.", this.name()), e, true); } } @@ -653,6 +787,13 @@ protected Image process_sub(Image img, int w, int h) { * Performs individual post-loading actions on the image. */ private void process() { - setImage(process_sub(getImage(), containerWidth, containerHeight)); + if (skinImages != null) { + for (int i = 0; i < skinImages.length; i++) + setImage(process_sub(getImages()[i], containerWidth, containerHeight), i); + } else if (defaultImages != null && skinImage == null) { + for (int i = 0; i < defaultImages.length; i++) + setImage(process_sub(getImages()[i], containerWidth, containerHeight), i); + } else + setImage(process_sub(getImage(), containerWidth, containerHeight)); } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/GameScore.java b/src/itdelatrisu/opsu/GameScore.java index fe471330..8028996c 100644 --- a/src/itdelatrisu/opsu/GameScore.java +++ b/src/itdelatrisu/opsu/GameScore.java @@ -249,35 +249,11 @@ public void clear() { */ public void loadImages(File dir) throws SlickException { // combo burst images - if (comboBurstImages != null) { - for (int i = 0; i < comboBurstImages.length; i++) { - if (!comboBurstImages[i].isDestroyed()) - comboBurstImages[i].destroy(); - } - } - LinkedList comboBurst = new LinkedList(); - String comboFormat = "comboburst-%d.png"; - int comboIndex = 0; - File comboFile = new File(dir, "comboburst.png"); - File comboFileN = new File(dir, String.format(comboFormat, comboIndex)); - if (comboFileN.isFile()) { // beatmap provides images - do { - comboBurst.add(new Image(comboFileN.getAbsolutePath())); - comboFileN = new File(dir, String.format(comboFormat, ++comboIndex)); - } while (comboFileN.isFile()); - } else if (comboFile.isFile()) // beatmap provides single image - comboBurst.add(new Image(comboFile.getAbsolutePath())); - else { // load default images - while (true) { - try { - Image comboImage = new Image(String.format(comboFormat, comboIndex++)); - comboBurst.add(comboImage); - } catch (Exception e) { - break; - } - } - } - comboBurstImages = comboBurst.toArray(new Image[comboBurst.size()]); + if (GameImage.COMBO_BURST.hasSkinImages() || + (!GameImage.COMBO_BURST.hasSkinImage() && GameImage.COMBO_BURST.getImages() != null)) + comboBurstImages = GameImage.COMBO_BURST.getImages(); + else + comboBurstImages = new Image[]{ GameImage.COMBO_BURST.getImage() }; // default symbol images defaultSymbols = new Image[10]; diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index 5f8e1b44..712d1fae 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -27,8 +27,6 @@ import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.states.Game; -import java.io.File; - import org.newdawn.slick.Animation; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; @@ -282,33 +280,15 @@ public static void init(GameContainer container, float circleSize, OsuFile osu) diameter = diameter * container.getWidth() / 640; // convert from Osupixels (640x480) // slider ball - if (sliderBall != null) { - for (int i = 0; i < sliderBall.getFrameCount(); i++) { - Image img = sliderBall.getImage(i); - if (!img.isDestroyed()) - img.destroy(); - } - } - sliderBall = new Animation(); - String sliderFormat = "sliderb%d.png"; - int sliderIndex = 0; - File dir = MusicController.getOsuFile().getFile().getParentFile(); - File slider = new File(dir, String.format(sliderFormat, sliderIndex)); - if (slider.isFile()) { - do { - sliderBall.addFrame(new Image(slider.getAbsolutePath()).getScaledCopy(diameter * 118 / 128, diameter * 118 / 128), 60); - slider = new File(dir, String.format(sliderFormat, ++sliderIndex)); - } while (slider.isFile()); - } else { - while (true) { - try { - Image sliderFrame = new Image(String.format(sliderFormat, sliderIndex++)); - sliderBall.addFrame(sliderFrame.getScaledCopy(diameter * 118 / 128, diameter * 118 / 128), 60); - } catch (Exception e) { - break; - } - } - } + Image[] sliderBallImages; + if (GameImage.SLIDER_BALL.hasSkinImages() || + (!GameImage.SLIDER_BALL.hasSkinImage() && GameImage.SLIDER_BALL.getImages() != null)) + sliderBallImages = GameImage.SLIDER_BALL.getImages(); + else + sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() }; + for (int i = 0; i < sliderBallImages.length; i++) + sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128); + sliderBall = new Animation(sliderBallImages, 60); GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128)); GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));