diff --git a/README.md b/README.md
index 02e884d4..b6029f61 100644
--- a/README.md
+++ b/README.md
@@ -17,10 +17,9 @@ opsu! also requires beatmaps to run, which are available for download on the
[osu!Mirror](https://osu.yas-online.net/) or [Bloodcat](http://bloodcat.com/osu/).
If osu! is already installed, this application will attempt to load songs
-directly from the osu! program folder. Otherwise, run this application from
-one directory above the root song directory, or place songs in the generated
-`songs` folder. This path can be changed at any time by editing the
-`BeatmapDirectory` value in the generated configuration file.
+directly from the osu! program folder. Otherwise, place songs in the generated
+`Songs` folder or set the `BeatmapDirectory` value in the generated
+configuration file to the path of the root song directory.
### First Run
The `Music Offset` value will likely need to be adjusted when playing for the
diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java
new file mode 100644
index 00000000..111cf20b
--- /dev/null
+++ b/src/itdelatrisu/opsu/GameImage.java
@@ -0,0 +1,178 @@
+/*
+ * opsu! - an open-source osu! client
+ * Copyright (C) 2014 Jeffrey Han
+ *
+ * opsu! is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opsu! is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opsu!. If not, see .
+ */
+package itdelatrisu.opsu;
+
+import itdelatrisu.opsu.states.Options;
+
+import java.io.File;
+
+import org.newdawn.slick.Image;
+import org.newdawn.slick.SlickException;
+import org.newdawn.slick.util.Log;
+
+/**
+ * Game images.
+ */
+public enum GameImage {
+ // Game
+ SECTION_PASS ("section-pass.png"),
+ SECTION_FAIL ("section-fail.png"),
+ WARNINGARROW ("play-warningarrow.png"),
+ SKIP ("play-skip.png"),
+ COUNTDOWN_READY ("ready.png"),
+ COUNTDOWN_3 ("count3.png"),
+ COUNTDOWN_2 ("count2.png"),
+ COUNTDOWN_1 ("count1.png"),
+ COUNTDOWN_GO ("go.png"),
+ HITCIRCLE_SELECT ("hitcircleselect.png"),
+ UNRANKED ("play-unranked.png"),
+
+ // Game Pause/Fail
+ PAUSE_CONTINUE ("pause-continue.png"),
+ PAUSE_RETRY ("pause-retry.png"),
+ PAUSE_BACK ("pause-back.png"),
+ PAUSE_OVERLAY ("pause-overlay.png"),
+ FAIL_BACKGROUND ("fail-background.png"),
+
+ // Circle
+ HITCIRCLE ("hitcircle.png"),
+ HITCIRCLE_OVERLAY ("hitcircleoverlay.png"),
+ APPROACHCIRCLE ("approachcircle.png"),
+
+ // Slider
+ SLIDER_FOLLOWCIRCLE ("sliderfollowcircle.png"),
+ REVERSEARROW ("reversearrow.png"),
+ SLIDER_TICK ("sliderscorepoint.png"),
+
+ // Spinner
+ SPINNER_CIRCLE ("spinner-circle.png"),
+ SPINNER_APPROACHCIRCLE ("spinner-approachcircle.png"),
+ SPINNER_METRE ("spinner-metre.png"),
+ SPINNER_SPIN ("spinner-spin.png"),
+ SPINNER_CLEAR ("spinner-clear.png"),
+ SPINNER_OSU ("spinner-osu.png"),
+
+ // Game Score
+ SCOREBAR_BG ("scorebar-bg.png"),
+ SCOREBAR_COLOUR ("scorebar-colour.png"),
+ SCOREBAR_KI ("scorebar-ki.png"),
+ SCOREBAR_KI_DANGER ("scorebar-kidanger.png"),
+ SCOREBAR_KI_DANGER2 ("scorebar-kidanger2.png"),
+ HIT_MISS ("hit0.png"),
+ HIT_50 ("hit50.png"),
+ HIT_100 ("hit100.png"),
+ HIT_300 ("hit300.png"),
+ HIT_100K ("hit100k.png"),
+ HIT_300K ("hit300k.png"),
+ HIT_300G ("hit300g.png"),
+ HIT_SLIDER10 ("sliderpoint10.png"),
+ HIT_SLIDER30 ("sliderpoint30.png"),
+ RANKING_SS ("ranking-X.png"),
+ RANKING_SS_SMALL ("ranking-X-small.png"),
+ RANKING_SSH ("ranking-XH.png"),
+ RANKING_SSH_SMALL ("ranking-XH-small.png"),
+ RANKING_S ("ranking-S.png"),
+ RANKING_S_SMALL ("ranking-S-small.png"),
+ RANKING_SH ("ranking-SH.png"),
+ RANKING_SH_SMALL ("ranking-SH-small.png"),
+ RANKING_A ("ranking-A.png"),
+ RANKING_A_SMALL ("ranking-A-small.png"),
+ RANKING_B ("ranking-B.png"),
+ RANKING_B_SMALL ("ranking-B-small.png"),
+ RANKING_C ("ranking-C.png"),
+ RANKING_C_SMALL ("ranking-C-small.png"),
+ RANKING_D ("ranking-D.png"),
+ RANKING_D_SMALL ("ranking-D-small.png"),
+ RANKING_PANEL ("ranking-panel.png"),
+ RANKING_PERFECT ("ranking-perfect.png"),
+ RANKING_TITLE ("ranking-title.png"),
+ RANKING_MAXCOMBO ("ranking-maxcombo.png"),
+ RANKING_ACCURACY ("ranking-accuracy.png"),
+ DEFAULT_0 ("default-0.png"),
+ DEFAULT_1 ("default-1.png"),
+ DEFAULT_2 ("default-2.png"),
+ DEFAULT_3 ("default-3.png"),
+ DEFAULT_4 ("default-4.png"),
+ DEFAULT_5 ("default-5.png"),
+ DEFAULT_6 ("default-6.png"),
+ DEFAULT_7 ("default-7.png"),
+ DEFAULT_8 ("default-8.png"),
+ DEFAULT_9 ("default-9.png"),
+ SCORE_0 ("score-0.png"),
+ SCORE_1 ("score-1.png"),
+ SCORE_2 ("score-2.png"),
+ SCORE_3 ("score-3.png"),
+ SCORE_4 ("score-4.png"),
+ SCORE_5 ("score-5.png"),
+ SCORE_6 ("score-6.png"),
+ SCORE_7 ("score-7.png"),
+ SCORE_8 ("score-8.png"),
+ SCORE_9 ("score-9.png"),
+ SCORE_COMMA ("score-comma.png"),
+ SCORE_DOT ("score-dot.png"),
+ SCORE_PERCENT ("score-percent.png"),
+ SCORE_X ("score-x.png");
+
+ /**
+ * The file name.
+ */
+ private String filename;
+
+ /**
+ * The associated image.
+ */
+ private Image img;
+
+ /**
+ * Constructor.
+ */
+ GameImage(String filename) {
+ this.filename = filename;
+ }
+
+ /**
+ * Returns the associated image.
+ */
+ public Image getImage() { return img; }
+
+ /**
+ * Sets an image.
+ */
+ public void setImage(Image img) { this.img = img; }
+
+ /**
+ * Sets an image.
+ * Scans the path for the image first, then uses the default image.
+ */
+ public void setImage(File dir) {
+ try {
+ // destroy the existing image, if any
+ if (img != null && !img.isDestroyed())
+ img.destroy();
+
+ // set a new image
+ File file = new File(dir, filename);
+ if (file.isFile() && !Options.isBeatmapSkinIgnored())
+ img = new Image(file.getAbsolutePath());
+ else
+ img = new Image(filename);
+ } catch (SlickException e) {
+ Log.error(String.format("Failed to set image '%s'.", filename), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/GameScore.java b/src/itdelatrisu/opsu/GameScore.java
index 530031ba..386bc5b7 100644
--- a/src/itdelatrisu/opsu/GameScore.java
+++ b/src/itdelatrisu/opsu/GameScore.java
@@ -20,6 +20,7 @@ package itdelatrisu.opsu;
import itdelatrisu.opsu.states.Options;
+import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -28,7 +29,6 @@ import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
-import org.newdawn.slick.util.Log;
/**
* Holds score data and renders all score-related elements.
@@ -179,26 +179,6 @@ public class GameScore {
*/
private float difficulty = 5f;
- /**
- * Scorebar-related images.
- */
- private Image
- bgImage, // background (always rendered)
- colourImage, // health bar (cropped)
- kiImage, // end image (50~100% health)
- kiDangerImage, // end image (25~50% health)
- kiDanger2Image; // end image (0~25% health)
-
- /**
- * Ranking screen images.
- */
- private Image
- rankingPanel, // panel to display text in
- perfectImage, // display if full combo
- rankingImage, // styled text "Ranking"
- comboImage, // styled text "Combo"
- accuracyImage; // styled text "Accuracy"
-
/**
* Default text symbol images.
*/
@@ -233,20 +213,7 @@ public class GameScore {
this.width = width;
this.height = height;
- hitResults = new Image[HIT_MAX];
- defaultSymbols = new Image[10];
- scoreSymbols = new HashMap(14);
- gradesLarge = new Image[GRADE_MAX];
- gradesSmall = new Image[GRADE_MAX];
- comboBurstImages = new Image[4];
-
clear();
-
- try {
- initializeImages();
- } catch (Exception e) {
- Log.error("Failed to initialize images.", e);
- }
}
/**
@@ -267,100 +234,150 @@ public class GameScore {
}
/**
- * Initialize all images tied to this object.
+ * Loads all game score images.
* @throws SlickException
*/
- private void initializeImages() throws SlickException {
- // scorebar
- setScorebarImage(
- new Image("scorebar-bg.png"),
- new Image("scorebar-colour.png"),
- new Image("scorebar-ki.png"),
- new Image("scorebar-kidanger.png"),
- new Image("scorebar-kidanger2.png")
- );
-
- // text symbol images
- for (int i = 0; i <= 9; i++) {
- defaultSymbols[i] = new Image(String.format("default-%d.png", i));
- scoreSymbols.put(Character.forDigit(i, 10), new Image(String.format("score-%d.png", i)));
- }
- scoreSymbols.put(',', new Image("score-comma.png"));
- scoreSymbols.put('.', new Image("score-dot.png"));
- scoreSymbols.put('%', new Image("score-percent.png"));
- scoreSymbols.put('x', new Image("score-x.png"));
-
- // hit result images
- hitResults[HIT_MISS] = new Image("hit0.png");
- hitResults[HIT_50] = new Image("hit50.png");
- hitResults[HIT_100] = new Image("hit100.png");
- hitResults[HIT_300] = new Image("hit300.png");
- hitResults[HIT_100K] = new Image("hit100k.png");
- hitResults[HIT_300K] = new Image("hit300k.png");
- hitResults[HIT_300G] = new Image("hit300g.png");
- hitResults[HIT_SLIDER10] = new Image("sliderpoint10.png");
- hitResults[HIT_SLIDER30] = new Image("sliderpoint30.png");
+ public void loadImages() throws SlickException {
+ File dir = MusicController.getOsuFile().getFile().getParentFile();
// combo burst images
- for (int i = 0; i <= 3; i++)
- comboBurstImages[i] = new Image(String.format("comboburst-%d.png", i));
+ 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()]);
// lighting image
- try {
- lighting = new Image("lighting.png");
- lighting1 = new Image("lighting1.png");
- } catch (Exception e) {
- // optional
+ if (lighting != null && !lighting.isDestroyed()) {
+ lighting.destroy();
+ lighting = null;
}
+ if (lighting1 != null && !lighting1.isDestroyed()) {
+ lighting1.destroy();
+ lighting1 = null;
+ }
+ File lightingFile = new File(dir, "lighting.png");
+ File lighting1File = new File(dir, "lighting1.png");
+ if (lightingFile.isFile()) { // beatmap provides images
+ try {
+ lighting = new Image(lightingFile.getAbsolutePath());
+ lighting1 = new Image(lighting1File.getAbsolutePath());
+ } catch (Exception e) {
+ // optional
+ }
+ } else { // load default image
+ try {
+ lighting = new Image("lighting.png");
+ lighting1 = new Image("lighting1.png");
+ } catch (Exception e) {
+ // optional
+ }
+ }
+
+ // scorebar
+ Image bg = GameImage.SCOREBAR_BG.getImage();
+ Image colour = GameImage.SCOREBAR_COLOUR.getImage();
+ int bgWidth = width / 2;
+ GameImage.SCOREBAR_BG.setImage(bg.getScaledCopy(bgWidth, bg.getHeight()));
+ GameImage.SCOREBAR_COLOUR.setImage(colour.getScaledCopy(bgWidth, colour.getHeight()));
+
+ // default symbol images
+ defaultSymbols = new Image[10];
+ defaultSymbols[0] = GameImage.DEFAULT_0.getImage();
+ defaultSymbols[1] = GameImage.DEFAULT_1.getImage();
+ defaultSymbols[2] = GameImage.DEFAULT_2.getImage();
+ defaultSymbols[3] = GameImage.DEFAULT_3.getImage();
+ defaultSymbols[4] = GameImage.DEFAULT_4.getImage();
+ defaultSymbols[5] = GameImage.DEFAULT_5.getImage();
+ defaultSymbols[6] = GameImage.DEFAULT_6.getImage();
+ defaultSymbols[7] = GameImage.DEFAULT_7.getImage();
+ defaultSymbols[8] = GameImage.DEFAULT_8.getImage();
+ defaultSymbols[9] = GameImage.DEFAULT_9.getImage();
+
+ // score symbol images
+ scoreSymbols = new HashMap(14);
+ scoreSymbols.put('0', GameImage.SCORE_0.getImage());
+ scoreSymbols.put('1', GameImage.SCORE_1.getImage());
+ scoreSymbols.put('2', GameImage.SCORE_2.getImage());
+ scoreSymbols.put('3', GameImage.SCORE_3.getImage());
+ scoreSymbols.put('4', GameImage.SCORE_4.getImage());
+ scoreSymbols.put('5', GameImage.SCORE_5.getImage());
+ scoreSymbols.put('6', GameImage.SCORE_6.getImage());
+ scoreSymbols.put('7', GameImage.SCORE_7.getImage());
+ scoreSymbols.put('8', GameImage.SCORE_8.getImage());
+ scoreSymbols.put('9', GameImage.SCORE_9.getImage());
+ scoreSymbols.put(',', GameImage.SCORE_COMMA.getImage());
+ scoreSymbols.put('.', GameImage.SCORE_DOT.getImage());
+ scoreSymbols.put('%', GameImage.SCORE_PERCENT.getImage());
+ scoreSymbols.put('x', GameImage.SCORE_X.getImage());
+
+ // hit result images
+ hitResults = new Image[HIT_MAX];
+ hitResults[HIT_MISS] = GameImage.HIT_MISS.getImage();
+ hitResults[HIT_50] = GameImage.HIT_50.getImage();
+ hitResults[HIT_100] = GameImage.HIT_100.getImage();
+ hitResults[HIT_300] = GameImage.HIT_300.getImage();
+ hitResults[HIT_100K] = GameImage.HIT_100K.getImage();
+ hitResults[HIT_300K] = GameImage.HIT_300K.getImage();
+ hitResults[HIT_300G] = GameImage.HIT_300G.getImage();
+ hitResults[HIT_SLIDER10] = GameImage.HIT_SLIDER10.getImage();
+ hitResults[HIT_SLIDER30] = GameImage.HIT_SLIDER30.getImage();
// letter grade images
- String[] grades = { "X", "XH", "S", "SH", "A", "B", "C", "D" };
- for (int i = 0; i < grades.length; i++) {
- gradesLarge[i] = new Image(String.format("ranking-%s.png", grades[i]));
- gradesSmall[i] = new Image(String.format("ranking-%s-small.png", grades[i]));
- }
+ gradesLarge = new Image[GRADE_MAX];
+ gradesSmall = new Image[GRADE_MAX];
+ gradesLarge[GRADE_SS] = GameImage.RANKING_SS.getImage();
+ gradesSmall[GRADE_SS] = GameImage.RANKING_SS_SMALL.getImage();
+ gradesLarge[GRADE_SSH] = GameImage.RANKING_SSH.getImage();
+ gradesSmall[GRADE_SSH] = GameImage.RANKING_SSH_SMALL.getImage();
+ gradesLarge[GRADE_S] = GameImage.RANKING_S.getImage();
+ gradesSmall[GRADE_S] = GameImage.RANKING_S_SMALL.getImage();
+ gradesLarge[GRADE_SH] = GameImage.RANKING_SH.getImage();
+ gradesSmall[GRADE_SH] = GameImage.RANKING_SH_SMALL.getImage();
+ gradesLarge[GRADE_A] = GameImage.RANKING_A.getImage();
+ gradesSmall[GRADE_A] = GameImage.RANKING_A_SMALL.getImage();
+ gradesLarge[GRADE_B] = GameImage.RANKING_B.getImage();
+ gradesSmall[GRADE_B] = GameImage.RANKING_B_SMALL.getImage();
+ gradesLarge[GRADE_C] = GameImage.RANKING_C.getImage();
+ gradesSmall[GRADE_C] = GameImage.RANKING_C_SMALL.getImage();
+ gradesLarge[GRADE_D] = GameImage.RANKING_D.getImage();
+ gradesSmall[GRADE_D] = GameImage.RANKING_D_SMALL.getImage();
// ranking screen elements
- setRankingImage(
- new Image("ranking-panel.png"),
- new Image("ranking-perfect.png"),
- new Image("ranking-title.png"),
- new Image("ranking-maxcombo.png"),
- new Image("ranking-accuracy.png")
- );
- }
-
- /**
- * Sets a background, health bar, and end image.
- * @param bgImage background image
- * @param colourImage health bar image
- * @param kiImage end image
- */
- public void setScorebarImage(Image bg, Image colour,
- Image ki, Image kiDanger, Image kiDanger2) {
- int bgWidth = width / 2;
- this.bgImage = bg.getScaledCopy(bgWidth, bg.getHeight());
- this.colourImage = colour.getScaledCopy(bgWidth, colour.getHeight());
- this.kiImage = ki;
- this.kiDangerImage = kiDanger;
- this.kiDanger2Image = kiDanger2;
- }
-
- /**
- * Sets a ranking panel, full combo, and ranking/combo/accuracy text image.
- * @param rankingPanel ranking panel image
- * @param perfectImage full combo image
- * @param rankingImage styled text "Ranking"
- * @param comboImage styled text "Combo"
- * @param accuracyImage styled text "Accuracy"
- */
- public void setRankingImage(Image rankingPanel, Image perfectImage,
- Image rankingImage, Image comboImage, Image accuracyImage) {
- this.rankingPanel = rankingPanel.getScaledCopy((height * 0.63f) / rankingPanel.getHeight());
- this.perfectImage = perfectImage.getScaledCopy((height * 0.16f) / perfectImage.getHeight());
- this.rankingImage = rankingImage.getScaledCopy((height * 0.15f) / rankingImage.getHeight());
- this.comboImage = comboImage.getScaledCopy((height * 0.05f) / comboImage.getHeight());
- this.accuracyImage = accuracyImage.getScaledCopy((height * 0.05f) / accuracyImage.getHeight());
+ Image rankingPanel = GameImage.RANKING_PANEL.getImage();
+ Image rankingPerfect = GameImage.RANKING_PERFECT.getImage();
+ Image rankingTitle = GameImage.RANKING_TITLE.getImage();
+ Image rankingMaxCombo = GameImage.RANKING_MAXCOMBO.getImage();
+ Image rankingAccuracy = GameImage.RANKING_ACCURACY.getImage();
+ GameImage.RANKING_PANEL.setImage(rankingPanel.getScaledCopy((height * 0.63f) / rankingPanel.getHeight()));
+ GameImage.RANKING_PERFECT.setImage(rankingPerfect.getScaledCopy((height * 0.16f) / rankingPerfect.getHeight()));
+ GameImage.RANKING_TITLE.setImage(rankingTitle.getScaledCopy((height * 0.15f) / rankingTitle.getHeight()));
+ GameImage.RANKING_MAXCOMBO.setImage(rankingMaxCombo.getScaledCopy((height * 0.05f) / rankingMaxCombo.getHeight()));
+ GameImage.RANKING_ACCURACY.setImage(rankingAccuracy.getScaledCopy((height * 0.05f) / rankingAccuracy.getHeight()));
}
/**
@@ -469,15 +486,19 @@ public class GameScore {
if (firstObjectTime >= 1500 && trackPosition < firstObjectTime - 500)
healthRatio = (float) trackPosition / (firstObjectTime - 500);
}
- bgImage.draw(0, 0);
- Image colourCropped = colourImage.getSubImage(0, 0, (int) (colourImage.getWidth() * healthRatio), colourImage.getHeight());
- colourCropped.draw(0, bgImage.getHeight() / 4f);
+ GameImage.SCOREBAR_BG.getImage().draw(0, 0);
+ Image colour = GameImage.SCOREBAR_COLOUR.getImage();
+ Image colourCropped = colour.getSubImage(0, 0, (int) (colour.getWidth() * healthRatio), colour.getHeight());
+ colourCropped.draw(0, GameImage.SCOREBAR_BG.getImage().getHeight() / 4f);
if (health >= 50f)
- kiImage.drawCentered(colourCropped.getWidth(), kiImage.getHeight() / 2f);
+ GameImage.SCOREBAR_KI.getImage().drawCentered(
+ colourCropped.getWidth(), GameImage.SCOREBAR_KI.getImage().getHeight() / 2f);
else if (health >= 25f)
- kiDangerImage.drawCentered(colourCropped.getWidth(), kiDangerImage.getHeight() / 2f);
+ GameImage.SCOREBAR_KI_DANGER.getImage().drawCentered(
+ colourCropped.getWidth(), GameImage.SCOREBAR_KI_DANGER.getImage().getHeight() / 2f);
else
- kiDanger2Image.drawCentered(colourCropped.getWidth(), kiDanger2Image.getHeight() / 2f);
+ GameImage.SCOREBAR_KI_DANGER2.getImage().drawCentered(
+ colourCropped.getWidth(), GameImage.SCOREBAR_KI_DANGER2.getImage().getHeight() / 2f);
// combo burst
if (comboBurstIndex != -1 && comboBurstAlpha > 0f) {
@@ -513,12 +534,14 @@ public class GameScore {
grade.draw(width - grade.getWidth(), height * 0.09f);
// header & "Ranking" text
- float rankingHeight = (rankingImage.getHeight() * 0.75f) + 3;
+ Image rankingTitle = GameImage.RANKING_TITLE.getImage();
+ float rankingHeight = (rankingTitle.getHeight() * 0.75f) + 3;
g.setColor(Utils.COLOR_BLACK_ALPHA);
g.fillRect(0, 0, width, rankingHeight);
- rankingImage.draw((width * 0.97f) - rankingImage.getWidth(), 0);
+ rankingTitle.draw((width * 0.97f) - rankingTitle.getWidth(), 0);
// ranking panel
+ Image rankingPanel = GameImage.RANKING_PANEL.getImage();
int rankingPanelWidth = rankingPanel.getWidth();
int rankingPanelHeight = rankingPanel.getHeight();
rankingPanel.draw(0, rankingHeight - (rankingHeight / 10f));
@@ -557,19 +580,25 @@ public class GameScore {
}
// combo and accuracy
+ Image rankingMaxCombo = GameImage.RANKING_MAXCOMBO.getImage();
+ Image rankingAccuracy = GameImage.RANKING_ACCURACY.getImage();
float textY = rankingHeight + (rankingPanelHeight * 0.87f) - (rankingHeight / 10f);
- float numbersX = comboImage.getWidth() * .07f;
- float numbersY = textY + comboImage.getHeight() * 0.7f;
- comboImage.draw(width * 0.01f, textY);
- accuracyImage.draw(rankingPanelWidth / 2f, textY);
+ float numbersX = rankingMaxCombo.getWidth() * .07f;
+ float numbersY = textY + rankingMaxCombo.getHeight() * 0.7f;
+ rankingMaxCombo.draw(width * 0.01f, textY);
+ rankingAccuracy.draw(rankingPanelWidth / 2f, textY);
drawSymbolString(String.format("%dx", comboMax),
(int) (width * 0.01f + numbersX), (int) numbersY, symbolTextScale, false);
drawSymbolString(String.format("%02.2f%%", getScorePercent()),
(int) (rankingPanelWidth / 2f + numbersX), (int) numbersY, symbolTextScale, false);
// full combo
- if (combo == fullObjectCount)
- perfectImage.draw(width * 0.08f, (height * 0.99f) - perfectImage.getHeight());
+ if (combo == fullObjectCount) {
+ GameImage.RANKING_PERFECT.getImage().draw(
+ width * 0.08f,
+ (height * 0.99f) - GameImage.RANKING_PERFECT.getImage().getHeight()
+ );
+ }
}
/**
diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java
index da6d4290..137a9e3d 100644
--- a/src/itdelatrisu/opsu/Utils.java
+++ b/src/itdelatrisu/opsu/Utils.java
@@ -104,7 +104,6 @@ public class Utils {
// game-related variables
private static GameContainer container;
-// private static StateBasedGame game;
private static Input input;
// This class should not be instantiated.
@@ -119,7 +118,6 @@ public class Utils {
public static void init(GameContainer container, StateBasedGame game)
throws SlickException {
Utils.container = container;
-// Utils.game = game;
Utils.input = container.getInput();
// game settings
diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java
index 167426e0..5cc9203f 100644
--- a/src/itdelatrisu/opsu/objects/Circle.java
+++ b/src/itdelatrisu/opsu/objects/Circle.java
@@ -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) {
diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java
index 5ad9cc2b..c4dd9ea2 100644
--- a/src/itdelatrisu/opsu/objects/Slider.java
+++ b/src/itdelatrisu/opsu/objects/Slider.java
@@ -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;
diff --git a/src/itdelatrisu/opsu/objects/Spinner.java b/src/itdelatrisu/opsu/objects/Spinner.java
index f982ea49..c950884a 100644
--- a/src/itdelatrisu/opsu/objects/Spinner.java
+++ b/src/itdelatrisu/opsu/objects/Spinner.java
@@ -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);
diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java
index 1b691079..2459c6b5 100644
--- a/src/itdelatrisu/opsu/states/Game.java
+++ b/src/itdelatrisu/opsu/states/Game.java
@@ -19,6 +19,7 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GUIMenuButton;
+import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameScore;
import itdelatrisu.opsu.MusicController;
import itdelatrisu.opsu.Opsu;
@@ -31,6 +32,7 @@ import itdelatrisu.opsu.objects.Circle;
import itdelatrisu.opsu.objects.Slider;
import itdelatrisu.opsu.objects.Spinner;
+import java.io.File;
import java.util.HashMap;
import java.util.Stack;
@@ -121,16 +123,6 @@ public class Game extends BasicGameState {
*/
private int breakIndex;
- /**
- * Warning arrows, pointing right and left.
- */
- private Image warningArrowR, warningArrowL;
-
- /**
- * Section pass and fail images (displayed at start of break, when necessary).
- */
- private Image breakStartPass, breakStartFail;
-
/**
* Break start time (0 if not in break).
*/
@@ -161,16 +153,6 @@ public class Game extends BasicGameState {
*/
private float beatLengthBase, beatLength;
- /**
- * Countdown-related images.
- */
- private Image
- countdownReady, // "READY?" text
- countdown3, // "3" text
- countdown1, // "2" text
- countdown2, // "1" text
- countdownGo; // "GO!" text
-
/**
* Whether the countdown sound has been played.
*/
@@ -178,11 +160,6 @@ public class Game extends BasicGameState {
countdownReadySound, countdown3Sound, countdown1Sound,
countdown2Sound, countdownGoSound;
- /**
- * Glowing hit circle outline which must be clicked when returning from pause menu.
- */
- private Image hitCircleSelect;
-
/**
* Mouse coordinates before game paused.
*/
@@ -204,11 +181,6 @@ public class Game extends BasicGameState {
*/
private Image playfield;
- /**
- * Image displayed during unranked plays.
- */
- private Image unrankedImage;
-
// game-related variables
private GameContainer container;
private StateBasedGame game;
@@ -229,41 +201,8 @@ public class Game extends BasicGameState {
int width = container.getWidth();
int height = container.getHeight();
- // spinners have fixed properties, and only need to be initialized once
- Spinner.init(container);
-
- // breaks
- breakStartPass = new Image("section-pass.png");
- breakStartFail = new Image("section-fail.png");
- warningArrowR = new Image("play-warningarrow.png");
- warningArrowL = warningArrowR.getFlippedCopy(true, false);
-
- // skip button
- Image skip = new Image("play-skip.png");
- float skipScale = (height * 0.1f) / skip.getHeight();
- skip = skip.getScaledCopy(skipScale);
- skipButton = new GUIMenuButton(skip,
- width - (skip.getWidth() / 2f),
- height - (skip.getHeight() / 2f));
-
- // countdown
- float countdownHeight = height / 3f;
- countdownReady = new Image("ready.png");
- countdownReady = countdownReady.getScaledCopy(countdownHeight / countdownReady.getHeight());
- countdown3 = new Image("count3.png");
- countdown3 = countdown3.getScaledCopy(countdownHeight / countdown3.getHeight());
- countdown2 = new Image("count2.png");
- countdown2 = countdown2.getScaledCopy(countdownHeight / countdown2.getHeight());
- countdown1 = new Image("count1.png");
- countdown1 = countdown1.getScaledCopy(countdownHeight / countdown1.getHeight());
- countdownGo = new Image("go.png");
- countdownGo = countdownGo.getScaledCopy(countdownHeight / countdownGo.getHeight());
-
- // hit circle select
- hitCircleSelect = new Image("hitcircleselect.png");
-
- // "unranked" image
- unrankedImage = new Image("play-unranked.png");
+ // create the associated GameScore object
+ score = new GameScore(width, height);
// playfield background
try {
@@ -271,9 +210,6 @@ public class Game extends BasicGameState {
} catch (Exception e) {
// optional
}
-
- // create the associated GameScore object
- score = new GameScore(width, height);
}
@Override
@@ -317,13 +253,13 @@ public class Game extends BasicGameState {
trackPosition - breakTime < 5000) {
// show break start
if (score.getHealth() >= 50) {
- breakStartPass.drawCentered(width / 2f, height / 2f);
+ GameImage.SECTION_PASS.getImage().drawCentered(width / 2f, height / 2f);
if (!breakSound) {
SoundController.playSound(SoundController.SOUND_SECTIONPASS);
breakSound = true;
}
} else {
- breakStartFail.drawCentered(width / 2f, height / 2f);
+ GameImage.SECTION_FAIL.getImage().drawCentered(width / 2f, height / 2f);
if (!breakSound) {
SoundController.playSound(SoundController.SOUND_SECTIONFAIL);
breakSound = true;
@@ -334,15 +270,18 @@ public class Game extends BasicGameState {
int endTimeDiff = endTime - trackPosition;
if ((endTimeDiff > 1500 && endTimeDiff < 2000) ||
(endTimeDiff > 500 && endTimeDiff < 1000)) {
- warningArrowR.draw(width * 0.15f, height * 0.15f);
- warningArrowR.draw(width * 0.15f, height * 0.75f);
- warningArrowL.draw(width * 0.75f, height * 0.15f);
- warningArrowL.draw(width * 0.75f, height * 0.75f);
+ Image arrow = GameImage.WARNINGARROW.getImage();
+ arrow.setRotation(0);
+ arrow.draw(width * 0.15f, height * 0.15f);
+ arrow.draw(width * 0.15f, height * 0.75f);
+ arrow.setRotation(180);
+ arrow.draw(width * 0.75f, height * 0.15f);
+ arrow.draw(width * 0.75f, height * 0.75f);
}
}
if (Options.isModActive(Options.MOD_AUTO))
- unrankedImage.drawCentered(width / 2, height * 0.077f);
+ GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
Utils.drawFPS();
Utils.drawCursor();
return;
@@ -380,36 +319,37 @@ public class Game extends BasicGameState {
int timeDiff = osu.objects[0].time - trackPosition;
if (timeDiff >= 500 && timeDiff < 3000) {
if (timeDiff >= 1500) {
- countdownReady.drawCentered(width / 2, height / 2);
+ GameImage.COUNTDOWN_READY.getImage().drawCentered(width / 2, height / 2);
if (!countdownReadySound) {
SoundController.playSound(SoundController.SOUND_READY);
countdownReadySound = true;
}
}
if (timeDiff < 2000) {
- countdown3.draw(0, 0);
+ GameImage.COUNTDOWN_3.getImage().draw(0, 0);
if (!countdown3Sound) {
SoundController.playSound(SoundController.SOUND_COUNT3);
countdown3Sound = true;
}
}
if (timeDiff < 1500) {
- countdown2.draw(width - countdown2.getWidth(), 0);
+ GameImage.COUNTDOWN_2.getImage().draw(width - GameImage.COUNTDOWN_2.getImage().getWidth(), 0);
if (!countdown2Sound) {
SoundController.playSound(SoundController.SOUND_COUNT2);
countdown2Sound = true;
}
}
if (timeDiff < 1000) {
- countdown1.drawCentered(width / 2, height / 2);
+ GameImage.COUNTDOWN_1.getImage().drawCentered(width / 2, height / 2);
if (!countdown1Sound) {
SoundController.playSound(SoundController.SOUND_COUNT1);
countdown1Sound = true;
}
}
} else if (timeDiff >= -500 && timeDiff < 500) {
- countdownGo.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
- countdownGo.drawCentered(width / 2, height / 2);
+ Image go = GameImage.COUNTDOWN_GO.getImage();
+ go.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
+ go.drawCentered(width / 2, height / 2);
if (!countdownGoSound) {
SoundController.playSound(SoundController.SOUND_GO);
countdownGoSound = true;
@@ -442,7 +382,7 @@ public class Game extends BasicGameState {
score.drawHitResults(trackPosition);
if (Options.isModActive(Options.MOD_AUTO))
- unrankedImage.drawCentered(width / 2, height * 0.077f);
+ GameImage.UNRANKED.getImage().drawCentered(width / 2, height * 0.077f);
// returning from pause screen
if (pauseTime > -1 && pausedMouseX > -1 && pausedMouseY > -1) {
@@ -451,8 +391,8 @@ public class Game extends BasicGameState {
g.fillRect(0, 0, width, height);
// draw glowing hit select circle and pulse effect
- int circleRadius = Circle.getHitCircle().getWidth();
- Image cursorCircle = hitCircleSelect.getScaledCopy(circleRadius, circleRadius);
+ int circleRadius = GameImage.HITCIRCLE.getImage().getWidth();
+ Image cursorCircle = GameImage.HITCIRCLE_SELECT.getImage().getScaledCopy(circleRadius, circleRadius);
cursorCircle.setAlpha(1.0f);
cursorCircle.drawCentered(pausedMouseX, pausedMouseY);
Image cursorCirclePulse = cursorCircle.getScaledCopy(1f + pausePulse);
@@ -643,7 +583,7 @@ public class Game extends BasicGameState {
// returning from pause screen
if (pauseTime > -1) {
double distance = Math.hypot(pausedMouseX - x, pausedMouseY - y);
- int circleRadius = Circle.getHitCircle().getWidth() / 2;
+ int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2;
if (distance < circleRadius) {
// unpause the game
pauseTime = -1;
@@ -698,6 +638,7 @@ public class Game extends BasicGameState {
if (restart != RESTART_FALSE) {
// new game
if (restart == RESTART_NEW) {
+ loadImages();
setMapModifiers();
// calculate map length (TODO: end on slider?)
@@ -802,6 +743,49 @@ public class Game extends BasicGameState {
input.isKeyDown(Input.KEY_Z) || input.isKeyDown(Input.KEY_X));
}
+ /**
+ * Loads all game images.
+ * @throws SlickException
+ */
+ private void loadImages() throws SlickException {
+ int width = container.getWidth();
+ int height = container.getHeight();
+
+ // set images
+ File parent = osu.getFile().getParentFile();
+ for (GameImage o : GameImage.values())
+ o.setImage(parent);
+
+ // skip button
+ Image skip = GameImage.SKIP.getImage();
+ float skipScale = (height * 0.1f) / skip.getHeight();
+ skip = skip.getScaledCopy(skipScale);
+ skipButton = new GUIMenuButton(skip,
+ width - (skip.getWidth() / 2f),
+ height - (skip.getHeight() / 2f));
+
+ // countdown
+ Image countdownReady = GameImage.COUNTDOWN_READY.getImage();
+ Image countdown3 = GameImage.COUNTDOWN_3.getImage();
+ Image countdown2 = GameImage.COUNTDOWN_2.getImage();
+ Image countdown1 = GameImage.COUNTDOWN_1.getImage();
+ Image countdownGo = GameImage.COUNTDOWN_GO.getImage();
+ float countdownHeight = height / 3f;
+ GameImage.COUNTDOWN_READY.setImage(
+ countdownReady.getScaledCopy(countdownHeight / countdownReady.getHeight()));
+ GameImage.COUNTDOWN_3.setImage(
+ countdown3.getScaledCopy(countdownHeight / countdown3.getHeight()));
+ GameImage.COUNTDOWN_2.setImage(
+ countdown2.getScaledCopy(countdownHeight / countdown2.getHeight()));
+ GameImage.COUNTDOWN_1.setImage(
+ countdown1.getScaledCopy(countdownHeight / countdown1.getHeight()));
+ GameImage.COUNTDOWN_GO.setImage(
+ countdownGo.getScaledCopy(countdownHeight / countdownGo.getHeight()));
+
+ ((GamePauseMenu) game.getState(Opsu.STATE_GAMEPAUSEMENU)).loadImages();
+ score.loadImages();
+ }
+
/**
* Set map modifiers.
*/
@@ -821,6 +805,7 @@ public class Game extends BasicGameState {
Circle.init(container, circleSize);
Slider.init(container, circleSize, osu);
+ Spinner.init(container);
// approachRate (hit object approach time)
if (approachRate < 5)
diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java
index 0fdc6c6d..e14f343e 100644
--- a/src/itdelatrisu/opsu/states/GamePauseMenu.java
+++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java
@@ -19,6 +19,7 @@
package itdelatrisu.opsu.states;
import itdelatrisu.opsu.GUIMenuButton;
+import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.MusicController;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.SoundController;
@@ -27,7 +28,6 @@ import itdelatrisu.opsu.Utils;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
-import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
@@ -36,7 +36,7 @@ import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;
/**
- * "Game Paused" state.
+ * "Game Pause/Fail" state.
*
* - [Continue] - unpause game (return to game state)
*
- [Retry] - restart game (return to game state)
@@ -59,18 +59,9 @@ public class GamePauseMenu extends BasicGameState {
*/
private GUIMenuButton continueButton, retryButton, backButton;
- /**
- * Background image for pause menu (optional).
- */
- private Image backgroundImage;
-
- /**
- * Background image for fail menu (optional).
- */
- private Image failImage;
-
// game-related variables
private StateBasedGame game;
+ private GameContainer container;
private int state;
public GamePauseMenu(int state) {
@@ -80,43 +71,18 @@ public class GamePauseMenu extends BasicGameState {
@Override
public void init(GameContainer container, StateBasedGame game)
throws SlickException {
+ this.container = container;
this.game = game;
-
- int width = container.getWidth();
- int height = container.getHeight();
-
- // initialize buttons
- continueButton = new GUIMenuButton(new Image("pause-continue.png"), width / 2f, height * 0.25f);
- retryButton = new GUIMenuButton(new Image("pause-retry.png"), width / 2f, height * 0.5f);
- backButton = new GUIMenuButton(new Image("pause-back.png"), width / 2f, height * 0.75f);
-
- // pause background image
- try {
- backgroundImage = new Image("pause-overlay.png").getScaledCopy(width, height);
- backgroundImage.setAlpha(0.7f);
- } catch (Exception e) {
- // optional
- }
-
- // fail image
- try {
- failImage = new Image("fail-background.png").getScaledCopy(width, height);
- failImage.setAlpha(0.7f);
- } catch (Exception e) {
- // optional
- }
}
@Override
public void render(GameContainer container, StateBasedGame game, Graphics g)
throws SlickException {
// background
- if (backgroundImage != null && Game.getRestart() != Game.RESTART_LOSE)
- backgroundImage.draw();
- else if (failImage != null && Game.getRestart() == Game.RESTART_LOSE)
- failImage.draw();
+ if (Game.getRestart() != Game.RESTART_LOSE)
+ GameImage.PAUSE_OVERLAY.getImage().draw();
else
- g.setBackground(Color.black);
+ GameImage.FAIL_BACKGROUND.getImage().draw();
// draw buttons
if (Game.getRestart() != Game.RESTART_LOSE)
@@ -202,4 +168,25 @@ public class GamePauseMenu extends BasicGameState {
Game.setRestart(restart);
game.enterState(Opsu.STATE_GAME);
}
+
+ /**
+ * Loads all game pause/fail menu images.
+ */
+ public void loadImages() {
+ int width = container.getWidth();
+ int height = container.getHeight();
+
+ // initialize buttons
+ continueButton = new GUIMenuButton(GameImage.PAUSE_CONTINUE.getImage(), width / 2f, height * 0.25f);
+ retryButton = new GUIMenuButton(GameImage.PAUSE_RETRY.getImage(), width / 2f, height * 0.5f);
+ backButton = new GUIMenuButton(GameImage.PAUSE_BACK.getImage(), width / 2f, height * 0.75f);
+
+ // pause background image
+ GameImage.PAUSE_OVERLAY.setImage(GameImage.PAUSE_OVERLAY.getImage().getScaledCopy(width, height));
+ GameImage.PAUSE_OVERLAY.getImage().setAlpha(0.7f);
+
+ // fail image
+ GameImage.FAIL_BACKGROUND.setImage(GameImage.FAIL_BACKGROUND.getImage().getScaledCopy(width, height));
+ GameImage.FAIL_BACKGROUND.getImage().setAlpha(0.7f);
+ }
}
diff --git a/src/itdelatrisu/opsu/states/Options.java b/src/itdelatrisu/opsu/states/Options.java
index e06dc341..85efff71 100644
--- a/src/itdelatrisu/opsu/states/Options.java
+++ b/src/itdelatrisu/opsu/states/Options.java
@@ -129,7 +129,8 @@ public class Options extends BasicGameState {
DYNAMIC_BACKGROUND,
SHOW_PERFECT_HIT,
BACKGROUND_DIM,
- FORCE_DEFAULT_PLAYFIELD;
+ FORCE_DEFAULT_PLAYFIELD,
+ IGNORE_BEATMAP_SKINS;
};
/**
@@ -189,6 +190,7 @@ public class Options extends BasicGameState {
private static final GameOption[] gameplayOptions = {
GameOption.BACKGROUND_DIM,
GameOption.FORCE_DEFAULT_PLAYFIELD,
+ GameOption.IGNORE_BEATMAP_SKINS,
GameOption.SHOW_HIT_LIGHTING,
GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT
@@ -311,6 +313,11 @@ public class Options extends BasicGameState {
*/
private static boolean forceDefaultPlayfield = false;
+ /**
+ * Whether or not to ignore resources in the beatmap folders.
+ */
+ private static boolean ignoreBeatmapSkins = false;
+
/**
* Game option coordinate modifiers (for drawing).
*/
@@ -545,6 +552,9 @@ public class Options extends BasicGameState {
case FORCE_DEFAULT_PLAYFIELD:
forceDefaultPlayfield = !forceDefaultPlayfield;
break;
+ case IGNORE_BEATMAP_SKINS:
+ ignoreBeatmapSkins = !ignoreBeatmapSkins;
+ break;
default:
break;
}
@@ -721,6 +731,12 @@ public class Options extends BasicGameState {
"Override the song background with the default playfield background."
);
break;
+ case IGNORE_BEATMAP_SKINS:
+ drawOption(pos, "Ignore All Beatmap Skins",
+ ignoreBeatmapSkins ? "Yes" : "No",
+ "Never use skin element overrides provided by beatmaps."
+ );
+ break;
case SHOW_HIT_LIGHTING:
drawOption(pos, "Show Hit Lighting",
showHitLighting ? "Yes" : "No",
@@ -920,6 +936,12 @@ public class Options extends BasicGameState {
*/
public static boolean isDefaultPlayfieldForced() { return forceDefaultPlayfield; }
+ /**
+ * Returns whether or not beatmap skins are ignored.
+ * @return true if ignored
+ */
+ public static boolean isBeatmapSkinIgnored() { return ignoreBeatmapSkins; }
+
/**
* Returns the current beatmap directory.
* If invalid, this will attempt to search for the directory,
@@ -1044,6 +1066,9 @@ public class Options extends BasicGameState {
case "ForceDefaultPlayfield":
forceDefaultPlayfield = Boolean.parseBoolean(value);
break;
+ case "IgnoreBeatmapSkins":
+ ignoreBeatmapSkins = Boolean.parseBoolean(value);
+ break;
case "HitLighting":
showHitLighting = Boolean.parseBoolean(value);
break;
@@ -1112,6 +1137,8 @@ public class Options extends BasicGameState {
writer.newLine();
writer.write(String.format("ForceDefaultPlayfield = %b", forceDefaultPlayfield));
writer.newLine();
+ writer.write(String.format("IgnoreBeatmapSkins = %b", ignoreBeatmapSkins));
+ writer.newLine();
writer.write(String.format("HitLighting = %b", showHitLighting));
writer.newLine();
writer.write(String.format("ComboBurst = %b", showComboBursts));