From 3b13cc794b4ec9997ca40f571f71bb0dd7eed0b7 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Tue, 20 Jan 2015 14:52:02 -0500 Subject: [PATCH] Implemented volume-bg and master volume setting. - All sounds are now multiplied by a master volume setting. - Changed all default volume levels. - Scrolling in the main menu and game states changes the master volume and displays a volume bar on the right side of the screen. - "volume-bg.png" image by @kouyang. Signed-off-by: Jeffrey Han --- res/volume-bg.png | Bin 0 -> 7857 bytes src/itdelatrisu/opsu/GameImage.java | 6 ++ src/itdelatrisu/opsu/Utils.java | 68 ++++++++++++++++++ .../opsu/audio/MusicController.java | 5 +- .../opsu/audio/SoundController.java | 6 +- src/itdelatrisu/opsu/states/Game.java | 7 ++ .../opsu/states/GamePauseMenu.java | 2 + src/itdelatrisu/opsu/states/GameRanking.java | 2 + src/itdelatrisu/opsu/states/MainMenu.java | 7 ++ src/itdelatrisu/opsu/states/MainMenuExit.java | 2 + src/itdelatrisu/opsu/states/Options.java | 53 ++++++++++++-- src/itdelatrisu/opsu/states/SongMenu.java | 2 + 12 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 res/volume-bg.png diff --git a/res/volume-bg.png b/res/volume-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..9858abceb26deff151df8e3c81703932d7a8c281 GIT binary patch literal 7857 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, cursorY = new LinkedList(); + /** + * Time to show volume image, in milliseconds. + */ + private static final int VOLUME_DISPLAY_TIME = 1500; + + /** + * Volume display elapsed time. + */ + private static int volumeDisplay = -1; + /** * Set of all Unicode strings already loaded. */ @@ -517,6 +528,63 @@ public class Utils { ); } + /** + * Draws the volume bar on the middle right-hand side of the game container. + * Only draws if the volume has recently been changed using with {@link #changeVolume(int)}. + * @param g the graphics context + */ + public static void drawVolume(Graphics g) { + if (volumeDisplay == -1) + return; + + int width = container.getWidth(), height = container.getHeight(); + Image img = GameImage.VOLUME.getImage(); + + // move image in/out + float xOffset = 0; + float ratio = (float) volumeDisplay / VOLUME_DISPLAY_TIME; + if (ratio <= 0.1f) + xOffset = img.getWidth() * (1 - (ratio * 10f)); + else if (ratio >= 0.9f) + xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f)); + + img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f); + float barHeight = img.getHeight() * 0.9f; + float volume = Options.getMasterVolume(); + g.setColor(Color.white); + g.fillRoundRect( + width - (img.getWidth() * 0.368f) + xOffset, + (height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)), + img.getWidth() * 0.15f, barHeight * volume, 3 + ); + } + + /** + * Updates volume display by a delta interval. + * @param delta the delta interval since the last call + */ + public static void updateVolumeDisplay(int delta) { + if (volumeDisplay == -1) + return; + + volumeDisplay += delta; + if (volumeDisplay > VOLUME_DISPLAY_TIME) + volumeDisplay = -1; + } + + /** + * Changes the master volume by a unit (positive or negative). + * @param units the number of units + */ + public static void changeVolume(int units) { + final float UNIT_OFFSET = 0.05f; + Options.setMasterVolume(container, Utils.getBoundedValue(Options.getMasterVolume(), UNIT_OFFSET * units, 0f, 1f)); + if (volumeDisplay == -1) + volumeDisplay = 0; + else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10) + volumeDisplay = VOLUME_DISPLAY_TIME / 10; + } + /** * Takes a screenshot. * @author http://wiki.lwjgl.org/index.php?title=Taking_Screen_Shots diff --git a/src/itdelatrisu/opsu/audio/MusicController.java b/src/itdelatrisu/opsu/audio/MusicController.java index 2fdca837..5ce69f93 100644 --- a/src/itdelatrisu/opsu/audio/MusicController.java +++ b/src/itdelatrisu/opsu/audio/MusicController.java @@ -133,7 +133,7 @@ public class MusicController { */ public static void playAt(final int position, final boolean loop) { if (trackExists()) { - SoundStore.get().setMusicVolume(Options.getMusicVolume()); + setVolume(Options.getMusicVolume() * Options.getMasterVolume()); player.setPosition(position / 1000f); if (loop) player.loop(); @@ -303,7 +303,8 @@ public class MusicController { * Toggles the volume dim state of the current track. */ public static void toggleTrackDimmed() { - setVolume((trackDimmed) ? Options.getMusicVolume() : Options.getMusicVolume() / 3f); + float volume = Options.getMusicVolume() * Options.getMasterVolume(); + setVolume((trackDimmed) ? volume : volume / 3f); trackDimmed = !trackDimmed; } diff --git a/src/itdelatrisu/opsu/audio/SoundController.java b/src/itdelatrisu/opsu/audio/SoundController.java index 03c4dba7..b8ff5a62 100644 --- a/src/itdelatrisu/opsu/audio/SoundController.java +++ b/src/itdelatrisu/opsu/audio/SoundController.java @@ -169,7 +169,7 @@ public class SoundController { * @param s the sound effect */ public static void playSound(SoundComponent s) { - playClip(s.getClip(), Options.getEffectVolume()); + playClip(s.getClip(), Options.getEffectVolume() * Options.getMasterVolume()); } /** @@ -180,7 +180,7 @@ public class SoundController { if (hitSound < 0) return; - float volume = Options.getHitSoundVolume() * sampleVolumeMultiplier; + float volume = Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume(); if (volume == 0f) return; @@ -202,7 +202,7 @@ public class SoundController { * @param s the hit sound */ public static void playHitSound(SoundComponent s) { - playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier); + playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume()); } /** diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 6d8b52f6..5ee08b62 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -421,6 +421,7 @@ public class Game extends BasicGameState { cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY); } + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -429,6 +430,7 @@ public class Game extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); skipButton.hoverUpdate(delta, mouseX, mouseY); @@ -707,6 +709,11 @@ public class Game extends BasicGameState { hitObjects[objectIndex].mousePressed(x, y); } + @Override + public void mouseWheelMoved(int newValue) { + Utils.changeVolume((newValue < 0) ? -1 : 1); + } + @Override public void enter(GameContainer container, StateBasedGame game) throws SlickException { diff --git a/src/itdelatrisu/opsu/states/GamePauseMenu.java b/src/itdelatrisu/opsu/states/GamePauseMenu.java index c291487f..54d38854 100644 --- a/src/itdelatrisu/opsu/states/GamePauseMenu.java +++ b/src/itdelatrisu/opsu/states/GamePauseMenu.java @@ -104,6 +104,7 @@ public class GamePauseMenu extends BasicGameState { retryButton.draw(); backButton.draw(); + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -112,6 +113,7 @@ public class GamePauseMenu extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); continueButton.hoverUpdate(delta, mouseX, mouseY); retryButton.hoverUpdate(delta, mouseX, mouseY); diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 4c6b28fb..eeacc8b1 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -129,6 +129,7 @@ public class GameRanking extends BasicGameState { exitButton.draw(); Utils.getBackButton().draw(); + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -137,6 +138,7 @@ public class GameRanking extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY); } diff --git a/src/itdelatrisu/opsu/states/MainMenu.java b/src/itdelatrisu/opsu/states/MainMenu.java index 8c3d5228..b4b7bd07 100644 --- a/src/itdelatrisu/opsu/states/MainMenu.java +++ b/src/itdelatrisu/opsu/states/MainMenu.java @@ -242,6 +242,7 @@ public class MainMenu extends BasicGameState { new SimpleDateFormat("h:mm a").format(new Date())), marginX, height - marginY - lineHeight); + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -250,6 +251,7 @@ public class MainMenu extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); logo.hoverUpdate(delta, mouseX, mouseY); playButton.hoverUpdate(delta, mouseX, mouseY); @@ -402,6 +404,11 @@ public class MainMenu extends BasicGameState { } } + @Override + public void mouseWheelMoved(int newValue) { + Utils.changeVolume((newValue < 0) ? -1 : 1); + } + @Override public void keyPressed(int key, char c) { switch (key) { diff --git a/src/itdelatrisu/opsu/states/MainMenuExit.java b/src/itdelatrisu/opsu/states/MainMenuExit.java index 7323f21a..c98bf1ed 100644 --- a/src/itdelatrisu/opsu/states/MainMenuExit.java +++ b/src/itdelatrisu/opsu/states/MainMenuExit.java @@ -107,6 +107,7 @@ public class MainMenuExit extends BasicGameState { "2. No", Color.white ); + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -115,6 +116,7 @@ public class MainMenuExit extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); // move buttons to center float yesX = yesButton.getX(), noX = noButton.getX(); diff --git a/src/itdelatrisu/opsu/states/Options.java b/src/itdelatrisu/opsu/states/Options.java index 65b5df1a..cdf1f756 100644 --- a/src/itdelatrisu/opsu/states/Options.java +++ b/src/itdelatrisu/opsu/states/Options.java @@ -172,14 +172,24 @@ public class Options extends BasicGameState { container.setVSync(getTargetFPS() == 60); } }, - MUSIC_VOLUME ("Music Volume", "Global music volume.") { + MASTER_VOLUME ("Master Volume", "Global volume level.") { + @Override + public String getValueString() { return String.format("%d%%", masterVolume); } + + @Override + public void drag(GameContainer container, int d) { + masterVolume = Utils.getBoundedValue(masterVolume, d, 0, 100); + container.setMusicVolume(getMasterVolume() * getMusicVolume()); + } + }, + MUSIC_VOLUME ("Music Volume", "Volume of music.") { @Override public String getValueString() { return String.format("%d%%", musicVolume); } @Override public void drag(GameContainer container, int d) { musicVolume = Utils.getBoundedValue(musicVolume, d, 0, 100); - container.setMusicVolume(getMusicVolume()); + container.setMusicVolume(getMasterVolume() * getMusicVolume()); } }, EFFECT_VOLUME ("Effect Volume", "Volume of menu and game sounds.") { @@ -480,6 +490,7 @@ public class Options extends BasicGameState { * Music options. */ private static final GameOption[] musicOptions = { + GameOption.MASTER_VOLUME, GameOption.MUSIC_VOLUME, GameOption.EFFECT_VOLUME, GameOption.HITSOUND_VOLUME, @@ -612,20 +623,25 @@ public class Options extends BasicGameState { */ private static boolean showComboBursts = true; + /** + * Global volume level. + */ + private static int masterVolume = 35; + /** * Default music volume. */ - private static int musicVolume = 30; + private static int musicVolume = 80; /** * Default sound effect volume. */ - private static int effectVolume = 20; + private static int effectVolume = 70; /** * Default hit sound volume. */ - private static int hitSoundVolume = 20; + private static int hitSoundVolume = 70; /** * Offset time, in milliseconds, for music position-related elements. @@ -847,6 +863,7 @@ public class Options extends BasicGameState { ); } + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -855,6 +872,7 @@ public class Options extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY); for (GameMod mod : GameMod.values()) @@ -1044,6 +1062,24 @@ public class Options extends BasicGameState { */ public static int getTargetFPS() { return targetFPS[targetFPSindex]; } + /** + * Returns the master volume level. + * @return the volume [0, 1] + */ + public static float getMasterVolume() { return masterVolume / 100f; } + + /** + * Sets the master volume level (if within valid range). + * @param container the game container + * @param volume the volume [0, 1] + */ + public static void setMasterVolume(GameContainer container, float volume) { + if (volume >= 0f && volume <= 1f) { + masterVolume = (int) (volume * 100f); + container.setMusicVolume(getMasterVolume() * getMusicVolume()); + } + } + /** * Returns the default music volume. * @return the volume [0, 1] @@ -1417,6 +1453,11 @@ public class Options extends BasicGameState { case "LoadVerbose": loadVerbose = Boolean.parseBoolean(value); break; + case "VolumeUniversal": + i = Integer.parseInt(value); + if (i >= 0 && i <= 100) + masterVolume = i; + break; case "VolumeMusic": i = Integer.parseInt(value); if (i >= 0 && i <= 100) @@ -1551,6 +1592,8 @@ public class Options extends BasicGameState { writer.newLine(); writer.write(String.format("LoadVerbose = %b", loadVerbose)); writer.newLine(); + writer.write(String.format("VolumeUniversal = %d", masterVolume)); + writer.newLine(); writer.write(String.format("VolumeMusic = %d", musicVolume)); writer.newLine(); writer.write(String.format("VolumeEffect = %d", effectVolume)); diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index ac53a59c..394abbc7 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -343,6 +343,7 @@ public class SongMenu extends BasicGameState { // back button Utils.getBackButton().draw(); + Utils.drawVolume(g); Utils.drawFPS(); Utils.drawCursor(); } @@ -351,6 +352,7 @@ public class SongMenu extends BasicGameState { public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { Utils.updateCursor(delta); + Utils.updateVolumeDisplay(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); Utils.getBackButton().hoverUpdate(delta, mouseX, mouseY); optionsButton.hoverUpdate(delta, mouseX, mouseY);