Added multi-image support to GameImage.
- Allows loading an undetermined number of files using a format string (e.g. combo bursts, slider balls). - Fixes bug with those images not being properly reloaded. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
131c8a5637
commit
e02cf60312
|
@ -104,6 +104,7 @@ public enum GameImage {
|
||||||
APPROACHCIRCLE ("approachcircle", "png"),
|
APPROACHCIRCLE ("approachcircle", "png"),
|
||||||
|
|
||||||
// Slider
|
// Slider
|
||||||
|
SLIDER_BALL ("sliderb", "sliderb%d", "png"),
|
||||||
SLIDER_FOLLOWCIRCLE ("sliderfollowcircle", "png"),
|
SLIDER_FOLLOWCIRCLE ("sliderfollowcircle", "png"),
|
||||||
REVERSEARROW ("reversearrow", "png"),
|
REVERSEARROW ("reversearrow", "png"),
|
||||||
SLIDER_TICK ("sliderscorepoint", "png"),
|
SLIDER_TICK ("sliderscorepoint", "png"),
|
||||||
|
@ -117,6 +118,7 @@ public enum GameImage {
|
||||||
SPINNER_OSU ("spinner-osu", "png"),
|
SPINNER_OSU ("spinner-osu", "png"),
|
||||||
|
|
||||||
// Game Score
|
// Game Score
|
||||||
|
COMBO_BURST ("comboburst", "comboburst-%d", "png"),
|
||||||
SCOREBAR_BG ("scorebar-bg", "png") {
|
SCOREBAR_BG ("scorebar-bg", "png") {
|
||||||
@Override
|
@Override
|
||||||
protected Image process_sub(Image img, int w, int h) {
|
protected Image process_sub(Image img, int w, int h) {
|
||||||
|
@ -395,6 +397,11 @@ public enum GameImage {
|
||||||
*/
|
*/
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The formatted file name string (for loading multiple images).
|
||||||
|
*/
|
||||||
|
private String filenameFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image file type.
|
* Image file type.
|
||||||
*/
|
*/
|
||||||
|
@ -416,11 +423,21 @@ public enum GameImage {
|
||||||
*/
|
*/
|
||||||
private Image defaultImage;
|
private Image defaultImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default image array.
|
||||||
|
*/
|
||||||
|
private Image[] defaultImages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The beatmap skin image (optional, temporary).
|
* The beatmap skin image (optional, temporary).
|
||||||
*/
|
*/
|
||||||
private Image skinImage;
|
private Image skinImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The beatmap skin image array (optional, temporary).
|
||||||
|
*/
|
||||||
|
private Image[] skinImages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container dimensions.
|
* Container dimensions.
|
||||||
*/
|
*/
|
||||||
|
@ -446,8 +463,10 @@ public enum GameImage {
|
||||||
* This does NOT destroy images, so be careful of memory leaks!
|
* This does NOT destroy images, so be careful of memory leaks!
|
||||||
*/
|
*/
|
||||||
public static void clearReferences() {
|
public static void clearReferences() {
|
||||||
for (GameImage img : GameImage.values())
|
for (GameImage img : GameImage.values()) {
|
||||||
img.defaultImage = img.skinImage = null;
|
img.defaultImage = img.skinImage = null;
|
||||||
|
img.defaultImages = img.skinImages = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -480,6 +499,19 @@ public enum GameImage {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of possible filenames (with extensions).
|
||||||
|
* @return filename list
|
||||||
|
*/
|
||||||
|
private static List<String> getFileNames(String filename, byte type) {
|
||||||
|
List<String> list = new ArrayList<String>(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).
|
* Constructor for game-related images (skinnable and preloaded).
|
||||||
* @param filename the image file name
|
* @param filename the image file name
|
||||||
|
@ -492,6 +524,20 @@ public enum GameImage {
|
||||||
this.preload = true;
|
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.
|
* Constructor for general images.
|
||||||
* @param filename the image file name
|
* @param filename the image file name
|
||||||
|
@ -523,11 +569,19 @@ public enum GameImage {
|
||||||
* The skin image takes priority over the default image.
|
* The skin image takes priority over the default image.
|
||||||
*/
|
*/
|
||||||
public Image getImage() {
|
public Image getImage() {
|
||||||
if (defaultImage == null)
|
|
||||||
setDefaultImage();
|
setDefaultImage();
|
||||||
return (skinImage != null) ? skinImage : defaultImage;
|
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.
|
* Sets the image associated with this resource to another image.
|
||||||
* The skin image takes priority over the default image.
|
* The skin image takes priority over the default image.
|
||||||
|
@ -540,16 +594,17 @@ public enum GameImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of possible filenames (with extensions).
|
* Sets an image associated with this resource to another image.
|
||||||
* @return filename list
|
* The skin image takes priority over the default image.
|
||||||
*/
|
*/
|
||||||
private List<String> getFileNames() {
|
public void setImage(Image img, int index) {
|
||||||
List<String> list = new ArrayList<String>(2);
|
if (skinImages != null) {
|
||||||
if ((type & IMG_PNG) != 0)
|
if (index < skinImages.length)
|
||||||
list.add(String.format("%s.png", filename));
|
this.skinImages[index] = img;
|
||||||
if ((type & IMG_JPG) != 0)
|
} else {
|
||||||
list.add(String.format("%s.jpg", filename));
|
if (index < defaultImages.length)
|
||||||
return list;
|
this.defaultImages[index] = img;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -557,9 +612,40 @@ public enum GameImage {
|
||||||
* If the default image has already been loaded, this will do nothing.
|
* If the default image has already been loaded, this will do nothing.
|
||||||
*/
|
*/
|
||||||
public void setDefaultImage() {
|
public void setDefaultImage() {
|
||||||
if (defaultImage != null)
|
if (defaultImage != null || defaultImages != null)
|
||||||
return;
|
return;
|
||||||
for (String name : getFileNames()) {
|
|
||||||
|
// load image array
|
||||||
|
if (filenameFormat != null) {
|
||||||
|
List<Image> list = new ArrayList<Image>();
|
||||||
|
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 {
|
||||||
// try loading the image
|
// try loading the image
|
||||||
Image img = new Image(name);
|
Image img = new Image(name);
|
||||||
|
@ -584,16 +670,49 @@ public enum GameImage {
|
||||||
if (dir == null)
|
if (dir == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// destroy the existing image, if any
|
// destroy the existing images, if any
|
||||||
destroySkinImage();
|
destroySkinImage();
|
||||||
|
|
||||||
// beatmap skins disabled
|
// beatmap skins disabled
|
||||||
if (Options.isBeatmapSkinIgnored())
|
if (Options.isBeatmapSkinIgnored())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// look for multiple skin images
|
||||||
|
if (filenameFormat != null) {
|
||||||
|
List<Image> list = new ArrayList<Image>();
|
||||||
|
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
|
// look for a skin image
|
||||||
String errorFile = null;
|
String errorFile = null;
|
||||||
for (String name : getFileNames()) {
|
for (String name : getFileNames(filename, type)) {
|
||||||
File file = new File(dir, name);
|
File file = new File(dir, name);
|
||||||
if (!file.isFile())
|
if (!file.isFile())
|
||||||
continue;
|
continue;
|
||||||
|
@ -624,17 +743,32 @@ public enum GameImage {
|
||||||
public boolean hasSkinImage() { return (skinImage != null && !skinImage.isDestroyed()); }
|
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() {
|
private void destroySkinImage() {
|
||||||
if (skinImage == null)
|
if (skinImage == null && skinImages == null)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
|
if (skinImage != null) {
|
||||||
if (!skinImage.isDestroyed())
|
if (!skinImage.isDestroyed())
|
||||||
skinImage.destroy();
|
skinImage.destroy();
|
||||||
skinImage = null;
|
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) {
|
} 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 @@ public enum GameImage {
|
||||||
* Performs individual post-loading actions on the image.
|
* Performs individual post-loading actions on the image.
|
||||||
*/
|
*/
|
||||||
private void process() {
|
private void process() {
|
||||||
|
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));
|
setImage(process_sub(getImage(), containerWidth, containerHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -249,35 +249,11 @@ public class GameScore {
|
||||||
*/
|
*/
|
||||||
public void loadImages(File dir) throws SlickException {
|
public void loadImages(File dir) throws SlickException {
|
||||||
// combo burst images
|
// combo burst images
|
||||||
if (comboBurstImages != null) {
|
if (GameImage.COMBO_BURST.hasSkinImages() ||
|
||||||
for (int i = 0; i < comboBurstImages.length; i++) {
|
(!GameImage.COMBO_BURST.hasSkinImage() && GameImage.COMBO_BURST.getImages() != null))
|
||||||
if (!comboBurstImages[i].isDestroyed())
|
comboBurstImages = GameImage.COMBO_BURST.getImages();
|
||||||
comboBurstImages[i].destroy();
|
else
|
||||||
}
|
comboBurstImages = new Image[]{ GameImage.COMBO_BURST.getImage() };
|
||||||
}
|
|
||||||
LinkedList<Image> comboBurst = new LinkedList<Image>();
|
|
||||||
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()]);
|
|
||||||
|
|
||||||
// default symbol images
|
// default symbol images
|
||||||
defaultSymbols = new Image[10];
|
defaultSymbols = new Image[10];
|
||||||
|
|
|
@ -27,8 +27,6 @@ import itdelatrisu.opsu.Utils;
|
||||||
import itdelatrisu.opsu.audio.MusicController;
|
import itdelatrisu.opsu.audio.MusicController;
|
||||||
import itdelatrisu.opsu.states.Game;
|
import itdelatrisu.opsu.states.Game;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -282,33 +280,15 @@ public class Slider implements HitObject {
|
||||||
diameter = diameter * container.getWidth() / 640; // convert from Osupixels (640x480)
|
diameter = diameter * container.getWidth() / 640; // convert from Osupixels (640x480)
|
||||||
|
|
||||||
// slider ball
|
// slider ball
|
||||||
if (sliderBall != null) {
|
Image[] sliderBallImages;
|
||||||
for (int i = 0; i < sliderBall.getFrameCount(); i++) {
|
if (GameImage.SLIDER_BALL.hasSkinImages() ||
|
||||||
Image img = sliderBall.getImage(i);
|
(!GameImage.SLIDER_BALL.hasSkinImage() && GameImage.SLIDER_BALL.getImages() != null))
|
||||||
if (!img.isDestroyed())
|
sliderBallImages = GameImage.SLIDER_BALL.getImages();
|
||||||
img.destroy();
|
else
|
||||||
}
|
sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() };
|
||||||
}
|
for (int i = 0; i < sliderBallImages.length; i++)
|
||||||
sliderBall = new Animation();
|
sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128);
|
||||||
String sliderFormat = "sliderb%d.png";
|
sliderBall = new Animation(sliderBallImages, 60);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128));
|
GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128));
|
||||||
GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));
|
GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user