Added initial support for loading beatmap skins.
- Added a GameImage enum for more organized loading of image resources. - Game image loading now takes place directly before each beatmap is loaded. - Added option 'IGNORE_BEATMAP_SKINS' to disable this feature. Other changes: - Slight correction in readme file: apparently the JAR will not run in the osu! program folder. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
|
||||
package itdelatrisu.opsu.objects;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameScore;
|
||||
import itdelatrisu.opsu.MusicController;
|
||||
import itdelatrisu.opsu.OsuHitObject;
|
||||
@@ -27,21 +28,12 @@ import itdelatrisu.opsu.states.Options;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.SlickException;
|
||||
|
||||
/**
|
||||
* Data type representing a circle object.
|
||||
*/
|
||||
public class Circle {
|
||||
/**
|
||||
* Images related to hit circles.
|
||||
*/
|
||||
private static Image
|
||||
hitCircle, // hit circle
|
||||
hitCircleOverlay, // hit circle overlay
|
||||
approachCircle; // approach circle
|
||||
|
||||
/**
|
||||
* The associated OsuHitObject.
|
||||
*/
|
||||
@@ -76,26 +68,11 @@ public class Circle {
|
||||
public static void init(GameContainer container, float circleSize) throws SlickException {
|
||||
int diameter = (int) (96 - (circleSize * 8));
|
||||
diameter = diameter * container.getWidth() / 640; // convert from Osupixels (640x480)
|
||||
hitCircle = new Image("hitcircle.png").getScaledCopy(diameter, diameter);
|
||||
hitCircleOverlay = new Image("hitcircleoverlay.png").getScaledCopy(diameter, diameter);
|
||||
approachCircle = new Image("approachcircle.png").getScaledCopy(diameter, diameter);
|
||||
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameter, diameter));
|
||||
GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameter, diameter));
|
||||
GameImage.APPROACHCIRCLE.setImage(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(diameter, diameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hit circle image.
|
||||
*/
|
||||
public static Image getHitCircle() { return hitCircle; }
|
||||
|
||||
/**
|
||||
* Returns the hit circle overlay image.
|
||||
*/
|
||||
public static Image getHitCircleOverlay() { return hitCircleOverlay; }
|
||||
|
||||
/**
|
||||
* Returns the approach circle image.
|
||||
*/
|
||||
public static Image getApproachCircle() { return approachCircle; }
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param hitObject the associated OsuHitObject
|
||||
@@ -121,11 +98,12 @@ public class Circle {
|
||||
|
||||
if (timeDiff >= 0) {
|
||||
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
||||
Utils.drawCentered(approachCircle.getScaledCopy(approachScale), hitObject.x, hitObject.y, color);
|
||||
Utils.drawCentered(hitCircleOverlay, hitObject.x, hitObject.y, Color.white);
|
||||
Utils.drawCentered(hitCircle, hitObject.x, hitObject.y, color);
|
||||
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale),
|
||||
hitObject.x, hitObject.y, color);
|
||||
Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), hitObject.x, hitObject.y, Color.white);
|
||||
Utils.drawCentered(GameImage.HITCIRCLE.getImage(), hitObject.x, hitObject.y, color);
|
||||
score.drawSymbolNumber(hitObject.comboNumber, hitObject.x, hitObject.y,
|
||||
hitCircle.getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
||||
GameImage.HITCIRCLE.getImage().getWidth() * 0.40f / score.getDefaultSymbolImage(0).getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +140,7 @@ public class Circle {
|
||||
*/
|
||||
public boolean mousePressed(int x, int y) {
|
||||
double distance = Math.hypot(hitObject.x - x, hitObject.y - y);
|
||||
int circleRadius = hitCircle.getWidth() / 2;
|
||||
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
||||
if (distance < circleRadius) {
|
||||
int result = hitResult(hitObject.time);
|
||||
if (result > -1) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package itdelatrisu.opsu.objects;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameScore;
|
||||
import itdelatrisu.opsu.MusicController;
|
||||
import itdelatrisu.opsu.OsuFile;
|
||||
@@ -26,6 +27,8 @@ import itdelatrisu.opsu.Utils;
|
||||
import itdelatrisu.opsu.states.Game;
|
||||
import itdelatrisu.opsu.states.Options;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.newdawn.slick.Animation;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.GameContainer;
|
||||
@@ -36,14 +39,6 @@ import org.newdawn.slick.SlickException;
|
||||
* Data type representing a slider object.
|
||||
*/
|
||||
public class Slider {
|
||||
/**
|
||||
* Images related to sliders.
|
||||
*/
|
||||
private static Image
|
||||
sliderFollowCircle, // slider follow circle
|
||||
reverseArrow, // reverse arrow (for repeats)
|
||||
sliderTick; // slider tick
|
||||
|
||||
/**
|
||||
* Slider ball animation.
|
||||
*/
|
||||
@@ -261,8 +256,8 @@ public class Slider {
|
||||
* Draws the full Bezier curve to the graphics context.
|
||||
*/
|
||||
public void draw() {
|
||||
Image hitCircle = Circle.getHitCircle();
|
||||
Image hitCircleOverlay = Circle.getHitCircleOverlay();
|
||||
Image hitCircle = GameImage.HITCIRCLE.getImage();
|
||||
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
|
||||
|
||||
// draw overlay and hit circle
|
||||
for (int i = curveX.length - 1; i >= 0; i--)
|
||||
@@ -283,12 +278,38 @@ public class Slider {
|
||||
int diameter = (int) (96 - (circleSize * 8));
|
||||
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();
|
||||
for (int i = 0; i <= 9; i++)
|
||||
sliderBall.addFrame(new Image(String.format("sliderb%d.png", i)).getScaledCopy(diameter * 118 / 128, diameter * 118 / 128), 60);
|
||||
sliderFollowCircle = new Image("sliderfollowcircle.png").getScaledCopy(diameter * 259 / 128, diameter * 259 / 128);
|
||||
reverseArrow = new Image("reversearrow.png").getScaledCopy(diameter, diameter);
|
||||
sliderTick = new Image("sliderscorepoint.png").getScaledCopy(diameter / 4, diameter / 4);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.SLIDER_TICK.setImage(GameImage.SLIDER_TICK.getImage().getScaledCopy(diameter / 4, diameter / 4));
|
||||
|
||||
sliderMultiplier = osu.sliderMultiplier;
|
||||
sliderTickRate = osu.sliderTickRate;
|
||||
@@ -310,8 +331,6 @@ public class Slider {
|
||||
this.comboEnd = comboEnd;
|
||||
|
||||
this.bezier = new Bezier();
|
||||
|
||||
// calculate slider time and ticks upon first update call
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,8 +341,8 @@ public class Slider {
|
||||
public void draw(int trackPosition, boolean currentObject) {
|
||||
int timeDiff = hitObject.time - trackPosition;
|
||||
|
||||
Image hitCircleOverlay = Circle.getHitCircleOverlay();
|
||||
Image hitCircle = Circle.getHitCircle();
|
||||
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
|
||||
Image hitCircle = GameImage.HITCIRCLE.getImage();
|
||||
|
||||
// bezier
|
||||
bezier.draw();
|
||||
@@ -332,7 +351,7 @@ public class Slider {
|
||||
if (currentObject && ticksT != null) {
|
||||
for (int i = 0; i < ticksT.length; i++) {
|
||||
float[] c = bezier.pointAt(ticksT[i]);
|
||||
sliderTick.drawCentered(c[0], c[1]);
|
||||
GameImage.SLIDER_TICK.getImage().drawCentered(c[0], c[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,19 +371,20 @@ public class Slider {
|
||||
|
||||
// repeats
|
||||
if (hitObject.repeat - 1 > currentRepeats) {
|
||||
Image arrow = GameImage.REVERSEARROW.getImage();
|
||||
if (currentRepeats % 2 == 0) { // last circle
|
||||
reverseArrow.setRotation(bezier.getEndAngle());
|
||||
reverseArrow.drawCentered(hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex]);
|
||||
arrow.setRotation(bezier.getEndAngle());
|
||||
arrow.drawCentered(hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex]);
|
||||
} else { // first circle
|
||||
reverseArrow.setRotation(bezier.getStartAngle());
|
||||
reverseArrow.drawCentered(hitObject.x, hitObject.y);
|
||||
arrow.setRotation(bezier.getStartAngle());
|
||||
arrow.drawCentered(hitObject.x, hitObject.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeDiff >= 0) {
|
||||
// approach circle
|
||||
float approachScale = 1 + (timeDiff * 2f / game.getApproachTime());
|
||||
Utils.drawCentered(Circle.getApproachCircle().getScaledCopy(approachScale),
|
||||
Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale),
|
||||
hitObject.x, hitObject.y, color);
|
||||
} else {
|
||||
float[] c = bezier.pointAt(getT(trackPosition, false));
|
||||
@@ -374,7 +394,7 @@ public class Slider {
|
||||
|
||||
// follow circle
|
||||
if (followCircleActive)
|
||||
sliderFollowCircle.drawCentered(c[0], c[1]);
|
||||
GameImage.SLIDER_FOLLOWCIRCLE.getImage().drawCentered(c[0], c[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +441,7 @@ public class Slider {
|
||||
return false;
|
||||
|
||||
double distance = Math.hypot(hitObject.x - x, hitObject.y - y);
|
||||
int circleRadius = Circle.getHitCircle().getWidth() / 2;
|
||||
int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
|
||||
if (distance < circleRadius) {
|
||||
int trackPosition = MusicController.getPosition();
|
||||
int timeDiff = Math.abs(trackPosition - hitObject.time);
|
||||
@@ -513,7 +533,7 @@ public class Slider {
|
||||
// check if cursor pressed and within end circle
|
||||
else if (game.isInputKeyPressed()) {
|
||||
double distance = Math.hypot(hitObject.sliderX[lastIndex] - mouseX, hitObject.sliderY[lastIndex] - mouseY);
|
||||
int followCircleRadius = sliderFollowCircle.getWidth() / 2;
|
||||
int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
|
||||
if (distance < followCircleRadius)
|
||||
ticksHit++;
|
||||
}
|
||||
@@ -550,7 +570,7 @@ public class Slider {
|
||||
// holding slider...
|
||||
float[] c = bezier.pointAt(getT(trackPosition, false));
|
||||
double distance = Math.hypot(c[0] - mouseX, c[1] - mouseY);
|
||||
int followCircleRadius = sliderFollowCircle.getWidth() / 2;
|
||||
int followCircleRadius = GameImage.SLIDER_FOLLOWCIRCLE.getImage().getWidth() / 2;
|
||||
if ((game.isInputKeyPressed() && distance < followCircleRadius) || isAutoMod) {
|
||||
// mouse pressed and within follow circle
|
||||
followCircleActive = true;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package itdelatrisu.opsu.objects;
|
||||
|
||||
import itdelatrisu.opsu.GameImage;
|
||||
import itdelatrisu.opsu.GameScore;
|
||||
import itdelatrisu.opsu.MusicController;
|
||||
import itdelatrisu.opsu.OsuHitObject;
|
||||
@@ -36,17 +37,6 @@ import org.newdawn.slick.SlickException;
|
||||
* Data type representing a spinner object.
|
||||
*/
|
||||
public class Spinner {
|
||||
/**
|
||||
* Images related to spinners.
|
||||
*/
|
||||
private static Image
|
||||
spinnerCircle, // spinner
|
||||
spinnerApproachCircle, // spinner approach circle (for end time)
|
||||
spinnerMetre, // spinner meter (subimage based on completion ratio)
|
||||
// spinnerOsuImage, // spinner "OSU!" text (complete)
|
||||
spinnerSpinImage, // spinner "SPIN!" text (start)
|
||||
spinnerClearImage; // spinner "CLEAR" text (passed)
|
||||
|
||||
/**
|
||||
* Container dimensions.
|
||||
*/
|
||||
@@ -91,12 +81,10 @@ public class Spinner {
|
||||
width = container.getWidth();
|
||||
height = container.getHeight();
|
||||
|
||||
spinnerCircle = new Image("spinner-circle.png").getScaledCopy(height * 9 / 10, height * 9 / 10);
|
||||
spinnerApproachCircle = new Image("spinner-approachcircle.png").getScaledCopy(spinnerCircle.getWidth(), spinnerCircle.getHeight());
|
||||
spinnerMetre = new Image("spinner-metre.png").getScaledCopy(width, height);
|
||||
spinnerSpinImage = new Image("spinner-spin.png");
|
||||
spinnerClearImage = new Image("spinner-clear.png");
|
||||
// spinnerOsuImage = new Image("spinner-osu.png");
|
||||
Image spinnerCircle = GameImage.SPINNER_CIRCLE.getImage();
|
||||
GameImage.SPINNER_CIRCLE.setImage(spinnerCircle.getScaledCopy(height * 9 / 10, height * 9 / 10));
|
||||
GameImage.SPINNER_APPROACHCIRCLE.setImage(GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(spinnerCircle.getWidth(), spinnerCircle.getHeight()));
|
||||
GameImage.SPINNER_METRE.setImage(GameImage.SPINNER_METRE.getImage().getScaledCopy(width, height));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,6 +123,7 @@ public class Spinner {
|
||||
return;
|
||||
|
||||
// spinner meter (subimage)
|
||||
Image spinnerMetre = GameImage.SPINNER_METRE.getImage();
|
||||
int spinnerMetreY = (spinnerComplete) ? 0 : (int) (spinnerMetre.getHeight() * (1 - (rotations / rotationsNeeded)));
|
||||
Image spinnerMetreSub = spinnerMetre.getSubImage(
|
||||
0, spinnerMetreY,
|
||||
@@ -143,12 +132,12 @@ public class Spinner {
|
||||
spinnerMetreSub.draw(0, height - spinnerMetreSub.getHeight());
|
||||
|
||||
// main spinner elements
|
||||
spinnerCircle.drawCentered(width / 2, height / 2);
|
||||
spinnerApproachCircle.getScaledCopy(1 - ((float) timeDiff / (hitObject.time - hitObject.endTime))).drawCentered(width / 2, height / 2);
|
||||
spinnerSpinImage.drawCentered(width / 2, height * 3 / 4);
|
||||
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2);
|
||||
GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(1 - ((float) timeDiff / (hitObject.time - hitObject.endTime))).drawCentered(width / 2, height / 2);
|
||||
GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4);
|
||||
|
||||
if (spinnerComplete) {
|
||||
spinnerClearImage.drawCentered(width / 2, height / 4);
|
||||
GameImage.SPINNER_CLEAR.getImage().drawCentered(width / 2, height / 4);
|
||||
int extraRotations = (int) (rotations - rotationsNeeded);
|
||||
if (extraRotations > 0)
|
||||
score.drawSymbolNumber(extraRotations * 1000, width / 2, height * 2 / 3, 1.0f);
|
||||
|
||||
Reference in New Issue
Block a user