Created a splash screen state for loading resources.
- Game now boots to splash screen, which displays parser progress. - Added option "LoadVerbose" to disable rendering the progress text. - Main Menu backgrounds now fade in regardless of DynamicBackground setting (as a transition from the splash screen). Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
0dbfd18c5f
commit
dea7e942a3
|
@ -25,6 +25,7 @@ import itdelatrisu.opsu.states.MainMenu;
|
|||
import itdelatrisu.opsu.states.MainMenuExit;
|
||||
import itdelatrisu.opsu.states.Options;
|
||||
import itdelatrisu.opsu.states.SongMenu;
|
||||
import itdelatrisu.opsu.states.Splash;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
@ -61,6 +62,7 @@ public class Opsu extends StateBasedGame {
|
|||
* Game states.
|
||||
*/
|
||||
public static final int
|
||||
STATE_SPLASH = 0,
|
||||
STATE_MAINMENU = 1,
|
||||
STATE_MAINMENUEXIT = 2,
|
||||
STATE_SONGMENU = 3,
|
||||
|
@ -80,6 +82,7 @@ public class Opsu extends StateBasedGame {
|
|||
|
||||
@Override
|
||||
public void initStatesList(GameContainer container) throws SlickException {
|
||||
addState(new Splash(STATE_SPLASH));
|
||||
addState(new MainMenu(STATE_MAINMENU));
|
||||
addState(new MainMenuExit(STATE_MAINMENUEXIT));
|
||||
addState(new SongMenu(STATE_SONGMENU));
|
||||
|
@ -125,10 +128,17 @@ public class Opsu extends StateBasedGame {
|
|||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
|
||||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
|
||||
|
||||
// clear the cache
|
||||
if (!Options.TMP_DIR.mkdir()) {
|
||||
for (File tmp : Options.TMP_DIR.listFiles())
|
||||
tmp.delete();
|
||||
}
|
||||
Options.TMP_DIR.deleteOnExit();
|
||||
|
||||
// start the game
|
||||
Opsu osuGame = new Opsu("opsu!");
|
||||
Opsu opsu = new Opsu("opsu!");
|
||||
try {
|
||||
AppGameContainer app = new AppGameContainer(osuGame);
|
||||
AppGameContainer app = new AppGameContainer(opsu);
|
||||
|
||||
// basic game settings
|
||||
int[] containerSize = Options.getContainerSize();
|
||||
|
@ -136,21 +146,6 @@ public class Opsu extends StateBasedGame {
|
|||
String[] icons = { "icon16.png", "icon32.png" };
|
||||
app.setIcons(icons);
|
||||
|
||||
// parse song directory
|
||||
OsuParser.parseAllFiles(Options.getBeatmapDir(),
|
||||
app.getWidth(), app.getHeight()
|
||||
);
|
||||
|
||||
// clear the cache
|
||||
if (!Options.TMP_DIR.mkdir()) {
|
||||
for (File tmp : Options.TMP_DIR.listFiles())
|
||||
tmp.delete();
|
||||
}
|
||||
Options.TMP_DIR.deleteOnExit();
|
||||
|
||||
// load sounds
|
||||
SoundController.init();
|
||||
|
||||
app.start();
|
||||
} catch (SlickException e) {
|
||||
Log.error("Error while creating game container.", e);
|
||||
|
|
|
@ -34,6 +34,21 @@ import org.newdawn.slick.util.Log;
|
|||
* Parser for OSU files.
|
||||
*/
|
||||
public class OsuParser {
|
||||
/**
|
||||
* The current file being parsed.
|
||||
*/
|
||||
private static File currentFile;
|
||||
|
||||
/**
|
||||
* The current directory number while parsing.
|
||||
*/
|
||||
private static int currentDirectoryIndex = -1;
|
||||
|
||||
/**
|
||||
* The total number of directories to parse.
|
||||
*/
|
||||
private static int totalDirectories = -1;
|
||||
|
||||
/**
|
||||
* The x and y multipliers for hit object coordinates.
|
||||
*/
|
||||
|
@ -62,7 +77,13 @@ public class OsuParser {
|
|||
xOffset = width / 5;
|
||||
yOffset = height / 5;
|
||||
|
||||
for (File folder : root.listFiles()) {
|
||||
// progress tracking
|
||||
File[] folders = root.listFiles();
|
||||
currentDirectoryIndex = 0;
|
||||
totalDirectories = folders.length;
|
||||
|
||||
for (File folder : folders) {
|
||||
currentDirectoryIndex++;
|
||||
if (!folder.isDirectory())
|
||||
continue;
|
||||
File[] files = folder.listFiles(new FilenameFilter() {
|
||||
|
@ -77,6 +98,8 @@ public class OsuParser {
|
|||
// create a new group entry
|
||||
ArrayList<OsuFile> osuFiles = new ArrayList<OsuFile>();
|
||||
for (File file : files) {
|
||||
currentFile = file;
|
||||
|
||||
// Parse hit objects only when needed to save time/memory.
|
||||
// Change boolean to 'true' to parse them immediately.
|
||||
parseFile(file, osuFiles, false);
|
||||
|
@ -86,6 +109,10 @@ public class OsuParser {
|
|||
Opsu.groups.addSongGroup(osuFiles);
|
||||
}
|
||||
}
|
||||
|
||||
currentFile = null;
|
||||
currentDirectoryIndex = -1;
|
||||
totalDirectories = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -554,4 +581,22 @@ public class OsuParser {
|
|||
int i = file.lastIndexOf('.');
|
||||
return (i != -1) ? file.substring(i + 1).toLowerCase() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the current file being parsed, or null if none.
|
||||
*/
|
||||
public static String getCurrentFileName() {
|
||||
return (currentFile != null) ? currentFile.getName() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the progress of file parsing, or -1 if not parsing.
|
||||
* @return the completion percent [0, 100] or -1
|
||||
*/
|
||||
public static int getParserProgress() {
|
||||
if (currentDirectoryIndex == -1 || totalDirectories == -1)
|
||||
return -1;
|
||||
|
||||
return currentDirectoryIndex * 100 / totalDirectories;
|
||||
}
|
||||
}
|
|
@ -521,7 +521,6 @@ public class Slider {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// end of slider
|
||||
if (overlap || trackPosition > hitObject.time + sliderTimeTotal) {
|
||||
tickIntervals++;
|
||||
|
|
|
@ -96,9 +96,9 @@ public class MainMenu extends BasicGameState {
|
|||
private Image backgroundImage;
|
||||
|
||||
/**
|
||||
* Alpha level for fade-in effect for dynamic backgrounds.
|
||||
* Background alpha level (for fade-in effect).
|
||||
*/
|
||||
private float dynamicBackgroundAlpha = 0f;
|
||||
private float bgAlpha = 0f;
|
||||
|
||||
// game-related variables
|
||||
private StateBasedGame game;
|
||||
|
@ -111,8 +111,6 @@ public class MainMenu extends BasicGameState {
|
|||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
Utils.init(container, game);
|
||||
|
||||
this.game = game;
|
||||
|
||||
osuStartTime = System.currentTimeMillis();
|
||||
|
@ -165,11 +163,12 @@ public class MainMenu extends BasicGameState {
|
|||
// draw background
|
||||
OsuFile osu = MusicController.getOsuFile();
|
||||
if (Options.isDynamicBackgroundEnabled() &&
|
||||
osu != null && osu.drawBG(width, height, dynamicBackgroundAlpha))
|
||||
osu != null && osu.drawBG(width, height, bgAlpha))
|
||||
;
|
||||
else if (backgroundImage != null)
|
||||
else if (backgroundImage != null) {
|
||||
backgroundImage.setAlpha(bgAlpha);
|
||||
backgroundImage.draw();
|
||||
else
|
||||
} else
|
||||
g.setBackground(Utils.COLOR_BLUE_BACKGROUND);
|
||||
|
||||
// draw buttons
|
||||
|
@ -224,11 +223,11 @@ public class MainMenu extends BasicGameState {
|
|||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
// dynamic backgrounds
|
||||
if (Options.isDynamicBackgroundEnabled() && dynamicBackgroundAlpha < 0.9f) {
|
||||
dynamicBackgroundAlpha += delta / 1000f;
|
||||
if (dynamicBackgroundAlpha > 0.9f)
|
||||
dynamicBackgroundAlpha = 0.9f;
|
||||
// fade in background
|
||||
if (bgAlpha < 0.9f) {
|
||||
bgAlpha += delta / 1000f;
|
||||
if (bgAlpha > 0.9f)
|
||||
bgAlpha = 0.9f;
|
||||
}
|
||||
|
||||
// buttons
|
||||
|
@ -296,12 +295,14 @@ public class MainMenu extends BasicGameState {
|
|||
OsuGroupNode node = menu.setFocus(Opsu.groups.getRandomNode(), -1, true);
|
||||
if (node != null)
|
||||
previous.add(node.index);
|
||||
dynamicBackgroundAlpha = 0f;
|
||||
if (Options.isDynamicBackgroundEnabled())
|
||||
bgAlpha = 0f;
|
||||
} else if (musicPrevious.contains(x, y)) {
|
||||
if (!previous.isEmpty()) {
|
||||
SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
||||
menu.setFocus(Opsu.groups.getBaseNode(previous.pop()), -1, true);
|
||||
dynamicBackgroundAlpha = 0f;
|
||||
if (Options.isDynamicBackgroundEnabled())
|
||||
bgAlpha = 0f;
|
||||
} else
|
||||
MusicController.setPosition(0);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,8 @@ public class Options extends BasicGameState {
|
|||
FIXED_CS,
|
||||
FIXED_HP,
|
||||
FIXED_AR,
|
||||
FIXED_OD;
|
||||
FIXED_OD,
|
||||
LOAD_VERBOSE;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -177,7 +178,8 @@ public class Options extends BasicGameState {
|
|||
GameOption.SHOW_FPS,
|
||||
GameOption.SCREENSHOT_FORMAT,
|
||||
GameOption.NEW_CURSOR,
|
||||
GameOption.DYNAMIC_BACKGROUND
|
||||
GameOption.DYNAMIC_BACKGROUND,
|
||||
GameOption.LOAD_VERBOSE
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -341,6 +343,11 @@ public class Options extends BasicGameState {
|
|||
fixedCS = 0f, fixedHP = 0f,
|
||||
fixedAR = 0f, fixedOD = 0f;
|
||||
|
||||
/**
|
||||
* Whether or not to render loading text in the splash screen.
|
||||
*/
|
||||
private static boolean loadVerbose = true;
|
||||
|
||||
/**
|
||||
* Game option coordinate modifiers (for drawing).
|
||||
*/
|
||||
|
@ -584,6 +591,9 @@ public class Options extends BasicGameState {
|
|||
case IGNORE_BEATMAP_SKINS:
|
||||
ignoreBeatmapSkins = !ignoreBeatmapSkins;
|
||||
break;
|
||||
case LOAD_VERBOSE:
|
||||
loadVerbose = !loadVerbose;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -750,6 +760,12 @@ public class Options extends BasicGameState {
|
|||
"The song background will be used as the main menu background."
|
||||
);
|
||||
break;
|
||||
case LOAD_VERBOSE:
|
||||
drawOption(pos, "Display Loading Text",
|
||||
loadVerbose ? "Yes" : "No",
|
||||
"Display loading progress in the splash screen."
|
||||
);
|
||||
break;
|
||||
case MUSIC_VOLUME:
|
||||
drawOption(pos, "Music Volume",
|
||||
String.format("%d%%", musicVolume),
|
||||
|
@ -1048,6 +1064,12 @@ public class Options extends BasicGameState {
|
|||
*/
|
||||
public static float getFixedOD() { return fixedOD; }
|
||||
|
||||
/**
|
||||
* Returns whether or not to render loading text in the splash screen.
|
||||
* @return true if enabled
|
||||
*/
|
||||
public static boolean isLoadVerbose() { return loadVerbose; }
|
||||
|
||||
/**
|
||||
* Returns the current beatmap directory.
|
||||
* If invalid, this will attempt to search for the directory,
|
||||
|
@ -1144,6 +1166,9 @@ public class Options extends BasicGameState {
|
|||
case "DynamicBackground":
|
||||
dynamicBackground = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "LoadVerbose":
|
||||
loadVerbose = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "VolumeMusic":
|
||||
i = Integer.parseInt(value);
|
||||
if (i >= 0 && i <= 100)
|
||||
|
@ -1243,6 +1268,8 @@ public class Options extends BasicGameState {
|
|||
writer.newLine();
|
||||
writer.write(String.format("DynamicBackground = %b", dynamicBackground));
|
||||
writer.newLine();
|
||||
writer.write(String.format("LoadVerbose = %b", loadVerbose));
|
||||
writer.newLine();
|
||||
writer.write(String.format("VolumeMusic = %d", musicVolume));
|
||||
writer.newLine();
|
||||
writer.write(String.format("VolumeEffect = %d", effectVolume));
|
||||
|
|
|
@ -85,7 +85,7 @@ public class SongMenu extends BasicGameState {
|
|||
/**
|
||||
* The current sort order (SORT_* constant).
|
||||
*/
|
||||
private byte currentSort;
|
||||
private byte currentSort = OsuGroupList.SORT_TITLE;
|
||||
|
||||
/**
|
||||
* The options button (to enter the "Game Options" menu).
|
||||
|
@ -135,11 +135,6 @@ public class SongMenu extends BasicGameState {
|
|||
this.game = game;
|
||||
this.input = container.getInput();
|
||||
|
||||
// initialize song list
|
||||
currentSort = OsuGroupList.SORT_TITLE;
|
||||
Opsu.groups.init(currentSort);
|
||||
setFocus(Opsu.groups.getRandomNode(), -1, true);
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
|
||||
|
|
125
src/itdelatrisu/opsu/states/Splash.java
Normal file
125
src/itdelatrisu/opsu/states/Splash.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu.states;
|
||||
|
||||
import itdelatrisu.opsu.Opsu;
|
||||
import itdelatrisu.opsu.OsuGroupList;
|
||||
import itdelatrisu.opsu.OsuParser;
|
||||
import itdelatrisu.opsu.SoundController;
|
||||
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.SlickException;
|
||||
import org.newdawn.slick.state.BasicGameState;
|
||||
import org.newdawn.slick.state.StateBasedGame;
|
||||
|
||||
public class Splash extends BasicGameState {
|
||||
/**
|
||||
* Logo image.
|
||||
*/
|
||||
private Image logo;
|
||||
|
||||
/**
|
||||
* Whether or not loading has completed.
|
||||
*/
|
||||
private boolean finished = false;
|
||||
|
||||
// game-related variables
|
||||
private int state;
|
||||
|
||||
public Splash(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(GameContainer container, StateBasedGame game)
|
||||
throws SlickException {
|
||||
final int width = container.getWidth();
|
||||
final int height = container.getHeight();
|
||||
|
||||
logo = new Image("logo.png");
|
||||
logo = logo.getScaledCopy((height / 1.2f) / logo.getHeight());
|
||||
logo.setAlpha(0f);
|
||||
|
||||
// load Utils class first (needed in other 'init' methods)
|
||||
Utils.init(container, game);
|
||||
|
||||
// load other resources in a new thread
|
||||
final SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// parse song directory
|
||||
OsuParser.parseAllFiles(Options.getBeatmapDir(), width, height);
|
||||
|
||||
// initialize song list
|
||||
Opsu.groups.init(OsuGroupList.SORT_TITLE);
|
||||
menu.setFocus(Opsu.groups.getRandomNode(), -1, true);
|
||||
|
||||
// load sounds
|
||||
SoundController.init();
|
||||
|
||||
finished = true;
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GameContainer container, StateBasedGame game, Graphics g)
|
||||
throws SlickException {
|
||||
g.setBackground(Color.black);
|
||||
|
||||
int width = container.getWidth();
|
||||
int height = container.getHeight();
|
||||
|
||||
logo.drawCentered(width / 2, height / 2);
|
||||
|
||||
// progress tracking
|
||||
String currentFile = OsuParser.getCurrentFileName();
|
||||
if (currentFile != null && Options.isLoadVerbose()) {
|
||||
g.setColor(Color.white);
|
||||
g.setFont(Utils.FONT_MEDIUM);
|
||||
int lineHeight = Utils.FONT_MEDIUM.getLineHeight();
|
||||
g.drawString(
|
||||
String.format("Loading... (%s%%)", OsuParser.getParserProgress()),
|
||||
25, height - 25 - (lineHeight * 2)
|
||||
);
|
||||
g.drawString(currentFile, 25, height - 25 - lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GameContainer container, StateBasedGame game, int delta)
|
||||
throws SlickException {
|
||||
// fade in logo
|
||||
float alpha = logo.getAlpha();
|
||||
if (alpha < 1f)
|
||||
logo.setAlpha(alpha + (delta / 400f));
|
||||
|
||||
// change states when loading complete
|
||||
if (finished && alpha >= 1f)
|
||||
game.enterState(Opsu.STATE_MAINMENU);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() { return state; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user